Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Difference From 7ce3553bf5f4bccd To 61ce7d6852d4e684
2017-01-26
| ||
02:09 | Several adjustments to set_pidp8i_leds() calls. The main practical improvement is that we can now see execute state on the front panel. It was in that state too briefly before it got reset to fetch. check-in: 3c2086101e user: tangent tags: trunk | |
01:44 | Removed the code that skips updating the global ledstatus[] values from the PDP-8 CPU core based on the human PoV limit. Because the GPIO thread runs asynchronously with it and has its own timing, these two high-jitter timing loops created a messy beat frequency on the front panel, which you could see in a tight KSF ; JMP .-1 loop, as OS/8 does when idle, waiting for a key. This mechanism came to us in the merger of the common parts of ILS and NLS, and ILS only needed it when it was modifying the LED brightness values values in the SIMH thread instead of in the GPIO thread as it does now. The only argument for continuing to do this — and for also doing it for NLS — is that it avoids a bunch of recalculation that the human couldn't see, but that is only true if the GPIO thread is able to stochastically sample the LED states so that even with the asynchronous jitter going on, a tight loop like the above eventually blurs together to a nice 50/50 mix of the two instructions' LED states, especially under the ILS. Ultimately, any attention paid to the human PoV limit has to be over in the GPIO thread. check-in: 61ce7d6852 user: tangent tags: trunk | |
01:08 | Comment fix check-in: ec527293eb user: tangent tags: trunk | |
2016-11-21
| ||
09:33 | Upstream version 20151215, containing everything from the tarball except for backups and *.o. Also, all CRLF text files converted to LF. check-in: ec36ee3dab user: tangent tags: trunk, v20151215 | |
09:11 | initial empty check-in check-in: 7ce3553bf5 user: tangent tags: trunk | |
> > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 | doc/*.png examples/*.pt labels/*.pdf media/*/*.bin media/*/*.dsk media/*/*.pt media/*/*.rk05 media/*/*.tu56 pics/*/*.jpg pics/*/*.png |
> > > > > | 1 2 3 4 5 | src/scp.* src/sim_*.[ch] src/sim_*.in src/PDP8/pdp8_*.[ch] src/PDP8/pidp8i.c.in |
> | 1 | doc/simh/*.doc |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | # Creators and Major Contributors to the PiDP-8/I Project * **Oscar Vermeulen <oscar.vermeulen@hotmail.com>**: - Creator of the project (both hardware and software) - Author of the modifications to the SimH PDP-8 simulator necessary to make it use the PiDP-8/I front panel hardware - Curator of the default set of binary demo media - Author of the simulator setup scripts - Initiator of much else in the project - Author of the bulk of the documentation - Host and major contributor to the PiDP-8/I support forum on Google Groups - Hardware kit assembler and distributor * **Robert M Supnik** Primary author of the SimH PDP-8 simulator upon which this project is based. * **Mike Barnes** Ported Oscar Vermeulen's SimH 3.9 based PiDP-8/I simulator to the new SimH 4.0 code base. * **Dylan McNamee** Creator of the "buildroot" feature used in the creation of the official 2015.12.15 release versions. * **Mark G. Thomas** Creator of the installation scripts for the 2015.12.15 release, which were folded into the `make install` handler within `Makefile.in`. Also wrote the version of the SysV init script that came with that release as `rc.pidp8`, shipped here as `pidp8i-init`. * **Ian Schofield <isysxp@gmail.com>** Modified the LED lamp driving code in the simulator to better simulate the incandescent lamps in the original PDP-8/I hardware. * **Henk Gooijen <henk.gooijen@boschrexroth.nl>** Pushed the PDP-8 simulator's internal EAE step counter value down into the PiDP-8/I's LED manipulation code, without which the step counter LEDs remain dark even when using the EAE. * **Paul R. Bernard <prb@downspout.ca>** wrote `src/test.c` and the core of what now appears as `README-test.md`. (The program builds and installs `pidp8i-test`.) He also provided a one-line fix that completes the work of Henk Gooijen's step counter patch. * **Rick Murphy <k1mu.nospam@gmail.com>** optimized the `pep001.pal` * example so that it fits into a single page of PDP-8 core, and provided several useful files in his OS/8 disk images that have managed to land in this software distribution's OS/8 disk image. * **Tony Hill <hill.anthony@gmail.com>** Merged all the upstream SIMH changes between late September 2015 and late December 2016 into the PiDP-8/I simulator. * **Warren Young <tangentsoft@gmail.com>** Did everything listed in `ChangeLog.md` that is not attributed to anyone else. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | # Licenses The PiDP-8/I software distribution is an agglomeration of software from multiple sources. Several different licenses apply to its parts. This file guides you to those individual licenses. ## SIMH License Most of the files in this software distribution are released under the terms of the SIMH license, a copy of which typically appears at the top of each file it applies to. This includes not only SIMH proper but also several files written by PiDP-8/I software project contributors who chose to license their contributions under the same license. For a few files, textual inclusion of the license inside the file itself was impractical, so this license is applied by reference to [a file included with the distribution][sl]. [sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md ## autosetup License The `configure` script and the contents of the `autosetup` directory are released under the FreeBSD license given in [`autosetup/LICENSE`][as]. [as]: https://tangentsoft.com/pidp8i/doc/trunk/autosetup/LICENSE ## palbart License The `palbart` program and its manual page are released under the terms of the license given in [`palbart/LICENSE.md`][pl]. [pl]: https://tangentsoft.com/pidp8i/doc/trunk/palbart/LICENSE.md ## OS/8 License The OS/8 media images included with this software distribution are released under the Digital License Agreement presented in [`media/os8/LICENSE.md`][dla]. [dla]: https://tangentsoft.com/pidp8i/doc/trunk/media/os8/LICENSE.md ## Other DEC Software The other files in the [`media`][md] and [`examples`][ed] directories that originate from Digital Equipment Corporation are believed to fall under the [public domain license][pdp8pd] DEC released all their PDP-8 software under after it stopped being ecomonmically viable. Documented releases for specific software (e.g. TSS/8) may be difficult to come by, however. [md]: https://tangentsoft.com/pidp8i/dir?ci=trunk&name=media [ed]: https://tangentsoft.com/pidp8i/dir?ci=trunk&name=examples ## ETOS License ETOS was a commercial product produced outside of DEC. No public documented declaration of license is known to be available for it, but we have [a third-hand report][el] that its creators are fine with ETOS being redistributed. [el]: http://mailman.trailing-edge.com/pipermail/simh/2017-January/016169.html |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 | # PiDP-8/I Changes ## Version 2017.01.23 * When any program that talks to the PiDP-8/I front panel starts up, it now prints out a banner modeled on the [Erlang configuration line][ecl]. For example, when I run the software in the development tree on my PiDP-8/I, I get the following: PiDP-8/I trunk:i49cd065c [pi3b] [ils] [serpcb] [gpio] It tells me that: * I'm running code built from Fossil checkin ID 49cd065c on the trunk branch, as opposed to a release version, which would be marked `release:v20170123` or similar. (The `i` here is a tag standing for "ID", as in Fossil checkin ID. Contrast `v` used to tag release version numbers.) * I'm running it on a Raspberry Pi 3 Model B with Ian Schofield's incandescent lamp simulator (ILS) feature enabled. * The software is built to expect that the PiDP-8/I PCB and the Pi board attached to it have had the serial mods made to them. * The GPIO module found the GPIO hardware and was able to attach to it. * I get a very different result when running it on my desktop machine: PiDP-8/I trunk:id8536d91 [cake] [nls] [nopcb] [rt] This tells me: * I'm running a different version of the development branch (i.e. the "trunk") of the code than what's running on the Pi. * It's not running on a Pi at all. (Cake ≠ pi.) * I've disabled the ILS feature, so it's running with the "no lamp simulator" (NLS) GPIO module. * Which is all to the good, because there's no point burning CPU power running the ILS code on a host machine that doesn't have a PiDP-8/I PCB attached. * The GPIO thread is running with real-time privileges. * The ILS feature can now be disabled at `configure` time via the new `--no-lamp-simulator` flag. This option is automatically set when building on a single-core Raspberry Pi. (The flag is there only to allow someone building the software on a multi-core host machine to disable the ILS.) * Tweaked the ILS decay constants to be asymmetric, better mimicking the way real incandescent lamps work: they heat up to full brightness faster than they fade to perceptively "off." * The LED values used by the GPIO thread were being recalculated way too often. In the ILS case, it was updating the values approximately at the same rate as the ILS's PWM core frequency, roughly 7,500 times per second, which is far higher than the human persistence of vision limit. While the PWM rate does need to be that fast to do its job, the underlying LED state values do not need to change nearly that often to fool the human into seeing instantaneous updates. The NLS case was actually worse, recalculating the LED values on every instruction executed by the PDP-8 CPU simulator, which even on a Pi 1 is likely to be a few MHz. In both the ILS and NLS cases, we now update the LED values about 100 times a second, maintaining that rate dynamically based on the current execution speed of the simulator. * In prior versions, the ILS was only updating at its intended rate when the PDP-8 simulator was running flat-out on a current multi-core Raspberry Pi. If you throttled the SIMH simulator to a slower execution rate, the display quality would start to degrade noticeably below about 1 MIPS. * With the prior fix, we now ship 5.script (i.e. the handler for starting the simulator with IF=5, or restarting it with IF=5 + `SING_STEP`) set to a throttle value of 30 kIPS, which allows the human to see each AC/MQ modification. The built-in delay loops are still there, else we'd have to drop this to well under 1 kIPS. * The `SING_INST` switch now immediately puts the processor into single instruction mode, not requiring a separate press of the `STOP` key, as in prior versions. This is the correct behavior according to the 1967-1968 edition of DEC's Small Computer Handbook for the PDP-8/I. * Greatly simplified the way single-instruction mode, processor stoppage, and the `CONT` key work. The prior implementation was error-prone and difficult to understand. This fixes a particularly bad interaction between the way `HLT` instructions and `CONT` key presses were handled, causing the processor to not resume correctly from `HLT` state. * Consolidated and cleaned up the bulk of the PiDP-8/I switch handling code so that it is not so intimately tied into the guts of the PDP-8 CPU emulator. This will greatly increase the chance that future updates to the upstream SIMH code will apply cleanly to our version. * Fixed a bug in `examples/bit-rotate.pal` which caused it to skip the actual bit rotation step. We were trying to microcode two instructions into one that the PDP-8 won't accept together, and we didn't catch it until now because the HLT bug masked it, and the `palbart` assembler we ship isn't smart enough to notice the bug. * Fully generalized the mechanism for generating `obj/*.lst`, `bin/*.pt`, and `boot/*.script` from `examples/*.pal`. You can now drop any PAL assembly language program into the `examples` directory and type `make` to build these various output forms automatically using the shipped version of `palbart`. This effectively turns this PiDP-8/I software distribution into a PDP-8 assembly language development environment: rapidly build, test, and debug your PAL programs on your PC before you deploy them to real hardware. Or, write PAL programs to debug the hardware or simulator, as we did with `examples/bit-rotate.pal`. * Fixed a sorting bug in the tool that generates `boot/*.script` from `obj/*.lst`, resulting in `dep` instructions that weren't sorted by core address. This didn't cause any real problem, but it made tracing the execution of a PAL assembly program difficult if you were trying to refer to the `boot/*.script` file to check that the PiDP-8/I's front panel is showing the correct register values. * Updated SIMH to the latest upstream version and shipping a subset of the SIMH docs as unversioned files from tangentsoft.com. * The `configure` script now aborts the build if it sees that you're trying to build the software as root, since that means it generates the init script and the pidp8i script expecting to run the installed software as root, not as your normal user. The most common way this happens is that you have a configured source tree, then change one of the `*.in` files and say `sudo make install`, thinking to build and install the change in one step. This fixes that. * Several improvements to the build system. [ecl]: http://stackoverflow.com/q/1182025/142454 ## Version 2017.01.16 * Prior releases did not include proper licensing for many of the included files. This project was, therefore, not a proper Open Source Software project. This problem has been fixed. In this release, many files that were previously technically only under standard copyright due to having no grant of license now have an explicit license, typically the same as SIMH itself. (Thank you to all of the authors who allowed me to apply this license to their contributions!) For several other files, I was able to trace down some prior license and include its text here for the first time. There remain a few "gray" items: the TSS/8 and ETOS disk images. See the [`COPYING.md` file][copying] for more on the current status of these OS images. If the legal status of these files clarifies in the future, this software distribution will react accordingly, even if that means removing these files from the distribution if we learn that these files are not freely-redistributable, as we currently believe them to be today. * The Step Counter LEDs on the front panel weren't being lit properly when EAE instructions were being used. Thanks for this patch go to Henk Gooijen and Paul R. Bernard. * The prior `boot/1.script` and `boot/5.script` files are no longer simply opaque lists of octal addresses and machine code. They are generated from PAL assembly files provided in the `examples` directory, so that you can now modify the assembly code and type `make` to rebuild these boot scripts. * The mechanism behind the prior item is fully general-purpose, not something that only works with `1.script` and `5.script`. Any `examples/*.pal` file found at `make` time is transformed into a SIMH boot script named after the PAL file and placed in the `boot` directory. This gives you an easier way to run PDP-8 assembly code inside the simulator. After saying `make` to transform `*.pal` into `*.script` files, you can run the program with `bin/pidp8i-sim boot/my-program.script` to poke your program's octal values into core and run it. This round-trip edit-and-run process is far faster than any of the options given in the [examples' `README.md` file][ex]. * Disassembled both versions of the RIM loader to commented, labeled PAL assembly language files. If you ever wanted to know what those 16 mysterious instructions printed on the front panel of your PiDP-8/I did, you can now read my pidgin interpretation of these programs in `examples/*-rim.loader.pal` and become just as confused as I am now. :) * The two RIM loader implementations now start with a nonstandard `HLT` instruction so that when you fire up the simulator with IF=1 to start the high-speed RIM loader, it automatically halts for you, so you don't have to remember to STOP the processor manually. There is currently [a bug][hltbug] in the way the simulator handles `HLT` instructions which prevents you from simply pressing START or CONT to enter the RIM loader after you've attached your paper tape, so you still have to manually toggle in the 7756 starting address and press START to load the tape into core. (I hope to fix this before the next release, but no promises.) * Added the `configure --throttle` feature for making the simulator run at a different speed than it normally does. See [`README-throttle.md`][rmth] for details. * The build system now reacts differently when building the PiDP-8/I software on a single-core Raspberry Pi: * If you're building the trunk or release branch, you'll get a configure error because it knows you can't run the current implementation of the incandescent lamp simulator on a single-core Pi. (Not enough spare CPU power, even with heavy amounts of throttling.) * If you're building the no-lamp-simulator branch, it inserts a throttle value into the generated `boot/*.script` files that do not already contain a throttle value so that the simulator doesn't hog 100% of the lone core, leaving some spare cycles for background tasks. The above `--throttle` feature overrides this. These features effectively replace the manual instructions in the old `README-single-core.md` file, which is no longer included with this software distribution, starting with this release. * Lowered the real-time priority of the GPIO thread from 98 to 4. This should not result in a user-visible change in behavior, but it is called out here in case it does. (In our testing, such high values simply aren't necessary to get the necessary performance, even on the trunk branch with the incandescent lamp simulator.) * Since v20161128, when you `make install` on a system with an existing PiDP-8/I software installation, the binary OS media images were not being overwritten, on purpose, since you may have modified them locally, so the installer chose not to overwrite your versions. With this release, the same principle applies to the SIMH boot scripts (e.g. `$prefix/share/boot/0.script`) since those are also things the user might want to modify. This release and prior ones do have important changes to some of these files, so if you do not wish to overwrite your local changes with a `make mediainstall` command, you might want to diff the two versions and decide which changes to copy over or merge into your local files. [hltbug]: https://tangentsoft.com/pidp8i/info/f961906a5c24f5de [copying]: https://tangentsoft.com/pidp8i/doc/trunk/COPYING.md [rmth]: https://tangentsoft.com/pidp8i/doc/trunk/README-throttle.md ## Version 2017.01.05 * Automated the process for merging in new SIMH updates. From within the PiDP-8/I software build directory, simply say `make simh-update` and it will do its best to merge in the latest upstream changes. This process is more for the PiDP-8/I software maintainers than for the end users of that software, but if you wish to update your SIMH software without waiting for a new release of *this* software, you now have a nice automated system for doing that. * Updated SIMH using that new process. The changes relevant to the PiDP-8/I since the prior update in release v20161226 are: * Many more improvements to the simulator's internal timer system. This should make deliberate underclocking more accurate. * It is now possible to get hex debug logs for the simulator console port by cranking up the simulator's debug level. * The simulator now reports the upstream Git commit ID it is based on in its version string, so that if you report bugs upstream to the SIMH project, you can give them a version number that will be meaningful to them. (They don't care about our vYYYYMMDD release numbers or our Fossil checkin IDs.) ## Version 2016.12.26 (The Boxing Day release) * Tony Hill updated SIMH to the latest upstream version. This change represents about 15 months worth of work in the [upstream project][simh] — plus a fair bit of work by Tony to merge it all — so I will only summarize the improvements affecting the PDP-8 simulator here: * Many improvements to the internal handling of timers. The most user-visible improvement is that you can now clock your emulated PDP-8 down to well below the performance of a real PDP-8 via `SET THROTTLE`, which can be useful for making blinkenlights demos run at human speeds without adding huge delay loops to the PDP-8 code implementing that demo. * Increased the number of supported terminals from four to either twelve or sixteen, depending on how you look at it. Eight of the additional supported terminal devices are conflict-free, while the final four variously conflict with one or more of the other features of the simulated PDP-8. If you want to use all 16, you will be unable to use the FPP, CT, MT and TSC features of the system. This limitation reflects the way the PDP-8 worked. It is not an arbitrary limitation of SIMH. * Added support for the LS8E printer interface option used by the WPS8 word processing system. * The simulator's command console now shows the FPP register descriptions when using it as a PDP-8 debugger. * Added the `SHOW TTIX/TTOX DEVNO` SIMH command to display the device numbers used for TTIX and TTOX. * The `SHOW TTIX SUMMARY` SIMH command is now case-insensitive. * Upstream improvements to host OS/compiler compatibility. This increases the chances that this software will build out of the box on random non-Raspbian systems such as your development laptop running some uncommon operating system. * When you `make install`, we now disable Deeper Thought 2 and the legacy `pidp8` service if we find them, since they conflict with our `pidp8i` service. * Added the install user to the `gpio` group if you `make install` if that group is present at install time. This is useful when building and installing the software on an existing Raspbian SD card while logged in as a user other than `pi` or `pidp8i`. [simh]: https://github.com/simh/simh/ ## Version 2016.12.18 * The entire software stack now runs without explicit root privileges. It now runs under the user and group of the one who built the software. For the few instances where it does need elevated privileges, a limited-scope set of sudo rules are installed that permit the simulator to run the necessary helper programs. * The power down and reboot front panel switch combinations are no longer sensitive to the order you flip the switches. * Changed the powerdown front panel switch combination to the more mnemonically sensible `Sing_Step` + `Sing_Inst` + `Stop`. Its prior switch combo — `Sing_Step` + `Sing_Inst` + `Start` — is now the reboot sequence, with the mnemomic "restart." * Removed the USB stick mount/unmount front panel switch combos. The automount feature precludes a need for a manual mount command, and unmount isn't necessary for paper tape images on FAT sticks. * The simulator now runs correctly on systems where the GPIO setup process fails. (Basically, anything that isn't a Raspberry Pi.) Prior to this, this failure was just blithely ignored, causing subsequent code to behave as though all switches were being pressed at the same time, causing utter havoc. The practical benefit of this is that you can now work with the software on your non-Pi desktop machine, losing out only on the front panel LEDs and switches. Everything else works just as on the Pi. You no longer need a separate vanilla SimH setup. * Added a locking mechanism that prevents `pidpi8-test` and `pidp8i-sim` from fighting over the front panel LEDs. While one of the two is running, the other refuses to run. * Added `examples/ac-mq-blinker.pal`, the PAL8 assembly code for the `boot/5.script` demo. * Fixed two unrelated problems with OS/8's FORTRAN IV implementation which prevented it from a) building new software; and b) running already-built binaries. Thanks go to Rick Murphy for providing the working OS/8 images from which the files needed to fix these two problems were extracted. * Added the VT100-patched `VTEDIT` TECO macro from Rick Murphy's OS/8 images, and made it automatically run when you run TECO from the OS/8 disk pack. Also added documentation for it in `VTEDIT.DC` on the disk pack as well as [in the wiki][vteditdoc]. * The default user name on the binary OS images is now `pidp8i` instead of `pi`, its password has changed to `edsonDeCastro1968`, and it demands a password change on first login. I realize it's a hassle, but I decided I didn't want to contribute to the plague of open-to-the-world IoT boxes. * Many build system and documentation improvements. [vteditdoc][https://tangentsoft.com/pidp8i/wiki?name=Using+VTEDIT] ## Version 2016.12.06 * The `pidp8i-test` program's LED test routines did not work correctly when built against the incandescent lamp simulator version of the GPIO module. Reworked the build so that this test program builds against the no-lamp-simulator version instead so that you don't have to choose between having the lamp simulator or having a working `pidp8i-test` program. * More improvements to `examples/pep001.pal`. * Extracted improved `PRINTS` routine from that example as `examples/routines/prints.pal`. ## Version 2016.12.05 * This release marks the first binary SD card image released under my maintainership of the software. As such, the major user-visible features in this release of the Fossil tree simply support that: * The `pidp8i-init` script now understands that the OS's SSH host keys may be missing, and re-generates them. Without this security measure, anyone who downloads that binary OS image could impersonate the SSH server on *your* PiDP-8/I. * Added a `RELEASE-PROCESS.md` document. This is primarily for my own benefit, to ensure that I don't miss a step, particularly given the complexity of producing the binary OS image. However, you may care to look into it to see what goes on over here on the other side of the Internet. :) * Added an OS/8 BASIC solution to Project Euler Problem #1, so you can see how much simpler it is compared to the PAL8 assembly language version added in the prior release. * Updated the PAL8 assembly version with several clever optimizations by Rick Murphy, the primary effect of which is that it now fits into a single page of PDP-8 core memory. ## Version 2016.12.03 * Debounced the switches. See [the mailing list post][cdb] announcing this fix for details. * Merged the [`pidp8i-test` program][testpg] from the mailing list. The LED testing portion of this program [currently][gpiols] only works correctly without the incandescent lamp simulation patch applied. * Added a solution to [Project Euler Problem #1][pep001] in PAL8 assembly language and wrote the [saga of my battle][p1saga] with this problem into the wiki. This also adds a couple of useful PAL8 routines in `examples/routines`. * Integrated David Gesswein's latest `palbart` program (v2.13) into the source distribution so that we don't have to: 1. ship pre-built RIM format paper tapes for the examples; and 2. put up with the old versions that OS package repos tend to have (Ubuntu is still shipping v2.4, from 6 years ago!) * Fixed a bug in the `make install` script that caused it to skip installing `screen` and `usbmount` from the OS's package repo when they are found to be missing. * Fixed a related bug that prevented it from disabling the serial console if you configure the software without `--serial-mod` and then install it, causing the serial console and the GPIO code in the PiDP-8/I simulator to fight over GPIO pins 14 and 15. * Removed the last of the duplicate binary media entries. This makes the zip files for this version well under half the size of those for the 2015.12.15 upstream release despite having more features. [cdb]: https://groups.google.com/d/msg/pidp-8/Fg9I8OFTXHU/VjamSoFxDAAJ [testpg]: https://groups.google.com/d/msg/pidp-8/UmIaBv2L9Ts/wB1CVeGDAwAJ [gpiols]: https://tangentsoft.com/pidp8i/tktview?name=9843cab968 [pep001]: https://projecteuler.net/problem=1 [p1saga]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.PA ## Version 2016.11.28 * Added an intelligent, powerful build system, replacing the bare-bones `Makefile` based build system in the upstream version. See [`README.md`][readme] for more info on this. * The installation is now completely relocatable via `./configure --prefix=/some/other/place`. The upstream version would not work if installed somewhere other than `/opt/pidp8` due to many hard-coded absolute paths. (This is enabled by the new build system, but fixing it was not simply a matter of swapping out the build system.) * Changed all of the various "PDP," "PDP-8", and "PiDP-8" strings to variants on "PiDP-8/I", partly for consistency and partly because it seems unlikely that this software will ever be used with anything other than the PiDP-8/I project. Part of this renaming means that the default installation location is now `/opt/pidp8i`, which has the nice side benefit that installing this version of the software will not overwrite an existing installation of the upstream version in `/opt/pidp8`. Another user-visible aspect of this change is that the upstream version's `pdp.sh` script to [re]enter the simulator is now called `pidp8i`. * Merged Ian Schofield's [Display update for the PiDP8][dupatch] patch. Currently it is not optional, but there is [a plan][dudis] to allow this feature to be disabled via a `configure` script option. * The scripts that control the startup sequence of the PiDP-8/I simulator now include helpful header comments and emit useful status messages to the console. Stare no more at opaque lists of SimH commands, wondering what each script does! * Merged `scanswitch` into the top-level `src` directory, since the only thing keeping it in a separate directory was the redundant `gpio.h` file. There were minor differences between the two `gpio.h` files, but their differences do not matter. * Installing multiple times no longer overwrites the binary OS/program media, since the disk images in particular may contain local changes. If you want your media images overwritten, you can insist on it via `make mediainstall`. * The installation tree follows the Linux Filesystem Hierarchy Standard, so that files are in locations an experienced Linux user would expect to find them. The biggest changes are that the content of the upstream `bootscripts` tree is now installed into `$prefix/share/boot`, and the OS/program media images which used to be in `imagefiles` are now in `$prefix/share/media`. * Added a bunch of ancillary material: [wiki articles][wiki], [USB stick label artwork][art], a PAL8 assembly [example program][ex] for you to toggle in, etc. Also filed a bunch of [tickets][tix] detailing feature proposals, known bugs and weaknesses, etc. If you were looking for ways to contribute to the development effort, these new resources provide a bunch of ideas. * Made some efforts toward portability. While this project will always center around Raspbian and the PiDP-8/I add-on board, the intent is that you should be able to unpack the project on any other Unix type system and at least get the simulator up and running with the normal SimH manual control over execution instead of the nice front panel controls provided by the PiDP-8/I board. In particular, the software now builds under Mac OS X, though it does not yet run properly. (The modified SimH PDP-8 simulator currently misbehaves if the PiDP-8/I panel is not present. Fixing this is on the radar.) * Fixed a bunch of bugs! [readme]: https://tangentsoft.com/pidp8i/doc/trunk/README.md [dupatch]: https://groups.google.com/forum/#!topic/pidp-8/fmjt7AD1gIA [dudis]: https://tangentsoft.com/pidp8i/tktview?name=e06f8ae936 [wiki]: https://tangentsoft.com/pidp8i/wcontent [ex]: https://tangentsoft.com/pidp8i/doc/trunk/examples/README.md [art]: https://tangentsoft.com/pidp8i/dir?c=trunk&name=labels [tix]: https://tangentsoft.com/pidp8i/tickets ## Version 2015.12.15 * The official upstream release of the software, still current as of late 2016, at least. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 | Hacking on the PiDP-8/I Software ==== If you are going to make any changes to the PiDP-8/I software, here are some rules and hints to keep in mind while you work. Getting Started with Fossil ---- The PiDP-8/I software project is hosted using the [Fossil][fossil] [distributed version control system][dvcs]. Fossil provides most of the features of GitHub under a simpler operating model than Subversion without tying you to a proprietary web service. This guide will introduce you to some of the basics, but you should also at least read the [Fossil Quick Start Guide][fqsd]. For a more thorough introduction, I recommend [the Schimpf book][fbook]. If you have questions, it is best to ask them on [its low-volumn mailing list][fml], though you may also ask me, either on [the PiDP-8/I mailing list][ggml] or via private email. Most Raspberry Pi OS distributions include Fossil in their package repository, and it is also available for all common desktop platforms. If you started with one of the binary OS images downloaded from tangentsoft.com, Fossil is already installed. If you don't like any of those options, you can also use [the official binaries][fbin]. [fbin]: http://fossil-scm.org/index.html/uv/download.html [dvcs]: http://en.wikipedia.org/wiki/Distributed_revision_control [fbook]: http://www.fossil-scm.org/schimpf-book/home [fml]: http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/fossil-users [fossil]: http://fossil-scm.org/ [fqsg]: http://fossil-scm.org/index.html/doc/trunk/www/quickstart.wiki [ggml]: https://groups.google.com/forum/#!forum/pidp-8 Fossil Anonymous Access ---- To clone the code repository anonymously, say: $ mkdir ~/museum # because where else do you store fossils? $ fossil clone https://tangentsoft.com/pidp8i ~/museum/pidp8i.fossil $ mkdir -p ~/src/pidp8i/trunk $ cd ~/src/pidp8i/trunk $ fossil open ~/museum/pidp8i.fossil The second command gets you a file called `pidp8i.fossil` containing the full history of PiDP-8/I from the upstream 2015.12.15 release onward. You can call that clone file anything you like and put it in any directory you like. Even the `.fossil` extension is just a convention, not a requirement. (There is one feature of Fossil that requires that file extension, but you probably won't use that feature.) Working With Existing Tags and Branches ---- The directory structure shown in the commands above is more complicated than strictly necessary, but it has a number of nice properties. First, it collects other software projects under a common top-level directory, which I'm calling `~/src`, but you are free to use any scheme you like. Second, the top-level project directory stores multiple separate checkouts, one for each branch or tag I'm actively working with at the moment. So, to add a few other checkouts, you could say: $ cd ~/src/pidp8i $ mkdir -p release # another branch $ mkdir -p v20151215 # a tag this time, not a branch ...etc... $ cd release $ fossil open ~/museum/pidp8i.fossil release $ cd ../v20151215 $ fossil open ~/museum/pidp8i.fossil v20151215 ...etc... This gives you multiple independent checkouts. The branch checkouts remain pinned to the tip of that branch, so that if someone else checks changes in on that branch and you say `fossil update`, those changes appear in your checkout of that branch. The tag checkouts behave differently, always showing a specific checkout with the given tag name. (In Fossil, tags and branches are related, but the details are beyond our scope here. See the [Fossil Quick Start Guide][fqsg] and the documents it links to for more details.) This directory scheme shows an important difference between Fossil and Git: with Git, the checkout and the clone are intermingled in the same directory tree, but in Fossil, they are strictly separate. Git can emulate Fossil's normal working style through its [worktree][gitwt] feature, but it's a kind of lash-up using symlinks and such, whereas with Fossil, there is no confusion: the repository clone is a single SQLite database file — here, `pidp8i.fossil` — and the checkouts are made from the contents of that database. Another important difference relative to Git is that with Fossil, local checkins attempt to automatically sync checked-in changes back to the repository you cloned from. (This only works if you have a login on the remote repository, the subject of the next section.) This solves a number of problems with Git, all stemming from the fact that Git almost actively tries to make sure every clone differs from every other in some important way. While Fossil does allow offline operation and local independent clones, its default mode of operation is to try and keep the clones in sync as much as possible. Git works the way it does because it was designed to meet the needs of the Linux kernel development project, which is inherently federated, so Git tries to operate in a federated model as well. Fossil is better for smaller, more coherent teams, where there is a single, clear goal for the project and a single source for its official code. Fossil helps remote developers cooperate, whereas Git helps remote developers go off on their own tangents for extended periods of time and optionally sync back up with each other occasionally. Fossil is a better match for the way the PiDP-8/I software project works: we want you to cooperate closely with us, not go off on wild tangents. [gitwt]: https://git-scm.com/docs/git-worktree Fossil Developer Access ---- If you have a developer account on tangentsoft.com's Fossil instance, just add your username to the URL like so: $ fossil clone http://username@tangentsoft.com/pidp8i pidp8i.fossil Fossil will ask you for the password for `username` on the remote Fossil instance, and it will offer to remember it for you. If you let it remember the password, operation from then on is scarcely different from working with an anonymous clone, except that on checkin, your changes will be sync'd back to the repository on tangentsoft.com if you're online at the time. If you're working offline, Fossil will still do the checkin, but you'll be able to sync with the central repoisitory once you get back online. It is best to work on a branch when unable to use Fossil's autosync feature, as you are less likely to have a sync conflict when attempting to send a new branch to the central server than in attempting to merge your changes to the tip of trunk into the current upstream trunk, which may well have changed since you went offline. You can purposely work offline by disabling autosync mode: $ fossil set autosync 0 Until you re-enable it (`autosync 1`) Fossil will stop trying to sync your local changes back to the central repo. In this mode, Fossil works more like Git's default mode, buying you many of the same problems that go along with that working style. I recommend disabling autosync mode only when you are truly going to be offline, and don't want Fossil attempting to sync when you know it will fail. Getting Developer Access ---- The administrator of this repository is Warren Young, whose email you can find on the [official PiDP-8/I project mailing list][ggml]. Developer access is available to anyone who makes a reasonable request. Creating Branches ---- Creating a branch in Fossil is scary-simple, to the point that those coming from other version control systems may ask, "Is that really all there is to it?" Yes, really, this is it: $ fossil ci --branch new-branch-name That is to say, you make your changes as you normally would; then when you go to check them in, you give the `--branch` option to the `ci/checkin` command to put the changes on a new branch, rather than add them to the same branch the changes were made against. While developers with login rights to the PiDP-8/I Fossil instance are allowed to check in on the trunk at any time, we recommend using branches whenever you're working on something experimental, or where you can't make the necessary changes in a single coherent checkin. Basically, `trunk` should always build without error, and it should always function correctly. Branches are for isolating work until it is ready to merge into the trunk. Here again we have a difference with Git: because Fossil normally syncs your work back to the central repository, this means we get to see the branches you are still working on. This is a *good thing*. Do not fear committing broken or otherwise bad code to a branch. [You are not your code.][daff] We are software developers, too: we understand that software development is an iterative process, and that not all ideas spring forth perfect and production-ready from the fingers of its developers. These public branches let your collaborators see what you're up to, and maybe lend advice or a hand in the work, but mostly public branches let your collaborators see what you're up to, so they're not surprised when the change finally lands in trunk. This is part of what I mean about Fossil fostering close cooperation rather than fostering wild tangents. Jim McCarthy (author of [Dynamics of Software Development][dosd]) has a presentation on YouTube that touches on this topic at a couple of points: * [Don't go dark](https://www.youtube.com/watch?v=9OJ9hplU8XA) * [Beware of a guy in a room](https://www.youtube.com/watch?v=oY6BCHqEbyc) Fossil's sync-by-default behavior fights these negative tendencies. [daff]: http://www.hanselman.com/blog/YouAreNotYourCode.aspx [dosd]: http://amzn.to/2iEVoBL Debug Builds ---- By default, the build system creates a release build, but you can force it to produce a binary without as much optimization and with debug symbols included: $ ./configure --debug-mode Manipulating the Build System Source Files ---- The [autosetup build system][asbs] is composed of these files and directories: auto.def autosetup/ configure Makefile.in Unlike with GNU Autoconf, which you may be familiar with, the `configure` script is not output from some other tool. It is just a driver for the Tcl and C code under the `autosetup` directory. If you have to modify any of these files to get some needed effect, you should try to get that change into the upstream project, then merge that change down into the local copy when it lands upstream. The bulk of the customization to the build system is in `auto.def`, which is a Tcl script run by `autosetup` via the `configure` script. Some knowledge of [Tcl syntax][tcldoc] will therefore be helpful in modifying it. If you do not have Tcl installed on your system, `configure` builds a minimal Tcl interpreter called `jimsh0`, based on the [Jim Tcl][jim] project. Developers working on the build system are encoruaged to use this stripped-down version of Tcl rather than "real" Tcl because Jim Tcl is more or less a strict subset of Tcl, so any changes you make that work with the `jimsh0` interpreter should also work with "real" Tcl, but not vice versa. If you have Tcl installed and don't really need it, consider uninstalling it to force `autosetup` to build and use `jimsh0`. The `Makefile.in` file is largely a standard [GNU `make`][gmake] file excepting only that it has variables substituted into it by [`autosetup`][asbs] using its `@VARIABLE@` syntax. At this time, we do not attempt to achieve compatibility with other `make` programs, though in the future we may need it to work with [BSD `make`][bmake] as well, so if you are adding features, you might want to stick to the common subset of features implemented by both the GNU and BSD flavors of `make`. We do not anticpate any need to support any other `make` flavors. (This, by the way, is why we're not using some heavy-weight build system such as the GNU Autotools, CMake, etc. The primary advantage of GNU Autotools is that you can generate source packages that will configure and build on weird and ancient flavors of Unix; we don't need that. Cross-platform build systems such as CMake ease building the same software on multiple disparate platforms straightforward, but the PiDP-8/I software is built primarily on and for a single operating system, Rasbpian Linux. It also happens to build and run on other modern Unix and Linux systems, for which we also do not need the full power of something like CMake. `autosetup` and GNU `make` suffice for our purposes here.) [asbs]: http://msteveb.github.io/autosetup/ [bmake]: https://www.freebsd.org/doc/en/books/developers-handbook/tools-make.html [gmake]: https://www.gnu.org/software/make/ [jim]: http://jim.tcl.tk/ [tcldoc]: http://wiki.tcl.tk/11485 Submitting Patches ---- If you do not have a developer login on the PiDP-8/I software repository, you can still send changes to the project. The simplest way is to say this after developing your change against the trunk of PiDP-8/I: $ fossil diff > my-changes.patch Then attach that file to a new [PiDP-8/I mailing list][ggml] message along with a declaration of the license you wish to contribute your changes under. We suggest using the [SIMH license][simhl], but any [non-viral][viral] [OSI-approved license][osil] should suffice. If your change is more than a small patch, `fossil diff` might not incorporate all of the changes you have made. The old unified `diff` format can't encode branch names, file renamings, file deletions, tags, checkin comments, and other Fossil-specific information. For such changes, it is better to send a Fossil bundle: $ fossil set autosync 0 # disable autosync $ fossil checkin --branch my-changes ...followed by more checkins on that branch... $ fossil bundle export --branch my-changes my-changes.bundle After that first `fossil checkin --branch ...` command, any subsequent changes will also be made on that branch without needing a `--branch` option until you explicitly switch to some other branch. This lets you build up a larger change on a private branch until you're ready to submit the whole thing as a bundle. Because you are working on a branch on your private copy of the PiDP-8/I Fossil repository, you are free to make as many checkins as you like on the new branch before giving the `bundle export` command. Once you are done with the bundle, send it to the mailing list just as with the patch. If you provide a quality patch, we are likely to offer you a developer login on [the repository][repo] so you don't have to continue with the patch or bundle methods. Please make your patches or experimental branch bundles against the tip of the current trunk. PiDP-8/I often drifts enough during development that a patch against a stable release may not apply to the trunk cleanly otherwise. [osil]: https://opensource.org/licenses [repo]: http://tangentsoft.com/pidp8i/ [simhl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md [viral]: https://en.wikipedia.org/wiki/Viral_license The PiDP-8/I Software Project Code Style Rules ---- Every code base should have a common code style. Love it or hate it, here are PiDP-8/I's current code style rules: **C Source Code** File types: `c`, `h`, `c.in` We follow the SIMH project's pre-existing code style when modifying one of its source files: * Spaces for indents, size 4 * DOS line endings. (Yes, even though this is a Linux-based project! All decent Linux text editors can cope with this.) * Function, structure, type, and variable names are all lowercase, with underscores separating words * Macro names are in `ALL_UPPERCASE_WITH_UNDERSCORES` * Whitespace in the SIMH C files is of a style I have never seen anywhere else in my decades of software development. This example shows the important features: int some_function (char some_parameter) { int some_variable = 0; if (some_parameter != '\0') { int nbytes = sizeof (some_parameter); char *buffer = malloc (4 * nbytes); switch (some_parameter) { case 'a': do_something_with_buffer ((char *)buffer); default: do_something_else (); } } else { some_other_function (with_a_large, "number of parameters", wraps_with_a_single, "indent level"); printf (stderr, "Failed to allocate buffer.\n"); } } It is vaguely like K&R C style except that: - The top level of statements in a function are not indented - The closing curly brace is indented to the same level as the statement(s) it contains - There is a space before all opening parentheses, not just those used in `if`, `while`, and similar flow control statements. Nested open parentheses do not have extra spaces, however. Only the outer opening parenthesis has a space separating it from what went before. - Multiple variables declared together don't have their types and variable names aligned in columns. I find that this style is mostly sensible, but with two serious problems: I find the indented closing curly braces confusing, and I find that the loss of the first indent level for the statements inside a function makes functions all visually run together in a screenful of code. Therefore, when we have the luxury to be working on a file separate from SIMH, we use a variant of its style with these two changes, which you can produce with this command: $ indent -kr -nce -cli4 -nlp -pcs -di1 -i4 -l100 \ -ncs -ss -nbbo FILES... That is, start with K&R, then: - nce: don't cuddle else - cli4: indent case statement labels 4 spaces - nlp: don't align continued statements at the opening parenthesis - pcs: put a space before the opening parenthesis of a function call - di1: don't line up variable types and names in separate columns - i4: use 4-space indents - l100: allow lines up to 100 columns before forcibly breaking them - ncs: don't put a space between a cast and its operand - ss: add a space before semicolon with empty loop body - nbbo: don't break long lines before || and && operators That gives the following, when applied to the above example: int some_function (char some_parameter) { int some_variable = 0; if (some_parameter != '\0') { int nbytes = sizeof (some_parameter); char *buffer = malloc (4 * nbytes); switch (some_parameter) { case 'a': do_something_with_buffer ((char *)buffer); default: do_something_else (); } } else { some_other_function (with_a_large, "number of parameters", wraps_with_a_single, "indent level"); printf (stderr, "Failed to allocate buffer.\n"); } } If that looks greatly different, realize that it is just two indenting level differences: add one indent at function level, except for the closing braces, which we leave at their previous position. SIMH occasionally exceeds 100-column lines. I recommend breaking long lines at 72 columns. Call me an 80-column traditionalist. BSD `indent` does't understand the `-kr` option, so you can use this alternative on BSD and macOS systems: $ indent -nce -cli4 -nlp -pcs -di1 -i4 -l100 \ -bap -ncdb -nfc1 -npsl -nsc FILES... When in doubt, mimic what you see in the current code. When still in doubt, ask on the mailing list. [indent]: http://linux.die.net/man/1/indent **Plain Text Files** File types: `md`, `txt` * Spaces for indents, size 4. * Unix line endings. The only common text editor I'm aware of that has a problem with this is Notepad, and people looking at these files anywhere other than unpacked on a Raspberry Pi box are probably looking at them through the Fossil web interface on tangentsoft.com. * Markdown files must follow the syntax flavor understood by [Fossil's Markdown interpreter][fmd]. [fmd]: https://tangentsoft.com/pidp8i/md_rules |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | ######################################################################## # Makefile.in - Processed by autosetup's configure script to generate # the GNU make(1) file for building the PiDP-8/I software. # # If you are seeing this at the top of a file called Makefile and you # intend to make edits, do that in Makefile.in. Saying "make" will then # re-build Makefile from that modified Makefile.in before proceeding to # do the "make" operation. # # Copyright © 2015-2017 Oscar Vermeulen, Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## # Git commit ID of the latest version of the SIMH 4 project on GitHub # that has been merged into this source base. SGCID=0046905f72e2372a8bf5dfc90425fd5214b0ba8a CFLAGS = @CFLAGS@ -Wno-unused-result -Wno-parentheses @BUILDMODE@ \ -DUSE_READER_THREAD -DHAVE_DLOPEN=so -DPIDP8I -DSIM_ASYNCH_IO \ -DHAVE_REGEX_H -DHAVE_GLOB -DSIM_GIT_COMMIT_ID=$(SGCID) \ -U__STRICT_ANSI__ \ -I @srcdir@/src -I @srcdir@/src/PDP8 -I @builddir@/src SIM = bin/pidp8i-sim BINS = bin/palbart $(SIM) bin/pidp8i-test libexec/scanswitch BUILDDIRS = bin libexec obj/PDP8 INSTDIRS = bin etc libexec share/boot share/media share/man/man1 OBJS = \ obj/gpio-common.o \ obj/sim_console.o \ obj/PDP8/pdp8_df.o \ obj/PDP8/pdp8_cpu.o \ obj/PDP8/pdp8_clk.o \ obj/PDP8/pdp8_ct.o \ obj/PDP8/pdp8_dt.o \ obj/PDP8/pdp8_fpp.o \ obj/PDP8/pdp8_lp.o \ obj/PDP8/pdp8_mt.o \ obj/PDP8/pdp8_pt.o \ obj/PDP8/pdp8_rf.o \ obj/PDP8/pdp8_rk.o \ obj/PDP8/pdp8_rl.o \ obj/PDP8/pdp8_rx.o \ obj/PDP8/pdp8_sys.o \ obj/PDP8/pdp8_td.o \ obj/PDP8/pdp8_tsc.o \ obj/PDP8/pdp8_tt.o \ obj/PDP8/pdp8_ttx.o \ obj/PDP8/pidp8i.o \ obj/scp.o \ obj/sim_disk.o \ obj/sim_ether.o \ obj/sim_fio.o \ obj/sim_serial.o \ obj/sim_sock.o \ obj/sim_tape.o \ obj/sim_timer.o \ obj/sim_tmxr.o LIBS = -lm -ldl -lpthread EXAMPLES := $(wildcard @srcdir@/examples/*.pal) EXAMPLES := $(subst @srcdir@/examples,bin,$(EXAMPLES)) EXAMPLES := $(EXAMPLES:.pal=.pt) LISTINGS := $(EXAMPLES:.pt=.lst) LISTINGS := $(subst bin/,obj/,$(LISTINGS)) BOOTSCRIPTS := $(LISTINGS:.lst=.script) BOOTSCRIPTS := $(subst obj/,boot/,$(BOOTSCRIPTS)) \ boot/1.script \ boot/5.script # List of *.in files from auto.def file, except for this present file # (Makefile.in) which is handled separately. This list should only # change when the list of "make-template" calls in auto.def changes. # # If the first file listed below changes, change the AUTOREBUILD rule # near the end of this file to match! INFILES = \ @srcdir@/bin/pidp8i.in \ @srcdir@/boot/0.script.in \ @srcdir@/boot/2.script.in \ @srcdir@/boot/3.script.in \ @srcdir@/boot/4.script.in \ @srcdir@/boot/6.script.in \ @srcdir@/boot/7.script.in \ @srcdir@/etc/pidp8i-init.in \ @srcdir@/etc/sudoers.in \ @srcdir@/examples/Makefile.in \ @srcdir@/src/Makefile.in \ @srcdir@/src/gpio-common.c.in \ @srcdir@/src/PDP8/Makefile.in \ @srcdir@/src/PDP8/pidp8i.c.in \ @srcdir@/src/scp.c.in \ @srcdir@/tools/simh-update.in OUTFILES := $(subst @srcdir@/,,$(INFILES)) OUTFILES := $(subst .in,,$(OUTFILES)) CLTXT = /boot/cmdline.txt .PHONY: tags all: $(OUTFILES) $(BUILDDIRS) $(BINS) $(BOOTSCRIPTS) $(LISTINGS) $(EXAMPLES) @chmod 755 @builddir@/bin/pidp8i clean: @rm -f $(BINS) $(BOOTSCRIPTS) $(EXAMPLES) $(LISTINGS) $(OBJS) \ @builddir@/tags \ @builddir@/obj/*.d \ @builddir@/obj/*.o \ @builddir@/obj/PDP8/*.d \ @srcdir@/examples/*.err @for f in $(OUTFILES) ; do test "$$f" = "$${f/Makefile//}" && rm $$f ; done @-rmdir -p $(BUILDDIRS) 2> /dev/null || true distclean: clean @rm -f \ @builddir@/config.log \ @builddir@/Makefile \ @builddir@/autosetup/jimsh0 \ @builddir@/examples/Makefile \ @builddir@/src/Makefile \ @builddir@/src/config.h \ @builddir@/src/PDP8/Makefile ctags tags: ctags -R @srcdir@ install: all @echo Installing to @prefix@... @# Create any missing install tree directories for d in $(INSTDIRS) ; do @INSTALL@ -m 755 -d @prefix@/$$d ; done @# Install files into those dirs and set their perms for f in $(BINS) ; do @INSTALL@ -m 755 -D -s $$f @prefix@/$$f ; done @INSTALL@ -m 755 @srcdir@/bin/pidp8i @prefix@/bin -for f in @prefix@/bin/pidp8i-* ; do setcap 'cap_sys_nice=eip' $$f ; done || true test -e @MEDIADIR@/os8/os8.rk05 || $(MAKE) mediainstall @# If this is a Debian-type system, install needed helper programs @test -x /usr/bin/apt-get -a ! -h /media/usb && apt-get -y install usbmount || true @test -x /usr/bin/apt-get -a ! -x /usr/bin/screen && apt-get -y install screen || true @# Disable competing services if this is a Raspberry Pi @(test -x /bin/systemctl && /bin/systemctl disable deeper || true) @(test -x /bin/systemctl && /bin/systemctl disable pidp8 || true) @# Install the init script if this system is systemd based. @INSTALL@ -m 750 @srcdir@/etc/pidp8i-init @prefix@/etc @( test -w /etc/init.d -a -x /bin/systemctl && \ ln -sf @ABSPREFIX@/etc/pidp8i-init /etc/init.d/pidp8i && \ /bin/systemctl enable pidp8i \ ) || true @# Give the install user permission to use GPIO if done on a Pi @grep -q '^gpio:' /etc/group && usermod -a -G gpio @INSTUSR@ || true @# Give the install user permission to shut down and reboot the Pi @# if this is a systemd/sudo based system. @( test -d /etc/sudoers.d -a -w /etc/sudoers.d -a -x /bin/systemctl && \ @INSTALL@ -m 440 -o root -g root @srcdir@/etc/sudoers \ /etc/sudoers.d/099_pidp8i \ ) || true @# Add installation bin dir to the non-root user's PATH unless it's @# already in there or we aren't running under sudo. @(for p in .profile .bash_profile ; do \ test -n "$$SUDO_USER" -a -w "/home/$$SUDO_USER/$$p" && \ ! grep -qF "@ABSPREFIX@/bin" "/home/$$SUDO_USER/$$p" && \ echo "export PATH=\$$PATH:@ABSPREFIX@/bin" >> "/home/$$SUDO_USER/$$p" ; \ done) || true @# Ditto for MANPATH @(for p in .profile .bash_profile ; do \ test -n "$$SUDO_USER" -a -w "/home/$$SUDO_USER/$$p" && \ ! grep -qF "@ABSPREFIX@/share/man" "/home/$$SUDO_USER/$$p" && \ echo "export MANPATH=\$$MANPATH:@ABSPREFIX@/share/man" >> "/home/$$SUDO_USER/$$p" ; \ done) || true @# If serial mod is disabled, turn off serial console and kgdb stuff @# in case they were enabled previously, else they will fight with @# our use of GPIO. @( test -z "@PCB_SERIAL_MOD@" -a -r $(CLTXT) && ! -w $(CLTXT) && \ cp -p $(CLTXT) "$(CLTXT)"_orig && \ sed -e 's/console\=[a-zA-Z0-9]+,[0-9]+ //' \ -e 's/kgdboc\=[a-zA-Z0-9]+,[0-9]+ //' -i $(CLTXT) \ ) || true @# Install palbart stuff @INSTALL@ -m 755 @builddir@/bin/palbart @prefix@/bin @INSTALL@ -m 644 @srcdir@/palbart/palbart.1 @prefix@/share/man/man1 mediainstall: @echo "[Re]installing OS and program media..." @cd @srcdir@ ; \ find media \( \ -name \*.bin -o \ -name \*.dsk -o \ -name \*.rk05 -o \ -name \*.tu56 \ \) -exec @INSTALL@ -D -m 664 -g @INSTGRP@ {} @ABSPREFIX@/share/{} \; @INSTALL@ -m 664 -g @INSTGRP@ @builddir@/boot/*.script @BOOTDIR@ os8test: @builddir@/$(SIM) @srcdir@/boot/0.script reconfig: @AUTOREMAKE@ release: all @srcdir@/tools/mkrel simh-update simh-update-f: @@builddir@/tools/simh-update $(subst simh-update,,$@) # Rule for compiling *.c to *.o and autogenerating dependency info. # Explained at http://scottmcpeak.com/autodepend/autodepend.html # # Reflect any changes here into near-duplicate below! obj/%.o: @srcdir@/src/%.c $(CC) -c $(CFLAGS) @srcdir@/src/$*.c -o obj/$*.o $(CC) -MM $(CFLAGS) @srcdir@/src/$*.c > obj/$*.d @mv -f obj/$*.d obj/$*.d.tmp @sed -e 's|.*:|obj/$*.o:|' < obj/$*.d.tmp > obj/$*.d @sed -e 's/.*://' -e 's/\\$$//' < obj/$*.d.tmp | fmt -1 | \ sed -e 's/^ *//' -e 's/$$/:/' >> obj/$*.d @rm -f obj/$*.d.tmp # Near-duplicate of above rule for those *.c files generated from *.c.in # files in the srcdir. Needed when building out-of-tree. obj/%.o: @builddir@/src/%.c $(CC) -c $(CFLAGS) @builddir@/src/$*.c -o obj/$*.o $(CC) -MM $(CFLAGS) @builddir@/src/$*.c > obj/$*.d @mv -f obj/$*.d obj/$*.d.tmp @sed -e 's|.*:|obj/$*.o:|' < obj/$*.d.tmp > obj/$*.d @sed -e 's/.*://' -e 's/\\$$//' < obj/$*.d.tmp | fmt -1 | \ sed -e 's/^ *//' -e 's/$$/:/' >> obj/$*.d @rm -f obj/$*.d.tmp # Rule for building example PAL assembly language programs. obj/%.lst bin/%.pt: @srcdir@/examples/%.pal bin/palbart @builddir@/bin/palbart -lr $< || cat @srcdir@/examples/$*.err mv @srcdir@/examples/$*.lst @builddir@/obj/$*.lst mv @srcdir@/examples/$*.rim @builddir@/bin/$*.pt # Rule for translating PAL assembly language program listings to SIMH # boot scripts. boot/%.script: obj/%.lst @srcdir@/tools/mkbootscript $< # Rules for making aliases of named example programs translated to boot # scripts as special numbered boot scripts boot/1.script: boot/hs-rim-loader.script ln -f $< $@ boot/5.script: boot/ac-mq-blinker.script ln -f $< $@ bin/palbart: @srcdir@/palbart/palbart.c $(CC) -Wall -Wno-strict-prototypes @BUILDMODE@ $< -o $@ $(CC) -MM $< -o obj/$*.d $(BUILDDIRS): mkdir -p $@ $(SIM): $(OBJS) obj/gpio-@LED_DRIVER_MODULE@ls.o $(CC) -o $@ $^ $(LIBS) bin/pidp8i-test: obj/test.o obj/gpio-nls.o obj/gpio-common.o $(CC) -o $@ $^ $(LIBS) libexec/scanswitch: obj/scanswitch.o obj/gpio-nls.o obj/gpio-common.o $(CC) -o $@ $^ ifeq ($(findstring clean,$(MAKECMDGOALS)),) Makefile: @srcdir@/Makefile.in @srcdir@/auto.def $(INFILES) @AUTODEPS@ @AUTOREMAKE@ && $(MAKE) # If you simply make $(OUTFILES) depend on $(INFILES), make(1) thinks it # can build all of $(OUTFILES) in parallel if given -jN, which causes # ./configure --args to run N times in parallel, which blows shit up # right good and proper. We purposely list only the first file in # $(INFILES) here to prevent that. The other files in $(INFILES) will # also be built, which confuses make(1) somewhat, but it figures things # out on the subsequent $(MAKE) call. bin/pidp8i: @srcdir@/bin/pidp8i.in @AUTOREMAKE@ && $(MAKE) endif -include $(OBJS:.o=.d) obj/scanswitch.d |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | # PCB Test Program ## Compiling and Installing `pidp8i-test` is a simple program to test [Oscar Vermeulen's PiDP-8/I Kit][project] during construction. It is built and installed alongside the other software with the normal `make` process. ## Running It If you are building the software on the Pi for the first time, log out of your user account after installing it, then back in so that the install script's changes to your user's `PATH` take effect. Thereafter, simply give these commands: $ sudo systemctl stop pidp8i $ pidp8i-test The first command ensures that the modified PDP-8 simulator is stopped during the test, since only one program can be talking to the switch and LED array at a given time. (This also applies to other programs like [Deeper Thought 2][dt2].) ## Test Procedure You can at any time hit Ctrl-C to stop the test. The test proceeds as follows: * All On test: It turns on all LEDs for 5 seconds. * All Off test: It turns off all LEDs for 5 seconds. * Row test: It turns on one full row of LEDs and pauses for 5 seconds, then switches to the next row. There are eight rows of LEDs of up to 12 LEDs each. * Column test: It then turns on one full column of LEDs and pauses for 5 seconds, then switches to the next column. There are 12 columns of LEDs with up to 8 LEDs each. (Some of the LEDs positions in a column are sometimes rather chaotic, it will require intimate knowledge of the schematic to verify. It's somewhat of a useless test but it might turn up an assembly error for someone.) * Switch test: It then goes into a single LED chase pattern and starts looking at switches. This loop is infinite. Every time it detects a change in the switch positions it prints out the full Octal bit pattern for the three switch banks. No attempt is made to name the actual switch that has been flipped. The goal is to verify switch functionality, not to debug the design of the circuit or the driver. When running this test, if you get a new line printed with a single bit change when you flip a single switch, the switch in question is working. If you get no output printed or multiple bits changed in the output printed something is wrong. If for some reason you need to decode the output bits to physical switches they appear as follows: | A | B | C |------------------- | 4000 | 0000 | 0000 The first twelve bits (labelled A) is the Switch Register. The bits left to right correspond to the SR switches also left to right. So above the SR1 switch is toggled down, ie 1. Every other SR switch is up, ie 0. The leftmost 6 bits (labelled B) are the 3 DF switches followed by the 3 IF switches. Again left to right. The rest of the bits are unused in the B section. The leftmost 8 bits (labelled C) are the remaining 8 switches starting at "START" and ending at "SING INST". Again Left to right. ## License This document is licensed under the same terms as the associated [`src/test.c` program][program]. [project]: http://obsolescence.wix.com/obsolescence#!pidp-8 [dt2]: https://github.com/VentureKing/Deeper-Thought-2 [program]: https://tangentsoft.com/pidp8i/doc/trunk/src/test.c |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | # Throttling the Simulator When you do not give the `--throttle` option to the `configure` script, the simulator's speed is set based on the number of CPU cores detected by the `tools/corecount` script. ## Multi-Core Default If `corecount` detects a multi-core system, the default behavior is to not throttle the simulator at all, since there are only 2 threads in the software that take substantial amounts of CPU power. The most hungry thread is the PDP-8 simulator proper, which runs flat-out, taking an entire core's available power by default. The other hungry thread is the one that drives the front panel LEDs, which takes about 15% of a single core's power on a Raspberry Pi 3 when you build the software with the incandescent lamp simulator enabled. This leaves over 2 cores worth of CPU power untapped on multi-core Raspberry Pis, so the system performance remains snappy even with the simulator running. You can force this behavior with `--throttle=none`. ## Single-Core Default If the `configure` script decides that you're building this on a single-core system, it purposely throttles the PDP-8 simulator so that it takes about 75% of a single core's worth of power on the slowest Raspberry Pi supported by this software. This leaves enough CPU power for some background tasks on a single-core Pi. This default assumes you are building without the incandescent lamp simulator feature enabled, as that currently takes so much CPU power to run that the simulator runs slower than even a PDP-8/S! (We're working on ways to improve the speed of that lamp simulator to let it run on single-core raspberry Pis.) Indeed, the build system will actively try to prevent you from building the incandescent lamp simulator feature on a single-core Pi. You can force the build system to select this throttle value even on a multi-core Pi with `--throttle=single-core`. You will erroneously get this single-core behavior if you run the `configure` script on a system where `tools/corecount` has no built-in way to count the CPU cores in your system correctly, so it returns 1, forcing a single-core build. That script currently only returns the correct value on Linux, BSD, and macOS systems. To fix it, you can either say `--throttle=none` or you can patch `tools/corecount` to properly report the number of cores on your system. If you choose the latter path, please send the patch to the mailing list so it can be integrated into the next release of the software. ## Underclocking If you want the software to run even slower, there are additional `configure --throttle` option values available to achieve that: * `--throttle=STRING`: any value not otherwise understood is passed directly to SIMH in `SET THROTTLE` commands inserted into the generated `boot/*.script` files. You can use any string here that SIMH itself supports; RTFM. * `--throttle=CPUTYPE`: if you give a value referencing one of the many PDP-8 family members, it selects a value based on the execution time of `TAD` in direct access mode on that processor: | Value | Alias For | Memory Cycle Time --------------------------------------------------- | `pdp8e` | 416k | 1.2 µs | `pdp8i`, `pdp8a` | 333k | 1.5 µs | `pdp8l`, `pdp8` | 313k | 1.6 µs | `ha6120` | 182k | 2.7 µs | `im6100a` | 200k | 2.5 µs | `im6100` | 100k | 5 µs | `im6100c` | 83k | 6 µs | `pdp8s` | 63k | 8 µs I chose `TAD` because it's a typical instruction for the processor, and its execution speed is based on the memory cycle time for the processor, an easy specification to find. Other instructions (e.g. most OPR instructions) execute faster than this, while others (e.g. IOT) execute far slower. (See the processor's manual for details.) SIMH, on the other hand, does not discriminate. When you say `--throttle=pdp8i`, causing the build system to insert `SET THROTTLE 333k` commands into the SIMH boot scripts, the SIMH PDP-8 simulator does its best to execute exactly 333,000 instructions per second, regardless of the instruction type. Consequently, if you were to benchmark this simulator configured with one of the options above, there would doubtless be some difference in execution speed, depending on the mix of instructions executed. (See the I/O Matters section below for a further complication.) The values for the Intersil and Harris CMOS microprocessors are for the fastest clock speed supported for that particular chip. Use the `STRING` form of this option if you wish to emulate an underclocked microprocessor. * `--throttle=human`: Causes the computer to throttle the human. "I'm sorry, Dave, but you are not worthy to run this software." "Aaackkthhhpptt..." No, wait, that can't be right. Let's see here...ah, yes, what it *actually* does is slows the processor down to 10 instructions per second, about the fastest that allows the human eye to easily discern LED state changes as separate. If you increase it very much above this, the eye starts seeing the LED state changes as a blur. This mode is useful for running otherwise-useful software as a "blinkenlights" demo. * `--throttle=trace`: Alias for `--throttle=1`, causing the simulator to act more or less like it's in single-instruction mode and you're pressing the `CONT` button once a second to step through a program. ## I/O Matters The throttle mechanism discussed above only affects the speed of the PDP-8 CPU simulator. It does not affect the speed of I/O operations. The only I/O channel you can throttle in the same way is a serial terminal by purposely choosing a slower bit rate for it than the maximum value. If you set it to 110 bps, it runs at the speed of a Teletype Model 33 ASR, the most common terminal type used for the PDP-8/I, and most other early PDP-8 flavors. Later PDP-8s were often paired with (or integrated into!) glass TTYs such as the VT05, which flew along at 2400 bps. Then things got really fancy with the VT52, which screamed along at 9600 bps. Wowee! I'm not aware of a way to make SIMH slow the other I/O operations, such as disk access speeds, in order to emulate the speed of the actual hardware. ## License Copyright © 2017 by Warren Young. This document is licensed under the terms of [the SIMH license][sl]. [sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | # Getting Started with the PiDP-8/I Software ## Prerequisites * A Raspberry Pi with the 40-pin GPIO connector. That rules out the first-generation Raspberry Pi model A and B boards which had a 26-pin GPIO connector. * An SD card containing Raspbian or something sufficiently close. This software is currently tested with the Jessie Lite distribution. Ideally, you will install a fresh OS image onto an unused SD card rather than use this software to modify an existing OS installation, but there is currently no known hard incompatibilty that prevents you from integrating this software into an existing OS. * This software distribution, unpacked somewhere convenient within the Raspberry Pi filesystem. Unlike with the [upstream 2015.12.15 release][upst], this present release of the software should *not* be unpacked into `/opt/pidp8`. I recommend that you unpack it into `$HOME/src`, `/usr/local/src` or similar, but it really doesn't matter where you put it, as long as your user has full write access to that directory. * A working C compiler and other standard Linux build tools, such as `make(1)`. On Debian type systems — including Raspbian — you can install such tools with `sudo apt install build-essential` ## Configuring, Building and Installing This software distribution builds and installs in the same way as most other Linux/Unix software these days. The short-and-sweet is: $ ./configure && make && sudo make install ### Configure Script Options You can change a few things about the way the software is built and installed by giving options to the `configure` script: #### --prefix Perhaps the most widely useful `configure` script option is `--prefix`, which lets you override the default installation directory, `/opt/pidp8i`. You could make it install the software under your home directory on the Pi with this command: $ ./configure --prefix=$HOME/pidp8i && sudo make install Although this is installing to a directory your user has write access to, you still need to install via `sudo` because the installation process does other things that do require `root` access. #### --no-lamp-simulator If you build the software on a multi-core host, the PDP-8/I simulator is normally built with Ian Schofield's incandescent lamp simulator feature, which drives the LEDs in a way that mimics the incandescent lamps used in the original PDP-8/I. This feature currently takes too much CPU power to run on anything but a multi-core Raspberry Pi, currently limited to the Pi 2 and Pi 3 series. If you configure the software on a single-core Pi — models A+, B+, and Zero — the simulator uses the original low-CPU-usage LED driving method instead. Those on a multi-core host who want this low-CPU-usage LED driving method can give the `--no-lamp-simulator` option to `configure`. #### --serial-mod If you have done the [serial mod][smod] to your PiDP-8/I PCB and the Raspberry Pi you have connected to it, add `--serial-mod` to the `configure` command above. If you do not give this flag at `configure` time with these hardware modifications in place, the front panel will not work correctly, and trying to run the software may even crash the Pi. If you give this flag and your PCBs are *not* modified, most of the hardware will work correctly, but several lights and switches will not work correctly. #### --throttle See [`README-throttle.md`][thro] for the values this option takes. If you don't give this option, the simulator runs as fast as possible, more or less. #### --help Run `./configure --help` for more information on your options here. ### Installing The `sudo make install` step in the command above does what most people want. That step will not overwrite the operating system and program media (e.g. the OS/8 RK05 disk cartridge image) when installing multiple times to the same location, but you can demand an overwrite with: $ sudo make mediainstall This can be helpful if you have damaged your OS/program media or simply want to return to the pristine versions as distributed. This will also overwrite the boot scripts in `$prefix/share/boot` with fresh versions from the source distribution. ## Testing You can test your PiDP-8/I LED and switch functions with these commands: $ sudo systemctl stop pidp8i $ pidp8i-test You may have to log out and back in before the second command will work, since the installation script modifies your normal user's `PATH` the first time you install onto a given system. It is important to stop the PiDP-8/I simulator before running the test program, since both programs need exclusive access to the LEDs and switches on the front panel. After you are done testing, you can start the PiDP-8/I simulator back up with: $ sudo systemctl start pidp8i See [`README-test.md`][test] for more details. ## Using the Software For the most part, this software distribution works like the upstream [2015.12.15 distribution][usd]. Its [documentation][prj] therefore describes this software too, for the most part. The largest user-visible difference between the two software distributions is that all of the shell commands affecting the software were renamed to include `pidp8i` in their name: 1. To start the simulator: $ sudo systemctl start pidp8i This will happen automatically on reboot unless you disable the service, such as in order to run one of the various [forks of Deeper Thought][dt2]. 2. To attach the terminal you're working on to the simulator: $ pidp8i 3. To detach from the simulator's terminal interface while leaving the PiDP-8/I simulator running, type <kbd>Ctrl-A d</kbd>. You can re-attach to it later with a `pidp8i` command. 4. To shut the simulator down while attached to its terminal interface, type <kbd>Ctrl-E</kbd> to pause the simulator, then at the `simh>` prompt type `quit`. Type `help` at that prompt to get some idea of what else you can do with the simulator command language, or read the [SIMH Users' Guide][sdoc]. 5. To shut the simulator down from the Raspbian command line: $ sudo systemctl stop pidp8i There are [other major differences][mdif] between the upstream distribution and this one. See that linked wiki article for details. ## License Copyright © 2016-2017 by Warren Young. This document is licensed under the terms of [the SIMH license][sl]. [prj]: https://tangentsoft.com/pidp8i/ [upst]: http://obsolescence.wixsite.com/obsolescence/pidp-8 [smod]: http://obsolescence.wixsite.com/obsolescence/2016-pidp-8-building-instructions [usd]: http://obsolescence.wixsite.com/obsolescence/pidp-8-details [dt2]: https://github.com/VentureKing/Deeper-Thought-2 [sdoc]: http://simh.trailing-edge.com/pdf/simh_doc.pdf [prj]: http://obsolescence.wixsite.com/obsolescence/pidp-8 [test]: https://tangentsoft.com/pidp8i/doc/trunk/README-test.md [thro]: https://tangentsoft.com/pidp8i/doc/trunk/README-throttle.md [mdif]: https://tangentsoft.com/pidp8i/wiki?name=Major+Differences [sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | # PiDP-8/I Software Release Process If you are just a user of this software, you need read no further. This document is for those producing release versions of the software, or for those curious about what goes into doing so. ## Update ChangeLog.md Trawl the Fossil timeline for user-visible changes since the last release, and write them up in user-focused form into the `ChangeLog.md` file. If a regular user of the software cannot see a given change, it shouldn't go in the `ChangeLog.md`; let it be documented via the timeline only. ## Update the Release Branch Run `make release` to check the `ChangeLog.md` file changes in, tagging that checkin with a release version tag of the form vYYYYMMDD and merge those changes into the `release` branch. It runs entirely automatically unless an error occurs, in which case it stops immediately, so check its output for errors before continuing. ## Update the Home Page Links The zip and tarball links on the front page produce files named after the date of the release. Those dates need to be updated immediately after tagging the release, since they point at the "release" tag applied by the previous step, so they begin shipping the new release immediately after tagging it. ## Produce the Normal Binary OS Image Start with the latest [Raspbian Lite OS image][os]. 1. If the version of the base OS has changed since the last binary OS image was created, download the new one and blast it onto an SD card used for no other purpose. Boot it up. 2. After logging in, retreive and initialize the BOSI process: $ wget https://tangentsoft.com/bosi $ chmod +x bosi $ ./bosi init It will either reboot the system after completing its tasks successfully or exit early, giving the reason it failed. 3. Test that the software starts up as it should. 4. Reset the OS configuration: $ exec sudo ./bosi reset The `exec` bit is required so that the `bosi` invocation is run as root without any processes running as `pi` in case the `init` step sees that user `pi` hasn't been changed to `pidp8i` here: the `usermod` command we give to make that change will refuse to do what we ask if there are any running processes owned by user `pi`. 5. Move the SD card to a USB reader plugged into the Pi, boot the Pi from its prior SD card, and shrink the OS image: $ wget https://tangentsoft.com/bosi $ chmod +x bosi $ ./bosi shrink 6. Move the USB reader to the Mac,¹ then say: $ bosi image[-nls] BLOCKS `BLOCKS` is the value output at the end of the `shrink` step. 7. The prior step rewrote the SD card with the image file it created. Boot it up and make sure it still works. If you're happy with it: $ bosi finish[-nls] 8. While the OS image uploads, compose the announcement message, and modify the front page to point to the new images. Post the announcement message and new front page once the uploads complete. [os]: https://www.raspberrypi.org/downloads/raspbian/ ## Produce the "No Lamp Simulator" Binary OS Image Log into the SD card from which you made the regular image above, then say `./bosi init --no-lamp-simulator`, and continue from step 3 above. When you get down to the `image` and `test` steps, give `image-nls` and `test-nls` instead. ---------------------- ### Footnotes 1. The image production steps could just as well be done on a Linux box or on a Windows box via Cygwin or WSL, but the commands in that final stage change due to OS differences. Since this document exists primarily for use by the one who uses it, there is little point in having alternatives for other desktop OSes above. Should someone else take over maintainership, they can translate the above commands for their own desktop PC. ### License Copyright © 2016-2017 by Warren Young. This document is licensed under the terms of [the SIMH license][sl]. [sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md |
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | Copyright © 2015-2017 by various authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of the authors above shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from those authors. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | ######################################################################## # auto.def - Configure file for the PiDP-8/I software build system, # based on autosetup. # # Copyright © 2016-2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## define defaultprefix /opt/pidp8i use cc use cc-lib options { debug-mode => "create a debug build (default is release)" no-lamp-simulator => "use simple LED driver instead of incandescent lamp simulator" serial-mod => "build simulator to expect the PCB serial mods" throttle: => "override the throttle values in the boot scripts" } if {[opt-bool serial-mod]} { msg-result "The simulator will expect the serial mods to the Pi and PiDP-8/I PCBs." define PCB_SERIAL_MOD } if {[opt-bool debug-mode]} { msg-result "Creating a debuggable build." define BUILDMODE {-O0 -g} } else { msg-result "Creating a release build." define BUILDMODE {-O2} } # Swap the incandescent lamp simulator feature out for the original LED # driving method on single-core hosts. The user can force this on # multi-core hosts via --no-lamp-simulator. set cores [exec tools/corecount] if {($cores < 2) || [opt-bool no-lamp-simulator]} { msg-result "Driving PiDP-8/I front panel LEDs using low-CPU-usage method." define LED_DRIVER_MODULE n define ILS_MODE 0 } else { msg-result "Driving PiDP-8/I front panel LEDs using incandescent lamp simulator." define LED_DRIVER_MODULE i define ILS_MODE 1 } # Translate --throttle value to a SIMH command set tv [opt-val throttle] set tvsl [string length $tv] if {($tvsl == 0 && $cores > 1) || $tv == "none"} { define SET_THROTTLE {set nothrottle} set tv "unlimited" } else { # Rewrite symbolic values with values SIMH can understand. See # README-throttle.md for the justification of these values. if {$tv == "single-core" || $tvsl == 0} { # value for ~75% CPU usage on a Pi Model B+ with the simple # LED driving code; conveniently, 4x the speed of a real PDP-8/I set tv "1332k" } elseif {$tv == "pdp8e"} { set tv "416k" } elseif {$tv == "pdp8i" || $tv == "pdp8a"} { set tv "333k" } elseif {$tv == "pdp8l" || $tv == "pdp8"} { set tv "313k" } elseif {$tv == "ha6120"} { set tv "182k" } elseif {$tv == "im6100a"} { set tv "200k" } elseif {$tv == "im6100"} { set tv "100k" } elseif {$tv == "im6100c"} { set tv "83k" } elseif {$tv == "pdp8s"} { set tv "63k" } elseif {$tv == "human"} { set tv "10/1000" } elseif {$tv == "trace"} { set tv "1/1000" } # else, assume --throttle was given a legal SIMH throttle value define SET_THROTTLE "set throttle $tv" } msg-result "Simulator CPU throttle set to $tv IPS" # Compiler and header checks cc-check-includes time.h cc-check-functions clock_nanosleep nanosleep usleep cc-check-functions sched_yield # We need to find an install(1) type program that supports -D. The # Raspberry Pi OSes typically used with the PiDB-8/I board do have this, # but this package also runs on non-Linux OSes (e.g. for testing on a # desktop Mac) so make sure we've got a suitable implementation. The # ginstall name is typical on non-Linux systems where GNU Coreutils was # installed alongside the core OS utilities. if {[cc-check-progs ginstall]} { define INSTALL ginstall } elseif {[cc-check-progs install]} { if {[catch {exec install -D -d . >& /dev/null} result] == 0} { define INSTALL install } else { user-error "install(1) does not support -D; install GNU Coreutils." } } else { user-error "No install(1) type program found; install GNU Coreutils." } msg-result "Found GNU install(1) program as [get-define INSTALL]." # Also find GNU readlink in the same way if {[cc-check-progs greadlink]} { set rlprg greadlink } elseif {[cc-check-progs readlink]} { if {[catch {exec readlink -f . >& /dev/null} result] == 0} { set rlprg readlink } else { user-error "readlink(1) does not support -D; install GNU Coreutils." } } else { user-error "No readlink(1) type program found; install GNU Coreutils." } msg-result "Found GNU readlink(1) as $rlprg." # Canonicalize some paths which may be relative and generate others from them define ABSPREFIX [exec $rlprg -f [get-define prefix]] define BOOTDIR "[get-define ABSPREFIX]/share/boot" define MEDIADIR "[get-define ABSPREFIX]/share/media" # Remember the name and primary group of the user who installed this, since # we want to give that group write privileges to some files when they're # installed, and we want them to own the screen(1) session. set instgrp [exec id -grn] set instusr [exec id -urn] if {$instusr == "root"} { msg-result "Error: This software will not install and run as root." user-error "Reconfigure without sudo!" } define INSTGRP $instgrp define INSTUSR $instusr msg-result "Install group for user-writeable files will be $instgrp." msg-result "Owner of screen(1) session will be $instusr." # Can we use any nonstandard flags here? We don't bother including # flags that both GCC and Clang support. The ones inside the "if" # block are those that Clang will accept in an autosetup test but # then will yell about if you try to use them. The test checks for # an -f sub-option that Clang doesn't currently support even enough # to fool autosetup. cc-check-standards c99 if {![opt-bool debug-mode]} { cc-check-flags -fipa-cp-clone cc-check-flags -fno-strict-overflow cc-check-flags -fpredictive-commoning if ([get-define HAVE_CFLAG_FIPA_CP_CLONE]) { cc-check-flags -fgcse-after-reload cc-check-flags -finline-functions cc-check-flags -fno-unsafe-loop-optimizations define-append CFLAGS "-D_GNU_SOURCE" } } # Embed this software's Fossil-based version string into gpio-common.c define VERSION [exec "[get-define srcdir]/tools/version"] # Write outputs. # # NOTE: If you change the list of files here, change INFILES in # Makefile.in, too. make-config-header src/config.h \ -auto {ENABLE_* HAVE_* PACKAGE_* SIZEOF_*} \ -bare {ILS_MODE PCB_SERIAL_MOD} make-template Makefile.in make-template bin/pidp8i.in make-template boot/0.script.in make-template boot/2.script.in make-template boot/3.script.in make-template boot/4.script.in make-template boot/6.script.in make-template boot/7.script.in make-template etc/pidp8i-init.in make-template etc/sudoers.in make-template examples/Makefile.in make-template src/Makefile.in make-template src/gpio-common.c.in make-template src/PDP8/Makefile.in make-template src/PDP8/pidp8i.c.in make-template src/scp.c.in make-template tools/simh-update.in exec chmod +x "[get-define builddir]/tools/simh-update" |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | Unless explicitly stated, all files which form part of autosetup are released under the following license: --------------------------------------------------------------------- autosetup - A build environment "autoconfigurator" Copyright (c) 2010-2011, WorkWare Systems <http://workware.net.au/> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE WORKWARE SYSTEMS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WORKWARE SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of WorkWare Systems. |
> | 1 | This is autosetup v0.6.6. See http://msteveb.github.com/autosetup/ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 | #!/bin/sh # Copyright (c) 2006-2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # vim:se syntax=tcl: # \ dir=`dirname "$0"`; exec "`$dir/find-tclsh`" "$0" "$@" set autosetup(version) 0.6.6 # Can be set to 1 to debug early-init problems set autosetup(debug) 0 ################################################################## # # Main flow of control, option handling # proc main {argv} { global autosetup define # There are 3 potential directories involved: # 1. The directory containing autosetup (this script) # 2. The directory containing auto.def # 3. The current directory # From this we need to determine: # a. The path to this script (and related support files) # b. The path to auto.def # c. The build directory, where output files are created # This is also complicated by the fact that autosetup may # have been run via the configure wrapper ([getenv WRAPPER] is set) # Here are the rules. # a. This script is $::argv0 # => dir, prog, exe, libdir # b. auto.def is in the directory containing the configure wrapper, # otherwise it is in the current directory. # => srcdir, autodef # c. The build directory is the current directory # => builddir, [pwd] # 'misc' is needed before we can do anything, so set a temporary libdir # in case this is the development version set autosetup(libdir) [file dirname $::argv0]/lib use misc # (a) set autosetup(dir) [realdir [file dirname [realpath $::argv0]]] set autosetup(prog) [file join $autosetup(dir) [file tail $::argv0]] set autosetup(exe) [getenv WRAPPER $autosetup(prog)] if {$autosetup(installed)} { set autosetup(libdir) $autosetup(dir) } else { set autosetup(libdir) [file join $autosetup(dir) lib] } autosetup_add_dep $autosetup(prog) # (b) if {[getenv WRAPPER ""] eq ""} { # Invoked directly set autosetup(srcdir) [pwd] } else { # Invoked via the configure wrapper set autosetup(srcdir) [file-normalize [file dirname $autosetup(exe)]] } set autosetup(autodef) [relative-path $autosetup(srcdir)/auto.def] # (c) set autosetup(builddir) [pwd] set autosetup(argv) $argv set autosetup(cmdline) {} # options is a list of known options set autosetup(options) {} # optset is a dictionary of option values set by the user based on getopt set autosetup(optset) {} # optdefault is a dictionary of default values for options set autosetup(optdefault) {} set autosetup(optionhelp) {} set autosetup(showhelp) 0 # Parse options use getopt # At the is point we don't know what is a valid option # We simply parse anything that looks like an option set autosetup(getopt) [getopt argv] #"=Core Options:" options-add { help:=local => "display help and options. Optionally specify a module name, such as --help=system" version => "display the version of autosetup" ref:=text manual:=text reference:=text => "display the autosetup command reference. 'text', 'wiki', 'asciidoc' or 'markdown'" debug => "display debugging output as autosetup runs" install:=. => "install autosetup to the current or given directory (in the 'autosetup/' subdirectory)" force init:=help => "create initial auto.def, etc. Use --init=help for known types" # Undocumented options option-checking=1 nopager quiet timing conf: } if {[opt-bool version]} { puts $autosetup(version) exit 0 } # autosetup --conf=alternate-auto.def if {[opt-val conf] ne ""} { set autosetup(autodef) [opt-val conf] } # Debugging output (set this early) incr autosetup(debug) [opt-bool debug] incr autosetup(force) [opt-bool force] incr autosetup(msg-quiet) [opt-bool quiet] incr autosetup(msg-timing) [opt-bool timing] # If the local module exists, source it now to allow for # project-local customisations if {[file exists $autosetup(libdir)/local.tcl]} { use local } # Now any auto-load modules foreach file [glob -nocomplain $autosetup(libdir)/*.auto $autosetup(libdir)/*/*.auto] { automf_load source $file } if {[opt-val help] ne ""} { incr autosetup(showhelp) use help autosetup_help [opt-val help] } if {[opt-val {manual ref reference}] ne ""} { use help autosetup_reference [opt-val {manual ref reference}] } # Allow combining --install and --init set earlyexit 0 if {[opt-val install] ne ""} { use install autosetup_install [opt-val install] incr earlyexit } if {[opt-val init] ne ""} { use init autosetup_init [opt-val init] incr earlyexit } if {$earlyexit} { exit 0 } if {![file exists $autosetup(autodef)]} { # Check for invalid option first options {} user-error "No auto.def found in \"$autosetup(srcdir)\" (use [file tail $::autosetup(exe)] --init to create one)" } # Parse extra arguments into autosetup(cmdline) foreach arg $argv { if {[regexp {([^=]*)=(.*)} $arg -> n v]} { dict set autosetup(cmdline) $n $v define $n $v } else { user-error "Unexpected parameter: $arg" } } autosetup_add_dep $autosetup(autodef) define CONFIGURE_OPTS "" foreach arg $autosetup(argv) { define-append CONFIGURE_OPTS [quote-if-needed $arg] } define AUTOREMAKE [file-normalize $autosetup(exe)] define-append AUTOREMAKE [get-define CONFIGURE_OPTS] # Log how we were invoked configlog "Invoked as: [getenv WRAPPER $::argv0] [quote-argv $autosetup(argv)]" # Note that auto.def is *not* loaded in the global scope source $autosetup(autodef) # Could warn here if options {} was not specified show-notices if {$autosetup(debug)} { msg-result "Writing all defines to config.log" configlog "================ defines ======================" foreach n [lsort [array names define]] { configlog "define $n $define($n)" } } exit 0 } # @opt-bool ?-nodefault? option ... # # Check each of the named, boolean options and if any have been explicitly enabled # or disabled by the user, return 1 or 0 accordingly. # # If the option was specified more than once, the last value wins. # e.g. With --enable-foo --disable-foo, [opt-bool foo] will return 0 # # If no value was specified by the user, returns the default value for the # first option. If -nodefault is given, this behaviour changes and # -1 is returned instead. # proc opt-bool {args} { set nodefault 0 if {[lindex $args 0] eq "-nodefault"} { set nodefault 1 set args [lrange $args 1 end] } option-check-names {*}$args foreach opt $args { if {[dict exists $::autosetup(optset) $opt]} { return [dict get $::autosetup(optset) $opt] } } if {$nodefault} { return -1 } # Default value is the default for the first option return [dict get $::autosetup(optdefault) [lindex $args 0]] } # @opt-val option-list ?default=""? # # Returns a list containing all the values given for the non-boolean options in 'option-list'. # There will be one entry in the list for each option given by the user, including if the # same option was used multiple times. # If only a single value is required, use something like: # ## lindex [opt-val $names] end # # If no options were set, $default is returned (exactly, not as a list). # proc opt-val {names {default ""}} { option-check-names {*}$names foreach opt $names { if {[dict exists $::autosetup(optset) $opt]} { lappend result {*}[dict get $::autosetup(optset) $opt] } } if {[info exists result]} { return $result } return $default } proc option-check-names {args} { foreach o $args { if {$o ni $::autosetup(options)} { autosetup-error "Request for undeclared option --$o" } } } # Parse the option definition in $opts and update # ::autosetup(setoptions) and ::autosetup(optionhelp) appropriately # proc options-add {opts {header ""}} { global autosetup # First weed out comment lines set realopts {} foreach line [split $opts \n] { if {![string match "#*" [string trimleft $line]]} { append realopts $line \n } } set opts $realopts for {set i 0} {$i < [llength $opts]} {incr i} { set opt [lindex $opts $i] if {[string match =* $opt]} { # This is a special heading lappend autosetup(optionhelp) $opt "" set header {} continue } #puts "i=$i, opt=$opt" regexp {^([^:=]*)(:)?(=)?(.*)$} $opt -> name colon equal value if {$name in $autosetup(options)} { autosetup-error "Option $name already specified" } #puts "$opt => $name $colon $equal $value" # Find the corresponding value in the user options # and set the default if necessary if {[string match "-*" $opt]} { # This is a documentation-only option, like "-C <dir>" set opthelp $opt } elseif {$colon eq ""} { # Boolean option lappend autosetup(options) $name if {$value eq "1"} { set opthelp "--disable-$name" } else { set opthelp "--$name" } # Set the default if {$value eq ""} { set value 0 } dict set autosetup(optdefault) $name $value if {[dict exists $autosetup(getopt) $name]} { # The option was specified by the user. Look at the last value. lassign [lindex [dict get $autosetup(getopt) $name] end] type setvalue if {$type eq "str"} { # Can we convert the value to a boolean? if {$setvalue in {1 enabled yes}} { set setvalue 1 } elseif {$setvalue in {0 disabled no}} { set setvalue 0 } else { user-error "Boolean option $name given as --$name=$setvalue" } } dict set autosetup(optset) $name $setvalue #puts "Found boolean option --$name=$setvalue" } } else { # String option. lappend autosetup(options) $name if {$equal eq "="} { # String option with optional value set opthelp "--$name?=$value?" } else { # String option with required value set opthelp "--$name=$value" } dict set autosetup(optdefault) $name $value # Get the values specified by the user if {[dict exists $autosetup(getopt) $name]} { set listvalue {} foreach pair [dict get $autosetup(getopt) $name] { lassign $pair type setvalue if {$type eq "bool" && $setvalue} { if {$equal ne "="} { user-error "Option --$name requires a value" } # If given as a boolean, use the default value set setvalue $value } lappend listvalue $setvalue } #puts "Found string option --$name=$listvalue" dict set autosetup(optset) $name $listvalue } } # Now create the help for this option if appropriate if {[lindex $opts $i+1] eq "=>"} { set desc [lindex $opts $i+2] #string match \n* $desc if {$header ne ""} { lappend autosetup(optionhelp) $header "" set header "" } # A multi-line description lappend autosetup(optionhelp) $opthelp $desc incr i 2 } } } # @module-options optionlist # # Like 'options', but used within a module. proc module-options {opts} { set header "" if {$::autosetup(showhelp) > 1 && [llength $opts]} { set header "Module Options:" } options-add $opts $header if {$::autosetup(showhelp)} { # Ensure that the module isn't executed on --help # We are running under eval or source, so use break # to prevent further execution #return -code break -level 2 return -code break } } proc max {a b} { expr {$a > $b ? $a : $b} } proc options-wrap-desc {text length firstprefix nextprefix initial} { set len $initial set space $firstprefix foreach word [split $text] { set word [string trim $word] if {$word == ""} { continue } if {$len && [string length $space$word] + $len >= $length} { puts "" set len 0 set space $nextprefix } incr len [string length $space$word] puts -nonewline $space$word set space " " } if {$len} { puts "" } } proc options-show {} { # Determine the max option width set max 0 foreach {opt desc} $::autosetup(optionhelp) { if {[string match =* $opt] || [string match \n* $desc]} { continue } set max [max $max [string length $opt]] } set indent [string repeat " " [expr $max+4]] set cols [getenv COLUMNS 80] catch { lassign [exec stty size] rows cols } incr cols -1 # Now output foreach {opt desc} $::autosetup(optionhelp) { if {[string match =* $opt]} { puts [string range $opt 1 end] continue } puts -nonewline " [format %-${max}s $opt]" if {[string match \n* $desc]} { puts $desc } else { options-wrap-desc [string trim $desc] $cols " " $indent [expr $max + 2] } } } # @options options-spec # # Specifies configuration-time options which may be selected by the user # and checked with opt-val and opt-bool. The format of options-spec follows. # # A boolean option is of the form: # ## name[=0|1] => "Description of this boolean option" # # The default is name=0, meaning that the option is disabled by default. # If name=1 is used to make the option enabled by default, the description should reflect # that with text like "Disable support for ...". # # An argument option (one which takes a parameter) is of the form: # ## name:[=]value => "Description of this option" # # If the name:value form is used, the value must be provided with the option (as --name=myvalue). # If the name:=value form is used, the value is optional and the given value is used as the default # if it is not provided. # # Undocumented options are also supported by omitting the "=> description. # These options are not displayed with --help and can be useful for internal options or as aliases. # # For example, --disable-lfs is an alias for --disable=largefile: # ## lfs=1 largefile=1 => "Disable large file support" # proc options {optlist} { # Allow options as a list or args options-add $optlist "Local Options:" if {$::autosetup(showhelp)} { options-show exit 0 } # Check for invalid options if {[opt-bool option-checking]} { foreach o [dict keys $::autosetup(getopt)] { if {$o ni $::autosetup(options)} { user-error "Unknown option --$o" } } } } proc config_guess {} { if {[file-isexec $::autosetup(dir)/config.guess]} { exec-with-stderr sh $::autosetup(dir)/config.guess if {[catch {exec-with-stderr sh $::autosetup(dir)/config.guess} alias]} { user-error $alias } return $alias } else { configlog "No config.guess, so using uname" string tolower [exec uname -p]-unknown-[exec uname -s][exec uname -r] } } proc config_sub {alias} { if {[file-isexec $::autosetup(dir)/config.sub]} { if {[catch {exec-with-stderr sh $::autosetup(dir)/config.sub $alias} alias]} { user-error $alias } } return $alias } # @define name ?value=1? # # Defines the named variable to the given value. # These (name, value) pairs represent the results of the configuration check # and are available to be checked, modified and substituted. # proc define {name {value 1}} { set ::define($name) $value #dputs "$name <= $value" } # @undefine name # # Undefine the named variable # proc undefine {name} { unset -nocomplain ::define($name) #dputs "$name <= <undef>" } # @define-append name value ... # # Appends the given value(s) to the given 'defined' variable. # If the variable is not defined or empty, it is set to $value. # Otherwise the value is appended, separated by a space. # Any extra values are similarly appended. # If any value is already contained in the variable (as a substring) it is omitted. # proc define-append {name args} { if {[get-define $name ""] ne ""} { # Make a token attempt to avoid duplicates foreach arg $args { if {[string first $arg $::define($name)] == -1} { append ::define($name) " " $arg } } } else { set ::define($name) [join $args] } #dputs "$name += [join $args] => $::define($name)" } # @get-define name ?default=0? # # Returns the current value of the 'defined' variable, or $default # if not set. # proc get-define {name {default 0}} { if {[info exists ::define($name)]} { #dputs "$name => $::define($name)" return $::define($name) } #dputs "$name => $default" return $default } # @is-defined name # # Returns 1 if the given variable is defined. # proc is-defined {name} { info exists ::define($name) } # @all-defines # # Returns a dictionary (name value list) of all defined variables. # # This is suitable for use with 'dict', 'array set' or 'foreach' # and allows for arbitrary processing of the defined variables. # proc all-defines {} { array get ::define } # @get-env name default # # If $name was specified on the command line, return it. # If $name was set in the environment, return it. # Otherwise return $default. # proc get-env {name default} { if {[dict exists $::autosetup(cmdline) $name]} { return [dict get $::autosetup(cmdline) $name] } getenv $name $default } # @env-is-set name # # Returns 1 if the $name was specified on the command line or in the environment. # Note that an empty environment variable is not considered to be set. # proc env-is-set {name} { if {[dict exists $::autosetup(cmdline) $name]} { return 1 } if {[getenv $name ""] ne ""} { return 1 } return 0 } # @readfile filename ?default=""? # # Return the contents of the file, without the trailing newline. # If the file doesn't exist or can't be read, returns $default. # proc readfile {filename {default_value ""}} { set result $default_value catch { set f [open $filename] set result [read -nonewline $f] close $f } return $result } # @writefile filename value # # Creates the given file containing $value. # Does not add an extra newline. # proc writefile {filename value} { set f [open $filename w] puts -nonewline $f $value close $f } proc quote-if-needed {str} { if {[string match {*[\" ]*} $str]} { return \"[string map [list \" \\" \\ \\\\] $str]\" } return $str } proc quote-argv {argv} { set args {} foreach arg $argv { lappend args [quote-if-needed $arg] } join $args } # @suffix suf list # # Takes a list and returns a new list with $suf appended # to each element # ## suffix .c {a b c} => {a.c b.c c.c} # proc suffix {suf list} { set result {} foreach p $list { lappend result $p$suf } return $result } # @prefix pre list # # Takes a list and returns a new list with $pre prepended # to each element # ## prefix jim- {a.c b.c} => {jim-a.c jim-b.c} # proc prefix {pre list} { set result {} foreach p $list { lappend result $pre$p } return $result } # @find-executable name # # Searches the path for an executable with the given name. # Note that the name may include some parameters, e.g. "cc -mbig-endian", # in which case the parameters are ignored. # Returns 1 if found, or 0 if not. # proc find-executable {name} { # Ignore any parameters set name [lindex $name 0] if {$name eq ""} { # The empty string is never a valid executable return 0 } foreach p [split-path] { dputs "Looking for $name in $p" set exec [file join $p $name] if {[file-isexec $exec]} { dputs "Found $name -> $exec" return 1 } } return 0 } # @find-an-executable ?-required? name ... # # Given a list of possible executable names, # searches for one of these on the path. # # Returns the name found, or "" if none found. # If the first parameter is '-required', an error is generated # if no executable is found. # proc find-an-executable {args} { set required 0 if {[lindex $args 0] eq "-required"} { set args [lrange $args 1 end] incr required } foreach name $args { if {[find-executable $name]} { return $name } } if {$required} { if {[llength $args] == 1} { user-error "failed to find: [join $args]" } else { user-error "failed to find one of: [join $args]" } } return "" } # @configlog msg # # Writes the given message to the configuration log, config.log # proc configlog {msg} { if {![info exists ::autosetup(logfh)]} { set ::autosetup(logfh) [open config.log w] } puts $::autosetup(logfh) $msg } # @msg-checking msg # # Writes the message with no newline to stdout. # proc msg-checking {msg} { if {$::autosetup(msg-quiet) == 0} { maybe-show-timestamp puts -nonewline $msg set ::autosetup(msg-checking) 1 } } # @msg-result msg # # Writes the message to stdout. # proc msg-result {msg} { if {$::autosetup(msg-quiet) == 0} { maybe-show-timestamp puts $msg set ::autosetup(msg-checking) 0 show-notices } } # @msg-quiet command ... # # msg-quiet evaluates it's arguments as a command with output # from msg-checking and msg-result suppressed. # # This is useful if a check needs to run a subcheck which isn't # of interest to the user. proc msg-quiet {args} { incr ::autosetup(msg-quiet) set rc [uplevel 1 $args] incr ::autosetup(msg-quiet) -1 return $rc } # Will be overridden by 'use misc' proc error-stacktrace {msg} { return $msg } proc error-location {msg} { return $msg } ################################################################## # # Debugging output # proc dputs {msg} { if {$::autosetup(debug)} { puts $msg } } ################################################################## # # User and system warnings and errors # # Usage errors such as wrong command line options # @user-error msg # # Indicate incorrect usage to the user, including if required components # or features are not found. # autosetup exits with a non-zero return code. # proc user-error {msg} { show-notices puts stderr "Error: $msg" puts stderr "Try: '[file tail $::autosetup(exe)] --help' for options" exit 1 } # @user-notice msg # # Output the given message to stderr. # proc user-notice {msg} { lappend ::autosetup(notices) $msg } # Incorrect usage in the auto.def file. Identify the location. proc autosetup-error {msg} { autosetup-full-error [error-location $msg] } # Like autosetup-error, except $msg is the full error message. proc autosetup-full-error {msg} { show-notices puts stderr $msg exit 1 } proc show-notices {} { if {$::autosetup(msg-checking)} { puts "" set ::autosetup(msg-checking) 0 } flush stdout if {[info exists ::autosetup(notices)]} { puts stderr [join $::autosetup(notices) \n] unset ::autosetup(notices) } } proc maybe-show-timestamp {} { if {$::autosetup(msg-timing) && $::autosetup(msg-checking) == 0} { puts -nonewline [format {[%6.2f] } [expr {([clock millis] - $::autosetup(start)) % 10000 / 1000.0}]] } } proc autosetup_version {} { return "autosetup v$::autosetup(version)" } ################################################################## # # Directory/path handling # proc realdir {dir} { set oldpwd [pwd] cd $dir set pwd [pwd] cd $oldpwd return $pwd } # Follow symlinks until we get to something which is not a symlink proc realpath {path} { while {1} { if {[catch { set path [file readlink $path] }]} { # Not a link break } } return $path } # Convert absolute path, $path into a path relative # to the given directory (or the current dir, if not given). # proc relative-path {path {pwd {}}} { set diff 0 set same 0 set newf {} set prefix {} set path [file-normalize $path] if {$pwd eq ""} { set pwd [pwd] } else { set pwd [file-normalize $pwd] } if {$path eq $pwd} { return . } # Try to make the filename relative to the current dir foreach p [split $pwd /] f [split $path /] { if {$p ne $f} { incr diff } elseif {!$diff} { incr same } if {$diff} { if {$p ne ""} { # Add .. for sibling or parent dir lappend prefix .. } if {$f ne ""} { lappend newf $f } } } if {$same == 1 || [llength $prefix] > 3} { return $path } file join [join $prefix /] [join $newf /] } # Add filename as a dependency to rerun autosetup # The name will be normalised (converted to a full path) # proc autosetup_add_dep {filename} { lappend ::autosetup(deps) [file-normalize $filename] } ################################################################## # # Library module support # # @use module ... # # Load the given library modules. # e.g. 'use cc cc-shared' # # Note that module 'X' is implemented in either 'autosetup/X.tcl' # or 'autosetup/X/init.tcl' # # The latter form is useful for a complex module which requires additional # support file. In this form, '$::usedir' is set to the module directory # when it is loaded. # proc use {args} { foreach m $args { if {[info exists ::libmodule($m)]} { continue } set ::libmodule($m) 1 if {[info exists ::modsource($m)]} { automf_load eval $::modsource($m) } else { set sources [list $::autosetup(libdir)/${m}.tcl $::autosetup(libdir)/${m}/init.tcl] set found 0 foreach source $sources { if {[file exists $source]} { incr found break } } if {$found} { # For the convenience of the "use" source, point to the directory # it is being loaded from set ::usedir [file dirname $source] automf_load source $source autosetup_add_dep $source } else { autosetup-error "use: No such module: $m" } } } } # Load module source in the global scope by executing the given command proc automf_load {args} { if {[catch [list uplevel #0 $args] msg opts] ni {0 2 3}} { autosetup-full-error [error-dump $msg $opts $::autosetup(debug)] } } # Initial settings set autosetup(exe) $::argv0 set autosetup(istcl) 1 set autosetup(start) [clock millis] set autosetup(installed) 0 set autosetup(msg-checking) 0 set autosetup(msg-quiet) 0 # Embedded modules are inserted below here set autosetup(installed) 1 # ----- module asciidoc-formatting ----- set modsource(asciidoc-formatting) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting # asciidoc format use formatting proc para {text} { regsub -all "\[ \t\n\]+" [string trim $text] " " } proc title {text} { underline [para $text] = nl } proc p {text} { puts [para $text] nl } proc code {text} { foreach line [parse_code_block $text] { puts " $line" } nl } proc codelines {lines} { foreach line $lines { puts " $line" } nl } proc nl {} { puts "" } proc underline {text char} { regexp "^(\[ \t\]*)(.*)" $text -> indent words puts $text puts $indent[string repeat $char [string length $words]] } proc section {text} { underline "[para $text]" - nl } proc subsection {text} { underline "$text" ~ nl } proc bullet {text} { puts "* [para $text]" } proc indent {text} { puts " :: " puts [para $text] } proc defn {first args} { set sep "" if {$first ne ""} { puts "${first}::" } else { puts " :: " } set defn [string trim [join $args \n]] regsub -all "\n\n" $defn "\n ::\n" defn puts $defn } } # ----- module formatting ----- set modsource(formatting) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides common text formatting # This is designed for documenation which looks like: # code {...} # or # code { # ... # ... # } # In the second case, we need to work out the indenting # and strip it from all lines but preserve the remaining indenting. # Note that all lines need to be indented with the same initial # spaces/tabs. # # Returns a list of lines with the indenting removed. # proc parse_code_block {text} { # If the text begins with newline, take the following text, # otherwise just return the original if {![regexp "^\n(.*)" $text -> text]} { return [list [string trim $text]] } # And trip spaces off the end set text [string trimright $text] set min 100 # Examine each line to determine the minimum indent foreach line [split $text \n] { if {$line eq ""} { # Ignore empty lines for the indent calculation continue } regexp "^(\[ \t\]*)" $line -> indent set len [string length $indent] if {$len < $min} { set min $len } } # Now make a list of lines with this indent removed set lines {} foreach line [split $text \n] { lappend lines [string range $line $min end] } # Return the result return $lines } } # ----- module getopt ----- set modsource(getopt) { # Copyright (c) 2006 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Simple getopt module # Parse everything out of the argv list which looks like an option # Everything which doesn't look like an option, or is after --, is left unchanged # Understands --enable-xxx and --with-xxx as synonyms for --xxx to enable the boolean option xxx. # Understands --disable-xxx and --without-xxx to disable the boolean option xxx. # # The returned value is a dictionary keyed by option name # Each value is a list of {type value} ... where type is "bool" or "str". # The value for a boolean option is 0 or 1. The value of a string option is the value given. proc getopt {argvname} { upvar $argvname argv set nargv {} set opts {} for {set i 0} {$i < [llength $argv]} {incr i} { set arg [lindex $argv $i] #dputs arg=$arg if {$arg eq "--"} { # End of options incr i lappend nargv {*}[lrange $argv $i end] break } if {[regexp {^--([^=][^=]+)=(.*)$} $arg -> name value]} { # --name=value dict lappend opts $name [list str $value] } elseif {[regexp {^--(enable-|disable-|with-|without-)?([^=]*)$} $arg -> prefix name]} { if {$prefix in {enable- with- ""}} { set value 1 } else { set value 0 } dict lappend opts $name [list bool $value] } else { lappend nargv $arg } } #puts "getopt: argv=[join $argv] => [join $nargv]" #array set getopt $opts #parray getopt set argv $nargv return $opts } } # ----- module help ----- set modsource(help) { # Copyright (c) 2010 WorkWare Systems http://workware.net.au/ # All rights reserved # Module which provides usage, help and the command reference proc autosetup_help {what} { use_pager puts "Usage: [file tail $::autosetup(exe)] \[options\] \[settings\]\n" puts "This is [autosetup_version], a build environment \"autoconfigurator\"" puts "See the documentation online at http://msteveb.github.com/autosetup/\n" if {$what eq "local"} { if {[file exists $::autosetup(autodef)]} { # This relies on auto.def having a call to 'options' # which will display options and quit source $::autosetup(autodef) } else { options-show } } else { incr ::autosetup(showhelp) if {[catch {use $what}]} { user-error "Unknown module: $what" } else { options-show } } exit 0 } # If not already paged and stdout is a tty, pipe the output through the pager # This is done by reinvoking autosetup with --nopager added proc use_pager {} { if {![opt-bool nopager] && [getenv PAGER ""] ne "" && [isatty? stdin] && [isatty? stdout]} { if {[catch { exec [info nameofexecutable] $::argv0 --nopager {*}$::argv |& {*}[getenv PAGER] >@stdout <@stdin 2>@stderr } msg opts] == 1} { if {[dict get $opts -errorcode] eq "NONE"} { # an internal/exec error puts stderr $msg exit 1 } } exit 0 } } # Outputs the autosetup references in one of several formats proc autosetup_reference {{type text}} { use_pager switch -glob -- $type { wiki {use wiki-formatting} ascii* {use asciidoc-formatting} md - markdown {use markdown-formatting} default {use text-formatting} } title "[autosetup_version] -- Command Reference" section {Introduction} p { See http://msteveb.github.com/autosetup/ for the online documentation for 'autosetup' } p { 'autosetup' provides a number of built-in commands which are documented below. These may be used from 'auto.def' to test for features, define variables, create files from templates and other similar actions. } automf_command_reference exit 0 } proc autosetup_output_block {type lines} { if {[llength $lines]} { switch $type { code { codelines $lines } p { p [join $lines] } list { foreach line $lines { bullet $line } nl } } } } # Generate a command reference from inline documentation proc automf_command_reference {} { lappend files $::autosetup(prog) lappend files {*}[lsort [glob -nocomplain $::autosetup(libdir)/*.tcl]] section "Core Commands" set type p set lines {} set cmd {} foreach file $files { set f [open $file] while {![eof $f]} { set line [gets $f] # Find lines starting with "# @*" and continuing through the remaining comment lines if {![regexp {^# @(.*)} $line -> cmd]} { continue } # Synopsis or command? if {$cmd eq "synopsis:"} { section "Module: [file rootname [file tail $file]]" } else { subsection $cmd } set lines {} set type p # Now the description while {![eof $f]} { set line [gets $f] if {![regexp {^#(#)? ?(.*)} $line -> hash cmd]} { break } if {$hash eq "#"} { set t code } elseif {[regexp {^- (.*)} $cmd -> cmd]} { set t list } else { set t p } #puts "hash=$hash, oldhash=$oldhash, lines=[llength $lines], cmd=$cmd" if {$t ne $type || $cmd eq ""} { # Finish the current block autosetup_output_block $type $lines set lines {} set type $t } if {$cmd ne ""} { lappend lines $cmd } } autosetup_output_block $type $lines } close $f } } } # ----- module init ----- set modsource(init) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module to help create auto.def and configure proc autosetup_init {type} { set help 0 if {$type in {? help}} { incr help } elseif {![dict exists $::autosetup(inittypes) $type]} { puts "Unknown type, --init=$type" incr help } if {$help} { puts "Use one of the following types (e.g. --init=make)\n" foreach type [lsort [dict keys $::autosetup(inittypes)]] { lassign [dict get $::autosetup(inittypes) $type] desc # XXX: Use the options-show code to wrap the description puts [format "%-10s %s" $type $desc] } return } lassign [dict get $::autosetup(inittypes) $type] desc script puts "Initialising $type: $desc\n" # All initialisations happens in the top level srcdir cd $::autosetup(srcdir) uplevel #0 $script } proc autosetup_add_init_type {type desc script} { dict set ::autosetup(inittypes) $type [list $desc $script] } # This is for in creating build-system init scripts # # If the file doesn't exist, create it containing $contents # If the file does exist, only overwrite if --force is specified. # proc autosetup_check_create {filename contents} { if {[file exists $filename]} { if {!$::autosetup(force)} { puts "I see $filename already exists." return } else { puts "I will overwrite the existing $filename because you used --force." } } else { puts "I don't see $filename, so I will create it." } writefile $filename $contents } } # ----- module install ----- set modsource(install) { # Copyright (c) 2006-2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which can install autosetup proc autosetup_install {dir} { if {[catch { cd $dir file mkdir autosetup set f [open autosetup/autosetup w] set publicmodules [glob $::autosetup(libdir)/*.auto] # First the main script, but only up until "CUT HERE" set in [open $::autosetup(dir)/autosetup] while {[gets $in buf] >= 0} { if {$buf ne "##-- CUT HERE --##"} { puts $f $buf continue } # Insert the static modules here # i.e. those which don't contain @synopsis: puts $f "set autosetup(installed) 1" foreach file [lsort [glob $::autosetup(libdir)/*.tcl]] { set buf [readfile $file] if {[string match "*\n# @synopsis:*" $buf]} { lappend publicmodules $file continue } set modname [file rootname [file tail $file]] puts $f "# ----- module $modname -----" puts $f "\nset modsource($modname) \{" puts $f $buf puts $f "\}\n" } } close $in close $f exec chmod 755 autosetup/autosetup # Install public modules foreach file $publicmodules { autosetup_install_file $file autosetup } # Install support files foreach file {config.guess config.sub jimsh0.c find-tclsh test-tclsh LICENSE} { autosetup_install_file $::autosetup(dir)/$file autosetup } exec chmod 755 autosetup/config.sub autosetup/config.guess autosetup/find-tclsh writefile autosetup/README.autosetup \ "This is [autosetup_version]. See http://msteveb.github.com/autosetup/\n" } error]} { user-error "Failed to install autosetup: $error" } puts "Installed [autosetup_version] to autosetup/" # Now create 'configure' if necessary autosetup_create_configure } proc autosetup_create_configure {} { if {[file exists configure]} { if {!$::autosetup(force)} { # Could this be an autosetup configure? if {![string match "*\nWRAPPER=*" [readfile configure]]} { puts "I see configure, but not created by autosetup, so I won't overwrite it." puts "Remove it or use --force to overwrite." return } } else { puts "I will overwrite the existing configure because you used --force." } } else { puts "I don't see configure, so I will create it." } writefile configure \ {#!/bin/sh dir="`dirname "$0"`/autosetup" WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/autosetup" "$@" } catch {exec chmod 755 configure} } # Append the contents of $file to filehandle $f proc autosetup_install_append {f file} { set in [open $file] puts $f [read $in] close $in } proc autosetup_install_file {file dir} { if {![file exists $file]} { error "Missing installation file '$file'" } writefile [file join $dir [file tail $file]] [readfile $file]\n } if {$::autosetup(installed)} { user-error "autosetup can only be installed from development source, not from installed copy" } } # ----- module markdown-formatting ----- set modsource(markdown-formatting) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting # markdown format (kramdown syntax) use formatting proc para {text} { regsub -all "\[ \t\n\]+" [string trim $text] " " text regsub -all {([^a-zA-Z])'([^']*)'} $text {\1**`\2`**} text regsub -all {^'([^']*)'} $text {**`\1`**} text regsub -all {(http[^ \t\n]*)} $text {[\1](\1)} text return $text } proc title {text} { underline [para $text] = nl } proc p {text} { puts [para $text] nl } proc codelines {lines} { puts "~~~~~~~~~~~~" foreach line $lines { puts $line } puts "~~~~~~~~~~~~" nl } proc code {text} { puts "~~~~~~~~~~~~" foreach line [parse_code_block $text] { puts $line } puts "~~~~~~~~~~~~" nl } proc nl {} { puts "" } proc underline {text char} { regexp "^(\[ \t\]*)(.*)" $text -> indent words puts $text puts $indent[string repeat $char [string length $words]] } proc section {text} { underline "[para $text]" - nl } proc subsection {text} { puts "### `$text`" nl } proc bullet {text} { puts "* [para $text]" } proc defn {first args} { puts "^" set defn [string trim [join $args \n]] if {$first ne ""} { puts "**${first}**" puts -nonewline ": " regsub -all "\n\n" $defn "\n: " defn } puts "$defn" } } # ----- module misc ----- set modsource(misc) { # Copyright (c) 2007-2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module containing misc procs useful to modules # Largely for platform compatibility set autosetup(istcl) [info exists ::tcl_library] set autosetup(iswin) [string equal windows $tcl_platform(platform)] if {$autosetup(iswin)} { # mingw/windows separates $PATH with semicolons # and doesn't have an executable bit proc split-path {} { split [getenv PATH .] {;} } proc file-isexec {exec} { # Basic test for windows. We ignore .bat if {[file isfile $exec] || [file isfile $exec.exe]} { return 1 } return 0 } } else { # unix separates $PATH with colons and has and executable bit proc split-path {} { split [getenv PATH .] : } proc file-isexec {exec} { file executable $exec } } # Assume that exec can return stdout and stderr proc exec-with-stderr {args} { exec {*}$args 2>@1 } if {$autosetup(istcl)} { # Tcl doesn't have the env command proc getenv {name args} { if {[info exists ::env($name)]} { return $::env($name) } if {[llength $args]} { return [lindex $args 0] } return -code error "environment variable \"$name\" does not exist" } proc isatty? {channel} { dict exists [fconfigure $channel] -xchar } } else { if {$autosetup(iswin)} { # On Windows, backslash convert all environment variables # (Assume that Tcl does this for us) proc getenv {name args} { string map {\\ /} [env $name {*}$args] } } else { # Jim on unix is simple alias getenv env } proc isatty? {channel} { set tty 0 catch { # isatty is a recent addition to Jim Tcl set tty [$channel isatty] } return $tty } } # In case 'file normalize' doesn't exist # proc file-normalize {path} { if {[catch {file normalize $path} result]} { if {$path eq ""} { return "" } set oldpwd [pwd] if {[file isdir $path]} { cd $path set result [pwd] } else { cd [file dirname $path] set result [file join [pwd] [file tail $path]] } cd $oldpwd } return $result } # If everything is working properly, the only errors which occur # should be generated in user code (e.g. auto.def). # By default, we only want to show the error location in user code. # We use [info frame] to achieve this, but it works differently on Tcl and Jim. # # This is designed to be called for incorrect usage in auto.def, via autosetup-error # proc error-location {msg} { if {$::autosetup(debug)} { return -code error $msg } # Search back through the stack trace for the first error in a .def file for {set i 1} {$i < [info level]} {incr i} { if {$::autosetup(istcl)} { array set info [info frame -$i] } else { lassign [info frame -$i] info(caller) info(file) info(line) } if {[string match *.def $info(file)]} { return "[relative-path $info(file)]:$info(line): Error: $msg" } #puts "Skipping $info(file):$info(line)" } return $msg } # If everything is working properly, the only errors which occur # should be generated in user code (e.g. auto.def). # By default, we only want to show the error location in user code. # We use [info frame] to achieve this, but it works differently on Tcl and Jim. # # This is designed to be called for incorrect usage in auto.def, via autosetup-error # proc error-stacktrace {msg} { if {$::autosetup(debug)} { return -code error $msg } # Search back through the stack trace for the first error in a .def file for {set i 1} {$i < [info level]} {incr i} { if {$::autosetup(istcl)} { array set info [info frame -$i] } else { lassign [info frame -$i] info(caller) info(file) info(line) } if {[string match *.def $info(file)]} { return "[relative-path $info(file)]:$info(line): Error: $msg" } #puts "Skipping $info(file):$info(line)" } return $msg } # Given the return from [catch {...} msg opts], returns an appropriate # error message. A nice one for Jim and a less-nice one for Tcl. # If 'fulltrace' is set, a full stack trace is provided. # Otherwise a simple message is provided. # # This is designed for developer errors, e.g. in module code or auto.def code # # proc error-dump {msg opts fulltrace} { if {$::autosetup(istcl)} { if {$fulltrace} { return "Error: [dict get $opts -errorinfo]" } else { return "Error: $msg" } } else { lassign $opts(-errorinfo) p f l if {$f ne ""} { set result "$f:$l: Error: " } append result "$msg\n" if {$fulltrace} { append result [stackdump $opts(-errorinfo)] } # Remove the trailing newline string trim $result } } } # ----- module text-formatting ----- set modsource(text-formatting) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting use formatting proc wordwrap {text length {firstprefix ""} {nextprefix ""}} { set len 0 set space $firstprefix foreach word [split $text] { set word [string trim $word] if {$word == ""} { continue } if {$len && [string length $space$word] + $len >= $length} { puts "" set len 0 set space $nextprefix } incr len [string length $space$word] # Use man-page conventions for highlighting 'quoted' and *quoted* # single words. # Use x^Hx for *bold* and _^Hx for 'underline'. # # less and more will both understand this. # Pipe through 'col -b' to remove them. if {[regexp {^'(.*)'([^a-zA-Z0-9_]*)$} $word -> bareword dot]} { regsub -all . $bareword "_\b&" word append word $dot } elseif {[regexp {^[*](.*)[*]([^a-zA-Z0-9_]*)$} $word -> bareword dot]} { regsub -all . $bareword "&\b&" word append word $dot } puts -nonewline $space$word set space " " } if {$len} { puts "" } } proc title {text} { underline [string trim $text] = nl } proc p {text} { wordwrap $text 80 nl } proc codelines {lines} { foreach line $lines { puts " $line" } nl } proc nl {} { puts "" } proc underline {text char} { regexp "^(\[ \t\]*)(.*)" $text -> indent words puts $text puts $indent[string repeat $char [string length $words]] } proc section {text} { underline "[string trim $text]" - nl } proc subsection {text} { underline "$text" ~ nl } proc bullet {text} { wordwrap $text 76 " * " " " } proc indent {text} { wordwrap $text 76 " " " " } proc defn {first args} { if {$first ne ""} { underline " $first" ~ } foreach p $args { if {$p ne ""} { indent $p } } } } # ----- module wiki-formatting ----- set modsource(wiki-formatting) { # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Module which provides text formatting # wiki.tcl.tk format output use formatting proc joinlines {text} { set lines {} foreach l [split [string trim $text] \n] { lappend lines [string trim $l] } join $lines } proc p {text} { puts [joinlines $text] puts "" } proc title {text} { puts "*** [joinlines $text] ***" puts "" } proc codelines {lines} { puts "======" foreach line $lines { puts " $line" } puts "======" } proc code {text} { puts "======" foreach line [parse_code_block $text] { puts " $line" } puts "======" } proc nl {} { } proc section {text} { puts "'''$text'''" puts "" } proc subsection {text} { puts "''$text''" puts "" } proc bullet {text} { puts " * [joinlines $text]" } proc indent {text} { puts " : [joinlines $text]" } proc defn {first args} { if {$first ne ""} { indent '''$first''' } foreach p $args { p $p } } } ################################################################## # # Entry/Exit # if {$autosetup(debug)} { main $argv } if {[catch {main $argv} msg opts] == 1} { show-notices autosetup-full-error [error-dump $msg $opts $::autosetup(debug)] if {!$autosetup(debug)} { puts stderr "Try: '[file tail $autosetup(exe)] --debug' for a full stack trace" } exit 1 } |
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc-db' module provides a knowledge based of system idiosyncrasies # In general, this module can always be included use cc module-options {} # openbsd needs sys/types.h to detect some system headers cc-include-needs sys/socket.h sys/types.h cc-include-needs netinet/in.h sys/types.h |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # Provides a library of common tests on top of the 'cc' module. use cc module-options {} # @cc-check-lfs # # The equivalent of the AC_SYS_LARGEFILE macro # # defines 'HAVE_LFS' if LFS is available, # and defines '_FILE_OFFSET_BITS=64' if necessary # # Returns 1 if 'LFS' is available or 0 otherwise # proc cc-check-lfs {} { cc-check-includes sys/types.h msg-checking "Checking if -D_FILE_OFFSET_BITS=64 is needed..." set lfs 1 if {[msg-quiet cc-with {-includes sys/types.h} {cc-check-sizeof off_t}] == 8} { msg-result no } elseif {[msg-quiet cc-with {-includes sys/types.h -cflags -D_FILE_OFFSET_BITS=64} {cc-check-sizeof off_t}] == 8} { define _FILE_OFFSET_BITS 64 msg-result yes } else { set lfs 0 msg-result none } define-feature lfs $lfs return $lfs } # @cc-check-endian # # The equivalent of the AC_C_BIGENDIAN macro # # defines 'HAVE_BIG_ENDIAN' if endian is known to be big, # or 'HAVE_LITTLE_ENDIAN' if endian is known to be little. # # Returns 1 if determined, or 0 if not. # proc cc-check-endian {} { cc-check-includes sys/types.h sys/param.h set rc 0 msg-checking "Checking endian..." cc-with {-includes {sys/types.h sys/param.h}} { if {[cctest -code { #if !defined(BIG_ENDIAN) || !defined(BYTE_ORDER) #error unknown #elif BYTE_ORDER != BIG_ENDIAN #error little #endif }]} { define-feature big-endian msg-result "big" set rc 1 } elseif {[cctest -code { #if !defined(LITTLE_ENDIAN) || !defined(BYTE_ORDER) #error unknown #elif BYTE_ORDER != LITTLE_ENDIAN #error big #endif }]} { define-feature little-endian msg-result "little" set rc 1 } else { msg-result "unknown" } } return $rc } # @cc-check-flags flag ?...? # # Checks whether the given C/C++ compiler flags can be used. Defines feature # names prefixed with 'HAVE_CFLAG' and 'HAVE_CXXFLAG' respectively, and # appends working flags to '-cflags' and 'CFLAGS' or 'CXXFLAGS'. proc cc-check-flags {args} { set result 1 array set opts [cc-get-settings] switch -exact -- $opts(-lang) { c++ { set lang C++ set prefix CXXFLAG } c { set lang C set prefix CFLAG } default { autosetup-error "cc-check-flags failed with unknown language: $opts(-lang)" } } foreach flag $args { msg-checking "Checking whether the $lang compiler accepts $flag..." if {[cctest -cflags $flag]} { msg-result yes define-feature $prefix$flag cc-with [list -cflags [list $flag]] define-append ${prefix}S $flag } else { msg-result no set result 0 } } return $result } # @cc-check-standards ver ?...? # # Checks whether the C/C++ compiler accepts one of the specified '-std=$ver' # options, and appends the first working one to '-cflags' and 'CFLAGS' or # 'CXXFLAGS'. proc cc-check-standards {args} { array set opts [cc-get-settings] foreach std $args { if {[cc-check-flags -std=$std]} { return $std } } return "" } # Checks whether $keyword is usable as alignof proc cctest_alignof {keyword} { msg-checking "Checking for $keyword..." if {[cctest -code [subst -nobackslashes { printf("minimum alignment is %d == %d\n", ${keyword}(char), ${keyword}('x')); }]]} then { msg-result ok define-feature $keyword } else { msg-result "not found" } } # @cc-check-c11 # # Checks for several C11/C++11 extensions and their alternatives. Currently # checks for '_Static_assert', '_Alignof', '__alignof__', '__alignof'. proc cc-check-c11 {} { msg-checking "Checking for _Static_assert..." if {[cctest -code { _Static_assert(1, "static assertions are available"); }]} then { msg-result ok define-feature _Static_assert } else { msg-result "not found" } cctest_alignof _Alignof cctest_alignof __alignof__ cctest_alignof __alignof } # @cc-check-alloca # # The equivalent of the AC_FUNC_ALLOCA macro # # Checks for the existence of alloca # defines HAVE_ALLOCA and returns 1 if it exists proc cc-check-alloca {} { cc-check-some-feature alloca { cctest -includes alloca.h -code { alloca (2 * sizeof (int)); } } } # @cc-signal-return-type # # The equivalent of the AC_TYPE_SIGNAL macro # # defines RETSIGTYPE to int or void proc cc-signal-return-type {} { msg-checking "Checking return type of signal handlers..." cc-with {-includes {sys/types.h signal.h}} { if {[cctest -code {return *(signal (0, 0)) (0) == 1;}]} { set type int } else { set type void } define RETSIGTYPE $type msg-result $type } } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc-shared' module provides support for shared libraries and shared objects. # It defines the following variables: # ## SH_CFLAGS Flags to use compiling sources destined for a shared library ## SH_LDFLAGS Flags to use linking (creating) a shared library ## SH_SOPREFIX Prefix to use to set the soname when creating a shared library ## SH_SOEXT Extension for shared libs ## SH_SOEXTVER Format for versioned shared libs - %s = version ## SHOBJ_CFLAGS Flags to use compiling sources destined for a shared object ## SHOBJ_LDFLAGS Flags to use linking a shared object, undefined symbols allowed ## SHOBJ_LDFLAGS_R - as above, but all symbols must be resolved ## SH_LINKFLAGS Flags to use linking an executable which will load shared objects ## LD_LIBRARY_PATH Environment variable which specifies path to shared libraries ## STRIPLIBFLAGS Arguments to strip a dynamic library module-options {} # Defaults: gcc on unix define SHOBJ_CFLAGS -fpic define SHOBJ_LDFLAGS -shared define SH_CFLAGS -fpic define SH_LDFLAGS -shared define SH_LINKFLAGS -rdynamic define SH_SOEXT .so define SH_SOEXTVER .so.%s define SH_SOPREFIX -Wl,-soname, define LD_LIBRARY_PATH LD_LIBRARY_PATH define STRIPLIBFLAGS --strip-unneeded # Note: This is a helpful reference for identifying the toolchain # http://sourceforge.net/apps/mediawiki/predef/index.php?title=Compilers switch -glob -- [get-define host] { *-*-darwin* { define SHOBJ_CFLAGS "-dynamic -fno-common" define SHOBJ_LDFLAGS "-bundle -undefined dynamic_lookup" define SHOBJ_LDFLAGS_R -bundle define SH_CFLAGS -dynamic define SH_LDFLAGS -dynamiclib define SH_LINKFLAGS "" define SH_SOEXT .dylib define SH_SOEXTVER .%s.dylib define SH_SOPREFIX -Wl,-install_name, define LD_LIBRARY_PATH DYLD_LIBRARY_PATH define STRIPLIBFLAGS -x } *-*-ming* - *-*-cygwin - *-*-msys { define SHOBJ_CFLAGS "" define SHOBJ_LDFLAGS -shared define SH_CFLAGS "" define SH_LDFLAGS -shared define SH_LINKFLAGS "" define SH_SOEXT .dll define SH_SOEXTVER .dll define SH_SOPREFIX "" define LD_LIBRARY_PATH PATH } sparc* { if {[msg-quiet cc-check-decls __SUNPRO_C]} { msg-result "Found sun stdio compiler" # sun stdio compiler # XXX: These haven't been fully tested. define SHOBJ_CFLAGS -KPIC define SHOBJ_LDFLAGS "-G" define SH_CFLAGS -KPIC define SH_LINKFLAGS -Wl,-export-dynamic define SH_SOPREFIX -Wl,-h, } else { # sparc has a very small GOT table limit, so use -fPIC define SH_CFLAGS -fPIC define SHOBJ_CFLAGS -fPIC } } *-*-solaris* { if {[msg-quiet cc-check-decls __SUNPRO_C]} { msg-result "Found sun stdio compiler" # sun stdio compiler # XXX: These haven't been fully tested. define SHOBJ_CFLAGS -KPIC define SHOBJ_LDFLAGS "-G" define SH_CFLAGS -KPIC define SH_LINKFLAGS -Wl,-export-dynamic define SH_SOPREFIX -Wl,-h, } } *-*-hpux { # XXX: These haven't been tested define SHOBJ_CFLAGS "+O3 +z" define SHOBJ_LDFLAGS -b define SH_CFLAGS +z define SH_LINKFLAGS -Wl,+s define LD_LIBRARY_PATH SHLIB_PATH } *-*-haiku { define SHOBJ_CFLAGS "" define SHOBJ_LDFLAGS -shared define SH_CFLAGS "" define SH_LDFLAGS -shared define SH_LINKFLAGS "" define SH_SOPREFIX "" define LD_LIBRARY_PATH LIBRARY_PATH } microblaze* { # Microblaze generally needs -fPIC rather than -fpic define SHOBJ_CFLAGS -fPIC define SH_CFLAGS -fPIC } } if {![is-defined SHOBJ_LDFLAGS_R]} { define SHOBJ_LDFLAGS_R [get-define SHOBJ_LDFLAGS] } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 | # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'cc' module supports checking various 'features' of the C or C++ # compiler/linker environment. Common commands are cc-check-includes, # cc-check-types, cc-check-functions, cc-with, make-autoconf-h and make-template. # # The following environment variables are used if set: # ## CC - C compiler ## CXX - C++ compiler ## CCACHE - Set to "none" to disable automatic use of ccache ## CFLAGS - Additional C compiler flags ## CXXFLAGS - Additional C++ compiler flags ## LDFLAGS - Additional compiler flags during linking ## LIBS - Additional libraries to use (for all tests) ## CROSS - Tool prefix for cross compilation # # The following variables are defined from the corresponding # environment variables if set. # ## CPPFLAGS ## LINKFLAGS ## CC_FOR_BUILD ## LD use system module-options {} # Note that the return code is not meaningful proc cc-check-something {name code} { uplevel 1 $code } # Checks for the existence of the given function by linking # proc cctest_function {function} { cctest -link 1 -declare "extern void $function\(void);" -code "$function\();" } # Checks for the existence of the given type by compiling proc cctest_type {type} { cctest -code "$type _x;" } # Checks for the existence of the given type/structure member. # e.g. "struct stat.st_mtime" proc cctest_member {struct_member} { # split at the first dot regexp {^([^.]+)[.](.*)$} $struct_member -> struct member cctest -code "static $struct _s; return sizeof(_s.$member);" } # Checks for the existence of the given define by compiling # proc cctest_define {name} { cctest -code "#ifndef $name\n#error not defined\n#endif" } # Checks for the existence of the given name either as # a macro (#define) or an rvalue (such as an enum) # proc cctest_decl {name} { cctest -code "#ifndef $name\n(void)$name;\n#endif" } # @cc-check-sizeof type ... # # Checks the size of the given types (between 1 and 32, inclusive). # Defines a variable with the size determined, or "unknown" otherwise. # e.g. for type 'long long', defines SIZEOF_LONG_LONG. # Returns the size of the last type. # proc cc-check-sizeof {args} { foreach type $args { msg-checking "Checking for sizeof $type..." set size unknown # Try the most common sizes first foreach i {4 8 1 2 16 32} { if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} { set size $i break } } msg-result $size set define [feature-define-name $type SIZEOF_] define $define $size } # Return the last result get-define $define } # Checks for each feature in $list by using the given script. # # When the script is evaluated, $each is set to the feature # being checked, and $extra is set to any additional cctest args. # # Returns 1 if all features were found, or 0 otherwise. proc cc-check-some-feature {list script} { set ret 1 foreach each $list { if {![check-feature $each $script]} { set ret 0 } } return $ret } # @cc-check-includes includes ... # # Checks that the given include files can be used proc cc-check-includes {args} { cc-check-some-feature $args { set with {} if {[dict exists $::autosetup(cc-include-deps) $each]} { set deps [dict keys [dict get $::autosetup(cc-include-deps) $each]] msg-quiet cc-check-includes {*}$deps foreach i $deps { if {[have-feature $i]} { lappend with $i } } } if {[llength $with]} { cc-with [list -includes $with] { cctest -includes $each } } else { cctest -includes $each } } } # @cc-include-needs include required ... # # Ensures that when checking for 'include', a check is first # made for each 'required' file, and if found, it is #included proc cc-include-needs {file args} { foreach depfile $args { dict set ::autosetup(cc-include-deps) $file $depfile 1 } } # @cc-check-types type ... # # Checks that the types exist. proc cc-check-types {args} { cc-check-some-feature $args { cctest_type $each } } # @cc-check-defines define ... # # Checks that the given preprocessor symbol is defined proc cc-check-defines {args} { cc-check-some-feature $args { cctest_define $each } } # @cc-check-decls name ... # # Checks that each given name is either a preprocessor symbol or rvalue # such as an enum. Note that the define used is HAVE_DECL_xxx # rather than HAVE_xxx proc cc-check-decls {args} { set ret 1 foreach name $args { msg-checking "Checking for $name..." set r [cctest_decl $name] define-feature "decl $name" $r if {$r} { msg-result "ok" } else { msg-result "not found" set ret 0 } } return $ret } # @cc-check-functions function ... # # Checks that the given functions exist (can be linked) proc cc-check-functions {args} { cc-check-some-feature $args { cctest_function $each } } # @cc-check-members type.member ... # # Checks that the given type/structure members exist. # A structure member is of the form "struct stat.st_mtime" proc cc-check-members {args} { cc-check-some-feature $args { cctest_member $each } } # @cc-check-function-in-lib function libs ?otherlibs? # # Checks that the given function can be found in one of the libs. # # First checks for no library required, then checks each of the libraries # in turn. # # If the function is found, the feature is defined and lib_$function is defined # to -l$lib where the function was found, or "" if no library required. # In addition, -l$lib is prepended to the LIBS define. # # If additional libraries may be needed for linking, they should be specified # as $extralibs as "-lotherlib1 -lotherlib2". # These libraries are not automatically added to LIBS. # # Returns 1 if found or 0 if not. # proc cc-check-function-in-lib {function libs {otherlibs {}}} { msg-checking "Checking libs for $function..." set found 0 cc-with [list -libs $otherlibs] { if {[cctest_function $function]} { msg-result "none needed" define lib_$function "" incr found } else { foreach lib $libs { cc-with [list -libs -l$lib] { if {[cctest_function $function]} { msg-result -l$lib define lib_$function -l$lib # prepend to LIBS define LIBS "-l$lib [get-define LIBS]" incr found break } } } } } if {$found} { define [feature-define-name $function] } else { msg-result "no" } return $found } # @cc-check-tools tool ... # # Checks for existence of the given compiler tools, taking # into account any cross compilation prefix. # # For example, when checking for "ar", first AR is checked on the command # line and then in the environment. If not found, "${host}-ar" or # simply "ar" is assumed depending upon whether cross compiling. # The path is searched for this executable, and if found AR is defined # to the executable name. # Note that even when cross compiling, the simple "ar" is used as a fallback, # but a warning is generated. This is necessary for some toolchains. # # It is an error if the executable is not found. # proc cc-check-tools {args} { foreach tool $args { set TOOL [string toupper $tool] set exe [get-env $TOOL [get-define cross]$tool] if {[find-executable {*}$exe]} { define $TOOL $exe continue } if {[find-executable {*}$tool]} { msg-result "Warning: Failed to find $exe, falling back to $tool which may be incorrect" define $TOOL $tool continue } user-error "Failed to find $exe" } } # @cc-check-progs prog ... # # Checks for existence of the given executables on the path. # # For example, when checking for "grep", the path is searched for # the executable, 'grep', and if found GREP is defined as "grep". # # If the executable is not found, the variable is defined as false. # Returns 1 if all programs were found, or 0 otherwise. # proc cc-check-progs {args} { set failed 0 foreach prog $args { set PROG [string toupper $prog] msg-checking "Checking for $prog..." if {![find-executable $prog]} { msg-result no define $PROG false incr failed } else { msg-result ok define $PROG $prog } } expr {!$failed} } # Adds the given settings to $::autosetup(ccsettings) and # returns the old settings. # proc cc-add-settings {settings} { if {[llength $settings] % 2} { autosetup-error "settings list is missing a value: $settings" } set prev [cc-get-settings] # workaround a bug in some versions of jimsh by forcing # conversion of $prev to a list llength $prev array set new $prev foreach {name value} $settings { switch -exact -- $name { -cflags - -includes { # These are given as lists lappend new($name) {*}$value } -declare { lappend new($name) $value } -libs { # Note that new libraries are added before previous libraries set new($name) [list {*}$value {*}$new($name)] } -link - -lang - -nooutput { set new($name) $value } -source - -sourcefile - -code { # XXX: These probably are only valid directly from cctest set new($name) $value } default { autosetup-error "unknown cctest setting: $name" } } } cc-store-settings [array get new] return $prev } proc cc-store-settings {new} { set ::autosetup(ccsettings) $new } proc cc-get-settings {} { return $::autosetup(ccsettings) } # Similar to cc-add-settings, but each given setting # simply replaces the existing value. # # Returns the previous settings proc cc-update-settings {args} { set prev [cc-get-settings] cc-store-settings [dict merge $prev $args] return $prev } # @cc-with settings ?{ script }? # # Sets the given 'cctest' settings and then runs the tests in 'script'. # Note that settings such as -lang replace the current setting, while # those such as -includes are appended to the existing setting. # # If no script is given, the settings become the default for the remainder # of the auto.def file. # ## cc-with {-lang c++} { ## # This will check with the C++ compiler ## cc-check-types bool ## cc-with {-includes signal.h} { ## # This will check with the C++ compiler, signal.h and any existing includes. ## ... ## } ## # back to just the C++ compiler ## } # # The -libs setting is special in that newer values are added *before* earlier ones. # ## cc-with {-libs {-lc -lm}} { ## cc-with {-libs -ldl} { ## cctest -libs -lsocket ... ## # libs will be in this order: -lsocket -ldl -lc -lm ## } ## } proc cc-with {settings args} { if {[llength $args] == 0} { cc-add-settings $settings } elseif {[llength $args] > 1} { autosetup-error "usage: cc-with settings ?script?" } else { set save [cc-add-settings $settings] set rc [catch {uplevel 1 [lindex $args 0]} result info] cc-store-settings $save if {$rc != 0} { return -code [dict get $info -code] $result } return $result } } # @cctest ?settings? # # Low level C compiler checker. Compiles and or links a small C program # according to the arguments and returns 1 if OK, or 0 if not. # # Supported settings are: # ## -cflags cflags A list of flags to pass to the compiler ## -includes list A list of includes, e.g. {stdlib.h stdio.h} ## -declare code Code to declare before main() ## -link 1 Don't just compile, link too ## -lang c|c++ Use the C (default) or C++ compiler ## -libs liblist List of libraries to link, e.g. {-ldl -lm} ## -code code Code to compile in the body of main() ## -source code Compile a complete program. Ignore -includes, -declare and -code ## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file] ## -nooutput 1 Treat any compiler output (e.g. a warning) as an error # # Unless -source or -sourcefile is specified, the C program looks like: # ## #include <firstinclude> /* same for remaining includes in the list */ ## ## declare-code /* any code in -declare, verbatim */ ## ## int main(void) { ## code /* any code in -code, verbatim */ ## return 0; ## } # # Any failures are recorded in 'config.log' # proc cctest {args} { set src conftest__.c set tmp conftest__ # Easiest way to merge in the settings cc-with $args { array set opts [cc-get-settings] } if {[info exists opts(-sourcefile)]} { set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"] } if {[info exists opts(-source)]} { set lines $opts(-source) } else { foreach i $opts(-includes) { if {$opts(-code) ne "" && ![feature-checked $i]} { # Compiling real code with an unchecked header file # Quickly (and silently) check for it now # Remove all -includes from settings before checking set saveopts [cc-update-settings -includes {}] msg-quiet cc-check-includes $i cc-store-settings $saveopts } if {$opts(-code) eq "" || [have-feature $i]} { lappend source "#include <$i>" } } lappend source {*}$opts(-declare) lappend source "int main(void) {" lappend source $opts(-code) lappend source "return 0;" lappend source "}" set lines [join $source \n] } # Build the command line set cmdline {} lappend cmdline {*}[get-define CCACHE] switch -exact -- $opts(-lang) { c++ { lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS] } c { lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS] } default { autosetup-error "cctest called with unknown language: $opts(-lang)" } } if {$opts(-link)} { lappend cmdline {*}[get-define LDFLAGS] } else { set tmp conftest__.o lappend cmdline -c } lappend cmdline {*}$opts(-cflags) {*}[get-define cc-default-debug ""] lappend cmdline $src -o $tmp {*}$opts(-libs) if {$opts(-link)} { lappend cmdline {*}[get-define LIBS] } # At this point we have the complete command line and the # complete source to be compiled. Get the result from cache if # we can if {[info exists ::cc_cache($cmdline,$lines)]} { msg-checking "(cached) " set ok $::cc_cache($cmdline,$lines) if {$::autosetup(debug)} { configlog "From cache (ok=$ok): [join $cmdline]" configlog "============" configlog $lines configlog "============" } return $ok } writefile $src $lines\n set ok 1 set err [catch {exec-with-stderr {*}$cmdline} result errinfo] if {$err || ($opts(-nooutput) && [string length $result])} { configlog "Failed: [join $cmdline]" configlog $result configlog "============" configlog "The failed code was:" configlog $lines configlog "============" set ok 0 } elseif {$::autosetup(debug)} { configlog "Compiled OK: [join $cmdline]" configlog "============" configlog $lines configlog "============" } file delete $src file delete $tmp # cache it set ::cc_cache($cmdline,$lines) $ok return $ok } # @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*? # # Deprecated - see make-config-header proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} { user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead" make-config-header $file -auto $autopatterns -bare $barepatterns } # @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ... # # Examines all defined variables which match the given patterns # and writes an include file, $file, which defines each of these. # Variables which match '-auto' are output as follows: # - defines which have the value "0" are ignored. # - defines which have integer values are defined as the integer value. # - any other value is defined as a string, e.g. "value" # Variables which match '-bare' are defined as-is. # Variables which match '-str' are defined as a string, e.g. "value" # Variables which match '-none' are omitted. # # Note that order is important. The first pattern which matches is selected # Default behaviour is: # # -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none * # # If the file would be unchanged, it is not written. proc make-config-header {file args} { set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]] file mkdir [file dirname $file] set lines {} lappend lines "#ifndef $guard" lappend lines "#define $guard" # Add some defaults lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* foreach n [lsort [dict keys [all-defines]]] { set value [get-define $n] set type [calc-define-output-type $n $args] switch -exact -- $type { -bare { # Just output the value unchanged } -none { continue } -str { set value \"[string map [list \\ \\\\ \" \\\"] $value]\" } -auto { # Automatically determine the type if {$value eq "0"} { lappend lines "/* #undef $n */" continue } if {![string is integer -strict $value]} { set value \"[string map [list \\ \\\\ \" \\\"] $value]\" } } "" { continue } default { autosetup-error "Unknown type in make-config-header: $type" } } lappend lines "#define $n $value" } lappend lines "#endif" set buf [join $lines \n] write-if-changed $file $buf { msg-result "Created $file" } } proc calc-define-output-type {name spec} { foreach {type patterns} $spec { foreach pattern $patterns { if {[string match $pattern $name]} { return $type } } } return "" } # Initialise some values from the environment or commandline or default settings foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS {CFLAGS "-g -O2"}} { lassign $i var default define $var [get-env $var $default] } if {[env-is-set CC]} { # Set by the user, so don't try anything else set try [list [get-env CC ""]] } else { # Try some reasonable options set try [list [get-define cross]cc [get-define cross]gcc] } define CC [find-an-executable {*}$try] if {[get-define CC] eq ""} { user-error "Could not find a C compiler. Tried: [join $try ", "]" } define CPP [get-env CPP "[get-define CC] -E"] # XXX: Could avoid looking for a C++ compiler until requested # Note that if CXX isn't found, we just set it to "false". It might not be needed. if {[env-is-set CXX]} { define CXX [find-an-executable -required [get-env CXX ""]] } else { define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++ false] } # CXXFLAGS default to CFLAGS if not specified define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]] # May need a CC_FOR_BUILD, so look for one define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false] if {[get-define CC] eq ""} { user-error "Could not find a C compiler. Tried: [join $try ", "]" } define CCACHE [find-an-executable [get-env CCACHE ccache]] # Initial cctest settings cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0} set autosetup(cc-include-deps) {} msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS]" if {[get-define CXX] ne "false"} { msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS]" } msg-result "Build C compiler...[get-define CC_FOR_BUILD]" # On Darwin, we prefer to use -g0 to avoid creating .dSYM directories # but some compilers may not support it, so test here. switch -glob -- [get-define host] { *-*-darwin* { if {[cctest -cflags {-g0}]} { define cc-default-debug -g0 } } } if {![cc-check-includes stdlib.h]} { user-error "Compiler does not work. See config.log" } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 | #! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2014 Free Software Foundation, Inc. timestamp='2014-11-04' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/>. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD # # Please send patches to <config-patches@gnu.org>. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ; set_cc_for_build= ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case "${UNAME_SYSTEM}" in Linux|GNU|GNU/*) # If the system lacks a compiler, then just pick glibc. # We could probably try harder. LIBC=gnu eval $set_cc_for_build cat <<-EOF > $dummy.c #include <features.h> #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #else LIBC=gnu #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` ;; esac # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} exit ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} exit ;; *:ekkoBSD:*:*) echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} exit ;; *:SolidBSD:*:*) echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} exit ;; macppc:MirBSD:*:*) echo powerpc-unknown-mirbsd${UNAME_RELEASE} exit ;; *:MirBSD:*:*) echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} exit ;; alpha:OSF1:*:*) case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` # Reset EXIT trap before exiting to avoid spurious non-zero exit code. exitcode=$? trap '' 0 exit $exitcode ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit ;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit ;; *:OS/390:*:*) echo i370-ibm-openedition exit ;; *:z/VM:*:*) echo s390-ibm-zvmoe exit ;; *:OS400:*:*) echo powerpc-ibm-os400 exit ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit ;; arm*:riscos:*:*|arm*:RISCOS:*:*) echo arm-unknown-riscos exit ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7; exit ;; esac ;; s390x:SunOS:*:*) echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) echo i386-pc-auroraux${UNAME_RELEASE} exit ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) eval $set_cc_for_build SUN_ARCH="i386" # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH="x86_64" fi fi echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit ;; m68k:machten:*:*) echo m68k-apple-machten${UNAME_RELEASE} exit ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include <stdio.h> /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`$dummy $dummyarg` && { echo "$SYSTEM_NAME"; exit; } echo mips-mips-riscos${UNAME_RELEASE} exit ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include <sys/systemcfg.h> main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` then echo "$SYSTEM_NAME" else echo rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/lslpp ] ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit ;; *:AIX:*:*) echo rs6000-ibm-aix exit ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include <stdlib.h> #include <unistd.h> int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then eval $set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include <unistd.h> int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && { echo "$SYSTEM_NAME"; exit; } echo unknown-hitachi-hiuxwe2 exit ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; *:UNICOS/mp:*:*) echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`/usr/bin/uname -p` case ${UNAME_PROCESSOR} in amd64) echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; *) echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; esac exit ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit ;; *:MINGW64*:*) echo ${UNAME_MACHINE}-pc-mingw64 exit ;; *:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit ;; *:MSYS*:*) echo ${UNAME_MACHINE}-pc-msys exit ;; i*:windows32*:*) # uname -m includes "-pc" on this system. echo ${UNAME_MACHINE}-mingw32 exit ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit ;; *:Interix*:*) case ${UNAME_MACHINE} in x86) echo i586-pc-interix${UNAME_RELEASE} exit ;; authenticamd | genuineintel | EM64T) echo x86_64-unknown-interix${UNAME_RELEASE} exit ;; IA64) echo ia64-unknown-interix${UNAME_RELEASE} exit ;; esac ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit ;; 8664:Windows_NT:*) echo x86_64-pc-mks exit ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) echo x86_64-unknown-cygwin exit ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit ;; *:GNU:*:*) # the GNU system echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit ;; *:GNU/*:*:*) # other systems with GNU libc and userland echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} exit ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit ;; aarch64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC="gnulibc1" ; fi echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arc:Linux:*:* | arceb:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; arm*:Linux:*:*) eval $set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then echo ${UNAME_MACHINE}-unknown-linux-${LIBC} else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi else echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf fi fi exit ;; avr32*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; cris:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; crisv32:Linux:*:*) echo ${UNAME_MACHINE}-axis-linux-${LIBC} exit ;; frv:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; hexagon:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:Linux:*:*) echo ${UNAME_MACHINE}-pc-linux-${LIBC} exit ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m32r*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; mips:Linux:*:* | mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef ${UNAME_MACHINE} #undef ${UNAME_MACHINE}el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=${UNAME_MACHINE}el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=${UNAME_MACHINE} #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } ;; openrisc*:Linux:*:*) echo or1k-unknown-linux-${LIBC} exit ;; or32:Linux:*:* | or1k*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; padre:Linux:*:*) echo sparc-unknown-linux-${LIBC} exit ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-${LIBC} exit ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; *) echo hppa-unknown-linux-${LIBC} ;; esac exit ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-${LIBC} exit ;; ppc:Linux:*:*) echo powerpc-unknown-linux-${LIBC} exit ;; ppc64le:Linux:*:*) echo powerpc64le-unknown-linux-${LIBC} exit ;; ppcle:Linux:*:*) echo powerpcle-unknown-linux-${LIBC} exit ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux-${LIBC} exit ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; tile*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; vax:Linux:*:*) echo ${UNAME_MACHINE}-dec-linux-${LIBC} exit ;; x86_64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; xtensa*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-${LIBC} exit ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit ;; i*86:syllable:*:*) echo ${UNAME_MACHINE}-pc-syllable exit ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` echo ${UNAME_MACHINE}-pc-isc$UNAME_REL elif /bin/uname -X 2>/dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configury will decide that # this is a cross-build. echo i586-pc-msdosdjgpp exit ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit ;; paragon:*:*:*) echo i860-intel-osf1 exit ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says <Richard.M.Bartel@ccMail.Census.GOV> echo i586-unisys-sysv4 exit ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes <hewes@openmarket.com>. # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. echo ${UNAME_MACHINE}-stratus-vos exit ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. echo i586-pc-haiku exit ;; x86_64:Haiku:*:*) echo x86_64-unknown-haiku exit ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit ;; SX-7:SUPER-UX:*:*) echo sx7-nec-superux${UNAME_RELEASE} exit ;; SX-8:SUPER-UX:*:*) echo sx8-nec-superux${UNAME_RELEASE} exit ;; SX-8R:SUPER-UX:*:*) echo sx8r-nec-superux${UNAME_RELEASE} exit ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown eval $set_cc_for_build if test "$UNAME_PROCESSOR" = unknown ; then UNAME_PROCESSOR=powerpc fi if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi fi elif test "$UNAME_PROCESSOR" = i386 ; then # Avoid executing cc on OS X 10.9, as it ships with a stub # that puts up a graphical alert prompting to install # developer tools. Any system running Mac OS X 10.7 or # later (Darwin 11 and later) is required to have a 64-bit # processor. This is not true of the ARM version of Darwin # that Apple uses in portable devices. UNAME_PROCESSOR=x86_64 fi echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit ;; *:QNX:*:4*) echo i386-pc-qnx exit ;; NEO-?:NONSTOP_KERNEL:*:*) echo neo-tandem-nsk${UNAME_RELEASE} exit ;; NSE-*:NONSTOP_KERNEL:*:*) echo nse-tandem-nsk${UNAME_RELEASE} exit ;; NSR-?:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit ;; *:ITS:*:*) echo pdp10-unknown-its exit ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit ;; *:DragonFly:*:*) echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` exit ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case "${UNAME_MACHINE}" in A*) echo alpha-dec-vms ; exit ;; I*) echo ia64-dec-vms ; exit ;; V*) echo vax-dec-vms ; exit ;; esac ;; *:XENIX:*:SysV) echo i386-pc-xenix exit ;; i*86:skyos:*:*) echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' exit ;; i*86:rdos:*:*) echo ${UNAME_MACHINE}-pc-rdos exit ;; i*86:AROS:*:*) echo ${UNAME_MACHINE}-pc-aros exit ;; x86_64:VMkernel:*:*) echo ${UNAME_MACHINE}-unknown-esx exit ;; esac cat >&2 <<EOF $0: unable to guess system type This script, last modified $timestamp, has failed to recognize the operating system you are using. It is advised that you download the most up to date version of the config scripts from http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD and http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD If the version you run ($0) is already up to date, please send the following data and any information you think might be pertinent to <config-patches@gnu.org> in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 | #! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2014 Free Software Foundation, Inc. timestamp='2014-12-03' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/>. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to <config-patches@gnu.org>. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to <config-patches@gnu.org>." version="\ GNU config.sub ($timestamp) Copyright 1992-2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ knetbsd*-gnu* | netbsd*-gnu* | \ kopensolaris*-gnu* | \ storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; android-linux) os=-linux-android basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis | -knuth | -cray | -microblaze*) os= basic_machine=$1 ;; -bluegene*) os=-cnk ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco6) os=-sco5v6 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco5v6*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*178) os=-lynxos178 ;; -lynx*5) os=-lynxos5 ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | aarch64 | aarch64_be \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | am33_2.0 \ | arc | arceb \ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | avr | avr32 \ | be32 | be64 \ | bfin \ | c4x | c8051 | clipper \ | d10v | d30v | dlx | dsp16xx \ | epiphany \ | fido | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | hexagon \ | i370 | i860 | i960 | ia64 \ | ip2k | iq2000 \ | k1om \ | le32 | le64 \ | lm32 \ | m32c | m32r | m32rle | m68000 | m68k | m88k \ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64octeon | mips64octeonel \ | mips64orion | mips64orionel \ | mips64r5900 | mips64r5900el \ | mips64vr | mips64vrel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mips64vr5900 | mips64vr5900el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa32r6 | mipsisa32r6el \ | mipsisa64 | mipsisa64el \ | mipsisa64r2 | mipsisa64r2el \ | mipsisa64r6 | mipsisa64r6el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipsr5900 | mipsr5900el \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | moxie \ | mt \ | msp430 \ | nds32 | nds32le | nds32be \ | nios | nios2 | nios2eb | nios2el \ | ns16k | ns32k \ | open8 | or1k | or1knd | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle \ | pyramid \ | riscv32 | riscv64 \ | rl78 | rx \ | score \ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ | spu \ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ | ubicom32 \ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ | visium \ | we32k \ | x86 | xc16x | xstormy16 | xtensa \ | z8k | z80) basic_machine=$basic_machine-unknown ;; c54x) basic_machine=tic54x-unknown ;; c55x) basic_machine=tic55x-unknown ;; c6x) basic_machine=tic6x-unknown ;; leon|leon[3-9]) basic_machine=sparc-$basic_machine ;; m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; ms1) basic_machine=mt-unknown ;; strongarm | thumb | xscale) basic_machine=arm-unknown ;; xgate) basic_machine=$basic_machine-unknown os=-none ;; xscaleeb) basic_machine=armeb-unknown ;; xscaleel) basic_machine=armel-unknown ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | aarch64-* | aarch64_be-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* | avr32-* \ | be32-* | be64-* \ | bfin-* | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* \ | c8051-* | clipper-* | craynv-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | hexagon-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* | iq2000-* \ | k1om-* \ | le32-* | le64-* \ | lm32-* \ | m32c-* | m32r-* | m32rle-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ | microblaze-* | microblazeel-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64octeon-* | mips64octeonel-* \ | mips64orion-* | mips64orionel-* \ | mips64r5900-* | mips64r5900el-* \ | mips64vr-* | mips64vrel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mips64vr5900-* | mips64vr5900el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa32r6-* | mipsisa32r6el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64r2-* | mipsisa64r2el-* \ | mipsisa64r6-* | mipsisa64r6el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipsr5900-* | mipsr5900el-* \ | mipstx39-* | mipstx39el-* \ | mmix-* \ | mt-* \ | msp430-* \ | nds32-* | nds32le-* | nds32be-* \ | nios-* | nios2-* | nios2eb-* | nios2el-* \ | none-* | np1-* | ns16k-* | ns32k-* \ | open8-* \ | or1k*-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | pyramid-* \ | rl78-* | romp-* | rs6000-* | rx-* \ | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ | sparclite-* \ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \ | tahoe-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tile*-* \ | tron-* \ | ubicom32-* \ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ | vax-* \ | visium-* \ | we32k-* \ | x86-* | x86_64-* | xc16x-* | xps100-* \ | xstormy16-* | xtensa*-* \ | ymp-* \ | z8k-* | z80-*) ;; # Recognize the basic CPU types without company name, with glob match. xtensa*) basic_machine=$basic_machine-unknown ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; abacus) basic_machine=abacus-unknown ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amd64-*) basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aros) basic_machine=i386-pc os=-aros ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; blackfin) basic_machine=bfin-unknown os=-linux ;; blackfin-*) basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; bluegene*) basic_machine=powerpc-ibm os=-cnk ;; c54x-*) basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c55x-*) basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c6x-*) basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` ;; c90) basic_machine=c90-cray os=-unicos ;; cegcc) basic_machine=arm-unknown os=-cegcc ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; craynv) basic_machine=craynv-cray os=-unicosmp ;; cr16 | cr16-*) basic_machine=cr16-unknown os=-elf ;; crds | unos) basic_machine=m68k-crds ;; crisv32 | crisv32-* | etraxfs*) basic_machine=crisv32-axis ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; crx) basic_machine=crx-unknown os=-elf ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dicos) basic_machine=i686-pc os=-dicos ;; djgpp) basic_machine=i586-pc os=-msdosdjgpp ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; leon-*|leon[3-9]-*) basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` ;; m68knommu) basic_machine=m68k-unknown os=-linux ;; m68knommu-*) basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; microblaze*) basic_machine=microblaze-xilinx ;; mingw64) basic_machine=x86_64-pc os=-mingw64 ;; mingw32) basic_machine=i686-pc os=-mingw32 ;; mingw32ce) basic_machine=arm-unknown os=-mingw32ce ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; moxiebox) basic_machine=moxie-unknown os=-moxiebox ;; msdos) basic_machine=i386-pc os=-msdos ;; ms1-*) basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` ;; msys) basic_machine=i686-pc os=-msys ;; mvs) basic_machine=i370-ibm os=-mvs ;; nacl) basic_machine=le32-unknown os=-nacl ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; neo-tandem) basic_machine=neo-tandem ;; nse-tandem) basic_machine=nse-tandem ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; openrisc | openrisc-*) basic_machine=or32-unknown ;; os400) basic_machine=powerpc-ibm os=-os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; parisc) basic_machine=hppa-unknown os=-linux ;; parisc-*) basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` os=-linux ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pc98) basic_machine=i386-pc ;; pc98-*) basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc | ppcbe) basic_machine=powerpc-unknown ;; ppc-* | ppcbe-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rdos | rdos64) basic_machine=x86_64-pc os=-rdos ;; rdos32) basic_machine=i386-pc os=-rdos ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sde) basic_machine=mipsisa32-sde os=-elf ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh5el) basic_machine=sh5le-unknown ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; strongarm-* | thumb-*) basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tile*) basic_machine=$basic_machine-unknown os=-linux-gnu ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; tpf) basic_machine=s390x-ibm os=-tpf ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xbox) basic_machine=i686-pc os=-mingw32 ;; xps | xps100) basic_machine=xps100-honeywell ;; xscale-* | xscalee[bl]-*) basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; z80-*-coff) basic_machine=z80-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; mmix) basic_machine=mmix-knuth ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -auroraux) os=-auroraux ;; -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | -sym* | -kopensolaris* | -plan9* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* | -aros* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ | -bitrig* | -openbsd* | -solidbsd* \ | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* | -cegcc* \ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux-dietlibc) os=-linux-dietlibc ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -os400*) os=-os400 ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -syllable*) os=-syllable ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -tpf*) os=-tpf ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -zvmoe) os=-zvmoe ;; -dicos*) os=-dicos ;; -nacl*) ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in score-*) os=-elf ;; spu-*) os=-elf ;; *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; c8051-*) os=-elf ;; hexagon-*) os=-elf ;; tic54x-*) os=-coff ;; tic55x-*) os=-coff ;; tic6x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 ;; m68*-cisco) os=-aout ;; mep-*) os=-elf ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-haiku) os=-haiku ;; *-ibm) os=-aix ;; *-knuth) os=-mmixware ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -cnk*|-aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -os400*) vendor=ibm ;; -ptx*) vendor=sequent ;; -tpf*) vendor=ibm ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: |
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # Copyright (c) 2012 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Auto-load module for 'make' build system integration use init autosetup_add_init_type make {Simple "make" build system} { autosetup_check_create auto.def \ {# Initial auto.def created by 'autosetup --init=make' use cc # Add any user options here options { } make-config-header config.h make-template Makefile.in } if {![file exists Makefile.in]} { puts "Note: I don't see Makefile.in. You will probably need to create one." } } |
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #!/bin/sh # Looks for a suitable tclsh or jimsh in the PATH # If not found, builds a bootstrap jimsh from source d=`dirname "$0"` { "$d/jimsh0" "$d/test-tclsh"; } 2>/dev/null && exit 0 PATH="$PATH:$d"; export PATH for tclsh in jimsh tclsh tclsh8.5 tclsh8.6; do { $tclsh "$d/test-tclsh"; } 2>/dev/null && exit 0 done echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0" for cc in ${CC_FOR_BUILD:-cc} gcc; do { $cc -o "$d/jimsh0" "$d/jimsh0.c"; } 2>/dev/null || continue "$d/jimsh0" "$d/test-tclsh" && exit 0 done echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc." echo false |
more than 10,000 changes
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | # Copyright (c) 2016 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'pkg-config' module allows package information to be found via pkg-config # # If not cross-compiling, the package path should be determined automatically # by pkg-config. # If cross-compiling, the default package path is the compiler sysroot. # If the C compiler doesn't support -print-sysroot, the path can be supplied # by the --sysroot option or by defining SYSROOT. # # PKG_CONFIG may be set to use an alternative to pkg-config use cc module-options { sysroot:dir => "Override compiler sysroot for pkg-config search path" } # @pkg-config-init ?required? # # Initialises the pkg-config system. Unless required is set to 0, # it is a fatal error if the pkg-config # This command will normally be called automatically as required, # but it may be invoked explicitly if lack of pkg-config is acceptable. # # Returns 1 if ok, or 0 if pkg-config not found/usable (only if required=0) # proc pkg-config-init {{required 1}} { if {[is-defined HAVE_PKG_CONFIG]} { return [get-define HAVE_PKG_CONFIG] } set found 0 define PKG_CONFIG [get-env PKG_CONFIG pkg-config] msg-checking "Checking for pkg-config..." if {[catch {exec [get-define PKG_CONFIG] --version} version]} { msg-result "[get-define PKG_CONFIG] (not found)" if {$required} { user-error "No usable pkg-config" } } else { msg-result $version define PKG_CONFIG_VERSION $version set found 1 if {[opt-val sysroot] ne ""} { define SYSROOT [file-normalize [opt-val sysroot]] msg-result "Using specified sysroot [get-define SYSROOT]" } elseif {[get-define build] ne [get-define host]} { if {[catch {exec-with-stderr [get-define CC] -print-sysroot} result errinfo] == 0} { # Use the compiler sysroot, if there is one define SYSROOT $result msg-result "Found compiler sysroot $result" } else { set msg "pkg-config: Cross compiling, but no compiler sysroot and no --sysroot supplied" if {$required} { user-error $msg } else { msg-result $msg } set found 0 } } if {[is-defined SYSROOT]} { set sysroot [get-define SYSROOT] # XXX: It's possible that these should be set only when invoking pkg-config global env set env(PKG_CONFIG_DIR) "" # Do we need to try /usr/local as well or instead? set env(PKG_CONFIG_LIBDIR) $sysroot/usr/lib/pkgconfig:$sysroot/usr/share/pkgconfig set env(PKG_CONFIG_SYSROOT_DIR) $sysroot } } define HAVE_PKG_CONFIG $found return $found } # @pkg-config module ?requirements? # # Use pkg-config to find the given module meeting the given requirements. # e.g. # ## pkg-config pango >= 1.37.0 # # If found, returns 1 and sets HAVE_PKG_PANGO to 1 along with: # ## PKG_PANGO_VERSION to the found version ## PKG_PANGO_LIBS to the required libs (--libs-only-l) ## PKG_PANGO_LDFLAGS to the required linker flags (--libs-only-L) ## PKG_PANGO_CFLAGS to the required compiler flags (--cflags) # # If not found, returns 0. # proc pkg-config {module args} { set ok [pkg-config-init] msg-checking "Checking for $module $args..." if {!$ok} { msg-result "no pkg-config" return 0 } if {[catch {exec [get-define PKG_CONFIG] --modversion "$module $args"} version]} { msg-result "not found" configlog "pkg-config --modversion $module $args: $version" return 0 } msg-result $version set prefix [feature-define-name $module PKG_] define HAVE_${prefix} define ${prefix}_VERSION $version define ${prefix}_LIBS [exec pkg-config --libs-only-l $module] define ${prefix}_LDFLAGS [exec pkg-config --libs-only-L $module] define ${prefix}_CFLAGS [exec pkg-config --cflags $module] return 1 } # @pkg-config-get module setting # # Convenience access to the results of pkg-config # # For example, [pkg-config-get pango CFLAGS] returns # the value of PKG_PANGO_CFLAGS, or "" if not defined. proc pkg-config-get {module name} { set prefix [feature-define-name $module PKG_] get-define ${prefix}_${name} "" } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # This module supports common system interrogation and options # such as --host, --build, --prefix, and setting srcdir, builddir, and EXEEXT # # It also support the 'feature' naming convention, where searching # for a feature such as sys/type.h defines HAVE_SYS_TYPES_H # # It defines the following variables, based on --prefix unless overridden by the user: # ## datadir ## sysconfdir ## sharedstatedir ## localstatedir ## infodir ## mandir ## includedir # Do "define defaultprefix myvalue" to set the default prefix *before* the first "use" set defaultprefix [get-define defaultprefix /usr/local] module-options [subst -noc -nob { host:host-alias => {a complete or partial cpu-vendor-opsys for the system where the application will run (defaults to the same value as --build)} build:build-alias => {a complete or partial cpu-vendor-opsys for the system where the application will be built (defaults to the result of running config.guess)} prefix:dir => {the target directory for the build (defaults to '$defaultprefix')} # These (hidden) options are supported for autoconf/automake compatibility exec-prefix: bindir: sbindir: includedir: mandir: infodir: libexecdir: datadir: libdir: sysconfdir: sharedstatedir: localstatedir: maintainer-mode=0 dependency-tracking=0 }] # Returns 1 if exists, or 0 if not # proc check-feature {name code} { msg-checking "Checking for $name..." set r [uplevel 1 $code] define-feature $name $r if {$r} { msg-result "ok" } else { msg-result "not found" } return $r } # @have-feature name ?default=0? # # Returns the value of the feature if defined, or $default if not. # See 'feature-define-name' for how the feature name # is translated into the define name. # proc have-feature {name {default 0}} { get-define [feature-define-name $name] $default } # @define-feature name ?value=1? # # Sets the feature 'define' to the given value. # See 'feature-define-name' for how the feature name # is translated into the define name. # proc define-feature {name {value 1}} { define [feature-define-name $name] $value } # @feature-checked name # # Returns 1 if the feature has been checked, whether true or not # proc feature-checked {name} { is-defined [feature-define-name $name] } # @feature-define-name name ?prefix=HAVE_? # # Converts a name to the corresponding define, # e.g. sys/stat.h becomes HAVE_SYS_STAT_H. # # Converts * to P and all non-alphanumeric to underscore. # proc feature-define-name {name {prefix HAVE_}} { string toupper $prefix[regsub -all {[^a-zA-Z0-9]} [regsub -all {[*]} $name p] _] } # If $file doesn't exist, or it's contents are different than $buf, # the file is written and $script is executed. # Otherwise a "file is unchanged" message is displayed. proc write-if-changed {file buf {script {}}} { set old [readfile $file ""] if {$old eq $buf && [file exists $file]} { msg-result "$file is unchanged" } else { writefile $file $buf\n uplevel 1 $script } } # @make-template template ?outfile? # # Reads the input file <srcdir>/$template and writes the output file $outfile. # If $outfile is blank/omitted, $template should end with ".in" which # is removed to create the output file name. # # Each pattern of the form @define@ is replaced with the corresponding # define, if it exists, or left unchanged if not. # # The special value @srcdir@ is substituted with the relative # path to the source directory from the directory where the output # file is created, while the special value @top_srcdir@ is substituted # with the relative path to the top level source directory. # # Conditional sections may be specified as follows: ## @if name == value ## lines ## @else ## lines ## @endif # # Where 'name' is a defined variable name and @else is optional. # If the expression does not match, all lines through '@endif' are ignored. # # The alternative forms may also be used: ## @if name ## @if name != value # # Where the first form is true if the variable is defined, but not empty or 0 # # Currently these expressions can't be nested. # proc make-template {template {out {}}} { set infile [file join $::autosetup(srcdir) $template] if {![file exists $infile]} { user-error "Template $template is missing" } # Define this as late as possible define AUTODEPS $::autosetup(deps) if {$out eq ""} { if {[file ext $template] ne ".in"} { autosetup-error "make_template $template has no target file and can't guess" } set out [file rootname $template] } set outdir [file dirname $out] # Make sure the directory exists file mkdir $outdir # Set up srcdir and top_srcdir to be relative to the target dir define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir] define top_srcdir [relative-path $::autosetup(srcdir) $outdir] set mapping {} foreach {n v} [array get ::define] { lappend mapping @$n@ $v } set result {} foreach line [split [readfile $infile] \n] { if {[info exists cond]} { set l [string trimright $line] if {$l eq "@endif"} { unset cond continue } if {$l eq "@else"} { set cond [expr {!$cond}] continue } if {$cond} { lappend result $line } continue } if {[regexp {^@if\s+(\w+)(.*)} $line -> name expression]} { lassign $expression equal value set varval [get-define $name ""] if {$equal eq ""} { set cond [expr {$varval ni {"" 0}}] } else { set cond [expr {$varval eq $value}] if {$equal ne "=="} { set cond [expr {!$cond}] } } continue } lappend result $line } writefile $out [string map $mapping [join $result \n]]\n msg-result "Created [relative-path $out] from [relative-path $template]" } # build/host tuples and cross-compilation prefix set build [opt-val build] define build_alias $build if {$build eq ""} { define build [config_guess] } else { define build [config_sub $build] } set host [opt-val host] define host_alias $host if {$host eq ""} { define host [get-define build] set cross "" } else { define host [config_sub $host] set cross $host- } define cross [get-env CROSS $cross] set prefix [opt-val prefix $defaultprefix] # These are for compatibility with autoconf define target [get-define host] define prefix $prefix define builddir $autosetup(builddir) define srcdir $autosetup(srcdir) # Allow this to come from the environment define top_srcdir [get-env top_srcdir [get-define srcdir]] # autoconf supports all of these set exec_prefix [opt-val exec-prefix $prefix] define exec_prefix $exec_prefix foreach {name defpath} { bindir /bin sbindir /sbin libexecdir /libexec libdir /lib } { define $name [opt-val $name $exec_prefix$defpath] } foreach {name defpath} { datadir /share sysconfdir /etc sharedstatedir /com localstatedir /var infodir /share/info mandir /share/man includedir /include } { define $name [opt-val $name $prefix$defpath] } define SHELL [get-env SHELL [find-an-executable sh bash ksh]] # Windows vs. non-Windows switch -glob -- [get-define host] { *-*-ming* - *-*-cygwin - *-*-msys { define-feature windows define EXEEXT .exe } default { define EXEEXT "" } } # Display msg-result "Host System...[get-define host]" msg-result "Build System...[get-define build]" |
> > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | # A small Tcl script to verify that the chosen # interpreter works. Sometimes we might e.g. pick up # an interpreter for a different arch. # Outputs the full path to the interpreter if {[catch {info version} version] == 0} { # This is Jim Tcl if {$version >= 0.72} { # Ensure that regexp works regexp (a.*?) a puts [info nameofexecutable] exit 0 } } elseif {[catch {info tclversion} version] == 0} { if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} { puts [info nameofexecutable] exit 0 } } exit 1 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | # Copyright (c) 2016 WorkWare Systems http://www.workware.net.au/ # All rights reserved # Auto-load module for 'tmake' build system integration use init autosetup_add_init_type tmake "Tcl-based tmake build system" { autosetup_check_create auto.def \ {# Initial auto.def created by 'autosetup --init=tmake' # vim:set syntax=tcl: use cc cc-lib cc-db cc-shared use tmake # Add any user options here # Really want a --configure that takes over the rest of the command line options { } cc-check-tools ar ranlib set objdir [get-env BUILDDIR objdir] make-config-header $objdir/include/autoconf.h make-tmake-settings $objdir/settings.conf {[A-Z]*} } autosetup_check_create project.spec \ {# Initial project.spec created by 'autosetup --init=tmake' # vim:set syntax=tcl: define? DESTDIR _install # XXX If configure creates additional/different files than include/autoconf.h # that should be reflected here # We use [set AUTOREMAKE] here to avoid rebuilding settings.conf # if the AUTOREMAKE command changes Depends {settings.conf include/autoconf.h} auto.def -msg {note Configuring...} -do { run [set AUTOREMAKE] >$build/config.out } -onerror {puts [readfile $build/config.out]} -fatal Clean config.out DistClean --source config.log DistClean settings.conf include/autoconf.h # If not configured, configure with default options # Note that it is expected that configure will normally be run # separately. This is just a convenience for a host build define? AUTOREMAKE configure TOPBUILDDIR=$TOPBUILDDIR --conf=auto.def Load settings.conf # e.g. for up autoconf.h IncludePaths include ifconfig CONFIGURED # Hmmm, but should we turn off AutoSubDirs? #AutoSubDirs off } if {![file exists build.spec]} { puts "Note: I don't see build.spec. Try running: tmake --genie" } } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | # Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/ # All rights reserved # @synopsis: # # The 'tmake' module makes it easy to support the tmake build system. # # The following variables are set: # ## CONFIGURED - to indicate that the project is configured use system module-options {} define CONFIGURED # @make-tmake-settings outfile patterns ... # # Examines all defined variables which match the given patterns (defaults to "*") # and writes a tmake-compatible .conf file defining those variables. # For example, if ABC is "3 monkeys" and ABC matches a pattern, then the file will include: # ## define ABC {3 monkeys} # # If the file would be unchanged, it is not written. # # Typical usage is: # # make-tmake-settings [get-env BUILDDIR objdir]/settings.conf {[A-Z]*} proc make-tmake-settings {file args} { file mkdir [file dirname $file] set lines {} if {[llength $args] == 0} { set args * } foreach n [lsort [dict keys [all-defines]]] { foreach p $args { if {[string match $p $n]} { set value [get-define $n] lappend lines "define $n [list $value]" break } } } set buf [join $lines \n] write-if-changed $file $buf { msg-result "Created $file" } } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #!/bin/sh ######################################################################## # pidp8i.in - Attach the current terminal to the screen(1) session # started by the SysV init script in etc/pidp8i-ini. # # Copyright © 2015-2017 Oscar Vermeulen and Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## if [ "$USER" != "@INSTUSR@" ] ; then exec su -c "$0" @INSTUSR@ ; fi procs=`screen -ls pidp8i | egrep '[0-9]+\.pidp8i' | wc -l` if [ $procs -ne 0 ]; then echo Joining simulator session already in progress... screen -r else cat <<ERROR Either the simulator isn't running, or it isn't running under a screen(1) session owned by @INSTUSR@. Did you start it via the init script? ERROR fi |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ; This script initializes a populated OS/8 environment on an ; RK05 cartridge disk pack. That's 10 whole megabytes, so big ; the OS requires that you split it into two partitions in order ; to address the whole disk! Thus the "RKB0:" references you ; will find in tutorials, as that refers to the second half ("B") ; of the first ("0") RK05 cartridge disk. The default location ; the OS uses is formally called "RKA0:", alias "SYS:". ; ; See 3.script if you want to load OS/8 via DECtape instead. ; ; This file is read on simulator startup when the IF switches are ; set to 0, so it defines the default starting environment for the ; simulator. ; ; If you need to soft-restart the simulator back into OS/8 from some ; other state -- that is, you initially started the simulator with ; IF != 0 -- you can set IF = 7 and toggle the Sing_Step switch on ; and back off, which restarts the simulator with 7.script, which in ; turn loads this one. This somewhat clumsy arrangement is required ; because toggling Sing_Step with IF = 0 must not be made to restart ; the simulator, else there would be no way to use Sing_Step for any ; of its other functions. See the PiDP-8/I instructions for details. ; reset echo Loading OS/8 from the RK05 cartridge disk... set cpu 32k set cpu noidle @SET_THROTTLE@ att rk0 @MEDIADIR@/os8/os8.rk05 boot rk0 |
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | ; This script initializes a populated TSS/8 environment on an ; RS08 fixed-head hard disk drive (384 kB!) controlled by the ; RF08 disk controller. ; echo Loading TSS/8 from the RS08 fixed-head disk... load @MEDIADIR@/tss8/tss8_init.bin set rf enabled set df disabled @SET_THROTTLE@ attach rf @MEDIADIR@/tss8/tss8_rf.dsk attach ttix 4000 run 24200 |
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | ; Loads OS/8 from DECtape, as opposed to the RK05 based environment ; in 0.script. ; echo Loading OS/8 from DECtape... set dt disabled set td enabled @SET_THROTTLE@ attach td0 @MEDIADIR@/os8/os8.tu56 boot td0 |
> > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ; This script loads Spacewar! directly into core, without a supporting ; OS. ; echo Spacewar! echo echo Keyboard controls from the VC8E program: echo echo Player 1 Player 2 Command echo 1 9 fire weapon echo 2 0 rotate CCW echo 3 - rotate CW echo 4 = thrusters echo echo Press both rotate keys simulatenously to warp into hyperspace. echo echo Press Ctrl-E to pause the simulator and return to the SimH echo command prompt, where you can say "quit", "help" and other echo things. See the SimH manual for details. echo l @MEDIADIR@/spacewar/spacewar.bin @SET_THROTTLE@ at ttix 2222 set ttox0 8b g 200 |
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | ; This script loads ETOS V5B from the RK05 cartridge disk drive. ; echo Loading ETOS from the RK05 cartridge disk drive... reset set cpu 32k set cpu noidle @SET_THROTTLE@ set tsc enabled attach ttix 4000 att rk0 @MEDIADIR@/etos/etosv5b-demo.rk05 boot -d rk0 |
> > | 1 2 | echo Restarting the PiDP-8/I into its default operating environment... do @BOOTDIR@/0.script |
> > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | On startup, scanswitch passes the script number to run on to pidp8, based on the IF switch settings. Standard boot scripts: IF switches filename description ---------------------------------------------------------------------- 000 0.script : OS/8 on 32K PDP-8 with RK05 disk cartridge 001 1.script : RIM Loader installed at 7756 010 2.script : TSS/8 011 3.script : OS/8 on DECtape. This uses SLOW td0, not dt0 100 4.script : spacewar! with vc8e output on localhost:2222 101 5.script : (empty) 110 6.script : ETOS Multi-user on OS/8 boot disk 111 7.script : OS/8. Same as 0.script. ---------------------------------------------------------------------- Default initial startup script - when no IF switches are set is 0.script |
> > > | 1 2 3 | #!/bin/sh dir="`dirname "$0"`/autosetup" WRAPPER="$0"; export WRAPPER; exec "`$dir/find-tclsh`" "$dir/autosetup" "$@" |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | /*********************************************************************** led-decay.cpp - Proof of concept for an LED brightness decay algorithm that simulates the appearance of incandescent light bulbs when fed a stream of samples over a given time period. Copyright © 2016 Warren Young Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of the authors above shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from those authors. ***********************************************************************/ #include <iostream> #include <vector> using namespace std; typedef vector<bool> vb; typedef vb::const_iterator vbc; // Keeping n samples. To think about this concretely, imagine that it // is 1 sample per millisecond over 0.1 sec, but realize that this is // scaleable, so that how long 1/n seconds is doesn't affect the math. static const size_t n = 100; // Decay function is 1 - x^2, meaning the most recent event is // considered 100%, with older events having increasingly lesser effect // on the overall brightness until we hit 0% consideration at the end of // the sample set. // // We need to scale that so that the total area under the decay // function's curve is 1, so that if we feed a 50% duty cycle in, we // get 50% out, but if we skew the 1s toward the front of the sample // set (i.e. closer to "now"), we get greater brightness than if they // are skewed toward the past. // // The Pi ships with Mathematica, which answers this question with: // // Solve[Integrate[z * (1 - x^2), {x, 0, 1}] == 1, z] // // We get z = 1.5. // // If you want a different decay function, it needs to substitute for // the 1 - x^2 bit. It needs to start at 1 and decay to 0 over the // range [0 <= x <= 1]. Run that through Mathematica to find the // resulting value of z that gives a total of 1 over the sample span. static double f(double x, bool v) { return v ? (1.5 * (1 - x * x)) : 0; } // Given n bits representing the state of the LED at time x=1/n, return // the total of applications of f on each bit. Order is most recent // event first, so it takes the strongest effect. static double cdf(const vb& vl) { double t = 0; for (size_t i = 0; i < n; ++i) { // We divide each f() return by n because it represents only 1/n // of the total area under the curve. This is a crude form of // numeric integration. t += f(i / double(n), vl[i]) / n; } return t; } // Generate a series of sampled LED values, then run those sample sets // through the above and show what brightness level that would generate. int main() { vb values(n); values.clear(); for (size_t i = 0; i < n; ++i) { values.push_back(true); } cout << "100% duty cycle: CDF = " << cdf(values) << endl; values.clear(); for (size_t i = 0; i < n; ++i) { values.push_back(i % 2 == 0); } cout << "50% duty cycle: CDF = " << cdf(values) << endl; values.clear(); for (size_t i = 0; i < n; ++i) { values.push_back(i % 4 == 0); } cout << "25% duty cycle: CDF = " << cdf(values) << endl; values.clear(); for (size_t i = 0; i < n; ++i) { values.push_back(i % 10 == 0); } cout << "10% duty cycle: CDF = " << cdf(values) << endl; values.assign(n, false); for (size_t i = 0; i < n / 2; ++i) { values[i] = true; } cout << "First half 'on': CDF = " << cdf(values) << endl; values.assign(n, false); for (size_t i = n / 2; i < n; ++i) { values[i] = true; } cout << "Second half 'on': CDF = " << cdf(values) << endl; values.assign(n, false); values[0] = true; cout << "1ms spike at the start: CDF = " << cdf(values) << endl; values.assign(n, false); values[n - 1] = true; cout << "1ms spike at the end: CDF = " << cdf(values) << endl; } |
cannot compute difference between binary files
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="673.51404" height="613.08057" viewBox="0 0 673.51405 613.08058" id="svg2" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="vtedit-keypad.svg" inkscape:export-filename="/usr/local/src/pidp8i/trunk/doc/vtedit-keypad.png" inkscape:export-xdpi="80.18" inkscape:export-ydpi="80.18"> <title id="title4295">VTEDIT Keypad Diagram</title> <defs id="defs4"> <linearGradient inkscape:collect="always" id="linearGradient4138"> <stop style="stop-color:#dbb56a;stop-opacity:0.70833331" offset="0" id="stop4140" /> <stop style="stop-color:#f0e6da;stop-opacity:1" offset="1" id="stop4142" /> </linearGradient> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4144" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" gradientUnits="userSpaceOnUse" gradientTransform="translate(-72.931483,8.918583)" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4254" gradientUnits="userSpaceOnUse" gradientTransform="translate(39.142981,8.918583)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4272" gradientUnits="userSpaceOnUse" gradientTransform="translate(151.21744,8.918583)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4324" gradientUnits="userSpaceOnUse" gradientTransform="translate(-72.931483,120.43745)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4326" gradientUnits="userSpaceOnUse" gradientTransform="translate(39.142981,120.43745)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4328" gradientUnits="userSpaceOnUse" gradientTransform="translate(151.21744,120.43745)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4380" gradientUnits="userSpaceOnUse" gradientTransform="translate(-72.931483,231.9563)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4382" gradientUnits="userSpaceOnUse" gradientTransform="translate(39.142981,231.9563)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4384" gradientUnits="userSpaceOnUse" gradientTransform="translate(151.21744,231.9563)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4434" gradientUnits="userSpaceOnUse" gradientTransform="translate(263.2919,8.761662)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4436" gradientUnits="userSpaceOnUse" gradientTransform="translate(263.2919,120.1236)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4438" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1,0,0,2.1178002,263.2919,83.80932)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4504" gradientUnits="userSpaceOnUse" gradientTransform="matrix(2.1255986,0,0,1,-190.0452,343.47519)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4508" gradientUnits="userSpaceOnUse" gradientTransform="translate(151.21744,343.47519)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4611" gradientUnits="userSpaceOnUse" gradientTransform="translate(-72.931483,-102.60028)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4613" gradientUnits="userSpaceOnUse" gradientTransform="translate(39.142981,-102.60028)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4615" gradientUnits="userSpaceOnUse" gradientTransform="translate(151.21744,-102.60028)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> <linearGradient inkscape:collect="always" xlink:href="#linearGradient4138" id="linearGradient4617" gradientUnits="userSpaceOnUse" gradientTransform="translate(263.2919,-102.60028)" x1="153.5432" y1="135.14371" x2="154.55334" y2="231.10818" /> </defs> <sodipodi:namedview id="base" pagecolor="#fcfaf0" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="1" inkscape:pageshadow="2" inkscape:zoom="1.4" inkscape:cx="237.26909" inkscape:cy="345.9566" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="2560" inkscape:window-height="1391" inkscape:window-x="0" inkscape:window-y="1" inkscape:window-maximized="1" showguides="true" inkscape:guide-bbox="true" units="px" fit-margin-top="32" fit-margin-left="32" fit-margin-right="32" fit-margin-bottom="32" /> <metadata id="metadata7"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title>VTEDIT Keypad Diagram</dc:title> <cc:license rdf:resource="https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md" /> <dc:date>December 2016</dc:date> <dc:creator> <cc:Agent> <dc:title>Warren Young</dc:title> </cc:Agent> </dc:creator> <dc:contributor> <cc:Agent> <dc:title>This graphical diagram was created based on an ASCII diagram that came with the version of VTEDIT patched for VT100/ANSI terminals.</dc:title> </cc:Agent> </dc:contributor> <dc:rights> <cc:Agent> <dc:title>see ../SIMH-LICENSE.md</dc:title> </cc:Agent> </dc:rights> <dc:language>Englis</dc:language> <dc:subject> <rdf:Bag> <rdf:li>VTEDIT</rdf:li> <rdf:li>PDP-8</rdf:li> <rdf:li>diagram</rdf:li> <rdf:li>keypad</rdf:li> </rdf:Bag> </dc:subject> <dc:description>Diagram showing the function of the keypad keys when pressed in the version of VTEDIT patched for VT100/ANSI terminals.</dc:description> </cc:Work> </rdf:RDF> </metadata> <g inkscape:groupmode="layer" id="layer5" inkscape:label="key caps" style="display:inline" transform="translate(17.282791,22)"> <rect transform="translate(-14.897034,-18.012959)" style="opacity:1;fill:url(#linearGradient4144);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4136" width="98.994949" height="100.0051" x="31.114243" y="141.03181" ry="10.357149" rx="11.25" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.25" ry="10.357149" y="141.03181" x="143.18871" height="100.0051" width="98.994949" id="rect4240" style="opacity:1;fill:url(#linearGradient4254);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" style="opacity:1;fill:url(#linearGradient4272);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4258" width="98.994949" height="100.0051" x="255.26317" y="141.03181" ry="10.357149" rx="11.25" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.25" ry="10.357149" y="252.55069" x="31.114243" height="100.0051" width="98.994949" id="rect4278" style="opacity:1;fill:url(#linearGradient4324);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" style="opacity:1;fill:url(#linearGradient4326);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4294" width="98.994949" height="100.0051" x="143.18871" y="252.55069" ry="10.357149" rx="11.25" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.25" ry="10.357149" y="252.55069" x="255.26317" height="100.0051" width="98.994949" id="rect4310" style="opacity:1;fill:url(#linearGradient4328);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" style="opacity:1;fill:url(#linearGradient4380);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4334" width="98.994949" height="100.0051" x="31.114243" y="364.06955" ry="10.357149" rx="11.25" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.25" ry="10.357149" y="364.06955" x="143.18871" height="100.0051" width="98.994949" id="rect4350" style="opacity:1;fill:url(#linearGradient4382);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" style="opacity:1;fill:url(#linearGradient4384);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4366" width="98.994949" height="100.0051" x="255.26317" y="364.06955" ry="10.357149" rx="11.25" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.25" ry="10.357149" y="140.87489" x="367.33762" height="100.0051" width="98.994949" id="rect4388" style="opacity:1;fill:url(#linearGradient4434);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" style="opacity:1;fill:url(#linearGradient4436);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4404" width="98.994949" height="100.0051" x="367.33762" y="252.23683" ry="10.357149" rx="11.25" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.25" ry="10.35715" y="363.59879" x="367.33762" height="211.79082" width="98.994949" id="rect4420" style="opacity:1;fill:url(#linearGradient4438);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3.00000024;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.249999" ry="10.357149" y="475.58841" x="31.114243" height="100.0051" width="210.42351" id="rect4442" style="opacity:1;fill:url(#linearGradient4504);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:2.99999976;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.25" ry="10.357149" y="475.58844" x="255.26317" height="100.0051" width="98.994949" id="rect4474" style="opacity:1;fill:url(#linearGradient4508);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.25" ry="10.357149" y="29.512959" x="31.114243" height="100.0051" width="98.994949" id="rect4549" style="opacity:1;fill:url(#linearGradient4611);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" style="opacity:1;fill:url(#linearGradient4613);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4565" width="98.994949" height="100.0051" x="143.18871" y="29.512959" ry="10.357149" rx="11.25" /> <rect transform="translate(-14.897034,-18.012959)" rx="11.25" ry="10.357149" y="29.512959" x="255.26317" height="100.0051" width="98.994949" id="rect4581" style="opacity:1;fill:url(#linearGradient4615);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> <rect transform="translate(-14.897034,-18.012959)" style="opacity:1;fill:url(#linearGradient4617);fill-opacity:1;fill-rule:nonzero;stroke:#eee0ca;stroke-width:3;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4597" width="98.994949" height="100.0051" x="367.33762" y="29.512959" ry="10.357149" rx="11.25" /> </g> <g inkscape:groupmode="layer" id="layer6" inkscape:label="key labels" style="display:inline" transform="translate(17.282791,22)"> <flowRoot xml:space="preserve" id="flowRoot4217" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" transform="translate(-86.06075,-4.043613)"><flowRegion id="flowRegion4219"><rect id="rect4221" width="86.785713" height="87.678574" x="110" y="137.71935" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara id="flowPara4223" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">7</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4227">Open</flowPara><flowPara id="flowPara4623" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Line◆</flowPara></flowRoot> <flowRoot transform="translate(26.013714,-4.043613)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4242" xml:space="preserve"><flowRegion id="flowRegion4244"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4246" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4248">8</flowPara><flowPara id="flowPara4252" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Page◆</flowPara></flowRoot> <flowRoot xml:space="preserve" id="flowRoot4260" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" transform="translate(138.08818,-4.043613)"><flowRegion id="flowRegion4262"><rect id="rect4264" width="86.785713" height="87.678574" x="110" y="137.71935" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara id="flowPara4266" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">9</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4270">Mark/</flowPara><flowPara id="flowPara4629" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Quote◆</flowPara></flowRoot> <flowRoot transform="translate(-86.06075,107.47526)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4280" xml:space="preserve"><flowRegion id="flowRegion4282"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4284" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4286">4</flowPara><flowPara id="flowPara4290" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Up</flowPara><flowPara id="flowPara4633" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Line◆</flowPara></flowRoot> <flowRoot xml:space="preserve" id="flowRoot4296" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" transform="translate(26.013714,107.47526)"><flowRegion id="flowRegion4298"><rect id="rect4300" width="86.785713" height="87.678574" x="110" y="137.71935" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara id="flowPara4302" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">5</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4306">Delete</flowPara><flowPara id="flowPara4637" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Char◆</flowPara></flowRoot> <flowRoot transform="translate(138.08818,107.47526)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4312" xml:space="preserve"><flowRegion id="flowRegion4314"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4316" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4318">6</flowPara><flowPara id="flowPara4322" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Delete/</flowPara><flowPara id="flowPara4641" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Restore</flowPara></flowRoot> <flowRoot xml:space="preserve" id="flowRoot4336" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" transform="translate(-86.06075,218.99411)"><flowRegion id="flowRegion4338"><rect id="rect4340" width="86.785713" height="87.678574" x="110" y="137.71935" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara id="flowPara4342" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">1</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4344">Top of</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4346">Page◆•</flowPara></flowRoot> <flowRoot transform="translate(26.013714,218.99411)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4352" xml:space="preserve"><flowRegion id="flowRegion4354"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4356" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4358">2</flowPara><flowPara id="flowPara4360" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Bottom</flowPara><flowPara id="flowPara4362" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">of Page</flowPara></flowRoot> <flowRoot xml:space="preserve" id="flowRoot4368" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" transform="translate(138.08818,218.99411)"><flowRegion id="flowRegion4370"><rect id="rect4372" width="86.785713" height="87.678574" x="110" y="137.71935" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara id="flowPara4374" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">3</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4376">Start</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4378">of Line</flowPara></flowRoot> <flowRoot transform="translate(250.16264,-4.200534)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4390" xml:space="preserve"><flowRegion id="flowRegion4392"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4394" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4396">-</flowPara><flowPara id="flowPara4398" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Search</flowPara><flowPara id="flowPara4400" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Arg◆</flowPara></flowRoot> <flowRoot xml:space="preserve" id="flowRoot4406" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" transform="translate(250.16264,107.16141)"><flowRegion id="flowRegion4408"><rect id="rect4410" width="86.785713" height="87.678574" x="110" y="137.71935" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara id="flowPara4412" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">,</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4414">End of</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4416">Line</flowPara></flowRoot> <flowRoot transform="translate(250.16264,276.73764)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4422" xml:space="preserve"><flowRegion id="flowRegion4424"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4426" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4428">Enter</flowPara><flowPara id="flowPara4430" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Search</flowPara><flowPara id="flowPara4432" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Arg◆</flowPara></flowRoot> <flowRoot transform="translate(-33.203607,330.513)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4444" xml:space="preserve"><flowRegion id="flowRegion4446"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4448" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4450">0</flowPara><flowPara id="flowPara4454" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Down</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4523">Line◆</flowPara></flowRoot> <flowRoot transform="translate(138.08818,330.513)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4476" xml:space="preserve"><flowRegion id="flowRegion4478"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4480" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4482">.</flowPara><flowPara id="flowPara4484" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Search</flowPara><flowPara id="flowPara4486" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Again◆</flowPara></flowRoot> <flowRoot transform="translate(-86.06075,-115.56247)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4551" xml:space="preserve"><flowRegion id="flowRegion4553"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4555" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4557">PF1</flowPara><flowPara id="flowPara4559" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Save</flowPara><flowPara id="flowPara4561" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Text◆•</flowPara></flowRoot> <flowRoot xml:space="preserve" id="flowRoot4567" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" transform="translate(26.013714,-115.56247)"><flowRegion id="flowRegion4569"><rect id="rect4571" width="86.785713" height="87.678574" x="110" y="137.71935" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara id="flowPara4573" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">PF2</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4575">TECO</flowPara><flowPara style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4577">Cmd◆</flowPara></flowRoot> <flowRoot transform="translate(138.08818,-115.56247)" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="flowRoot4583" xml:space="preserve"><flowRegion id="flowRegion4585"><rect y="137.71935" x="110" height="87.678574" width="86.785713" id="rect4587" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4589">PF3</flowPara><flowPara id="flowPara4591" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Paste</flowPara><flowPara id="flowPara4593" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1">Text</flowPara></flowRoot> <flowRoot xml:space="preserve" id="flowRoot4599" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" transform="translate(250.16264,-115.56247)"><flowRegion id="flowRegion4601"><rect id="rect4603" width="86.785713" height="87.678574" x="110" y="137.71935" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" /></flowRegion><flowPara style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:22.5px;font-family:Verdana;-inkscape-font-specification:Verdana;text-align:center;text-anchor:middle;fill:#422b00;fill-opacity:1" id="flowPara4609">PF4</flowPara></flowRoot> </g> <g inkscape:label="KEY" inkscape:groupmode="layer" id="layer1" transform="translate(2.3857574,3.9870415)" style="display:inline"> <text xml:space="preserve" style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="153.57143" y="180.93362" id="text4213" sodipodi:linespacing="125%"><tspan sodipodi:role="line" id="tspan4215" x="153.57143" y="180.93362" /></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="503.61438" y="56.824276" id="text4643" sodipodi:linespacing="125%"><tspan sodipodi:role="line" x="503.61438" y="56.824276" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:20px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" id="tspan4701">KEY</tspan></text> <text sodipodi:linespacing="125%" id="text5371" y="183.68143" x="503.61438" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" xml:space="preserve"><tspan id="tspan5385" dy="1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" y="183.68143" x="503.61438" sodipodi:role="line">• command</tspan><tspan id="tspan5387" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" y="205.55643" x="503.61438" sodipodi:role="line"> operates</tspan><tspan id="tspan5389" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" y="227.43143" x="503.61438" sodipodi:role="line"> from Dot to</tspan><tspan id="tspan5391" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" y="249.30643" x="503.61438" sodipodi:role="line"> Mark if Mark</tspan><tspan id="tspan5393" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" y="271.18143" x="503.61438" sodipodi:role="line"> is set</tspan></text> <text xml:space="preserve" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:Verdana;-inkscape-font-specification:Verdana;letter-spacing:0px;word-spacing:0px;opacity:1;fill:#422b00;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="503.61438" y="88.967133" id="text5395" sodipodi:linespacing="125%"><tspan sodipodi:role="line" x="503.61438" y="88.967133" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" id="tspan5399">◆ takes opt arg</tspan><tspan sodipodi:role="line" x="503.61438" y="111.48743" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" id="tspan5401"> as: ESC [-]</tspan><tspan sodipodi:role="line" x="503.61438" y="133.36243" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" id="tspan5403"> <digits></tspan><tspan sodipodi:role="line" x="503.61438" y="155.23743" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" id="tspan5405"> <key(s)></tspan><tspan sodipodi:role="line" x="503.61438" y="177.11243" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:17.5px;font-family:Verdana;-inkscape-font-specification:Verdana;fill:#422b00;fill-opacity:1" id="tspan5417" /></text> </g> </svg> |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | #!/bin/sh ### BEGIN INIT INFO # Provides: pidp8i # Required-Start: $syslog # Required-Stop: $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 6 # Short-Description:PiDP-8/I simulator # Description: The PiDP-8/I simulator is a modified version of # the SimH PDP-8 simulator for the PiDP-8/I front # panel project for the Raspberry Pi. ### END INIT INFO ######################################################################## # Init script for Oscar Vermeulen's PiDP-8/I emulator front panel. # # Original author: Mark G Thomas <mark@misty.com> 2015-05-09 # # Copyright © 2015 Mark G Thomas # Copyright © 2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## PATH=/sbin:/usr/sbin:/bin:/usr/bin umask 022 . /lib/lsb/init-functions prefix="@ABSPREFIX@" sim="$prefix/bin/pidp8i-sim" scanswitch="$prefix/libexec/scanswitch" # Requires screen utility for detached pidp8i console functionality. test -x /usr/bin/screen || ( echo "screen not found" && exit 0 ) # Also check for other needed binaries test -x $scanswitch || ( echo "$scanswitch not found" && exit 0 ) test -x $sim || ( echo "$sim not found" && exit 0 ) # Check if pidp8i is already runnning under screen. # is_running() { procs=`screenu -ls pidp8i | egrep '[0-9]+\.pidp8i' | wc -l` test $procs -gt 0 && return 0 || return 1 } # Wrapper around screen(1) to drop privileges and pass given args screenu() { if [ "$USER" = "@INSTUSR@" ] then /usr/bin/screen $* else su -c "/usr/bin/screen $*" @INSTUSR@ fi } do_start() { if is_running ; then echo "PiDP-8/I is already running, not starting again." >&2 exit 0 fi # Regenerate SSH host keys if this is the first run on a fresh image if [ ! -f /etc/ssh/ssh_host_ecdsa_key -a -x /usr/sbin/dpkg-reconfigure ] then log_daemon_msg "Regenerating SSH host keys..." "pidp8i" /usr/sbin/dpkg-reconfigure openssh-server fi $scanswitch >/dev/null 2>&1 script=$? if [ $script -eq 8 ]; then echo "PiDP-8/I STOP switch detected, aborting." >&2 exit 0 elif [ $script -lt 8 ]; then bscript="@BOOTDIR@/""$script"".script" echo "Booting from $bscript..." else echo "Bad return value $script from $scanswitch!" exit 1 fi log_daemon_msg "Starting PiDP-8/I simulator" "pidp8i" screenu -dmS pidp8i "$sim" $bscript status=$? log_end_msg $status return $status } do_stop() { if ! is_running ; then echo "PiDP-8/I is already stopped." >&2 status=1 else log_daemon_msg "Stopping PiDP-8/I simulator" "pidp8i" screenu -S pidp8i -X quit status=$? log_end_msg $status fi return $status } case "$1" in start) do_start ;; stop) do_stop ;; restart) do_stop do_start ;; status) screenu -ls pidp8i | egrep '[0-9]+\.pidp8i' ;; *) log_action_msg "Usage: /etc/init.d/pidp8i {start|stop|restart|status}" || true exit 1 esac exit 0 |
> > | 1 2 | @INSTUSR@ ALL=NOPASSWD: /bin/systemctl poweroff @INSTUSR@ ALL=NOPASSWD: /bin/systemctl reboot |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | ######################################################################## # Makefile.in - Processed by autosetup's configure script to generate # an intermediate GNU make(1) file for building the PiDP-8/I software # from within its examples/ subdirectory. # # The resulting Makefile will redirect simple "make" calls to the top # level as well as the major top-level targets (e.g. "make clean") but # purposefully will not redirect anything like an installation or "run # the system" type target. Its only purpose is to help out those who # are working on the examples from within this directory. If you need # to work on the wider system, do it from the project's top level. # # If you are seeing this at the top of a file called Makefile and you # intend to make edits, do that in Makefile.in. Saying "make" will then # re-build Makefile from that modified Makefile.in before proceeding to # do the "make" operation. # # Copyright © 2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## all clean ctags distclean tags reconfig: cd @builddir@; make $@ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | # Example Programs ## What's Provided The `examples` directory holds short example programs for your PiDP-8/I, plus a number of subroutines you may find helpful in writing your own programs: | Example | What It Does ----------------------------- | `add.pal` | 2 + 3 = 5 The simplest program here; used below as a meta-example | `hello.pal` | writes "HELLO, WORLD!" to the console; tests PRINTS subroutine | `pep001.*` | Project Euler Problem #1 solutions, various languages | `routines/decprt` | prints an unsigned 12-bit decimal integer to the console | `routines/prints` | prints an ASCIIZ string stored as a series of 8-bit bytes to the console The `pep001.*` files are a case study series in solving a simple problem, which lets you compare the solutions along several axes. Some are much longer than others, but some will run faster and/or take less memory. It is interesting to compare them. There are writeups on each of these: * [**`pep001.pal`**][pal] — PAL8 Assembly Language * [**`pep001.bas`**][bas] — OS/8 BASIC [pal]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.PA [bas]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.BA ## How to Use the BASIC Examples To use the example BASIC program, simply transcribe it into OS/8 BASIC: .R BASIC NEW OR OLD--NEW FILE NAME--PAL001.BA READY 10 FOR I = 1 TO 999 10 FOR I = 1 TO 999 20 A = I / 3 \ B = I / 5 30 IF INT(A) = A GOTO 60 40 IF INT(B) = B GOTO 60 50 GOTO 70 60 T = T + I 70 NEXT I 80 PRINT "TOTAL: "; T 90 END SAVE READY RUN PAL001 BA 4A TOTAL: xxxxxxx READY BYE If you're SSH'd into the PiDP-8/I, "transcribing" is simply a matter of cut-and-paste into the terminal window. I've obscured the output on purpose, since I don't want this page to be a spoiler for the Project Euler site. If you get a 2-letter code from BASIC in response to your `RUN` command, it means you have an error in the program. See the BASIC section of the OS/8 Handbook for a decoding guide. ## How to Use the Assembly Language Examples For each PAL8 assembly program in `examples/*.pal`, there are two additional files: | Extension | Meaning ----------------------------- | `*.pal` | the PAL8 assembly source code for the program | `*.lst` | the human-readable assembler output | `*.pt` | the machine-readable assembler output (RIM format) There are three ways to run these on your PiDP-8/I, each starting with one of the above three files: 1. Transcribe the assembly program text to a file within a PDP-8 operating system and assemble it inside the simulator. 2. Toggle the program in from the front panel. I can recommend this method only for very short programs. 3. Copy the `*.pt` file to a USB stick and use the PiDP-8/I's [automatic media mounting feature][howto]. This is the fastest method. I cover each of these options below, in the same order as the list above. ## Option 1: Transcribing the Assembly Code into an OS/8 Session To transcribe [`examples/add.pal`][pal] into the OS/8 simulation on a PiDP-8/I: .R EDIT *ADD.PA< #A ← append to ADD.PA *0200 CLA CLL MAIN, TAD A TAD B DCA C HLT A, 2 B, 3 C, ← hit Ctrl-L to leave text edit mode #E ← saves program text to disk .PAL ADD-LS ERRORS DETECTED: 0 LINKS GENERATED: 0 .DIR ADD.* /A ADD .PA 1 ADD .BN 1 ADD .LS 1 399 FREE BLOCKS If you see some cryptic line from the assembler like `DE C` instead of the `ERRORS DETECTED: 0` bit, an error has occurred. Table 3-3 in my copy the OS/8 Handbook explains these. You will also have an `ADD.ER` file explaining what happened. You can instead say `EXE ADD` to assemble and execute that program in a single step, but beware that because the program halts the processor, your OS/8 session also halts. If you take the opportunity as intended to examine memory location `C` — 0207 — pressing `Start` to resume will cause the processor to try executing the instruction at 0210, and who knows what that will do? Even if you pass up the opportunity to examine `C`, pressing `Start` immediately after the halt will do the same, except that we know what it will do: it will try to execute the 0002 value stored at `A` as an instruction! (I believe it means `AND` the accumulator with memory location 2.) The solution to these problems is simple: .EDIT ADD ← don't need "R" because file exists #R ← read first page in; isn't automatic! #4D ← get rid of that pesky DCA line #5I ← insert above "A" def'n, now on line 5 JMP 7600 ← Ctrl-L again to exit edit mode #E ← save and exit .EXE ADD As before, the processor stops, but this time because we didn't move the result from the accumulator to memory location `C`, we can see the answer on the accumulator line on the front panel. Pressing `Start` this time continues to the next instruction which re-enters OS/8. Much nicer! As you can see, this option is the most educational, as it matches the working experience of PDP-8 assembly language programmers back in the day. The tools may differ — the user may prefer `TECO` over `EDIT` or MACRO-8 over PAL8 — but the idea is the same regardless. If you have the finished assembly code already on your computer and are SSH'd into the PiDP-8/I machine, there is a shortcut for all of the above. At the OS/8 command line, say: .R PIP *ADD.PA<TTY: Now you can simply copy the assembly language text in your desktop PC's text editor, paste it into the SSH window, and then hit Ctrl-Z to tell `PIP` that the text input from the terminal (`TTY:`) is finished. This is not only a smidge simpler than doing the same thing via `EDIT`, it also avoids a certain limitation of `EDIT` that starts to bite you once your program text exceeds about 5,600 characters. ## Option 2: Toggling a Programs in Via the Front Panel After building the PiDP-8/I software, each of the `examples/*.pal` files is assembled by `palbart` which writes out a human-readable listing file to `obj/*.lst`, named after the source file. Take [`obj/add.lst`][lst] as an example, in which you will find three columns of numbers on the code-bearing lines: 10 00200 7300 11 00201 1205 12 00202 1206 13 00203 3207 14 00204 7402 16 00205 0002 17 00206 0003 The first number refers to the corresponding line number in [`add.pal`][pal], the second is a PDP-8 memory address, and the third is the value stored at that address. To toggle the `add` program in, press `Stop` to halt the processor, then twiddle the switches like so: | Set SR Switches To... | Then Toggle... |------------------------------------------------ | 000 010 000 000 | `Load Add` | 111 011 000 000 | `Dep` | 001 010 000 101 | `Dep` | 001 010 000 110 | `Dep` | 011 010 000 111 | `Dep` | 111 100 000 010 | `Dep` | 000 000 000 010 | `Dep` | 000 000 000 011 | `Dep` To run it, repeat the first step in the table above, loading the program's starting address (0200) first into the switch register (SR) and then into the PDP-8's program counter (PC) via `Load Add`. Then toggle `Start` to run the program. If you then: If you then toggle 000 010 000 111 into SR, press `Load Add` followed by `Exam`, you should see 000 000 000 101 in the third row of lights, the bit pattern for five at memory location 0207, that being the correct answer for "2 + 3" in the expected location, which is what we expected `add.pal` to do. You could load that address back up again and `Dep` a different value into that location, then start the program over again at 0200 to observe that this memory location does, indeed, get overwritten with 0005. We only need one `Load Add` operation in the table above because all of the memory addresses in this program are sequential; there are no jumps in the values in the second column. Not all programs are that way, so pay attention! Beware that this program does not contain an explicit value for memory location 0207 at the start, but it does overwrite this location with the answer, since location `C` is defined as having the address just after the last value you entered via SR above, 0206. That is the source of the "07" in the lower two digits of the fourth instruction, 3207. ## Option 3: Loading a Program from Paper Tape The `palbart` assembly process described above also produces paper tape output files in RIM format in `bin/*.pt`. The simplest way to load these assembly examples into your PiDP-8/I is to copy each such file to a USB stick, one file per stick. Then, load the paper tape into the simulated PDP-8/I's core memory. The following is distilled from the [How to Use the PiDP-8/I][howto] section of the PiDP-8/I documentation: 1. Set the IF switches (first white group) to 001, and toggle `Sing Step` to reboot the simulator into the high-speed RIM loader. If the simulator wasn't already running, restarting the simulator with IF=1 will achieve the same end as toggling `Sing Step` while it's running. Reset the IF switches to 0. 2. Insert the USB stick containing the `*.pt` file you want to load into the Pi. 3. Set the DF switches (first brown group) to 001, then toggle `Sing Step` again. This attaches the tape to the high-speed paper tape reader peripheral within the PDP-8 simulator. Set DF back to 0. 4. Set the switch register (SR) to 7756 (111 111 101 110) then press `Load Add`, then `Start`. 5. Hit `Stop`, then reset SR to 0200 (000 010 000 000), that being the starting location of these example programs. Press `Load Add`, then `Start` to run the program. There is an SVG template for USB stick labels in the distribution under the [`labels/`][label] directory, for use if you find yourself creating long-lived USB sticks. See [`labels/README.md`][lread] for more information. ## License Copyright © 2016-2017 by Warren Young. This document is licensed under the terms of [the SIMH license][sl]. [lst]: https://tangentsoft.com/pidp8i/doc/trunk/examples/add.lst [pal]: https://tangentsoft.com/pidp8i/doc/trunk/examples/add.pal [label]: https://tangentsoft.com/pidp8i/dir?ci=trunk&name=labels [lread]: https://tangentsoft.com/pidp8i/doc/trunk/labels/README.md [howto]: http://obsolescence.wixsite.com/obsolescence/how-to-use-the-pidp-8 [sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | / ac-mq-blinker.pal - Rapidly modify AC and MQ / / This program twiddles AC and MQ rapidly, with a small amount of delay / between each update so the lights aren't just a solid blur. / / While this program runs at full speed, only AC and MQ appear to a / human to really change. PC also changes, of course, but since the / program spends so much of its time in the delay loop at the top, it / appears to be stuck at PC=1. / / It also modifies MB rapidly, but the pattern we use means it looks / like the lamps aren't changing, but are all on, dimmed by varying / amounts. / / From: http://dustyoldcomputers.com/pdp8/pdp8i/testprogs/acmqblinker.html / / SIMH: SET THROTTLE 30k / / Copyright © 2000 Robert Krten / / Permission is hereby granted, free of charge, to any person obtaining a / copy of this software and associated documentation files (the "Software"), / to deal in the Software without restriction, including without limitation / the rights to use, copy, modify, merge, publish, distribute, sublicense, / and/or sell copies of the Software, and to permit persons to whom the / Software is furnished to do so, subject to the following conditions: / / The above copyright notice and this permission notice shall be included in / all copies or substantial portions of the Software. / / THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR / IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, / FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL / THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER / LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING / FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER / DEALINGS IN THE SOFTWARE. / / Except as contained in this notice, the names of the authors above shall / not be used in advertising or otherwise to promote the sale, use or other / dealings in this Software without prior written authorization from those / authors. //////////////////////////////////////////////////////////////////////// PAGE 0 loop, ISZ delay / create a delay JMP loop CLA / clear AC so we can load it TAD value / get value MQL / stash AC into MQ TAD value / fetch value again CMA / complement AC ISZ value / get to next value NOP / ignore possible "skip" from ISZ JMP loop / and do it all again *20 / skip over the autoincrement registers delay, 0 value, 0 $ |
> > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | / add.pal - Add two numbers and halt, with sum in location 0207 / / This is a more space-efficient alternative to the program given at: / / http://mrhowson.edublogs.org/2016/11/27/pidp-8i-second-toggle-some-assembly-code/ PAGE 1 / code starts at core page 1; must avoid page 0 CLA CLL / clear AC and Link; two OPRs, one instruction! TAD A / add A to AC, which is zero, so "load A" TAD B / add B to AC DCA C / store sum in AC at C HLT / halt program A, 2 / set "A" variable to 2 B, 3 / and "B" to 3 C, / "C" result variable lives immediately past B, / and has no initial value because it is always / overwritten with the answer $ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | / asr33-rim-loader.pal / / Load paper tapes in RIM format from a Teletype Model 33 ASR's paper / tape reader. By contrast with the loader in hs-rim-loader.pal, this / is the low-speed paper tape RIM loader. / / This is the RIM loader printed on the front panel of the PDP-8/I. The / RIM loader in hs-rim-loader.pal (which is translated to boot/1.script / at build time) is similar to this one, but it works on the DEC high- / speed paper tape reader peripheral, which is where the PiDP-8/I's / automatic paper tape mounting feature attaches any tape images it / finds via USB. / / Therefore, you cannot use this RIM loader if you want to use the / PiDP-8/I's automatic media attachment mechanism. It is included / here mainly for documentation at the moment, but also in case / someone works out a way to build the simulator so that it can / conditionally load tapes from an emulated ASR-33. / / Raw disassembly done from the octal values by Bernhard Baehr's PDP-8/E / Simulator. Comments and labels by Warren Young. Original copyright / by Digital Equipment Corporation: this program appeared in DEC manuals / and was printed on the front panel of every PDP-8/I. / / SIMH: echo Installing the RIM loader for the ASR 33 paper tape reader... *7755 HLT / nonstandard: auto-halt on SIMH startup / normal RIM loader entry point, 7756 NEXT, KCC / clear PTR flag RBYTE1, KSF / loop until PTR is ready JMP RBYTE1 KRB / read first byte in CLL RTL / shift it left by 2, clearing link as well RTL / and 2 more again SPA / if top bit of AC is set... JMP RBYTE1 / ...AC contains the addr's value RTL / ...else rotate it another 2 positions RBYTE2, KSF / wait for next character JMP RBYTE2 KRS / read second byte in SNL / if link is set... DCA I BYTE / ...it's the value's address GOTVAL, DCA BYTE / ...else it's the value at that addr JMP NEXT / go round again, getting next value from PTR BYTE, 0 $ |
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | / bit-rotate.pal - Bit rotation and HLT demo / / This example demonstrates the bit rotation and HLT instructions of the / processor. It is meant to be run with the simulator in free-running / mode, since the embedded HLT instruction lets you see the state of AC / after each rotation. There are two HLT instructions so that you can / see the initial 1 value, and then see it change after each rotation. PAGE 1 CLA CLL IAC / clear link and AC, then bump AC: AC=1 HLT / let user see initial AC=1 value LOOP, RAL / rotate AC left HLT / and halt again JMP LOOP / on CONT, around it goes... $ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | / HELLO - "Hello, World!" program for PAL8 assembly, which also / happens to test the PRINTS routine, included inline below. / / Created by Warren Young of tangentsoft.com, 2016.11.30 / / Copyright © 2016 Warren Young / / Permission is hereby granted, free of charge, to any person obtaining a / copy of this software and associated documentation files (the "Software"), / to deal in the Software without restriction, including without limitation / the rights to use, copy, modify, merge, publish, distribute, sublicense, / and/or sell copies of the Software, and to permit persons to whom the / Software is furnished to do so, subject to the following conditions: / / The above copyright notice and this permission notice shall be included in / all copies or substantial portions of the Software. / / THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR / IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, / FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL / THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER / LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING / FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER / DEALINGS IN THE SOFTWARE. / / Except as contained in this notice, the names of the authors above shall / not be used in advertising or otherwise to promote the sale, use or other / dealings in this Software without prior written authorization from those / authors. //////////////////////////////////////////////////////////////////////// //// MAIN ///////////////////////////////////////////////////////////// PAGE 1 CLA TLS / send null character to terminal to prepare it TAD (HWSTR) JMS PRINTS HLT // "HELLO, WORLD!\r\n" in octal ASCIIZ HWSTR, 110; 105; 114; 114; 117; / HELLO 54; / comma 40; / space 127; 117; 122; 114; 104; / WORLD 41; / bang 15; 12; / CRLF 0 / null string terminator //// PRINTS //////////////////////////////////////////////////////////// PRINTS,0 DCA SADDR / save AC as string address PSNEXT, TAD I SADDR / load next character SNA JMP I PRINTS / found the null terminator; leave TSF / wait for terminal to be ready JMP .-1 TLS / write character to the terminal CLA / increment string address pointer TAD SADDR IAC DCA SADDR JMP PSNEXT / look at next character SADDR, 0 //// END /////////////////////////////////////////////////////////////// $ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | / hs-rim-loader.pal / / Load paper tapes in RIM format from the DEC high-speed PTR. / / This routine differs from that printed in DEC's manuals in that it / starts with a HLT instruction so that when you [re]start SIMH with / IF=1, running this program, the processor auto-halts, giving the user / the opportunity to attach a paper tape via DF or ATTACH, then start / the processor at 7756 as normal. / / The RIM loader code printed on the front panel of the PDP-8/I differs / because it is for the the paper tape reader built into a Teletype / Model 33 ASR. See asr33-rim-loader.pal for that other implementation, / including more information about it. / / Raw disassembly done from the octal values by Bernhard Baehr's PDP-8/E / Simulator. Comments and labels by Warren Young. Original copyright / by Digital Equipment Corporation: this program appeared in many DEC / manuals printed throughout the PDP-8 era variants of which were made / (and thus documented) from 1965 to 1979. / / SIMH: echo Installing the RIM loader for the DEC high-speed tape reader... *7755 HLT / nonstandard: auto-halt on SIMH startup / normal RIM loader entry point, 7756 RFC / clear PTR flag RBYTE1, RSF / loop until PTR is ready JMP RBYTE1 RCC / read first byte in CLL RTL / shift it left by 2, clearing link as well RTL / and 2 more again SPA / if top bit of AC is set... JMP GOTVAL / ...AC contains the addr's value RTL / ...else rotate it another 2 positions RBYTE2, RSF / wait for next character JMP RBYTE2 RCC / read second byte in SNL / if link is set... DCA I BYTE / ...it's the value's address GOTVAL, DCA BYTE / ...else it's the value at that addr JMP RBYTE1 / go round again, getting next value from PTR BYTE, 0 $ |
> > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 | 1 REM Copyright (c) 2016 by Warren Young 2 REM Released under the terms of ../SIMH-LICENSE.md 3 REM ------------------------------------------------------------------ 10 FOR I = 1 TO 999 20 A = I / 3 \ B = I / 5 30 IF INT(A) = A GOTO 60 40 IF INT(B) = B GOTO 60 50 GOTO 70 60 T = T + I 70 NEXT I 80 PRINT "TOTAL: "; T 90 END |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | / Project Euler Problem #1, Multiples of 3 and 5: / / If we list all the natural numbers below 10 that are multiples of / 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. / Find the sum of all the multiples of 3 or 5 below 1000. / / Initial solution by Warren Young of tangentsoft.com, 2016.11.30 / Optimized by Rick Murphy of the mailing list, 2016.12.04 / / Copyright © 2016-2017 Warren Young and Rick Murphy / / Permission is hereby granted, free of charge, to any person obtaining a / copy of this software and associated documentation files (the "Software"), / to deal in the Software without restriction, including without limitation / the rights to use, copy, modify, merge, publish, distribute, sublicense, / and/or sell copies of the Software, and to permit persons to whom the / Software is furnished to do so, subject to the following conditions: / / The above copyright notice and this permission notice shall be included in / all copies or substantial portions of the Software. / / THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR / IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, / FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL / THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER / LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING / FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER / DEALINGS IN THE SOFTWARE. / / Except as contained in this notice, the names of the authors above shall / not be used in advertising or otherwise to promote the sale, use or other / dealings in this Software without prior written authorization from those / authors. //////////////////////////////////////////////////////////////////////// //// DIY Assembler Instructions //////////////////////////////////////// / Our assembler doesn't know the EAE instructions, so teach it DVI=7407 / integer divide .+1 into {AC:MQ}, answer in MQ / Combined microcoded instruction aliases CLR=CLA CLL / clear both AC and L AC3=CLA CLL CML IAC RAL / set AC to 3 //// MAIN ////////////////////////////////////////////////////////////// / Program entry point. We purposely reinitialize global variables and / processor state in case we're restarting this program in-core. PAGE 1 MAIN, AC3 / start with 3, because we know 1 & 2 can't work DCA CURR DCA STOTAL / reset total to 0 TAD (ANSWER-1) / write "ANSWER: " to the terminal JMS PRINTS //// MLCORE //////////////////////////////////////////////////////////// / The core of the main loop. MAIN just inits the globals and falls / through to us. MLCORE, AC3 / try dividing 3 into CURR first JMS ISMOD0 SNA CLA / if ISMOD0 left AC = 0, CURR divided evenly by JMP NXITER / 3, so skip 5 lest we count multiples of 15 2x TAD (5) / no good; try dividing 5 into CURR instead JMS ISMOD0 NXITER, CLA / loop cleanup TAD CURR CIA TAD MAX / = 0 if CURR == MAX SNA CLA / if so, leave calculation loop JMP MLDONE ISZ CURR / CURR still < MAX, so increment CURR; never skips TAD STOTAL / if STOTAL is getting too big, print... CIA / a subtotal and zero STOTAL so we don't... TAD STMAX / overflow the 12-bit limit SNL JMP MLCORE / STMAX - STOTAL > 0 so re-enter loop core JMS SHOWST / exceeded threshold, so display subtotal and " + " DCA STOTAL / take advantage of free zero left by SHOWST TAD (PLUS-1) JMS PRINTS JMP MLCORE MLDONE, JMS SHOWST / done; show answer TAD (CRLF-1) / don't need CLA; SHOWST left AC = 0 JMS PRINTS / End program gracefully, either re-entering OS/8 if we can see / that its entry point looks sane, or halting with the answer in / AC so the user can see the answer on the front panel. OS8ENT, / 7600, OS/8's entry point, happens to also be... ENDG, 7600 / ...the group 2 variant of CLA; yes, we know, yuck! TAD I OS8ENT TAD OS8INS1 / add its negative SNA CLA / if it's zero'd out, then... JMP I OS8ENT / re-enter OS/8 TAD STOTAL / else not running under OS/8... HLT / so halt with STOTAL displayed in AC lights OS8INS1,-4207 / first OS/8 instruction at entry point, negated //// ISMOD0 //////////////////////////////////////////////////////////// / If passed AC divides evenly into CURR (in C-speak, CURR % AC == 0) / add CURR to STOTAL and return 0 in AC. Else, return nonzero in AC and / leave STOTAL untouched. ISMOD0, 0 DCA DIVISOR / Divide CURR by DIVISOR, passed as AC TAD CURR / load CURR into just-cleared AC MQL DVI / move CURR to MQ, divide by DIVISOR... DIVISOR,0 / ...quotient in MQ, remainder in AC SZA JMP I ISMOD0 / remainder nonzero, so leave early / Division left AC empty, so CURR divides evenly by DIVISOR! TAD CURR / don't need to clear AC; prior test says AC == 0 TAD STOTAL DCA STOTAL JMP I ISMOD0 //// SHOWST //////////////////////////////////////////////////////////// / Write STOTAL value to terminal in decimal. We purposely do not follow / it with anything, as our callers variously follow it with " + " or a / CRLF pair. Leaves AC = 0 because DECRPT does. SHOWST, 0 CLR TAD STOTAL JMS DECPRT / print answer on console, in decimal JMP I SHOWST / and done //// TYPE ////////////////////////////////////////////////////////////// / Send a character out to the terminal. Shared core of PRINTS and / DECPRT. TYPE, 0 TSF JMP .-1 TLS CLA JMP I TYPE //// PRINTS //////////////////////////////////////////////////////////// / Write an ASCIIZ string to the terminal. Expects to receive the / address of the string - 1 in AC. (The -1 hassle saves an instruction / or two in our use of an autoincrement register.) Uses the autoinc / register at location 10. SADDR=10 / autoinc register for walking the string PRINTS, 0 DCA SADDR / save AC as string address PSNEXT, TAD I SADDR / load next character SNA JMP I PRINTS / found the null terminator; leave JMS TYPE / Print that character JMP PSNEXT / look at next character //// DECPRT //////////////////////////////////////////////////////////// / Decimal number printer; variant of examples/routines/decprt.pal / Leaves AC = 0. DECPRT, 0 DCA VALUE /SAVE INPUT DCA DIGIT /CLEAR TAD CNTRZA DCA CNTRZB /SET COUNTER TO FOUR TAD ADDRZA DCA ARROW /SET TABLE POINTER SKP DCA VALUE /SAVE CLL TAD VALUE ARROW, TAD TENPWR /SUBTRACT POWER OF TEN SZL ISZ DIGIT /DEVELOP BCD DIGIT SZL JMP ARROW-3 /LOOP CLA /HAVE BCD DIGIT TAD DIGIT /GET DIGIT TAD K260 /MAKE IT ASCII JMS TYPE DCA DIGIT /CLEAR ISZ ARROW /UPDATE POINTER ISZ CNTRZB /DONE ALL FOUR? JMP ARROW-1 /NO: CONTINUE JMP I DECPRT /YES: EXIT ADDRZA, TAD TENPWR CNTRZA, -4 TENPWR, -1750 /ONE THOUSAND -0144 /ONE HUNDRED -0012 /TEN -0001 /ONE K260, 260 VALUE, 0 DIGIT, 0 CNTRZB, 0 //// Global Variables ////////////////////////////////////////////////// CURR, 0 / current number we're checking STOTAL, 0 / subtotal, printed and reset occasionally //// Constants ///////////////////////////////////////////////////////// DECIMAL MAX, 999 / check natural numbers CURR to MAX; must be < 2048! STMAX, 1024 / subtotal max; avoids overflow of 12-bit signed int OCTAL CRLF, 15;12;0 / ASCII character values, zero-terminated PLUS, 40;53;40;0 ANSWER, 101;116;123;127;105;122;72;40;0 //// END /////////////////////////////////////////////////////////////// / Assembler-generated constants will appear below this in the list file $ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | /COPYRIGHT 1971, DIGITAL EQUIPMENT CORPORATION /MAYNARD, MASSACHUSETTS /DIGITAL 8-22-U /UNSIGNED DECIMAL PRINT /CALL WITH NUMBER TO BE TYPED IN C(AC) /RETURN TO LOCATION FOLLOWING THE JMS DECPRT, 0 DCA VALUE /SAVE INPUT DCA DIGIT /CLEAR TAD CNTRZA DCA CNTRZB /SET COUNTER TO FOUR TAD ADDRZA DCA ARROW /SET TABLE POINTER SKP DCA VALUE /SAVE CLL TAD VALUE ARROW, TAD TENPWR /SUBTRACT POWER OF TEN SZL ISZ DIGIT /DEVELOP BCD DIGIT SZL JMP ARROW-3 /LOOP CLA /HAVE BCD DIGIT TAD DIGIT /GET DIGIT TAD K260 /MAKE IT ASCII TSF /OR TAD DIGIT JMP .-1 /JMS TDIGIT(SEE 8-19-U) TLS /TYPE DIGIT CLA DCA DIGIT /CLEAR ISZ ARROW /UPDATE POINTER ISZ CNTRZB /DONE ALL FOUR? JMP ARROW-1 /NO: CONTINUE JMP I DECPRT /YES: EXIT ADDRZA, TAD TENPWR CNTRZA, -4 TENPWR, -1750 /ONE THOUSAND -0144 /ONE HUNDRED -0012 /TEN -0001 /ONE K260, 260 VALUE, 0 DIGIT, 0 CNTRZB, 0 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | / PRINTS - Print an ASCIIZ string to the terminal / / It expects to receive the address of the string - 1 in AC. (The -1 / hassle saves an instruction or two in our use of an autoincrement / register.) / / This routine uses the autoinc register at location 10. / / Created by Warren Young of tangentsoft.com, 2016.11.30 / Improved by Rick Murphy of the PiDP-8/I mailing list, 2016.12.03 / / Copyright © 2016 Warren Young and Rick Murphy / / Permission is hereby granted, free of charge, to any person obtaining a / copy of this software and associated documentation files (the "Software"), / to deal in the Software without restriction, including without limitation / the rights to use, copy, modify, merge, publish, distribute, sublicense, / and/or sell copies of the Software, and to permit persons to whom the / Software is furnished to do so, subject to the following conditions: / / The above copyright notice and this permission notice shall be included in / all copies or substantial portions of the Software. / / THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR / IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, / FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL / THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER / LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING / FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER / DEALINGS IN THE SOFTWARE. / / Except as contained in this notice, the names of the authors above shall / not be used in advertising or otherwise to promote the sale, use or other / dealings in this Software without prior written authorization from those / authors. //////////////////////////////////////////////////////////////////////// TYPE, 0 / helper routine for sending a single character TSF JMP .-1 TLS CLA JMP I TYPE SADDR=10 / autoinc register for walking the string PRINTS, 0 DCA SADDR / save AC as string address PSNEXT, TAD I SADDR / load next character SNA JMP I PRINTS / found the null terminator; leave JMS TYPE / print that character JMP PSNEXT / look at next character |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | # USB Stick Labels ## What It Is This directory contains an Inkscape document (`*.svg`) containing the DECtape logo and USB stick labels with graphics based on that label. There are three example labels in the document: * **BIN Loader** for `media/copytoUSBsticks/binloader.pt` * **FOCAL-69** for `media/copytoUSBsticks/focal.pt` * **ADD.PA** for `examples/add.pt` These labels use the "DECtape" graphics even though they're paper tapes, primarily because it's a nice graphic and I haven't bothered to draw something appropriate to paper tapes yet. The labels print out at approximately 30×16mm each, which fits the USB sticks I have here, but you may need to scale the printout for your particular USB sticks. ## Affixing the Labels This document is not designed with any particular self-adhesive label stock in mind. Instead, I simply use rubber cement as a contact adhesive to affix these labels to the USB stick. Simply cut the label(s) you want to use out with scissors, paint both the back of the label and the top of the USB stick with rubber cement, and let it dry for a minute or so. When the glue is dry-looking, carefully place the label where you want it on the USB stick. You won't have much of a chance to move the label around after the two dried glue patches touch, so be careful with your placement. Press the label firmly against the stick, pressing repeatedly to cover the entire surface, then rub around the label gently to brush away any excess cement. Protip: Use the part of the page you cut the labels out of as a protective mat to work on. It will let you apply cement to the label fully edge-to-edge without messing up your work surface. The labels will be much more durable if there is no unglued bit near the edge for fingernails and such to snag on. You were going to throw this excess material away, so you might as well get one final use out of it, yes? ## Fonts This SVG file uses a non-free font called [Dottie][font] for the faux dot matrix text. There are [free alternatives][alt], but none of the ones I liked allow redistribution, so I couldn't include one of them in this repository. [font]: https://www.fonts.com/font/ingrimayne-type/dottie/regular [alt]: http://www.1001fonts.com/digital+dot-matrix-fonts.html ## PDF Version If you don't want to use one of those alternative fonts or simply like the look of Dottie and don't need custom labels, this directory also includes a PDF of the same design with the necessary subset of Dottie embedded, so that you can print it out. |
cannot compute difference between binary files
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 | <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" id="svg2" version="1.1" inkscape:version="0.91 r13725" xml:space="preserve" width="765" height="990" viewBox="0 0 765 990" sodipodi:docname="dectape-usb-key.svg"><title id="title4244">PiDP-8/I USB key labels</title><metadata id="metadata8"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title>PiDP-8/I USB key labels</dc:title><dc:date>December 2016</dc:date><dc:creator><cc:Agent><dc:title>Warren Young</dc:title></cc:Agent></dc:creator><dc:rights><cc:Agent><dc:title>see license</dc:title></cc:Agent></dc:rights><dc:language>English</dc:language><dc:subject><rdf:Bag><rdf:li>label</rdf:li><rdf:li>USB</rdf:li><rdf:li>DEC</rdf:li><rdf:li>PDP-8</rdf:li><rdf:li>dot matrix</rdf:li><rdf:li>DECtape</rdf:li></rdf:Bag></dc:subject><dc:description>Graphical labels for use on USB sticks containing binary media images suitable for use with the PiDP-8/I's USB stick auto-attaching feature.</dc:description><cc:license rdf:resource="https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md" /></cc:Work></rdf:RDF></metadata><defs id="defs6"><clipPath clipPathUnits="userSpaceOnUse" id="clipPath18"><path d="M 0,0 612,0 612,792 0,792 0,0 Z" id="path20" inkscape:connector-curvature="0" /></clipPath><filter inkscape:label="Opacity" style="color-interpolation-filters:sRGB;" id="filter3471"><feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 -0 " result="colormatrix" id="feColorMatrix3473" /><feComposite in2="colormatrix" operator="arithmetic" k2="0.343085" result="composite" id="feComposite3475" /></filter></defs><sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="2560" inkscape:window-height="1391" id="namedview4" showgrid="false" inkscape:zoom="3.17" inkscape:cx="117.85871" inkscape:cy="792.26542" inkscape:window-x="0" inkscape:window-y="1" inkscape:window-maximized="1" inkscape:current-layer="layer2" showguides="true" inkscape:guide-bbox="true"><sodipodi:guide position="278.68734,454.12002" orientation="0,1" id="guide3550" /><sodipodi:guide position="294.28136,404.66471" orientation="0,1" id="guide3552" /></sodipodi:namedview><g id="g10" inkscape:groupmode="layer" inkscape:label="reference" transform="matrix(1.25,0,0,-1.25,0,990)" style="display:inline"><g id="g12" /></g><g inkscape:groupmode="layer" id="layer1" inkscape:label="inner elements" style="display:inline" /><g inkscape:groupmode="layer" id="layer2" inkscape:label="ring" style="display:inline"><g id="g4253" transform="translate(-3.4700316,-242.58675)"><ellipse ry="37.22398" rx="39.077209" style="display:inline;fill:none;fill-opacity:1;stroke:none;stroke-width:0.99528235;stroke-opacity:1" id="circle4384" cx="88.770988" cy="306.1496" /><g inkscape:label="#g3477" style="display:inline;fill:#1e97ec;fill-opacity:1;stroke:none" id="digital-logo" transform="matrix(0.05536257,0,0,-0.05536257,75.852793,325.57701)"><g style="fill:#1e97ec;fill-opacity:1;stroke:none" id="g3479" clip-path="url(#clipPath18)"><path d="m 101.3,107.13 c 0.02,-2.96 -3.43,-7.55 -7.63,-7.56 -4.48,0.02 -8.87,4.08 -8.87,12.2 0,8.24 4.38,12.48 8.73,12.5 4.34,-0.02 7.79,-4.67 7.77,-8.2 l 0,-8.94 z m 0.23,22.74 0,18.13 8.9,0 0,-56.4 -9.2,0 0,2.6 c -0.75,-0.76 -3.92,-4.07 -9.33,-4.07 -5.64,-0.01 -15.73,4.69 -15.73,21.64 0,14.37 7.54,21.8 16.5,21.8 3.32,0 6.58,-1.18 8.86,-3.7 z M 72,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m 7,0 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 61.18,-20.72 c 0,3.59 -4.04,7.3 -7.78,7.3 -3.22,0 -8.2,-2.05 -8.2,-11.24 0,-9.79 6.12,-11.22 8.15,-11.22 4.03,0 7.83,2.77 7.83,7.05 l 0,8.11 z M 171,172 l 42.5,0 0,-100 -42.5,0 0,100 z m 28.52,-43.91 c -0.93,1.33 -3.98,4.35 -8.34,4.35 -6.77,-0.01 -16.21,-3.92 -16.21,-20.89 0.01,-13.38 10.7,-17.87 15.09,-17.87 4.74,-0.01 8.26,2.75 9.22,3.79 l 0,-4.21 c 0,-3.11 -3.19,-6.15 -8.53,-6.15 -4.6,0.01 -5.08,2.96 -5.08,4.42 l -9.4,0 c 0,-4.67 3.01,-11.49 13.91,-11.5 9.76,0 12.98,3.73 14.37,5.13 1.17,1.18 2.92,4.42 2.92,7.01 l 0,39.83 -7.94,0 -0.01,-3.91 z M 220.5,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 56.03,9.75 -8.23,0 0,-14.54 -4.31,0 0,-6.75 4.31,0 0,-25.44 c 0,-4.85 2.49,-8.66 9.43,-8.65 3.25,0 3.12,-0.02 5.94,0.47 l 0,8.4 c -1.84,-0.32 -2.41,-0.45 -3.31,-0.42 -1.73,0.05 -3.83,0.66 -3.83,3.74 l 0,21.89 6.52,0 0,6.76 -6.52,0 0,14.54 z M 312.5,72 270,72 l 0,100 42.5,0 0,-100 z m 49.5,0 -42.5,0 0,100 42.5,0 0,-100 z m -42.5,0 z m 6.99,47.28 7.71,0 c 0,4.1 2.12,6.25 6.16,6.25 6.44,0.01 6.23,-2.86 6.22,-6.45 -3.98,-1.55 -3.61,-1.51 -9.68,-2.89 -7.71,-1.78 -12.43,-5.08 -12.44,-13.07 0.01,-7.81 5.57,-11.98 11.84,-11.97 6.28,-0.01 7.58,2.02 10.18,3.75 l 0,-3.12 9.84,0 c -2.32,3.74 -1.71,4.47 -1.73,7.28 0.01,2.2 0,23.74 0,23.74 0.01,4.27 -2.21,6.88 -3.66,7.75 -3.62,2.16 -8.09,2.42 -10.57,2.43 -8.81,-0.02 -13.88,-4.45 -13.87,-13.7 z m 20.09,-7.82 0,-5.45 c 0,-2.49 -3.03,-7.31 -8.98,-7.32 -2.9,-0.01 -4.52,2.14 -4.53,4.93 0.01,2.8 2.38,4.95 4.39,5.35 2.09,0.42 6.9,1.65 9.12,2.49 z M 369,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,56.9 9,0 0,-56.9 z" style="fill:#1e97ec;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path3481" inkscape:connector-curvature="0" /></g></g><g transform="matrix(0.140057,0,0,0.140057,28.143932,232.97391)" inkscape:label="#g4177" id="text" style="display:inline"><text xml:space="preserve" style="font-style:normal;font-weight:normal;font-size:32px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#1e97ec;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="234.70551" y="-270.57205" id="text3511" sodipodi:linespacing="125%" transform="matrix(1.25,0,0,1.25,0,990)"><tspan sodipodi:role="line" id="tspan3513" x="234.70551" y="-270.57205" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:13px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';fill:#1e97ec;fill-opacity:1;stroke:none">DIGITAL EQUIPMENT CORPORATION</tspan></text> <text transform="matrix(1.25,0,0,1.25,0,990)" sodipodi:linespacing="125%" id="text3515" y="-258.09683" x="252.52724" style="font-style:normal;font-weight:normal;font-size:32px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#1e97ec;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" xml:space="preserve"><tspan style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:11px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';fill:#1e97ec;fill-opacity:1;stroke:none" y="-258.09683" x="252.52724" id="tspan3517" sodipodi:role="line">MAYNARD, MASSACHUSETTS, 01754</tspan></text> <text transform="matrix(1.25,0,0,1.25,0,990)" sodipodi:linespacing="125%" id="text3519" y="-237.42363" x="236.84412" style="font-style:normal;font-weight:normal;font-size:32px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#1e97ec;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" xml:space="preserve"><tspan style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:15px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:0.80000001px;fill:#1e97ec;fill-opacity:1;stroke:none" y="-237.42363" x="236.84412" id="tspan3521" sodipodi:role="line">REEL NO.</tspan></text> <text xml:space="preserve" style="font-style:normal;font-weight:normal;font-size:32px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#1e97ec;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="422.72479" y="-237.42363" id="text3523" sodipodi:linespacing="125%" transform="matrix(1.25,0,0,1.25,0,990)"><tspan sodipodi:role="line" id="tspan3525" x="422.72479" y="-237.42363" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:15px;font-family:'Helvetica Neue';-inkscape-font-specification:'Helvetica Neue Bold';letter-spacing:0.80000001px;fill:#1e97ec;fill-opacity:1;stroke:none">DATE</tspan></text> </g><path inkscape:connector-curvature="0" d="m 72.199362,308.19926 0,6.84856 1.404033,-0.0156 c 0,0 4.448809,0.2496 4.461705,-3.60369 0.01175,-3.51016 -4.430504,-3.27607 -4.430504,-3.27607 z m 1.326032,1.31043 0,4.1965 0.686416,0 c 0,0 2.523387,0.12492 2.542859,-2.30886 0.0156,-1.95004 -2.402456,-1.91884 -2.402456,-1.91884 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="path3539" /><path inkscape:connector-curvature="0" id="path3554" d="m 78.252303,308.02765 0,6.92656 3.588086,0 0,-1.29483 -2.106051,0 0,-1.54443 2.028048,0 0,-1.31043 -2.074848,0 0,-1.41964 2.059249,0 0,-1.35723 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path3556" d="m 87.48772,310.0089 1.450835,0 c 0,0 -0.702017,-2.23085 -3.416481,-2.23085 -2.714464,0 -3.572483,2.38685 -3.572483,3.74408 0,1.35724 1.294829,3.43208 3.775287,3.43208 2.480459,0 3.229277,-2.04364 3.229277,-2.04364 l -1.466435,0 c 0,0 -0.358807,0.71761 -1.747241,0.71761 -1.388432,0 -2.402456,-0.99842 -2.402456,-2.23085 0,-1.23243 0.951623,-2.16845 2.230851,-2.16845 1.279232,0 1.918846,0.78002 1.918846,0.78002 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path3558" d="m 89.219362,308.02765 0,6.92656 1.669238,0 0,-5.46012 -1.201229,0 0,-1.46644 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path3560" d="m 92.152229,314.95421 0,-5.47572 1.29483,0 0.0312,-1.45084 3.697286,0 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path3562" d="m 93.868269,314.95421 1.357232,-1.90324 1.560036,0 0,1.90324 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path sodipodi:nodetypes="cccc" inkscape:connector-curvature="0" id="path3564" d="m 96.052321,311.84974 0.748816,-1.06082 0,1.04522 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path3566" d="m 98.080369,308.02765 0,6.92656 0.530412,0 0,-6.92656 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path3568" d="m 99.87441,309.32248 0,1.79404 0.67082,0.0156 c 0,0 0.99842,-0.1248 0.99842,-0.92042 0,-0.79562 -1.02962,-0.85802 -1.02962,-0.85802 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path3570" d="m 99.936811,312.28655 0,2.66766 3.057669,0 0,-6.92656 -1.84084,0 c 0,0 1.56004,0.35881 1.56004,2.32446 0,1.96564 -1.90325,1.95004 -1.90325,1.95004 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><g transform="matrix(0.140057,0,0,0.140057,28.143932,232.97391)" inkscape:label="#g4171" id="dectape-graphics" style="display:inline"><path transform="matrix(1.25,0,0,-1.25,0,990)" style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 139.98514,433.20896 410.6593,-5.07131 c 0,0 -5.97508,23.16953 -13.43181,38.13146 -167.11395,1.42458 -326.54993,2.93466 -382.95298,3.28558 -9.80882,-18.26995 -14.27451,-36.34573 -14.27451,-36.34573 z" id="path3574" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc" /><path transform="matrix(1.25,0,0,-1.25,0,990)" style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 163.42461,486.64088 c 0,0 17.16387,25.4632 30.545,37.27097 14.36613,-10e-6 304.87219,-2.95063 304.87219,-2.95063 0,0 18.39873,-19.40688 28.73226,-37.55356 -41.33413,-0.25205 -364.14945,3.23322 -364.14945,3.23322 z" id="path3576" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc" /><path transform="matrix(1.25,0,0,-1.25,0,990)" style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 208.21343,538.27798 277.34946,-3.48528 c 0,0 -34.54921,30.89095 -64.85267,39.09636 -20.66706,0.50408 -148.33167,1.15579 -148.33167,1.15579 0,0 -35.4761,-11.99339 -64.16512,-36.76687 z" id="path3578" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc" /><path transform="matrix(1.25,0,0,-1.25,0,990)" style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 446.79083,363.29602 0,-7.57424 -11.58413,0 0,-9.53463 11.49503,0 0,-6.23761 -11.76235,0 0,-9.62373 11.85145,0 0,-6.59404 56.4949,0 c 0,0 38.49493,6.86137 38.49493,45.35631 0,38.49494 -42.59393,44.82166 -42.59393,44.82166 l -363.02869,3.74256 c 0,0 -5.43563,-22.45538 -5.43563,-40.00979 15.77223,0 363.1178,-2.67326 363.1178,-2.67326 0,0 6.86137,0.35644 6.86137,-6.1485 0,-6.50493 -6.23761,-5.52473 -6.23761,-5.52473 z" id="path3572" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccccccsccccscc" /></g><path inkscape:label="#circle4400" inkscape:connector-curvature="0" id="ring" d="M 88.506113,265.74856 A 40.577442,40.577442 0 0 0 47.928702,306.32597 40.577442,40.577442 0 0 0 88.506113,346.90365 40.577442,40.577442 0 0 0 129.0838,306.32597 40.577442,40.577442 0 0 0 88.506113,265.74856 Z m 0,2.02535 A 38.375756,38.552254 0 0 1 126.88201,306.32597 38.375756,38.552254 0 0 1 88.506113,344.8783 38.375756,38.552254 0 0 1 50.130497,306.32597 38.375756,38.552254 0 0 1 88.506113,267.77391 Z" style="display:inline;fill:#53a6c2;fill-opacity:1;stroke:none;stroke-width:1.16711712;stroke-opacity:1" /></g><text xml:space="preserve" style="font-style:normal;font-weight:normal;font-size:5.5970993px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="3.6412585" y="379.60495" id="text4175" sodipodi:linespacing="125%"><tspan sodipodi:role="line" id="tspan4177" x="3.6412585" y="379.60495" /></text> <text xml:space="preserve" style="font-style:normal;font-weight:normal;font-size:10.07446671px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="48.558857" y="344.43402" id="text4177" sodipodi:linespacing="125%"><tspan sodipodi:role="line" x="48.558857" y="344.43402" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.29654121px;font-family:Dottie;-inkscape-font-specification:Dottie" id="tspan4185" /></text> <text sodipodi:linespacing="125%" id="text4251" y="445.44971" x="3.6412585" style="font-style:normal;font-weight:normal;font-size:5.5970993px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" xml:space="preserve"><tspan y="445.44971" x="3.6412585" id="tspan4253" sodipodi:role="line" /></text> <ellipse ry="37.22398" rx="39.077209" style="display:inline;fill:none;fill-opacity:1;stroke:none;stroke-width:0.99528235;stroke-opacity:1" id="ellipse4269" cx="188.95584" cy="309.14252" /><g id="g4403" transform="translate(-3.4700316,-242.58675)"><rect style="fill:none;fill-opacity:1;stroke:#dcdcdc;stroke-width:0.33938482;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4249" width="98.517426" height="51.003555" x="42.359982" y="431.91147" /><text xml:space="preserve" style="font-style:normal;font-weight:normal;font-size:12.70192051px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="46.723854" y="443.1936" id="text4255" sodipodi:linespacing="125%"><tspan sodipodi:role="line" id="tspan4257" x="46.723854" y="443.1936" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie">BIN LOADER</tspan><tspan id="tspan4425" sodipodi:role="line" x="46.723854" y="453.11697" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie">DEC-08-LBAA-PM</tspan><tspan sodipodi:role="line" x="46.723854" y="463.04034" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie" id="tspan4261">5/10/67 SA:7777</tspan><tspan sodipodi:role="line" x="46.723854" y="472.96371" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie" id="tspan4263" /></text> <g id="g4393" transform="translate(-0.5513317,64.832146)"><g transform="matrix(0.03699838,0,0,-0.03699838,88.960048,419.11479)" id="g4395" style="display:inline;fill:#1e97ec;fill-opacity:1;stroke:none" inkscape:label="#g3477"><g clip-path="url(#clipPath18)" id="g4397" style="fill:#1e97ec;fill-opacity:1;stroke:none"><path inkscape:connector-curvature="0" id="path4399" style="fill:#1e97ec;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 101.3,107.13 c 0.02,-2.96 -3.43,-7.55 -7.63,-7.56 -4.48,0.02 -8.87,4.08 -8.87,12.2 0,8.24 4.38,12.48 8.73,12.5 4.34,-0.02 7.79,-4.67 7.77,-8.2 l 0,-8.94 z m 0.23,22.74 0,18.13 8.9,0 0,-56.4 -9.2,0 0,2.6 c -0.75,-0.76 -3.92,-4.07 -9.33,-4.07 -5.64,-0.01 -15.73,4.69 -15.73,21.64 0,14.37 7.54,21.8 16.5,21.8 3.32,0 6.58,-1.18 8.86,-3.7 z M 72,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m 7,0 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 61.18,-20.72 c 0,3.59 -4.04,7.3 -7.78,7.3 -3.22,0 -8.2,-2.05 -8.2,-11.24 0,-9.79 6.12,-11.22 8.15,-11.22 4.03,0 7.83,2.77 7.83,7.05 l 0,8.11 z M 171,172 l 42.5,0 0,-100 -42.5,0 0,100 z m 28.52,-43.91 c -0.93,1.33 -3.98,4.35 -8.34,4.35 -6.77,-0.01 -16.21,-3.92 -16.21,-20.89 0.01,-13.38 10.7,-17.87 15.09,-17.87 4.74,-0.01 8.26,2.75 9.22,3.79 l 0,-4.21 c 0,-3.11 -3.19,-6.15 -8.53,-6.15 -4.6,0.01 -5.08,2.96 -5.08,4.42 l -9.4,0 c 0,-4.67 3.01,-11.49 13.91,-11.5 9.76,0 12.98,3.73 14.37,5.13 1.17,1.18 2.92,4.42 2.92,7.01 l 0,39.83 -7.94,0 -0.01,-3.91 z M 220.5,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 56.03,9.75 -8.23,0 0,-14.54 -4.31,0 0,-6.75 4.31,0 0,-25.44 c 0,-4.85 2.49,-8.66 9.43,-8.65 3.25,0 3.12,-0.02 5.94,0.47 l 0,8.4 c -1.84,-0.32 -2.41,-0.45 -3.31,-0.42 -1.73,0.05 -3.83,0.66 -3.83,3.74 l 0,21.89 6.52,0 0,6.76 -6.52,0 0,14.54 z M 312.5,72 270,72 l 0,100 42.5,0 0,-100 z m 49.5,0 -42.5,0 0,100 42.5,0 0,-100 z m -42.5,0 z m 6.99,47.28 7.71,0 c 0,4.1 2.12,6.25 6.16,6.25 6.44,0.01 6.23,-2.86 6.22,-6.45 -3.98,-1.55 -3.61,-1.51 -9.68,-2.89 -7.71,-1.78 -12.43,-5.08 -12.44,-13.07 0.01,-7.81 5.57,-11.98 11.84,-11.97 6.28,-0.01 7.58,2.02 10.18,3.75 l 0,-3.12 9.84,0 c -2.32,3.74 -1.71,4.47 -1.73,7.28 0.01,2.2 0,23.74 0,23.74 0.01,4.27 -2.21,6.88 -3.66,7.75 -3.62,2.16 -8.09,2.42 -10.57,2.43 -8.81,-0.02 -13.88,-4.45 -13.87,-13.7 z m 20.09,-7.82 0,-5.45 c 0,-2.49 -3.03,-7.31 -8.98,-7.32 -2.9,-0.01 -4.52,2.14 -4.53,4.93 0.01,2.8 2.38,4.95 4.39,5.35 2.09,0.42 6.9,1.65 9.12,2.49 z M 369,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,56.9 9,0 0,-56.9 z" /></g></g><path id="path4401" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 106.30956,411.81751 0,4.57684 0.9383,-0.0104 c 0,0 2.97311,0.1668 2.98173,-2.40832 0.008,-2.34582 -2.96088,-2.18938 -2.96088,-2.18938 z m 0.88618,0.87575 0,2.80449 0.45873,0 c 0,0 1.68636,0.0835 1.69937,-1.54299 0.0104,-1.3032 -1.60555,-1.28235 -1.60555,-1.28235 z" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 110.35469,411.70282 0,4.62897 2.3979,0 0,-0.86532 -1.40746,0 0,-1.03213 1.35532,0 0,-0.87575 -1.3866,0 0,-0.94874 1.37618,0 0,-0.90703 z" id="path4403" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 116.52666,413.02688 0.96958,0 c 0,0 -0.46915,-1.49086 -2.28321,-1.49086 -1.81406,0 -2.38747,1.59511 -2.38747,2.50214 0,0.90703 0.86533,2.29363 2.523,2.29363 1.65767,0 2.1581,-1.36575 2.1581,-1.36575 l -0.98,0 c 0,0 -0.23979,0.47958 -1.16767,0.47958 -0.92789,0 -1.60555,-0.66724 -1.60555,-1.49086 0,-0.82363 0.63596,-1.44916 1.49086,-1.44916 0.8549,0 1.28236,0.52128 1.28236,0.52128 z" id="path4405" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 117.6839,411.70282 0,4.62897 1.11554,0 0,-3.64895 -0.80277,0 0,-0.98002 z" id="path4407" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 119.64391,416.33179 0,-3.65938 0.86533,0 0.0208,-0.96959 2.47087,0 z" id="path4409" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 120.79073,416.33179 0.90703,-1.27192 1.04255,0 0,1.27192 z" id="path4411" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 122.25032,414.2571 0.50043,-0.70894 0,0.69851 z" id="path4413" inkscape:connector-curvature="0" sodipodi:nodetypes="cccc" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 123.60565,411.70282 0,4.62897 0.35447,0 0,-4.62897 z" id="path4415" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 124.80459,412.56815 0,1.19894 0.4483,0.0104 c 0,0 0.66724,-0.0834 0.66724,-0.61511 0,-0.53171 -0.68809,-0.57341 -0.68809,-0.57341 z" id="path4417" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 124.84629,414.54901 0,1.78278 2.04342,0 0,-4.62897 -1.23022,0 c 0,0 1.04256,0.23979 1.04256,1.55342 0,1.31363 -1.27192,1.3032 -1.27192,1.3032 z" id="path4419" inkscape:connector-curvature="0" /><path style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 129.14164,411.70282 0,0.88618 -1.35533,0 0,1.11554 1.34491,0 0,0.7298 -1.37619,0 0,1.12596 1.38661,0 0,0.77149 6.60983,0 c 0,0 4.50387,-0.80276 4.50387,-5.30663 0,-4.50386 -4.98345,-5.24408 -4.98345,-5.24408 l -90.87296,-0.43787 c 0,0 -0.04525,2.62725 -0.04525,4.68109 1.845329,0 90.29268,0.31277 90.29268,0.31277 0,0 0.80277,-0.0417 0.80277,0.71937 0,0.76107 -0.72979,0.64638 -0.72979,0.64638 z" id="path4421" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccccccsccccscc" /></g></g><g id="g4381" transform="translate(-3.4700316,-242.58675)"><rect y="366.06671" x="42.359982" height="51.003555" width="98.517426" id="rect4187" style="fill:none;fill-opacity:1;stroke:#dcdcdc;stroke-width:0.33938482;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /><g transform="translate(-0.5513317,-0.5513317)" id="g4377"><g inkscape:label="#g3477" style="display:inline;fill:#1e97ec;fill-opacity:1;stroke:none" id="g4271" transform="matrix(0.03699838,0,0,-0.03699838,88.960048,419.11479)"><g style="fill:#1e97ec;fill-opacity:1;stroke:none" id="g4273" clip-path="url(#clipPath18)"><path d="m 101.3,107.13 c 0.02,-2.96 -3.43,-7.55 -7.63,-7.56 -4.48,0.02 -8.87,4.08 -8.87,12.2 0,8.24 4.38,12.48 8.73,12.5 4.34,-0.02 7.79,-4.67 7.77,-8.2 l 0,-8.94 z m 0.23,22.74 0,18.13 8.9,0 0,-56.4 -9.2,0 0,2.6 c -0.75,-0.76 -3.92,-4.07 -9.33,-4.07 -5.64,-0.01 -15.73,4.69 -15.73,21.64 0,14.37 7.54,21.8 16.5,21.8 3.32,0 6.58,-1.18 8.86,-3.7 z M 72,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m 7,0 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 61.18,-20.72 c 0,3.59 -4.04,7.3 -7.78,7.3 -3.22,0 -8.2,-2.05 -8.2,-11.24 0,-9.79 6.12,-11.22 8.15,-11.22 4.03,0 7.83,2.77 7.83,7.05 l 0,8.11 z M 171,172 l 42.5,0 0,-100 -42.5,0 0,100 z m 28.52,-43.91 c -0.93,1.33 -3.98,4.35 -8.34,4.35 -6.77,-0.01 -16.21,-3.92 -16.21,-20.89 0.01,-13.38 10.7,-17.87 15.09,-17.87 4.74,-0.01 8.26,2.75 9.22,3.79 l 0,-4.21 c 0,-3.11 -3.19,-6.15 -8.53,-6.15 -4.6,0.01 -5.08,2.96 -5.08,4.42 l -9.4,0 c 0,-4.67 3.01,-11.49 13.91,-11.5 9.76,0 12.98,3.73 14.37,5.13 1.17,1.18 2.92,4.42 2.92,7.01 l 0,39.83 -7.94,0 -0.01,-3.91 z M 220.5,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 56.03,9.75 -8.23,0 0,-14.54 -4.31,0 0,-6.75 4.31,0 0,-25.44 c 0,-4.85 2.49,-8.66 9.43,-8.65 3.25,0 3.12,-0.02 5.94,0.47 l 0,8.4 c -1.84,-0.32 -2.41,-0.45 -3.31,-0.42 -1.73,0.05 -3.83,0.66 -3.83,3.74 l 0,21.89 6.52,0 0,6.76 -6.52,0 0,14.54 z M 312.5,72 270,72 l 0,100 42.5,0 0,-100 z m 49.5,0 -42.5,0 0,100 42.5,0 0,-100 z m -42.5,0 z m 6.99,47.28 7.71,0 c 0,4.1 2.12,6.25 6.16,6.25 6.44,0.01 6.23,-2.86 6.22,-6.45 -3.98,-1.55 -3.61,-1.51 -9.68,-2.89 -7.71,-1.78 -12.43,-5.08 -12.44,-13.07 0.01,-7.81 5.57,-11.98 11.84,-11.97 6.28,-0.01 7.58,2.02 10.18,3.75 l 0,-3.12 9.84,0 c -2.32,3.74 -1.71,4.47 -1.73,7.28 0.01,2.2 0,23.74 0,23.74 0.01,4.27 -2.21,6.88 -3.66,7.75 -3.62,2.16 -8.09,2.42 -10.57,2.43 -8.81,-0.02 -13.88,-4.45 -13.87,-13.7 z m 20.09,-7.82 0,-5.45 c 0,-2.49 -3.03,-7.31 -8.98,-7.32 -2.9,-0.01 -4.52,2.14 -4.53,4.93 0.01,2.8 2.38,4.95 4.39,5.35 2.09,0.42 6.9,1.65 9.12,2.49 z M 369,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,56.9 9,0 0,-56.9 z" style="fill:#1e97ec;fill-opacity:1;fill-rule:nonzero;stroke:none" id="path4275" inkscape:connector-curvature="0" /></g></g><path inkscape:connector-curvature="0" d="m 106.30956,411.81751 0,4.57684 0.9383,-0.0104 c 0,0 2.97311,0.1668 2.98173,-2.40832 0.008,-2.34582 -2.96088,-2.18938 -2.96088,-2.18938 z m 0.88618,0.87575 0,2.80449 0.45873,0 c 0,0 1.68636,0.0835 1.69937,-1.54299 0.0104,-1.3032 -1.60555,-1.28235 -1.60555,-1.28235 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="path4295" /><path inkscape:connector-curvature="0" id="path4297" d="m 110.35469,411.70282 0,4.62897 2.3979,0 0,-0.86532 -1.40746,0 0,-1.03213 1.35532,0 0,-0.87575 -1.3866,0 0,-0.94874 1.37618,0 0,-0.90703 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path4299" d="m 116.52666,413.02688 0.96958,0 c 0,0 -0.46915,-1.49086 -2.28321,-1.49086 -1.81406,0 -2.38747,1.59511 -2.38747,2.50214 0,0.90703 0.86533,2.29363 2.523,2.29363 1.65767,0 2.1581,-1.36575 2.1581,-1.36575 l -0.98,0 c 0,0 -0.23979,0.47958 -1.16767,0.47958 -0.92789,0 -1.60555,-0.66724 -1.60555,-1.49086 0,-0.82363 0.63596,-1.44916 1.49086,-1.44916 0.8549,0 1.28236,0.52128 1.28236,0.52128 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path4301" d="m 117.6839,411.70282 0,4.62897 1.11554,0 0,-3.64895 -0.80277,0 0,-0.98002 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path4303" d="m 119.64391,416.33179 0,-3.65938 0.86533,0 0.0208,-0.96959 2.47087,0 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path4305" d="m 120.79073,416.33179 0.90703,-1.27192 1.04255,0 0,1.27192 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path sodipodi:nodetypes="cccc" inkscape:connector-curvature="0" id="path4307" d="m 122.25032,414.2571 0.50043,-0.70894 0,0.69851 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path4309" d="m 123.60565,411.70282 0,4.62897 0.35447,0 0,-4.62897 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path4311" d="m 124.80459,412.56815 0,1.19894 0.4483,0.0104 c 0,0 0.66724,-0.0834 0.66724,-0.61511 0,-0.53171 -0.68809,-0.57341 -0.68809,-0.57341 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path inkscape:connector-curvature="0" id="path4313" d="m 124.84629,414.54901 0,1.78278 2.04342,0 0,-4.62897 -1.23022,0 c 0,0 1.04256,0.23979 1.04256,1.55342 0,1.31363 -1.27192,1.3032 -1.27192,1.3032 z" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /><path sodipodi:nodetypes="cccccccccccsccccscc" inkscape:connector-curvature="0" id="path4323" d="m 129.14164,411.70282 0,0.88618 -1.35533,0 0,1.11554 1.34491,0 0,0.7298 -1.37619,0 0,1.12596 1.38661,0 0,0.77149 6.60983,0 c 0,0 4.50387,-0.80276 4.50387,-5.30663 0,-4.50386 -4.98345,-5.24408 -4.98345,-5.24408 l -90.87296,-0.43787 c 0,0 -0.04525,2.62725 -0.04525,4.68109 1.845329,0 90.29268,0.31277 90.29268,0.31277 0,0 0.80277,-0.0417 0.80277,0.71937 0,0.76107 -0.72979,0.64638 -0.72979,0.64638 z" style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" /></g><text sodipodi:linespacing="125%" id="text4427" y="378.7012" x="46.723854" style="font-style:normal;font-weight:normal;font-size:12.70192051px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" xml:space="preserve"><tspan id="tspan4455" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie" y="378.7012" x="46.723854" sodipodi:role="line">FOCAL-69</tspan><tspan id="tspan4459" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie" y="388.62457" x="46.723854" sodipodi:role="line">DEC-08-AJAB-PB</tspan><tspan id="tspan4465" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie" y="398.54794" x="46.723854" sodipodi:role="line">4/29/68 SA:0200</tspan></text> </g><rect style="fill:none;fill-opacity:1;stroke:#dcdcdc;stroke-width:0.33938482;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="rect4428" width="98.517426" height="51.003555" x="38.88995" y="254.30894" /><text xml:space="preserve" style="font-style:normal;font-weight:normal;font-size:12.70192051px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" x="43.253822" y="265.59109" id="text4430" sodipodi:linespacing="125%"><tspan sodipodi:role="line" id="tspan4432" x="43.253822" y="265.59109" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie">ADD.PA</tspan><tspan id="tspan4434" sodipodi:role="line" x="43.253822" y="275.51447" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie">PIDP-8/I Example</tspan><tspan sodipodi:role="line" x="43.253822" y="285.43784" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie" id="tspan4436">11/27/16 SA:0200</tspan><tspan sodipodi:role="line" x="43.253822" y="295.36121" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:7.93869972px;font-family:Dottie;-inkscape-font-specification:Dottie" id="tspan4438" /></text> <g id="g4440" transform="translate(-4.0213633,-112.77037)"><g transform="matrix(0.03699838,0,0,-0.03699838,88.960048,419.11479)" id="g4442" style="display:inline;fill:#1e97ec;fill-opacity:1;stroke:none" inkscape:label="#g3477"><g clip-path="url(#clipPath18)" id="g4444" style="fill:#1e97ec;fill-opacity:1;stroke:none"><path inkscape:connector-curvature="0" id="path4446" style="fill:#1e97ec;fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 101.3,107.13 c 0.02,-2.96 -3.43,-7.55 -7.63,-7.56 -4.48,0.02 -8.87,4.08 -8.87,12.2 0,8.24 4.38,12.48 8.73,12.5 4.34,-0.02 7.79,-4.67 7.77,-8.2 l 0,-8.94 z m 0.23,22.74 0,18.13 8.9,0 0,-56.4 -9.2,0 0,2.6 c -0.75,-0.76 -3.92,-4.07 -9.33,-4.07 -5.64,-0.01 -15.73,4.69 -15.73,21.64 0,14.37 7.54,21.8 16.5,21.8 3.32,0 6.58,-1.18 8.86,-3.7 z M 72,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m 7,0 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 61.18,-20.72 c 0,3.59 -4.04,7.3 -7.78,7.3 -3.22,0 -8.2,-2.05 -8.2,-11.24 0,-9.79 6.12,-11.22 8.15,-11.22 4.03,0 7.83,2.77 7.83,7.05 l 0,8.11 z M 171,172 l 42.5,0 0,-100 -42.5,0 0,100 z m 28.52,-43.91 c -0.93,1.33 -3.98,4.35 -8.34,4.35 -6.77,-0.01 -16.21,-3.92 -16.21,-20.89 0.01,-13.38 10.7,-17.87 15.09,-17.87 4.74,-0.01 8.26,2.75 9.22,3.79 l 0,-4.21 c 0,-3.11 -3.19,-6.15 -8.53,-6.15 -4.6,0.01 -5.08,2.96 -5.08,4.42 l -9.4,0 c 0,-4.67 3.01,-11.49 13.91,-11.5 9.76,0 12.98,3.73 14.37,5.13 1.17,1.18 2.92,4.42 2.92,7.01 l 0,39.83 -7.94,0 -0.01,-3.91 z M 220.5,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,40.4 9,0 0,-40.4 z m -9,0 z m 9,45.9 -9,0 0,10 9,0 0,-10 z m -9,0 z m 56.03,9.75 -8.23,0 0,-14.54 -4.31,0 0,-6.75 4.31,0 0,-25.44 c 0,-4.85 2.49,-8.66 9.43,-8.65 3.25,0 3.12,-0.02 5.94,0.47 l 0,8.4 c -1.84,-0.32 -2.41,-0.45 -3.31,-0.42 -1.73,0.05 -3.83,0.66 -3.83,3.74 l 0,21.89 6.52,0 0,6.76 -6.52,0 0,14.54 z M 312.5,72 270,72 l 0,100 42.5,0 0,-100 z m 49.5,0 -42.5,0 0,100 42.5,0 0,-100 z m -42.5,0 z m 6.99,47.28 7.71,0 c 0,4.1 2.12,6.25 6.16,6.25 6.44,0.01 6.23,-2.86 6.22,-6.45 -3.98,-1.55 -3.61,-1.51 -9.68,-2.89 -7.71,-1.78 -12.43,-5.08 -12.44,-13.07 0.01,-7.81 5.57,-11.98 11.84,-11.97 6.28,-0.01 7.58,2.02 10.18,3.75 l 0,-3.12 9.84,0 c -2.32,3.74 -1.71,4.47 -1.73,7.28 0.01,2.2 0,23.74 0,23.74 0.01,4.27 -2.21,6.88 -3.66,7.75 -3.62,2.16 -8.09,2.42 -10.57,2.43 -8.81,-0.02 -13.88,-4.45 -13.87,-13.7 z m 20.09,-7.82 0,-5.45 c 0,-2.49 -3.03,-7.31 -8.98,-7.32 -2.9,-0.01 -4.52,2.14 -4.53,4.93 0.01,2.8 2.38,4.95 4.39,5.35 2.09,0.42 6.9,1.65 9.12,2.49 z M 369,72 l 42.5,0 0,100 -42.5,0 0,-100 z m 42.5,0 z m -16.75,19.6 -9,0 0,56.9 9,0 0,-56.9 z" /></g></g><path id="path4448" style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 106.30956,411.81751 0,4.57684 0.9383,-0.0104 c 0,0 2.97311,0.1668 2.98173,-2.40832 0.008,-2.34582 -2.96088,-2.18938 -2.96088,-2.18938 z m 0.88618,0.87575 0,2.80449 0.45873,0 c 0,0 1.68636,0.0835 1.69937,-1.54299 0.0104,-1.3032 -1.60555,-1.28235 -1.60555,-1.28235 z" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 110.35469,411.70282 0,4.62897 2.3979,0 0,-0.86532 -1.40746,0 0,-1.03213 1.35532,0 0,-0.87575 -1.3866,0 0,-0.94874 1.37618,0 0,-0.90703 z" id="path4450" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 116.52666,413.02688 0.96958,0 c 0,0 -0.46915,-1.49086 -2.28321,-1.49086 -1.81406,0 -2.38747,1.59511 -2.38747,2.50214 0,0.90703 0.86533,2.29363 2.523,2.29363 1.65767,0 2.1581,-1.36575 2.1581,-1.36575 l -0.98,0 c 0,0 -0.23979,0.47958 -1.16767,0.47958 -0.92789,0 -1.60555,-0.66724 -1.60555,-1.49086 0,-0.82363 0.63596,-1.44916 1.49086,-1.44916 0.8549,0 1.28236,0.52128 1.28236,0.52128 z" id="path4452" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 117.6839,411.70282 0,4.62897 1.11554,0 0,-3.64895 -0.80277,0 0,-0.98002 z" id="path4454" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 119.64391,416.33179 0,-3.65938 0.86533,0 0.0208,-0.96959 2.47087,0 z" id="path4456" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 120.79073,416.33179 0.90703,-1.27192 1.04255,0 0,1.27192 z" id="path4458" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 122.25032,414.2571 0.50043,-0.70894 0,0.69851 z" id="path4460" inkscape:connector-curvature="0" sodipodi:nodetypes="cccc" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 123.60565,411.70282 0,4.62897 0.35447,0 0,-4.62897 z" id="path4462" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 124.80459,412.56815 0,1.19894 0.4483,0.0104 c 0,0 0.66724,-0.0834 0.66724,-0.61511 0,-0.53171 -0.68809,-0.57341 -0.68809,-0.57341 z" id="path4464" inkscape:connector-curvature="0" /><path style="display:inline;fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 124.84629,414.54901 0,1.78278 2.04342,0 0,-4.62897 -1.23022,0 c 0,0 1.04256,0.23979 1.04256,1.55342 0,1.31363 -1.27192,1.3032 -1.27192,1.3032 z" id="path4466" inkscape:connector-curvature="0" /><path style="fill:#1e97ec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.80000001px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 129.14164,411.70282 0,0.88618 -1.35533,0 0,1.11554 1.34491,0 0,0.7298 -1.37619,0 0,1.12596 1.38661,0 0,0.77149 6.60983,0 c 0,0 4.50387,-0.80276 4.50387,-5.30663 0,-4.50386 -4.98345,-5.24408 -4.98345,-5.24408 l -90.87296,-0.43787 c 0,0 -0.04525,2.62725 -0.04525,4.68109 1.845329,0 90.29268,0.31277 90.29268,0.31277 0,0 0.80277,-0.0417 0.80277,0.71937 0,0.76107 -0.72979,0.64638 -0.72979,0.64638 z" id="path4468" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccccccccsccccscc" /></g></g></svg> |
cannot compute difference between binary files
cannot compute difference between binary files
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | The PiDP is typically used with a USB hub as its 'PiDP Universal Storage Device'. Image files (disk images, paper tape images, DECtape images) are then stored on a USB stick, and when inserted to the USB hub the first image file can be mounted into the emulated device. The image files in this directory are typical candidates to put on USB sticks. Mounting works as follows: 1. Select the device you want to mount on by setting the Data Field switches Switch Settings File Extension -------------------------------------------------------------------------------- 000 - mount USB paper tape on the high-speed paper tape reader .pt 001 - mount USB paper tape on the paper tape punch .pt 010 - mount DECtape on DT0 (TU55) .dt 011 - mount DECtape on DT1 (TU55) .dt 100 - mount 8" floppy disk on RX0 (RX01/02) .rx 101 - mount 8" floppy disk on RX1 (RX01/02) .rx 110 - mount 10MB removable disk cartridge on RL0 (RL8A) .rl 111 - mount 10MB removable disk cartridge on RL1 (RL8A) .rl 2. Toggle Sing_Step and Sing_Inst switches together 3. The PiDP will scan all inserted USB sticks and mount the first unmounted image file for that device. Scanning requires the image file to have the extension as per the above table. This is equivalent to using the attach command from the simh command line. Notes: - Multiple image files can reside on one USB stick, as long as they do not have the same extension (and your USB stick is large enough). - You can put any other files on the sticks too, the PiDP will just ignore them. - You can, of course, also just use the simh attach command to mount any image files on the SD card, and ignore the "PiDP Universal USB Storage Device" altogether. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | At main console: ---------------- To run ETOS: R ETOS to start the operating system. Hit return at the option prompt. To login enter LOGIN and then hit return which should then give the login prompt. No prompt will be displayed for entering the login command. At the prompt enter account number such as 0,3 and hit return. Then enter the password at the password prompt. On the distribution ETOS pack the following users exist: At terminal (telnet localhost 4000): ----------- LOGIN;0,4 USER1 <CTRL-M> Users: ------ Account Password 0,4 USER1 0,5 USER2 Shutdown: --------- You can To shutdown enter . ^VS (^V is control-V) !PRIV 4040 !SHUTUP See: ---- http://www.pdp8.net/os/etos/ (introduction) http://highgate.comm.sfu.ca/pdp8/index.html (manuals, search page for ETOS) ftp://ftp.pdp8online.com/images/etos/ (disk images) |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | # Digital License Agreement This document is your Proof of License and the legal agreement governing your use of the OS/8 software. ## 1 DEFINITION SOFTWARE TECHNOLOGY shall mean the sources and binaries to the OS/8, an operating system that runs on PDP-8 computers. DIGITAL’S INTELLECTUAL PROPERTY RIGHTS shall mean DIGITAL’s patent, copyright and trade secret rights in its SOFTWARE TECHNOLOGY. ## 2 LICENSE GRANT Digital grants to Customer a worldwide, non-exclusive, royalty-free license under DIGITAL’s INTELLECTUAL PROPERTY RIGHTS to reproduce, modify, use and distribute the SOFTWARE TECHNOLOGY solely for non-commercial uses. ## 3 TECHNOLOGY TRANSFER AND ACCEPTANCE 3.1 CUSTOMER acknowledges that it accepts the SOFTWARE TECHNOLOGY "AS IS". 3.2 DIGITAL is under no obligation to supply error corrections or updates to the SOFTWARE TECHNOLOGY as they become available, or to provide training, support or consulting for the SOFTWARE TECHNOLOGY. ## 4 WARRANTY DISCLAIMER/LIMITATION OF LIABILITY DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO ANY SOFTWARE TECHNOLOGY LICENSED TO CUSTOMER HEREUNDER, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE, INTELLECTUAL PROPERTY INFRINGEMENT OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF ANY SOFTWARE TECHNOLOGY LICENSE HEREUNDER. ## 5 INDEMNITY CUSTOMER will hold DIGITAL harmless against all liabilities, demands, damages, expenses, or losses arising out of use by CUSTOMER of SOFTWARE TECHNOLOGY or information furnished under this Agreement. ## 6 TERM AND TERMINATION 6.1 This Agreement shall be effective until otherwise terminated. Either party may terminate this Agreement at any time upon 30 days written notice. 6.2 If CUSTOMER shall fail to perform or observe any of the terms and conditions to be performed or observed by it under this Agreement, DIGITAL may in its sole discretion thereafter elect to terminate this Agreement, and this Agreement and all the obligations owed and rights granted herein to CUSTOMER shall immediately terminate. 6.3 The parties agree that the termination of this Agreement shall not release either party from any other liability which shall have accrued to the other party at the time such termination becomes effective, nor affect in any manner the survival of any right, duty or obligation of either party. 6.4 In the event of any termination of this Agreement for any reason, CUSTOMER shall delete all original and all whole or partial copies and derivatives of the SOFTWARE TECHNOLOGY provided to CUSTOMER under this Agreement. CUSTOMER further shall cease to use and distribute the SOFTWARE TECHNOLOGY in all forms immediately upon the date of termination. ## 7 GENERAL TERMS 7.1 This Agreement shall be governed by the laws of the Commonwealth of Massachusetts. 7.2 This Agreement imposes personal obligations on CUSTOMER. CUSTOMER shall not assign any rights under this Agreement not specifically transferable by its terms without the written consent of DIGITAL. 7.3 The SOFTWARE TECHNOLOGY obtained under this Agreement may be subject to US and other government export control regulations. CUSTOMER assures that it will comply with these regulations whenever it exports or re-exports a controlled product or technical data obtained from DIGITAL or any product produced directly from the SOFTWARE TECHNOLOGY. 7.4 The waiver of a breach hereunder may be effected only by a writing signed by the waiving party and shall not constitute a waiver of any other breach. 7.5 CUSTOMER acknowledges that he has read this Agreement, understands it and agrees to be bound by its term and further agrees that it is the complete and exclusive statement of the Agreement between the parties which supersedes all communications and understanding between the parties relating to the subject matter of this Agreement. |
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
> > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # palbart License The following was extracted from the top of [`palbart.c`][1] in this directory: --------- This is free software. There is no fee for using it. You may make any changes that you wish and also give it away. If you can make commercial product out of it, fine, but do not put any limits on the purchaser's right to do the same. If you improve it or fix any bugs, it would be nice if you told me and offered me a copy of the new version. --------- [1]: https://tangentsoft.com/pidp8i/doc/trunk/palbart/palbart.c |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | .\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH PALBART 1 "January 16, 2000" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp <n> insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME palbart \- BART enhanced PDP8 crossassembler .SH SYNOPSIS .B palbart .RI [options] inputfile .br .SH DESCRIPTION This manual page documents briefly the .B palbart command. It is a cross-assembler to for PDP/8 assembly language programs. It will produce an output file in bin format, rim format, and using the appropriate pseudo-ops, a combination of rim and bin formats. A listing file is always produced and with an optional symbol table and/or a symbol cross-reference (concordance). The permanent symbol table can be output in a form that may be read back in so a customized permanent symbol table can be produced. Any detected errors are output to a separate file giving the filename in which they were detected along with the line number, column number and error message as well as marking the error in the listing file. .PP The following file name extensions are used: .PP .pal source code (input) .PP .lst assembly listing (output) .PP .bin assembly output in DEC's bin format (output) .PP .rim assembly output in DEC's rim format (output) .PP .err assembly errors detected (if any) (output) .PP .prm permanent symbol table in form suitable for reading after the EXPUNGE pseudo-op. .PP .SH OPTIONS A summary of options is included below. .TP .B \-d Show symbol table at end of assembly .TP .B \-h Display help. .TP .B \-l Allow generation of literals (default is no literal generation) Show version of program. .TP .B \-p Generate a file with the permanent symbols in it. (To get the current symbol table, assemble a file than has only a $ in it.) .TP .B \-r Produce output in rim format (default is bin format) .TP .B \-v Display version information. .TP .B \-x Generate a cross-reference (concordance) of user symbols. .SH DIAGNOSTICS Assembler error diagnostics are output to an error file and inserted in the listing file. Each line in the error file has the form .PP <filename>(<line>:<col>) : error: <message> at Loc = <loc> .PP An example error message is: .br bintst.pal(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 .PP The error diagnostics put in the listing start with a two character error code (if appropriate) and a short message. A carat '^' is placed under the item in error if appropriate. An example error message is: .PP 17 07616 3000 DCA UNDEF .br UD undefined ^ .br 18 07617 1777 TAD I DUMMY .PP When an indirect is generated, an at character '@' is placed after the the instruction value in the listing as an indicator as follows: .PP 14 03716 1777@ TAD OFFPAG .PP Undefined symbols are marked in the symbol table listing by prepending a '?' to the symbol. Redefined symbols are marked in the symbol table listing by prepending a '#' to the symbol. Examples are: .PP #REDEF 04567 .br SWITCH 07612 .br ?UNDEF 00000 .PP Refer to the code for the diagnostic messages generated. .SH BUGS Only a minimal effort has been made to keep the listing format anything like the PAL-8 listing format. The operation of the conditional assembly pseudo-ops may not function exactly as the DEC versions. I did not have any examples of these so the implementation is my interpretation of how they should work. .PP The RIMPUNch and BINPUNch pseudo-ops do not change the binary output file type that was specified on startup. This was intentional and and allows rim formatted data to be output prior to the actual binary formatted data. On UN*X style systems, the same effect can be achieved ing the "cat" command, but on DOS/Windows systems, doing this was a major chore. .PP The floating point input does not generate values exactly as the DEC compiler does. I worked out several examples by hand and believe that this implementation is slightly more accurate. If I am mistaken, let me know and, if possible, a better method of generating the values. .br .SH HISTORICAL NOTE This assembler was written to support the fleet of PDP-8 systems used by the Bay Area Rapid Transit System. As of early 1997, this includes about 40 PDP-8/E systems driving the train destination signs in passenger stations. .SH REFERENCES This assembler is based on the pal assember by: .br Douglas Jones <jones@cs.uiowa.edu> and .br Rich Coon <coon@convexw.convex.com> .SH DISCLAIMER See the symbol table for the set of pseudo-ops supported. .PP See the code for pseudo-ops that are not standard for PDP/8 assembly. .PP Refer to DEC's "Programming Languages (for the PDP/8)" for complete documentation of pseudo-ops. .PP Refer to DEC's "Introduction to Programming (for the PDP/8)" or a lower level introduction to the assembly language. .SH WARRANTY If you don't like it the way it works or if it doesn't work, that's tough. You're welcome to fix it yourself. That's what you get for using free software. .SH COPYRIGHT NOTICE This is free software. There is no fee for using it. You may make any changes that you wish and also give it away. If you can make a commercial product out of it, fine, but do not put any limits on the purchaser's right to do the same. If you improve it or fix any bugs, it would be nice if you told me and offered me a copy of the new version. Gary Messenbrink <gam@rahul.net> .SH VERSIONS Version Date by Comments .br v1.0 12Apr96 GAM Original .br v1.1 18Nov96 GAM Permanent symbol table initialization error. .br v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. .br v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). .br v1.4 29Nov96 GAM Fixed bug in checksum generation. .br v2.1 08Dec96 GAM Added concordance processing (cross reference). .br v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). .br v2.3 2Feb97 GAM Fixed paging problem in cross reference output. .br v2.4 11Apr97 GAM Fixed problem with some labels being put in cross reference multiple times. .SH AUTHOR This manual page was written by Vince Mulhollon <vlm@execpc.com>, for the Debian GNU/Linux system (but may be used by others). |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 | /******************************************************************************/ /* */ /* Program: PAL (BART version) */ /* File: pal.c */ /* Author: Gary A. Messenbrink */ /* gam@rahul.net */ /* */ /* Purpose: A 2 pass PDP-8 pal-like assembler. */ /* */ /* PAL(1) */ /* */ /* NAME */ /* pal - a PDP/8 pal-like assembler. */ /* */ /* SYNOPSIS: */ /* pal [ -$ -d -h -e -l -p -r -t -v -x ] inputfile */ /* */ /* DESCRIPTION */ /* This is a cross-assembler to for PDP/8 assembly language programs. */ /* It will produce an output file in bin format, rim format, and using the */ /* appropriate pseudo-ops, a combination of rim and bin formats. */ /* A listing file is always produced and with an optional symbol table */ /* and/or a symbol cross-reference (concordance). The permanent symbol */ /* table can be output in a form that may be read back in so a customized */ /* permanent symbol table can be produced. Any detected errors are output */ /* to a separate file giving the filename in which they were detected */ /* along with the line number, column number and error message as well as */ /* marking the error in the listing file. */ /* The following file name extensions are used: */ /* .pal source code (input) */ /* .lst assembly listing (output) */ /* .bin assembly output in DEC's bin format (output) */ /* .rim assembly output in DEC's rim format (output) */ /* .err assembly errors detected (if any) (output) */ /* .prm permanent symbol table in form suitable for reading after */ /* the EXPUNGE pseudo-op. */ /* */ /* OPTIONS */ /* -$ Allow files to not end with $ */ /* -d Dump the symbol table at end of assembly */ /* -h Show help */ /* -e Don't allow generation of links */ /* -l Allow generation of links (default is link generation) */ /* -n No redefinition of permanent symbols with labels */ /* -p Generate a file with the permanent symbols in it. */ /* (To get the current symbol table, assemble a file than has only */ /* a $ in it.) */ /* -r Produce output in rim format (default is bin format) */ /* -tN Set tab stops to N spaces (default is 8) */ /* -v Display program version. */ /* -x Generate a cross-reference (concordance) of user symbols. */ /* */ /* DIAGNOSTICS */ /* Assembler error diagnostics are output to an error file and inserted */ /* in the listing file. Each line in the error file has the form */ /* */ /* <filename>(<line>:<col>) : error: <message> at Loc = <loc> */ /* */ /* An example error message is: */ /* */ /* bintst.pal(17:9) : error: undefined symbol "UNDEF" at Loc = 07616 */ /* */ /* The error diagnostics put in the listing start with a two character */ /* error code (if appropriate) and a short message. A carat '^' is */ /* placed under the item in error if appropriate. */ /* An example error message is: */ /* */ /* 17 07616 3000 DCA UNDEF */ /* UD undefined ^ */ /* 18 07617 1777 TAD I DUMMY */ /* */ /* When an indirect is generated, an at character '@' is placed after the */ /* the instruction value in the listing as an indicator as follows: */ /* */ /* 14 03716 1777@ TAD OFFPAG */ /* */ /* Undefined symbols are marked in the symbol table listing by prepending */ /* a '?' to the symbol. Redefined symbols are marked in the symbol table */ /* listing by prepending a '#' to the symbol. Examples are: */ /* */ /* #REDEF 04567 */ /* SWITCH 07612 */ /* ?UNDEF 00000 */ /* */ /* Refer to the code for the diagnostic messages generated. */ /* */ /* BUGS */ /* This program will accept source that real PAL will not. To ensure */ /* valid source assemble on real or simulated PDP-8. */ /* Different PAL versions have different permanent symbols defined. This */ /* program define more than and PAL version. By default redefining them */ /* as a label is not an error. It is for normal PAL. The -n flag will */ /* make redefining an error. */ /* */ /* Only a minimal effort has been made to keep the listing format */ /* anything like the PAL-8 listing format. */ /* The operation of the conditional assembly pseudo-ops may not function */ /* exactly as the DEC versions. I did not have any examples of these so */ /* the implementation is my interpretation of how they should work. */ /* */ /* The RIMPUNch and BINPUNch pseudo-ops do not change the binary output */ /* file type that was specified on startup. This was intentional and */ /* and allows rim formatted data to be output prior to the actual binary */ /* formatted data. On UN*X style systems, the same effect can be achieved */ /* by using the "cat" command, but on DOS/Windows systems, doing this was */ /* a major chore. */ /* */ /* The floating point input does not generate values exactly as the DEC */ /* compiler does. I worked out several examples by hand and believe that */ /* this implementation is slightly more accurate. If I am mistaken, */ /* let me know and, if possible, a better method of generating the values. */ /* */ /* CDF .-. */ /* Generates 2201 when assembled at 5000. This looks like a bug in OS/8 */ /* PAL */ /* */ /* BUILD and INSTALLATION */ /* The current version has only been built under Linux. */ /* Earlier versions have been built and successfully executed on: */ /* a. Linux (80486 CPU)using gcc */ /* b. RS/6000 (AIX 3.2.5) */ /* c. Borland C++ version 3.1 (large memory model) */ /* d. Borland C++ version 4.52 (large memory model) */ /* with no modifications to the source code. */ /* */ /* On UNIX type systems, store the the program as the pal command */ /* and on PC type systems, store it as pal.exe */ /* */ /* HISTORICAL NOTE: */ /* This assembler was written to support the fleet of PDP-8 systems */ /* used by the Bay Area Rapid Transit System. As of early 1997, */ /* this includes about 40 PDP-8/E systems driving the train destination */ /* signs in passenger stations. */ /* */ /* REFERENCES: */ /* This assembler is based on the pal assembler by: */ /* Douglas Jones <jones@cs.uiowa.edu> and */ /* Rich Coon <coon@convexw.convex.com> */ /* */ /* DISCLAIMER: */ /* See the symbol table for the set of pseudo-ops supported. */ /* See the code for pseudo-ops that are not standard for PDP/8 assembly. */ /* Refer to DEC's "Programming Languages (for the PDP/8)" for complete */ /* documentation of pseudo-ops. */ /* Refer to DEC's "Introduction to Programming (for the PDP/8)" or a */ /* lower level introduction to the assembly language. */ /* */ /* WARRANTY: */ /* If you don't like it the way it works or if it doesn't work, that's */ /* tough. You're welcome to fix it yourself. That's what you get for */ /* using free software. */ /* */ /* COPYRIGHT NOTICE: */ /* This is free software. There is no fee for using it. You may make */ /* any changes that you wish and also give it away. If you can make */ /* a commercial product out of it, fine, but do not put any limits on */ /* the purchaser's right to do the same. If you improve it or fix any */ /* bugs, it would be nice if you told me and offered me a copy of the */ /* new version. */ /* */ /* */ /* Amendments Record: */ /* Version Date by Comments */ /* ------- ------- --- --------------------------------------------------- */ /* v1.0 12Apr96 GAM Original */ /* v1.1 18Nov96 GAM Permanent symbol table initialization error. */ /* v1.2 20Nov96 GAM Added BINPUNch and RIMPUNch pseudo-operators. */ /* v1.3 24Nov96 GAM Added DUBL pseudo-op (24 bit integer constants). */ /* v1.4 29Nov96 GAM Fixed bug in checksum generation. */ /* v2.1 08Dec96 GAM Added concordance processing (cross reference). */ /* v2.2 10Dec96 GAM Added FLTG psuedo-op (floating point constants). */ /* v2.3 2Feb97 GAM Fixed paging problem in cross reference output. */ /* DJG: I started with the 2.5 RK version but found when looking on the net */ /* later that multiple diverging version existed. I have tried to combine */ /* the fixed into one version. I took the version info below from the versions*/ /* I pulled from. */ /* http://dustyoldcomputers.com/pdp-common/reference/host/index.html */ /* http://www.dunnington.u-net.com/public/PDP-8/palbart.c */ /* http://sourcecodebrowser.com/palbart/2.4/palbart-2_84_8c_source.html */ /* http://packages.qa.debian.org/p/palbart.html */ /* v2.4 11Apr97 GAM Fixed problem with some labels being put in cross */ /* reference multiple times. */ /* Started with RK version, Attempted to merge */ /* GAM V2.4 and PNT change DJG */ /* v2.4 29Oct07 RK Added 4 character tabstop; IOTs for TA8/E. */ /* v2.4 19Jan03 PNT Added ASCII pseudo-op, like TEXT but not packed. */ /* v2.5 03Nov07 RK Fixed buffer overflow problem in readLine and */ /* increased symbol table size */ /* v2.6 14Jul03 PNT Added missing TTY symbols, and "1st TTY" symbols. */ /* v2.7 14Jun13 DJG David Gesswein djg@pdp8online.com */ /* Merged other changes found online giving duplicate */ /* Versions in the history */ /* Didn't copy over deleting -l literal flag */ /* All fixes to make it match OS/8 PAL8 better */ /* Fixed handling of IFDEF type conditionals */ /* Fixed excessive redefined symbol errors */ /* PAL8 uses 12 bit symbols and this program label */ /* symbols are 15 bit. */ /* Added FILENAME and DEVNAME psuedo ops */ /* Added OPR and KCF instructions. Fixed RMF */ /* Allowed space after = */ /* Prevented I and D from being deleted by EXPUNGE */ /* Allowed permanent symbols to be redefined with error*/ /* PAL8 updates without message. Error is just warning*/ /* Fixed certain cases of memory reference generation */ /* Allowed unary + */ /* Fixed " character literal at end of line */ /* Fixed errors in reloc handling */ /* Fixed [CDF CIF type expressions */ /* Made title default to first line */ /* Fixed checksum when nopunch used */ /* Fixed FIXTAB */ /* Probably added more subtle bugs */ /* v2.8 15Jun13 DJG Merged versions found on net. See above */ /* Added * to RELOC addresses in listing */ /* Changed default to literal/links on. Added -e to */ /* turn off */ /* Fixed PAGE when RELOC used */ /* Changed SPF to TFL and SPI to TSK */ /* Make error when changing permanent symbol to label */ /* if -e flag is used */ /* Allow space oring in IFZERO etc */ /* Fixed handling of page zero overflow */ /* v2.9 23Jun13 DJG Fixed properly all pages literal handling */ /* changing page doesn't cause loss of last literal */ /* location used. */ /* Fixed bin generation if no origin set */ /* v2.9a 01Jul13 DJG Fixed Comment. Binaries not updated */ /* v2.10 08Feb14 DJG Changed trailer to 8 bytes since pip didn't like */ /* trailer of one 0x80 */ /* v2.11 19Apr15 DPI Fixed incorrect link generation with impled 0200 */ /* starting address. Patch from Doug Ingrams */ /* v2.12 28Apr15 DJG Fixed incorrect handling of reloc, expressions with */ /* undefined symbols. Fixed conditional assembly with */ /* undefined symbols. Added new flag to allow file to */ /* not end with $ */ /* v2.13 02May15 DPI Fixed bug in readLine when removing \r from a blank */ /* line. Changed -s to -$ in -h display. Corrected */ /* version comment. */ /* v2.13 03May15 DJG Moved TITLE, BANK to new additional option. */ /* Change release variable below when you update. Send changes back to */ /* David Gesswein, djg@pdp8online.com. */ /******************************************************************************/ #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> char *release = "pal-2.13, 03 May 2015"; /* Set to 1 and use -e flag to make ( and [ literals errors */ #define LITERAL_ERROR 0 #define LINELEN 132 #define LIST_LINES_PER_PAGE 55 /* Includes 5 line page header. */ #define NAMELEN 128 #define SYMBOL_COLUMNS 5 #define SYMLEN 7 #define SYMBOL_TABLE_SIZE 4096 #define TITLELEN 63 #define XREF_COLUMNS 8 #define ADDRESS_FIELD 00177 #define FIELD_FIELD 070000 #define INDIRECT_BIT 00400 #define LAST_PAGE_LOC 00177 #define OP_CODE 07000 #define PAGE_BIT 00200 #ifdef PAGE_SIZE #undef PAGE_SIZE #endif #define PAGE_SIZE 00200 #define PAGE_FIELD 07600 #define PAGE_ZERO_END 00200 /* Macro to get the number of elements in an array. */ #define DIM(a) (sizeof(a)/sizeof(a[0])) /* Macro to get the address plus one of the end of an array. */ #define BEYOND(a) ((a) + DIM(A)) #define is_blank(c) ((c==' ') || (c=='\t') || (c=='\f') || (c=='>')) #define isend(c) ((c=='\0')|| (c=='\n')) #define isdone(c) ((c=='/') || (isend(c)) || (c==';')) /* Macros for testing symbol attributes. Each macro evaluates to non-zero */ /* (true) if the stated condition is met. */ /* Use these to test attributes. The proper bits are extracted and then */ /* tested. */ #define M_CONDITIONAL(s) ((s & CONDITION) == CONDITION) #define M_DEFINED(s) ((s & DEFINED) == DEFINED) #define M_DUPLICATE(s) ((s & DUPLICATE) == DUPLICATE) #define M_FIXED(s) ((s & FIXED) == FIXED) #define M_LABEL(s) ((s & LABEL) == LABEL) #define M_MRI(s) ((s & MRI) == MRI) #define M_MRIFIX(s) ((s & MRIFIX) == MRIFIX) #define M_PSEUDO(s) ((s & PSEUDO) == PSEUDO) #define M_REDEFINED(s) ((s & REDEFINED) == REDEFINED) #define M_UNDEFINED(s) (!M_DEFINED(s)) #define M_PERM_REDEFINED(s) ((s & PERM_REDEFINED) == PERM_REDEFINED) /* This macro is used to test symbols by the conditional assembly pseudo-ops. */ #define M_DEF(s) (M_DEFINED(s)) #define M_COND(s) (M_CONDITIONAL(s)) #define M_DEFINED_CONDITIONALLY(t) (M_DEF(t) && ((pass==1) ||!M_COND(t))) typedef unsigned char BOOL; typedef unsigned char BYTE; typedef short int WORD16; typedef long int WORD32; #ifndef FALSE #define FALSE 0 #define TRUE (!FALSE) #endif /* Line listing styles. Used to control listing of lines. */ enum linestyle_t { LINE, LINE_VAL, LINE_LOC_VAL, LOC_VAL }; typedef enum linestyle_t LINESTYLE_T; /* Symbol Types. */ /* Note that the names that have FIX as the suffix contain the FIXED bit */ /* included in the value. */ /* */ /* The CONDITION bit is used when processing the conditional assembly PSEUDO- */ /* OPs (e.g., IFDEF). During pass 1 of the assembly, the symbol is either */ /* defined or undefined. The condition bit is set when the symbol is defined */ /* during pass 1 and reset on pass 2 at the location the symbol was defined */ /* during pass 1. When processing conditionals during pass 2, if the symbol */ /* is defined and the condition bit is set, the symbol is treated as if it */ /* were undefined. This gives consistent behavior of the conditional */ /* pseudo-ops during both pass 1 and pass 2. */ enum symtyp { UNDEFINED = 0000, DEFINED = 0001, FIXED = 0002, MRI = 0004 | DEFINED, LABEL = 0010 | DEFINED, REDEFINED = 0020 | DEFINED, DUPLICATE = 0040 | DEFINED, PSEUDO = 0100 | FIXED | DEFINED, CONDITION = 0200 | DEFINED, PERM_REDEFINED = 0400, MRIFIX = MRI | FIXED | DEFINED, DEFFIX = DEFINED | FIXED }; typedef enum symtyp SYMTYP; enum pseudo_t { BANK, BINPUNCH, DECIMAL, DUBL, EJECT, ENPUNCH, EXPUNGE, FIELD, FIXMRI, FIXTAB, FLTG, IFDEF, IFNDEF, IFNZERO, IFZERO, NOPUNCH, OCTAL, PAGE, PAUSE, RELOC, RIMPUNCH, SEGMNT, TEXT, TITLE, XLIST, ZBLOCK, FILENAME, DEVICE, ASCII }; typedef enum pseudo_t PSEUDO_T; struct sym_t { SYMTYP type; char name[SYMLEN]; WORD16 val; int xref_index; int xref_count; }; typedef struct sym_t SYM_T; struct lpool_t { WORD16 loc; WORD16 last_punched; WORD16 pool[PAGE_SIZE]; }; typedef struct lpool_t LPOOL_T; struct emsg_t { char *list; char *file; }; typedef struct emsg_t EMSG_T; struct errsave_t { char *mesg; int col; }; typedef struct errsave_t ERRSAVE_T; struct fltg_ { WORD16 exponent; WORD32 mantissa; }; typedef struct fltg_ FLTG_T; /*----------------------------------------------------------------------------*/ /* Function Prototypes */ int binarySearch( char *name, int start, int symbol_count ); int compareSymbols( const void *a, const void *b ); void conditionFalse( void ); void conditionTrue( void ); SYM_T *defineLexeme( int start, int term, WORD16 val, SYMTYP type ); SYM_T *defineSymbol( char *name, WORD16 val, SYMTYP type, WORD16 start); void endOfBinary( void ); void errorLexeme( EMSG_T *mesg, int col ); void errorMessage( EMSG_T *mesg, int col ); void errorSymbol( EMSG_T *mesg, char *name, int col ); SYM_T *eval( void ); WORD32 evalDubl( WORD32 initial_value ); FLTG_T *evalFltg( void ); SYM_T *evalSymbol( void ); void getArgs( int argc, char *argv[] ); WORD32 getDublExpr( void ); WORD32 getDublExprs( void ); FLTG_T *getFltgExpr( void ); FLTG_T *getFltgExprs( void ); SYM_T *getExpr( void ); WORD16 getExprs( void ); WORD16 incrementClc( void ); void inputDubl( void ); void inputFltg( void ); WORD16 insertLiteral( LPOOL_T *pool, WORD16 value, int fieldpage_index ); char *lexemeToName( char *name, int from, int term ); void listLine( void ); SYM_T *lookup( char *name ); void moveToEndOfLine( void ); void nextLexBlank( void ); void nextLexeme( void ); void normalizeFltg( FLTG_T *fltg ); void onePass( void ); void printCrossReference( void ); void printErrorMessages( void ); void printLine(char *line, WORD16 loc, WORD16 val, LINESTYLE_T linestyle); void printPageBreak( void ); void printPermanentSymbolTable( void ); void printSymbolTable( void ); BOOL pseudoOperators( PSEUDO_T val ); void punchChecksum( void ); void punchLocObject( WORD16 loc, WORD16 val ); void punchLiteralPool( LPOOL_T *p, BOOL punch_page0 ); void punchOutObject( WORD16 loc, WORD16 val ); void punchLeader( int count ); void punchObject( WORD16 val ); void punchOrigin( WORD16 loc ); void readLine( void ); void saveError( char *mesg, int cc ); BOOL testForLiteralCollision( WORD16 loc ); void topOfForm( char *title, char *sub_title ); /*----------------------------------------------------------------------------*/ /* Table of pseudo-ops (directives) which are used to setup the symbol */ /* table on startup and when the EXPUNGE pseudo-op is executed. */ SYM_T pseudo[] = { { PSEUDO, "ASCII", ASCII }, /* Put 8-bit ASCII into memory (see TEXT) */ { PSEUDO, "BINPUN", BINPUNCH }, /* Output in Binary Loader format. */ { PSEUDO, "DECIMA", DECIMAL }, /* Read literal constants in base 10. */ { PSEUDO, "DEVICE", DEVICE }, /* Pack 6 bit device name into memory */ { PSEUDO, "DUBL", DUBL }, /* Ignored (unsupported). */ { PSEUDO, "EJECT", EJECT }, /* Eject a page in the listing. */ { PSEUDO, "ENPUNC", ENPUNCH }, /* Turn on object code generation. */ { PSEUDO, "EXPUNG", EXPUNGE }, /* Remove all symbols from symbol table. */ { PSEUDO, "FIELD", FIELD }, /* Set origin to memory field. */ { PSEUDO, "FILENA", FILENAME }, /* Pack 6 bit filename into memory. */ { PSEUDO, "FIXMRI", FIXMRI }, /* Like =, but creates mem ref instruction*/ { PSEUDO, "FIXTAB", FIXTAB }, /* Mark current symbols as permanent. */ { PSEUDO, "FLTG", FLTG }, /* Ignored (unsupported). */ { PSEUDO, "IFDEF", IFDEF }, /* Assemble if symbol is defined. */ { PSEUDO, "IFNDEF", IFNDEF }, /* Assemble if symbol is not defined. */ { PSEUDO, "IFNZER", IFNZERO }, /* Assemble if symbol value is not 0. */ { PSEUDO, "IFNZRO", IFNZERO }, /* Assemble if symbol value is not 0. */ { PSEUDO, "IFZERO", IFZERO }, /* Assemble if symbol value is 0. */ { PSEUDO, "NOPUNC", NOPUNCH }, /* Turn off object code generation. */ { PSEUDO, "OCTAL", OCTAL }, /* Read literal constants in base 8. */ { PSEUDO, "PAGE", PAGE }, /* Set orign to page +1 or page n (0..37).*/ { PSEUDO, "PAUSE", PAUSE }, /* Ignored */ { PSEUDO, "RELOC", RELOC }, /* Assemble to run at a different address.*/ { PSEUDO, "RIMPUN", RIMPUNCH }, /* Output in Read In Mode format. */ { PSEUDO, "SEGMNT", SEGMNT }, /* Like page, but with page size=1K words.*/ { PSEUDO, "TEXT", TEXT }, /* Pack 6 bit trimmed ASCII into memory. */ { PSEUDO, "XLIST", XLIST }, /* Toggle listing generation. */ { PSEUDO, "ZBLOCK", ZBLOCK }, /* Zero a block of memory. */ { PSEUDO, "TITLE", TITLE }, /* Use the text string as a listing title.*/ { PSEUDO, "BANK", BANK } /* Like field, select some 32K out of 128K*/ }; /* Number o extended pseudo operators to ignore unless command option specified * to enable */ #define NUMBER_ADDITIONAL_PSEUDO 2 /* Symbol Table */ /* The table is put in lexical order on startup, so symbols can be */ /* inserted as desired into the initial table. */ /* really_permanent_symbols aren't removed by EXPUNGE */ SYM_T really_permanent_symbols[] = { { MRIFIX, "I", 00400 }, /* INDIRECT ADDRESSING */ { MRIFIX, "Z", 00000 } /* PAGE ZERO ADDRESS */ }; SYM_T permanent_symbols[] = { /* Memory Reference Instructions */ { MRIFIX, "AND", 00000 }, /* LOGICAL AND */ { MRIFIX, "TAD", 01000 }, /* TWO'S COMPLEMENT ADD */ { MRIFIX, "ISZ", 02000 }, /* INCREMENT AND SKIP IF ZERO */ { MRIFIX, "DCA", 03000 }, /* DEPOSIT AND CLEAR ACC */ { MRIFIX, "JMP", 05000 }, /* JUMP */ { MRIFIX, "JMS", 04000 }, /* JUMP TO SUBROUTINE */ /* Floating Point Interpreter Instructions */ { MRIFIX, "FEXT", 00000 }, /* FLOATING EXIT */ { MRIFIX, "FADD", 01000 }, /* FLOATING ADD */ { MRIFIX, "FSUB", 02000 }, /* FLOATING SUBTRACT */ { MRIFIX, "FMPY", 03000 }, /* FLOATING MULTIPLY */ { MRIFIX, "FDIV", 04000 }, /* FLOATING DIVIDE */ { MRIFIX, "FGET", 05000 }, /* FLOATING GET */ { MRIFIX, "FPUT", 06000 }, /* FLOATING PUT */ { FIXED, "FNOR", 07000 }, /* FLOATING NORMALIZE */ { FIXED, "FEXT", 00000 }, /* EXIT FROM FLOATING POINT INTERPRETER */ { FIXED, "SQUARE", 00001 }, /* SQUARE C(FAC) */ { FIXED, "SQROOT", 00002 }, /* TAKE SQUARE ROOT OF C(FAC) */ /* Group 1 Operate Microinstrcutions */ { FIXED, "OPR", 07000 }, /* NO OPERATION */ { FIXED, "NOP", 07000 }, /* NO OPERATION */ { FIXED, "IAC", 07001 }, /* INCREMENT AC */ { FIXED, "RAL", 07004 }, /* ROTATE AC AND LINK LEFT ONE */ { FIXED, "RTL", 07006 }, /* ROTATE AC AND LINK LEFT TWO */ { FIXED, "RAR", 07010 }, /* ROTATE AC AND LINK RIGHT ONE */ { FIXED, "RTR", 07012 }, /* ROTATE AC AND LINK RIGHT TWO */ { FIXED, "CML", 07020 }, /* COMPLEMENT LINK */ { FIXED, "CMA", 07040 }, /* COMPLEMEMNT AC */ { FIXED, "CLL", 07100 }, /* CLEAR LINK */ { FIXED, "CLA", 07200 }, /* CLEAR AC */ /* Group 2 Operate Microinstructions */ { FIXED, "BSW", 07002 }, /* Swap bytes in AC (PDP/8e) */ { FIXED, "HLT", 07402 }, /* HALT THE COMPUTER */ { FIXED, "OSR", 07404 }, /* INCLUSIVE OR SR WITH AC */ { FIXED, "SKP", 07410 }, /* SKIP UNCONDITIONALLY */ { FIXED, "SNL", 07420 }, /* SKIP ON NON-ZERO LINK */ { FIXED, "SZL", 07430 }, /* SKIP ON ZERO LINK */ { FIXED, "SZA", 07440 }, /* SKIP ON ZERO AC */ { FIXED, "SNA", 07450 }, /* SKIP ON NON=ZERO AC */ { FIXED, "SMA", 07500 }, /* SKIP MINUS AC */ { FIXED, "SPA", 07510 }, /* SKIP ON POSITIVE AC (ZERO IS POSITIVE) */ /* Combined Operate Microinstructions */ { FIXED, "CIA", 07041 }, /* COMPLEMENT AND INCREMENT AC */ { FIXED, "STL", 07120 }, /* SET LINK TO 1 */ { FIXED, "GLK", 07204 }, /* GET LINK (PUT LINK IN AC BIT 11) */ { FIXED, "STA", 07240 }, /* SET AC TO -1 */ { FIXED, "LAS", 07604 }, /* LOAD ACC WITH SR */ /* MQ Instructions (PDP/8e) */ { FIXED, "MQL", 07421 }, /* Load MQ from AC, then clear AC. */ { FIXED, "MQA", 07501 }, /* Inclusive OR MQ with AC */ { FIXED, "SWP", 07521 }, /* Swap AC and MQ */ { FIXED, "ACL", 07701 }, /* Load MQ into AC */ /* Program Interrupt */ { FIXED, "IOT", 06000 }, { FIXED, "ION", 06001 }, /* TURN INTERRUPT PROCESSOR ON */ { FIXED, "IOF", 06002 }, /* TURN INTERRUPT PROCESSOR OFF */ /* Program Interrupt, PDP-8/e */ { FIXED, "SKON", 06000 }, /* Skip if interrupt on and turn int off. */ { FIXED, "SRQ", 06003 }, /* Skip on interrupt request. */ { FIXED, "GTF", 06004 }, /* Get interrupt flags. */ { FIXED, "RTF", 06005 }, /* Restore interrupt flags. */ { FIXED, "SGT", 06006 }, /* Skip on greater than flag. */ { FIXED, "CAF", 06007 }, /* Clear all flags. */ /* Keyboard/Reader */ { FIXED, "KCF", 06030 }, /* CLEAR KEYBOAR FLAG */ { FIXED, "KSF", 06031 }, /* SKIP ON KEYBOARD FLAG */ { FIXED, "KCC", 06032 }, /* CLEAR KEYBOARD FLAG & READ CHAR */ { FIXED, "KRS", 06034 }, /* READ KEYBOARD BUFFER (STATIC) */ { FIXED, "KIE", 06035 }, /* AC11 TO KEYBD/RDR INT ENABLE F/F */ { FIXED, "KRB", 06036 }, /* READ KEYBOARD BUFFER & CLEAR FLAG */ /* Teleprinter/Punch */ { FIXED, "TFL", 06040 }, /* SET TELEPRINTER/PUNCH FLAG */ { FIXED, "TSF", 06041 }, /* SKIP ON TELEPRINTER FLAG */ { FIXED, "TCF", 06042 }, /* CLEAR TELEPRINTER FLAG */ { FIXED, "TPC", 06044 }, /* LOAD TELEPRINTER & PRINT */ { FIXED, "TSK", 06045 }, /* SKIP IF TELETYPE INTERRUPT */ { FIXED, "TLS", 06046 }, /* LOAD TELPRINTER & CLEAR FLAG */ /* High Speed Paper Tape Reader */ { FIXED, "RSF", 06011 }, /* SKIP ON READER FLAG */ { FIXED, "RRB", 06012 }, /* READ READER BUFFER AND CLEAR FLAG */ { FIXED, "RFC", 06014 }, /* READER FETCH CHARACTER */ /* PC8-E High Speed Paper Tape Reader & Punch */ { FIXED, "RPE", 06010 }, /* Set interrupt enable for reader/punch */ { FIXED, "PCE", 06020 }, /* Clear interrupt enable for rdr/punch */ { FIXED, "RCC", 06016 }, /* Read reader buffer, clear flags & buf, */ /* and fetch character. */ /* High Speed Paper Tape Punch */ { FIXED, "PSF", 06021 }, /* SKIP ON PUNCH FLAG */ { FIXED, "PCF", 06022 }, /* CLEAR ON PUNCH FLAG */ { FIXED, "PPC", 06024 }, /* LOAD PUNCH BUFFER AND PUNCH CHARACTER* */ { FIXED, "PLS", 06026 }, /* LOAD PUNCH BUFFER AND CLEAR FLAG */ /* DECassette TU60 (RK 20071008) */ { FIXED, "KCLR", 06700 }, /* Clear all (clear A and B) */ { FIXED, "KSDR", 06701 }, /* Skip if data flag set */ { FIXED, "KSEN", 06702 }, /* Skip if EOT/BOT, not ready, or empty */ { FIXED, "KSBF", 06703 }, /* Skip if ready flag set */ { FIXED, "KLSA", 06704 }, /* AC4-11 -> A, clear A, -(AC4-11) -> A */ { FIXED, "KSAF", 06705 }, /* Skip on any flag or error */ { FIXED, "KGOA", 06706 }, /* Assert status A and transfer data to AC*/ { FIXED, "KRSB", 06707 }, /* Transfer B -> AC4-11 */ /* DECtape Transport Type TU55 and DECtape Control Type TC01 */ { FIXED, "DTRA", 06761 }, /* Contents of status register is ORed */ /* into AC bits 0-9 */ { FIXED, "DTCA", 06762 }, /* Clear status register A, all flags */ /* undisturbed */ { FIXED, "DTXA", 06764 }, /* Status register A loaded by exclusive */ /* OR from AC. If AC bit 10=0, clear */ /* error flags; if AC bit 11=0, DECtape */ /* control flag is cleared. */ { FIXED, "DTLA", 06766 }, /* Combination of DTCA and DTXA */ { FIXED, "DTSF", 06771 }, /* Skip if error flag is 1 or if DECtape */ /* control flag is 1 */ { FIXED, "DTRB", 06772 }, /* Contents of status register B is */ /* ORed into AC */ { FIXED, "DTLB", 06774 }, /* Memory field portion of status */ /* register B loaded from AC bits 6-8 */ /* Disk File and Control, Type DF32 */ { FIXED, "DCMA", 06601 }, /* CLEAR DISK MEMORY REQUEST AND */ /* INTERRUPT FLAGS */ { FIXED, "DMAR", 06603 }, /* LOAD DISK FROM AC, CLEAR AC READ */ /* INTO CORE, CLEAR INTERRUPT FLAG */ { FIXED, "DMAW", 06605 }, /* LOAD DISK FROM AC, WRITE ONTO DISK */ /* FROM CORE, CLEAR INTERRUPT FLAG */ { FIXED, "DCEA", 06611 }, /* CLEAR DISK EXTENDED ADDRESS AND */ { FIXED, "DSAC", 06612 }, /* SKIP IF ADDRESS CONFIRMED FLAG = 1 */ /* MEMORY ADDRESS EXTENSION REGISTER */ { FIXED, "DEAL", 06615 }, /* CLEAR DISK EXTENDED ADDRESS AND */ /* MEMORY ADDRESS EXTENSION REGISTER */ /* AND LOAD SAME FROM AC */ { FIXED, "DEAC", 06616 }, /* CLEAR AC, LOAD AC FROM DISK EXTENDED */ /* ADDRESS REGISTER, SKIP IF ADDRESS */ /* CONFIRMED FLAG = 1 */ { FIXED, "DFSE", 06621 }, /* SKIP IF PARITY ERROR, DATA REQUEST */ /* LATE, OR WRITE LOCK SWITCH FLAG = 0 */ /* (NO ERROR) */ { FIXED, "DFSC", 06622 }, /* SKIP IF COMPLETION FLAG = 1 (DATA */ /* TRANSFER COMPLETE) */ { FIXED, "DMAC", 06626 }, /* CLEAR AC, LOAD AC FROM DISK MEMORY */ /* ADDRESS REGISTER */ /* Disk File and Control, Type RF08 */ { FIXED, "DCIM", 06611 }, { FIXED, "DIML", 06615 }, { FIXED, "DIMA", 06616 }, { FIXED, "DISK", 06623 }, { FIXED, "DCXA", 06641 }, { FIXED, "DXAL", 06643 }, { FIXED, "DXAC", 06645 }, { FIXED, "DMMT", 06646 }, /* Memory Extension Control, Type 183 */ { FIXED, "CDF", 06201 }, /* CHANGE DATA FIELD */ { FIXED, "CIF", 06202 }, /* CHANGE INSTRUCTION FIELD */ { FIXED, "CDI", 06203 }, /* Change data & instrution field. */ { FIXED, "RDF", 06214 }, /* READ DATA FIELD */ { FIXED, "RIF", 06224 }, /* READ INSTRUCTION FIELD */ { FIXED, "RIB", 06234 }, /* READ INTERRUPT BUFFER */ { FIXED, "RMF", 06244 }, /* RESTORE MEMORY FIELD */ /* Memory Parity, Type MP8/I (MP8/L) */ { FIXED, "SMP", 06101 }, /* SKIP IF MEMORY PARITY FLAG = 0 */ { FIXED, "CMP", 06104 }, /* CLEAR MEMORY PAIRTY FLAG */ /* Memory Parity, Type MP8-E (PDP8/e) */ { FIXED, "DPI", 06100 }, /* Disable parity interrupt. */ { FIXED, "SNP", 06101 }, /* Skip if no parity error. */ { FIXED, "EPI", 06103 }, /* Enable parity interrupt. */ { FIXED, "CNP", 06104 }, /* Clear parity error flag. */ { FIXED, "CEP", 06106 }, /* Check for even parity. */ { FIXED, "SPO", 06107 }, /* Skip on parity option. */ /* Data Communications Systems, Type 680I */ { FIXED, "TTINCR", 06401 }, /* The content of the line select */ /* register is incremented by one. */ { FIXED, "TTI", 06402 }, /* The line status word is read and */ /* sampled. If the line is active for */ /* the fourth time, the line bit is */ /* shifted into the character assembly */ /* word. If the line bit is active for */ /* a number of times less than four, */ /* the count is incremented. If the */ /* line is not active, the active/inac- */ /* tive status of the line is recorded */ { FIXED, "TTO", 06404 }, /* The character in the AC is shifted */ /* right one position, zeros are shifted */ /* into vacated positions, and the orig- */ /* inal content of AC11 is transferred */ /* out of the computer on the TTY line. */ { FIXED, "TTCL", 06411 }, /* The line select register is cleared. */ { FIXED, "TTSL", 06412 }, /* The line select register is loaded by */ /* an OR transfer from the content of */ /* of AC5-11, the the AC is cleared. */ { FIXED, "TTRL", 06414 }, /* The content of the line select regis- */ /* ter is read into AC5-11 by an OR */ /* transfer. */ { FIXED, "TTSKP", 06421 }, /* Skip if clock flag is a 1. */ { FIXED, "TTXON", 06424 }, /* Clock 1 is enabled to request a prog- */ /* ram interrupt and clock 1 flag is */ /* cleared. */ { FIXED, "TTXOF", 06422 }, /* Clock 1 is disabled from causing a */ /* program interrupt and clock 1 flag */ /* is cleared. */ }; /* End-of-Symbols for Permanent Symbol Table */ /* Global variables */ SYM_T *symtab; /* Symbol Table */ int symbol_top; /* Number of entries in symbol table. */ SYM_T *fixed_symbols; /* Start of the fixed symbol table entries. */ int number_of_fixed_symbols; /*----------------------------------------------------------------------------*/ WORD16 *xreftab; /* Start of the concordance table. */ ERRSAVE_T error_list[20]; int save_error_count; #define GET_PAGE_INDEX(x) (((x) & 07600) >> 7) #define MAX_PAGES 32 LPOOL_T cp[MAX_PAGES]; /* Storage for page constants. */ int max_page_used[MAX_PAGES]; char s_detected[] = "detected"; char s_error[] = "error"; char s_errors[] = "errors"; char s_no[] = "No"; char s_page[] = "Page"; char s_symtable[] = "Symbol Table"; char s_xref[] = "Cross Reference"; char s_generated[] = "generated"; char s_link[] = "link"; char s_links[] = "links"; /* Assembler diagnostic messages. */ /* Some attempt has been made to keep continuity with the PAL-III and */ /* MACRO-8 diagnostic messages. If a diagnostic indicator, (e.g., IC) */ /* exists, then the indicator is put in the listing as the first two */ /* characters of the diagnostic message. The PAL-III indicators where used */ /* when there was a choice between using MACRO-8 and PAL-III indicators. */ /* The character pairs and their meanings are: */ /* DT Duplicate Tag (symbol) */ /* IC Illegal Character */ /* ID Illegal Redefinition of a symbol. An attempt was made to give */ /* a symbol a new value not via =. */ /* IE Illegal Equals An equal sign was used in the wrong context, */ /* (e.g., A+B=C, or TAD A+=B) */ /* II Illegal Indirect An off page reference was made, but a literal */ /* could not be generated because the indirect bit was already set. */ /* IR Illegal Reference (address is not on current page or page zero) */ /* ND No $ (the program terminator) at end of file. */ /* PE Current, Non-Zero Page Exceeded (literal table flowed into code) */ /* RD ReDefintion of a symbol */ /* ST Symbol Table full */ /* UA Undefined Address (undefined symbol) */ /* ZE Zero Page Exceeded (see above, or out of space) */ EMSG_T duplicate_label = { "DT duplicate", "duplicate label" }; EMSG_T illegal_blank = { "IC illegal blank", "illegal blank" }; EMSG_T illegal_character = { "IC illegal char", "illegal character" }; EMSG_T illegal_expression = { "IC in expression", "illegal expression" }; EMSG_T label_syntax = { "IC label syntax", "label syntax" }; EMSG_T not_a_number = { "IC numeric syntax", "numeric syntax of" }; EMSG_T number_not_radix = { "IC radix", "number not in current radix"}; EMSG_T symbol_syntax = { "IC symbol syntax", "symbol syntax" }; EMSG_T illegal_equals = { "IE illegal =", "illegal equals" }; EMSG_T illegal_indirect = { "II off page", "illegal indirect" }; EMSG_T illegal_reference = { "IR off page", "illegal reference" }; EMSG_T undefined_symbol = { "UD undefined", "undefined symbol" }; EMSG_T redefined_symbol = { "RD redefined", "redefined symbol" }; EMSG_T illegal_redefine = { "ID redefined", "Illegal redefine of symbol" }; EMSG_T literal_overflow = { "PE page exceeded", "current page literal capacity exceeded" }; EMSG_T pz_literal_overflow = { "ZE page exceeded", "page zero capacity exceeded" }; EMSG_T dubl_overflow = { "dubl overflow", "DUBL value overflow" }; EMSG_T fltg_overflow = { "fltg overflow", "FLTG value overflow" }; EMSG_T zblock_too_small = { "expr too small", "ZBLOCK value too small" }; EMSG_T zblock_too_large = { "expr too large", "ZBLOCK value too large" }; EMSG_T end_of_file = { "ND no $ at EOF", "No $ at End-of-File" }; EMSG_T no_pseudo_op = { "not implemented", "not implemented pseudo-op" }; EMSG_T illegal_field_value = { "expr out of range", "field value not in range of 0 through 7" }; EMSG_T literal_gen_off = { "literals off", "literal generation is off" }; EMSG_T no_literal_value = { "no value", "no literal value" }; EMSG_T text_string = { "no delimiter", "text string delimiters not matched" }; EMSG_T in_rim_mode = { "not OK in rim mode" "FIELD pseudo-op not valid in RIM mode" }; EMSG_T lt_expected = { "'<' expected", "'<' expected" }; EMSG_T symbol_table_full = { "ST Symbol Tbl Full", "Symbol Table Full" }; /*----------------------------------------------------------------------------*/ FILE *errorfile; FILE *infile; FILE *listfile; FILE *listsave; FILE *objectfile; FILE *objectsave; char errorpathname[NAMELEN]; char filename[NAMELEN]; char listpathname[NAMELEN]; char objectpathname[NAMELEN]; char *pathname; char permpathname[NAMELEN]; int tabstops; /* number of characters to expand a tab to */ int list_lineno; int list_pageno; char list_title[LINELEN]; BOOL list_title_set; /* Set if TITLE pseudo-op used. */ char line[LINELEN]; /* Input line. */ int lineno; /* Current line number. */ int page_lineno; /* print line number on current page. */ BOOL listed; /* Listed flag. */ BOOL listedsave; int cc; /* Column Counter (char position in line). */ WORD16 checksum; /* Generated checksum */ BOOL binary_data_output; /* Set true when data has been output. */ WORD16 clc; /* Location counter */ WORD16 cplc; /* Current page literal counter. */ char delimiter; /* Character immediately after eval'd term. */ int errors; /* Number of errors found so far. */ int links; /* Number of links generated so far. */ BOOL error_in_line; /* TRUE if error on current line. */ int errors_pass_1; /* Number of errors on pass 1. */ WORD16 field; /* Current field */ WORD16 fieldlc; /* location counter without field portion. */ BOOL fltg_input; /* TRUE when doing floating point input. */ BOOL indirect_generated; /* TRUE if an off page address generated. */ int last_xref_lexstart; /* Column where last xref symbol was located. */ int last_xref_lineno; /* Line where last xref symbol was located. */ int lexstartprev; /* Where previous lexeme started. */ int lextermprev; /* Where previous lexeme ended. */ int lexstart; /* Index of current lexeme on line. */ int lexterm; /* Index of character after current lexeme. */ BOOL literals_ok; /* Generate literals, ignore ID redefine err */ BOOL perm_redef_error; /* Make redefining perm sym with labels error */ int maxcc; /* Current line length. */ BOOL overflow; /* Overflow flag for math routines. */ int pass; /* Number of current pass. */ BOOL print_permanent_symbols; WORD16 pzlc; /* Page Zero literal counter. */ WORD16 radix; /* Default number radix. */ WORD16 reloc; /* The relocation distance. */ BOOL rim_mode; /* Generate rim format, defaults to bin */ BOOL dollar_not_required; /* $ not required at end of file */ BOOL additional_enabled; /* True if extended functions over PAL8 */ /* enabled */ BOOL symtab_print; /* Print symbol table flag */ BOOL xref; FLTG_T fltg_ac; /* Value holder for evalFltg() */ SYM_T sym_eval = { DEFINED, "", 0 }; /* Value holder for eval() */ SYM_T sym_getexpr = { DEFINED, "", 0 }; /* Value holder for getexpr() */ SYM_T sym_undefined = { UNDEFINED, "", 0 };/* Symbol Table Terminator */ /******************************************************************************/ /* */ /* Function: main */ /* */ /* Synopsis: Starting point. Controls order of assembly. */ /* */ /******************************************************************************/ int main( int argc, char *argv[] ) { int ix; int space; /* Set the default values for global symbols. */ binary_data_output = FALSE; fltg_input = FALSE; literals_ok = TRUE; perm_redef_error = FALSE; print_permanent_symbols = FALSE; rim_mode = FALSE; dollar_not_required = FALSE; additional_enabled = FALSE; symtab_print = FALSE; xref = FALSE; pathname = NULL; /* Get the options and pathnames */ getArgs( argc, argv ); /* Setup the error file in case symbol table overflows while installing the */ /* permanent symbols. */ errorfile = fopen( errorpathname, "w" ); if (errorfile == NULL) { fprintf( stderr, "Could not open error file %s: %s\n", errorpathname, strerror(errno)); exit( -1 ); } errors = 0; save_error_count = 0; pass = 0; /* This is required for symbol table initialization. */ symtab = (SYM_T *) malloc( sizeof( SYM_T ) * SYMBOL_TABLE_SIZE ); if( symtab == NULL ) { fprintf( stderr, "Could not allocate memory for symbol table.\n"); exit( -1 ); } /* Place end marker in symbol table. */ symtab[0] = sym_undefined; symbol_top = 0; number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Enter the pseudo-ops into the symbol table */ for( ix = 0; ix < DIM( pseudo ) - (additional_enabled ? 0 : NUMBER_ADDITIONAL_PSEUDO) ; ix++ ) { defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); } /* Enter the predefined symbols into the table. */ /* Also make them part of the permanent symbol table. */ for( ix = 0; ix < DIM( really_permanent_symbols ); ix++ ) { defineSymbol( really_permanent_symbols[ix].name, really_permanent_symbols[ix].val, really_permanent_symbols[ix].type | DEFFIX , 0 ); } /* Enter the predefined symbols into the table. */ /* Also make them part of the permanent symbol table. */ for( ix = 0; ix < DIM( permanent_symbols ); ix++ ) { defineSymbol( permanent_symbols[ix].name, permanent_symbols[ix].val, permanent_symbols[ix].type | DEFFIX , 0 ); } number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Do pass one of the assembly */ checksum = 0; pass = 1; page_lineno = LIST_LINES_PER_PAGE; onePass(); errors_pass_1 = errors; /* Set up for pass two */ rewind( infile ); /*Opened in main errorfile = fopen( errorpathname, "w" );*/ objectfile = fopen( objectpathname, "wb" ); if (objectfile == NULL) { fprintf( stderr, "Could not open object file %s: %s\n", objectpathname, strerror(errno)); exit( -1 ); } objectsave = objectfile; listfile = fopen( listpathname, "w" ); if (listfile == NULL) { fprintf( stderr, "Could not open list file %s: %s\n", listpathname, strerror(errno)); exit( -1 ); } listsave = NULL; punchLeader( 0 ); checksum = 0; /* Do pass two of the assembly */ errors = 0; save_error_count = 0; page_lineno = LIST_LINES_PER_PAGE; if( xref ) { /* Get the amount of space that will be required for the concordance. */ for( space = 0, ix = 0; ix < symbol_top; ix++ ) { symtab[ix].xref_index = space; /* Index into concordance table. */ space += symtab[ix].xref_count + 1; symtab[ix].xref_count = 0; /* Clear the count for pass 2. */ } /* Allocate the necessary space. */ xreftab = (WORD16 *) malloc( sizeof( WORD16 ) * space ); /* Clear the cross reference space. */ for( ix = 0; ix < space; ix++ ) { xreftab[ix] = 0; } } pass = 2; onePass(); /* Undo effects of NOPUNCH for any following checksum */ objectfile = objectsave; punchChecksum(); /* Works great for trailer. */ punchLeader( 8 ); /* undo effects of XLIST for any following output to listing file. */ if( listfile == NULL ) { listfile = listsave; } /* Display value of error counter. */ if( errors == 0 ) { fprintf( listfile, "\n %s %s %s\n", s_no, s_detected, s_errors ); } else { fprintf( errorfile, "\n %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); fprintf( listfile, "\n %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); fprintf( stderr, " %d %s %s\n", errors, s_detected, ( errors == 1 ? s_error : s_errors )); } /* Display value of link counter. */ if( links == 0 ) { fprintf( listfile, " %s %s %s\n", s_no, s_links, s_generated ); } else { fprintf( errorfile, " %d %s %s\n", links, ( links == 1 ? s_link : s_links ), s_generated); fprintf( listfile, " %d %s %s\n", links, ( links == 1 ? s_link : s_links ), s_generated); fprintf( stderr, " %d %s %s\n", links, ( links == 1 ? s_link : s_links ), s_generated); } if( symtab_print ) { printSymbolTable(); } if( print_permanent_symbols ) { printPermanentSymbolTable(); } if( xref ) { printCrossReference(); } fclose( objectfile ); fclose( listfile ); fclose( errorfile ); if( errors == 0 && errors_pass_1 == 0 ) { remove( errorpathname ); } return( errors != 0 ); } /* main() */ /******************************************************************************/ /* */ /* Function: getArgs */ /* */ /* Synopsis: Parse command line, set flags accordingly and setup input and */ /* output files. */ /* */ /******************************************************************************/ void getArgs( int argc, char *argv[] ) { int len; int ix, jx; /* Set the defaults */ errorfile = NULL; infile = NULL; listfile = NULL; listsave = NULL; objectfile = NULL; objectsave = NULL; tabstops = 8; for( ix = 1; ix < argc; ix++ ) { if( argv[ix][0] == '-' ) { for( jx = 1; argv[ix][jx] != 0; jx++ ) { switch( argv[ix][jx] ) { case '$': dollar_not_required = TRUE; break; case 'd': symtab_print = TRUE; break; case 'a': additional_enabled = TRUE; break; case 'r': rim_mode = TRUE; break; case 'e': literals_ok = FALSE; break; case 'l': literals_ok = TRUE; break; case 'n': perm_redef_error = TRUE; break; case 'p': print_permanent_symbols = TRUE; break; /* Added -tN; RK 20071029 */ /* Damn, this is ugly, we should use getopt() */ case 't': if (argv [ix][jx + 1]) { tabstops = atoi (argv [ix] + (jx + 1)); /* advance past numbers */ for (jx++; argv [ix][jx]; jx++) ; jx--; } else { ix++; if (ix >= argc) { fprintf( stderr, "%s: missing argument for -t, expected number of tabsopts\n", argv[0] ); exit( -1 ); } for (jx = 0; argv [ix][jx]; jx++) ; jx--; tabstops = atoi (argv [ix]); } break; case 'x': xref = TRUE; break; case 'v': fprintf( stderr, "%s\n", release ); fflush( stderr ); exit( -1 ); break; default: fprintf( stderr, "%s: unknown flag: %s\n", argv[0], argv[ix] ); case 'h': fprintf( stderr, " -$ -- allow file to not end with $\n" ); fprintf( stderr, " -a -- enable additional function not in PAL8\n" ); fprintf( stderr, " -d -- dump symbol table\n" ); fprintf( stderr, " -e -- error if link generated\n" ); fprintf( stderr, " -h -- show this help\n" ); fprintf( stderr, " -l -- generate literal/link (default)\n" ); fprintf( stderr, " -n -- no redefining with label permanent symbols\n" ); fprintf( stderr, " -p -- output permanent symbols to file\n" ); fprintf( stderr, " -r -- output rim format file\n" ); fprintf( stderr, " -t N -- set tab stops to N\n" ); fprintf( stderr, " -v -- display version\n" ); fprintf( stderr, " -x -- output cross reference to file\n" ); fflush( stderr ); exit( -1 ); } /* end switch */ } /* end for */ } else { if( pathname != NULL ) { fprintf( stderr, "%s: too many input files\n", argv[0] ); exit( -1 ); } pathname = &argv[ix][0]; } } /* end for */ if( pathname == NULL ) { fprintf( stderr, "%s: no input file specified\n", argv[0] ); exit( -1 ); } len = strlen( pathname ); if( len > NAMELEN - 5 ) { fprintf( stderr, "%s: pathname \"%s\" too long\n", argv[0], pathname ); exit( -1 ); } /* Now open the input file. */ if(( infile = fopen( pathname, "r" )) == NULL ) { fprintf( stderr, "%s: cannot open \"%s\": %s\n", argv[0], pathname, strerror(errno) ); exit( -1 ); } /* Now make the pathnames */ /* Find last '.', if it exists. */ jx = len - 1; while( pathname[jx] != '.' && pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) { jx--; } switch( pathname[jx] ) { case '.': break; case '/': case '\\': jx = len; break; default: break; } /* Add the pathname extensions. */ strncpy( objectpathname, pathname, jx ); objectpathname[jx] = '\0'; strcat( objectpathname, rim_mode ? ".rim" : ".bin" ); strncpy( listpathname, pathname, jx ); listpathname[jx] = '\0'; strcat( listpathname, ".lst" ); strncpy( errorpathname, pathname, jx ); errorpathname[jx] = '\0'; strcat( errorpathname, ".err" ); strncpy( permpathname, pathname, jx ); permpathname[jx] = '\0'; strcat( permpathname, ".prm" ); /* Extract the filename from the path. */ if( isalpha( pathname[0] ) && pathname[1] == ':' && pathname[2] != '\\' ) { pathname[1] = '\\'; /* MS-DOS style pathname */ } jx = len - 1; while( pathname[jx] != '/' && pathname[jx] != '\\' && jx >= 0 ) { jx--; } strcpy( filename, &pathname[jx + 1] ); } /* getArgs() */ /******************************************************************************/ /* */ /* Function: clearLiteralTable */ /* */ /* Synopsis: Clear the cp and max_page_used data storing literal */ /* information. */ /* */ /******************************************************************************/ void clearLiteralTable() { int i; for (i = 0; i < DIM(cp); i++) { cp[i].loc = 0200; /* Points to end of page for [] operands. */ cp[i].last_punched = 0200; /* Points to end of page for [] operands. */ } memset(max_page_used, 0, sizeof(max_page_used)); } /******************************************************************************/ /* */ /* Function: onePass */ /* */ /* Synopsis: Do one assembly pass. */ /* */ /******************************************************************************/ void onePass() { char name[SYMLEN]; WORD16 newclc; BOOL scanning_line; int start; SYM_T *sym; int term; WORD16 val; clc = 0200; /* Default starting address is 200 octal. */ field = 0; fieldlc = clc & 07777; reloc = 0; clearLiteralTable(); listed = TRUE; lineno = 0; list_pageno = 0; list_lineno = 0; last_xref_lexstart = 0; last_xref_lineno = 0; list_title_set = FALSE; radix = 8; /* Initial radix is octal (base 8). */ if( !rim_mode ) { /* Put out initial origin if not in rim mode. */ punchOrigin( clc ); } while( TRUE ) { readLine(); nextLexeme(); scanning_line = TRUE; while( scanning_line ) { if( isend( line[lexstart] )) { scanning_line = FALSE; } else { switch( line[lexstart] ) { case '/': scanning_line = FALSE; break; case ';': nextLexeme(); break; case '$': endOfBinary(); return; case '*': nextLexeme(); /* Skip '*', (set origin symbol) */ newclc = ((getExpr())->val & 07777 ) | field; /* Do not change Current Location Counter if an error occurred. */ if( !error_in_line ) { if(( newclc & 07600 ) != ( clc & 07600 ) ) { /* Current page has changed. */ punchLiteralPool( cp, 0 ); } clc = newclc - reloc; fieldlc = clc & 07777; if( !rim_mode ) { /* Not rim mode, put out origin. */ punchOrigin( clc ); } printLine( line, 0, fieldlc, LINE_VAL ); } break; default: switch( line[lexterm] ) { case ',': if( isalpha( line[lexstart] )) { /* Use lookup so symbol will not be counted as reference. */ sym = lookup( lexemeToName( name, lexstart, lexterm )); if( M_DEFINED( sym->type )) { if( (sym->val & 07777) != ( ( clc+reloc ) & 07777) && pass == 2 ) { errorSymbol( &duplicate_label, sym->name, lexstart ); } sym->type = sym->type | DUPLICATE; } /* Must call define on pass 2 to generate concordance. */ defineLexeme( lexstart, lexterm, ( clc + reloc ), LABEL ); } else { errorLexeme( &label_syntax, lexstart ); } nextLexeme(); /* skip label */ nextLexeme(); /* skip comma */ break; case '=': if( isalpha( line[lexstart] )) { start = lexstart; term = lexterm; delimiter = line[lexterm]; nextLexBlank(); /* skip symbol */ nextLexeme(); /* skip trailing =, allow blank */ delimiter = line[lexterm]; val = getExprs(); defineLexeme( start, term, val, DEFINED ); printLine( line, 0, val, LINE_VAL ); } else { errorLexeme( &symbol_syntax, lexstartprev ); nextLexeme(); /* skip symbol */ nextLexeme(); /* skip trailing = */ getExprs(); /* skip expression */ } break; default: if( isalpha( line[lexstart] )) { sym = evalSymbol(); val = sym->val; if( M_PSEUDO( sym->type )) { nextLexeme(); /* Skip symbol */ scanning_line = pseudoOperators( (PSEUDO_T)val & 07777 ); } else { /* Identifier is not a pseudo-op, interpret as load value */ punchOutObject( clc, getExprs() & 07777 ); incrementClc(); } } else { /* Identifier is a value, interpret as load value */ punchOutObject( clc, getExprs() & 07777 ); incrementClc(); } break; } /* end switch */ break; } /* end switch */ } /* end if */ } /* end while( scanning_line ) */ } /* end while( TRUE ) */ } /* onePass() */ /******************************************************************************/ /* */ /* Function: fixMRIInstruction */ /* */ /* Synopsis: Now that we have the final value figure out if page 0, current */ /* page, or indirect needed and max final instruction */ /* */ /******************************************************************************/ WORD16 fixMRIInstruction(WORD16 instruction, WORD16 value) { /* Now have the address part of the MRI instruction. */ if( value < 00200 ) { instruction |= value; /* Page zero MRI. */ } else if( (( fieldlc + reloc ) & 07600 ) <= value && value <= ((( fieldlc + reloc ) & 07600) | 0177 )) { instruction |= ( PAGE_BIT | (value & ADDRESS_FIELD )); /* Current page MRI */ } else { if(( instruction & INDIRECT_BIT ) == INDIRECT_BIT ) { /* Already indirect, can't generate */ errorSymbol( &illegal_indirect, NULL, lexstartprev ); } else { if( literals_ok ) { /* Now fix off page reference. */ /* Search current page literal pool for needed instruction. */ /* Set Indirect Current Page */ instruction |= ( 00600 | insertLiteral( cp, value, GET_PAGE_INDEX(clc) )); indirect_generated = TRUE; if (pass == 2) { links++; } } else { errorSymbol( &illegal_reference, NULL, lexstartprev ); instruction |= ( value & 0177 ); } } } return instruction; } /******************************************************************************/ /* */ /* Function: getExprs */ /* */ /* Synopsis: Or together a list of blank separated expressions, from the */ /* current lexeme onward. Leave the current lexeme as */ /* the last one in the list. */ /* */ /******************************************************************************/ WORD16 getExprs() { SYM_T *symv; SYM_T *symt; WORD16 temp; SYMTYP temp_type; WORD16 value; SYMTYP value_type; BOOL MRI_held = FALSE; WORD16 held_value = 0; symv = getExpr(); value = symv->val; value_type = symv->type; while( TRUE ) { if( isdone( line[lexstart] )) { if (MRI_held) { value = fixMRIInstruction(value, held_value); } return( value ); } switch( line[lexstart] ) { case ')': case ']': case '<': if (MRI_held) { value = fixMRIInstruction(value, held_value); } return( value ); default: break; } /* Interpret space as logical or */ symt = getExpr(); temp = symt->val & 07777; temp_type = symt->type; switch( value_type & (MRI | MRIFIX)) { case MRI: case MRIFIX: /* Previous symbol was a Memory Reference Instruction. */ switch( temp_type & (MRI | MRIFIX) ) { case MRI: case MRIFIX: /* If we have held value don't or in more MRI's to instuction, they */ /* are now instuction value */ if (MRI_held) { held_value |= temp; } else { /* Current symbol is also a Memory Reference Instruction. */ value |= temp; /* Just OR the MRI instructions. */ } break; default: held_value |= temp; MRI_held = TRUE; break; } break; default: if (value_type == UNDEFINED || temp_type == UNDEFINED) { value = 0; } else { value |= temp; /* Normal 12 bit value. */ } break; } } /* end while */ } /* getExprs() */ /******************************************************************************/ /* */ /* Function: getExpr */ /* */ /* Synopsis: Get an expression, from the current lexeme onward, leave the */ /* current lexeme as the one after the expression. Expressions */ /* contain terminal symbols (identifiers) separated by operators. */ /* */ /******************************************************************************/ SYM_T *getExpr() { SYM_T *sym; delimiter = line[lexterm]; if( line[lexstart] == '-' ) { nextLexBlank(); sym_getexpr = *(eval()); sym_getexpr.val = ( - sym_getexpr.val ) & 07777; } else { if( line[lexstart] == '+' ) { nextLexBlank(); } sym_getexpr = *(eval()); sym_getexpr.val = sym_getexpr.val & 07777; } if( is_blank( delimiter )) { return( &sym_getexpr ); } /* Here we assume the current lexeme is the operator separating the */ /* previous operator from the next, if any. */ while( TRUE ) { /* assert line[lexstart] == delimiter */ if( is_blank( delimiter )) { return( &sym_getexpr ); } switch( line[lexstart] ) { case '+': /* add */ nextLexBlank(); /* skip over the operator */ sym = eval(); sym_getexpr.val += sym->val; if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) { sym_getexpr.val = 0; sym_getexpr.type = UNDEFINED; } break; case '-': /* subtract */ nextLexBlank(); /* skip over the operator */ sym = eval(); sym_getexpr.val -= sym->val; if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) { sym_getexpr.val = 0; sym_getexpr.type = UNDEFINED; } break; case '^': /* multiply */ nextLexBlank(); /* skip over the operator */ sym = eval(); sym_getexpr.val *= sym->val; if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) { sym_getexpr.val = 0; sym_getexpr.type = UNDEFINED; } break; case '%': /* divide */ nextLexBlank(); /* skip over the operator */ sym = eval(); sym_getexpr.val /= sym->val; if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) { sym_getexpr.val = 0; sym_getexpr.type = UNDEFINED; } break; case '&': /* and */ nextLexBlank(); /* skip over the operator */ sym = eval(); sym_getexpr.val &= sym->val; if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) { sym_getexpr.val = 0; sym_getexpr.type = UNDEFINED; } break; case '!': /* or */ nextLexBlank(); /* skip over the operator */ sym = eval(); sym_getexpr.val |= sym->val; if (sym_getexpr.type == UNDEFINED || sym->type == UNDEFINED) { sym_getexpr.val = 0; sym_getexpr.type = UNDEFINED; } break; default: if( isend( line[lexstart] )) { return( &sym_getexpr ); } switch( line[lexstart] ) { case '/': case ';': case ')': case ']': case '<': break; case '=': errorMessage( &illegal_equals, lexstart ); moveToEndOfLine(); sym_getexpr.val = 0; break; default: errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); sym_getexpr.val = 0; break; } return( &sym_getexpr ); } } /* end while */ } /* getExpr() */ /******************************************************************************/ /* */ /* Function: eval */ /* */ /* Synopsis: Get the value of the current lexeme, set delimiter and advance.*/ /* */ /******************************************************************************/ SYM_T *eval() { WORD16 digit; int from; WORD16 loc; SYM_T *sym; WORD16 val; val = 0; delimiter = line[lexterm]; if( isalpha( line[lexstart] )) { sym = evalSymbol(); if( M_UNDEFINED( sym->type ) && pass == 2 ) { errorSymbol( &undefined_symbol, sym->name, lexstart ); nextLexeme(); return( sym ); } else { nextLexeme(); return( sym ); } } else if( isdigit( line[lexstart] )) { from = lexstart; val = 0; while( from < lexterm ) { if( isdigit( line[from] )) { digit = (WORD16) line[from++] - (WORD16) '0'; if( digit < radix ) { val = val * radix + digit; } else { errorLexeme( &number_not_radix, from - 1 ); val = 0; from = lexterm; } } else { errorLexeme( ¬_a_number, lexstart ); val = 0; from = lexterm; } } nextLexeme(); sym_eval.val = val; return( &sym_eval ); } else { switch( line[lexstart] ) { case '"': /* Character literal */ if( cc + 1 <= maxcc ) { val = line[lexstart + 1] | 0200; delimiter = line[lexstart + 2]; cc = lexstart + 2; } else { errorMessage( &no_literal_value, lexstart ); } nextLexeme(); break; case '.': /* Value of Current Location Counter */ val = (clc & 07777) + reloc; nextLexeme(); break; case '[': /* Generate literal on page zero. */ if( !literals_ok && LITERAL_ERROR) { errorMessage( &literal_gen_off, lexstart ); } nextLexBlank(); /* Skip bracket */ val = getExprs() & 07777; if( line[lexstart] == ']' ) { nextLexBlank(); /* Skip end bracket */ } sym_eval.val = (literals_ok || !LITERAL_ERROR) ? insertLiteral( cp , val, GET_PAGE_INDEX(field)) : 0; return( &sym_eval ); case '(': /* Generate literal on current page. */ if( !literals_ok && LITERAL_ERROR) { errorMessage( &literal_gen_off, lexstart ); } nextLexBlank(); /* Skip paren */ val = getExprs() & 07777; if( line[lexstart] == ')' ) { nextLexBlank(); /* Skip end paren */ } loc = (literals_ok || !LITERAL_ERROR) ? insertLiteral( cp, val, GET_PAGE_INDEX(clc) ) : 0; sym_eval.val = loc + (( clc + reloc ) & 077600 ); return( &sym_eval ); default: switch( line[lexstart] ) { case '=': errorMessage( &illegal_equals, lexstart ); moveToEndOfLine(); break; default: errorMessage( &illegal_character, lexstart ); break; } val = 0; /* On error, set value to zero. */ nextLexBlank(); /* Go past illegal character. */ } } sym_eval.val = val; return( &sym_eval ); } /* eval() */ /******************************************************************************/ /* */ /* Function: inputDubl */ /* */ /* Synopsis: Get the value of the current lexeme as a double word. */ /* */ /******************************************************************************/ void inputDubl() { WORD32 dublvalue; BOOL scanning_line; scanning_line = TRUE; do { while( scanning_line ) { if( isend( line[lexstart] )) { scanning_line = FALSE; } else { switch( line[lexstart] ) { case '/': scanning_line = FALSE; break; case ';': nextLexeme(); break; case '+': delimiter = line[lexterm]; nextLexBlank(); case '-': default: if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) { dublvalue = getDublExprs(); punchOutObject( clc, (WORD16)(( dublvalue >> 12 ) & 07777 )); incrementClc(); punchOutObject( clc, (WORD16)( dublvalue & 07777 )); incrementClc(); } else { return; /* Non-numeric input, back to assembly. */ } break; } /* end switch */ } /* end if */ if( error_in_line ) { return; /* Error occurred, exit DUBL input mode. */ } } /* end while( scanning_line ) */ readLine(); nextLexeme(); scanning_line = TRUE; } while( TRUE ); } /* inputDubl() */ /******************************************************************************/ /* */ /* Function: getDublExprs */ /* */ /* Synopsis: Get a DUBL expression. */ /* */ /******************************************************************************/ WORD32 getDublExprs() { WORD32 dublvalue; dublvalue = getDublExpr(); while( TRUE ) { if( isdone( line[lexstart] )) { return( dublvalue ); } else { errorMessage( &illegal_expression, lexstart - 1 ); return( 0 ); } } /* end while */ } /* getDublExprs() */ /******************************************************************************/ /* */ /* Function: getDublExpr */ /* */ /* Synopsis: Get the value of the current lexeme as a double word. The */ /* number is always considered to have a decimal radix. */ /* */ /******************************************************************************/ WORD32 getDublExpr() { WORD32 dublvalue; delimiter = line[lexterm]; if( line[lexstart] == '-' ) { nextLexBlank(); dublvalue = evalDubl( 0 ); nextLexeme(); /* Test for any value greater than 23 bits in length. */ if( (unsigned long int)dublvalue > 040000000L ) { errorMessage( &dubl_overflow, lexstart ); dublvalue = 0; } dublvalue = -dublvalue; } else { dublvalue = evalDubl( 0 ); nextLexeme(); /* Test for any value greater than 23 bits in length. */ if( (unsigned long int)dublvalue > 037777777L ) { errorMessage( &dubl_overflow, lexstart ); dublvalue = 0; } } if( is_blank( delimiter )) { return( dublvalue ); } /* Here we assume the current lexeme is the operator separating the */ /* previous operator from the next, if any. */ while( TRUE ) { /* assert line[lexstart] == delimiter */ if( is_blank( delimiter )) { errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); dublvalue = 0; return( dublvalue ); } switch( line[lexstart] ) { case '+': /* add */ case '-': /* subtract */ case '^': /* multiply */ case '%': /* divide */ case '&': /* and */ case '!': /* or */ errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); dublvalue = 0; break; default: if( isend( line[lexstart] )) { return( dublvalue ); } switch( line[lexstart] ) { case '/': case ';': break; default: errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); dublvalue = 0; break; } return( dublvalue ); } } /* end while */ } /* getDublExpr() */ /******************************************************************************/ /* */ /* Function: evalDubl */ /* */ /* Synopsis: Get the value of the current lexeme as a double word. The */ /* number is always considered to have a decimal radix. */ /* */ /******************************************************************************/ WORD32 evalDubl( WORD32 initial_value ) { WORD32 digit; int from; WORD32 dublvalue; WORD32 olddublvalue; overflow = FALSE; delimiter = line[lexterm]; from = lexstart; dublvalue = initial_value; while( from < lexterm ) { if( isdigit( line[from] )) { olddublvalue = dublvalue; digit = (WORD32)( line[from++] - '0' ); dublvalue = dublvalue * 10 + digit; if( dublvalue < olddublvalue ) { overflow = TRUE; } } else { errorLexeme( ¬_a_number, from ); dublvalue = 0; from = lexterm; } } return( dublvalue ); } /* evalDubl() */ /******************************************************************************/ /* */ /* Function: inputFltg */ /* */ /* Synopsis: Get the value of the current lexeme as a Floating Point const. */ /* */ /******************************************************************************/ void inputFltg() { FLTG_T *fltg; BOOL scanning_line; fltg_input = TRUE; /* Set lexeme scanner for floating point. */ scanning_line = TRUE; while( TRUE ) { while( scanning_line ) { if( isend( line[lexstart] )) { scanning_line = FALSE; } else { switch( line[lexstart] ) { case '/': scanning_line = FALSE; break; case ';': nextLexeme(); break; case '+': delimiter = line[lexterm]; nextLexBlank(); case '-': default: if( isdigit( line[lexstart] ) || line[lexstart] == '-' ) { fltg = getFltgExprs(); punchOutObject( clc, ( fltg->exponent & 07777 )); incrementClc(); punchOutObject( clc, (WORD16)(( fltg->mantissa >> 12 ) & 07777 )); incrementClc(); punchOutObject( clc, (WORD16)( fltg->mantissa & 07777 )); incrementClc(); } else { fltg_input = FALSE; /* Reset lexeme scanner. */ return; /* Non-numeric input, back to assembly. */ } break; } /* end switch */ } /* end if */ if( error_in_line ) { fltg_input = FALSE; /* Reset lexeme scanner. */ return; /* Error occurred, exit FLTG input mode. */ } } /* end while( scanning_line ) */ readLine(); nextLexeme(); scanning_line = TRUE; } } /* inputFltg() */ /******************************************************************************/ /* */ /* Function: getFltgExprs */ /* */ /* Synopsis: Get a FLTG expression. */ /* */ /******************************************************************************/ FLTG_T *getFltgExprs() { FLTG_T *fltg; fltg = getFltgExpr(); while( TRUE ) { if( isdone( line[lexstart] )) { return( fltg ); } else { errorMessage( &illegal_expression, lexstart - 1 ); return( 0 ); } } /* end while */ } /* getFltgExprs() */ /******************************************************************************/ /* */ /* Function: getFltgExpr */ /* */ /* Synopsis: Get the value of the current lexeme as a double word. The */ /* number is always considered to have a decimal radix. */ /* */ /******************************************************************************/ FLTG_T *getFltgExpr() { FLTG_T *fltg; delimiter = line[lexterm]; fltg = evalFltg(); /* Test for any value greater than 23 bits in length. */ if( (unsigned long int)fltg->mantissa> 077777777L ) { errorMessage( &fltg_overflow, lexstart ); } if( is_blank( delimiter )) { return( fltg ); } /* Here we assume the current lexeme is the operator separating the */ /* previous operator from the next, if any. */ while( TRUE ) { /* assert line[lexstart] == delimiter */ if( is_blank( delimiter )) { errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); fltg = 0; return( fltg ); } switch( line[lexstart] ) { case '+': /* add */ case '-': /* subtract */ case '^': /* multiply */ case '%': /* divide */ case '&': /* and */ case '!': /* or */ errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); fltg = NULL; break; default: if( isend( line[lexstart] )) { return( fltg ); } switch( line[lexstart] ) { case '/': case ';': break; default: errorMessage( &illegal_expression, lexstart ); moveToEndOfLine(); fltg = NULL; break; } return( fltg ); } } /* end while */ } /* getFltgExpr() */ /******************************************************************************/ /* */ /* Function: evalFltg */ /* */ /* Synopsis: Get the value of the current lexeme as a floating point value. */ /* Floating point input is alwasy considered decimal. */ /* The general format of a floating point number is: */ /* +-ddd.dddE+-dd where each d is a decimal digit. */ /* */ /******************************************************************************/ FLTG_T *evalFltg() { int current_state; int current_col; WORD16 exponent; FLTG_T *fltg; WORD32 input_value; BOOL negate; BOOL negate_exponent; int next_state; int right_digits; /* This uses a lexical analyzer to parse the floating point format. */ static BYTE state_table[][10] = { /* 0 1 2 3 4 5 6 Oolumn index */ /* + - d . E sp p State Comment */ { 2, 1, 3, 4, 10, 10, 10 }, /* 0 Initial state. */ { 11, 11, 3, 4, 11, 11, 11 }, /* 1 - */ { 11, 11, 3, 4, 11, 11, 11 }, /* 2 + */ { 10, 10, 10, 4, 6, 10, 10 }, /* 3 # (+-ddd) */ { 11, 11, 5, 11, 11, 10, 10 }, /* 4 . (+-ddd.) */ { 11, 11, 11, 11, 6, 10, 11 }, /* 5 # (+-ddd.ddd) */ { 8, 7, 9, 11, 11, 11, 11 }, /* 6 E (+-ddd.dddE) */ { 11, 11, 9, 11, 11, 11, 11 }, /* 7 - (+-ddd.dddE- */ { 11, 11, 9, 11, 11, 11, 11 }, /* 8 + (+-ddd.dddE+ */ { 11, 11, 11, 11, 11, 10, 11 } /* 9 # (+-ddd.dddE+-dd */ /* 10 Completion state */ /* 11 Error state. */ }; delimiter = line[lexterm]; fltg = &fltg_ac; fltg->exponent = 0; fltg->mantissa = 0; input_value = 0; negate = FALSE; negate_exponent = FALSE; next_state = 0; exponent = 0; right_digits = 0; current_state = 0; while( TRUE ) { /* Classify character. This is the column index. */ switch( line[lexstart] ) { case '+': current_col = 0; break; case '-': current_col = 1; break; case '.': current_col = 3; break; case 'E': case 'e': current_col = 4; break; default: if( isdigit( line[lexstart] )) { current_col = 2; } else if( isdone( line[lexstart] )) { current_col = 5; } else { current_col = 6; } break; } next_state = state_table[current_state][current_col]; switch( next_state ) { case 1: /* - */ negate = TRUE; case 2: /* + */ delimiter = line[lexterm]; /* Move past the + or - character. */ nextLexBlank(); break; case 3: /* Number (+-ddd) */ input_value = evalDubl( 0 ); /* Integer part of the number. */ nextLexeme(); /* Move past previous lexeme. */ break; case 4: delimiter = line[lexterm]; nextLexBlank(); /* Move past the . character. */ break; case 5: /* . (+-ddd.ddd) */ /* Fractional part of the number. */ input_value = evalDubl( input_value ); right_digits = lexterm - lexstart;/* Digit count to right of decimal. */ nextLexeme(); /* Move past previous lexeme. */ break; case 6: /* E (+-ddd.dddE) */ delimiter = line[lexterm]; /* Move past the E. */ nextLexBlank(); break; case 7: /* - (+-ddd.dddE-) */ negate_exponent = TRUE; case 8: /* + (+-ddd.dddE+) */ delimiter = line[lexterm]; /* Move past the + or - character. */ nextLexBlank(); break; case 9: /* # (+-ddd.dddE+-dd) */ exponent = (int)evalDubl( 0 ); /* Exponent of floating point number. */ if( negate_exponent ) { exponent = - exponent; } nextLexeme(); /* Move past previous lexeme. */ break; case 10: /* Floating number parsed, convert */ /* the number. */ /* Get the exponent for the number as input. */ exponent -= right_digits; /* Remove trailing zeros and adjust the exponent accordingly. */ while(( input_value % 10 ) == 0 ) { input_value /= 10; exponent++; } /* Convert the number to floating point. The number is calculated with */ /* a 27 bit mantissa to improve precision. The extra 3 bits are */ /* discarded after the result has been calculated. */ fltg->exponent = 26; fltg->mantissa = input_value << 3; normalizeFltg( fltg ); while( exponent != 0 ) { if( exponent < 0 ) { /* Decimal point is to the left. */ fltg->mantissa /= 10; normalizeFltg( fltg ); exponent++; } else if( exponent > 0 ) { /* Decimal point is to the right. */ fltg->mantissa *= 10; normalizeFltg( fltg ); exponent--; } } /* Discard the extra precsion used for calculating the number. */ fltg->mantissa >>= 3; fltg->exponent -= 3; if( negate ) { fltg->mantissa = (- fltg->mantissa ) & 077777777L; } return( fltg ); case 11: /* Error in format. */ /* Not a properly constructued floating point number. */ return( fltg ); default: break; } /* Set state for next pass through the loop. */ current_state = next_state; } } /* evalFltg() */ /******************************************************************************/ /* */ /* Function: normalizeFltg */ /* */ /* Synopsis: Normalize a PDP-8 double precision floating point number. */ /* */ /******************************************************************************/ void normalizeFltg( FLTG_T *fltg ) { /* Normalize the floating point number. */ if( fltg->mantissa != 0 ) { if(( fltg->mantissa & ~0x3FFFFFFL ) == 0 ) { while(( fltg->mantissa & ~0x1FFFFFFL ) == 0 ) { fltg->mantissa <<= 1; fltg->exponent--; } } else { while(( fltg->mantissa & ~0x3FFFFFFL ) != 0 ) { fltg->mantissa >>= 1; fltg->exponent++; } } } else { fltg->exponent = 0; } return; } /******************************************************************************/ /* */ /* Function: incrementClc */ /* */ /* Synopsis: Set the next assembly location. Test for collision with */ /* the literal tables. */ /* */ /******************************************************************************/ WORD16 incrementClc() { testForLiteralCollision( clc ); /* Incrementing the location counter is not to change field setting. */ clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 ); fieldlc = clc & 07777; return( clc ); } /* incrementClc() */ /******************************************************************************/ /* */ /* Function: testForLiteralCollision */ /* */ /* Synopsis: Test the given location for collision with the literal tables. */ /* */ /******************************************************************************/ BOOL testForLiteralCollision( WORD16 loc ) { WORD16 pagelc; BOOL result = FALSE; WORD16 tmppage; int tmpfield; tmpfield = GET_PAGE_INDEX(loc); tmppage = loc & 07600; pagelc = loc & 00177; if ( pagelc > max_page_used[tmpfield] ) { max_page_used[tmpfield] = pagelc; } if ( pagelc >= cp[tmpfield].loc ) { if ( tmppage == 0 ) { errorMessage( &pz_literal_overflow, -1 ); } else { errorMessage( &literal_overflow, -1 ); } result = TRUE; } return( result ); } /* testForLiteralCollision() */ /******************************************************************************/ /* */ /* Function: readLine */ /* */ /* Synopsis: Get next line of input. Print previous line if needed. */ /* */ /******************************************************************************/ void readLine() { WORD16 ix; WORD16 iy; char inpline[LINELEN]; listLine(); /* List previous line if needed. */ lineno++; /* Count lines read. */ indirect_generated = FALSE; /* Mark no indirect address generated. */ listed = FALSE; /* Mark as not listed. */ cc = 0; /* Initialize column counter. */ lexstartprev = 0; error_in_line = FALSE; if(( fgets( inpline, LINELEN - 1, infile )) == NULL ) { inpline[0] = '$'; inpline[1] = '\n'; inpline[2] = '\0'; if (!dollar_not_required) { error_in_line = TRUE; } } /* Remove any tabs from the input line by inserting the required number */ /* of spaces to simulate N character tab stops, where N defaults to 8 and */ /* is set by the command line option -t. (RK 20071029) */ /* Ignore \r if there is one. (DPI 20150501) */ for( ix = 0, iy = 0; inpline[ix] != '\0' && iy < (LINELEN - 2); ix++ ) { switch( inpline[ix] ) { case '\t': do { line[iy] = ' '; iy++; } while(( iy % tabstops ) != 0 && iy < (LINELEN - 2)); break; case '\r': /* dont copy the carriage return */ break; default: line[iy] = inpline[ix]; iy++; break; } } if (iy >= (LINELEN - 2)) { line [iy] = '\n'; iy++; } line[iy] = '\0'; maxcc = iy; /* Save the current line length. */ /* Save the first line for possible use as the listing title. */ if( lineno == 1 ) { strcpy( list_title, line ); } } /* readLine() */ /******************************************************************************/ /* */ /* Function: listLine */ /* */ /* Synopsis: Output a line to the listing file. */ /* */ /******************************************************************************/ void listLine() /* generate a line of listing if not already done! */ { if( listfile != NULL && listed == FALSE ) { printLine( line, 0, 0, LINE ); } } /* listLine() */ /******************************************************************************/ /* */ /* Function: printPageBreak */ /* */ /* Synopsis: Output a Top of Form and listing header if new page necessary. */ /* */ /******************************************************************************/ void printPageBreak() { if( page_lineno >= LIST_LINES_PER_PAGE ) /* ( list_lineno % LIST_LINES_PER_PAGE ) == 0 ) */ { if( !list_title_set ) { /* strcpy( list_title, line ); */ if( list_title[strlen(list_title) - 1] == '\n' ) { list_title[strlen(list_title) - 1] = '\0'; } if( strlen( list_title ) > TITLELEN ) { list_title[TITLELEN] = '\0'; } list_title_set = TRUE; } topOfForm( list_title, NULL ); } } /* printPageBreak() */ /******************************************************************************/ /* */ /* Function: printLine */ /* */ /* Synopsis: Output a line to the listing file with new page if necessary. */ /* */ /******************************************************************************/ void printLine( char *line, WORD16 loc, WORD16 val, LINESTYLE_T linestyle ) { char rlc; if( listfile == NULL ) { save_error_count = 0; return; } printPageBreak(); list_lineno++; page_lineno++; if (reloc == 0) { rlc = ' '; } else { rlc = '*'; } switch( linestyle ) { default: case LINE: fprintf(listfile, "%5d ", lineno ); fputs( line, listfile ); listed = TRUE; break; case LINE_VAL: fprintf(listfile, "%5d %4.4o ", lineno, val ); fputs( line, listfile ); listed = TRUE; break; case LINE_LOC_VAL: if( !listed ) { if( indirect_generated ) { fprintf( listfile, "%5d %5.5o%c %4.4o@ ", lineno, loc, rlc, val ); } else { fprintf( listfile, "%5d %5.5o%c %4.4o ", lineno, loc, rlc, val ); } fputs( line, listfile ); listed = TRUE; } else { fprintf( listfile, " %5.5o%c %4.4o\n", loc, rlc, val ); } break; case LOC_VAL: fprintf( listfile, " %5.5o%c %4.4o\n", loc, rlc, val ); break; } printErrorMessages(); } /* printLine() */ /******************************************************************************/ /* */ /* Function: printErrorMessages */ /* */ /* Synopsis: Output any error messages from the current list of errors. */ /* */ /******************************************************************************/ void printErrorMessages() { WORD16 ix; WORD16 iy; if( listfile != NULL ) { /* If any errors, display them now. */ for( iy = 0; iy < save_error_count; iy++ ) { printPageBreak(); fprintf( listfile, "%-18.18s", error_list[iy].mesg ); if( error_list[iy].col >= 0 ) { for( ix = 0; ix < error_list[iy].col; ix++ ) { if( line[ix] == '\t' ) { putc( '\t', listfile ); } else { putc( ' ', listfile ); } } fputs( "^", listfile ); list_lineno++; page_lineno++; } fputs( "\n", listfile ); } } save_error_count = 0; } /* printErrorMessages() */ /******************************************************************************/ /* */ /* Function: endOfBinary */ /* */ /* Synopsis: Outputs both literal tables at the end of a binary segment. */ /* */ /******************************************************************************/ void endOfBinary() { /* Punch page 0 also. */ punchLiteralPool( cp, 1 ); if( error_in_line ) { listed = TRUE; clc = ( clc & 070000 ) + (( clc - 1 ) & 07777 ); errorMessage( &end_of_file, -1 ); clc = ( clc & 070000 ) + (( clc + 1 ) & 07777 ); } else { listLine(); /* List line if not done yet. */ } return; } /* endOfBinary() */ /******************************************************************************/ /* */ /* Function: punchChecksum */ /* */ /* Synopsis: Output a checksum if the current mode requires it and an */ /* object file exists. */ /* */ /******************************************************************************/ void punchChecksum() { /* If the assembler has output any BIN data output the checksum. */ if( binary_data_output && !rim_mode ) { punchLocObject( 0, checksum ); } binary_data_output = FALSE; checksum = 0; } /* punchChecksum() */ /******************************************************************************/ /* */ /* Function: punchLeader */ /* */ /* Synopsis: Generate 2 feet of leader on object file, as per DEC */ /* documentation. Paper tape has 10 punches per inch. */ /* */ /******************************************************************************/ void punchLeader( int count ) { int ix; /* If value is zero, set to the default of 2 feet of leader. */ count = ( count == 0 ) ? 240 : count; if( objectfile != NULL ) { for( ix = 0; ix < count; ix++ ) { fputc( 0200, objectfile ); } } } /* punchLeader() */ /******************************************************************************/ /* */ /* Function: punchOrigin */ /* */ /* Synopsis: Output an origin to the object file. */ /* */ /******************************************************************************/ void punchOrigin( WORD16 loc ) { punchObject((( loc >> 6 ) & 0077 ) | 0100 ); punchObject( loc & 0077 ); } /* punchOrigin() */ /******************************************************************************/ /* */ /* Function: punchObject */ /* */ /* Synopsis: Put one character to object file and include it in checksum. */ /* */ /******************************************************************************/ void punchObject( WORD16 val ) { val &= 0377; if( objectfile != NULL ) { fputc( val, objectfile ); checksum += val; } binary_data_output = TRUE; } /* punchObject() */ /******************************************************************************/ /* */ /* Function: punchOutObject */ /* */ /* Synopsis: Output the current line and then then punch value to the */ /* object file. */ /* */ /******************************************************************************/ void punchOutObject( WORD16 loc, WORD16 val ) { /* Adding reloc makes printout agree with PAL8 where is prints the */ /* relocated address, not the address in the BIN file */ printLine( line,( ( field | loc ) + reloc ), val, LINE_LOC_VAL ); punchLocObject( loc, val ); } /* punchOutObject() */ /******************************************************************************/ /* */ /* Function: punchLocObject */ /* */ /* Synopsis: Output the word (with origin if rim format) to the object file.*/ /* */ /******************************************************************************/ void punchLocObject( WORD16 loc, WORD16 val ) { if( rim_mode ) { punchOrigin( loc ); } punchObject(( val >> 6 ) & 0077 ); punchObject( val & 0077 ); } /* punchLocObject() */ /******************************************************************************/ /* */ /* Function: punchLiteralPool */ /* */ /* Synopsis: Output the current page data. */ /* */ /******************************************************************************/ void punchLiteralPool( LPOOL_T *p, BOOL punch_page0 ) { WORD16 loc; WORD16 tmplc; int lpool_page = 0; /* Silence false uninitialized error from GCC */ int i; for (i = MAX_PAGES-1; i >= 0; i--) { lpool_page = (i << 7) & 07600; if ( p[i].loc != p[i].last_punched && (punch_page0 || lpool_page != 0) ) { if( !rim_mode ) { /* Put out origin if not in rim mode. */ punchOrigin( p[i].loc | lpool_page ); } /* Put the literals in the object file. */ for( loc = p[i].loc; loc < p[i].last_punched; loc++ ) { tmplc = loc + lpool_page; printLine( line, (field | tmplc), p[i].pool[loc], LOC_VAL ); punchLocObject( tmplc, p[i].pool[loc] ); } p[i].last_punched = p[i].loc; } } } /* punchLiteralPool() */ /******************************************************************************/ /* */ /* Function: insertLiteral */ /* */ /* Synopsis: Add a value to the given literal pool if not already in pool. */ /* Return the location of the value in the pool. */ /* */ /******************************************************************************/ WORD16 insertLiteral( LPOOL_T *pool, WORD16 value, int fieldpage_index ) { WORD16 ix; LPOOL_T *p; p = &pool[fieldpage_index]; /* Search the literal pool for any occurence of the needed value. */ ix = PAGE_SIZE - 1; while( ix >= p->loc && p->pool[ix] != value ) { ix--; } /* Check if value found in literal pool. If not, then insert value. */ if( ix < p->loc ) { (p->loc)--; p->pool[p->loc] = value; ix = p->loc; if( max_page_used[fieldpage_index] >= p->loc ) { if ( (fieldpage_index & 017) == 0 ) { errorMessage( &pz_literal_overflow, -1 ); } else { errorMessage( &literal_overflow, -1 ); } } } return( ix ); } /* insertLiteral() */ /******************************************************************************/ /* */ /* Function: printSymbolTable */ /* */ /* Synopsis: Output the symbol table. */ /* */ /******************************************************************************/ void printSymbolTable() { int col; int cx; char *fmt; int ix; char mark; int page; int row; int symbol_base; int symbol_lines; symbol_base = number_of_fixed_symbols; for( page=0, list_lineno=0, col=0, ix=symbol_base; ix < symbol_top; page++ ) { topOfForm( list_title, s_symtable ); symbol_lines = LIST_LINES_PER_PAGE - page_lineno; for( row = 0; page_lineno < LIST_LINES_PER_PAGE && ix < symbol_top; row++) { list_lineno++; page_lineno++; fprintf( listfile, "%5d", list_lineno ); for( col = 0; col < SYMBOL_COLUMNS && ix < symbol_top; col++ ) { /* Get index of symbol for the current line and column */ cx = symbol_lines * ( SYMBOL_COLUMNS * page + col ) + row; cx += symbol_base; /* Make sure that there is a symbol to be printed. */ if( number_of_fixed_symbols <= cx && cx < symbol_top ) { switch( symtab[cx].type & LABEL ) { case LABEL: fmt = " %c%-6.6s %5.5o "; break; default: fmt = " %c%-6.6s %4.4o "; break; } switch( symtab[cx].type & ( DEFINED | REDEFINED )) { case UNDEFINED: mark = '?'; break; case REDEFINED: mark = '#'; break; default: mark = ' '; break; } fprintf( listfile, fmt, mark, symtab[cx].name, symtab[cx].val ); ix++; } } fprintf( listfile, "\n" ); } } } /* printSymbolTable() */ /******************************************************************************/ /* */ /* Function: printPermanentSymbolTable */ /* */ /* Synopsis: Output the permanent symbol table to a file suitable for */ /* being input after the EXPUNGE pseudo-op. */ /* */ /******************************************************************************/ void printPermanentSymbolTable() { int ix; FILE *permfile; char *s_type; if(( permfile = fopen( permpathname, "w" )) == NULL ) { exit( 2 ); } fprintf( permfile, "/ PERMANENT SYMBOL TABLE\n/\n" ); fprintf( permfile, " EXPUNGE\n/\n" ); /* Print the memory reference instructions first. */ s_type = "FIXMRI"; for( ix = 0; ix < symbol_top; ix++ ) { if( M_MRI( symtab[ix].type )) { fprintf( permfile, "%-7s %s=%4.4o\n", s_type, symtab[ix].name, symtab[ix].val ); } } s_type = " "; for( ix = 0; ix < symbol_top; ix++ ) { if( M_FIXED( symtab[ix].type )) { if( !M_MRI( symtab[ix].type ) && !M_PSEUDO( symtab[ix].type )) { fprintf( permfile, "%-7s %s=%4.4o\n", s_type, symtab[ix].name, symtab[ix].val ); } } } fprintf( permfile, "/\n FIXTAB\n" ); fclose( permfile ); } /* printPermanentSymbolTable() */ /******************************************************************************/ /* */ /* Function: printCrossReference */ /* */ /* Synopsis: Output a cross reference (concordance) for the file being */ /* assembled. */ /* */ /******************************************************************************/ void printCrossReference() { int ix; int symbol_base; int xc; int xc_index; int xc_refcount; int xc_cols; /* Force top of form for first page. */ page_lineno = LIST_LINES_PER_PAGE; list_lineno = 0; symbol_base = number_of_fixed_symbols; for( ix = symbol_base; ix < symbol_top; ix++ ) { list_lineno++; page_lineno++; if( page_lineno >= LIST_LINES_PER_PAGE ) { topOfForm( list_title, s_xref ); } fprintf( listfile, "%5d", list_lineno ); /* Get reference count & index into concordance table for this symbol. */ xc_refcount = symtab[ix].xref_count; xc_index = symtab[ix].xref_index; /* Determine how to label symbol on concordance. */ switch( symtab[ix].type & ( DEFINED | REDEFINED )) { case UNDEFINED: fprintf( listfile, " U "); break; case REDEFINED: fprintf( listfile, " M %5d ", xreftab[xc_index] ); break; default: fprintf( listfile, " A %5d ", xreftab[xc_index] ); break; } fprintf( listfile, "%-6.6s ", symtab[ix].name ); /* Output the references, 8 numbers per line after symbol name. */ for( xc_cols = 0, xc = 1; xc < xc_refcount + 1; xc++, xc_cols++ ) { if( xc_cols >= XREF_COLUMNS ) { xc_cols = 0; page_lineno++; if( page_lineno >= LIST_LINES_PER_PAGE ) { topOfForm( list_title, s_xref); } list_lineno++; fprintf( listfile, "\n%5d%-19s", list_lineno, " " ); } fprintf( listfile, " %5d", xreftab[xc_index + xc] ); } fprintf( listfile, "\n" ); } } /* printCrossReference() */ /******************************************************************************/ /* */ /* Function: topOfForm */ /* */ /* Synopsis: Prints title and sub-title on top of next page of listing. */ /* */ /******************************************************************************/ void topOfForm( char *title, char *sub_title ) { char temp[10]; list_pageno++; strcpy( temp, s_page ); sprintf( temp, "%s %d", s_page, list_pageno ); /* Output a top of form if not the first page of the listing. */ if( list_pageno > 1 ) { fprintf( listfile, "\f" ); } fprintf( listfile, "\n\n\n %-63s %10s\n", title, temp ); /* Reset the current page line counter. */ page_lineno = 3; if( sub_title != NULL ) { fprintf( listfile, "%80s\n", sub_title ); page_lineno++; } else { fprintf( listfile, "\n" ); page_lineno++; } fprintf( listfile, "\n" ); page_lineno++; } /* topOfForm() */ /******************************************************************************/ /* */ /* Function: lexemeToName */ /* */ /* Synopsis: Convert the current lexeme into a string. */ /* */ /******************************************************************************/ char *lexemeToName( char *name, int from, int term ) { int to; to = 0; while( from < term && to < ( SYMLEN - 1 )) { name[to++] = toupper( line[from++] ); } while( to < SYMLEN ) { name[to++] = '\0'; } return( name ); } /* lexemeToName() */ /******************************************************************************/ /* */ /* Function: defineLexeme */ /* */ /* Synopsis: Put lexeme into symbol table with a value. */ /* */ /******************************************************************************/ SYM_T *defineLexeme( int start, /* start of lexeme being defined. */ int term, /* end+1 of lexeme being defined. */ WORD16 val, /* value of lexeme being defined. */ SYMTYP type ) /* how symbol is being defined. */ { char name[SYMLEN]; lexemeToName( name, start, term); return( defineSymbol( name, val, type, start )); } /* defineLexeme() */ /******************************************************************************/ /* */ /* Function: defineSymbol */ /* */ /* Synopsis: Define a symbol in the symbol table, enter symbol name if not */ /* not already in table. */ /* */ /******************************************************************************/ SYM_T *defineSymbol( char *name, WORD16 val, SYMTYP type, WORD16 start ) { SYM_T *sym; int xref_count; if( strlen( name ) < 1 ) { return( &sym_undefined ); /* Protect against non-existent names. */ } sym = lookup( name ); /* OS/8 PAL8 seems to allow permanent symbold to be redefined without error */ if( ( M_FIXED( sym->type ) && pass == 1 && perm_redef_error ) || (M_PERM_REDEFINED( sym->type ) && (sym->val != val)) ) { type |= PERM_REDEFINED; } xref_count = 0; /* Set concordance for normal defintion. */ if( M_DEFINED( sym->type )) { if( pass == 2 && ( (sym->val & 07777) != (val & 07777) || M_PERM_REDEFINED(sym->type)) ) { /* Generate diagnostic if redefining a symbol. */ if( M_PERM_REDEFINED( sym->type ) && (M_LABEL(sym->type) || M_LABEL(type)) ) { errorSymbol( &illegal_redefine, sym->name, start ); } else { /* Generate diagnostic if redefining a symbol. */ if( M_REDEFINED( sym->type ) && (M_LABEL(sym->type) || M_LABEL(type)) ) { errorSymbol( &redefined_symbol, sym->name, start ); } } type = type | REDEFINED; sym->xref_count++; /* Referenced suymbol, count it. */ xref_count = sym->xref_count; } } if( pass == 2 && xref ) { /* Put the definition line number in the concordance table. */ /* Defined symbols are not counted as references. */ xreftab[sym->xref_index] = lineno; /* Put the line number in the concordance table. */ xreftab[sym->xref_index + xref_count] = lineno; } /* Now set the value and the type. */ sym->val = ( M_LABEL(type) ) ? val : val & 07777; sym->type = ( pass == 1 ) ? ( type | CONDITION ) : type; return( sym ); } /* defineSymbol() */ /******************************************************************************/ /* */ /* Function: lookup */ /* */ /* Synopsis: Find a symbol in table. If not in table, enter symbol in */ /* table as undefined. Return address of symbol in table. */ /* */ /******************************************************************************/ SYM_T *lookup( char *name ) { int ix; /* Insertion index */ int lx; /* Left index */ int rx; /* Right index */ /* First search the permanent symbols. */ lx = 0; ix = binarySearch( name, lx, number_of_fixed_symbols ); /* If symbol not in permanent symbol table. */ if( ix < 0 ) { /* Now try the user symbol table. */ ix = binarySearch( name, number_of_fixed_symbols, symbol_top ); /* If symbol not in user symbol table. */ if( ix < 0 ) { /* Must put symbol in table if index is negative. */ ix = ~ix; if( symbol_top + 1 >= SYMBOL_TABLE_SIZE ) { errorSymbol( &symbol_table_full, name, lexstart ); exit( 1 ); } for( rx = symbol_top; rx >= ix; rx-- ) { symtab[rx + 1] = symtab[rx]; } symbol_top++; /* Enter the symbol as UNDEFINED with a value of zero. */ strcpy( symtab[ix].name, name ); symtab[ix].type = UNDEFINED; symtab[ix].val = 0; symtab[ix].xref_count = 0; if( xref && pass == 2 ) { xreftab[symtab[ix].xref_index] = 0; } } } return( &symtab[ix] ); /* Return the location of the symbol. */ } /* lookup() */ /******************************************************************************/ /* */ /* Function: binarySearch */ /* */ /* Synopsis: Searches the symbol table within the limits given. If the */ /* symbol is not in the table, it returns the insertion point. */ /* */ /******************************************************************************/ int binarySearch( char *name, int start, int symbol_count ) { int lx; /* Left index */ int mx; /* Middle index */ int rx; /* Right index */ int compare; /* Results of comparison */ lx = start; rx = symbol_count - 1; while( lx <= rx ) { mx = ( lx + rx ) / 2; /* Find center of search area. */ compare = strcmp( name, symtab[mx].name ); if( compare < 0 ) { rx = mx - 1; } else if( compare > 0 ) { lx = mx + 1; } else { return( mx ); /* Found a match in symbol table. */ } } /* end while */ return( ~lx ); /* Return insertion point. */ } /* binarySearch() */ /******************************************************************************/ /* */ /* Function: compareSymbols */ /* */ /* Synopsis: Used to presort the symbol table when starting assembler. */ /* */ /******************************************************************************/ int compareSymbols( const void *a, const void *b ) { return( strcmp( ((SYM_T *) a)->name, ((SYM_T *) b)->name )); } /* compareSymbols() */ /******************************************************************************/ /* */ /* Function: evalSymbol */ /* */ /* Synopsis: Get the pointer for the symbol table entry if exists. */ /* If symbol doesn't exist, return a pointer to the undefined sym */ /* */ /******************************************************************************/ SYM_T *evalSymbol() { char name[SYMLEN]; SYM_T *sym; sym = lookup( lexemeToName( name, lexstart, lexterm )); /* The symbol goes in the concordance iff it is in a different position in */ /* the assembler source file. */ if( lexstart != last_xref_lexstart || lineno != last_xref_lineno ) { sym->xref_count++; /* Count the number of references to symbol. */ last_xref_lexstart = lexstart; last_xref_lineno = lineno; /* Put the line number in the concordance table. */ if( xref && pass == 2 ) { xreftab[sym->xref_index + sym->xref_count] = lineno; } } return( sym ); } /* evalSymbol() */ /******************************************************************************/ /* */ /* Function: moveToEndOfLine */ /* */ /* Synopsis: Move the parser input to the end of the current input line. */ /* */ /******************************************************************************/ void moveToEndOfLine() { while( !isend( line[cc] )) cc++; lexstart = cc; lexterm = cc; lexstartprev = lexstart; } /* moveToEndOfLine() */ /******************************************************************************/ /* */ /* Function: nextLexeme */ /* */ /* Synopsis: Get the next lexical element from input line. */ /* */ /******************************************************************************/ void nextLexeme() { /* Save start column of previous lexeme for diagnostic messages. */ lexstartprev = lexstart; lextermprev = lexterm; while( is_blank( line[cc] )) { cc++; } lexstart = cc; if( isalnum( line[cc] )) { while( isalnum( line[cc] )) { cc++; } } else if( isend( line[cc] )) { /* End-of-Line, don't advance cc! */ } else { switch( line[cc] ) { case '"': /* Quoted letter */ if( cc + 2 < maxcc ) { cc++; cc++; } else { errorMessage( &no_literal_value, lexstart ); cc++; } break; case '/': /* Comment, don't advance cc! */ break; default: /* All other punctuation. */ cc++; break; } } lexterm = cc; } /* nextLexeme() */ /******************************************************************************/ /* */ /* Function: nextLexBlank */ /* */ /* Synopsis: Used to prevent illegal blanks in expressions. */ /* */ /******************************************************************************/ void nextLexBlank() { nextLexeme(); if( is_blank( delimiter )) { errorMessage( &illegal_blank, lexstart - 1 ); } delimiter = line[lexterm]; } /* nextLexBlank() */ /******************************************************************************/ /* */ /* Function: pseudoOperators */ /* */ /* Synopsis: Process pseudo-ops (directives). */ /* */ /******************************************************************************/ BOOL pseudoOperators( PSEUDO_T val ) { int count, count2; int delim; int index; int ix; int lexstartsave; WORD16 newfield; WORD16 oldclc; int pack; BOOL status; SYM_T *sym; FILE *temp; int term; WORD16 value; char os8_name[8]; int reloc_clc; status = TRUE; switch( (PSEUDO_T) val ) { case ASCII: /* added 18-Jan-2003 PNT -- derived from TEXT */ delim = line[lexstart]; index = lexstart + 1; while( line[index] != delim && !isend( line[index] )) { punchOutObject( clc, (line[index] & 127) | 128 ); incrementClc(); index++; } if( isend( line[index] )) { cc = index; lexterm = cc; errorMessage( &text_string, cc ); } else { cc = index + 1; lexterm = cc; } nextLexeme(); break; case BANK: errorSymbol( &no_pseudo_op, "BANK", lexstartprev ); /* should select a different 32K out of 128K */ break; case BINPUNCH: /* If there has been data output and this is a mode switch, set up to */ /* output data in BIN mode. */ if( binary_data_output && rim_mode ) { clearLiteralTable(); punchLeader( 8 ); /* Generate a short leader/trailer. */ checksum = 0; binary_data_output = FALSE; } rim_mode = FALSE; break; case DECIMAL: radix = 10; break; case DUBL: inputDubl(); break; case EJECT: page_lineno = LIST_LINES_PER_PAGE; /* This will force a page break. */ status = FALSE; /* This will force reading of next line */ break; case ENPUNCH: if( pass == 2 ) { objectfile = objectsave; } break; case EXPUNGE: /* Erase symbol table */ if( pass == 1 ) { symtab[0] = sym_undefined; symbol_top = 0; number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Enter the pseudo-ops into the symbol table. */ for( ix = 0; ix < DIM( pseudo ); ix++ ) { defineSymbol( pseudo[ix].name, pseudo[ix].val, pseudo[ix].type, 0 ); } /* Enter the really permanent symbols into the table. */ /* Also make them part of the permanent symbol table. */ for( ix = 0; ix < DIM( really_permanent_symbols ); ix++ ) { defineSymbol( really_permanent_symbols[ix].name, really_permanent_symbols[ix].val, really_permanent_symbols[ix].type | DEFFIX , 0 ); } number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; } break; case FIELD: /* Punch page 0 also */ punchLiteralPool( cp, 1 ); newfield = field >> 12; lexstartsave = lexstartprev; if( isdone( line[lexstart] )) { newfield += 1; /* Blank FIELD directive. */ } else { newfield = (getExpr())->val; /* FIELD with argument. */ } if( rim_mode ) { errorMessage( &in_rim_mode, lexstartsave ); /* Can't change fields. */ } else if( newfield > 7 || newfield < 0 ) { errorMessage( &illegal_field_value, lexstartprev ); } else { value = (( newfield & 0007 ) << 3 ) | 00300; punchObject( value ); if( objectfile != NULL ) /* Only fix checksum if punching */ { checksum -= value; /* Field punches are not added to checksum. */ } field = newfield << 12; } clc = 0200 | field; fieldlc = clc & 07777; if( !rim_mode ) { punchOrigin( clc ); } clearLiteralTable(); break; case FIXMRI: if( line[lexterm] == '=' && isalpha( line[lexstart] )) { lexstartsave = lexstart; term = lexterm; nextLexeme(); /* Skip symbol. */ nextLexeme(); /* Skip trailing = */ defineLexeme( lexstartsave, term, getExprs(), MRI ); } else { errorLexeme( &symbol_syntax, lexstart ); nextLexeme(); /* Skip symbol. */ nextLexeme(); /* Skip trailing = */ (void) getExprs(); /* Skip expression. */ } break; case FIXTAB: if (pass == 1) /* Only fix on first pass, on second all are defined */ { /* Mark all current symbols as permanent symbols. */ for( ix = 0; ix < symbol_top; ix++ ) { symtab[ix].type = symtab[ix].type | FIXED; } number_of_fixed_symbols = symbol_top; fixed_symbols = &symtab[symbol_top - 1]; /* Re-sort the symbol table */ qsort( symtab, symbol_top, sizeof(symtab[0]), compareSymbols ); } break; case FLTG: inputFltg(); /* errorSymbol( &no_pseudo_op, "FLTG", lexstartprev ); */ break; case IFDEF: if( isalpha( line[lexstart] )) { sym = evalSymbol(); nextLexeme(); if( M_DEFINED_CONDITIONALLY( sym->type )) { conditionTrue(); } else { conditionFalse(); } } else { errorLexeme( &label_syntax, lexstart ); } break; case IFNDEF: if( isalpha( line[lexstart] )) { sym = evalSymbol(); nextLexeme(); if( M_DEFINED_CONDITIONALLY( sym->type )) { conditionFalse(); } else { conditionTrue(); } } else { errorLexeme( &label_syntax, lexstart ); } break; case IFNZERO: if( getExprs() == 0 ) { conditionFalse(); } else { conditionTrue(); } break; case IFZERO: if( getExprs() == 0 ) { conditionTrue(); } else { conditionFalse(); } break; case NOPUNCH: if( pass == 2 ) { objectfile = NULL; } break; case OCTAL: radix = 8; break; case PAGE: reloc_clc = clc + reloc; punchLiteralPool( cp, 0 ); oldclc = clc; if( isdone( line[lexstart] )) { clc = (( reloc_clc + 0177 ) & 077600) - reloc; /* No argumnet. */ fieldlc = clc & 07777; } else { value = (getExpr())->val; clc = field + (( value & 037 ) << 7 ) - reloc; fieldlc = clc & 07777; } testForLiteralCollision( clc + reloc ); if( !rim_mode && clc != oldclc ) { punchOrigin( clc ); } break; case PAUSE: break; case RELOC: if( isdone( line[lexstart] )) { reloc = 0; /* Blank RELOC directive. */ } else { value = (getExpr())->val; /* RELOC with argument. */ reloc = (value & 07777) - ( clc & 07777); } break; case RIMPUNCH: /* If the assembler has output any BIN data, output the literal tables */ /* and the checksum for what has been assembled and setup for RIM mode. */ if( binary_data_output && !rim_mode ) { endOfBinary(); clearLiteralTable(); punchChecksum(); punchLeader( 8 ); /* Generate a short leader/trailer. */ } rim_mode = TRUE; break; case SEGMNT: punchLiteralPool( cp, 0 ); if( isdone( line[lexstart] )) { /* No argument. */ clc = ( clc & 06000 ) + 02000; fieldlc = clc & 07777; } else { getExpr(); clc = ( val & 003 ) << 10; fieldlc = clc & 07777; } if( !rim_mode ) { punchOrigin( clc ); } testForLiteralCollision( clc ); break; case TEXT: delim = line[lexstart]; pack = 0; count = 0; index = lexstart + 1; while( line[index] != delim && !isend( line[index] )) { pack = ( pack << 6 ) | ( line[index] & 077 ); count++; if( count > 1 ) { punchOutObject( clc, pack ); incrementClc(); count = 0; pack = 0; } index++; } if( count != 0 ) { punchOutObject( clc, pack << 6 ); incrementClc(); } else { punchOutObject( clc, 0 ); incrementClc(); } if( isend( line[index] )) { cc = index; lexterm = cc; errorMessage( &text_string, cc ); } else { cc = index + 1; lexterm = cc; } nextLexeme(); break; case FILENAME: memset(os8_name, 0, sizeof(os8_name)); delimiter=line[lexstart]; if (delimiter != '.') { for (index = lexstart, count = 0; index < lexterm && count < 6; index++) { os8_name[count++] = line[index]; } delimiter=line[lexterm]; if (delimiter == '.') { nextLexeme(); /* Skip . */ } } nextLexeme(); if (delimiter == '.') { for (index = lexstart, count = 6; index < lexterm && count < 8; index++) { os8_name[count++] = line[index]; } } pack = 0; count = 0; for (count2 = 0; count2 < 8; count2++) { pack = ( pack << 6 ) | ( os8_name[count2] & 077 ); count++; if( count > 1 ) { punchOutObject( clc, pack ); incrementClc(); count = 0; pack = 0; } } nextLexeme(); break; case DEVICE: memset(os8_name, 0, sizeof(os8_name)); for (index = lexstart, count = 0; index < lexterm && count < 4; index++) { os8_name[count++] = line[index]; } pack = 0; count = 0; for (count2 = 0; count2 < 4; count2++) { pack = ( pack << 6 ) | ( os8_name[count2] & 077 ); count++; if( count > 1 ) { punchOutObject( clc, pack ); incrementClc(); count = 0; pack = 0; } } nextLexeme(); break; case TITLE: delim = line[lexstart]; ix = lexstart + 1; /* Find string delimiter. */ do { if( list_title[ix] == delim && list_title[ix + 1] == delim ) { ix++; } ix++; } while( line[ix] != delim && !isend(line[ix]) ); if( line[ix] == delim ) { count = 0; ix = lexstart + 1; do { if( list_title[ix] == delim && list_title[ix + 1] == delim ) { ix++; } list_title[count] = line[ix]; count++; ix++; list_title[count] = '\0'; } while( line[ix] != delim && !isend(line[ix]) ); if( strlen( list_title ) > TITLELEN ) { list_title[TITLELEN] = '\0'; } cc = ix + 1; lexterm = cc; page_lineno = LIST_LINES_PER_PAGE;/* Force top of page for new titles. */ list_title_set = TRUE; } else { cc = ix; lexterm = cc; errorMessage( &text_string, cc ); } nextLexeme(); break; case XLIST: if( isdone( line[lexstart] )) { temp = listfile; /* Blank XLIST directive. */ listfile = listsave; listsave = temp; } else { if( (getExpr())->val == 0 ) { if( listfile == NULL ) { listfile = listsave; listsave = NULL; } } else { if( listfile != NULL ) { listsave = listfile; listfile = NULL; } } } break; case ZBLOCK: value = (getExpr())->val; if( value < 0 ) { errorMessage( &zblock_too_small, lexstartprev ); } else if( value + ( clc & 07777 ) - 1 > 07777 ) { errorMessage( &zblock_too_large, lexstartprev ); } else { for( ; value > 0; value-- ) { punchOutObject( clc, 0 ); incrementClc(); } } break; default: break; } /* end switch for pseudo-ops */ return( status ); } /* pseudoOperators() */ /******************************************************************************/ /* */ /* Function: conditionFalse */ /* */ /* Synopsis: Called when a false conditional has been evaluated. */ /* Lex should be the opening <; ignore all text until */ /* the closing >. */ /* */ /******************************************************************************/ void conditionFalse() { int level; if( line[lexstart] == '<' ) { /* Invariant: line[cc] is the next unexamined character. */ level = 1; while( level > 0 ) { if( isend( line[cc] )) { readLine(); } else { switch( line[cc] ) { case '>': level--; cc++; break; case '<': level++; cc++; break; case '$': level = 0; cc++; break; default: cc++; break; } /* end switch */ } /* end if */ } /* end while */ nextLexeme(); } else { errorMessage( <_expected, lexstart ); } } /* conditionFalse() */ /******************************************************************************/ /* */ /* Function: conditionTrue */ /* */ /* Synopsis: Called when a true conditional has been evaluated. */ /* Lex should be the opening <; skip it and setup for */ /* normal assembly. */ /* */ /******************************************************************************/ void conditionTrue() { if( line[lexstart] == '<' ) { nextLexeme(); /* Skip the opening '<' */ } else { errorMessage( <_expected, lexstart ); } } /* conditionTrue() */ /******************************************************************************/ /* */ /* Function: errorLexeme */ /* */ /* Synopsis: Display an error message using the current lexical element. */ /* */ /******************************************************************************/ void errorLexeme( EMSG_T *mesg, int col ) { char name[SYMLEN]; errorSymbol( mesg, lexemeToName( name, lexstart, lexterm ), col ); } /* errorLexeme() */ /******************************************************************************/ /* */ /* Function: errorSymbol */ /* */ /* Synopsis: Display an error message with a given string. */ /* */ /******************************************************************************/ void errorSymbol( EMSG_T *mesg, char *name, int col ) { char linecol[12]; char *s; if( pass == 2 ) { s = ( name == NULL ) ? "" : name ; errors++; sprintf( linecol, "(%d:%d)", lineno, col + 1 ); fprintf( errorfile, "%s%-9s : error: %s \"%s\" at Loc = %5.5o\n", filename, linecol, mesg->file, s, clc ); saveError( mesg->list, col ); } error_in_line = TRUE; } /* errorSymbol() */ /******************************************************************************/ /* */ /* Function: errorMessage */ /* */ /* Synopsis: Display an error message without a name argument. */ /* */ /******************************************************************************/ void errorMessage( EMSG_T *mesg, int col ) { char linecol[12]; if( pass == 2 ) { errors++; sprintf( linecol, "(%d:%d)", lineno, col + 1 ); fprintf( errorfile, "%s%-9s : error: %s at Loc = %5.5o\n", filename, linecol, mesg->file, clc ); saveError( mesg->list, col ); } error_in_line = TRUE; } /* errorMessage() */ /******************************************************************************/ /* */ /* Function: saveError */ /* */ /* Synopsis: Save the current error in a list so it may displayed after the */ /* the current line is printed. */ /* */ /******************************************************************************/ void saveError( char *mesg, int col ) { if( save_error_count < DIM( error_list )) { error_list[save_error_count].mesg = mesg; error_list[save_error_count].col = col; save_error_count++; } error_in_line = TRUE; if( listed ) { printErrorMessages(); } } /* saveError() */ /* End-of-File */ |
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
cannot compute difference between binary files
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | v 20130925 2 C 39600 60100 1 0 0 5V-plus-1.sym { T 39600 60100 5 10 1 1 0 0 1 pinnumber=1 } C 39700 58200 1 0 0 gnd-1.sym { T 39600 58400 5 10 1 1 0 0 1 pinnumber=7 } C 40100 58200 1 0 0 gnd-1.sym { T 40000 58400 5 10 1 1 0 0 1 pinnumber=8 } C 40000 60100 1 0 0 5V-plus-1.sym { T 40000 60100 5 10 1 1 0 0 1 pinnumber=2 } C 38500 60600 1 270 0 switch-spst-1.sym { T 39200 60200 5 10 0 0 270 0 1 device=SPST T 38800 60400 5 10 1 1 270 0 1 refdes=Switch } N 40200 59300 40200 60100 4 N 39800 58500 39800 58700 4 N 40200 58500 40200 59000 4 C 37900 60300 1 0 0 BNC-1.sym { T 38250 60950 5 10 0 0 0 0 1 device=BNC T 37900 61000 5 10 1 1 0 0 1 refdes=DC Jack } B 39400 57800 1200 3000 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1 { T 40700 60100 5 10 1 1 270 0 1 name=Expansion Port Pins } N 38000 60300 38000 58700 4 N 38000 58700 39800 58700 4 N 38000 59000 40200 59000 4 N 39800 60100 39800 59600 4 N 38500 59600 39800 59600 4 N 38500 59300 38500 59800 4 N 40200 59300 38500 59300 4 N 38400 60800 38500 60800 4 N 38500 60800 38500 60600 4 T 37800 56300 9 10 1 0 0 0 5 Copyright © 2016 by Warren Young This file is licensed under the terms of the SIMH license, a copy of which is in ../SIMH-LICENSE.md. |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | ######################################################################## # Makefile.in - Processed by autosetup's configure script to generate # an intermediate GNU make(1) file for building the PiDP-8/I software # from within its src/ subdirectory. # # The resulting Makefile will redirect simple "make" calls to the top # level as well as the major top-level targets (e.g. "make clean") but # purposefully will not redirect anything like an installation or "run # the system" type target. Its only purpose is to help out those who # are working on the PiDP-8/I project's C source code from within this # directory. If you need to work on the wider system, do it from the # project's top level. # # If you are seeing this at the top of a file called Makefile and you # intend to make edits, do that in Makefile.in. Saying "make" will then # re-build Makefile from that modified Makefile.in before proceeding to # do the "make" operation. # # Copyright © 2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## all clean ctags distclean tags reconfig: cd @builddir@; make $@ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | ######################################################################## # Makefile.in - Processed by autosetup's configure script to generate # an intermediate GNU make(1) file for building the PiDP-8/I software # from within its src/PDP8 subdirectory. # # The resulting Makefile will redirect simple "make" calls to the top # level as well as the major top-level targets (e.g. "make clean") but # purposefully will not redirect anything like an installation or "run # the system" type target. Its only purpose is to help out those who # are working on the PiDP-8/I project's C source code from within this # directory. If you need to work on the wider system, do it from the # project's top level. # # If you are seeing this at the top of a file called Makefile and you # intend to make edits, do that in Makefile.in. Saying "make" will then # re-build Makefile from that modified Makefile.in before proceeding to # do the "make" operation. # # Copyright © 2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## all clean ctags distclean tags reconfig: cd @builddir@; make $@ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 | /* pdp8_cpu.c: PDP-8 CPU simulator Copyright (c) 1993-2016, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ---------------------------------------------------------------------------- Portions copyright (c) 2015-2017, Oscar Vermeulen, Ian Schofield, and Warren Young Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of the authors above shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from those authors. ---------------------------------------------------------------------------- cpu central processor 18-Sep-16 RMS Added alternate dispatch table for non-contiguous devices 17-Sep-13 RMS Fixed boot in wrong field problem (Dave Gesswein) 28-Apr-07 RMS Removed clock initialization 30-Oct-06 RMS Added idle and infinite loop detection 30-Sep-06 RMS Fixed SC value after DVI overflow (Don North) 22-Sep-05 RMS Fixed declarations (Sterling Garwood) 16-Aug-05 RMS Fixed C++ declaration and cast problems 06-Nov-04 RMS Added =n to SHOW HISTORY 31-Dec-03 RMS Fixed bug in set_cpu_hist 13-Oct-03 RMS Added instruction history Added TSC8-75 support (Bernhard Baehr) 12-Mar-03 RMS Added logical name support 04-Oct-02 RMS Revamped device dispatching, added device number support 06-Jan-02 RMS Added device enable/disable routines 30-Dec-01 RMS Added old PC queue 16-Dec-01 RMS Fixed bugs in EAE 07-Dec-01 RMS Revised to use new breakpoint package 30-Nov-01 RMS Added RL8A, extended SET/SHOW support 16-Sep-01 RMS Fixed bug in reset routine, added KL8A support 10-Aug-01 RMS Removed register from declarations 17-Jul-01 RMS Moved function prototype 07-Jun-01 RMS Fixed bug in JMS to non-existent memory 25-Apr-01 RMS Added device enable/disable support 18-Mar-01 RMS Added DF32 support 05-Mar-01 RMS Added clock calibration support 15-Feb-01 RMS Added DECtape support 14-Apr-99 RMS Changed t_addr to unsigned The register state for the PDP-8 is: AC<0:11> accumulator MQ<0:11> multiplier-quotient L link flag PC<0:11> program counter IF<0:2> instruction field IB<0:2> instruction buffer DF<0:2> data field UF user flag UB user buffer SF<0:6> interrupt save field The PDP-8 has three instruction formats: memory reference, I/O transfer, and operate. The memory reference format is: 0 1 2 3 4 5 6 7 8 9 10 11 +--+--+--+--+--+--+--+--+--+--+--+--+ | op |in|zr| page offset | memory reference +--+--+--+--+--+--+--+--+--+--+--+--+ <0:2> mnemonic action 000 AND AC = AC & M[MA] 001 TAD L'AC = AC + M[MA] 010 DCA M[MA] = AC, AC = 0 011 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 100 JMS M[MA] = PC, PC = MA + 1 101 JMP PC = MA <3:4> mode action 00 page zero MA = IF'0'IR<5:11> 01 current page MA = IF'PC<0:4>'IR<5:11> 10 indirect page zero MA = xF'M[IF'0'IR<5:11>] 11 indirect current page MA = xF'M[IF'PC<0:4>'IR<5:11>] where x is D for AND, TAD, ISZ, DCA, and I for JMS, JMP. Memory reference instructions can access an address space of 32K words. The address space is divided into eight 4K word fields; each field is divided into thirty-two 128 word pages. An instruction can directly address, via its 7b offset, locations 0-127 on page zero or on the current page. All 32k words can be accessed via indirect addressing and the instruction and data field registers. If an indirect address is in locations 0010-0017 of any field, the indirect address is incremented and rewritten to memory before use. The I/O transfer format is as follows: 0 1 2 3 4 5 6 7 8 9 10 11 +--+--+--+--+--+--+--+--+--+--+--+--+ | op | device | pulse | I/O transfer +--+--+--+--+--+--+--+--+--+--+--+--+ The IO transfer instruction sends the the specified pulse to the specified I/O device. The I/O device may take data from the AC, return data to the AC, initiate or cancel operations, or skip on status. The operate format is as follows: +--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 1| 1| 0| | | | | | | | | operate group 1 +--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | | | | | | | | | +--- increment AC 3 | | | | | | +--- rotate 1 or 2 4 | | | | | +--- rotate left 4 | | | | +--- rotate right 4 | | | +--- complement L 2 | | +--- complement AC 2 | +--- clear L 1 +-- clear AC 1 +--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 1| 1| 1| | | | | | | | 0| operate group 2 +--+--+--+--+--+--+--+--+--+--+--+--+ | | | | | | | | | | | | | +--- halt 3 | | | | | +--- or switch register 3 | | | | +--- reverse skip sense 1 | | | +--- skip on L != 0 1 | | +--- skip on AC == 0 1 | +--- skip on AC < 0 1 +-- clear AC 2 +--+--+--+--+--+--+--+--+--+--+--+--+ | 1| 1| 1| 1| | | | | | | | 1| operate group 3 +--+--+--+--+--+--+--+--+--+--+--+--+ | | | | \______/ | | | | | | | +--|-----+--- EAE command 3 | | +--- AC -> MQ, 0 -> AC 2 | +--- MQ v AC --> AC 2 +-- clear AC 1 The operate instruction can be microprogrammed to perform operations on the AC, MQ, and link. This routine is the instruction decode routine for the PDP-8. It is called from the simulator control program to execute instructions in simulated memory, starting at the simulated PC. It runs until 'reason' is set non-zero. General notes: 1. Reasons to stop. The simulator can be stopped by: HALT instruction breakpoint encountered unimplemented instruction and stop_inst flag set I/O error in I/O simulator 2. Interrupts. Interrupts are maintained by three parallel variables: dev_done device done flags int_enable interrupt enable flags int_req interrupt requests In addition, int_req contains the interrupt enable flag, the CIF not pending flag, and the ION not pending flag. If all three of these flags are set, and at least one interrupt request is set, then an interrupt occurs. 3. Non-existent memory. On the PDP-8, reads to non-existent memory return zero, and writes are ignored. In the simulator, the largest possible memory is instantiated and initialized to zero. Thus, only writes outside the current field (indirect writes) need be checked against actual memory size. 3. Adding I/O devices. These modules must be modified: pdp8_defs.h add device number and interrupt definitions pdp8_sys.c add sim_devices table entry */ /* ---PiDP change------------------------------------------------------------------------------------------- */ #include "pidp8i.h" /* ---PiDP end---------------------------------------------------------------------------------------------- */ #define PCQ_SIZE 64 /* must be 2**n */ #define PCQ_MASK (PCQ_SIZE - 1) #define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = MA #define UNIT_V_NOEAE (UNIT_V_UF) /* EAE absent */ #define UNIT_NOEAE (1 << UNIT_V_NOEAE) #define UNIT_V_MSIZE (UNIT_V_UF + 1) /* dummy mask */ #define UNIT_MSIZE (1 << UNIT_V_MSIZE) #define OP_KSF 06031 /* for idle */ #define HIST_PC 0x40000000 #define HIST_MIN 64 #define HIST_MAX 65536 typedef struct { int32 pc; int32 ea; int16 ir; int16 opnd; int16 lac; int16 mq; } InstHistory; uint16 M[MAXMEMSIZE] = { 0 }; /* main memory */ int32 saved_LAC = 0; /* saved L'AC */ int32 saved_MQ = 0; /* saved MQ */ int32 saved_PC = 0; /* saved IF'PC */ int32 saved_DF = 0; /* saved Data Field */ int32 IB = 0; /* Instruction Buffer */ int32 SF = 0; /* Save Field */ int32 emode = 0; /* EAE mode */ int32 gtf = 0; /* EAE gtf flag */ int32 SC = 0; /* EAE shift count */ int32 UB = 0; /* User mode Buffer */ int32 UF = 0; /* User mode Flag */ int32 OSR = 0; /* Switch Register */ int32 tsc_ir = 0; /* TSC8-75 IR */ int32 tsc_pc = 0; /* TSC8-75 PC */ int32 tsc_cdf = 0; /* TSC8-75 CDF flag */ int32 tsc_enb = 0; /* TSC8-75 enabled */ int32 cpu_astop = 0; /* address stop */ int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ int32 pcq_p = 0; /* PC queue ptr */ REG *pcq_r = NULL; /* PC queue reg ptr */ int32 dev_done = 0; /* dev done flags */ int32 int_enable = INT_INIT_ENABLE; /* intr enables */ int32 int_req = 0; /* intr requests */ int32 stop_inst = 0; /* trap on ill inst */ int32 (*dev_tab[DEV_MAX])(int32 IR, int32 dat); /* device dispatch */ int32 hst_p = 0; /* history pointer */ int32 hst_lnt = 0; /* history length */ InstHistory *hst = NULL; /* instruction history */ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); t_stat cpu_reset (DEVICE *dptr); t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_bool build_dev_tab (void); /* CPU data structures cpu_dev CPU device descriptor cpu_unit CPU unit descriptor cpu_reg CPU register list cpu_mod CPU modifier list */ UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; REG cpu_reg[] = { { ORDATAD (PC, saved_PC, 15, "program counter") }, { ORDATAD (AC, saved_LAC, 12, "accumulator") }, { FLDATAD (L, saved_LAC, 12, "link") }, { ORDATAD (MQ, saved_MQ, 12, "multiplier-quotient") }, { ORDATAD (SR, OSR, 12, "front panel switches") }, { GRDATAD (IF, saved_PC, 8, 3, 12, "instruction field") }, { GRDATAD (DF, saved_DF, 8, 3, 12, "data field") }, { GRDATAD (IB, IB, 8, 3, 12, "instruction field buffter") }, { ORDATAD (SF, SF, 7, "save field") }, { FLDATAD (UB, UB, 0, "user mode buffer") }, { FLDATAD (UF, UF, 0, "user mode flag") }, { ORDATAD (SC, SC, 5, "EAE shift counter") }, { FLDATAD (GTF, gtf, 0, "EAE greater than flag") }, { FLDATAD (EMODE, emode, 0, "EAE mode (0 = A, 1 = B)") }, { FLDATAD (ION, int_req, INT_V_ION, "interrupt enable") }, { FLDATAD (ION_DELAY, int_req, INT_V_NO_ION_PENDING, "interrupt enable delay for ION") }, { FLDATAD (CIF_DELAY, int_req, INT_V_NO_CIF_PENDING, "interrupt enable delay for CIF") }, { FLDATAD (PWR_INT, int_req, INT_V_PWR, "power fail interrupt") }, { FLDATAD (UF_INT, int_req, INT_V_UF, "user mode violation interrupt") }, { ORDATAD (INT, int_req, INT_V_ION+1, "interrupt pending flags"), REG_RO }, { ORDATAD (DONE, dev_done, INT_V_DIRECT, "device done flags"), REG_RO }, { ORDATAD (ENABLE, int_enable, INT_V_DIRECT, "device interrupt enable flags"), REG_RO }, { BRDATAD (PCQ, pcq, 8, 15, PCQ_SIZE, "PC prior to last JMP, JMS, or interrupt; most recent PC change first"), REG_RO+REG_CIRC }, { ORDATA (PCQP, pcq_p, 6), REG_HRO }, { FLDATAD (STOP_INST, stop_inst, 0, "stop on undefined instruction") }, { ORDATAD (WRU, sim_int_char, 8, "interrupt character") }, { NULL } }; MTAB cpu_mod[] = { { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL }, { UNIT_NOEAE, 0, "EAE", "EAE", NULL }, { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", &cpu_set_hist, &cpu_show_hist }, { 0 } }; DEVICE cpu_dev = { "CPU", &cpu_unit, cpu_reg, cpu_mod, 1, 8, 15, 1, 8, 12, &cpu_ex, &cpu_dep, &cpu_reset, NULL, NULL, NULL, NULL, 0 }; t_stat sim_instr (void) { int32 IR, MB, IF, DF, LAC, MQ; uint32 PC, MA; int32 device, pulse, temp, iot_data; t_stat reason; /* Restore register state */ if (build_dev_tab ()) /* build dev_tab */ return SCPE_STOP; PC = saved_PC & 007777; /* load local copies */ IF = saved_PC & 070000; DF = saved_DF & 070000; LAC = saved_LAC & 017777; MQ = saved_MQ & 07777; int_req = INT_UPDATE; reason = 0; /* ---PiDP add--------------------------------------------------------------------------------------------- */ // Set some register values we care about which may not get values // before we need them, and which weren't set above. MA = MB = IR = 0; // Light up LEDs for 1st time. Only needed when STOP switch set at start. set_pidp8i_leds(PC, MA, MB, IR, LAC, MQ, IF, DF, SC, int_req, pls_fetch); /* ---PiDP end---------------------------------------------------------------------------------------------- */ /* Main instruction fetch/decode loop */ while (reason == 0) { /* loop until halted */ /* ---PiDP add--------------------------------------------------------------------------------------------- */ awfulHackFlag = 0; // no do script pending. Did I mention awful? /* ---PiDP end---------------------------------------------------------------------------------------------- */ if (sim_interval <= 0) { /* check clock queue */ if ((reason = sim_process_event ())) break; } /* ---PiDP add--------------------------------------------------------------------------------------------- */ switch (handle_flow_control_switches(M, &PC, &MA, &MB, &LAC, &IF, &DF, &int_req)) { case pft_stop: // Don't choke off the SIMH event queue handler. sim_interval = sim_interval - 1; // Update LEDs even in STOP mode. // // Note M[MA] used in this call, not MB. If we pass MB, the // simulator never processes Ctrl-E in STOP mode. FIXME? set_pidp8i_leds(PC, MA, M[MA], IR, LAC, MQ, IF, DF, SC, int_req, pls_fetch); // Go no further in STOP mode. In particular, fetch no more // instructions, and do not touch PC! continue; case pft_halt: // Clear all registers and halt simulator PC = saved_PC = 0; IF = saved_PC = 0; DF = saved_DF = 0; LAC = saved_LAC = 0; MQ = saved_MQ = 0; int_req = 0; reason = STOP_HALT; continue; case pft_normal: // execute normally break; } /* ---PiDP end---------------------------------------------------------------------------------------------- */ if (int_req > INT_PENDING) { /* interrupt? */ int_req = int_req & ~INT_ION; /* interrupts off */ SF = (UF << 6) | (IF >> 9) | (DF >> 12); /* form save field */ IF = IB = DF = UF = UB = 0; /* clear mem ext */ PCQ_ENTRY; /* save old PC */ M[0] = PC; /* save PC in 0 */ PC = 1; /* fetch next from 1 */ } MA = IF | PC; /* form PC */ if (sim_brk_summ && sim_brk_test (MA, (1u << SIM_BKPT_V_SPC) | SWMASK ('E'))) { /* breakpoint? */ reason = STOP_IBKPT; /* stop simulation */ break; } IR = M[MA]; /* fetch instruction */ int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ sim_interval = sim_interval - 1; /* ---PiDP add--------------------------------------------------------------------------------------------- */ // Update the front panel LEDs with the results of our instruction // fetch. This is above the goto label below because while in // single instruction mode, there is no point updating the LEDs // until we fetch another instruction, as above. Until we get // another CONT press and fetch another instruction, the LEDs are // already set correctly. set_pidp8i_leds(PC, MA, M[MA], IR, LAC, MQ, IF, DF, SC, int_req, pls_fetch); /* ---PiDP end---------------------------------------------------------------------------------------------- */ PC = (PC + 1) & 07777; /* increment PC */ /* Instruction decoding. The opcode (IR<0:2>), indirect flag (IR<3>), and page flag (IR<4>) are decoded together. This produces 32 decode points, four per major opcode. For IOT, the extra decode points are not useful; for OPR, only the group flag (IR<3>) is used. AND, TAD, ISZ, DCA calculate a full 15b effective address. JMS, JMP calculate a 12b field-relative effective address. Autoindex calculations always occur within the same field as the instruction fetch. The field must exist; otherwise, the instruction fetched would be 0000, and indirect addressing could not occur. Note that MA contains IF'PC. */ if (hst_lnt) { /* history enabled? */ int32 ea; hst_p = (hst_p + 1); /* next entry */ if (hst_p >= hst_lnt) hst_p = 0; hst[hst_p].pc = MA | HIST_PC; /* save PC, IR, LAC, MQ */ hst[hst_p].ir = IR; hst[hst_p].lac = LAC; hst[hst_p].mq = MQ; if (IR < 06000) { /* mem ref? */ if (IR & 0200) ea = (MA & 077600) | (IR & 0177); else ea = IF | (IR & 0177); /* direct addr */ if (IR & 0400) { /* indirect? */ if (IR < 04000) { /* mem operand? */ if ((ea & 07770) != 00010) ea = DF | M[ea]; else ea = DF | ((M[ea] + 1) & 07777); } else { /* no, jms/jmp */ if ((ea & 07770) != 00010) ea = IB | M[ea]; else ea = IB | ((M[ea] + 1) & 07777); } } hst[hst_p].ea = ea; /* save eff addr */ hst[hst_p].opnd = M[ea]; /* save operand */ } } switch ((IR >> 7) & 037) { /* decode IR<0:4> */ /* Opcode 0, AND */ case 000: /* AND, dir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ LAC = LAC & (M[MA] | 010000); break; case 001: /* AND, dir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ LAC = LAC & (M[MA] | 010000); break; case 002: /* AND, indir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ LAC = LAC & (M[MA] | 010000); break; case 003: /* AND, indir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ LAC = LAC & (M[MA] | 010000); break; /* Opcode 1, TAD */ case 004: /* TAD, dir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ LAC = (LAC + M[MA]) & 017777; break; case 005: /* TAD, dir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ LAC = (LAC + M[MA]) & 017777; break; case 006: /* TAD, indir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ LAC = (LAC + M[MA]) & 017777; break; case 007: /* TAD, indir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ LAC = (LAC + M[MA]) & 017777; break; /* Opcode 2, ISZ */ case 010: /* ISZ, dir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ if (MB == 0) PC = (PC + 1) & 07777; break; case 011: /* ISZ, dir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ if (MB == 0) PC = (PC + 1) & 07777; break; case 012: /* ISZ, indir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ MB = (M[MA] + 1) & 07777; if (MEM_ADDR_OK (MA)) M[MA] = MB; if (MB == 0) PC = (PC + 1) & 07777; break; case 013: /* ISZ, indir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ MB = (M[MA] + 1) & 07777; if (MEM_ADDR_OK (MA)) M[MA] = MB; if (MB == 0) PC = (PC + 1) & 07777; break; /* Opcode 3, DCA */ case 014: /* DCA, dir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ M[MA] = LAC & 07777; LAC = LAC & 010000; break; case 015: /* DCA, dir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ M[MA] = LAC & 07777; LAC = LAC & 010000; break; case 016: /* DCA, indir, zero */ MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; LAC = LAC & 010000; break; case 017: /* DCA, indir, curr */ MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; LAC = LAC & 010000; break; /* Opcode 4, JMS. From Bernhard Baehr's description of the TSC8-75: (In user mode) the current JMS opcode is moved to the ERIOT register, the ECDF flag is cleared. The address of the JMS instruction is loaded into the ERTB register and the TSC8-75 I/O flag is raised. When the TSC8-75 is enabled, the target addess of the JMS is loaded into PC, but nothing else (loading of IF, UF, clearing the interrupt inhibit flag, storing of the return address in the first word of the subroutine) happens. When the TSC8-75 is disabled, the JMS is performed as usual. */ case 020: /* JMS, dir, zero */ PCQ_ENTRY; MA = IR & 0177; /* dir addr, page zero */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } if (UF && tsc_enb) { /* user mode, TSC enab? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } else { /* normal */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ MA = IF | MA; if (MEM_ADDR_OK (MA)) M[MA] = PC; } PC = (MA + 1) & 07777; break; case 021: /* JMS, dir, curr */ PCQ_ENTRY; MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } if (UF && tsc_enb) { /* user mode, TSC enab? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } else { /* normal */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ MA = IF | MA; if (MEM_ADDR_OK (MA)) M[MA] = PC; } PC = (MA + 1) & 07777; break; case 022: /* JMS, indir, zero */ PCQ_ENTRY; MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = M[MA]; else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } if (UF && tsc_enb) { /* user mode, TSC enab? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } else { /* normal */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ MA = IF | MA; if (MEM_ADDR_OK (MA)) M[MA] = PC; } PC = (MA + 1) & 07777; break; case 023: /* JMS, indir, curr */ PCQ_ENTRY; MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = M[MA]; else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } if (UF && tsc_enb) { /* user mode, TSC enab? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } else { /* normal */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ MA = IF | MA; if (MEM_ADDR_OK (MA)) M[MA] = PC; } PC = (MA + 1) & 07777; break; /* Opcode 5, JMP. From Bernhard Baehr's description of the TSC8-75: (In user mode) the current JMP opcode is moved to the ERIOT register, the ECDF flag is cleared. The address of the JMP instruction is loaded into the ERTB register and the TSC8-75 I/O flag is raised. Then the JMP is performed as usual (including the setting of IF, UF and clearing the interrupt inhibit flag). */ case 024: /* JMP, dir, zero */ PCQ_ENTRY; MA = IR & 0177; /* dir addr, page zero */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ if (tsc_enb) { /* TSC8 enabled? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } } IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ PC = MA; break; /* If JMP direct, also check for idle (KSF/JMP *-1) and infinite loop */ case 025: /* JMP, dir, curr */ PCQ_ENTRY; MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ if (tsc_enb) { /* TSC8 enabled? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } } if (sim_idle_enab && /* idling enabled? */ (IF == IB)) { /* to same bank? */ if (MA == ((PC - 2) & 07777)) { /* 1) JMP *-1? */ if (!(int_req & (INT_ION|INT_TTI)) && /* iof, TTI flag off? */ (M[IB|((PC - 2) & 07777)] == OP_KSF)) /* next is KSF? */ sim_idle (TMR_CLK, FALSE); /* we're idle */ } /* end JMP *-1 */ else if (MA == ((PC - 1) & 07777)) { /* 2) JMP *? */ if (!(int_req & INT_ION)) /* iof? */ reason = STOP_LOOP; /* then infinite loop */ else if (!(int_req & INT_ALL)) /* ion, not intr? */ sim_idle (TMR_CLK, FALSE); /* we're idle */ } /* end JMP */ } /* end idle enabled */ IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ PC = MA; break; case 026: /* JMP, indir, zero */ PCQ_ENTRY; MA = IF | (IR & 0177); /* dir addr, page zero */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = M[MA]; else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ if (tsc_enb) { /* TSC8 enabled? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } } IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ PC = MA; break; case 027: /* JMP, indir, curr */ PCQ_ENTRY; MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = M[MA]; else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (UF) { /* user mode? */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ if (tsc_enb) { /* TSC8 enabled? */ tsc_pc = (PC - 1) & 07777; /* save PC */ int_req = int_req | INT_TSC; /* request intr */ } } IF = IB; /* change IF */ UF = UB; /* change UF */ int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ PC = MA; break; /* Opcode 7, OPR group 1 */ case 034:case 035: /* OPR, group 1 */ switch ((IR >> 4) & 017) { /* decode IR<4:7> */ case 0: /* nop */ break; case 1: /* CML */ LAC = LAC ^ 010000; break; case 2: /* CMA */ LAC = LAC ^ 07777; break; case 3: /* CMA CML */ LAC = LAC ^ 017777; break; case 4: /* CLL */ LAC = LAC & 07777; break; case 5: /* CLL CML = STL */ LAC = LAC | 010000; break; case 6: /* CLL CMA */ LAC = (LAC ^ 07777) & 07777; break; case 7: /* CLL CMA CML */ LAC = (LAC ^ 07777) | 010000; break; case 010: /* CLA */ LAC = LAC & 010000; break; case 011: /* CLA CML */ LAC = (LAC & 010000) ^ 010000; break; case 012: /* CLA CMA = STA */ LAC = LAC | 07777; break; case 013: /* CLA CMA CML */ LAC = (LAC | 07777) ^ 010000; break; case 014: /* CLA CLL */ LAC = 0; break; case 015: /* CLA CLL CML */ LAC = 010000; break; case 016: /* CLA CLL CMA */ LAC = 07777; break; case 017: /* CLA CLL CMA CML */ LAC = 017777; break; } /* end switch opers */ if (IR & 01) /* IAC */ LAC = (LAC + 1) & 017777; switch ((IR >> 1) & 07) { /* decode IR<8:10> */ case 0: /* nop */ break; case 1: /* BSW */ LAC = (LAC & 010000) | ((LAC >> 6) & 077) | ((LAC & 077) << 6); break; case 2: /* RAL */ LAC = ((LAC << 1) | (LAC >> 12)) & 017777; break; case 3: /* RTL */ LAC = ((LAC << 2) | (LAC >> 11)) & 017777; break; case 4: /* RAR */ LAC = ((LAC >> 1) | (LAC << 12)) & 017777; break; case 5: /* RTR */ LAC = ((LAC >> 2) | (LAC << 11)) & 017777; break; case 6: /* RAL RAR - undef */ LAC = LAC & (IR | 010000); /* uses AND path */ break; case 7: /* RTL RTR - undef */ LAC = (LAC & 010000) | (MA & 07600) | (IR & 0177); break; /* uses address path */ } /* end switch shifts */ break; /* end group 1 */ /* OPR group 2. From Bernhard Baehr's description of the TSC8-75: (In user mode) HLT (7402), OSR (7404) and microprogrammed combinations with HLT and OSR: Additional to raising a user mode interrupt, the current OPR opcode is moved to the ERIOT register and the ECDF flag is cleared. */ case 036:case 037: /* OPR, groups 2, 3 */ if ((IR & 01) == 0) { /* group 2 */ switch ((IR >> 3) & 017) { /* decode IR<6:8> */ case 0: /* nop */ break; case 1: /* SKP */ PC = (PC + 1) & 07777; break; case 2: /* SNL */ if (LAC >= 010000) PC = (PC + 1) & 07777; break; case 3: /* SZL */ if (LAC < 010000) PC = (PC + 1) & 07777; break; case 4: /* SZA */ if ((LAC & 07777) == 0) PC = (PC + 1) & 07777; break; case 5: /* SNA */ if ((LAC & 07777) != 0) PC = (PC + 1) & 07777; break; case 6: /* SZA | SNL */ if ((LAC == 0) || (LAC >= 010000)) PC = (PC + 1) & 07777; break; case 7: /* SNA & SZL */ if ((LAC != 0) && (LAC < 010000)) PC = (PC + 1) & 07777; break; case 010: /* SMA */ if ((LAC & 04000) != 0) PC = (PC + 1) & 07777; break; case 011: /* SPA */ if ((LAC & 04000) == 0) PC = (PC + 1) & 07777; break; case 012: /* SMA | SNL */ if (LAC >= 04000) PC = (PC + 1) & 07777; break; case 013: /* SPA & SZL */ if (LAC < 04000) PC = (PC + 1) & 07777; break; case 014: /* SMA | SZA */ if (((LAC & 04000) != 0) || ((LAC & 07777) == 0)) PC = (PC + 1) & 07777; break; case 015: /* SPA & SNA */ if (((LAC & 04000) == 0) && ((LAC & 07777) != 0)) PC = (PC + 1) & 07777; break; case 016: /* SMA | SZA | SNL */ if ((LAC >= 04000) || (LAC == 0)) PC = (PC + 1) & 07777; break; case 017: /* SPA & SNA & SZL */ if ((LAC < 04000) && (LAC != 0)) PC = (PC + 1) & 07777; break; } /* end switch skips */ if (IR & 0200) /* CLA */ LAC = LAC & 010000; if ((IR & 06) && UF) { /* user mode? */ int_req = int_req | INT_UF; /* request intr */ tsc_ir = IR; /* save instruction */ tsc_cdf = 0; /* clear flag */ } else { if (IR & 04) { /* OSR */ //--- PiDP add-------------------------------------------------------------------------- OSR = get_switch_register(); /* FIXME: [fad3ad73ea] */ //--- PiDP end-------------------------------------------------------------------------- LAC = LAC | OSR; } if (IR & 02) { /* HLT */ //--- PiDP change-------------------------------------------------------------------------- // reason = STOP_HALT; set_stop_mode(); //--- end of PiDP change-------------------------------------------------------------------------- } } break; } /* end if group 2 */ /* OPR group 3 standard MQA!MQL exchanges AC and MQ, as follows: temp = MQ; MQ = LAC & 07777; LAC = LAC & 010000 | temp; */ temp = MQ; /* group 3 */ if (IR & 0200) /* CLA */ LAC = LAC & 010000; if (IR & 0020) { /* MQL */ MQ = LAC & 07777; LAC = LAC & 010000; } if (IR & 0100) /* MQA */ LAC = LAC | temp; if ((IR & 0056) && (cpu_unit.flags & UNIT_NOEAE)) { reason = stop_inst; /* EAE not present */ break; } /* OPR group 3 EAE The EAE operates in two modes: Mode A, PDP-8/I compatible Mode B, extended capability Mode B provides eight additional subfunctions; in addition, some of the Mode A functions operate differently in Mode B. The mode switch instructions are decoded explicitly and cannot be microprogrammed with other EAE functions (SWAB performs an MQL as part of standard group 3 decoding). If mode switching is decoded, all other EAE timing is suppressed. */ if (IR == 07431) { /* SWAB */ emode = 1; /* set mode flag */ break; } if (IR == 07447) { /* SWBA */ emode = gtf = 0; /* clear mode, gtf */ break; } /* If not switching modes, the EAE operation is determined by the mode and IR<6,8:10>: <6:10> mode A mode B comments 0x000 NOP NOP 0x001 SCL ACS 0x010 MUY MUY if mode B, next = address 0x011 DVI DVI if mode B, next = address 0x100 NMI NMI if mode B, clear AC if result = 4000'0000 0x101 SHL SHL if mode A, extra shift 0x110 ASR ASR if mode A, extra shift 0x111 LSR LSR if mode A, extra shift 1x000 SCA SCA 1x001 SCA + SCL DAD 1x010 SCA + MUY DST 1x011 SCA + DVI SWBA NOP if not detected earlier 1x100 SCA + NMI DPSZ 1x101 SCA + SHL DPIC must be combined with MQA!MQL 1x110 SCA + ASR DCM must be combined with MQA!MQL 1x111 SCA + LSR SAM EAE instructions which fetch memory operands use the CPU's DEFER state to read the first word; if the address operand is in locations x0010 - x0017, it is autoincremented. */ if (emode == 0) /* mode A? clr gtf */ gtf = 0; switch ((IR >> 1) & 027) { /* decode IR<6,8:10> */ case 020: /* mode A, B: SCA */ LAC = LAC | SC; break; case 000: /* mode A, B: NOP */ break; case 021: /* mode B: DAD */ if (emode) { MA = IF | PC; if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ MQ = MQ + M[MA]; MA = DF | ((MA + 1) & 07777); LAC = (LAC & 07777) + M[MA] + (MQ >> 12); MQ = MQ & 07777; PC = (PC + 1) & 07777; break; } LAC = LAC | SC; /* mode A: SCA then */ case 001: /* mode B: ACS */ if (emode) { SC = LAC & 037; LAC = LAC & 010000; } else { /* mode A: SCL */ SC = (~M[IF | PC]) & 037; PC = (PC + 1) & 07777; } break; case 022: /* mode B: DST */ if (emode) { MA = IF | PC; if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ if (MEM_ADDR_OK (MA)) M[MA] = MQ & 07777; MA = DF | ((MA + 1) & 07777); if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; PC = (PC + 1) & 07777; break; } LAC = LAC | SC; /* mode A: SCA then */ case 002: /* MUY */ MA = IF | PC; if (emode) { /* mode B: defer */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ } temp = (MQ * M[MA]) + (LAC & 07777); LAC = (temp >> 12) & 07777; MQ = temp & 07777; PC = (PC + 1) & 07777; SC = 014; /* 12 shifts */ break; case 023: /* mode B: SWBA */ if (emode) break; LAC = LAC | SC; /* mode A: SCA then */ case 003: /* DVI */ MA = IF | PC; if (emode) { /* mode B: defer */ if ((MA & 07770) != 00010) /* indirect; autoinc? */ MA = DF | M[MA]; else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ } if ((LAC & 07777) >= M[MA]) { /* overflow? */ LAC = LAC | 010000; /* set link */ MQ = ((MQ << 1) + 1) & 07777; /* rotate MQ */ SC = 0; /* no shifts */ } else { temp = ((LAC & 07777) << 12) | MQ; MQ = temp / M[MA]; LAC = temp % M[MA]; SC = 015; /* 13 shifts */ } PC = (PC + 1) & 07777; break; case 024: /* mode B: DPSZ */ if (emode) { if (((LAC | MQ) & 07777) == 0) PC = (PC + 1) & 07777; break; } LAC = LAC | SC; /* mode A: SCA then */ case 004: /* NMI */ temp = (LAC << 12) | MQ; /* preserve link */ for (SC = 0; ((temp & 017777777) != 0) && (temp & 040000000) == ((temp << 1) & 040000000); SC++) temp = temp << 1; LAC = (temp >> 12) & 017777; MQ = temp & 07777; if (emode && ((LAC & 07777) == 04000) && (MQ == 0)) LAC = LAC & 010000; /* clr if 4000'0000 */ break; case 025: /* mode B: DPIC */ if (emode) { temp = (LAC + 1) & 07777; /* SWP already done! */ LAC = MQ + (temp == 0); MQ = temp; break; } LAC = LAC | SC; /* mode A: SCA then */ case 5: /* SHL */ SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ if (SC > 25) /* >25? result = 0 */ temp = 0; else temp = ((LAC << 12) | MQ) << SC; /* <=25? shift LAC:MQ */ LAC = (temp >> 12) & 017777; MQ = temp & 07777; PC = (PC + 1) & 07777; SC = emode? 037: 0; /* SC = 0 if mode A */ break; case 026: /* mode B: DCM */ if (emode) { temp = (-LAC) & 07777; /* SWP already done! */ LAC = (MQ ^ 07777) + (temp == 0); MQ = temp; break; } LAC = LAC | SC; /* mode A: SCA then */ case 6: /* ASR */ SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ temp = ((LAC & 07777) << 12) | MQ; /* sext from AC0 */ if (LAC & 04000) temp = temp | ~037777777; if (emode && (SC != 0)) gtf = (temp >> (SC - 1)) & 1; if (SC > 25) temp = (LAC & 04000)? -1: 0; else temp = temp >> SC; LAC = (temp >> 12) & 017777; MQ = temp & 07777; PC = (PC + 1) & 07777; SC = emode? 037: 0; /* SC = 0 if mode A */ break; case 027: /* mode B: SAM */ if (emode) { temp = LAC & 07777; LAC = MQ + (temp ^ 07777) + 1; /* L'AC = MQ - AC */ gtf = (temp <= MQ) ^ ((temp ^ MQ) >> 11); break; } LAC = LAC | SC; /* mode A: SCA then */ case 7: /* LSR */ SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ temp = ((LAC & 07777) << 12) | MQ; /* clear link */ if (emode && (SC != 0)) gtf = (temp >> (SC - 1)) & 1; if (SC > 24) /* >24? result = 0 */ temp = 0; else temp = temp >> SC; /* <=24? shift AC:MQ */ LAC = (temp >> 12) & 07777; MQ = temp & 07777; PC = (PC + 1) & 07777; SC = emode? 037: 0; /* SC = 0 if mode A */ break; } /* end switch */ break; /* end case 7 */ /* Opcode 6, IOT. From Bernhard Baehr's description of the TSC8-75: (In user mode) Additional to raising a user mode interrupt, the current IOT opcode is moved to the ERIOT register. When the IOT is a CDF instruction (62x1), the ECDF flag is set, otherwise it is cleared. */ case 030:case 031:case 032:case 033: /* IOT */ if (UF) { /* privileged? */ int_req = int_req | INT_UF; /* request intr */ tsc_ir = IR; /* save instruction */ if ((IR & 07707) == 06201) /* set/clear flag */ tsc_cdf = 1; else tsc_cdf = 0; break; } device = (IR >> 3) & 077; /* device = IR<3:8> */ /* --------------------------------------------------------------------------------------------------------- */ // the IOT ION, IOF do not light pause, anything else does: /* --------------------------------------------------------------------------------------------------------- */ pulse = IR & 07; /* pulse = IR<9:11> */ iot_data = LAC & 07777; /* AC unchanged */ switch (device) { /* decode IR<3:8> */ case 000: /* CPU control */ switch (pulse) { /* decode IR<9:11> */ case 0: /* SKON */ if (int_req & INT_ION) PC = (PC + 1) & 07777; int_req = int_req & ~INT_ION; break; case 1: /* ION */ int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; break; case 2: /* IOF */ int_req = int_req & ~INT_ION; break; case 3: /* SRQ */ if (int_req & INT_ALL) PC = (PC + 1) & 07777; break; case 4: /* GTF */ LAC = (LAC & 010000) | ((LAC & 010000) >> 1) | (gtf << 10) | (((int_req & INT_ALL) != 0) << 9) | (((int_req & INT_ION) != 0) << 7) | SF; break; case 5: /* RTF */ gtf = ((LAC & 02000) >> 10); UB = (LAC & 0100) >> 6; IB = (LAC & 0070) << 9; DF = (LAC & 0007) << 12; LAC = ((LAC & 04000) << 1) | iot_data; int_req = (int_req | INT_ION) & ~INT_NO_CIF_PENDING; break; case 6: /* SGT */ if (gtf) PC = (PC + 1) & 07777; break; case 7: /* CAF */ gtf = 0; emode = 0; int_req = int_req & INT_NO_CIF_PENDING; dev_done = 0; int_enable = INT_INIT_ENABLE; LAC = 0; reset_all (1); /* reset all dev */ break; } /* end switch pulse */ break; /* end case 0 */ case 020:case 021:case 022:case 023: case 024:case 025:case 026:case 027: /* memory extension */ /* --------------------------------------------------------------------------------------------------------- */ // Memory extension does not trigger IOP pauses --> do not light pause /* --------------------------------------------------------------------------------------------------------- */ switch (pulse) { /* decode IR<9:11> */ case 1: /* CDF */ DF = (IR & 0070) << 9; break; case 2: /* CIF */ IB = (IR & 0070) << 9; int_req = int_req & ~INT_NO_CIF_PENDING; break; case 3: /* CDF CIF */ DF = IB = (IR & 0070) << 9; int_req = int_req & ~INT_NO_CIF_PENDING; break; case 4: switch (device & 07) { /* decode IR<6:8> */ case 0: /* CINT */ int_req = int_req & ~INT_UF; break; case 1: /* RDF */ LAC = LAC | (DF >> 9); break; case 2: /* RIF */ LAC = LAC | (IF >> 9); break; case 3: /* RIB */ LAC = LAC | SF; break; case 4: /* RMF */ UB = (SF & 0100) >> 6; IB = (SF & 0070) << 9; DF = (SF & 0007) << 12; int_req = int_req & ~INT_NO_CIF_PENDING; break; case 5: /* SINT */ if (int_req & INT_UF) PC = (PC + 1) & 07777; break; case 6: /* CUF */ UB = 0; int_req = int_req & ~INT_NO_CIF_PENDING; break; case 7: /* SUF */ UB = 1; int_req = int_req & ~INT_NO_CIF_PENDING; break; } /* end switch device */ break; default: reason = stop_inst; break; } /* end switch pulse */ break; /* end case 20-27 */ case 010: /* power fail */ switch (pulse) { /* decode IR<9:11> */ case 1: /* SBE */ break; case 2: /* SPL */ if (int_req & INT_PWR) PC = (PC + 1) & 07777; break; case 3: /* CAL */ int_req = int_req & ~INT_PWR; break; default: reason = stop_inst; break; } /* end switch pulse */ break; /* end case 10 */ default: /* I/O device */ if (dev_tab[device]) { /* dev present? */ /* ---PiDP add--------------------------------------------------------------------------------------------- */ // Any other device will trigger IOP, so light pause set_pidp8i_leds(PC, MA, MB, IR, LAC, MQ, IF, DF, SC, int_req, pls_pause); /* ---PiDP end---------------------------------------------------------------------------------------------- */ iot_data = dev_tab[device] (IR, iot_data); LAC = (LAC & 010000) | (iot_data & 07777); if (iot_data & IOT_SKP) PC = (PC + 1) & 07777; if (iot_data >= IOT_REASON) reason = iot_data >> IOT_V_REASON; } else reason = stop_inst; /* stop on flag */ break; } /* end switch device */ break; /* end case IOT */ } /* end switch opcode */ /* ---PiDP add--------------------------------------------------------------------------------------------- */ if (IR < 05000) set_pidp8i_leds(PC, MA, MB, IR, LAC, MQ, IF, DF, SC, int_req, pls_execute); /* ---PiDP end---------------------------------------------------------------------------------------------- */ } /* end while */ /* Simulation halted */ saved_PC = IF | (PC & 07777); /* save copies */ saved_DF = DF & 070000; saved_LAC = LAC & 017777; saved_MQ = MQ & 07777; pcq_r->qptr = pcq_p; /* update pc q ptr */ return reason; } /* end sim_instr */ /* Reset routine */ t_stat cpu_reset (DEVICE *dptr) { int_req = (int_req & ~INT_ION) | INT_NO_CIF_PENDING; saved_DF = IB = saved_PC & 070000; UF = UB = gtf = emode = 0; pcq_r = find_reg ("PCQ", NULL, dptr); if (pcq_r) pcq_r->qptr = 0; else return SCPE_IERR; sim_brk_types = SWMASK ('E') | SWMASK('I'); sim_brk_dflt = SWMASK ('E'); return SCPE_OK; } /* Set PC for boot (PC<14:12> will typically be 0) */ void cpu_set_bootpc (int32 pc) { saved_PC = pc; /* set PC, IF */ saved_DF = IB = pc & 070000; /* set IB, DF */ return; } /* Memory examine */ t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { if (addr >= MEMSIZE) return SCPE_NXM; if (vptr != NULL) *vptr = M[addr] & 07777; return SCPE_OK; } /* Memory deposit */ t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { if (addr >= MEMSIZE) return SCPE_NXM; M[addr] = val & 07777; return SCPE_OK; } /* Memory size change */ t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { int32 mc = 0; uint32 i; if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) return SCPE_ARG; for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) return SCPE_OK; MEMSIZE = val; for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; return SCPE_OK; } /* Change device number for a device */ t_stat set_dev (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { DEVICE *dptr; DIB *dibp; uint32 newdev; t_stat r; if (cptr == NULL) return SCPE_ARG; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; newdev = get_uint (cptr, 8, DEV_MAX - 1, &r); /* get new */ if ((r != SCPE_OK) || (newdev == dibp->dev)) return r; dibp->dev = newdev; /* store */ return SCPE_OK; } /* Show device number for a device */ t_stat show_dev (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { DEVICE *dptr; DIB *dibp; if (uptr == NULL) return SCPE_IERR; dptr = find_dev_from_unit (uptr); if (dptr == NULL) return SCPE_IERR; dibp = (DIB *) dptr->ctxt; if (dibp == NULL) return SCPE_IERR; fprintf (st, "devno=%02o", dibp->dev); if (dibp->num > 1) fprintf (st, "-%2o", dibp->dev + dibp->num - 1); return SCPE_OK; } /* CPU device handler - should never get here! */ int32 bad_dev (int32 IR, int32 AC) { return (SCPE_IERR << IOT_V_REASON) | AC; /* broken! */ } /* Build device dispatch table */ t_bool build_dev_tab (void) { DEVICE *dptr; DIB *dibp; uint32 i, j; static const uint8 std_dev[] = { 000, 010, 020, 021, 022, 023, 024, 025, 026, 027 }; for (i = 0; i < DEV_MAX; i++) /* clr table */ dev_tab[i] = NULL; for (i = 0; i < ((uint32) sizeof (std_dev)); i++) /* std entries */ dev_tab[std_dev[i]] = &bad_dev; for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* add devices */ dibp = (DIB *) dptr->ctxt; /* get DIB */ if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ if (dibp->dsp_tbl) { /* dispatch table? */ DIB_DSP *dspp = dibp->dsp_tbl; /* set ptr */ for (j = 0; j < dibp->num; j++, dspp++) { /* loop thru tbl */ if (dspp->dsp) { /* any dispatch? */ if (dev_tab[dspp->dev]) { /* already filled? */ sim_printf ("%s device number conflict at %02o\n", sim_dname (dptr), dibp->dev + j); return TRUE; } dev_tab[dspp->dev] = dspp->dsp; /* fill */ } /* end if dsp */ } /* end for j */ } /* end if dsp_tbl */ else { /* inline dispatches */ for (j = 0; j < dibp->num; j++) { /* loop thru disp */ if (dibp->dsp[j]) { /* any dispatch? */ if (dev_tab[dibp->dev + j]) { /* already filled? */ sim_printf ("%s device number conflict at %02o\n", sim_dname (dptr), dibp->dev + j); return TRUE; } dev_tab[dibp->dev + j] = dibp->dsp[j]; /* fill */ } /* end if dsp */ } /* end for j */ } /* end else */ } /* end if enb */ } /* end for i */ return FALSE; } /* Set history */ t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { int32 i, lnt; t_stat r; if (cptr == NULL) { for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; hst_p = 0; return SCPE_OK; } lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; hst_p = 0; if (hst_lnt) { free (hst); hst_lnt = 0; hst = NULL; } if (lnt) { hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); if (hst == NULL) return SCPE_MEM; hst_lnt = lnt; } return SCPE_OK; } /* Show history */ t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { int32 l, k, di, lnt; const char *cptr = (const char *) desc; t_stat r; t_value sim_eval; InstHistory *h; if (hst_lnt == 0) /* enabled? */ return SCPE_NOFNC; if (cptr) { lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; } else lnt = hst_lnt; di = hst_p - lnt; /* work forward */ if (di < 0) di = di + hst_lnt; fprintf (st, "PC L AC MQ ea IR\n\n"); for (k = 0; k < lnt; k++) { /* print specified */ h = &hst[(++di) % hst_lnt]; /* entry pointer */ if (h->pc & HIST_PC) { /* instruction? */ l = (h->lac >> 12) & 1; /* link */ fprintf (st, "%05o %o %04o %04o ", h->pc & ADDRMASK, l, h->lac & 07777, h->mq); if (h->ir < 06000) fprintf (st, "%05o ", h->ea); else fprintf (st, " "); sim_eval = h->ir; if ((fprint_sym (st, h->pc & ADDRMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) fprintf (st, "(undefined) %04o", h->ir); if (h->ir < 04000) fprintf (st, " [%04o]", h->opnd); fputc ('\n', st); /* end line */ } /* end else instruction */ } /* end for */ return SCPE_OK; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 | /* pdp8_dt.c: PDP-8 DECtape simulator Copyright (c) 1993-2013, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. dt TC08/TU56 DECtape 17-Sep-13 RMS Changed to use central set_bootpc routine 23-Jun-06 RMS Fixed switch conflict in ATTACH 07-Jan-06 RMS Fixed unaligned register access bug (Doug Carman) 16-Aug-05 RMS Fixed C++ declaration and cast problems 25-Jan-04 RMS Revised for device debug support 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR 18-Oct-03 RMS Fixed bugs in read all, tightened timing 25-Apr-03 RMS Revised for extended file support 14-Mar-03 RMS Fixed sizing interaction with save/restore 17-Oct-02 RMS Fixed bug in end of reel logic 04-Oct-02 RMS Added DIB, device number support 12-Sep-02 RMS Added support for 16b format 30-May-02 RMS Widened POS to 32b 06-Jan-02 RMS Changed enable/disable support 30-Nov-01 RMS Added read only unit, extended SET/SHOW support 24-Nov-01 RMS Changed POS, STATT, LASTT, FLG to arrays 29-Aug-01 RMS Added casts to PDP-18b packup routine 17-Jul-01 RMS Moved function prototype 11-May-01 RMS Fixed bug in reset 25-Apr-01 RMS Added device enable/disable support 18-Apr-01 RMS Changed to rewind tape before boot 19-Mar-01 RMS Changed bootstrap to support 4k disk monitor 15-Mar-01 RMS Added 129th word to PDP-8 format PDP-8 DECtapes are represented in memory by fixed length buffer of 16b words. Three file formats are supported: 18b/36b 256 words per block [256 x 18b] 16b 256 words per block [256 x 16b] 12b 129 words per block [129 x 12b] When a 16b or 18/36bb DECtape file is read in, it is converted to 12b format. DECtape motion is measured in 3b lines. Time between lines is 33.33us. Tape density is nominally 300 lines per inch. The format of a DECtape (as taken from the TD8E formatter) is: reverse end zone 8192 reverse end zone codes ~ 10 feet reverse buffer 200 interblock codes block 0 : block n forward buffer 200 interblock codes forward end zone 8192 forward end zone codes ~ 10 feet A block consists of five 18b header words, a tape-specific number of data words, and five 18b trailer words. All systems except the PDP-8 use a standard block length of 256 words; the PDP-8 uses a standard block length of 86 words (x 18b = 129 words x 12b). Because a DECtape file only contains data, the simulator cannot support write timing and mark track and can only do a limited implementation of read all and write all. Read all assumes that the tape has been conventionally written forward: header word 0 0 header word 1 block number (for forward reads) header words 2,3 0 header word 4 checksum (for reverse reads) : trailer word 4 checksum (for forward reads) trailer words 3,2 0 trailer word 1 block number (for reverse reads) trailer word 0 0 Write all writes only the data words and dumps the non-data words in the bit bucket. */ #include "pdp8_defs.h" #define DT_NUMDR 8 /* #drives */ #define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ #define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ #define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ #define UNIT_WLK (1 << UNIT_V_WLK) #define UNIT_8FMT (1 << UNIT_V_8FMT) #define UNIT_11FMT (1 << UNIT_V_11FMT) #define STATE u3 /* unit state */ #define LASTT u4 /* last time update */ #define WRITTEN u5 /* device buffer is dirty and needs flushing */ #define DT_WC 07754 /* word count */ #define DT_CA 07755 /* current addr */ #define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ /* System independent DECtape constants */ #define DT_LPERMC 6 /* lines per mark track */ #define DT_BLKWD 1 /* blk no word in h/t */ #define DT_CSMWD 4 /* checksum word in h/t */ #define DT_HTWRD 5 /* header/trailer words */ #define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ #define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ #define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ #define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ #define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ /* 16b, 18b, 36b DECtape constants */ #define D18_WSIZE 6 /* word size in lines */ #define D18_BSIZE 384 /* block size in 12b */ #define D18_TSIZE 578 /* tape size */ #define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) #define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) #define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ #define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE) #define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32)) #define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16)) /* 12b DECtape constants */ #define D8_WSIZE 4 /* word size in lines */ #define D8_BSIZE 129 /* block size in 12b */ #define D8_TSIZE 1474 /* tape size */ #define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) #define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) #define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ #define D8_FILSIZ (D8_CAPAC * sizeof (int16)) /* This controller */ #define DT_CAPAC D8_CAPAC /* default */ #define DT_WSIZE D8_WSIZE /* Calculated constants, per unit */ #define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) #define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) #define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) #define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) #define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) #define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) #define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) #define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) #define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) #define DT_QREZ(u) (((u)->pos) < DT_EZLIN) #define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) #define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) /* Status register A */ #define DTA_V_UNIT 9 /* unit select */ #define DTA_M_UNIT 07 #define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) #define DTA_V_MOT 7 /* motion */ #define DTA_M_MOT 03 #define DTA_V_MODE 6 /* mode */ #define DTA_V_FNC 3 /* function */ #define DTA_M_FNC 07 #define FNC_MOVE 00 /* move */ #define FNC_SRCH 01 /* search */ #define FNC_READ 02 /* read */ #define FNC_RALL 03 /* read all */ #define FNC_WRIT 04 /* write */ #define FNC_WALL 05 /* write all */ #define FNC_WMRK 06 /* write timing */ #define DTA_V_ENB 2 /* int enable */ #define DTA_V_CERF 1 /* clr error flag */ #define DTA_V_CDTF 0 /* clr DECtape flag */ #define DTA_FWDRV (1u << (DTA_V_MOT + 1)) #define DTA_STSTP (1u << DTA_V_MOT) #define DTA_MODE (1u << DTA_V_MODE) #define DTA_ENB (1u << DTA_V_ENB) #define DTA_CERF (1u << DTA_V_CERF) #define DTA_CDTF (1u << DTA_V_CDTF) #define DTA_RW (07777 & ~(DTA_CERF | DTA_CDTF)) #define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT) #define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) #define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) /* Status register B */ #define DTB_V_ERF 11 /* error flag */ #define DTB_V_MRK 10 /* mark trk err */ #define DTB_V_END 9 /* end zone err */ #define DTB_V_SEL 8 /* select err */ #define DTB_V_PAR 7 /* parity err */ #define DTB_V_TIM 6 /* timing err */ #define DTB_V_MEX 3 /* memory extension */ #define DTB_M_MEX 07 #define DTB_MEX (DTB_M_MEX << DTB_V_MEX) #define DTB_V_DTF 0 /* DECtape flag */ #define DTB_ERF (1u << DTB_V_ERF) #define DTB_MRK (1u << DTB_V_MRK) #define DTB_END (1u << DTB_V_END) #define DTB_SEL (1u << DTB_V_SEL) #define DTB_PAR (1u << DTB_V_PAR) #define DTB_TIM (1u << DTB_V_TIM) #define DTB_DTF (1u << DTB_V_DTF) #define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \ DTB_PAR | DTB_TIM) #define DTB_GETMEX(x) (((x) & DTB_MEX) << (12 - DTB_V_MEX)) /* DECtape state */ #define DTS_V_MOT 3 /* motion */ #define DTS_M_MOT 07 #define DTS_STOP 0 /* stopped */ #define DTS_DECF 2 /* decel, fwd */ #define DTS_DECR 3 /* decel, rev */ #define DTS_ACCF 4 /* accel, fwd */ #define DTS_ACCR 5 /* accel, rev */ #define DTS_ATSF 6 /* @speed, fwd */ #define DTS_ATSR 7 /* @speed, rev */ #define DTS_DIR 01 /* dir mask */ #define DTS_V_FNC 0 /* function */ #define DTS_M_FNC 07 #define DTS_OFR 7 /* "off reel" */ #define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) #define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) #define DTS_V_2ND 6 /* next state */ #define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ #define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) #define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) #define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ ((DTS_STA (y, z)) << DTS_V_2ND) #define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ ((DTS_STA (y, z)) << DTS_V_3RD) #define DTS_NXTSTA(x) (x >> DTS_V_2ND) /* Operation substates */ #define DTO_WCO 1 /* wc overflow */ #define DTO_SOB 2 /* start of block */ /* Logging */ #define LOG_MS 001 /* move, search */ #define LOG_RW 002 /* read, write */ #define LOG_BL 004 /* block # lblk */ #define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \ int_req = int_req | INT_DTA; \ else int_req = int_req & ~INT_DTA; #define ABS(x) (((x) < 0)? (-(x)): (x)) extern uint16 M[]; extern int32 int_req; extern UNIT cpu_unit; int32 dtsa = 0; /* status A */ int32 dtsb = 0; /* status B */ int32 dt_ltime = 12; /* interline time */ int32 dt_dctime = 40000; /* decel time */ int32 dt_substate = 0; int32 dt_logblk = 0; int32 dt_stopoffr = 0; int32 dt76 (int32 IR, int32 AC); int32 dt77 (int32 IR, int32 AC); t_stat dt_svc (UNIT *uptr); t_stat dt_reset (DEVICE *dptr); t_stat dt_attach (UNIT *uptr, CONST char *cptr); void dt_flush (UNIT *uptr); t_stat dt_detach (UNIT *uptr); t_stat dt_boot (int32 unitno, DEVICE *dptr); void dt_deselect (int32 oldf); void dt_newsa (int32 newf); void dt_newfnc (UNIT *uptr, int32 newsta); t_bool dt_setpos (UNIT *uptr); void dt_schedez (UNIT *uptr, int32 dir); void dt_seterr (UNIT *uptr, int32 e); int32 dt_comobv (int32 val); int32 dt_csum (UNIT *uptr, int32 blk); int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir); /* DT data structures dt_dev DT device descriptor dt_unit DT unit list dt_reg DT register list dt_mod DT modifier list */ DIB dt_dib = { DEV_DTA, 2, { &dt76, &dt77 } }; UNIT dt_unit[] = { { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) } }; REG dt_reg[] = { { ORDATAD (DTSA, dtsa, 12, "status register A") }, { ORDATAD (DTSB, dtsb, 12, "status register B") }, { FLDATAD (INT, int_req, INT_V_DTA, "interrupt pending flag") }, { FLDATAD (ENB, dtsa, DTA_V_ENB, "interrupt enable flag") }, { FLDATAD (DTF, dtsb, DTB_V_DTF, "DECtape flag") }, { FLDATAD (ERF, dtsb, DTB_V_ERF, "error flag") }, { ORDATAD (WC, M[DT_WC], 12, "word count (memory location 7755)"), REG_FIT }, { ORDATAD (CA, M[DT_CA], 12, "current address (memory location 7754)"), REG_FIT }, { DRDATAD (LTIME, dt_ltime, 24, "time between lines"), REG_NZ | PV_LEFT }, { DRDATAD (DCTIME, dt_dctime, 24, "time to decelerate to a full stop"), REG_NZ | PV_LEFT }, { ORDATAD (SUBSTATE, dt_substate, 2, "read/write command substate") }, { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, { URDATAD (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, DT_NUMDR, PV_LEFT | REG_RO, "position, in lines, units 0 to 7") }, { URDATAD (STATT, dt_unit[0].STATE, 8, 18, 0, DT_NUMDR, REG_RO, "unit state, units 0 to 7") }, { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0, DT_NUMDR, REG_HRO) }, { FLDATAD (STOP_OFFR, dt_stopoffr, 0, "stop on off-reel error") }, { ORDATA (DEVNUM, dt_dib.dev, 6), REG_HRO }, { NULL } }; MTAB dt_mod[] = { { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_dev, &show_dev, NULL }, { 0 } }; DEBTAB dt_deb[] = { { "MOTION", LOG_MS }, { "DATA", LOG_RW }, { "BLOCK", LOG_BL }, { NULL, 0 } }; DEVICE dt_dev = { "DT", dt_unit, dt_reg, dt_mod, DT_NUMDR, 8, 24, 1, 8, 12, NULL, NULL, &dt_reset, &dt_boot, &dt_attach, &dt_detach, &dt_dib, DEV_DISABLE | DEV_DEBUG, 0, dt_deb, NULL, NULL }; /* IOT routines */ int32 dt76 (int32 IR, int32 AC) { int32 pulse = IR & 07; int32 old_dtsa = dtsa, fnc; UNIT *uptr; if (pulse & 01) /* DTRA */ AC = AC | dtsa; if (pulse & 06) { /* select */ if (pulse & 02) /* DTCA */ dtsa = 0; if (pulse & 04) { /* DTXA */ if ((AC & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR; if ((AC & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF; dtsa = dtsa ^ (AC & DTA_RW); AC = 0; /* clr AC */ } if ((old_dtsa ^ dtsa) & DTA_UNIT) dt_deselect (old_dtsa); uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */ fnc = DTA_GETFNC (dtsa); /* get fnc */ if (((uptr->flags) & UNIT_DIS) || /* disabled? */ (fnc >= FNC_WMRK) || /* write mark? */ ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) || ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT))) dt_seterr (uptr, DTB_SEL); /* select err */ else dt_newsa (dtsa); DT_UPDINT; } return AC; } int32 dt77 (int32 IR, int32 AC) { int32 pulse = IR & 07; if ((pulse & 01) && (dtsb & (DTB_ERF |DTB_DTF))) /* DTSF */ AC = IOT_SKP | AC; if (pulse & 02) /* DTRB */ AC = AC | dtsb; if (pulse & 04) { /* DTLB */ dtsb = (dtsb & ~DTB_MEX) | (AC & DTB_MEX); AC = AC & ~07777; /* clear AC */ } return AC; } /* Unit deselect */ void dt_deselect (int32 oldf) { int32 old_unit = DTA_GETUNIT (oldf); UNIT *uptr = dt_dev.units + old_unit; int32 old_mot = DTS_GETMOT (uptr->STATE); if (old_mot >= DTS_ATSF) /* at speed? */ dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); else if (old_mot >= DTS_ACCF) /* accelerating? */ DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); return; } /* Command register change 1. If change in motion, stop to start - schedule acceleration - set function as next state 2. If change in motion, start to stop - if not already decelerating (could be reversing), schedule deceleration 3. If change in direction, - if not decelerating, schedule deceleration - set accelerating (other dir) as next state - set function as next next state 4. If not accelerating or at speed, - schedule acceleration - set function as next state 5. If not yet at speed, - set function as next state 6. If at speed, - set function as current state, schedule function */ void dt_newsa (int32 newf) { int32 new_unit, prev_mot, new_fnc; int32 prev_mving, new_mving, prev_dir, new_dir; UNIT *uptr; new_unit = DTA_GETUNIT (newf); /* new, old units */ uptr = dt_dev.units + new_unit; if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ dt_seterr (uptr, DTB_SEL); /* no, error */ return; } prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ prev_mving = prev_mot != DTS_STOP; /* previous moving? */ prev_dir = prev_mot & DTS_DIR; /* previous dir? */ new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ new_fnc = DTA_GETFNC (newf); /* new function? */ if ((prev_mving | new_mving) == 0) /* stop to stop */ return; if (new_mving & ~prev_mving) { /* start? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* schedule acc */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_mving & ~new_mving) { /* stop? */ if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime); /* schedule decel */ } DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ return; } if (prev_dir ^ new_dir) { /* dir chg? */ if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* stop current */ sim_activate (uptr, dt_dctime); /* schedule decel */ } DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ return; } if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ if (dt_setpos (uptr)) /* update pos */ return; sim_cancel (uptr); /* cancel cur */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } if (prev_mot < DTS_ATSF) { /* not at speed? */ DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ return; } dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ return; } /* Schedule new DECtape function This routine is only called if - the selected unit is attached - the selected unit is at speed (forward or backward) This routine - updates the selected unit's position - updates the selected unit's state - schedules the new operation */ void dt_newfnc (UNIT *uptr, int32 newsta) { int32 fnc, dir, blk, unum, relpos, newpos; uint32 oldpos; oldpos = uptr->pos; /* save old pos */ if (dt_setpos (uptr)) /* update pos */ return; uptr->STATE = newsta; /* update state */ fnc = DTS_GETFNC (uptr->STATE); /* set variables */ dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; unum = (int32) (uptr - dt_dev.units); if (oldpos == uptr->pos) /* bump pos */ uptr->pos = uptr->pos + (dir? -1: 1); blk = DT_LIN2BL (uptr->pos, uptr); if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ dt_seterr (uptr, DTB_END); /* set ez flag, stop */ return; } sim_cancel (uptr); /* cancel cur op */ dt_substate = DTO_SOB; /* substate = block start */ switch (fnc) { /* case function */ case DTS_OFR: /* off reel */ if (dir) /* rev? < start */ newpos = -1000; else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ break; case FNC_MOVE: /* move */ dt_schedez (uptr, dir); /* sched end zone */ if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n", unum, (dir? "backward": "forward")); return; /* done */ case FNC_SRCH: /* search */ if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; else newpos = DT_BLK2LN ((DT_QREZ (uptr)? 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s]\n", unum, (dir? "backward": "forward")); break; case FNC_WRIT: /* write */ case FNC_READ: /* read */ case FNC_RALL: /* read all */ case FNC_WALL: /* write all */ if (DT_QEZ (uptr)) { /* in "ok" end zone? */ if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); break; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { dt_seterr (uptr, DTB_SEL); return; } if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); break; default: dt_seterr (uptr, DTB_SEL); /* bad state */ return; } sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Update DECtape position DECtape motion is modeled as a constant velocity, with linear acceleration and deceleration. The motion equations are as follows: t = time since operation started tmax = time for operation (accel, decel only) v = at speed velocity in lines (= 1/dt_ltime) Then: at speed dist = t * v accel dist = (t^2 * v) / (2 * tmax) decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) This routine uses the relative (integer) time, rather than the absolute (floating point) time, to allow save and restore of the start times. */ t_bool dt_setpos (UNIT *uptr) { uint32 new_time, ut, ulin, udelt; int32 mot = DTS_GETMOT (uptr->STATE); int32 unum, delta; new_time = sim_grtime (); /* current time */ ut = new_time - uptr->LASTT; /* elapsed time */ if (ut == 0) /* no time gone? exit */ return FALSE; uptr->LASTT = new_time; /* update last time */ switch (mot & ~DTS_DIR) { /* case on motion */ case DTS_STOP: /* stop */ delta = 0; break; case DTS_DECF: /* slowing */ ulin = ut / (uint32) dt_ltime; udelt = dt_dctime / dt_ltime; delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); break; case DTS_ACCF: /* accelerating */ ulin = ut / (uint32) dt_ltime; udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; delta = (ulin * ulin) / (2 * udelt); break; case DTS_ATSF: /* at speed */ delta = ut / (uint32) dt_ltime; break; } if (mot & DTS_DIR) /* update pos */ uptr->pos = uptr->pos - delta; else uptr->pos = uptr->pos + delta; if (((int32) uptr->pos < 0) || ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { detach_unit (uptr); /* off reel? */ uptr->STATE = uptr->pos = 0; unum = (int32) (uptr - dt_dev.units); if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ dt_seterr (uptr, DTB_SEL); /* error */ return TRUE; } return FALSE; } /* Unit service Unit must be attached, detach cancels operation */ t_stat dt_svc (UNIT *uptr) { int32 mot = DTS_GETMOT (uptr->STATE); int32 dir = mot & DTS_DIR; int32 fnc = DTS_GETFNC (uptr->STATE); int16 *fbuf = (int16 *) uptr->filebuf; int32 unum = uptr - dt_dev.units; int32 blk, wrd, ma, relpos, dat; uint32 ba; /* Motion cases Decelerating - if next state != stopped, must be accel reverse Accelerating - next state must be @speed, schedule function At speed - do functional processing */ switch (mot) { case DTS_DECF: case DTS_DECR: /* decelerating */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ if (uptr->STATE) /* not stopped? */ sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* must be reversing */ return SCPE_OK; case DTS_ACCF: case DTS_ACCR: /* accelerating */ dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ return SCPE_OK; case DTS_ATSF: case DTS_ATSR: /* at speed */ break; /* check function */ default: /* other */ dt_seterr (uptr, DTB_SEL); /* state error */ return SCPE_OK; } /* Functional cases Move - must be at end zone Search - transfer block number, schedule next block Off reel - detach unit (it must be deselected) */ if (dt_setpos (uptr)) /* upd pos; off reel? */ return IORETURN (dt_stopoffr, STOP_DTOFF); if (DT_QEZ (uptr)) { /* in end zone? */ dt_seterr (uptr, DTB_END); /* end zone error */ return SCPE_OK; } blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ switch (fnc) { /* at speed, check fnc */ case FNC_MOVE: /* move */ dt_seterr (uptr, DTB_END); /* end zone error */ return SCPE_OK; case FNC_SRCH: /* search */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr word cnt */ ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ if (MEM_ADDR_OK (ma)) /* store block # */ M[ma] = blk & 07777; if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ break; case DTS_OFR: /* off reel */ detach_unit (uptr); /* must be deselected */ uptr->STATE = uptr->pos = 0; /* no visible action */ break; /* Read has four subcases Start of block, not wc ovf - check that DTF is clear, otherwise normal Normal - increment MA, WC, copy word from tape to memory if read dir != write dir, bits must be scrambled if wc overflow, next state is wc overflow if end of block, possibly set DTF, next state is start of block Wc ovf, not start of block - if end of block, possibly set DTF, next state is start of block Wc ovf, start of block - if end of block reached, timing error, otherwise, continue to next word */ case FNC_READ: /* read */ wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ switch (dt_substate) { /* case on substate */ case DTO_SOB: /* start of block */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } if (DEBUG_PRI (dt_dev, LOG_RW) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n", unum, blk, (dir? "backward": "forward"), ((dtsa & DTA_MODE)? " continuous": " ")); dt_substate = 0; /* fall through */ case 0: /* normal read */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & 07777; ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ dat = fbuf[ba]; /* get tape word */ if (dir) /* rev? comp obv */ dat = dt_comobv (dat); if (MEM_ADDR_OK (ma)) /* mem addr legal? */ M[ma] = dat; if (M[DT_WC] == 0) /* wc ovf? */ dt_substate = DTO_WCO; case DTO_WCO: /* wc ovf, not sob */ if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ sim_activate (uptr, DT_WSIZE * dt_ltime); else { dt_substate = dt_substate | DTO_SOB; sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ } break; case DTO_WCO | DTO_SOB: /* next block */ if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */ dt_seterr (uptr, DTB_TIM); /* timing error */ else sim_activate (uptr, DT_WSIZE * dt_ltime); break; } break; /* Write has four subcases Start of block, not wc ovf - check that DTF is clear, set block direction Normal - increment MA, WC, copy word from memory to tape if wc overflow, next state is wc overflow if end of block, possibly set DTF, next state is start of block Wc ovf, not start of block - copy 0 to tape if end of block, possibly set DTF, next state is start of block Wc ovf, start of block - schedule end zone */ case FNC_WRIT: /* write */ wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ switch (dt_substate) { /* case on substate */ case DTO_SOB: /* start block */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } if (DEBUG_PRI (dt_dev, LOG_RW) || (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk, (dir? "backward": "forward"), ((dtsa & DTA_MODE)? " continuous": " ")); dt_substate = 0; /* fall through */ case 0: /* normal write */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & 07777; case DTO_WCO: /* wc ovflo */ ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ dat = dt_substate? 0: M[ma]; /* get word */ if (dir) /* rev? comp obv */ dat = dt_comobv (dat); fbuf[ba] = dat; /* write word */ uptr->WRITTEN = TRUE; if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; if (M[DT_WC] == 0) dt_substate = DTO_WCO; if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ sim_activate (uptr, DT_WSIZE * dt_ltime); else { dt_substate = dt_substate | DTO_SOB; sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ } break; case DTO_WCO | DTO_SOB: /* all done */ dt_schedez (uptr, dir); /* sched end zone */ break; } break; /* Read all has two subcases Not word count overflow - increment MA, WC, copy word from tape to memory Word count overflow - schedule end zone */ case FNC_RALL: switch (dt_substate) { /* case on substate */ case 0: case DTO_SOB: /* read in progress */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & 07777; ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; dat = fbuf[ba]; /* get tape word */ if (dir) /* rev? comp obv */ dat = dt_comobv (dat); } else dat = dt_gethdr (uptr, blk, relpos, dir); /* get hdr */ sim_activate (uptr, DT_WSIZE * dt_ltime); if (MEM_ADDR_OK (ma)) /* mem addr legal? */ M[ma] = dat; if (M[DT_WC] == 0) dt_substate = DTO_WCO; if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ break; case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ dt_schedez (uptr, dir); /* sched end zone */ break; } /* end case substate */ break; /* Write all has two subcases Not word count overflow - increment MA, WC, copy word from memory to tape Word count overflow - schedule end zone */ case FNC_WALL: switch (dt_substate) { /* case on substate */ case 0: case DTO_SOB: /* read in progress */ if (dtsb & DTB_DTF) { /* DTF set? */ dt_seterr (uptr, DTB_TIM); /* timing error */ return SCPE_OK; } relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ M[DT_CA] = (M[DT_CA] + 1) & 07777; ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ if ((relpos >= DT_HTLIN) && /* in data zone? */ (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { dat = M[ma]; /* get mem word */ if (dir) dat = dt_comobv (dat); wrd = DT_LIN2WD (uptr->pos, uptr); ba = (blk * DTU_BSIZE (uptr)) + wrd; fbuf[ba] = dat; /* write word */ if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; } /* ignore hdr */ sim_activate (uptr, DT_WSIZE * dt_ltime); if (M[DT_WC] == 0) dt_substate = DTO_WCO; if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) dtsb = dtsb | DTB_DTF; /* set DTF */ break; case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ dt_schedez (uptr, dir); /* sched end zone */ break; } /* end case substate */ break; default: dt_seterr (uptr, DTB_SEL); /* impossible state */ break; } DT_UPDINT; /* update interrupts */ return SCPE_OK; } /* Reading the header is complicated, because 18b words are being parsed out 12b at a time. The sequence of word numbers is directionally sensitive Forward Reverse Word Word Content Word Word Content (abs) (rel) (abs) (rel) 137 8 fwd csm'00 6 6 rev csm'00 138 9 0000 5 5 0000 139 10 0000 4 4 0000 140 11 0000 3 3 0000 141 12 00'lo rev blk 2 2 00'lo fwd blk 142 13 hi rev blk 1 1 hi fwd blk 143 14 0000 0 0 0000 0 0 0000 143 14 0000 1 1 0000 142 13 0000 2 2 hi fwd blk 141 12 hi rev blk 3 3 lo fwd blk'00 140 11 lo rev blk'00 4 4 0000 139 10 0000 5 5 0000 138 9 0000 6 6 0000 137 8 0000 7 7 rev csm 136 7 00'fwd csm */ int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir) { if (relpos >= DT_HTLIN) relpos = relpos - (DT_WSIZE * DTU_BSIZE (uptr)); if (dir) { /* reverse */ switch (relpos / DT_WSIZE) { case 6: /* rev csm */ return 077; case 2: /* lo fwd blk */ return dt_comobv ((blk & 077) << 6); case 1: /* hi fwd blk */ return dt_comobv (blk >> 6); case 12: /* hi rev blk */ return (blk >> 6) & 07777; case 11: /* lo rev blk */ return ((blk & 077) << 6); case 7: /* fwd csum */ return (dt_comobv (dt_csum (uptr, blk)) << 6); default: /* others */ return 07777; } } else { /* forward */ switch (relpos / DT_WSIZE) { case 8: /* fwd csum */ return (dt_csum (uptr, blk) << 6); case 12: /* lo rev blk */ return dt_comobv ((blk & 077) << 6); case 13: /* hi rev blk */ return dt_comobv (blk >> 6); case 2: /* hi fwd blk */ return ((blk >> 6) & 07777); case 3: /* lo fwd blk */ return ((blk & 077) << 6); case 7: /* rev csum */ return 077; default: /* others */ break; } } return 0; } /* Utility routines */ /* Set error flag */ void dt_seterr (UNIT *uptr, int32 e) { int32 mot = DTS_GETMOT (uptr->STATE); dtsa = dtsa & ~DTA_STSTP; /* clear go */ dtsb = dtsb | DTB_ERF | e; /* set error flag */ if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ sim_cancel (uptr); /* cancel activity */ if (dt_setpos (uptr)) /* update position */ return; sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ } DT_UPDINT; return; } /* Schedule end zone */ void dt_schedez (UNIT *uptr, int32 dir) { int32 newpos; if (dir) /* rev? rev ez */ newpos = DT_EZLIN - DT_WSIZE; else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); return; } /* Complement obverse routine */ int32 dt_comobv (int32 dat) { dat = dat ^ 07777; /* compl obverse */ dat = ((dat >> 9) & 07) | ((dat >> 3) & 070) | ((dat & 070) << 3) | ((dat & 07) << 9); return dat; } /* Checksum routine */ int32 dt_csum (UNIT *uptr, int32 blk) { int16 *fbuf = (int16 *) uptr->filebuf; int32 ba = blk * DTU_BSIZE (uptr); int32 i, csum, wrd; csum = 077; /* init csum */ for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ wrd = fbuf[ba + i] ^ 07777; /* get ~word */ csum = csum ^ (wrd >> 6) ^ wrd; } return (csum & 077); } /* Reset routine */ t_stat dt_reset (DEVICE *dptr) { int32 i, prev_mot; UNIT *uptr; for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ uptr = dt_dev.units + i; if (sim_is_running) { /* CAF? */ prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ if (dt_setpos (uptr)) /* update pos */ continue; sim_cancel (uptr); sim_activate (uptr, dt_dctime); /* sched decel */ DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); } } else { sim_cancel (uptr); /* sim reset */ uptr->STATE = 0; uptr->LASTT = sim_grtime (); } } dtsa = dtsb = 0; /* clear status */ DT_UPDINT; /* reset interrupt */ return SCPE_OK; } /* Bootstrap routine This is actually the 4K disk monitor bootstrap, which also works with OS/8. The reverse is not true - the OS/8 bootstrap doesn't work with the disk monitor. */ #define BOOT_START 0200 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 07600, /* 200, CLA CLL */ 01216, /* TAD MVB ; move back */ 04210, /* JMS DO ; action */ 01217, /* TAD K7577 ; addr */ 03620, /* DCA I CA */ 01222, /* TAD RDF ; read fwd */ 04210, /* JMS DO ; action */ 05600, /* JMP I 200 ; enter boot */ 00000, /* DO, 0 */ 06766, /* DTCA!DTXA ; start tape */ 03621, /* DCA I WC ; clear wc */ 06771, /* DTSF ; wait */ 05213, /* JMP .-1 */ 05610, /* JMP I DO */ 00600, /* MVB, 0600 */ 07577, /* K7577, 7757 */ 07755, /* CA, 7755 */ 07754, /* WC, 7754 */ 00220 /* RF, 0220 */ }; t_stat dt_boot (int32 unitno, DEVICE *dptr) { size_t i; if (unitno) /* only unit 0 */ return SCPE_ARG; if (dt_dib.dev != DEV_DTA) /* only std devno */ return STOP_NOTSTD; dt_unit[unitno].pos = DT_EZLIN; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; cpu_set_bootpc (BOOT_START); return SCPE_OK; } /* Attach routine Determine 12b, 16b, or 18b/36b format Allocate buffer If 16b or 18b, read 16b or 18b format and convert to 12b in buffer If 12b, read data into buffer */ t_stat dt_attach (UNIT *uptr, CONST char *cptr) { uint32 pdp18b[D18_NBSIZE]; uint16 pdp11b[D18_NBSIZE], *fbuf; int32 i, k; int32 u = uptr - dt_dev.units; t_stat r; uint32 ba, sz; r = attach_unit (uptr, cptr); /* attach */ if (r != SCPE_OK) return r; /* fail? */ if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; if (sim_switches & SWMASK ('F')) /* att 18b? */ uptr->flags = uptr->flags & ~UNIT_8FMT; else if (sim_switches & SWMASK ('S')) /* att 16b? */ uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ (sz = sim_fsize (uptr->fileref))) { if (sz == D11_FILSIZ) uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; else if (sz > D8_FILSIZ) uptr->flags = uptr->flags & ~UNIT_8FMT; } } uptr->capac = DTU_CAPAC (uptr); /* set capacity */ uptr->filebuf = calloc (uptr->capac, sizeof (uint16)); if (uptr->filebuf == NULL) { /* can't alloc? */ detach_unit (uptr); return SCPE_MEM; } fbuf = (uint16 *) uptr->filebuf; /* file buffer */ sim_printf ("%s%d: ", sim_dname (&dt_dev), u); if (uptr->flags & UNIT_8FMT) sim_printf ("12b format"); else if (uptr->flags & UNIT_11FMT) sim_printf ("16b format"); else sim_printf ("18b/36b format"); sim_printf (", buffering file in memory\n"); uptr->io_flush = dt_flush; if (uptr->flags & UNIT_8FMT) /* 12b? */ uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16), uptr->capac, uptr->fileref); else { /* 16b/18b */ for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ if (uptr->flags & UNIT_11FMT) { k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); for (i = 0; i < k; i++) pdp18b[i] = pdp11b[i]; } else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); if (k == 0) break; for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0; for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */ fbuf[ba] = (pdp18b[k] >> 6) & 07777; fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) | ((pdp18b[k + 1] >> 12) & 077); fbuf[ba + 2] = pdp18b[k + 1] & 07777; ba = ba + 3; } /* end blk loop */ } /* end file loop */ uptr->hwmark = ba; } /* end else */ uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ uptr->pos = DT_EZLIN; /* beyond leader */ uptr->LASTT = sim_grtime (); /* last pos update */ return SCPE_OK; } /* Detach routine Cancel in progress operation If 12b, write buffer to file If 16b or 18b, convert 12b buffer to 16b or 18b and write to file Deallocate buffer */ void dt_flush (UNIT* uptr) { uint32 pdp18b[D18_NBSIZE]; uint16 pdp11b[D18_NBSIZE], *fbuf; int32 i, k; uint32 ba; if (uptr->WRITTEN && uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ rewind (uptr->fileref); /* start of file */ fbuf = (uint16 *) uptr->filebuf; /* file buffer */ if (uptr->flags & UNIT_8FMT) /* PDP8? */ fxwrite (uptr->filebuf, sizeof (uint16), /* write file */ uptr->hwmark, uptr->fileref); else { /* 16b/18b */ for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */ for (k = 0; k < D18_NBSIZE; k = k + 2) { pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) | ((uint32) (fbuf[ba + 1] >> 6) & 077); pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) | ((uint32) (fbuf[ba + 2] & 07777)); ba = ba + 3; } /* end loop blk */ if (uptr->flags & UNIT_11FMT) { /* 16b? */ for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i]; fxwrite (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); } else fxwrite (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); } /* end loop buf */ } /* end else */ if (ferror (uptr->fileref)) sim_perror ("I/O error"); } uptr->WRITTEN = FALSE; /* no longer dirty */ } t_stat dt_detach (UNIT* uptr) { int u = (int)(uptr - dt_dev.units); if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (sim_is_active (uptr)) { sim_cancel (uptr); if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; DT_UPDINT; } uptr->STATE = uptr->pos = 0; } if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ sim_printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); dt_flush (uptr); } /* end if hwmark */ free (uptr->filebuf); /* release buf */ uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ uptr->filebuf = NULL; /* clear buf ptr */ uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */ uptr->capac = DT_CAPAC; /* default size */ return detach_unit (uptr); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | /* pdp8_pt.c: PDP-8 paper tape reader/punch simulator Copyright (c) 1993-2013, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. ptr,ptp PC8E paper tape reader/punch 17-Mar-13 RMS Modified to use central set_bootpc routine 25-Apr-03 RMS Revised for extended file support 04-Oct-02 RMS Added DIBs 30-May-02 RMS Widened POS to 32b 30-Nov-01 RMS Added read only unit support 30-Mar-98 RMS Added RIM loader as PTR bootstrap */ #include "pdp8_defs.h" extern int32 int_req, int_enable, dev_done, stop_inst; int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ int32 ptr (int32 IR, int32 AC); int32 ptp (int32 IR, int32 AC); t_stat ptr_svc (UNIT *uptr); t_stat ptp_svc (UNIT *uptr); t_stat ptr_reset (DEVICE *dptr); t_stat ptp_reset (DEVICE *dptr); t_stat ptr_boot (int32 unitno, DEVICE *dptr); /* PTR data structures ptr_dev PTR device descriptor ptr_unit PTR unit descriptor ptr_reg PTR register list */ DIB ptr_dib = { DEV_PTR, 1, { &ptr } }; UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT }; REG ptr_reg[] = { { ORDATAD (BUF, ptr_unit.buf, 8, "last data item processed") }, { FLDATAD (DONE, dev_done, INT_V_PTR, "device done flag") }, { FLDATAD (ENABLE, int_enable, INT_V_PTR, "interrupt enable flag") }, { FLDATAD (INT, int_req, INT_V_PTR, "interrupt pending flag") }, { DRDATAD (POS, ptr_unit.pos, T_ADDR_W, "position in the input file"), PV_LEFT }, { DRDATAD (TIME, ptr_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT }, { FLDATAD (STOP_IOE, ptr_stopioe, 0, "stop on I/O error") }, { NULL } }; MTAB ptr_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, { 0 } }; DEVICE ptr_dev = { "PTR", &ptr_unit, ptr_reg, ptr_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptr_reset, &ptr_boot, NULL, NULL, &ptr_dib, 0 }; /* PTP data structures ptp_dev PTP device descriptor ptp_unit PTP unit descriptor ptp_reg PTP register list */ DIB ptp_dib = { DEV_PTP, 1, { &ptp } }; UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; REG ptp_reg[] = { { ORDATAD (BUF, ptp_unit.buf, 8, "last data item processed") }, { FLDATAD (DONE, dev_done, INT_V_PTP, "device done flag") }, { FLDATAD (ENABLE, int_enable, INT_V_PTP, "interrupt enable flag") }, { FLDATAD (INT, int_req, INT_V_PTP, "interrupt pending flag") }, { DRDATAD (POS, ptp_unit.pos, T_ADDR_W, "position in the output file"), PV_LEFT }, { DRDATAD (TIME, ptp_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT }, { FLDATAD (STOP_IOE, ptp_stopioe, 0, "stop on I/O error") }, { NULL } }; MTAB ptp_mod[] = { { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, { 0 } }; DEVICE ptp_dev = { "PTP", &ptp_unit, ptp_reg, ptp_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &ptp_reset, NULL, NULL, NULL, &ptp_dib, 0 }; /* Paper tape reader: IOT routine */ int32 ptr (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 0: /* RPE */ int_enable = int_enable | (INT_PTR+INT_PTP); /* set enable */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 1: /* RSF */ return (dev_done & INT_PTR)? IOT_SKP + AC: AC; case 6: /* RFC!RRB */ sim_activate (&ptr_unit, ptr_unit.wait); case 2: /* RRB */ dev_done = dev_done & ~INT_PTR; /* clear flag */ int_req = int_req & ~INT_PTR; /* clear int req */ return (AC | ptr_unit.buf); /* or data to AC */ case 4: /* RFC */ sim_activate (&ptr_unit, ptr_unit.wait); dev_done = dev_done & ~INT_PTR; /* clear flag */ int_req = int_req & ~INT_PTR; /* clear int req */ return AC; default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat ptr_svc (UNIT *uptr) { int32 temp; if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptr_stopioe, SCPE_UNATT); if ((temp = getc (ptr_unit.fileref)) == EOF) { if (feof (ptr_unit.fileref)) { if (ptr_stopioe) sim_printf ("PTR end of file\n"); else return SCPE_OK; } else sim_perror ("PTR I/O error"); clearerr (ptr_unit.fileref); return SCPE_IOERR; } dev_done = dev_done | INT_PTR; /* set done */ int_req = INT_UPDATE; /* update interrupts */ ptr_unit.buf = temp & 0377; ptr_unit.pos = ptr_unit.pos + 1; return SCPE_OK; } /* Reset routine */ t_stat ptr_reset (DEVICE *dptr) { ptr_unit.buf = 0; dev_done = dev_done & ~INT_PTR; /* clear done, int */ int_req = int_req & ~INT_PTR; int_enable = int_enable | INT_PTR; /* set enable */ sim_cancel (&ptr_unit); /* deactivate unit */ return SCPE_OK; } /* Paper tape punch: IOT routine */ int32 ptp (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 0: /* PCE */ int_enable = int_enable & ~(INT_PTR+INT_PTP); /* clear enables */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 1: /* PSF */ return (dev_done & INT_PTP)? IOT_SKP + AC: AC; case 2: /* PCF */ dev_done = dev_done & ~INT_PTP; /* clear flag */ int_req = int_req & ~INT_PTP; /* clear int req */ return AC; case 6: /* PLS */ dev_done = dev_done & ~INT_PTP; /* clear flag */ int_req = int_req & ~INT_PTP; /* clear int req */ case 4: /* PPC */ ptp_unit.buf = AC & 0377; /* load punch buf */ sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ return AC; default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat ptp_svc (UNIT *uptr) { dev_done = dev_done | INT_PTP; /* set done */ int_req = INT_UPDATE; /* update interrupts */ if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ return IORETURN (ptp_stopioe, SCPE_UNATT); if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { sim_perror ("PTP I/O error"); clearerr (ptp_unit.fileref); return SCPE_IOERR; } ptp_unit.pos = ptp_unit.pos + 1; return SCPE_OK; } /* Reset routine */ t_stat ptp_reset (DEVICE *dptr) { ptp_unit.buf = 0; dev_done = dev_done & ~INT_PTP; /* clear done, int */ int_req = int_req & ~INT_PTP; int_enable = int_enable | INT_PTP; /* set enable */ sim_cancel (&ptp_unit); /* deactivate unit */ return SCPE_OK; } /* Bootstrap routine */ #define BOOT_START 07756 #define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) static const uint16 boot_rom[] = { 06014, /* 7756, RFC */ 06011, /* 7757, LOOP, RSF */ 05357, /* JMP .-1 */ 06016, /* RFC RRB */ 07106, /* CLL RTL*/ 07006, /* RTL */ 07510, /* SPA*/ 05374, /* JMP 7774 */ 07006, /* RTL */ 06011, /* RSF */ 05367, /* JMP .-1 */ 06016, /* RFC RRB */ 07420, /* SNL */ 03776, /* DCA I 7776 */ 03376, /* 7774, DCA 7776 */ 05357, /* JMP 7757 */ 00000, /* 7776, 0 */ 05301 /* 7777, JMP 7701 */ }; t_stat ptr_boot (int32 unitno, DEVICE *dptr) { size_t i; extern uint16 M[]; if (ptr_dib.dev != DEV_PTR) /* only std devno */ return STOP_NOTSTD; for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; cpu_set_bootpc (BOOT_START); return SCPE_OK; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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); } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 | /* pdp8_tt.c: PDP-8 console terminal simulator Copyright (c) 1993-2016, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. tti,tto KL8E terminal input/output 18-Apr-12 RMS Revised to use clock coscheduling 18-Jun-07 RMS Added UNIT_IDLE flag to console input 18-Oct-06 RMS Synced keyboard to clock 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode 22-Nov-05 RMS Revised for new terminal processing routines 28-May-04 RMS Removed SET TTI CTRL-C 29-Dec-03 RMS Added console output backpressure support 25-Apr-03 RMS Revised for extended file support 02-Mar-02 RMS Added SET TTI CTRL-C 22-Dec-02 RMS Added break support 01-Nov-02 RMS Added 7B/8B support 04-Oct-02 RMS Added DIBs, device number support 30-May-02 RMS Widened POS to 32b 07-Sep-01 RMS Moved function prototypes */ #include "pdp8_defs.h" #include "sim_tmxr.h" #include <ctype.h> extern int32 int_req, int_enable, dev_done, stop_inst; extern int32 tmxr_poll; int32 tti (int32 IR, int32 AC); int32 tto (int32 IR, int32 AC); t_stat tti_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr); t_stat tti_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr); t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc); /* TTI data structures tti_dev TTI device descriptor tti_unit TTI unit descriptor tti_reg TTI register list tti_mod TTI modifiers list */ DIB tti_dib = { DEV_TTI, 1, { &tti } }; UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE|TT_MODE_KSR, 0), SERIAL_IN_WAIT }; REG tti_reg[] = { { ORDATAD (BUF, tti_unit.buf, 8, "last data item processed") }, { FLDATAD (DONE, dev_done, INT_V_TTI, "device done flag") }, { FLDATAD (ENABLE, int_enable, INT_V_TTI, "interrupt enable flag") }, { FLDATAD (INT, int_req, INT_V_TTI, "interrupt pending flag") }, { DRDATAD (POS, tti_unit.pos, T_ADDR_W, "number of characters input"), PV_LEFT }, { DRDATAD (TIME, tti_unit.wait, 24, "input polling interval (if 0, the keyboard is polled synchronously with the clock)"), PV_LEFT+REG_NZ }, { NULL } }; MTAB tti_mod[] = { { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev, NULL }, { 0 } }; DEVICE tti_dev = { "TTI", &tti_unit, tti_reg, tti_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tti_reset, NULL, NULL, NULL, &tti_dib, 0 }; uint32 tti_buftime; /* time input character arrived */ /* TTO data structures tto_dev TTO device descriptor tto_unit TTO unit descriptor tto_reg TTO register list */ DIB tto_dib = { DEV_TTO, 1, { &tto } }; UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }; REG tto_reg[] = { { ORDATAD (BUF, tto_unit.buf, 8, "last date item processed") }, { FLDATAD (DONE, dev_done, INT_V_TTO, "device done flag") }, { FLDATAD (ENABLE, int_enable, INT_V_TTO, "interrupt enable flag") }, { FLDATAD (INT, int_req, INT_V_TTO, "interrupt pending flag") }, { DRDATAD (POS, tto_unit.pos, T_ADDR_W, "number of characters output"), PV_LEFT }, { DRDATAD (TIME, tto_unit.wait, 24, "time form I/O initiation to interrupt"), PV_LEFT }, { NULL } }; MTAB tto_mod[] = { { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, { 0 } }; DEVICE tto_dev = { "TTO", &tto_unit, tto_reg, tto_mod, 1, 10, 31, 1, 8, 8, NULL, NULL, &tto_reset, NULL, NULL, NULL, &tto_dib, 0 }; /* Terminal input: IOT routine */ int32 tti (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 0: /* KCF */ dev_done = dev_done & ~INT_TTI; /* clear flag */ int_req = int_req & ~INT_TTI; return AC; case 1: /* KSF */ return (dev_done & INT_TTI)? IOT_SKP + AC: AC; case 2: /* KCC */ dev_done = dev_done & ~INT_TTI; /* clear flag */ int_req = int_req & ~INT_TTI; return 0; /* clear AC */ case 4: /* KRS */ return (AC | tti_unit.buf); /* return buffer */ case 5: /* KIE */ if (AC & 1) int_enable = int_enable | (INT_TTI+INT_TTO); else int_enable = int_enable & ~(INT_TTI+INT_TTO); int_req = INT_UPDATE; /* update interrupts */ return AC; case 6: /* KRB */ dev_done = dev_done & ~INT_TTI; /* clear flag */ int_req = int_req & ~INT_TTI; sim_activate_abs (&tti_unit, tti_unit.wait); /* check soon for more input */ return (tti_unit.buf); /* return buffer */ default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat tti_svc (UNIT *uptr) { int32 c; sim_clock_coschedule (uptr, tmxr_poll); /* continue poll */ if ((dev_done & INT_TTI) && /* prior character still pending and < 500ms? */ ((sim_os_msec () - tti_buftime) < 500)) return SCPE_OK; if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ return c; if (c & SCPE_BREAK) /* break? */ uptr->buf = 0; else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); tti_buftime = sim_os_msec (); uptr->pos = uptr->pos + 1; dev_done = dev_done | INT_TTI; /* set done */ int_req = INT_UPDATE; /* update interrupts */ return SCPE_OK; } /* Reset routine */ t_stat tti_reset (DEVICE *dptr) { tmxr_set_console_units (&tti_unit, &tto_unit); tti_unit.buf = 0; dev_done = dev_done & ~INT_TTI; /* clear done, int */ int_req = int_req & ~INT_TTI; int_enable = int_enable | INT_TTI; /* set enable */ if (!sim_is_running) /* RESET (not CAF)? */ sim_activate (&tti_unit, KBD_WAIT (tti_unit.wait, tmxr_poll)); return SCPE_OK; } /* Terminal output: IOT routine */ int32 tto (int32 IR, int32 AC) { switch (IR & 07) { /* decode IR<9:11> */ case 0: /* TLF */ dev_done = dev_done | INT_TTO; /* set flag */ int_req = INT_UPDATE; /* update interrupts */ return AC; case 1: /* TSF */ return (dev_done & INT_TTO)? IOT_SKP + AC: AC; case 2: /* TCF */ dev_done = dev_done & ~INT_TTO; /* clear flag */ int_req = int_req & ~INT_TTO; /* clear int req */ return AC; case 5: /* SPI */ return (int_req & (INT_TTI+INT_TTO))? IOT_SKP + AC: AC; case 6: /* TLS */ dev_done = dev_done & ~INT_TTO; /* clear flag */ int_req = int_req & ~INT_TTO; /* clear int req */ case 4: /* TPC */ sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ tto_unit.buf = AC; /* load buffer */ return AC; default: return (stop_inst << IOT_V_REASON) + AC; } /* end switch */ } /* Unit service */ t_stat tto_svc (UNIT *uptr) { int32 c; t_stat r; c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); if (c >= 0) { if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output char; error? */ sim_activate (uptr, uptr->wait); /* try again */ return ((r == SCPE_STALL)? SCPE_OK: r); /* if !stall, report */ } } dev_done = dev_done | INT_TTO; /* set done */ int_req = INT_UPDATE; /* update interrupts */ uptr->pos = uptr->pos + 1; return SCPE_OK; } /* Reset routine */ t_stat tto_reset (DEVICE *dptr) { tto_unit.buf = 0; dev_done = dev_done & ~INT_TTO; /* clear done, int */ int_req = int_req & ~INT_TTO; int_enable = int_enable | INT_TTO; /* set enable */ sim_cancel (&tto_unit); /* deactivate unit */ return SCPE_OK; } t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; return SCPE_OK; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | /* pidp8i.c: PiDP-8/I additions to the PDP-8 simulator Copyright © 2015-2017 by Oscar Vermeulen, Ian Schofield, and Warren Young Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of the authors above shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from those authors. */ #include "pidp8i.h" #include "../gpio-common.h" #include <dirent.h> // for USB stick searching //// EXPORTED GLOBALS ////////////////////////////////////////////////// // truly terrible even for me - break out of sim and start new script in scp.c int awfulHackFlag = 0; //// INTERNAL GLOBALS AND CONSTANTS //////////////////////////////////// // Single-shot flag for the STOP switch. It's global because several of // the functions below need to examine or modify it, since more than // just the STOP switch handler can put us into STOP mode. static int swStop = 0; //// set_pidp8i_leds /////////////////////////////////////////////////// // Given all of the PDP-8's internal registers that affect the front // panel display, modify the GPIO thread's LED state values accordingly. // // Also update the LED brightness values based on those new states. void set_pidp8i_leds (uint32 sPC, uint32 sMA, uint16 sMB, uint16 sIR, int32 sLAC, int32 sMQ, int32 sIF, int32 sDF, int32 sSC, int32 int_req, pidp8i_led_state_t eTT) { // First time thru, allocate temp working space so we can flash the // LED values over instantaneously, avoiding the need for cross- // thread locking to get a coherent display update. // // Allocated dynamically only because C doesn't let us say // temp_led_status[nledrows] on the stack, because nledrows // isn't const-enough for C. (It is for C++; grrr.) #define LS_BYTES (nledrows * sizeof(uint16)) static uint16* temp_led_status = 0; if (temp_led_status == 0) temp_led_status = malloc(LS_BYTES); // Groups 0-4, easy cases: single-register LED strings temp_led_status[0] = (uint32) sPC; temp_led_status[1] = (uint32) sMA; temp_led_status[2] = (uint32) sMB; temp_led_status[3] = (uint32) sLAC; temp_led_status[4] = (uint32) sMQ; // Group 5a: instruction type column, decoded from current instruction // in the IR register uint32 tempLeds = 0; switch ((sIR & 0xE00) >> 9) { case 0: tempLeds |= (1 << 11); break; // 000 AND case 1: tempLeds |= (1 << 10); break; // 001 TAD case 2: tempLeds |= (1 << 9); break; // 010 DCA case 3: tempLeds |= (1 << 8); break; // 011 ISZ case 4: tempLeds |= (1 << 7); break; // 100 JMS case 5: tempLeds |= (1 << 6); break; // 101 JMP case 6: tempLeds |= (1 << 5); break; // 110 IOT case 7: tempLeds |= (1 << 4); break; // 111-0 and 111-1 OPR group 1 & 2 } // Group 5b: first three LEDs at the top of the next column over if ((((sIR & 0xE00) >> 9) <= 5) && // is it a memory reference instruction? ((sIR & 0x100) != 0)) { // is it indirect addressing? tempLeds += (1 << 1); // then set Defer LED } if (eTT == pls_execute) tempLeds |= (1 << 2); // set Execute LED if (eTT == pls_fetch) tempLeds |= (1 << 3); // set Fetch LED // Group 5 is done: set LEDs all at once temp_led_status[5] = tempLeds; // Group 6: remaining LEDs in upper right block plus step count LEDs. // Some of these are handled in the main instruction decoding loop. tempLeds = 0; if (eTT == pls_pause) tempLeds |= (1 << 8); // set Pause LED if (int_req & INT_ION) tempLeds |= (1 << 9); // set ION LED if (swStop == 0) tempLeds |= (1 << 7); // set Run LED tempLeds |= ((uint32)(sSC) & 0x1f) << 2; // set Step Count LEDs temp_led_status[6] = tempLeds; // Group 7: DF, IF, and Link. // // DF and IF are shifted up 12 bits for the simulator's convenience so // they can simply be OR'd with PC to construct 15-bit addresses, so // shift these values down to their positions in the LED string. // // Then OR in the Link bit, which SIMH keeps in bit 12 (0-based) of // the 13-bit LAC register. tempLeds = (uint32) (sDF >> 3) | (sIF >> 6); tempLeds |= (uint32) ((sLAC & 010000) >> 7); temp_led_status[7] = tempLeds; // Set all the LED values instantaneously. (If the compiler here // doesn't implement this atomically, it's near-to; it may actually // be a single CPU instruction.) memcpy(ledstatus, temp_led_status, LS_BYTES); } //// mount_usb_stick_file ////////////////////////////////////////////// // Search for a PDP-8 media image in one of the Pi's USB auto-mount // directories and attempt to ATTACH it to the simulator. static void mount_usb_stick_file (int devNo, char *devCode) { char sFoundFile[CBUFSIZE] = { '\0' }; char sUSBPath[CBUFSIZE]; // will be "/media/usb0" etc char fileExtension[4]; // will be ".RX" etc int i, j; // Build expected file name extension from the first two characters of // the passed-in device code. fileExtension[0] = '.'; // extension starts with a . strncpy (fileExtension + 1, devCode, 2); // extension is PT, RX, RL etc fileExtension[2] = '\0'; // chop off device number // Forget the prior file attached to this PDP-8 device. The only reason // we keep track is so we don't have the same media image file attached // to both devices of a given type we support. That is, you can't have // a given floppy image file attached to both RX01 drives, but you *can* // repeatedly re-ATTACH the same floppy image to the first RX01 drive. static char mountedFiles[8][CBUFSIZE]; mountedFiles[devNo][0] = '\0'; for (i = 0; i < 8 && sFoundFile[0] == '\0'; ++i) { // search all 8 USB mount points, numbered 0-7 snprintf (sUSBPath, sizeof (sUSBPath), "/media/usb%d", i); DIR *pDir = opendir (sUSBPath); if (pDir) { struct dirent* pDirent; while ((pDirent = readdir (pDir)) != 0) { // search all files in directory if (strstr (pDirent->d_name, fileExtension)) { snprintf (sFoundFile, sizeof (sFoundFile), "%s/%s", sUSBPath, pDirent->d_name); for (j = 0; j < 7; ++j) { if (strncmp (mountedFiles[j], sFoundFile, CBUFSIZE) == 0) { break; // already mounted; skip next } } if (j == 7) { // Media image file is not already mounted, so leave while // loop with path set to mount it break; } else { // It's mounted, so forget its path, else we will stop // searching the other USB mount points sFoundFile[0] = '\0'; } } } closedir (pDir); } else { // Not a Pi or the USB auto-mounting software isn't installed printf ("\r\nCannot open %s: %s\r\n", sUSBPath, strerror (errno)); return; } } if (sFoundFile[0]) { // no file found, exit if (access (sFoundFile, R_OK) == 0) { if (attach_cmd ((int32) 0, sFoundFile) == SCPE_OK) { // issue ATTACH command // add file to mount list strncpy (mountedFiles[devNo], sFoundFile, CBUFSIZE); printf ("\r\nMounted %s %s\r\n", devCode, mountedFiles[devNo]); } else { printf ("\r\nSIMH error mounting %s\r\n", devCode); } } else { printf ("\r\nCannot read medium image %s from USB: %s\r\n", sFoundFile, strerror (errno)); } } else { printf ("\r\nNo unmounted %s file found\r\n", devCode); } } //// handle_sing_step ////////////////////////////////////////////////// // Handle SING_STEP combinations as nonstandard functions with respect // to a real PDP-8, since SIMH doesn't try to emulate the PDP-8's // single-stepping mode — not to be confused with single-instruction // mode, which SIMH *does* emulate — so the SING_STEP switch is free // for our nonstandard uses. // // This is separate from handle_flow_control_switches() only because // there are so many cases here that it would obscure the overall flow // of our calling function to do all this there. static pidp8i_flow_t handle_sing_step(int closed) { // If SING_STEP is open, we do nothing here except reset the single-shot // flag if it was set. static int single_shot = 0; if (!closed) { single_shot = 0; return pft_normal; } // There are two sets of SING_STEP combos: first up are those where the // other switches involved have to be set already, and the function is // triggered as soon as SING_STEP closes. These are functions we don't // want re-executing repeatedly while SING_STEP remains closed. if (single_shot == 0) { // SING_STEP switch was open last we knew, and now it's closed, so // set the single-shot flag. single_shot = 1; // 1. Convert DF switch values to a device number, which // we will map to a PDP-8 device type, then attempt to // ATTACH some unmounted medium from USB to that device // // We treat DF == 0 as nothing to mount, since we use // SING_STEP for other things, so we need a way to // decide which meaning of SING_STEP to take here. // // The shift by 9 is how many non-DF bits are below // DF in switchstatus[1] // // The bit complement is because closed DF switches show // as 0, because they're dragging the pull-up down, but // we want those treated as 1s, and vice versa. uint16_t css1 = ~switchstatus[1]; int swDevice = (css1 & SS1_DF_ALL) >> 9; if (swDevice) { char swDevCode[4] = { '\0' }; switch (swDevice) { case 1: strcpy(swDevCode, "ptr"); break; // PTR paper tape reader case 2: strcpy(swDevCode, "ptp"); break; // High speed paper tape punch case 3: strcpy(swDevCode, "dt0"); break; // TC08 DECtape (#8 is first!) case 4: strcpy(swDevCode, "dt1"); break; case 5: strcpy(swDevCode, "rx0"); break; // RX8E (8/e peripheral!) case 6: strcpy(swDevCode, "rx1"); break; case 7: strcpy(swDevCode, "rl0"); break; // RL8A } if (swDevCode[0]) mount_usb_stick_file(swDevice, swDevCode); } // 2. Do the same with IF, except that the switch value // is used to decide which boot script to restart with via // SIMH's DO command. // // The shift value of 6 is because the IF switches are 3 // down from the DF switches above. int swScript = (css1 & SS1_IF_ALL) >> 6; if (swScript) { // build filename from IF value char sScript[256]; snprintf(sScript, sizeof(sScript), "@BOOTDIR@/%d.script", swScript); printf("\r\n\nRebooting %s\r\n\r\n", sScript); awfulHackFlag = swScript; // this triggers a do command after leaving the simulator run. return pft_halt; } } // end if single-shot flag clear else { // Now handle the second set of SING_STEP special-function // combos, being those where the switches can be pressed in any // order, so that we take action when the last one of the set // closes, no matter which one that is. These immediately exit // the SIMH instruction interpreter, so they won't re-execute // merely because the human isn't fast enough to lift his finger // by the time the next iteration of that loop starts. // 3. Scan for host poweroff command (Sing_Step + Sing_Inst + Stop) if ((switchstatus[2] & (SS2_S_INST | SS2_STOP)) == 0) { printf("\r\nShutdown\r\n\r\n"); awfulHackFlag = 8; // this triggers an exit command after leaving the simulator run. if (spawn_cmd (0, "sudo /bin/systemctl poweroff") != SCPE_OK) { printf("\r\n\r\npoweroff failed\r\n\r\n"); } return pft_halt; } // 4. Scan for host reboot command (Sing_Step + Sing_Inst + Start) if ((switchstatus[2] & (SS2_S_INST | SS2_START)) == 0) { printf("\r\nReboot\r\n\r\n"); awfulHackFlag = 8; // this triggers an exit command after leaving the simulator run. if (spawn_cmd (0, "sudo /bin/systemctl reboot") != SCPE_OK) { printf("\r\n\r\nreboot failed\r\n\r\n"); } return pft_halt; } #if 0 // These combos once meant something, but no longer do. If you // reassign them, think carefully whether they should continue to // be handled here and not above in the "if" branch. If nothing // prevents your function from being re-executed while SING_STEP // remains closed and re-execution would be bad, move the test // under the aegis of the single_shot flag. // 5. Sing_Step + Sing_Inst + Load Add if ((switchstatus[2] & (SS2_S_INST | SS2_L_ADD)) == 0) { } // 6. Sing_Step + Sing_Inst + Deposit if ((switchstatus[2] & (SS2_S_INST | SS2_DEP)) == 0) { } #endif } return pft_normal; } //// handle_flow_control_switches ////////////////////////////////////// // Process all of the PiDP-8/I front panel switches that can affect the // flow path of the PDP-8 simulator's instruction interpretation loop, // returning a code telling the simulator our decision. // // The simulator passes in pointers to PDP-8 registers we may modify as // a side effect of handling these switches. pidp8i_flow_t handle_flow_control_switches(uint16* pM, uint32 *pPC, uint32 *pMA, int32 *pMB, int32 *pLAC, int32 *pIF, int32 *pDF, int32* pint_req) { // Exit early if the blink() thread has not attached itself to the GPIO // peripheral in the Pi, since that means we cannot safely interpret the // data in the switchstatus array. This is especially important on // non-Pi hosts, since switchstatus will remain zeroed, which we would // interpret as "all switches are pressed!", causing havoc. // // It would be cheaper for our caller to check this for us and skip the // call, but there's no good reason to expose such implementations // details to it. We're trying to keep the PDP-8 simulator's CPU core // as free of PiDP-8/I details as is practical. if (!pidp8i_gpio_present) return pft_normal; // Handle the nonstandard SING_STEP + X combos, some of which halt // the processor. if (handle_sing_step ((switchstatus[2] & SS2_S_STEP) == 0) == pft_halt) { return pft_halt; } // Check for SING_INST switch close... static int swSingInst = 0; if (((switchstatus[2] & SS2_S_INST) == 0) && (swSingInst == 0)) { // Put the processor in stop mode until we get a CONT or START // switch closure. Technically this is wrong according to DEC's // docs: we're supposed to finish executing the next instruction // before we "clear the RUN flip-flop" in DEC terms, whereas // we're testing these switches before we fetch the next // instruction. Show me how it matters, and I'll fix it. :) swSingInst = 1; swStop = 1; } // ...and SING_INST switch open if (swSingInst && (switchstatus[2] & SS2_S_INST)) { swSingInst = 0; } // Check for START switch press... static int swStart = 0; if (((switchstatus[2] & SS2_START) == 0) && (swStart == 0)) { *pint_req = *pint_req & ~INT_ION; // disable ION. says so in handbook, true? *pLAC = 0; // clear L and AC; *pMB = 0; // clear MB. *pMA = *pPC & 07777; // transfer PC into MA (FIXME: does IR make this unnecessary?) swStop = 0; // START cancels STOP mode swSingInst = 0; // allow SING INST mode re-entry swStart = 1; // make it single-shot } // ...and START switch release if (swStart && (switchstatus[2] & SS2_START)) { swStart = 0; } // Check for CONT switch press... static int swCont = 0; if (((switchstatus[2] & SS2_CONT) == 0) && (swCont < 2)) { if (swCont == 0) { // On the initial CONT press, release stop mode regardless // of how it was enabled to execute the next instruction. // // FIXME: Are we handling MB correctly? [973271ae36] swStop = 0; swCont = 1; // make it single-shot } else if (swCont == 1) { // The second time we come back in here while the CONT // switch is still down -- the human is too slow to // release it between iterations -- we stop paying attention // to it until the switch opens; check below. Re-enter stop // mode if SING_INST is still closed, else leave stop mode // because we must have stopped via STOP or HLT. swStop = !!swSingInst; swCont = 2; } } // ...and CONT switch release if (swCont && (switchstatus[2] & SS2_CONT)) { swCont = 0; } // Check for LOAD_ADD switch press. (No "and release" beacuse it's // harmless if this keeps happening until the slow human releases the // switch. This function is idempotent.) if ((switchstatus[2] & SS2_L_ADD) == 0) { // Copy SR into PC. We're XORing instead of masking and copying // because the switchstatus bits are opposite what we want here: we // get a 0 from the GPIO peripheral when the switch is closed, which // is the switch's "1" position. *pPC = switchstatus[0] ^ 07777; // Copy DF switch settings to DF register // // The shift is because the DF positions inside the switchstatus[1] // register happen to be 3 bit positions off of where we want them // in DF here: we want to be able to logically OR PC and DF to make // 15-bit data access addresses. // // We complement the bits here for the same reason we XOR'd the PC // value above. uint16_t css1 = ~switchstatus[1]; *pDF = (css1 & SS1_DF_ALL) << 3; // Do the same for IF. The only difference comes from the fact that // IF is the next 3 bits down in switchstatus[1]. *pIF = (css1 & SS1_IF_ALL) << 6; } // Check for DEP switch press... static int swDep = 0; if (((switchstatus[2] & SS2_DEP) == 0) && (swDep == 0)) { pM[*pPC] = switchstatus[0] ^ 07777; // XOR rationale above /* ??? in 66 handbook: strictly speaking, SR goes into AC, then AC into MB. Does it clear AC afterwards? If not, needs fix */ *pMB = pM[*pPC]; *pMA = *pPC & 07777; // MA trails PC on FP *pPC = (*pPC + 1) & 07777; // increment PC swDep = 1; // make it single-shot } // ...and DEP switch release if (swDep && (switchstatus[2] & SS2_DEP)) { swDep = 0; } // Check for EXAM switch press... static int swExam = 0; if (((switchstatus[2] & SS2_EXAM) == 0) && (swExam == 0)) { *pMB = pM[*pPC]; *pMA = *pPC & 07777; // MA trails PC on FP *pPC = (*pPC + 1) & 07777; // increment PC swExam = 1; // make it single-shot } // ...and EXAM switch release if (swExam && (switchstatus[2] & SS2_EXAM)) { swExam = 0; } // Check for STOP switch press. No "and release" because we get out of // STOP mode with START or CONT, not by releasing STOP, and while in // STOP mode, this switch's function is idempotent. if ((switchstatus[2] & SS2_STOP) == 0) { swStop = 1; } // If any of the above put us into STOP mode, go no further. In // particular, fetch no more instructions, and do not touch PC! if (swStop == 1) return pft_stop; return pft_normal; } //// get_switch_register /////////////////////////////////////////////// // Return the current contents of the switch register int32 get_switch_register(void) { return switchstatus[0] ^ 07777; } //// set_stop_mode ///////////////////////////////////////////////////// // Set the STOP mode flag. This is a wrapper around a module-global // variable that the CPU simulator core currently needs to set. void set_stop_mode(void) { swStop = 1; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | /* pidp8i.h: Interface between PiDP-8/I additions and the stock SIMH PDP-8 simulator Copyright © 2015-2017 by Oscar Vermeulen and Warren Young Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of the authors above shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from those authors. */ #if !defined(PIDP8I_H) #define PIDP8I_H #include "pdp8_defs.h" typedef enum { pft_normal, pft_halt, pft_stop, } pidp8i_flow_t; typedef enum { pls_fetch, pls_execute, pls_pause, } pidp8i_led_state_t; extern int awfulHackFlag; extern int32 get_switch_register(void); extern pidp8i_flow_t handle_flow_control_switches(uint16* pM, uint32 *pPC, uint32 *pMA, int32 *pMB, int32 *pLAC, int32 *pIF, int32 *pDF, int32* pint_req); extern void set_pidp8i_leds (uint32 sPC, uint32 sMA, uint16 sMB, uint16 sIR, int32 sLAC, int32 sMQ, int32 sIF, int32 sDF, int32 sSC, int32 int_req, pidp8i_led_state_t eTT); extern void set_stop_mode(void); #endif // !defined(PIDP8I_H) |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 | /* * gpio-common.c: functions common to both gpio.c and gpio-nls.c * * Copyright © 2015-2017 Oscar Vermeulen and Warren Young * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the names of the authors above shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from those * authors. * * www.obsolescenceguaranteed.blogspot.com * * The only communication with the main program (simh): * - external variable ledstatus is read to determine which leds to light. * - external variable switchstatus is updated with current switch settings. */ #include "gpio-common.h" #include "config.h" #include <pthread.h> #include <sys/file.h> #include <sys/time.h> #include <ctype.h> #include <errno.h> #include <stdlib.h> #include <string.h> #ifdef HAVE_TIME_H # include <time.h> #endif #define BLOCK_SIZE (4*1024) // Flag set after we successfully init the GPIO mechanism. While this // is false, the rest of the code knows not to expect useful values for // LED and switch states. It is also useful as a cross-thread signal, // since merely starting the blink() thread doesn't tell you whether it // managed to lock the GPIO device. uint8_t pidp8i_gpio_present; struct bcm2835_peripheral gpio; // needs initialisation // A constant meaning "indeterminate milliseconds", used for error // returns from ms_time() and for the case where the switch is in the // stable state in the switch_state array. static const ms_time_t na_ms = (ms_time_t)-1; // Adjust columns to scan based on whether the serial mod was done, as // that affects the free GPIOs for our use, and how the PCB connects // them to the LED matrix. #ifdef PCB_SERIAL_MOD uint8_t cols[] = {13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2}; #else uint8_t cols[] = {13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 15, 14}; #endif uint8_t ledrows[] = {20, 21, 22, 23, 24, 25, 26, 27}; uint8_t rows[] = {16, 17, 18}; // Array sizes. Must be declared twice because we need to export them, // and C doesn't have true const, as C++ does. #define NCOLS (sizeof(cols) / sizeof(cols[0])) #define NLEDROWS (sizeof(ledrows) / sizeof(ledrows[0])) #define NROWS (sizeof(rows) / sizeof(rows[0])) const size_t ncols = NCOLS; const size_t nledrows = NLEDROWS; const size_t nrows = NROWS; // The public switch and LED API: other threads poke values into // ledstatus to affect our GPIO LED pin driving loop and read values // from switchstatus to discover our current published value of the // switch states. The latter may differ from the *actual* switch // states due to the debouncing procedure. uint16_t switchstatus[NROWS]; // bitfield: sparse nrows x ncols switch matrix uint16_t ledstatus[NLEDROWS]; // bitfield: sparse nledrows x ncols LED matrix // Time-delayed reaction to switch changes to debounce the contacts. // This is especially important with the incandescent lamp simulation // feature enabled since that speeds up the GPIO scanning loop, making // it more susceptible to contact bounce. struct switch_state { // switch state currently reported via switchstatus[] int stable_state; // ms the switch state has been != stable_state, or na_ms // if it is currently in that same state ms_time_t last_change; }; static struct switch_state gss[NROWS][NCOLS]; int gss_initted = 0; static const ms_time_t debounce_ms = 50; // time switch state must remain stable // Name of GPIO memory-mapped device static const char* gpio_mem_dev = "/dev/gpiomem"; // Exposes the physical address defined in the passed structure int map_peripheral(struct bcm2835_peripheral *p) { // Open the GPIO device if ((p->mem_fd = open(gpio_mem_dev, O_RDWR|O_SYNC) ) < 0) { #ifdef DEBUG printf("Failed to open %s: %s\n", gpio_mem_dev, strerror(errno)); puts("Disabling PiDP-8/I front panel functionality."); #endif return -1; } // Attempt to lock it. If we can't, another program has it locked, // so we shouldn't keep running; it'll just end in tears. if (flock(p->mem_fd, LOCK_EX | LOCK_NB) < 0) { if (errno == EWOULDBLOCK) { printf("Failed to lock %s. Only one PiDP-8/I\n", gpio_mem_dev); puts("program can be running at a given time."); } else { printf("Failed to lock %s: %s\n", gpio_mem_dev, strerror(errno)); puts("Only one PiDP-8/I program can be running at a given time."); } return -1; } // Map the GPIO peripheral into our address space if ((p->map = mmap( NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, p->mem_fd, p->addr_p)) == MAP_FAILED) { perror("mmap"); return -1; } // Success! p->addr = (volatile unsigned int *)p->map; pidp8i_gpio_present = 1; return 0; } void unmap_peripheral(struct bcm2835_peripheral *p) { // Unwind the map_peripheral() steps in reverse order if (pidp8i_gpio_present) { if (p->mem_fd > 0) { if (p->map) munmap(p->map, BLOCK_SIZE); flock(p->mem_fd, LOCK_UN); close(p->mem_fd); } pidp8i_gpio_present = 0; } } unsigned bcm_host_get_peripheral_address(void) // find Pi's GPIO base address { unsigned address = ~0; FILE *fp = fopen("/proc/device-tree/soc/ranges", "rb"); if (fp) { unsigned char buf[4]; fseek(fp, 4, SEEK_SET); if (fread(buf, 1, sizeof buf, fp) == sizeof buf) address = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3] << 0; fclose(fp); } return address == ~0 ? 0x20000000 : address; } void sleep_ns(ns_time_t ns) { struct timespec ts = { 0, ns }; #if defined(HAVE_CLOCK_NANOSLEEP) clock_nanosleep(CLOCK_REALTIME, 0, &ts, NULL); #elif defined(HAVE_NANOSLEEP) nanosleep(&ts, NULL); #elif defined(HAVE_USLEEP) usleep(ns / 1000); #else # error Cannot build GPIO controller without high-res "sleep" function! #endif } // Like time(2) except that it returns milliseconds since the Unix epoch ms_time_t ms_time(ms_time_t* pt) { struct timeval tv; if (gettimeofday(&tv, 0) == 0) { ms_time_t t = (ms_time_t)(tv.tv_sec / 1000.0 + tv.tv_usec * 1000.0); if (pt) *pt = t; return t; } else { return na_ms; } } // Save given switch state ss into the exported switchstatus bitfield // so the simulator core will see it. (Constrast the gss matrix, // which holds our internal view of the unstable truth.) static void report_ss(int row, int col, int ss, struct switch_state* pss) { pss->stable_state = ss; pss->last_change = na_ms; int mask = 1 << col; if (ss) switchstatus[row] |= mask; else switchstatus[row] &= ~mask; #ifdef DEBUG printf("%cSS[%d][%02d] = %d ", gss_initted ? 'N' : 'I', row, col, ss); #endif } // Given the state of the switch at (row,col), work out if this requires // a change in our exported switch state. void debounce_switch(int row, int col, int ss, ms_time_t now_ms) { struct switch_state* pss = &gss[row][col]; if (!gss_initted) { // First time thru, so set this switch's module-global and // exported state to its defaults now that we know the switch's // initial state. report_ss(row, col, ss, pss); } else if (ss == pss->stable_state) { // This switch is still/again in the state we consider "stable", // which we are reporting in our switchstatus bitfield. Reset // the debounce timer in case it is returning to its stable // state from a brief blip into the other state. pss->last_change = na_ms; } else if (pss->last_change == na_ms) { // This switch just left what we consider the "stable" state, so // start the debounce timer. pss->last_change = now_ms; } else if ((now_ms - pss->last_change) > debounce_ms) { // Switch has been in the new state long enough for the contacts // to have stopped bouncing: report its state change to outsiders. report_ss(row, col, ss, pss); } // else, switch was in the new state both this time and the one prior, // but it hasn't been there long enough to report it } // Write a configuration string tag. static const char* pi_type(int p) { if (p == 0x20200000) { return "pi1+"; } else { FILE* fp = fopen("/proc/device-tree/model", "r"); if (fp) { static char ac[60]; if (fgets(ac, sizeof(ac), fp) && (strlen(ac) > 20)) { if (strstr(ac, "Raspberry Pi ") == ac) { int series = atoi(ac + 13); char model = 'x'; char* pm = strstr(ac, " Model "); if (pm) model = tolower(pm[7]); snprintf(ac, sizeof(ac), "pi%d%c", series, model); return ac; } } return "pi2+"; } } return 0; // not a Pi } // The GPIO thread entry point: initializes GPIO and then calls // the blink_core() implementation linked to this program. void *blink(void *terminate) { // Find GPIO address (it varies by Pi model) gpio.addr_p = bcm_host_get_peripheral_address() + 0x200000; // Set thread to real time priority struct sched_param sp; sp.sched_priority = 4; // not high, just above the minimum of 1 int rt = pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp) == 0; // Map the GPIO peripheral, but hold off exiting if it fails, until // we report its absence in the config line. int mapped = map_peripheral(&gpio) == 0; // Tell the user about our configuration, succinctly const char* pt = pi_type(mapped ? gpio.addr_p : 0); printf( "PiDP-8/I @VERSION@ [%s] [@LED_DRIVER_MODULE@ls] [%spcb]%s" #ifdef DEBUG " [debug]" #endif "%s", pt ? pt : "cake", // pt == 0 == not a Pi pt ? #ifdef PCB_SERIAL_MOD "ser" : #else "std" : #endif "no", mapped ? " [gpio]" : "", rt ? " [rt]" : "" ); // Hand off control to the blink_core() variant linked to this // program: either the new incandescent lamp simulator or the old // stock version. if (mapped) { // initialise GPIO (all pins used as inputs, with pull-ups enabled on cols) // INSERT CODE HERE TO SET GPIO 14 AND 15 TO I/O INSTEAD OF ALT 0. // AT THE MOMENT, USE "sudo ./gpio mode 14 in" and "sudo ./gpio mode 15 in". "sudo ./gpio readall" to verify. #define pgpio (&gpio) int i; for (i = 0; i <nledrows; i++) { // Define ledrows as input INP_GPIO(ledrows[i]); GPIO_CLR = 1 << ledrows[i]; // so go to Low when switched to output } for (i = 0; i < ncols; i++) { // Define cols as input INP_GPIO(cols[i]); } for (i = 0; i < nrows; i++) { // Define rows as input INP_GPIO(rows[i]); } // BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs GPIO_PULL = 2; // pull-up usleep(1); // must wait 150 cycles #ifdef PCB_SERIAL_MOD GPIO_PULLCLK0 = 0x03ffc; // selects GPIO pins 2..13 (frees up serial port on 14 & 15) #else GPIO_PULLCLK0 = 0x0fff0; // selects GPIO pins 4..15 (assumes we avoid pins 2 and 3!) #endif usleep(1); GPIO_PULL = 0; // reset GPPUD register usleep(1); GPIO_PULLCLK0 = 0; // remove clock usleep(1); // probably unnecessary // BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs GPIO_PULL = 1; // pull-down to avoid ghosting (dec2015) usleep(1); // must wait 150 cycles GPIO_PULLCLK0 = 0x0ff00000; // selects GPIO pins 20..27 usleep(1); GPIO_PULL = 0; // reset GPPUD register usleep(1); GPIO_PULLCLK0 = 0; // remove clock usleep(1); // probably unnecessary // BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs GPIO_PULL = 0; // no pull-up no pull down just float usleep(1); // must wait 150 cycles GPIO_PULLCLK0 = 0x070000; // selects GPIO pins 16..18 usleep(1); GPIO_PULL = 0; // reset GPPUD register usleep(1); GPIO_PULLCLK0 = 0; // remove clock usleep(1); // probably unnecessary extern void blink_core(struct bcm2835_peripheral*, int* terminate); blink_core(&gpio, (int*)terminate); } // blink_core() leaves all cols, rows, ledrows are set to input, and // it's safe to leave them in that state. No need to de-init GPIO. gss_initted = 0; return mapped ? 0 : (void*)-1; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | /* * gpio-common.h: public interface for the PiDP-8/I's GPIO module * * Copyright © 2015-2017 Oscar Vermeulen and Warren Young * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the names of the authors above shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from those * authors. */ #if !defined(PIDP8I_GPIO_H) #define PIDP8I_GPIO_H #include <unistd.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdint.h> // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) #define INP_GPIO(g) *(pgpio->addr + ((g)/10)) &= ~(7<<(((g)%10)*3)) #define OUT_GPIO(g) *(pgpio->addr + ((g)/10)) |= (1<<(((g)%10)*3)) #define SET_GPIO_ALT(g,a) *(pgpio->addr + (((g)/10))) |= (((a)<=3?(a) + 4:(a)==4?3:2)<<(((g)%10)*3)) #define GPIO_SET *(pgpio->addr + 7) // sets bits which are 1 ignores bits which are 0 #define GPIO_CLR *(pgpio->addr + 10) // clears bits which are 1 ignores bits which are 0 #define GPIO_READ(g) *(pgpio->addr + 13) &= (1<<(g)) #define GPIO_PULL *(pgpio->addr + 37) // pull up/pull down #define GPIO_PULLCLK0 *(pgpio->addr + 38) // pull up/pull down clock // Switch masks, SSn, used against switchstatus[n] #define SS0_SR_B11 04000 #define SS0_SR_B10 02000 #define SS0_SR_B09 01000 #define SS0_SR_B08 00400 #define SS0_SR_B07 00200 #define SS0_SR_B06 00100 #define SS0_SR_B05 00040 #define SS0_SR_B04 00020 #define SS0_SR_B03 00010 #define SS0_SR_B02 00004 #define SS0_SR_B01 00002 #define SS0_SR_B00 00001 #define SS1_DF_B2 04000 #define SS1_DF_B1 02000 #define SS1_DF_B0 01000 #define SS1_DF_ALL (SS1_DF_B2 | SS1_DF_B1 | SS1_DF_B0) #define SS1_IF_B2 00400 #define SS1_IF_B1 00200 #define SS1_IF_B0 00100 #define SS1_IF_ALL (SS1_IF_B2 | SS1_IF_B1 | SS1_IF_B0) #define SS2_START 04000 #define SS2_L_ADD 02000 #define SS2_DEP 01000 #define SS2_EXAM 00400 #define SS2_CONT 00200 #define SS2_STOP 00100 #define SS2_S_STEP 00040 #define SS2_S_INST 00020 // Info for accessing the GPIO peripheral on the SoC struct bcm2835_peripheral { uint32_t addr_p; int mem_fd; void *map; volatile unsigned int *addr; }; typedef uint64_t ns_time_t; typedef useconds_t us_time_t; typedef uint32_t ms_time_t; extern uint16_t switchstatus[]; extern uint16_t ledstatus[]; extern uint8_t cols[]; extern uint8_t ledrows[]; extern uint8_t rows[]; extern const size_t ncols, nledrows, nrows; extern uint8_t pidp8i_gpio_present; extern int gss_initted; extern void *blink(void *ptr); // thread entry point to the gpio module extern unsigned bcm_host_get_peripheral_address(void); extern void debounce_switch(int row, int col, int ss, ms_time_t now_ms); extern int map_peripheral(struct bcm2835_peripheral *p); extern ms_time_t ms_time(ms_time_t* pt); extern void sleep_ns(ns_time_t ns); #define sleep_us(us) usleep(us) #define sleep_ms(ms) usleep(ms * 1000) void unmap_peripheral(struct bcm2835_peripheral *p); #endif // !defined(PIDP8I_GPIO_H) |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | /* * gpio-ils.c: implements blink_core() for Ian Schofield's incandescent * lamp simulator * * Copyright © 2015-2017 Oscar Vermeulen, Ian Schofield, and Warren Young * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the names of the authors above shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from those * authors. * * www.obsolescenceguaranteed.blogspot.com */ #include "gpio-common.h" #include "sim_defs.h" //// CONSTANTS ///////////////////////////////////////////////////////// // Brightness range is [0, MAX_BRIGHTNESS) truncated. #define MAX_BRIGHTNESS 32 // On each iteration, we add or subtract a proportion of the current LED // value back to it as its new brightness, based on the LED's current // internal state. This gives a nonlinear increase/decrease behavior, // where rising from "off" or dropping from "full-on" is fast to start // and slows down as it approaches its destination. // // We use an asymmetric function depending on whether the LED is turning // on or off to better mimic the behavior of an incandescent lamp, which // reaches full brightness faster than it turns fully off. #define RISING_FACTOR 0.02 #define FALLING_FACTOR 0.008 //// blink_core //////////////////////////////////////////////////////// // The GPIO module's main loop core, called from thread entry point in // gpio-common.c. void blink_core(struct bcm2835_peripheral* pgpio, int* terminate) { int i, j, k; const us_time_t intervl = 5; // light each row of leds 5 µs ms_time_t now_ms; float brtval[96]; uint8 brctr[96], bctr = 0, ndx; memset(brtval, 0, sizeof (brtval)); while (*terminate == 0) { // prepare for lighting LEDs by setting col pins to output for (i = 0; i < ncols; i++) { INP_GPIO(cols[i]); OUT_GPIO(cols[i]); // Define cols as output } if (bctr == 0) { memset(brctr, 0, sizeof (brctr)); bctr = MAX_BRIGHTNESS; } // Increase or decrease each LED's brightness based on whether // it is currently enabled. These values affect the duty cycle // of the signal put out by the GPIO thread to each LED, thus // controlling brightness. float *p = brtval; for (int row = 0; row < nledrows; ++row) { for (int col = 0, msk = 1; col < ncols; ++col, ++p, msk <<= 1) { if (ledstatus[row] & msk) *p += (MAX_BRIGHTNESS - *p) * RISING_FACTOR; else *p -= *p * FALLING_FACTOR; } } // light up LEDs for (i = ndx = 0; i < nledrows; i++) { // Toggle columns for this ledrow (which LEDs should be on (CLR = on)) for (k = 0; k < ncols; k++, ndx++) { if (++brctr[ndx] < brtval[ndx]) GPIO_CLR = 1 << cols[k]; else GPIO_SET = 1 << cols[k]; } // Toggle this ledrow on INP_GPIO(ledrows[i]); GPIO_SET = 1 << ledrows[i]; // test for flash problem OUT_GPIO(ledrows[i]); sleep_us(intervl); // Toggle ledrow off GPIO_CLR = 1 << ledrows[i]; // superstition INP_GPIO(ledrows[i]); sleep_us(intervl); } // prepare for reading switches ms_time(&now_ms); for (i = 0; i < ncols; i++) { INP_GPIO(cols[i]); // flip columns to input. Need internal pull-ups enabled. } // read three rows of switches for (i = 0; i < nrows; i++) { INP_GPIO(rows[i]); OUT_GPIO(rows[i]); // turn on one switch row GPIO_CLR = 1 << rows[i]; // and output 0V to overrule built-in pull-up from column input pin sleep_ns(intervl * 1000 / 100); for (j = 0; j < ncols; j++) { // ncols switches in each row int ss = GPIO_READ(cols[j]); debounce_switch(i, j, !!ss, now_ms); } INP_GPIO(rows[i]); // stop sinking current from this row of switches } fflush(stdout); gss_initted = 1; bctr--; #if defined(HAVE_SCHED_YIELD) sched_yield(); #endif } } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | /* * gpio-nls.c: implements blink_core() with the original simple LED driver * * This file differs from gpio.c in that it does not include the * incandescent lamp simulator feature by Ian Schofield. It is * more directly descended from the original gpio.c by Oscar Vermeulen. * * Copyright © 2015-2017 Oscar Vermeulen and Warren Young * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the names of the authors above shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from those * authors. * * www.obsolescenceguaranteed.blogspot.com */ #include "gpio-common.h" //// blink_core //////////////////////////////////////////////////////// // The GPIO module's main loop core, called from thread entry point in // gpio-common.c. void blink_core(struct bcm2835_peripheral* pgpio, int* terminate) { int i, j, k; const us_time_t intervl = 300; // light each row of leds 300 µs ms_time_t now_ms; while (*terminate == 0) { // prepare for lighting LEDs by setting col pins to output for (i = 0; i < ncols; i++) { INP_GPIO(cols[i]); OUT_GPIO(cols[i]); // Define cols as output } // light up LEDs for (i = 0; i < nledrows; i++) { // Toggle columns for this ledrow (which LEDs should be on (CLR = on)) for (k = 0; k <ncols; k++) { if ((ledstatus[i] & (1 << k)) == 0) GPIO_SET = 1 << cols[k]; else GPIO_CLR = 1 << cols[k]; } // Toggle this ledrow on INP_GPIO(ledrows[i]); GPIO_SET = 1 << ledrows[i]; // test for flash problem OUT_GPIO(ledrows[i]); sleep_us(intervl); // Toggle ledrow off GPIO_CLR = 1 << ledrows[i]; // superstition INP_GPIO(ledrows[i]); sleep_ns(10000); // waste of cpu cycles but may help against udn2981 ghosting, not flashes though } // prepare for reading switches ms_time(&now_ms); for (i = 0; i < ncols; i++) INP_GPIO(cols[i]); // flip columns to input. Need internal pull-ups enabled. // read three rows of switches for (i = 0; i < nrows; i++) { INP_GPIO(rows[i]); OUT_GPIO(rows[i]); // turn on one switch row GPIO_CLR = 1 << rows[i]; // and output 0V to overrule built-in pull-up from column input pin sleep_us(intervl / 100); for (j = 0; j < ncols; j++) { // ncols switches in each row int ss = GPIO_READ(cols[j]); debounce_switch(i, j, !!ss, now_ms); } INP_GPIO(rows[i]); // stop sinking current from this row of switches } fflush(stdout); gss_initted = 1; #if defined(HAVE_SCHED_YIELD) sched_yield(); #endif } } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | /* * Scan switches for PiDP-8/I front panel * * www.obsolescenceguaranteed.blogspot.com * * Copyright (c) 2015-2016 Oscar Vermeulen * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the names of the authors above shall * not be used in advertising or otherwise to promote the sale, use or other * dealings in this Software without prior written authorization from those * authors. * */ #include "gpio-common.h" #define short_wait() sleep_ms(100) #define pgpio (&gpio) int main() { int i,j,k,switchscan[2], tmp; struct bcm2835_peripheral gpio; // ------------ Find gpio address (different for Pi 2) ------------- gpio.addr_p = bcm_host_get_peripheral_address() + 0x200000; if (gpio.addr_p== 0x20200000) printf("scanswitch - RPi Plus\n"); else printf("scanswitch - RPi 2\n"); if(map_peripheral(&gpio) == -1) { printf("Failed to map the physical GPIO registers into the virtual memory space.\n"); return -1; } // initialise GPIO (all pins used as inputs, with pull-ups enabled on cols) for (i=0;i<nledrows;i++) // Define ledrows as input INP_GPIO(ledrows[i]); for (i=0;i<ncols;i++) // Define cols as input INP_GPIO(cols[i]); for (i=0;i<nrows;i++) // Define rows as input INP_GPIO(rows[i]); // BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs GPIO_PULL = 2; // pull-up short_wait(); // must wait 150 cycles GPIO_PULLCLK0 = 0x0fff0; // selects GPIO pins 4..15 (assumes we avoid pins 2 and 3!) short_wait(); GPIO_PULL = 0; // reset GPPUD register short_wait(); GPIO_PULLCLK0 = 0; // remove clock short_wait(); // probably unnecessary // BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs GPIO_PULL = 0; // no pull-up no pull-down just float short_wait(); // must wait 150 cycles GPIO_PULLCLK0 = 0x0ff00000; // selects GPIO pins 20..27 short_wait(); GPIO_PULL = 0; // reset GPPUD register short_wait(); GPIO_PULLCLK0 = 0; // remove clock short_wait(); // probably unnecessary // BCM2835 ARM Peripherals PDF p 101 & elinux.org/RPi_Low-level_peripherals#Internal_Pull-Ups_.26_Pull-Downs GPIO_PULL = 0; // no pull-up no pull down just float short_wait(); // must wait 150 cycles GPIO_PULLCLK0 = 0x070000; // selects GPIO pins 16..18 short_wait(); GPIO_PULL = 0; // reset GPPUD register short_wait(); GPIO_PULLCLK0 = 0; // remove clock short_wait(); // probably unnecessary // -------------------------------------------------- // prepare for reading switches for (uint8_t row=1;row<=2;row++) // do rows 2 (for IF switches) and 3 (for STOP switch) { INP_GPIO(rows[row]); OUT_GPIO(rows[row]); // turn on one switch row GPIO_CLR = 1 << rows[row]; // and output 0V to overrule built-in pull-up from column input pin sleep_us(10); // unnecessarily long? switchscan[row-1]=0; for (j=0;j<ncols;j++) // 12 switches in each row { tmp = GPIO_READ(cols[j]); if (tmp==0) switchscan[row-1] += 1<<j; } INP_GPIO(rows[row]); // stop sinking current from this row of switches } unmap_peripheral(&gpio); if ( ((switchscan[1] >> 6) & 1) == 1 ) // STOP switch enabled, return 8; // 8: STOP enabled, no bootscript else return (switchscan[0] >> 6) & 07; // 0-7: x.script to be used in PiDP-8/I } |
more than 10,000 changes
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | /* scp.h: simulator control program headers Copyright (c) 1993-2009, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 05-Dec-10 MP Added macro invocation of sim_debug 09-Aug-06 JDB Added assign_device and deassign_device 14-Jul-06 RMS Added sim_activate_abs 06-Jan-06 RMS Added fprint_stopped_gen Changed arg type in sim_brk_test 07-Feb-05 RMS Added ASSERT command 09-Sep-04 RMS Added reset_all_p 14-Feb-04 RMS Added debug prototypes (from Dave Hittner) 02-Jan-04 RMS Split out from SCP */ #ifndef SIM_SCP_H_ #define SIM_SCP_H_ 0 #ifdef __cplusplus extern "C" { #endif /* run_cmd parameters */ #define RU_RUN 0 /* run */ #define RU_GO 1 /* go */ #define RU_STEP 2 /* step */ #define RU_NEXT 3 /* step or step/over */ #define RU_CONT 4 /* continue */ #define RU_BOOT 5 /* boot */ /* exdep_cmd parameters */ #define EX_D 0 /* deposit */ #define EX_E 1 /* examine */ #define EX_I 2 /* interactive */ /* brk_cmd parameters */ #define SSH_ST 0 /* set */ #define SSH_SH 1 /* show */ #define SSH_CL 2 /* clear */ /* get_sim_opt parameters */ #define CMD_OPT_SW 001 /* switches */ #define CMD_OPT_OF 002 /* output file */ #define CMD_OPT_SCH 004 /* search */ #define CMD_OPT_DFT 010 /* defaults */ /* Command processors */ t_stat reset_cmd (int32 flag, CONST char *ptr); t_stat exdep_cmd (int32 flag, CONST char *ptr); t_stat eval_cmd (int32 flag, CONST char *ptr); t_stat load_cmd (int32 flag, CONST char *ptr); t_stat run_cmd (int32 flag, CONST char *ptr); void run_cmd_message (const char *unechod_cmdline, t_stat r); t_stat attach_cmd (int32 flag, CONST char *ptr); t_stat detach_cmd (int32 flag, CONST char *ptr); t_stat assign_cmd (int32 flag, CONST char *ptr); t_stat deassign_cmd (int32 flag, CONST char *ptr); t_stat save_cmd (int32 flag, CONST char *ptr); t_stat restore_cmd (int32 flag, CONST char *ptr); t_stat exit_cmd (int32 flag, CONST char *ptr); t_stat set_cmd (int32 flag, CONST char *ptr); t_stat show_cmd (int32 flag, CONST char *ptr); t_stat set_default_cmd (int32 flg, CONST char *cptr); t_stat pwd_cmd (int32 flg, CONST char *cptr); t_stat dir_cmd (int32 flg, CONST char *cptr); t_stat type_cmd (int32 flg, CONST char *cptr); t_stat brk_cmd (int32 flag, CONST char *ptr); t_stat do_cmd (int32 flag, CONST char *ptr); t_stat goto_cmd (int32 flag, CONST char *ptr); t_stat return_cmd (int32 flag, CONST char *ptr); t_stat shift_cmd (int32 flag, CONST char *ptr); t_stat call_cmd (int32 flag, CONST char *ptr); t_stat on_cmd (int32 flag, CONST char *ptr); t_stat noop_cmd (int32 flag, CONST char *ptr); t_stat assert_cmd (int32 flag, CONST char *ptr); t_stat send_cmd (int32 flag, CONST char *ptr); t_stat expect_cmd (int32 flag, CONST char *ptr); t_stat help_cmd (int32 flag, CONST char *ptr); t_stat screenshot_cmd (int32 flag, CONST char *ptr); t_stat spawn_cmd (int32 flag, CONST char *ptr); t_stat echo_cmd (int32 flag, CONST char *ptr); /* Allow compiler to help validate printf style format arguments */ #if !defined __GNUC__ #define GCC_FMT_ATTR(n, m) #endif #if !defined(GCC_FMT_ATTR) #define GCC_FMT_ATTR(n, m) __attribute__ ((format (__printf__, n, m))) #endif /* Utility routines */ t_stat sim_process_event (void); t_stat sim_activate (UNIT *uptr, int32 interval); t_stat _sim_activate (UNIT *uptr, int32 interval); t_stat sim_activate_abs (UNIT *uptr, int32 interval); t_stat sim_activate_notbefore (UNIT *uptr, int32 rtime); t_stat sim_activate_after (UNIT *uptr, uint32 usecs_walltime); t_stat sim_activate_after_d (UNIT *uptr, double usecs_walltime); t_stat _sim_activate_after (UNIT *uptr, double usecs_walltime); t_stat sim_activate_after_abs (UNIT *uptr, uint32 usecs_walltime); t_stat sim_activate_after_abs_d (UNIT *uptr, double usecs_walltime); t_stat _sim_activate_after_abs (UNIT *uptr, double usecs_walltime); t_stat sim_cancel (UNIT *uptr); t_bool sim_is_active (UNIT *uptr); int32 sim_activate_time (UNIT *uptr); int32 _sim_activate_time (UNIT *uptr); double sim_activate_time_usecs (UNIT *uptr); t_stat sim_run_boot_prep (int32 flag); double sim_gtime (void); uint32 sim_grtime (void); int32 sim_qcount (void); t_stat attach_unit (UNIT *uptr, CONST char *cptr); t_stat detach_unit (UNIT *uptr); t_stat assign_device (DEVICE *dptr, const char *cptr); t_stat deassign_device (DEVICE *dptr); t_stat reset_all (uint32 start_device); t_stat reset_all_p (uint32 start_device); const char *sim_dname (DEVICE *dptr); const char *sim_uname (UNIT *dptr); t_stat get_yn (const char *ques, t_stat deflt); int sim_isspace (char c); int sim_islower (char c); int sim_isalpha (char c); int sim_isprint (char c); int sim_isdigit (char c); int sim_isgraph (char c); int sim_isalnum (char c); int sim_strncasecmp (const char *string1, const char *string2, size_t len); int sim_strcasecmp (const char *string1, const char *string2); CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st); const char *put_switches (char *buf, size_t bufsize, uint32 sw); CONST char *get_glyph (const char *iptr, char *optr, char mchar); CONST char *get_glyph_nc (const char *iptr, char *optr, char mchar); CONST char *get_glyph_quoted (const char *iptr, char *optr, char mchar); CONST char *get_glyph_cmd (const char *iptr, char *optr); t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status); CONST char *get_range (DEVICE *dptr, CONST char *cptr, t_addr *lo, t_addr *hi, uint32 rdx, t_addr max, char term); t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize); char *sim_encode_quoted_string (const uint8 *iptr, uint32 size); void fprint_buffer_string (FILE *st, const uint8 *buf, uint32 size); t_value strtotv (CONST char *cptr, CONST char **endptr, uint32 radix); int Fprintf (FILE *f, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /* Use scp.c provided fprintf function */ #define fprintf Fprintf #define fputs(_s,_f) Fprintf(_f,"%s",_s) #define fputc(_c,_f) Fprintf(_f,"%c",_c) t_stat sim_set_memory_load_file (const unsigned char *data, size_t size); int Fgetc (FILE *f); t_stat fprint_val (FILE *stream, t_value val, uint32 rdx, uint32 wid, uint32 fmt); t_stat sprint_val (char *buf, t_value val, uint32 rdx, uint32 wid, uint32 fmt); t_stat sim_print_val (t_value val, uint32 radix, uint32 width, uint32 format); const char *sim_fmt_secs (double seconds); const char *sim_fmt_numeric (double number); const char *sprint_capac (DEVICE *dptr, UNIT *uptr); char *read_line (char *cptr, int32 size, FILE *stream); void fprint_reg_help (FILE *st, DEVICE *dptr); void fprint_set_help (FILE *st, DEVICE *dptr); void fprint_show_help (FILE *st, DEVICE *dptr); CTAB *find_cmd (const char *gbuf); DEVICE *find_dev (const char *ptr); DEVICE *find_unit (const char *ptr, UNIT **uptr); DEVICE *find_dev_from_unit (UNIT *uptr); t_stat sim_register_internal_device (DEVICE *dptr); void sim_sub_args (char *in_str, size_t in_str_size, char *do_arg[]); REG *find_reg (CONST char *ptr, CONST char **optr, DEVICE *dptr); CTAB *find_ctab (CTAB *tab, const char *gbuf); C1TAB *find_c1tab (C1TAB *tab, const char *gbuf); SHTAB *find_shtab (SHTAB *tab, const char *gbuf); t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr); BRKTAB *sim_brk_fnd (t_addr loc); uint32 sim_brk_test (t_addr bloc, uint32 btyp); void sim_brk_clrspc (uint32 spc, uint32 btyp); void sim_brk_npc (uint32 cnt); void sim_brk_setact (const char *action); const char *sim_brk_message(void); t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 after, uint32 delay); t_stat sim_show_send_input (FILE *st, const SEND *snd); t_bool sim_send_poll_data (SEND *snd, t_stat *stat); t_stat sim_send_clear (SEND *snd); t_stat sim_set_expect (EXPECT *exp, CONST char *cptr); t_stat sim_set_noexpect (EXPECT *exp, const char *cptr); t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, uint32 after, int32 switches, const char *act); t_stat sim_exp_clr (EXPECT *exp, const char *match); t_stat sim_exp_clrall (EXPECT *exp); t_stat sim_exp_show (FILE *st, CONST EXPECT *exp, const char *match); t_stat sim_exp_showall (FILE *st, const EXPECT *exp); t_stat sim_exp_check (EXPECT *exp, uint8 data); CONST char *match_ext (CONST char *fnam, const char *ext); t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); const char *sim_error_text (t_stat stat); t_stat sim_string_to_stat (const char *cptr, t_stat *cond); t_stat sim_cancel_step (void); void sim_printf (const char *fmt, ...) GCC_FMT_ATTR(1, 2); void sim_perror (const char *msg); t_stat sim_messagef (t_stat stat, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void sim_data_trace(DEVICE *dptr, UNIT *uptr, const uint8 *data, const char *position, size_t len, const char *txt, uint32 reason); void sim_debug_bits_hdr (uint32 dbits, DEVICE* dptr, const char *header, BITFIELD* bitdefs, uint32 before, uint32 after, int terminate); void sim_debug_bits (uint32 dbits, DEVICE* dptr, BITFIELD* bitdefs, uint32 before, uint32 after, int terminate); #if defined (__DECC) && defined (__VMS) && (defined (__VAX) || (__DECC_VER < 60590001)) #define CANT_USE_MACRO_VA_ARGS 1 #endif #if defined(__cplusplus) #ifdef CANT_USE_MACRO_VA_ARGS #define _sim_debug sim_debug void sim_debug (uint32 dbits, void* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4); #else void _sim_debug (uint32 dbits, void* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4); #define sim_debug(dbits, dptr, ...) do { if (sim_deb && dptr && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__);} while (0) #endif #else #ifdef CANT_USE_MACRO_VA_ARGS #define _sim_debug sim_debug void sim_debug (uint32 dbits, DEVICE* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4); #else void _sim_debug (uint32 dbits, DEVICE* dptr, const char *fmt, ...) GCC_FMT_ATTR(3, 4); #define sim_debug(dbits, dptr, ...) do { if (sim_deb && dptr && ((dptr)->dctrl & dbits)) _sim_debug (dbits, dptr, __VA_ARGS__);} while (0) #endif #endif void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr); #define SCP_HELP_FLAT (1u << 31) /* Force flat help when prompting is not possible */ #define SCP_HELP_ONECMD (1u << 30) /* Display one topic, do not prompt */ #define SCP_HELP_ATTACH (1u << 29) /* Top level topic is ATTACH help */ t_stat scp_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *help, const char *cptr, ...); t_stat scp_vhelp (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *help, const char *cptr, va_list ap); t_stat scp_helpFromFile (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *help, const char *cptr, ...); t_stat scp_vhelpFromFile (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *help, const char *cptr, va_list ap); /* Global data */ extern DEVICE *sim_dflt_dev; extern int32 sim_interval; extern int32 sim_switches; extern int32 sim_quiet; extern int32 sim_step; extern t_stat sim_last_cmd_stat; /* Command Status */ extern FILE *sim_log; /* log file */ extern FILEREF *sim_log_ref; /* log file file reference */ extern FILE *sim_deb; /* debug file */ extern FILEREF *sim_deb_ref; /* debug file file reference */ extern int32 sim_deb_switches; /* debug display flags */ extern struct timespec sim_deb_basetime; /* debug base time for relative time output */ extern DEVICE **sim_internal_devices; extern uint32 sim_internal_device_count; extern UNIT *sim_clock_queue; extern int32 sim_is_running; extern t_bool sim_processing_event; /* Called from sim_process_event */ extern char *sim_prompt; /* prompt string */ extern const char *sim_savename; /* Simulator Name used in Save/Restore files */ extern t_value *sim_eval; extern volatile int32 stop_cpu; extern uint32 sim_brk_types; /* breakpoint info */ extern uint32 sim_brk_dflt; extern uint32 sim_brk_summ; extern uint32 sim_brk_match_type; extern t_addr sim_brk_match_addr; extern BRKTYPTAB *sim_brk_type_desc; /* type descriptions */ extern FILE *stdnul; extern t_bool sim_asynch_enabled; #if defined(SIM_ASYNCH_IO) int sim_aio_update_queue (void); void sim_aio_activate (ACTIVATE_API caller, UNIT *uptr, int32 event_time); #endif /* VM interface */ extern char sim_name[]; extern DEVICE *sim_devices[]; extern REG *sim_PC; extern const char *sim_stop_messages[]; extern t_stat sim_instr (void); extern t_stat sim_load (FILE *ptr, CONST char *cptr, CONST char *fnam, int flag); extern int32 sim_emax; extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); extern t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw); /* The per-simulator init routine is a weak global that defaults to NULL The other per-simulator pointers can be overrriden by the init routine */ WEAK extern void (*sim_vm_init) (void); extern char *(*sim_vm_read) (char *ptr, int32 size, FILE *stream); extern void (*sim_vm_post) (t_bool from_scp); extern CTAB *sim_vm_cmd; extern void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr); extern void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr); extern t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr); extern t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason); extern t_value (*sim_vm_pc_value) (void); extern t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs); #ifdef __cplusplus } #endif #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 | /* sim_console.c: simulator console I/O library Copyright (c) 1993-2014, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 02-Jan-14 RMS Added tab stop routines 18-Mar-12 RMS Removed unused reference to sim_switches (Dave Bryan) 07-Dec-11 MP Added sim_ttisatty to support reasonable behaviour (i.e. avoid in infinite loop) in the main command input loop when EOF is detected and input is coming from a file (or a null device: /dev/null or NUL:) This may happen when a simulator is running in a background process. 17-Apr-11 MP Cleaned up to support running in a background/detached process 20-Jan-11 MP Fixed support for BREAK key on Windows to account for/ignore other keyboard Meta characters. 18-Jan-11 MP Added log file reference count support 17-Jan-11 MP Added support for a "Buffered" behaviors which include: - If Buffering is enabled and Telnet is enabled, a telnet connection is not required for simulator operation (instruction execution). - If Buffering is enabled, all console output is written to the buffer at all times (deleting the oldest buffer contents on overflow). - when a connection is established on the console telnet port, the whole contents of the Buffer is presented on the telnet session and connection will then proceed as if the connection had always been there. This concept allows a simulator to run in the background and when needed a console session to be established. The "when needed" case usually will be interested in what already happened before looking to address what to do, hence the buffer contents being presented. 28-Dec-10 MP Added support for BREAK key on Windows 30-Sep-06 RMS Fixed non-printable characters in KSR mode 22-Jun-06 RMS Implemented SET/SHOW PCHAR 31-May-06 JDB Fixed bug if SET CONSOLE DEBUG with no argument 22-Nov-05 RMS Added central input/output conversion support 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy 28-Oct-04 JDB Fixed SET CONSOLE to allow comma-separated parameters 20-Aug-04 RMS Added OS/2 EMX fixes (Holger Veit) 14-Jul-04 RMS Revised Windows console code (Dave Bryan) 28-May-04 RMS Added SET/SHOW CONSOLE RMS Added break, delete character maps 02-Jan-04 RMS Removed timer routines, added Telnet console routines RMS Moved console logging to OS-independent code 25-Apr-03 RMS Added long seek support (Mark Pizzolato) Added Unix priority control (Mark Pizzolato) 24-Sep-02 RMS Removed VT support, added Telnet console support Added CGI support (Brian Knittel) Added MacOS sleep (Peter Schorn) 14-Jul-02 RMS Added Windows priority control (Mark Pizzolato) 20-May-02 RMS Added Windows VT support (Fischer Franz) 01-Feb-02 RMS Added VAX fix (Robert Alan Byer) 19-Sep-01 RMS More MacOS changes 31-Aug-01 RMS Changed int64 to t_int64 for Windoze 20-Jul-01 RMS Added MacOS support (Louis Chretien, Peter Schorn, Ben Supnik) 15-May-01 RMS Added logging support 05-Mar-01 RMS Added clock calibration support 08-Dec-00 BKR Added OS/2 support (Bruce Ray) 18-Aug-98 RMS Added BeOS support 13-Oct-97 RMS Added NetBSD terminal support 25-Jan-97 RMS Added POSIX terminal I/O support 02-Jan-97 RMS Fixed bug in sim_poll_kbd This module implements the following routines to support terminal and Remote Console I/O: sim_poll_kbd poll for keyboard input sim_putchar output character to console sim_putchar_s output character to console, stall if congested sim_set_console set console parameters sim_show_console show console parameters sim_set_remote_console set remote console parameters sim_show_remote_console show remote console parameters sim_set_cons_buff set console buffered sim_set_cons_unbuff set console unbuffered sim_set_cons_log set console log sim_set_cons_nolog set console nolog sim_show_cons_buff show console buffered sim_show_cons_log show console log sim_tt_inpcvt convert input character per mode sim_tt_outcvt convert output character per mode sim_cons_get_send get console send structure address sim_cons_get_expect get console expect structure address sim_show_cons_send_input show pending input data sim_show_cons_expect show expect rules and state sim_ttinit called once to get initial terminal state sim_ttrun called to put terminal into run state sim_ttcmd called to return terminal to command state sim_ttclose called once before the simulator exits sim_ttisatty called to determine if running interactively sim_os_poll_kbd poll for keyboard input sim_os_putchar output character to console The first group is OS-independent; the second group is OS-dependent. The following routines are exposed but deprecated: sim_set_telnet set console to Telnet port sim_set_notelnet close console Telnet port sim_show_telnet show console status */ #include "sim_defs.h" #include "sim_tmxr.h" #include "sim_serial.h" #include "sim_timer.h" #include <ctype.h> #include <math.h> #ifdef __HAIKU__ #define nice(n) ({}) #endif /* Forward Declaraations of Platform specific routines */ static t_stat sim_os_poll_kbd (void); static t_bool sim_os_poll_kbd_ready (int ms_timeout); static t_stat sim_os_putchar (int32 out); static t_stat sim_os_ttinit (void); static t_stat sim_os_ttrun (void); static t_stat sim_os_ttcmd (void); static t_stat sim_os_ttclose (void); static t_bool sim_os_ttisatty (void); static t_stat sim_set_rem_telnet (int32 flag, CONST char *cptr); static t_stat sim_set_rem_bufsize (int32 flag, CONST char *cptr); static t_stat sim_set_rem_connections (int32 flag, CONST char *cptr); static t_stat sim_set_rem_timeout (int32 flag, CONST char *cptr); static t_stat sim_set_rem_master (int32 flag, CONST char *cptr); /* Deprecated CONSOLE HALT, CONSOLE RESPONSE and CONSOLE DELAY support */ static t_stat sim_set_halt (int32 flag, CONST char *cptr); static t_stat sim_set_response (int32 flag, CONST char *cptr); static t_stat sim_set_delay (int32 flag, CONST char *cptr); #define KMAP_WRU 0 #define KMAP_BRK 1 #define KMAP_DEL 2 #define KMAP_MASK 0377 #define KMAP_NZ 0400 int32 sim_int_char = 005; /* interrupt character */ int32 sim_brk_char = 000; /* break character */ int32 sim_tt_pchar = 0x00002780; #if defined (_WIN32) || defined (__OS2__) || (defined (__MWERKS__) && defined (macintosh)) int32 sim_del_char = '\b'; /* delete character */ #else int32 sim_del_char = 0177; #endif static t_stat sim_con_poll_svc (UNIT *uptr); /* console connection poll routine */ static t_stat sim_con_reset (DEVICE *dptr); /* console reset routine */ static t_stat sim_con_attach (UNIT *uptr, CONST char *ptr); /* console attach routine (save,restore) */ static t_stat sim_con_detach (UNIT *uptr); /* console detach routine (save,restore) */ UNIT sim_con_units[2] = {{ UDATA (&sim_con_poll_svc, UNIT_ATTABLE, 0)}}; /* console connection unit */ #define sim_con_unit sim_con_units[0] /* debugging bitmaps */ #define DBG_TRC TMXR_DBG_TRC /* trace routine calls */ #define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */ #define DBG_RCV TMXR_DBG_RCV /* display Received Data */ #define DBG_RET TMXR_DBG_RET /* display Returned Received Data */ #define DBG_ASY TMXR_DBG_ASY /* asynchronous thread activity */ #define DBG_CON TMXR_DBG_CON /* display connection activity */ #define DBG_EXP 0x00000001 /* Expect match activity */ #define DBG_SND 0x00000002 /* Send (Inject) data activity */ static DEBTAB sim_con_debug[] = { {"TRC", DBG_TRC, "routine calls"}, {"XMT", DBG_XMT, "Transmitted Data"}, {"RCV", DBG_RCV, "Received Data"}, {"RET", DBG_RET, "Returned Received Data"}, {"ASY", DBG_ASY, "asynchronous activity"}, {"CON", DBG_CON, "connection activity"}, {"EXP", DBG_EXP, "Expect match activity"}, {"SND", DBG_SND, "Send (Inject) data activity"}, {0} }; static REG sim_con_reg[] = { { ORDATAD (WRU, sim_int_char, 8, "interrupt character") }, { ORDATAD (BRK, sim_brk_char, 8, "break character") }, { ORDATAD (DEL, sim_del_char, 8, "delete character ") }, { ORDATAD (PCHAR, sim_tt_pchar, 32, "printable character mask") }, { 0 }, }; static MTAB sim_con_mod[] = { { 0 }, }; static const char *sim_con_telnet_description (DEVICE *dptr) { return "Console telnet support"; } DEVICE sim_con_telnet = { "CON-TELNET", sim_con_units, sim_con_reg, sim_con_mod, 2, 0, 0, 0, 0, 0, NULL, NULL, sim_con_reset, NULL, sim_con_attach, sim_con_detach, NULL, DEV_DEBUG, 0, sim_con_debug, NULL, NULL, NULL, NULL, NULL, sim_con_telnet_description}; TMLN sim_con_ldsc = { 0 }; /* console line descr */ TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc, NULL, &sim_con_telnet };/* console line mux */ SEND sim_con_send = {SEND_DEFAULT_DELAY, &sim_con_telnet, DBG_SND}; EXPECT sim_con_expect = {&sim_con_telnet, DBG_EXP}; static t_bool sim_con_console_port = TRUE; /* Enable automatic WRU console polling */ t_stat sim_set_noconsole_port (void) { sim_con_console_port = FALSE; return SCPE_OK; } /* Unit service for console connection polling */ static t_stat sim_con_poll_svc (UNIT *uptr) { if ((sim_con_tmxr.master == 0) && /* not Telnet and not serial and not WRU polling? */ (sim_con_ldsc.serport == 0) && (sim_con_console_port)) return SCPE_OK; /* done */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ sim_con_ldsc.rcve = 1; /* rcv enabled */ sim_activate_after(uptr, 1000000); /* check again in 1 second */ if (!sim_con_console_port) /* WRU poll needed */ sim_poll_kbd(); /* sets global stop_cpu when WRU received */ if (sim_con_ldsc.conn) tmxr_send_buffered_data (&sim_con_ldsc); /* try to flush any buffered data */ return SCPE_OK; } static t_stat sim_con_reset (DEVICE *dptr) { dptr->units[1].flags = UNIT_DIS; return sim_con_poll_svc (&dptr->units[0]); /* establish polling as needed */ } /* Console Attach/Detach - only used indirectly in restore */ static t_stat sim_con_attach (UNIT *uptr, CONST char *ptr) { return tmxr_attach (&sim_con_tmxr, &sim_con_unit, ptr); } static t_stat sim_con_detach (UNIT *uptr) { return sim_set_notelnet (0, NULL); } /* Set/show data structures */ static CTAB set_con_tab[] = { { "WRU", &sim_set_kmap, KMAP_WRU | KMAP_NZ }, { "BRK", &sim_set_kmap, KMAP_BRK }, { "DEL", &sim_set_kmap, KMAP_DEL |KMAP_NZ }, { "PCHAR", &sim_set_pchar, 0 }, { "SPEED", &sim_set_cons_speed, 0 }, { "TELNET", &sim_set_telnet, 0 }, { "NOTELNET", &sim_set_notelnet, 0 }, { "SERIAL", &sim_set_serial, 0 }, { "NOSERIAL", &sim_set_noserial, 0 }, { "LOG", &sim_set_logon, 0 }, { "NOLOG", &sim_set_logoff, 0 }, { "DEBUG", &sim_set_debon, 0 }, { "NODEBUG", &sim_set_deboff, 0 }, #define CMD_WANTSTR 0100000 { "HALT", &sim_set_halt, 1 | CMD_WANTSTR }, { "NOHALT", &sim_set_halt, 0 }, { "DELAY", &sim_set_delay, 0 }, { "RESPONSE", &sim_set_response, 1 | CMD_WANTSTR }, { "NORESPONSE", &sim_set_response, 0 }, { NULL, NULL, 0 } }; static CTAB set_rem_con_tab[] = { { "CONNECTIONS", &sim_set_rem_connections, 0 }, { "TELNET", &sim_set_rem_telnet, 1 }, { "BUFFERSIZE", &sim_set_rem_bufsize, 1 }, { "NOTELNET", &sim_set_rem_telnet, 0 }, { "TIMEOUT", &sim_set_rem_timeout, 0 }, { "MASTER", &sim_set_rem_master, 1 }, { "NOMASTER", &sim_set_rem_master, 0 }, { NULL, NULL, 0 } }; static SHTAB show_con_tab[] = { { "WRU", &sim_show_kmap, KMAP_WRU }, { "BRK", &sim_show_kmap, KMAP_BRK }, { "DEL", &sim_show_kmap, KMAP_DEL }, { "PCHAR", &sim_show_pchar, 0 }, { "SPEED", &sim_show_cons_speed, 0 }, { "LOG", &sim_show_cons_log, 0 }, { "TELNET", &sim_show_telnet, 0 }, { "DEBUG", &sim_show_cons_debug, 0 }, { "BUFFERED", &sim_show_cons_buff, 0 }, { "EXPECT", &sim_show_cons_expect, 0 }, { "HALT", &sim_show_cons_expect, 0 }, { "INPUT", &sim_show_cons_send_input, 0 }, { "RESPONSE", &sim_show_cons_send_input, 0 }, { "DELAY", &sim_show_cons_expect, 0 }, { NULL, NULL, 0 } }; static CTAB set_con_telnet_tab[] = { { "LOG", &sim_set_cons_log, 0 }, { "NOLOG", &sim_set_cons_nolog, 0 }, { "BUFFERED", &sim_set_cons_buff, 0 }, { "NOBUFFERED", &sim_set_cons_unbuff, 0 }, { "UNBUFFERED", &sim_set_cons_unbuff, 0 }, { NULL, NULL, 0 } }; static CTAB set_con_serial_tab[] = { { "LOG", &sim_set_cons_log, 0 }, { "NOLOG", &sim_set_cons_nolog, 0 }, { NULL, NULL, 0 } }; static int32 *cons_kmap[] = { &sim_int_char, &sim_brk_char, &sim_del_char }; /* Console I/O package. The console terminal can be attached to the controlling window or to a Telnet connection. If attached to a Telnet connection, the console is described by internal terminal multiplexor sim_con_tmxr and internal terminal line description sim_con_ldsc. */ /* SET CONSOLE command */ t_stat sim_set_console (int32 flag, CONST char *cptr) { char *cvptr, gbuf[CBUFSIZE]; CTAB *ctptr; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; while (*cptr != 0) { /* do all mods */ cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */ if ((cvptr = strchr (gbuf, '='))) /* = value? */ *cvptr++ = 0; get_glyph (gbuf, gbuf, 0); /* modifier to UC */ if ((ctptr = find_ctab (set_con_tab, gbuf))) { /* match? */ r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ if (r != SCPE_OK) return r; } else return SCPE_NOPARAM; } return SCPE_OK; } /* SHOW CONSOLE command */ t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { char gbuf[CBUFSIZE]; SHTAB *shptr; int32 i; if (*cptr == 0) { /* show all */ for (i = 0; show_con_tab[i].name; i++) show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr); return SCPE_OK; } while (*cptr != 0) { cptr = get_glyph (cptr, gbuf, ','); /* get modifier */ if ((shptr = find_shtab (show_con_tab, gbuf))) shptr->action (st, dptr, uptr, shptr->arg, cptr); else return SCPE_NOPARAM; } return SCPE_OK; } t_stat sim_rem_con_poll_svc (UNIT *uptr); /* remote console connection poll routine */ t_stat sim_rem_con_data_svc (UNIT *uptr); /* remote console connection data routine */ t_stat sim_rem_con_reset (DEVICE *dptr); /* remote console reset routine */ UNIT sim_rem_con_unit[2] = { { UDATA (&sim_rem_con_poll_svc, UNIT_IDLE, 0) }, /* remote console connection polling unit */ { UDATA (&sim_rem_con_data_svc, UNIT_IDLE|UNIT_DIS, 0) }}; /* console data handling unit */ DEBTAB sim_rem_con_debug[] = { {"TRC", DBG_TRC, "routine calls"}, {"XMT", DBG_XMT, "Transmitted Data"}, {"RCV", DBG_RCV, "Received Data"}, {"CON", DBG_CON, "connection activity"}, {0} }; MTAB sim_rem_con_mod[] = { { 0 }, }; static const char *sim_rem_con_description (DEVICE *dptr) { return "Remote Console Facility"; } DEVICE sim_remote_console = { "REM-CON", sim_rem_con_unit, NULL, sim_rem_con_mod, 2, 0, 0, 0, 0, 0, NULL, NULL, sim_rem_con_reset, NULL, NULL, NULL, NULL, DEV_DEBUG | DEV_NOSAVE, 0, sim_rem_con_debug, NULL, NULL, NULL, NULL, NULL, sim_rem_con_description}; #define MAX_REMOTE_SESSIONS 40 /* Arbitrary Session Limit */ static int32 *sim_rem_buf_size = NULL; static int32 *sim_rem_buf_ptr = NULL; static char **sim_rem_buf = NULL; static t_bool *sim_rem_single_mode = NULL; /* per line command mode (single command or must continue) */ static TMXR sim_rem_con_tmxr = { 0, 0, 0, NULL, NULL, &sim_remote_console };/* remote console line mux */ static uint32 sim_rem_read_timeout = 30; /* seconds before automatic continue */ static uint32 *sim_rem_read_timeouts = NULL;/* per line read timeout (default from sim_rem_read_timeout) */ static int32 sim_rem_active_number = -1; /* -1 - not active, >= 0 is index of active console */ int32 sim_rem_cmd_active_line = -1; /* step in progress on line # */ static CTAB *sim_rem_active_command = NULL; /* active command */ static char *sim_rem_command_buf; /* active command buffer */ static t_bool sim_log_temp = FALSE; /* temporary log file active */ static char sim_rem_con_temp_name[PATH_MAX+1]; static t_bool sim_rem_master_mode = FALSE; /* Master Mode Enabled Flag */ static t_bool sim_rem_master_was_enabled = FALSE; /* Master was Enabled */ static t_bool sim_rem_master_was_connected = FALSE; /* Master Mode has been connected */ static t_offset sim_rem_cmd_log_start = 0; /* Log File saved position */ /* SET REMOTE CONSOLE command */ t_stat sim_set_remote_console (int32 flag, CONST char *cptr) { char *cvptr, gbuf[CBUFSIZE]; CTAB *ctptr; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; while (*cptr != 0) { /* do all mods */ cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */ if ((cvptr = strchr (gbuf, '='))) /* = value? */ *cvptr++ = 0; get_glyph (gbuf, gbuf, 0); /* modifier to UC */ if ((ctptr = find_ctab (set_rem_con_tab, gbuf))) { /* match? */ r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ if (r != SCPE_OK) return r; } else return SCPE_NOPARAM; } return SCPE_OK; } /* SHOW REMOTE CONSOLE command */ t_stat sim_show_remote_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { int32 i, connections; TMLN *lp; if (*cptr != 0) return SCPE_NOPARAM; if (sim_rem_active_number >= 0) { if (sim_rem_master_mode && (sim_rem_active_number == 0)) fprintf (st, "Running from Master Mode Remote Console Connection\n"); else fprintf (st, "Running from Remote Console Connection %d\n", sim_rem_active_number); } if (sim_rem_con_tmxr.lines > 1) fprintf (st, "Remote Console Input Connections from %d sources are supported concurrently\n", sim_rem_con_tmxr.lines); if (sim_rem_read_timeout) fprintf (st, "Remote Console Input automatically continues after %d seconds\n", sim_rem_read_timeout); if (!sim_rem_con_tmxr.master) fprintf (st, "Remote Console Command input is disabled\n"); else { fprintf (st, "Remote Console Command Input listening on TCP port: %s\n", sim_rem_con_unit[0].filename); fprintf (st, "Remote Console Per Command Output buffer size: %d bytes\n", sim_rem_con_tmxr.buffered); } for (i=connections=0; i<sim_rem_con_tmxr.lines; i++) { lp = &sim_rem_con_tmxr.ldsc[i]; if (!lp->conn) continue; ++connections; if (connections == 1) fprintf (st, "Remote Console Connections:\n"); tmxr_fconns (st, lp, i); if (sim_rem_read_timeouts[i] != sim_rem_read_timeout) { if (sim_rem_read_timeouts[i]) fprintf (st, "Remote Console Input on connection %d automatically continues after %d seconds\n", i, sim_rem_read_timeouts[i]); else fprintf (st, "Remote Console Input on connection %d does not continue automatically\n", i); } } return SCPE_OK; } /* Unit service for remote console connection polling */ t_stat sim_rem_con_poll_svc (UNIT *uptr) { int32 c; c = tmxr_poll_conn (&sim_rem_con_tmxr); if (c >= 0) { /* poll connect */ TMLN *lp = &sim_rem_con_tmxr.ldsc[c]; char wru_name[8]; sim_activate_after(uptr+1, 1000000); /* start data poll after 1 second */ lp->rcve = 1; /* rcv enabled */ sim_rem_buf_ptr[c] = 0; /* start with empty command buffer */ sim_rem_single_mode[c] = TRUE; /* start in single command mode */ sim_rem_read_timeouts[c] = sim_rem_read_timeout; /* Start with default timeout */ if (isprint(sim_int_char&0xFF)) sprintf(wru_name, "'%c'", sim_int_char&0xFF); else if (sim_int_char <= 26) sprintf(wru_name, "^%c", '@' + (sim_int_char&0xFF)); else sprintf(wru_name, "'\\%03o'", sim_int_char&0xFF); tmxr_linemsgf (lp, "%s Remote Console\r\n" "Enter single commands or to enter multiple command mode enter the %s character\r" "%s", sim_name, wru_name, ((sim_rem_master_mode && (c == 0)) ? "" : "\nSimulator Running...")); if (sim_rem_master_mode && (c == 0)) /* Master Mode session? */ sim_rem_single_mode[c] = FALSE; /* start in multi-command mode */ tmxr_send_buffered_data (lp); /* flush buffered data */ } sim_activate_after(uptr, 1000000); /* check again in 1 second */ if (sim_con_ldsc.conn) tmxr_send_buffered_data (&sim_con_ldsc); /* try to flush any buffered data */ return SCPE_OK; } static t_stat x_continue_cmd (int32 flag, CONST char *cptr) { return SCPE_IERR; /* This routine should never be called */ } static t_stat x_step_cmd (int32 flag, CONST char *cptr) { return SCPE_IERR; /* This routine should never be called */ } static t_stat x_run_cmd (int32 flag, CONST char *cptr) { return SCPE_IERR; /* This routine should never be called */ } static t_stat x_help_cmd (int32 flag, CONST char *cptr); static CTAB allowed_remote_cmds[] = { { "EXAMINE", &exdep_cmd, EX_E }, { "DEPOSIT", &exdep_cmd, EX_D }, { "EVALUATE", &eval_cmd, 0 }, { "ATTACH", &attach_cmd, 0 }, { "DETACH", &detach_cmd, 0 }, { "ASSIGN", &assign_cmd, 0 }, { "DEASSIGN", &deassign_cmd, 0 }, { "CONTINUE", &x_continue_cmd, 0 }, { "STEP", &x_step_cmd, 0 }, { "PWD", &pwd_cmd, 0 }, { "SAVE", &save_cmd, 0 }, { "DIR", &dir_cmd, 0 }, { "LS", &dir_cmd, 0 }, { "ECHO", &echo_cmd, 0 }, { "SET", &set_cmd, 0 }, { "SHOW", &show_cmd, 0 }, { "HELP", &x_help_cmd, 0 }, { NULL, NULL } }; static CTAB allowed_master_remote_cmds[] = { { "EXAMINE", &exdep_cmd, EX_E }, { "DEPOSIT", &exdep_cmd, EX_D }, { "EVALUATE", &eval_cmd, 0 }, { "ATTACH", &attach_cmd, 0 }, { "DETACH", &detach_cmd, 0 }, { "ASSIGN", &assign_cmd, 0 }, { "DEASSIGN", &deassign_cmd, 0 }, { "CONTINUE", &x_continue_cmd, 0 }, { "STEP", &x_step_cmd, 0 }, { "PWD", &pwd_cmd, 0 }, { "SAVE", &save_cmd, 0 }, { "CD", &set_default_cmd, 0 }, { "DIR", &dir_cmd, 0 }, { "LS", &dir_cmd, 0 }, { "ECHO", &echo_cmd, 0 }, { "SET", &set_cmd, 0 }, { "SHOW", &show_cmd, 0 }, { "HELP", &x_help_cmd, 0 }, { "EXIT", &exit_cmd, 0 }, { "QUIT", &exit_cmd, 0 }, { "RUN", &x_run_cmd, RU_RUN }, { "GO", &x_run_cmd, RU_GO }, { "BOOT", &x_run_cmd, RU_BOOT }, { "BREAK", &brk_cmd, SSH_ST }, { "NOBREAK", &brk_cmd, SSH_CL }, { NULL, NULL } }; static CTAB allowed_single_remote_cmds[] = { { "ATTACH", &attach_cmd, 0 }, { "DETACH", &detach_cmd, 0 }, { "EXAMINE", &exdep_cmd, EX_E }, { "EVALUATE", &eval_cmd, 0 }, { "PWD", &pwd_cmd, 0 }, { "DIR", &dir_cmd, 0 }, { "LS", &dir_cmd, 0 }, { "ECHO", &echo_cmd, 0 }, { "SHOW", &show_cmd, 0 }, { "HELP", &x_help_cmd, 0 }, { NULL, NULL } }; static t_stat x_help_cmd (int32 flag, CONST char *cptr) { CTAB *cmdp, *cmdph; if (*cptr) { int32 saved_switches = sim_switches; t_stat r; sim_switches |= SWMASK ('F'); r = help_cmd (flag, cptr); sim_switches = saved_switches; return r; } sim_printf ("Help is available for the following Remote Console commands:\r\n"); for (cmdp=allowed_remote_cmds; cmdp->name != NULL; ++cmdp) { cmdph = find_cmd (cmdp->name); if (cmdph && cmdph->help) sim_printf (" %s\r\n", cmdp->name); } sim_printf ("Enter \"HELP cmd\" for detailed help on a command\r\n"); return SCPE_OK; } static t_stat _sim_rem_message (const char *cmd, t_stat stat) { CTAB *cmdp = NULL; t_stat stat_nomessage = stat & SCPE_NOMESSAGE; /* extract possible message supression flag */ cmdp = find_cmd (cmd); stat = SCPE_BARE_STATUS(stat); /* remove possible flag */ if (!stat_nomessage) { if (cmdp && (cmdp->message)) /* special message handler? */ cmdp->message (NULL, stat); /* let it deal with display */ else { if (stat >= SCPE_BASE) /* error? */ sim_printf ("%s\r\n", sim_error_text (stat)); } } return stat; } static void _sim_rem_log_out (TMLN *lp) { char cbuf[4*CBUFSIZE]; if (sim_log) { int32 unwritten; fflush (sim_log); sim_fseeko (sim_log, sim_rem_cmd_log_start, SEEK_SET); cbuf[sizeof(cbuf)-1] = '\0'; while (fgets (cbuf, sizeof(cbuf)-1, sim_log)) tmxr_linemsgf (lp, "%s", cbuf); if (!tmxr_input_pending_ln (lp)) { do { unwritten = tmxr_send_buffered_data (lp); if (unwritten == lp->txbsz) sim_os_ms_sleep (100); } while (unwritten == lp->txbsz); } } } void sim_remote_process_command (void) { char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], *argv[1] = {NULL}; CONST char *cptr; int32 saved_switches = sim_switches; t_stat stat; strcpy (cbuf, sim_rem_command_buf); while (isspace(cbuf[0])) memmove (cbuf, cbuf+1, strlen(cbuf+1)+1); /* skip leading whitespace */ sim_sub_args (cbuf, sizeof(cbuf), argv); cptr = cbuf; cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ sim_rem_active_command = find_cmd (gbuf); /* find command */ sim_ttcmd (); /* restore console */ stat = sim_rem_active_command->action (sim_rem_active_command->arg, cptr);/* execute command */ if (stat != SCPE_OK) stat = _sim_rem_message (gbuf, stat); /* display results */ sim_last_cmd_stat = SCPE_BARE_STATUS(stat); sim_ttrun (); /* set console mode */ sim_cancel (&sim_rem_con_unit[1]); /* force immediate activation of sim_rem_con_data_svc */ sim_activate (&sim_rem_con_unit[1], -1); sim_switches = saved_switches; /* restore original switches */ } /* Unit service for remote console data polling */ t_stat sim_rem_con_data_svc (UNIT *uptr) { int32 i, j, c = 0; t_stat stat = SCPE_OK; t_bool active_command = FALSE; int32 steps = 0; t_bool was_active_command = (sim_rem_cmd_active_line != -1); t_bool got_command; t_bool close_session = FALSE; TMLN *lp; char cbuf[4*CBUFSIZE], gbuf[CBUFSIZE], *argv[1] = {NULL}; CONST char *cptr; CTAB *cmdp = NULL; CTAB *basecmdp = NULL; uint32 read_start_time = 0; tmxr_poll_rx (&sim_rem_con_tmxr); /* poll input */ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); (i < sim_rem_con_tmxr.lines) && (!active_command); i++) { t_bool master_session = (sim_rem_master_mode && (i == 0)); lp = &sim_rem_con_tmxr.ldsc[i]; if (!lp->conn) continue; if (master_session && !sim_rem_master_was_connected) { tmxr_linemsgf (lp, "\nMaster Mode Session\r\n"); tmxr_send_buffered_data (lp); /* flush any buffered data */ } sim_rem_master_was_connected |= master_session; /* Remember if master ever connected */ stat = SCPE_OK; if ((was_active_command) || (master_session && !sim_rem_single_mode[i])) { if (was_active_command) { sim_rem_cmd_active_line = -1; /* Done with active command */ if (!sim_rem_active_command) { /* STEP command? */ stat = SCPE_STEP; _sim_rem_message ("STEP", stat); /* produce a STEP complete message */ } _sim_rem_log_out (lp); sim_rem_active_command = NULL; /* Restart loop to process available input */ was_active_command = FALSE; i = -1; continue; } else { sim_is_running = 0; sim_stop_timer_services (); for (j=0; j < sim_rem_con_tmxr.lines; j++) { TMLN *lpj = &sim_rem_con_tmxr.ldsc[j]; if ((i == j) || (!lpj->conn)) continue; tmxr_linemsgf (lpj, "\nRemote Master Console(%s) Entering Commands\n", lp->ipad); tmxr_send_buffered_data (lpj); /* flush any buffered data */ } lp = &sim_rem_con_tmxr.ldsc[i]; } } else { c = tmxr_getc_ln (lp); if (!(TMXR_VALID & c)) continue; c = c & ~TMXR_VALID; if (sim_rem_single_mode[i]) { if (c == sim_int_char) { /* ^E (the interrupt character) must start continue mode console interaction */ sim_rem_single_mode[i] = FALSE; /* enter multi command mode */ sim_is_running = 0; sim_stop_timer_services (); stat = SCPE_STOP; _sim_rem_message ("RUN", stat); _sim_rem_log_out (lp); for (j=0; j < sim_rem_con_tmxr.lines; j++) { TMLN *lpj = &sim_rem_con_tmxr.ldsc[j]; if ((i == j) || (!lpj->conn)) continue; tmxr_linemsgf (lpj, "\nRemote Console %d(%s) Entering Commands\n", i, lp->ipad); tmxr_send_buffered_data (lpj); /* flush any buffered data */ } lp = &sim_rem_con_tmxr.ldsc[i]; if (!master_session) tmxr_linemsg (lp, "\r\nSimulator paused.\r\n"); if (!master_session && sim_rem_read_timeouts[i]) { tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", sim_rem_read_timeouts[i]); tmxr_linemsgf (lp, "\r\n"); tmxr_send_buffered_data (lp); /* flush any buffered data */ } } else { if ((sim_rem_buf_ptr[i] == 0) && /* At beginning of input line */ ((c == '\n') || /* Ignore bare LF between commands (Microsoft Telnet bug) */ (c == '\r'))) /* Ignore empty commands */ continue; if ((c == '\004') || (c == '\032')) { /* EOF character (^D or ^Z) ? */ tmxr_linemsgf (lp, "\r\nGoodbye\r\n"); tmxr_send_buffered_data (lp); /* flush any buffered data */ tmxr_reset_ln (lp); continue; } if (sim_rem_buf_ptr[i] == 0) { /* we just picked up the first character on a command line */ if (!master_session) tmxr_linemsgf (lp, "\r\n%s", sim_prompt); else tmxr_linemsgf (lp, "\r\n%s", sim_is_running ? "SIM> " : "sim> "); sim_debug (DBG_XMT, &sim_remote_console, "Prompt Written: %s\n", sim_is_running ? "SIM> " : "sim> "); if (!tmxr_input_pending_ln (lp)) tmxr_send_buffered_data (lp); /* flush any buffered data */ } } } } got_command = FALSE; while (1) { if (stat == SCPE_EXIT) return stat|SCPE_NOMESSAGE; if (!sim_rem_single_mode[i]) { read_start_time = sim_os_msec(); if (master_session) tmxr_linemsg (lp, "sim> "); else tmxr_linemsg (lp, sim_prompt); tmxr_send_buffered_data (lp); /* flush any buffered data */ } do { if (!sim_rem_single_mode[i]) { c = tmxr_getc_ln (lp); if (!(TMXR_VALID & c)) { tmxr_send_buffered_data (lp); /* flush any buffered data */ if (!master_session && sim_rem_read_timeouts[i] && ((sim_os_msec() - read_start_time)/1000 >= sim_rem_read_timeouts[i])) { while (sim_rem_buf_ptr[i] > 0) {/* Erase current input line */ tmxr_linemsg (lp, "\b \b"); --sim_rem_buf_ptr[i]; } if (sim_rem_buf_ptr[i]+80 >= sim_rem_buf_size[i]) { sim_rem_buf_size[i] += 1024; sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]); } strcpy (sim_rem_buf[i], "CONTINUE ! Automatic continue due to timeout"); tmxr_linemsgf (lp, "%s\n", sim_rem_buf[i]); got_command = TRUE; break; } sim_os_ms_sleep (50); tmxr_poll_rx (&sim_rem_con_tmxr); /* poll input */ if (!lp->conn) { /* if connection lost? */ sim_rem_single_mode[i] = TRUE; /* No longer multi-command more */ break; /* done waiting */ } continue; } read_start_time = sim_os_msec(); c = c & ~TMXR_VALID; } switch (c) { case 0: /* no data */ break; case '\b': /* Backspace */ case 127: /* Rubout */ if (sim_rem_buf_ptr[i] > 0) { tmxr_linemsg (lp, "\b \b"); --sim_rem_buf_ptr[i]; } break; case 27: /* escape */ case 21: /* ^U */ while (sim_rem_buf_ptr[i] > 0) { tmxr_linemsg (lp, "\b \b"); --sim_rem_buf_ptr[i]; } break; case '\n': if (sim_rem_buf_ptr[i] == 0) break; case '\r': tmxr_linemsg (lp, "\r\n"); if (sim_rem_buf_ptr[i]+1 >= sim_rem_buf_size[i]) { sim_rem_buf_size[i] += 1024; sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]); } sim_rem_buf[i][sim_rem_buf_ptr[i]++] = '\0'; sim_debug (DBG_RCV, &sim_remote_console, "Got Command (%d bytes still in buffer): %s\n", tmxr_input_pending_ln (lp), sim_rem_buf[i]); got_command = TRUE; break; case '\004': /* EOF (^D) */ case '\032': /* EOF (^Z) */ while (sim_rem_buf_ptr[i] > 0) { /* Erase current input line */ tmxr_linemsg (lp, "\b \b"); --sim_rem_buf_ptr[i]; } if (!sim_rem_single_mode[i]) { if (sim_rem_buf_ptr[i]+80 >= sim_rem_buf_size[i]) { sim_rem_buf_size[i] += 1024; sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]); } strcpy (sim_rem_buf[i], "CONTINUE ! Automatic continue before close"); tmxr_linemsgf (lp, "%s\n", sim_rem_buf[i]); got_command = TRUE; } close_session = TRUE; break; default: tmxr_putc_ln (lp, c); if (sim_rem_buf_ptr[i]+2 >= sim_rem_buf_size[i]) { sim_rem_buf_size[i] += 1024; sim_rem_buf[i] = (char *)realloc (sim_rem_buf[i], sim_rem_buf_size[i]); } sim_rem_buf[i][sim_rem_buf_ptr[i]++] = (char)c; sim_rem_buf[i][sim_rem_buf_ptr[i]] = '\0'; if (((size_t)sim_rem_buf_ptr[i]) >= sizeof(cbuf)) got_command = TRUE; /* command too long */ break; } c = 0; if ((!got_command) && (sim_rem_single_mode[i]) && (tmxr_input_pending_ln (lp))) { c = tmxr_getc_ln (lp); c = c & ~TMXR_VALID; } } while ((!got_command) && ((!sim_rem_single_mode[i]) || c)); if (!tmxr_input_pending_ln (lp)) tmxr_send_buffered_data (lp); /* flush any buffered data */ if ((sim_rem_single_mode[i]) && !got_command) { break; } sim_printf ("Remote Console Command from %s> %s\r\n", lp->ipad, sim_rem_buf[i]); got_command = FALSE; if (strlen(sim_rem_buf[i]) >= sizeof(cbuf)) { sim_printf ("\r\nLine too long. Ignored. Continuing Simulator execution\r\n"); tmxr_linemsgf (lp, "\nLine too long. Ignored. Continuing Simulator execution\n"); tmxr_send_buffered_data (lp); /* try to flush any buffered data */ break; } strcpy (cbuf, sim_rem_buf[i]); sim_rem_buf_ptr[i] = 0; sim_rem_buf[i][sim_rem_buf_ptr[i]] = '\0'; while (isspace(cbuf[0])) memmove (cbuf, cbuf+1, strlen(cbuf+1)+1); /* skip leading whitespace */ if (cbuf[0] == '\0') { if (sim_rem_single_mode[i]) { sim_rem_single_mode[i] = FALSE; break; } else continue; } strcpy (sim_rem_command_buf, cbuf); sim_sub_args (cbuf, sizeof(cbuf), argv); cptr = cbuf; cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ sim_switches = 0; /* init switches */ sim_rem_active_number = i; if (!sim_log) { /* Not currently logging? */ int32 save_quiet = sim_quiet; sim_quiet = 1; sprintf (sim_rem_con_temp_name, "sim_remote_console_%d.temporary_log", (int)getpid()); sim_set_logon (0, sim_rem_con_temp_name); sim_quiet = save_quiet; sim_log_temp = TRUE; } sim_rem_cmd_log_start = sim_ftell (sim_log); basecmdp = find_cmd (gbuf); /* validate basic command */ if (basecmdp == NULL) { if ((gbuf[0] == ';') || (gbuf[0] == '#')) { /* ignore comment */ sim_rem_cmd_active_line = i; was_active_command = TRUE; sim_rem_active_command = &allowed_single_remote_cmds[0];/* Dummy */ i = i - 1; break; } else stat = SCPE_UNK; } else { if ((cmdp = find_ctab (sim_rem_single_mode[i] ? allowed_single_remote_cmds : (master_session ? allowed_master_remote_cmds : allowed_remote_cmds), gbuf))) {/* lookup command */ if (cmdp->action == &x_continue_cmd) stat = SCPE_OK; else { if (cmdp->action == &exit_cmd) return SCPE_EXIT; if (cmdp->action == &x_step_cmd) { steps = 1; /* default of 1 instruction */ stat = SCPE_OK; if (*cptr != 0) { /* argument? */ cptr = get_glyph (cptr, gbuf, 0);/* get next glyph */ if (*cptr != 0) /* should be end */ stat = SCPE_2MARG; else { steps = (int32) get_uint (gbuf, 10, INT_MAX, &stat); if ((stat != SCPE_OK) || (steps <= 0)) /* error? */ stat = SCPE_ARG; } } if (stat != SCPE_OK) cmdp = NULL; } else { if (cmdp->action == &x_run_cmd) { sim_switches |= SIM_SW_HIDE;/* Request Setup only */ stat = basecmdp->action (cmdp->arg, cptr); sim_switches &= ~SIM_SW_HIDE;/* Done with Setup only mode */ if (stat == SCPE_OK) { /* switch to CONTINUE after x_run_cmd() did RUN setup */ cmdp = find_ctab (allowed_master_remote_cmds, "CONTINUE"); } } else stat = SCPE_REMOTE; /* force processing outside of sim_instr() */ } } } else stat = SCPE_INVREM; } sim_rem_active_number = -1; if ((stat != SCPE_OK) && (stat != SCPE_REMOTE)) stat = _sim_rem_message (gbuf, stat); _sim_rem_log_out (lp); if (master_session && !sim_rem_master_mode) { sim_rem_single_mode[i] = TRUE; return SCPE_STOP; } if (cmdp && (cmdp->action == &x_continue_cmd)) { sim_rem_cmd_active_line = -1; /* Not active_command */ if (sim_log_temp && /* If we setup a temporary log, clean it now */ (!sim_rem_master_mode)) { int32 save_quiet = sim_quiet; sim_quiet = 1; sim_set_logoff (0, NULL); sim_quiet = save_quiet; remove (sim_rem_con_temp_name); sim_log_temp = FALSE; } else { fflush (sim_log); sim_rem_cmd_log_start = sim_ftell (sim_log); } if (!sim_rem_single_mode[i]) { tmxr_linemsg (lp, "Simulator Running..."); tmxr_send_buffered_data (lp); for (j=0; j < sim_rem_con_tmxr.lines; j++) { TMLN *lpj = &sim_rem_con_tmxr.ldsc[j]; if ((i == j) || (!lpj->conn)) continue; tmxr_linemsg (lpj, "Simulator Running..."); tmxr_send_buffered_data (lpj); } sim_is_running = 1; sim_start_timer_services (); } if (cmdp && (cmdp->action == &x_continue_cmd)) sim_rem_single_mode[i] = TRUE; else { if (!sim_rem_single_mode[i]) { if (master_session) tmxr_linemsgf (lp, "%s", "sim> "); else tmxr_linemsgf (lp, "%s", sim_prompt); tmxr_send_buffered_data (lp); } } break; } if ((cmdp && (cmdp->action == &x_step_cmd)) || (stat == SCPE_REMOTE)) { sim_rem_cmd_active_line = i; break; } } if (close_session) { tmxr_linemsgf (lp, "\r\nGoodbye\r\n"); tmxr_send_buffered_data (lp); /* flush any buffered data */ tmxr_reset_ln (lp); sim_rem_single_mode[i] = FALSE; } } if (sim_rem_master_was_connected && /* Master mode ever connected? */ !sim_rem_con_tmxr.ldsc[0].sock) /* Master Connection lost? */ return SCPE_EXIT; /* simulator has been 'unplugged' */ if (sim_rem_cmd_active_line != -1) { if (steps) sim_activate(uptr, steps); /* check again after 'steps' instructions */ else return SCPE_REMOTE; /* force sim_instr() to exit to process command */ } else sim_activate_after(uptr, 100000); /* check again in 100 milliaeconds */ if (sim_rem_master_was_enabled && !sim_rem_master_mode) {/* Transitioning out of master mode? */ lp = &sim_rem_con_tmxr.ldsc[0]; tmxr_linemsgf (lp, "Non Master Mode Session..."); /* report transition */ tmxr_send_buffered_data (lp); /* flush any buffered data */ return SCPE_STOP|SCPE_NOMESSAGE; /* Unwind to the normal input path */ } else return SCPE_OK; /* keep going */ } t_stat sim_rem_con_reset (DEVICE *dptr) { if (sim_rem_con_tmxr.lines) { int32 i; for (i=0; i<sim_rem_con_tmxr.lines; i++) if (sim_rem_con_tmxr.ldsc[i].conn) break; if (i != sim_rem_con_tmxr.lines) sim_activate_after (&dptr->units[1], 100000); /* continue polling for open sessions */ return sim_rem_con_poll_svc (&dptr->units[0]); /* establish polling as needed */ } return SCPE_OK; } static t_stat sim_set_rem_telnet (int32 flag, CONST char *cptr) { t_stat r; if (flag) { r = sim_parse_addr (cptr, NULL, 0, NULL, NULL, 0, NULL, NULL); if (r == SCPE_OK) { if (sim_rem_con_tmxr.master) /* already open? */ sim_set_rem_telnet (0, NULL); /* close first */ if (sim_rem_con_tmxr.lines == 0) /* Ir no connection limit set */ sim_set_rem_connections (0, "1"); /* use 1 */ sim_rem_con_tmxr.buffered = 8192; /* Use big enough buffers */ sim_register_internal_device (&sim_remote_console); r = tmxr_attach (&sim_rem_con_tmxr, &sim_rem_con_unit[0], cptr);/* open master socket */ if (r == SCPE_OK) sim_activate_after(&sim_rem_con_unit[0], 1000000); /* check for connection in 1 second */ return r; } return SCPE_NOPARAM; } else { if (sim_rem_con_tmxr.master) { int32 i; tmxr_detach (&sim_rem_con_tmxr, &sim_rem_con_unit[0]); for (i=0; i<sim_rem_con_tmxr.lines; i++) { free (sim_rem_buf[i]); sim_rem_buf[i] = NULL; sim_rem_buf_size[i] = 0; sim_rem_buf_ptr[i] = 0; sim_rem_single_mode[i] = TRUE; } } } return SCPE_OK; } static t_stat sim_set_rem_connections (int32 flag, CONST char *cptr) { int32 lines; t_stat r; int32 i; if (cptr == NULL) return SCPE_ARG; lines = (int32) get_uint (cptr, 10, MAX_REMOTE_SESSIONS, &r); if (r != SCPE_OK) return r; if (sim_rem_con_tmxr.master) return SCPE_ARG; for (i=0; i<sim_rem_con_tmxr.lines; i++) free (sim_rem_buf[i]); sim_rem_con_tmxr.lines = lines; sim_rem_con_tmxr.ldsc = (TMLN *)realloc (sim_rem_con_tmxr.ldsc, sizeof(*sim_rem_con_tmxr.ldsc)*lines); memset (sim_rem_con_tmxr.ldsc, 0, sizeof(*sim_rem_con_tmxr.ldsc)*lines); sim_rem_buf = (char **)realloc (sim_rem_buf, sizeof(*sim_rem_buf)*lines); memset (sim_rem_buf, 0, sizeof(*sim_rem_buf)*lines); sim_rem_buf_size = (int32 *)realloc (sim_rem_buf_size, sizeof(*sim_rem_buf_size)*lines); memset (sim_rem_buf_size, 0, sizeof(*sim_rem_buf_size)*lines); sim_rem_buf_ptr = (int32 *)realloc (sim_rem_buf_ptr, sizeof(*sim_rem_buf_ptr)*lines); memset (sim_rem_buf_ptr, 0, sizeof(*sim_rem_buf_ptr)*lines); sim_rem_single_mode = (t_bool *)realloc (sim_rem_single_mode, sizeof(*sim_rem_single_mode)*lines); memset (sim_rem_single_mode, 0, sizeof(*sim_rem_single_mode)*lines); sim_rem_read_timeouts = (uint32 *)realloc (sim_rem_read_timeouts, sizeof(*sim_rem_read_timeouts)*lines); memset (sim_rem_read_timeouts, 0, sizeof(*sim_rem_read_timeouts)*lines); sim_rem_command_buf = (char *)realloc (sim_rem_command_buf, 4*CBUFSIZE+1); memset (sim_rem_command_buf, 0, 4*CBUFSIZE+1); return SCPE_OK; } static t_stat sim_set_rem_timeout (int32 flag, CONST char *cptr) { int32 timeout; t_stat r; if (cptr == NULL) return SCPE_ARG; timeout = (int32) get_uint (cptr, 10, 3600, &r); if (r != SCPE_OK) return r; if (sim_rem_active_number >= 0) sim_rem_read_timeouts[sim_rem_active_number] = timeout; else sim_rem_read_timeout = timeout; return SCPE_OK; } static t_stat sim_set_rem_bufsize (int32 flag, CONST char *cptr) { char cmdbuf[CBUFSIZE]; int32 bufsize; t_stat r; if (cptr == NULL) return SCPE_ARG; bufsize = (int32) get_uint (cptr, 10, 32768, &r); if (r != SCPE_OK) return r; if (bufsize < 1400) return sim_messagef (SCPE_ARG, "%d is too small. Minimum size is 1400\n", bufsize); sprintf(cmdbuf, "BUFFERED=%d", bufsize); return tmxr_open_master (&sim_rem_con_tmxr, cmdbuf); /* open master socket */ } /* Enable or disable Remote Console master mode */ /* In master mode, commands are subsequently processed from the primary/initial (master mode) remote console session. Commands are processed from that source until that source disables master mode or the simulator exits */ static t_stat sim_set_rem_master (int32 flag, CONST char *cptr) { t_stat stat = SCPE_OK; if (cptr && *cptr) return SCPE_2MARG; if (sim_rem_active_number > 0) { sim_printf ("Can't change Remote Console mode from Remote Console\n"); return SCPE_INVREM; } if (sim_rem_con_tmxr.master || (!flag)) /* Remote Console Enabled? */ sim_rem_master_mode = flag; else { sim_printf ("Can't enable Remote Console Master mode with Remote Console disabled\n"); return SCPE_INVREM; } if (sim_rem_master_mode) { t_stat stat_nomessage; sim_printf ("Command input starting on Master Remote Console Session\n"); stat = sim_run_boot_prep (0); sim_rem_master_was_enabled = TRUE; while (sim_rem_master_mode) { sim_rem_single_mode[0] = FALSE; sim_cancel (&sim_rem_con_unit[1]); sim_activate (&sim_rem_con_unit[1], -1); stat = run_cmd (RU_GO, ""); if (stat != SCPE_TTMO) { stat_nomessage = stat & SCPE_NOMESSAGE; /* extract possible message supression flag */ stat = _sim_rem_message ("RUN", stat); } if (stat == SCPE_EXIT) sim_rem_master_mode = FALSE; } sim_rem_master_was_enabled = FALSE; sim_rem_master_was_connected = FALSE; if (sim_log_temp) { /* If we setup a temporary log, clean it now */ int32 save_quiet = sim_quiet; sim_quiet = 1; sim_set_logoff (0, NULL); sim_quiet = save_quiet; remove (sim_rem_con_temp_name); sim_log_temp = FALSE; } stat |= stat_nomessage; } else { sim_rem_single_mode[0] = TRUE; /* Force remote session into single command mode */ } return stat; } /* Set keyboard map */ t_stat sim_set_kmap (int32 flag, CONST char *cptr) { DEVICE *dptr = sim_devices[0]; int32 val, rdx; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; if (dptr->dradix == 16) rdx = 16; else rdx = 8; val = (int32) get_uint (cptr, rdx, 0177, &r); if ((r != SCPE_OK) || ((val == 0) && (flag & KMAP_NZ))) return SCPE_ARG; *(cons_kmap[flag & KMAP_MASK]) = val; return SCPE_OK; } /* Show keyboard map */ t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { if (sim_devices[0]->dradix == 16) fprintf (st, "%s = %X\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); else fprintf (st, "%s = %o\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); return SCPE_OK; } /* Set printable characters */ t_stat sim_set_pchar (int32 flag, CONST char *cptr) { DEVICE *dptr = sim_devices[0]; uint32 val, rdx; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; if (dptr->dradix == 16) rdx = 16; else rdx = 8; val = (uint32) get_uint (cptr, rdx, 0xFFFFFFFF, &r); if ((r != SCPE_OK) || ((val & 0x00002400) == 0)) return SCPE_ARG; sim_tt_pchar = val; return SCPE_OK; } /* Show printable characters */ t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { if (sim_devices[0]->dradix == 16) fprintf (st, "pchar mask = %X", sim_tt_pchar); else fprintf (st, "pchar mask = %o", sim_tt_pchar); if (sim_tt_pchar) { static const char *pchars[] = {"NUL(^@)", "SOH(^A)", "STX(^B)", "ETX(^C)", "EOT(^D)", "ENQ(^E)", "ACK(^F)", "BEL(^G)", "BS(^H)" , "HT(^I)", "LF(^J)", "VT(^K)", "FF(^L)", "CR(^M)", "SO(^N)", "SI(^O)", "DLE(^P)", "DC1(^Q)", "DC2(^R)", "DC3(^S)", "DC4(^T)", "NAK(^U)", "SYN(^V)", "ETB(^W)", "CAN(^X)", "EM(^Y)", "SUB(^Z)", "ESC", "FS", "GS", "RS", "US"}; int i; t_bool found = FALSE; fprintf (st, " {"); for (i=31; i>=0; i--) if (sim_tt_pchar & (1 << i)) { fprintf (st, "%s%s", found ? "," : "", pchars[i]); found = TRUE; } fprintf (st, "}"); } fprintf (st, "\n"); return SCPE_OK; } /* Set input speed (bps) */ t_stat sim_set_cons_speed (int32 flag, CONST char *cptr) { return tmxr_set_line_speed (&sim_con_ldsc, cptr); } t_stat sim_show_cons_speed (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { if (sim_con_ldsc.rxbps) { fprintf (st, "Speed = %d", sim_con_ldsc.rxbps); if (sim_con_ldsc.rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE) fprintf (st, "*%.0f", sim_con_ldsc.rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE); fprintf (st, " bps\n"); } return SCPE_OK; } /* Set log routine */ t_stat sim_set_logon (int32 flag, CONST char *cptr) { char gbuf[CBUFSIZE]; t_stat r; time_t now; if ((cptr == NULL) || (*cptr == 0)) /* need arg */ return SCPE_2FARG; cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ if (*cptr != 0) /* now eol? */ return SCPE_2MARG; sim_set_logoff (0, NULL); /* close cur log */ r = sim_open_logfile (gbuf, FALSE, &sim_log, &sim_log_ref); /* open log */ if (r != SCPE_OK) /* error? */ return r; if (!sim_quiet) printf ("Logging to file \"%s\"\n", sim_logfile_name (sim_log, sim_log_ref)); fprintf (sim_log, "Logging to file \"%s\"\n", sim_logfile_name (sim_log, sim_log_ref)); /* start of log */ time(&now); fprintf (sim_log, "Logging to file \"%s\" at %s", sim_logfile_name (sim_log, sim_log_ref), ctime(&now)); return SCPE_OK; } /* Set nolog routine */ t_stat sim_set_logoff (int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) /* now eol? */ return SCPE_2MARG; if (sim_log == NULL) /* no log? */ return SCPE_OK; if (!sim_quiet) printf ("Log file closed\n"); fprintf (sim_log, "Log file closed\n"); sim_close_logfile (&sim_log_ref); /* close log */ sim_log = NULL; return SCPE_OK; } /* Show log status */ t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_log) fprintf (st, "Logging enabled to \"%s\"\n", sim_logfile_name (sim_log, sim_log_ref)); else fprintf (st, "Logging disabled\n"); return SCPE_OK; } /* Set debug routine */ t_stat sim_set_debon (int32 flag, CONST char *cptr) { char gbuf[CBUFSIZE]; t_stat r; time_t now; sim_deb_switches = sim_switches; /* save debug switches */ if ((cptr == NULL) || (*cptr == 0)) /* need arg */ return SCPE_2FARG; cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ if (*cptr != 0) /* now eol? */ return SCPE_2MARG; r = sim_open_logfile (gbuf, FALSE, &sim_deb, &sim_deb_ref); if (r != SCPE_OK) return r; if (sim_deb_switches & SWMASK ('R')) { clock_gettime(CLOCK_REALTIME, &sim_deb_basetime); if (!(sim_deb_switches & (SWMASK ('A') | SWMASK ('T')))) sim_deb_switches |= SWMASK ('T'); } if (!sim_quiet) { sim_printf ("Debug output to \"%s\"\n", sim_logfile_name (sim_deb, sim_deb_ref)); if (sim_deb_switches & SWMASK ('P')) sim_printf (" Debug messages contain current PC value\n"); if (sim_deb_switches & SWMASK ('T')) sim_printf (" Debug messages display time of day as hh:mm:ss.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : ""); if (sim_deb_switches & SWMASK ('A')) sim_printf (" Debug messages display time of day as seconds.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : ""); time(&now); fprintf (sim_deb, "Debug output to \"%s\" at %s", sim_logfile_name (sim_deb, sim_deb_ref), ctime(&now)); show_version (sim_deb, NULL, NULL, 0, NULL); } if (sim_deb_switches & SWMASK ('N')) sim_deb_switches &= ~SWMASK ('N'); /* Only process the -N flag initially */ return SCPE_OK; } t_stat sim_debug_flush (void) { int32 saved_quiet = sim_quiet; int32 saved_sim_switches = sim_switches; int32 saved_deb_switches = sim_deb_switches; struct timespec saved_deb_basetime = sim_deb_basetime; char saved_debug_filename[CBUFSIZE]; if (sim_deb == NULL) /* no debug? */ return SCPE_OK; if (sim_deb == sim_log) { /* debug is log */ fflush (sim_deb); /* fflush is the best we can do */ return SCPE_OK; } strcpy (saved_debug_filename, sim_logfile_name (sim_deb, sim_deb_ref)); sim_quiet = 1; sim_set_deboff (0, NULL); sim_switches = saved_deb_switches; sim_set_debon (0, saved_debug_filename); sim_deb_basetime = saved_deb_basetime; sim_switches = saved_sim_switches; sim_quiet = saved_quiet; return SCPE_OK; } /* Set nodebug routine */ t_stat sim_set_deboff (int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) /* now eol? */ return SCPE_2MARG; if (sim_deb == NULL) /* no debug? */ return SCPE_OK; sim_close_logfile (&sim_deb_ref); sim_deb = NULL; sim_deb_switches = 0; if (!sim_quiet) sim_printf ("Debug output disabled\n"); return SCPE_OK; } /* Show debug routine */ t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { int32 i; if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_deb) { fprintf (st, "Debug output enabled to \"%s\"\n", sim_logfile_name (sim_deb, sim_deb_ref)); if (sim_deb_switches & SWMASK ('P')) fprintf (st, " Debug messages contain current PC value\n"); if (sim_deb_switches & SWMASK ('T')) fprintf (st, " Debug messages display time of day as hh:mm:ss.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : ""); if (sim_deb_switches & SWMASK ('A')) fprintf (st, " Debug messages display time of day as seconds.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : ""); for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { if (!(dptr->flags & DEV_DIS) && (dptr->flags & DEV_DEBUG) && (dptr->dctrl)) { fprintf (st, "Device: %-6s ", dptr->name); show_dev_debug (st, dptr, NULL, 0, NULL); } } for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) { if (!(dptr->flags & DEV_DIS) && (dptr->flags & DEV_DEBUG) && (dptr->dctrl)) { fprintf (st, "Device: %-6s ", dptr->name); show_dev_debug (st, dptr, NULL, 0, NULL); } } } else fprintf (st, "Debug output disabled\n"); return SCPE_OK; } /* SET CONSOLE command */ /* Set console to Telnet port (and parameters) */ t_stat sim_set_telnet (int32 flag, CONST char *cptr) { char *cvptr, gbuf[CBUFSIZE]; CTAB *ctptr; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; while (*cptr != 0) { /* do all mods */ cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */ if ((cvptr = strchr (gbuf, '='))) /* = value? */ *cvptr++ = 0; get_glyph (gbuf, gbuf, 0); /* modifier to UC */ if ((ctptr = find_ctab (set_con_telnet_tab, gbuf))) { /* match? */ r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ if (r != SCPE_OK) return r; } else { if (sim_con_tmxr.master) /* already open? */ sim_set_notelnet (0, NULL); /* close first */ r = tmxr_attach (&sim_con_tmxr, &sim_con_unit, gbuf);/* open master socket */ if (r == SCPE_OK) sim_activate_after(&sim_con_unit, 1000000); /* check for connection in 1 second */ else return r; } } return SCPE_OK; } /* Close console Telnet port */ t_stat sim_set_notelnet (int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) /* too many arguments? */ return SCPE_2MARG; if (sim_con_tmxr.master == 0) /* ignore if already closed */ return SCPE_OK; return tmxr_close_master (&sim_con_tmxr); /* close master socket */ } /* Show console Telnet status */ t_stat sim_show_telnet (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) return SCPE_2MARG; if ((sim_con_tmxr.master == 0) && (sim_con_ldsc.serport == 0)) fprintf (st, "Connected to console window\n"); else { if (sim_con_ldsc.serport) { fprintf (st, "Connected to "); tmxr_fconns (st, &sim_con_ldsc, -1); } else if (sim_con_ldsc.sock == 0) fprintf (st, "Listening on port %s\n", sim_con_tmxr.port); else { fprintf (st, "Listening on port %s, connection from %s\n", sim_con_tmxr.port, sim_con_ldsc.ipad); tmxr_fconns (st, &sim_con_ldsc, -1); } tmxr_fstats (st, &sim_con_ldsc, -1); } return SCPE_OK; } /* Set console to Buffering */ t_stat sim_set_cons_buff (int32 flg, CONST char *cptr) { char cmdbuf[CBUFSIZE]; sprintf(cmdbuf, "BUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ } /* Set console to NoBuffering */ t_stat sim_set_cons_unbuff (int32 flg, CONST char *cptr) { char cmdbuf[CBUFSIZE]; sprintf(cmdbuf, "UNBUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ } /* Set console to Logging */ t_stat sim_set_cons_log (int32 flg, CONST char *cptr) { char cmdbuf[CBUFSIZE]; sprintf(cmdbuf, "LOG%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ } /* Set console to NoLogging */ t_stat sim_set_cons_nolog (int32 flg, CONST char *cptr) { char cmdbuf[CBUFSIZE]; sprintf(cmdbuf, "NOLOG%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ } t_stat sim_show_cons_log (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_con_tmxr.ldsc->txlog) fprintf (st, "Log File being written to %s\n", sim_con_tmxr.ldsc->txlogname); else fprintf (st, "No Logging\n"); return SCPE_OK; } t_stat sim_show_cons_buff (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) return SCPE_2MARG; if (!sim_con_tmxr.ldsc->txbfd) fprintf (st, "Unbuffered\n"); else fprintf (st, "Buffer Size = %d\n", sim_con_tmxr.ldsc->txbsz); return SCPE_OK; } /* Set console Debug Mode */ t_stat sim_set_cons_debug (int32 flg, CONST char *cptr) { return set_dev_debug (&sim_con_telnet, &sim_con_unit, flg, cptr); } t_stat sim_show_cons_debug (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) return SCPE_2MARG; return show_dev_debug (st, &sim_con_telnet, &sim_con_unit, flag, cptr); } /* Set console to Serial port (and parameters) */ t_stat sim_set_serial (int32 flag, CONST char *cptr) { char *cvptr, gbuf[CBUFSIZE], ubuf[CBUFSIZE]; CTAB *ctptr; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; while (*cptr != 0) { /* do all mods */ cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */ if ((cvptr = strchr (gbuf, '='))) /* = value? */ *cvptr++ = 0; get_glyph (gbuf, ubuf, 0); /* modifier to UC */ if ((ctptr = find_ctab (set_con_serial_tab, ubuf))) { /* match? */ r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ if (r != SCPE_OK) return r; } else { SERHANDLE serport = sim_open_serial (gbuf, NULL, &r); if (serport != INVALID_HANDLE) { sim_close_serial (serport); if (r == SCPE_OK) { char cbuf[CBUFSIZE]; if ((sim_con_tmxr.master) || /* already open? */ (sim_con_ldsc.serport)) sim_set_noserial (0, NULL); /* close first */ sprintf(cbuf, "Connect=%s", gbuf); r = tmxr_attach (&sim_con_tmxr, &sim_con_unit, cbuf);/* open master socket */ sim_con_ldsc.rcve = 1; /* rcv enabled */ if (r == SCPE_OK) sim_activate_after(&sim_con_unit, 1000000); /* check for connection in 1 second */ return r; } } return SCPE_ARG; } } return SCPE_OK; } /* Close console Serial port */ t_stat sim_set_noserial (int32 flag, CONST char *cptr) { if (cptr && (*cptr != 0)) /* too many arguments? */ return SCPE_2MARG; if (sim_con_ldsc.serport == 0) /* ignore if already closed */ return SCPE_OK; return tmxr_close_master (&sim_con_tmxr); /* close master socket */ } /* Show the console expect rules and state */ t_stat sim_show_cons_expect (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr) { return sim_exp_show (st, &sim_con_expect, cptr); } /* Log File Open/Close/Show Support */ /* Open log file */ t_stat sim_open_logfile (const char *filename, t_bool binary, FILE **pf, FILEREF **pref) { char gbuf[CBUFSIZE]; const char *tptr; if ((filename == NULL) || (*filename == 0)) /* too few arguments? */ return SCPE_2FARG; tptr = get_glyph (filename, gbuf, 0); if (*tptr != 0) /* now eol? */ return SCPE_2MARG; sim_close_logfile (pref); *pf = NULL; if (strcmp (gbuf, "LOG") == 0) { /* output to log? */ if (sim_log == NULL) /* any log? */ return SCPE_ARG; *pf = sim_log; *pref = sim_log_ref; if (*pref) ++(*pref)->refcount; } else if (strcmp (gbuf, "DEBUG") == 0) { /* output to debug? */ if (sim_deb == NULL) /* any debug? */ return SCPE_ARG; *pf = sim_deb; *pref = sim_deb_ref; if (*pref) ++(*pref)->refcount; } else if (strcmp (gbuf, "STDOUT") == 0) { /* output to stdout? */ *pf = stdout; *pref = NULL; } else if (strcmp (gbuf, "STDERR") == 0) { /* output to stderr? */ *pf = stderr; *pref = NULL; } else { *pref = (FILEREF *)calloc (1, sizeof(**pref)); if (!*pref) return SCPE_MEM; get_glyph_nc (filename, gbuf, 0); /* reparse */ strncpy ((*pref)->name, gbuf, sizeof((*pref)->name)-1); if (sim_switches & SWMASK ('N')) /* if a new log file is requested */ *pf = sim_fopen (gbuf, (binary ? "w+b" : "w+"));/* then open an empty file */ else /* otherwise */ *pf = sim_fopen (gbuf, (binary ? "a+b" : "a+"));/* append to an existing file */ if (*pf == NULL) { /* error? */ free (*pref); *pref = NULL; return SCPE_OPENERR; } setvbuf (*pf, NULL, _IOFBF, 65536); (*pref)->file = *pf; (*pref)->refcount = 1; /* need close */ } return SCPE_OK; } /* Close log file */ t_stat sim_close_logfile (FILEREF **pref) { if (NULL == *pref) return SCPE_OK; (*pref)->refcount = (*pref)->refcount - 1; if ((*pref)->refcount > 0) { *pref = NULL; return SCPE_OK; } fclose ((*pref)->file); free (*pref); *pref = NULL; return SCPE_OK; } /* Show logfile support routine */ const char *sim_logfile_name (FILE *st, FILEREF *ref) { if (!st) return ""; if (st == stdout) return "STDOUT"; if (st == stderr) return "STDERR"; if (!ref) return ""; return ref->name; } /* Check connection before executing (including a remote console which may be required in master mode) */ t_stat sim_check_console (int32 sec) { int32 c, trys = 0; if (sim_rem_master_mode) { for (;trys < sec; ++trys) { sim_rem_con_poll_svc (&sim_rem_con_unit[0]); if (sim_rem_con_tmxr.ldsc[0].conn) break; if ((trys % 10) == 0) { /* Status every 10 sec */ sim_printf ("Waiting for Remote Console connection\r\n"); fflush (stdout); if (sim_log) /* log file? */ fflush (sim_log); } sim_os_sleep (1); /* wait 1 second */ } if ((sim_rem_con_tmxr.ldsc[0].conn) && (!sim_con_ldsc.serport) && (sim_con_tmxr.master == 0) && (sim_con_console_port)) { tmxr_linemsgf (&sim_rem_con_tmxr.ldsc[0], "\r\nConsole port must be Telnet or Serial with Master Remote Console\r\n"); tmxr_linemsgf (&sim_rem_con_tmxr.ldsc[0], "Goodbye\r\n"); while (tmxr_send_buffered_data (&sim_rem_con_tmxr.ldsc[0])) sim_os_ms_sleep (100); sim_os_ms_sleep (100); tmxr_reset_ln (&sim_rem_con_tmxr.ldsc[0]); sim_printf ("Console port must be Telnet or Serial with Master Remote Console\r\n"); return SCPE_EXIT; } } if (trys == sec) { return SCPE_TTMO; /* timed out */ } if (sim_con_ldsc.serport) if (tmxr_poll_conn (&sim_con_tmxr) >= 0) sim_con_ldsc.rcve = 1; /* rcv enabled */ if ((sim_con_tmxr.master == 0) || /* serial console or not Telnet? done */ (sim_con_ldsc.serport)) return SCPE_OK; if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) { /* connected or buffered ? */ tmxr_poll_rx (&sim_con_tmxr); /* poll (check disconn) */ if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) { /* still connected? */ if (!sim_con_ldsc.conn) { sim_printf ("Running with Buffered Console\r\n"); /* print transition */ fflush (stdout); if (sim_log) /* log file? */ fflush (sim_log); } return SCPE_OK; } } for (; trys < sec; trys++) { /* loop */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) { /* poll connect */ sim_con_ldsc.rcve = 1; /* rcv enabled */ if (trys) { /* if delayed */ sim_printf ("Running\r\n"); /* print transition */ fflush (stdout); if (sim_log) /* log file? */ fflush (sim_log); } return SCPE_OK; /* ready to proceed */ } c = sim_os_poll_kbd (); /* check for stop char */ if ((c == SCPE_STOP) || stop_cpu) return SCPE_STOP; if ((trys % 10) == 0) { /* Status every 10 sec */ sim_printf ("Waiting for console Telnet connection\r\n"); fflush (stdout); if (sim_log) /* log file? */ fflush (sim_log); } sim_os_sleep (1); /* wait 1 second */ } return SCPE_TTMO; /* timed out */ } /* Get Send object address for console */ SEND *sim_cons_get_send (void) { return &sim_con_send; } /* Get Expect object address for console */ EXPECT *sim_cons_get_expect (void) { return &sim_con_expect; } /* Display console Queued input data status */ t_stat sim_show_cons_send_input (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { return sim_show_send_input (st, &sim_con_send); } /* Poll for character */ t_stat sim_poll_kbd (void) { t_stat c; if (sim_send_poll_data (&sim_con_send, &c)) /* injected input characters available? */ return c; if (!sim_rem_master_mode) { if ((sim_con_ldsc.rxbps) && /* rate limiting && */ (sim_gtime () < sim_con_ldsc.rxnexttime)) /* too soon? */ return SCPE_OK; /* not yet */ c = sim_os_poll_kbd (); /* get character */ if (c == SCPE_STOP) { /* ^E */ stop_cpu = 1; /* Force a stop (which is picked up by sim_process_event */ return SCPE_OK; } if ((sim_con_tmxr.master == 0) && /* not Telnet? */ (sim_con_ldsc.serport == 0)) { /* and not serial? */ if (c && sim_con_ldsc.rxbps) /* got something && rate limiting? */ sim_con_ldsc.rxnexttime = /* compute next input time */ floor (sim_gtime () + ((sim_con_ldsc.rxdelta * sim_timer_inst_per_sec ())/sim_con_ldsc.rxbpsfactor)); if (c) sim_debug (DBG_RCV, &sim_con_telnet, "sim_poll_kbd() returning: '%c' (0x%02X)\n", sim_isprint (c & 0xFF) ? c & 0xFF : '.', c); return c; /* in-window */ } if (!sim_con_ldsc.conn) { /* no telnet or serial connection? */ if (!sim_con_ldsc.txbfd) /* unbuffered? */ return SCPE_LOST; /* connection lost */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ sim_con_ldsc.rcve = 1; /* rcv enabled */ else /* fall through to poll reception */ return SCPE_OK; /* unconnected and buffered - nothing to receive */ } } tmxr_poll_rx (&sim_con_tmxr); /* poll for input */ if ((c = (t_stat)tmxr_getc_ln (&sim_con_ldsc))) /* any char? */ return (c & (SCPE_BREAK | 0377)) | SCPE_KFLAG; return SCPE_OK; } /* Output character */ t_stat sim_putchar (int32 c) { sim_exp_check (&sim_con_expect, c); if ((sim_con_tmxr.master == 0) && /* not Telnet? */ (sim_con_ldsc.serport == 0)) { /* and not serial port */ if (sim_log) /* log file? */ fputc (c, sim_log); sim_debug (DBG_XMT, &sim_con_telnet, "sim_putchar('%c' (0x%02X)\n", sim_isprint (c) ? c : '.', c); return sim_os_putchar (c); /* in-window version */ } if (!sim_con_ldsc.conn) { /* no Telnet or serial connection? */ if (!sim_con_ldsc.txbfd) /* unbuffered? */ return SCPE_LOST; /* connection lost */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ sim_con_ldsc.rcve = 1; /* rcv enabled */ } tmxr_putc_ln (&sim_con_ldsc, c); /* output char */ tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ return SCPE_OK; } t_stat sim_putchar_s (int32 c) { t_stat r; sim_exp_check (&sim_con_expect, c); if ((sim_con_tmxr.master == 0) && /* not Telnet? */ (sim_con_ldsc.serport == 0)) { /* and not serial port */ if (sim_log) /* log file? */ fputc (c, sim_log); sim_debug (DBG_XMT, &sim_con_telnet, "sim_putchar('%c' (0x%02X)\n", sim_isprint (c) ? c : '.', c); return sim_os_putchar (c); /* in-window version */ } if (!sim_con_ldsc.conn) { /* no Telnet or serial connection? */ if (!sim_con_ldsc.txbfd) /* non-buffered Telnet connection? */ return SCPE_LOST; /* lost */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ sim_con_ldsc.rcve = 1; /* rcv enabled */ } if (sim_con_ldsc.xmte == 0) /* xmt disabled? */ r = SCPE_STALL; else r = tmxr_putc_ln (&sim_con_ldsc, c); /* no, Telnet output */ tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ return r; /* return status */ } /* Input character processing */ int32 sim_tt_inpcvt (int32 c, uint32 mode) { uint32 md = mode & TTUF_M_MODE; if (md != TTUF_MODE_8B) { uint32 par_mode = (mode >> TTUF_W_MODE) & TTUF_M_PAR; static int32 nibble_even_parity = 0x699600; /* bit array indicating the even parity for each index (offset by 8) */ c = c & 0177; if (md == TTUF_MODE_UC) { if (islower (c)) c = toupper (c); if (mode & TTUF_KSR) c = c | 0200; } switch (par_mode) { case TTUF_PAR_EVEN: c |= (((nibble_even_parity >> ((c & 0xF) + 1)) ^ (nibble_even_parity >> (((c >> 4) & 0xF) + 1))) & 0x80); break; case TTUF_PAR_ODD: c |= ((~((nibble_even_parity >> ((c & 0xF) + 1)) ^ (nibble_even_parity >> (((c >> 4) & 0xF) + 1)))) & 0x80); break; case TTUF_PAR_MARK: c = c | 0x80; break; } } else c = c & 0377; return c; } /* Output character processing */ int32 sim_tt_outcvt (int32 c, uint32 mode) { uint32 md = mode & TTUF_M_MODE; if (md != TTUF_MODE_8B) { c = c & 0177; if (md == TTUF_MODE_UC) { if (islower (c)) c = toupper (c); if ((mode & TTUF_KSR) && (c >= 0140)) return -1; } if (((md == TTUF_MODE_UC) || (md == TTUF_MODE_7P)) && ((c == 0177) || ((c < 040) && !((sim_tt_pchar >> c) & 1)))) return -1; } else c = c & 0377; return c; } /* Tab stop array handling *desc points to a uint8 array of length val Columns with tabs set are non-zero; columns without tabs are 0 */ t_stat sim_tt_settabs (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { uint8 *temptabs, *tabs = (uint8 *) desc; int32 i, d; t_stat r; char gbuf[CBUFSIZE]; if ((cptr == NULL) || (tabs == NULL) || (val <= 1)) return SCPE_IERR; if (*cptr == 0) return SCPE_2FARG; if ((temptabs = (uint8 *)malloc (val)) == NULL) return SCPE_MEM; for (i = 0; i < val; i++) temptabs[i] = 0; do { cptr = get_glyph (cptr, gbuf, ';'); d = (int32)get_uint (gbuf, 10, val, &r); if ((r != SCPE_OK) || (d == 0)) { free (temptabs); return SCPE_ARG; } temptabs[d - 1] = 1; } while (*cptr != 0); for (i = 0; i < val; i++) tabs[i] = temptabs[i]; free (temptabs); return SCPE_OK; } t_stat sim_tt_showtabs (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { const uint8 *tabs = (const uint8 *) desc; int32 i, any; if ((st == NULL) || (val == 0) || (desc == NULL)) return SCPE_IERR; for (i = any = 0; i < val; i++) { if (tabs[i] != 0) { fprintf (st, (any? ";%d": "%d"), i + 1); any = 1; } } fprintf (st, (any? "\n": "no tabs set\n")); return SCPE_OK; } #if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX) extern pthread_mutex_t sim_tmxr_poll_lock; extern pthread_cond_t sim_tmxr_poll_cond; extern int32 sim_tmxr_poll_count; extern t_bool sim_tmxr_poll_running; extern int32 sim_is_running; pthread_t sim_console_poll_thread; /* Keyboard Polling Thread Id */ t_bool sim_console_poll_running = FALSE; pthread_cond_t sim_console_startup_cond; static void * _console_poll(void *arg) { int wait_count = 0; DEVICE *d; /* Boost Priority for this I/O thread vs the CPU instruction execution thread which, in general, won't be readily yielding the processor when this thread needs to run */ sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - starting\n"); pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_cond_signal (&sim_console_startup_cond); /* Signal we're ready to go */ while (sim_asynch_enabled) { if (!sim_is_running) { if (wait_count) { sim_debug (DBG_ASY, d, "_console_poll() - Removing interest in %s. Other interest: %d\n", d->name, sim_con_ldsc.uptr->a_poll_waiter_count); --sim_con_ldsc.uptr->a_poll_waiter_count; --sim_tmxr_poll_count; } break; } /* If we started something, let it finish before polling again */ if (wait_count) { sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - waiting for %d units\n", wait_count); pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock); sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - continuing with after wait\n"); } pthread_mutex_unlock (&sim_tmxr_poll_lock); wait_count = 0; if (sim_os_poll_kbd_ready (1000)) { sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - Keyboard Data available\n"); pthread_mutex_lock (&sim_tmxr_poll_lock); ++wait_count; if (!sim_con_ldsc.uptr->a_polling_now) { sim_con_ldsc.uptr->a_polling_now = TRUE; sim_con_ldsc.uptr->a_poll_waiter_count = 1; d = find_dev_from_unit(sim_con_ldsc.uptr); sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - Activating %s\n", d->name); pthread_mutex_unlock (&sim_tmxr_poll_lock); _sim_activate (sim_con_ldsc.uptr, 0); pthread_mutex_lock (&sim_tmxr_poll_lock); } else { d = find_dev_from_unit(sim_con_ldsc.uptr); sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - Already Activated %s %d times\n", d->name, sim_con_ldsc.uptr->a_poll_waiter_count); ++sim_con_ldsc.uptr->a_poll_waiter_count; } } else pthread_mutex_lock (&sim_tmxr_poll_lock); sim_tmxr_poll_count += wait_count; } pthread_mutex_unlock (&sim_tmxr_poll_lock); sim_debug (DBG_ASY, &sim_con_telnet, "_console_poll() - exiting\n"); return NULL; } #endif /* defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX) */ t_stat sim_ttinit (void) { sim_con_tmxr.ldsc->mp = &sim_con_tmxr; sim_register_internal_device (&sim_con_telnet); tmxr_startup (); return sim_os_ttinit (); } t_stat sim_ttrun (void) { if (!sim_con_tmxr.ldsc->uptr) { /* If simulator didn't declare its input polling unit */ sim_con_unit.dynflags &= ~UNIT_TM_POLL; /* we can't poll asynchronously */ sim_con_unit.dynflags |= TMUF_NOASYNCH; /* disable asynchronous behavior */ } else { #if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX) if (sim_asynch_enabled) { sim_con_tmxr.ldsc->uptr->dynflags |= UNIT_TM_POLL;/* flag console input device as a polling unit */ sim_con_unit.dynflags |= UNIT_TM_POLL; /* flag as polling unit */ } #endif } #if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX) pthread_mutex_lock (&sim_tmxr_poll_lock); if (sim_asynch_enabled) { pthread_attr_t attr; pthread_cond_init (&sim_console_startup_cond, NULL); pthread_attr_init (&attr); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); pthread_create (&sim_console_poll_thread, &attr, _console_poll, NULL); pthread_attr_destroy( &attr); pthread_cond_wait (&sim_console_startup_cond, &sim_tmxr_poll_lock); /* Wait for thread to stabilize */ pthread_cond_destroy (&sim_console_startup_cond); sim_console_poll_running = TRUE; } pthread_mutex_unlock (&sim_tmxr_poll_lock); #endif tmxr_start_poll (); return sim_os_ttrun (); } t_stat sim_ttcmd (void) { #if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX) pthread_mutex_lock (&sim_tmxr_poll_lock); if (sim_console_poll_running) { pthread_cond_signal (&sim_tmxr_poll_cond); pthread_mutex_unlock (&sim_tmxr_poll_lock); pthread_join (sim_console_poll_thread, NULL); sim_console_poll_running = FALSE; } else pthread_mutex_unlock (&sim_tmxr_poll_lock); #endif tmxr_stop_poll (); return sim_os_ttcmd (); } t_stat sim_ttclose (void) { tmxr_shutdown (); return sim_os_ttclose (); } t_bool sim_ttisatty (void) { return sim_os_ttisatty (); } /* Platform specific routine definitions */ /* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */ #if defined (VMS) #if defined(__VAX) #define sys$assign SYS$ASSIGN #define sys$qiow SYS$QIOW #define sys$dassgn SYS$DASSGN #endif #include <descrip.h> #include <ttdef.h> #include <tt2def.h> #include <iodef.h> #include <ssdef.h> #include <starlet.h> #include <unistd.h> #define EFN 0 uint32 tty_chan = 0; int buffered_character = 0; typedef struct { unsigned short sense_count; unsigned char sense_first_char; unsigned char sense_reserved; unsigned int stat; unsigned int stat2; } SENSE_BUF; typedef struct { unsigned short status; unsigned short count; unsigned int dev_status; } IOSB; SENSE_BUF cmd_mode = { 0 }; SENSE_BUF run_mode = { 0 }; static t_stat sim_os_ttinit (void) { unsigned int status; IOSB iosb; $DESCRIPTOR (terminal_device, "tt"); status = sys$assign (&terminal_device, &tty_chan, 0, 0); if (status != SS$_NORMAL) return SCPE_TTIERR; status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE, &iosb, 0, 0, &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; run_mode = cmd_mode; run_mode.stat = cmd_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC); run_mode.stat2 = cmd_mode.stat2 | TT2$M_PASTHRU; return SCPE_OK; } static t_stat sim_os_ttrun (void) { unsigned int status; IOSB iosb; status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, &run_mode, sizeof (run_mode), 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; return SCPE_OK; } static t_stat sim_os_ttcmd (void) { unsigned int status; IOSB iosb; status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; return SCPE_OK; } static t_stat sim_os_ttclose (void) { sim_ttcmd (); sys$dassgn (tty_chan); return SCPE_OK; } static t_bool sim_os_ttisatty (void) { return isatty (fileno (stdin)); } static t_stat sim_os_poll_kbd_data (void) { unsigned int status, term[2]; unsigned char buf[4]; IOSB iosb; SENSE_BUF sense; term[0] = 0; term[1] = 0; status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb, 0, 0, &sense, 8, 0, term, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; if (sense.sense_count == 0) return SCPE_OK; term[0] = 0; term[1] = 0; status = sys$qiow (EFN, tty_chan, IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, &iosb, 0, 0, buf, 1, 0, term, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_OK; if (buf[0] == sim_int_char) return SCPE_STOP; if (sim_brk_char && (buf[0] == sim_brk_char)) return SCPE_BREAK; return (buf[0] | SCPE_KFLAG); } static t_stat sim_os_poll_kbd (void) { t_stat response; if (response = buffered_character) { buffered_character = 0; return response; } return sim_os_poll_kbd_data (); } static t_bool sim_os_poll_kbd_ready (int ms_timeout) { unsigned int status, term[2]; unsigned char buf[4]; IOSB iosb; term[0] = 0; term[1] = 0; status = sys$qiow (EFN, tty_chan, IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, &iosb, 0, 0, buf, 1, (ms_timeout+999)/1000, term, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return FALSE; if (buf[0] == sim_int_char) buffered_character = SCPE_STOP; else if (sim_brk_char && (buf[0] == sim_brk_char)) buffered_character = SCPE_BREAK; else buffered_character = (buf[0] | SCPE_KFLAG); return TRUE; } static t_stat sim_os_putchar (int32 out) { unsigned int status; char c; IOSB iosb; c = out; status = sys$qiow (EFN, tty_chan, IO$_WRITELBLK | IO$M_NOFORMAT, &iosb, 0, 0, &c, 1, 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTOERR; return SCPE_OK; } /* Win32 routines */ #elif defined (_WIN32) #include <fcntl.h> #include <io.h> #include <windows.h> #define RAW_MODE 0 static HANDLE std_input; static HANDLE std_output; static DWORD saved_input_mode; static DWORD saved_output_mode; #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT #define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200 #endif #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif /* Note: This routine catches all the potential events which some aspect of the windows system can generate. The CTRL_C_EVENT won't be generated by a user typing in a console session since that session is in RAW mode. In general, Ctrl-C on a simulator's console terminal is a useful character to be passed to the simulator. This code does nothing to disable or affect that. */ static BOOL WINAPI ControlHandler(DWORD dwCtrlType) { DWORD Mode; extern void int_handler (int sig); switch (dwCtrlType) { case CTRL_BREAK_EVENT: // Use CTRL-Break or CTRL-C to simulate case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode int_handler(0); return TRUE; case CTRL_CLOSE_EVENT: // Window is Closing case CTRL_LOGOFF_EVENT: // User is logging off if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &Mode)) return TRUE; // Not our User, so ignore case CTRL_SHUTDOWN_EVENT: // System is shutting down int_handler(0); return TRUE; } return FALSE; } static t_stat sim_os_ttinit (void) { SetConsoleCtrlHandler( ControlHandler, TRUE ); std_input = GetStdHandle (STD_INPUT_HANDLE); std_output = GetStdHandle (STD_OUTPUT_HANDLE); if ((std_input) && /* Not Background process? */ (std_input != INVALID_HANDLE_VALUE)) GetConsoleMode (std_input, &saved_input_mode); /* Save Input Mode */ if ((std_output) && /* Not Background process? */ (std_output != INVALID_HANDLE_VALUE)) GetConsoleMode (std_output, &saved_output_mode); /* Save Output Mode */ return SCPE_OK; } static t_stat sim_os_ttrun (void) { if ((std_input) && /* If Not Background process? */ (std_input != INVALID_HANDLE_VALUE)) { if (!GetConsoleMode(std_input, &saved_input_mode)) return SCPE_TTYERR; if ((!SetConsoleMode(std_input, ENABLE_VIRTUAL_TERMINAL_INPUT)) && (!SetConsoleMode(std_input, RAW_MODE))) return SCPE_TTYERR; } if ((std_output) && /* If Not Background process? */ (std_output != INVALID_HANDLE_VALUE)) { if (GetConsoleMode(std_output, &saved_output_mode)) SetConsoleMode(std_output, ENABLE_VIRTUAL_TERMINAL_PROCESSING|ENABLE_PROCESSED_OUTPUT); } if (sim_log) { fflush (sim_log); _setmode (_fileno (sim_log), _O_BINARY); } sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL); return SCPE_OK; } static t_stat sim_os_ttcmd (void) { if (sim_log) { fflush (sim_log); _setmode (_fileno (sim_log), _O_TEXT); } sim_os_set_thread_priority (PRIORITY_NORMAL); if ((std_input) && /* If Not Background process? */ (std_input != INVALID_HANDLE_VALUE) && (!SetConsoleMode(std_input, saved_input_mode))) /* Restore Normal mode */ return SCPE_TTYERR; if ((std_output) && /* If Not Background process? */ (std_output != INVALID_HANDLE_VALUE) && (!SetConsoleMode(std_output, saved_output_mode))) /* Restore Normal mode */ return SCPE_TTYERR; return SCPE_OK; } static t_stat sim_os_ttclose (void) { return SCPE_OK; } static t_bool sim_os_ttisatty (void) { DWORD Mode; return (std_input) && (std_input != INVALID_HANDLE_VALUE) && GetConsoleMode (std_input, &Mode); } static t_stat sim_os_poll_kbd (void) { int c = -1; DWORD nkbevents, nkbevent; INPUT_RECORD rec; sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_poll_kbd()\n"); if ((std_input == NULL) || /* No keyboard for */ (std_input == INVALID_HANDLE_VALUE)) /* background processes */ return SCPE_OK; if (!GetNumberOfConsoleInputEvents(std_input, &nkbevents)) return SCPE_TTYERR; while (c == -1) { if (0 == nkbevents) return SCPE_OK; if (!ReadConsoleInput(std_input, &rec, 1, &nkbevent)) return SCPE_TTYERR; if (0 == nkbevent) return SCPE_OK; --nkbevents; if (rec.EventType == KEY_EVENT) { if (rec.Event.KeyEvent.bKeyDown) { if (0 == rec.Event.KeyEvent.uChar.UnicodeChar) { /* Special Character/Keys? */ if (rec.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) /* Pause/Break Key */ c = sim_brk_char | SCPE_BREAK; else if (rec.Event.KeyEvent.wVirtualKeyCode == '2') /* ^@ */ c = 0; /* return NUL */ } else c = rec.Event.KeyEvent.uChar.AsciiChar; } } } if ((c & 0177) == sim_del_char) c = 0177; if ((c & 0177) == sim_int_char) return SCPE_STOP; if ((sim_brk_char && ((c & 0177) == sim_brk_char)) || (c & SCPE_BREAK)) return SCPE_BREAK; return c | SCPE_KFLAG; } static t_bool sim_os_poll_kbd_ready (int ms_timeout) { sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_poll_kbd_ready()\n"); if ((std_input == NULL) || /* No keyboard for */ (std_input == INVALID_HANDLE_VALUE)) { /* background processes */ Sleep (ms_timeout); return FALSE; } return (WAIT_OBJECT_0 == WaitForSingleObject (std_input, ms_timeout)); } #define BELL_CHAR 7 /* Bell Character */ #define BELL_INTERVAL_MS 500 /* No more than 2 Bell Characters Per Second */ #define ESC_CHAR 033 /* Escape Character */ #define CSI_CHAR 0233 /* Control Sequence Introducer */ #define NUL_CHAR 0000 /* NUL character */ #define ESC_HOLD_USEC_DELAY 8000 /* Escape hold interval */ #define ESC_HOLD_MAX 32 /* Maximum Escape hold buffer */ static uint8 out_buf[ESC_HOLD_MAX]; /* Buffered characters pending output */ static int32 out_ptr = 0; static t_stat sim_out_hold_svc (UNIT *uptr) { DWORD unused; WriteConsoleA(std_output, out_buf, out_ptr, &unused, NULL); out_ptr = 0; return SCPE_OK; } #define out_hold_unit sim_con_units[1] static t_stat sim_os_putchar (int32 c) { DWORD unused; uint32 now; static uint32 last_bell_time; if (c != 0177) { switch (c) { case BELL_CHAR: now = sim_os_msec (); if ((now - last_bell_time) > BELL_INTERVAL_MS) { WriteConsoleA(std_output, &c, 1, &unused, NULL); last_bell_time = now; } break; case NUL_CHAR: break; case CSI_CHAR: case ESC_CHAR: if (out_ptr) { WriteConsoleA(std_output, out_buf, out_ptr, &unused, NULL); out_ptr = 0; sim_cancel (&out_hold_unit); } out_buf[out_ptr++] = (uint8)c; sim_activate_after (&out_hold_unit, ESC_HOLD_USEC_DELAY); out_hold_unit.action = &sim_out_hold_svc; break; default: if (out_ptr) { if (out_ptr >= ESC_HOLD_MAX) { /* Stop buffering if full */ WriteConsoleA(std_output, out_buf, out_ptr, &unused, NULL); out_ptr = 0; WriteConsoleA(std_output, &c, 1, &unused, NULL); } else out_buf[out_ptr++] = (uint8)c; } else WriteConsoleA(std_output, &c, 1, &unused, NULL); } } return SCPE_OK; } /* OS/2 routines, from Bruce Ray and Holger Veit */ #elif defined (__OS2__) #include <conio.h> static t_stat sim_os_ttinit (void) { return SCPE_OK; } static t_stat sim_os_ttrun (void) { return SCPE_OK; } static t_stat sim_os_ttcmd (void) { return SCPE_OK; } static t_stat sim_os_ttclose (void) { return SCPE_OK; } static t_bool sim_os_ttisatty (void) { return 1; } static t_stat sim_os_poll_kbd (void) { int c; #if defined (__EMX__) switch (c = _read_kbd(0,0,0)) { /* EMX has _read_kbd */ case -1: /* no char*/ return SCPE_OK; case 0: /* char pending */ c = _read_kbd(0,1,0); break; default: /* got char */ break; } #else if (!kbhit ()) return SCPE_OK; c = getch(); #endif if ((c & 0177) == sim_del_char) c = 0177; if ((c & 0177) == sim_int_char) return SCPE_STOP; if (sim_brk_char && ((c & 0177) == sim_brk_char)) return SCPE_BREAK; return c | SCPE_KFLAG; } static t_bool sim_os_poll_kbd_ready (int ms_timeout) /* Don't know how to do this on this platform */ { sim_os_ms_sleep (MIN(20,ms_timeout)); /* Wait a little */ return TRUE; /* force a poll */ } static t_stat sim_os_putchar (int32 c) { if (c != 0177) { #if defined (__EMX__) putchar (c); #else putch (c); #endif fflush (stdout); } return SCPE_OK; } /* Metrowerks CodeWarrior Macintosh routines, from Louis Chretien and Peter Schorn */ #elif defined (__MWERKS__) && defined (macintosh) #include <console.h> #include <Mactypes.h> #include <string.h> #include <sioux.h> #include <unistd.h> #include <siouxglobals.h> #include <Traps.h> #include <LowMem.h> /* function prototypes */ Boolean SIOUXIsAppWindow(WindowPtr window); void SIOUXDoMenuChoice(long menuValue); void SIOUXUpdateMenuItems(void); void SIOUXUpdateScrollbar(void); int ps_kbhit(void); int ps_getch(void); extern pSIOUXWin SIOUXTextWindow; static CursHandle iBeamCursorH = NULL; /* contains the iBeamCursor */ static void updateCursor(void) { WindowPtr window; window = FrontWindow(); if (SIOUXIsAppWindow(window)) { GrafPtr savePort; Point localMouse; GetPort(&savePort); SetPort(window); #if TARGET_API_MAC_CARBON GetGlobalMouse(&localMouse); #else localMouse = LMGetMouseLocation(); #endif GlobalToLocal(&localMouse); if (PtInRect(localMouse, &(*SIOUXTextWindow->edit)->viewRect) && iBeamCursorH) { SetCursor(*iBeamCursorH); } else { SetCursor(&qd.arrow); } TEIdle(SIOUXTextWindow->edit); SetPort(savePort); } else { SetCursor(&qd.arrow); TEIdle(SIOUXTextWindow->edit); } return; } int ps_kbhit(void) { EventRecord event; int c; updateCursor(); SIOUXUpdateScrollbar(); while (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask | highLevelEventMask | diskEvt, &event)) { SIOUXHandleOneEvent(&event); } if (SIOUXQuitting) { exit(1); } if (EventAvail(keyDownMask,&event)) { c = event.message&charCodeMask; if ((event.modifiers & cmdKey) && (c > 0x20)) { GetNextEvent(keyDownMask, &event); SIOUXHandleOneEvent(&event); if (SIOUXQuitting) { exit(1); } return false; } return true; } else { return false; } } int ps_getch(void) { int c; EventRecord event; fflush(stdout); updateCursor(); while(!GetNextEvent(keyDownMask,&event)) { if (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask | highLevelEventMask | diskEvt, &event)) { SIOUXUpdateScrollbar(); SIOUXHandleOneEvent(&event); } } if (SIOUXQuitting) { exit(1); } c = event.message&charCodeMask; if ((event.modifiers & cmdKey) && (c > 0x20)) { SIOUXUpdateMenuItems(); SIOUXDoMenuChoice(MenuKey(c)); } if (SIOUXQuitting) { exit(1); } return c; } /* Note that this only works if the call to sim_ttinit comes before any output to the console */ static t_stat sim_os_ttinit (void) { int i; /* this blank will later be replaced by the number of characters */ char title[50] = " "; unsigned char ptitle[50]; SIOUXSettings.autocloseonquit = TRUE; SIOUXSettings.asktosaveonclose = FALSE; SIOUXSettings.showstatusline = FALSE; SIOUXSettings.columns = 80; SIOUXSettings.rows = 40; SIOUXSettings.toppixel = 42; SIOUXSettings.leftpixel = 6; iBeamCursorH = GetCursor(iBeamCursor); strcat(title, sim_name); strcat(title, " Simulator"); title[0] = strlen(title) - 1; /* Pascal string done */ for (i = 0; i <= title[0]; i++) { /* copy to unsigned char */ ptitle[i] = title[i]; } SIOUXSetTitle(ptitle); return SCPE_OK; } static t_stat sim_os_ttrun (void) { return SCPE_OK; } static t_stat sim_os_ttcmd (void) { return SCPE_OK; } static t_stat sim_os_ttclose (void) { return SCPE_OK; } static t_bool sim_os_ttisatty (void) { return 1; } static t_stat sim_os_poll_kbd (void) { int c; if (!ps_kbhit ()) return SCPE_OK; c = ps_getch(); if ((c & 0177) == sim_del_char) c = 0177; if ((c & 0177) == sim_int_char) return SCPE_STOP; if (sim_brk_char && ((c & 0177) == sim_brk_char)) return SCPE_BREAK; return c | SCPE_KFLAG; } static t_bool sim_os_poll_kbd_ready (int ms_timeout) /* Don't know how to do this on this platform */ { sim_os_ms_sleep (MIN(20,ms_timeout)); /* Wait a little */ return TRUE; /* force a poll */ } static t_stat sim_os_putchar (int32 c) { if (c != 0177) { putchar (c); fflush (stdout); } return SCPE_OK; } /* BSD UNIX routines */ #elif defined (BSDTTY) #include <sgtty.h> #include <fcntl.h> #include <unistd.h> struct sgttyb cmdtty,runtty; /* V6/V7 stty data */ struct tchars cmdtchars,runtchars; /* V7 editing */ struct ltchars cmdltchars,runltchars; /* 4.2 BSD editing */ int cmdfl,runfl; /* TTY flags */ static t_stat sim_os_ttinit (void) { cmdfl = fcntl (0, F_GETFL, 0); /* get old flags and status */ runfl = cmdfl | FNDELAY; if (ioctl (0, TIOCGETP, &cmdtty) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCGETC, &cmdtchars) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCGLTC, &cmdltchars) < 0) return SCPE_TTIERR; runtty = cmdtty; /* initial run state */ runtty.sg_flags = cmdtty.sg_flags & ~(ECHO|CRMOD) | CBREAK; runtchars.t_intrc = sim_int_char; /* interrupt */ runtchars.t_quitc = 0xFF; /* no quit */ runtchars.t_startc = 0xFF; /* no host sync */ runtchars.t_stopc = 0xFF; runtchars.t_eofc = 0xFF; runtchars.t_brkc = 0xFF; runltchars.t_suspc = 0xFF; /* no specials of any kind */ runltchars.t_dsuspc = 0xFF; runltchars.t_rprntc = 0xFF; runltchars.t_flushc = 0xFF; runltchars.t_werasc = 0xFF; runltchars.t_lnextc = 0xFF; return SCPE_OK; /* return success */ } static t_stat sim_os_ttrun (void) { runtchars.t_intrc = sim_int_char; /* in case changed */ fcntl (0, F_SETFL, runfl); /* non-block mode */ if (ioctl (0, TIOCSETP, &runtty) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCSETC, &runtchars) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCSLTC, &runltchars) < 0) return SCPE_TTIERR; sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL)l /* lower priority */ return SCPE_OK; } static t_stat sim_os_ttcmd (void) { sim_os_set_thread_priority (PRIORITY_NORMAL); /* restore priority */ fcntl (0, F_SETFL, cmdfl); /* block mode */ if (ioctl (0, TIOCSETP, &cmdtty) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCSETC, &cmdtchars) < 0) return SCPE_TTIERR; if (ioctl (0, TIOCSLTC, &cmdltchars) < 0) return SCPE_TTIERR; return SCPE_OK; } static t_stat sim_os_ttclose (void) { return sim_ttcmd (); } static t_bool sim_os_ttisatty (void) { return isatty (fileno (stdin)); } static t_stat sim_os_poll_kbd (void) { int status; unsigned char buf[1]; status = read (0, buf, 1); if (status != 1) return SCPE_OK; if (sim_brk_char && (buf[0] == sim_brk_char)) return SCPE_BREAK; if (sim_int_char && (buf[0] == sim_int_char)) return SCPE_STOP; return (buf[0] | SCPE_KFLAG); } static t_bool sim_os_poll_kbd_ready (int ms_timeout) { fd_set readfds; struct timeval timeout; if (!isatty (0)) { /* skip if !tty */ sim_os_ms_sleep (ms_timeout); return FALSE; } FD_ZERO (&readfds); FD_SET (0, &readfds); timeout.tv_sec = (ms_timeout*1000)/1000000; timeout.tv_usec = (ms_timeout*1000)%1000000; return (1 == select (1, &readfds, NULL, NULL, &timeout)); } static t_stat sim_os_putchar (int32 out) { char c; c = out; write (1, &c, 1); return SCPE_OK; } /* POSIX UNIX routines, from Leendert Van Doorn */ #else #include <termios.h> #include <unistd.h> struct termios cmdtty, runtty; static t_stat sim_os_ttinit (void) { if (!isatty (fileno (stdin))) /* skip if !tty */ return SCPE_OK; if (tcgetattr (0, &cmdtty) < 0) /* get old flags */ return SCPE_TTIERR; runtty = cmdtty; runtty.c_lflag = runtty.c_lflag & ~(ECHO | ICANON); /* no echo or edit */ runtty.c_oflag = runtty.c_oflag & ~OPOST; /* no output edit */ runtty.c_iflag = runtty.c_iflag & ~ICRNL; /* no cr conversion */ #if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL) runtty.c_cc[VINTR] = 0; /* OS X doesn't deliver SIGINT to main thread when enabled */ #else runtty.c_cc[VINTR] = sim_int_char; /* interrupt */ #endif runtty.c_cc[VQUIT] = 0; /* no quit */ runtty.c_cc[VERASE] = 0; runtty.c_cc[VKILL] = 0; runtty.c_cc[VEOF] = 0; runtty.c_cc[VEOL] = 0; runtty.c_cc[VSTART] = 0; /* no host sync */ runtty.c_cc[VSUSP] = 0; runtty.c_cc[VSTOP] = 0; #if defined (VREPRINT) runtty.c_cc[VREPRINT] = 0; /* no specials */ #endif #if defined (VDISCARD) runtty.c_cc[VDISCARD] = 0; #endif #if defined (VWERASE) runtty.c_cc[VWERASE] = 0; #endif #if defined (VLNEXT) runtty.c_cc[VLNEXT] = 0; #endif runtty.c_cc[VMIN] = 0; /* no waiting */ runtty.c_cc[VTIME] = 0; #if defined (VDSUSP) runtty.c_cc[VDSUSP] = 0; #endif #if defined (VSTATUS) runtty.c_cc[VSTATUS] = 0; #endif return SCPE_OK; } static t_stat sim_os_ttrun (void) { if (!isatty (fileno (stdin))) /* skip if !tty */ return SCPE_OK; #if defined(USE_SIM_VIDEO) && defined(HAVE_LIBSDL) runtty.c_cc[VINTR] = 0; /* OS X doesn't deliver SIGINT to main thread when enabled */ #else runtty.c_cc[VINTR] = sim_int_char; /* in case changed */ #endif if (tcsetattr (0, TCSAFLUSH, &runtty) < 0) return SCPE_TTIERR; sim_os_set_thread_priority (PRIORITY_BELOW_NORMAL); /* try to lower pri */ return SCPE_OK; } static t_stat sim_os_ttcmd (void) { if (!isatty (fileno (stdin))) /* skip if !tty */ return SCPE_OK; sim_os_set_thread_priority (PRIORITY_NORMAL); /* try to raise pri */ if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0) return SCPE_TTIERR; return SCPE_OK; } static t_stat sim_os_ttclose (void) { return sim_ttcmd (); } static t_bool sim_os_ttisatty (void) { return isatty (fileno (stdin)); } static t_stat sim_os_poll_kbd (void) { int status; unsigned char buf[1]; status = read (0, buf, 1); if (status != 1) return SCPE_OK; if (sim_brk_char && (buf[0] == sim_brk_char)) return SCPE_BREAK; if (sim_int_char && (buf[0] == sim_int_char)) return SCPE_STOP; return (buf[0] | SCPE_KFLAG); } static t_bool sim_os_poll_kbd_ready (int ms_timeout) { fd_set readfds; struct timeval timeout; if (!sim_os_ttisatty()) { /* skip if !tty */ sim_os_ms_sleep (ms_timeout); return FALSE; } FD_ZERO (&readfds); FD_SET (0, &readfds); timeout.tv_sec = (ms_timeout*1000)/1000000; timeout.tv_usec = (ms_timeout*1000)%1000000; return (1 == select (1, &readfds, NULL, NULL, &timeout)); } static t_stat sim_os_putchar (int32 out) { char c; c = out; (void)write (1, &c, 1); return SCPE_OK; } #endif /* Decode a string. A string containing encoded control characters is decoded into the equivalent character string. Escape targets @, A-Z, and [\]^_ form control characters 000-037. */ #define ESCAPE_CHAR '~' static void decode (char *decoded, const char *encoded) { char c; while ((c = *decoded++ = *encoded++)) /* copy the character */ if (c == ESCAPE_CHAR) { /* does it start an escape? */ if ((isalpha (*encoded)) || /* is next character "A-Z" or "a-z"? */ (*encoded == '@') || /* or "@"? */ ((*encoded >= '[') && (*encoded <= '_'))) /* or "[\]^_"? */ *(decoded - 1) = *encoded++ & 037; /* convert back to control character */ else { if ((*encoded == '\0') || /* single escape character at EOL? */ (*encoded++ != ESCAPE_CHAR)) /* or not followed by another escape? */ decoded--; /* drop the encoding */ } } return; } /* Set console halt */ static t_stat sim_set_halt (int32 flag, CONST char *cptr) { if (flag == 0) /* no halt? */ sim_exp_clrall (&sim_con_expect); /* disable halt checks */ else { char *mbuf; char *mbuf2; if (cptr == NULL || *cptr == 0) /* no match string? */ return SCPE_2FARG; /* need an argument */ sim_exp_clrall (&sim_con_expect); /* make sure that none currently exist */ mbuf = (char *)malloc (1 + strlen (cptr)); decode (mbuf, cptr); /* save decoded match string */ mbuf2 = (char *)malloc (3 + strlen(cptr)); sprintf (mbuf2, "%s%s%s", (sim_switches & SWMASK ('A')) ? "\n" : "", mbuf, (sim_switches & SWMASK ('I')) ? "" : "\n"); free (mbuf); mbuf = sim_encode_quoted_string ((uint8 *)mbuf2, strlen (mbuf2)); sim_exp_set (&sim_con_expect, mbuf, 0, sim_con_expect.after, EXP_TYP_PERSIST, NULL); free (mbuf); free (mbuf2); } return SCPE_OK; } /* Set console response */ static t_stat sim_set_response (int32 flag, CONST char *cptr) { if (flag == 0) /* no response? */ sim_send_clear (&sim_con_send); else { uint8 *rbuf; if (cptr == NULL || *cptr == 0) return SCPE_2FARG; /* need arg */ rbuf = (uint8 *)malloc (1 + strlen(cptr)); decode ((char *)rbuf, cptr); /* decod string */ sim_send_input (&sim_con_send, rbuf, strlen((char *)rbuf), 0, 0); /* queue it for output */ free (rbuf); } return SCPE_OK; } /* Set console delay */ static t_stat sim_set_delay (int32 flag, CONST char *cptr) { int32 val; t_stat r; if (cptr == NULL || *cptr == 0) /* no argument string? */ return SCPE_2FARG; /* need an argument */ val = (int32) get_uint (cptr, 10, INT_MAX, &r); /* parse the argument */ if (r == SCPE_OK) /* parse OK? */ sim_con_expect.after = val; /* save the delay value */ return r; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | /* sim_console.h: simulator console I/O library headers Copyright (c) 1993-2014, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 02-Jan-14 RMS Added tab stop routines 17-Jan-11 MP Added buffered line capabilities 22-Jun-06 RMS Implemented SET/SHOW PCHAR 22-Nov-05 RMS Added central input/output conversion support 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy 28-May-04 RMS Added SET/SHOW CONSOLE 02-Jan-04 RMS Removed timer routines, added Telnet console routines */ #ifndef SIM_CONSOLE_H_ #define SIM_CONSOLE_H_ 0 #ifdef __cplusplus extern "C" { #endif #define TTUF_V_MODE (UNIT_V_UF + 0) #define TTUF_W_MODE 2 #define TTUF_MODE_7B 0 #define TTUF_MODE_8B 1 #define TTUF_MODE_UC 2 #define TTUF_MODE_7P 3 #define TTUF_M_MODE ((1u << TTUF_W_MODE) - 1) #define TTUF_V_PAR (TTUF_V_MODE + TTUF_W_MODE) #define TTUF_W_PAR 2 #define TTUF_PAR_SPACE 0 #define TTUF_PAR_MARK 1 #define TTUF_PAR_EVEN 2 #define TTUF_PAR_ODD 3 #define TTUF_M_PAR ((1u << TTUF_W_PAR) - 1) #define TTUF_KSR (1u << (TTUF_W_MODE + TTUF_W_PAR)) #define TTUF_V_UF (TTUF_V_MODE + TTUF_W_MODE + TTUF_W_PAR) #define TT_MODE (TTUF_M_MODE << TTUF_V_MODE) #define TT_MODE_7B (TTUF_MODE_7B << TTUF_V_MODE) #define TT_MODE_8B (TTUF_MODE_8B << TTUF_V_MODE) #define TT_MODE_UC (TTUF_MODE_UC << TTUF_V_MODE) #define TT_MODE_7P (TTUF_MODE_7P << TTUF_V_MODE) #define TT_MODE_KSR (TT_MODE_UC) /* 7 bit modes allow for an 8th bit parity mode */ #define TT_PAR (TTUF_M_PAR << TTUF_V_PAR) #define TT_PAR_SPACE (TTUF_PAR_SPACE << TTUF_V_PAR) #define TT_PAR_MARK (TTUF_PAR_MARK << TTUF_V_PAR) #define TT_PAR_EVEN (TTUF_PAR_EVEN << TTUF_V_PAR) #define TT_PAR_ODD (TTUF_PAR_ODD << TTUF_V_PAR) /* TT_GET_MODE returns both the TT_MODE and TT_PAR fields since they together are passed into sim_tt_inpcvt() */ #define TT_GET_MODE(x) (((x) >> TTUF_V_MODE) & (TTUF_M_MODE | (TTUF_M_PAR << TTUF_W_MODE))) t_stat sim_set_console (int32 flag, CONST char *cptr); t_stat sim_set_remote_console (int32 flag, CONST char *cptr); void sim_remote_process_command (void); t_stat sim_set_kmap (int32 flag, CONST char *cptr); t_stat sim_set_telnet (int32 flag, CONST char *cptr); t_stat sim_set_notelnet (int32 flag, CONST char *cptr); t_stat sim_set_serial (int32 flag, CONST char *cptr); t_stat sim_set_noserial (int32 flag, CONST char *cptr); t_stat sim_set_logon (int32 flag, CONST char *cptr); t_stat sim_set_logoff (int32 flag, CONST char *cptr); t_stat sim_set_debon (int32 flag, CONST char *cptr); t_stat sim_set_cons_debug (int32 flg, CONST char *cptr); t_stat sim_set_cons_buff (int32 flg, CONST char *cptr); t_stat sim_set_cons_unbuff (int32 flg, CONST char *cptr); t_stat sim_set_cons_log (int32 flg, CONST char *cptr); t_stat sim_set_cons_nolog (int32 flg, CONST char *cptr); t_stat sim_set_deboff (int32 flag, CONST char *cptr); t_stat sim_set_cons_expect (int32 flg, CONST char *cptr); t_stat sim_set_cons_noexpect (int32 flg, CONST char *cptr); t_stat sim_debug_flush (void); t_stat sim_set_pchar (int32 flag, CONST char *cptr); t_stat sim_set_cons_speed (int32 flag, CONST char *cptr); t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_remote_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_telnet (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_cons_speed (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_cons_buff (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_cons_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_cons_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_show_cons_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_check_console (int32 sec); t_stat sim_open_logfile (const char *filename, t_bool binary, FILE **pf, FILEREF **pref); t_stat sim_close_logfile (FILEREF **pref); const char *sim_logfile_name (FILE *st, FILEREF *ref); SEND *sim_cons_get_send (void); EXPECT *sim_cons_get_expect (void); t_stat sim_show_cons_send_input (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_stat sim_set_noconsole_port (void); t_stat sim_poll_kbd (void); t_stat sim_putchar (int32 c); t_stat sim_putchar_s (int32 c); t_stat sim_ttinit (void); t_stat sim_ttrun (void); t_stat sim_ttcmd (void); t_stat sim_ttclose (void); t_bool sim_ttisatty (void); int32 sim_tt_inpcvt (int32 c, uint32 mode); int32 sim_tt_outcvt (int32 c, uint32 mode); t_stat sim_tt_settabs (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat sim_tt_showtabs (FILE *st, UNIT *uptr, int32 val, CONST void *desc); extern int32 sim_rem_cmd_active_line; /* command in progress on line # */ extern int32 sim_int_char; /* interrupt character */ extern int32 sim_brk_char; /* break character */ extern int32 sim_tt_pchar; /* printable character mask */ extern int32 sim_del_char; /* delete character */ #ifdef __cplusplus } #endif #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 | /* sim_defs.h: simulator definitions Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 05-Jan-11 MP Added Asynch I/O support 18-Jan-11 MP Added log file reference count support 21-Jul-08 RMS Removed inlining support 28-May-08 RMS Added inlining support 28-Jun-07 RMS Added IA64 VMS support (from Norm Lastovica) 18-Jun-07 RMS Added UNIT_IDLE flag 18-Mar-07 RMS Added UNIT_TEXT flag 07-Mar-07 JDB Added DEBUG_PRJ macro 18-Oct-06 RMS Added limit check for clock synchronized keyboard waits 13-Jul-06 RMS Guarantee CBUFSIZE is at least 256 07-Jan-06 RMS Added support for breakpoint spaces Added REG_FIT flag 16-Aug-05 RMS Fixed C++ declaration and cast problems 11-Mar-05 RMS Moved 64b data type definitions outside USE_INT64 07-Feb-05 RMS Added assertion fail stop 05-Nov-04 RMS Added support for SHOW opt=val 20-Oct-04 RMS Converted all base types to typedefs 21-Sep-04 RMS Added switch to flag stop message printout 06-Feb-04 RMS Moved device and unit user flags fields (V3.2) RMS Added REG_VMAD 29-Dec-03 RMS Added output stall status 15-Jun-03 RMS Added register flag REG_VMIO 23-Apr-03 RMS Revised for 32b/64b t_addr 14-Mar-03 RMS Lengthened default serial output wait 31-Mar-03 RMS Added u5, u6 fields 18-Mar-03 RMS Added logical name support Moved magtape definitions to sim_tape.h Moved breakpoint definitions from scp.c 03-Mar-03 RMS Added sim_fsize 08-Feb-03 RMS Changed sim_os_sleep to void, added match_ext 05-Jan-03 RMS Added hidden switch definitions, device dyn memory support, parameters for function pointers, case sensitive SET support 22-Dec-02 RMS Added break flag 08-Oct-02 RMS Increased simulator error code space Added Telnet errors Added end of medium support Added help messages to CTAB Added flag and context fields to DEVICE Added restore flag masks Revised 64b definitions 02-May-02 RMS Removed log status codes 22-Apr-02 RMS Added magtape record length error 30-Dec-01 RMS Generalized timer package, added circular arrays 07-Dec-01 RMS Added breakpoint package 01-Dec-01 RMS Added read-only unit support, extended SET/SHOW features, improved error messages 24-Nov-01 RMS Added unit-based registers 27-Sep-01 RMS Added queue count prototype 17-Sep-01 RMS Removed multiple console support 07-Sep-01 RMS Removed conditional externs on function prototypes 31-Aug-01 RMS Changed int64 to t_int64 for Windoze 17-Jul-01 RMS Added additional function prototypes 27-May-01 RMS Added multiple console support 15-May-01 RMS Increased string buffer size 25-Feb-01 RMS Revisions for V2.6 15-Oct-00 RMS Editorial revisions for V2.5 11-Jul-99 RMS Added unsigned int data types 14-Apr-99 RMS Converted t_addr to unsigned 04-Oct-98 RMS Additional definitions for V2.4 The interface between the simulator control package (SCP) and the simulator consists of the following routines and data structures sim_name simulator name string sim_devices[] array of pointers to simulated devices sim_PC pointer to saved PC register descriptor sim_interval simulator interval to next event sim_stop_messages[] array of pointers to stop messages sim_instr() instruction execution routine sim_load() binary loader routine sim_emax maximum number of words in an instruction In addition, the simulator must supply routines to print and parse architecture specific formats print_sym print symbolic output parse_sym parse symbolic input */ #ifndef SIM_DEFS_H_ #define SIM_DEFS_H_ 0 #include <stddef.h> #include <stdlib.h> #include <stdio.h> #if defined(_MSC_VER) && (_MSC_VER < 1900) #define snprintf _snprintf /* poor man's snprintf which will work most of the time but has different return value */ #endif #include <stdarg.h> #include <string.h> #include <errno.h> #include <limits.h> #ifdef _WIN32 #include <winsock2.h> #undef PACKED /* avoid macro name collision */ #undef ERROR /* avoid macro name collision */ #undef MEM_MAPPED /* avoid macro name collision */ #include <process.h> #endif #ifdef USE_REGEX #undef USE_REGEX #endif #if defined(HAVE_PCREPOSIX_H) #include <pcreposix.h> #define USE_REGEX 1 #elif defined(HAVE_REGEX_H) #include <regex.h> #define USE_REGEX 1 #endif #ifdef __cplusplus extern "C" { #endif /* avoid macro names collisions */ #ifdef MAX #undef MAX #endif #ifdef MIN #undef MIN #endif #ifdef PMASK #undef PMASK #endif #ifdef RS #undef RS #endif #ifdef PAGESIZE #undef PAGESIZE #endif #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif /* SCP API shim. The SCP API for version 4.0 introduces a number of "pointer-to-const" parameter qualifiers that were not present in the 3.x versions. To maintain compatibility with the earlier versions, the new qualifiers are expressed as "CONST" rather than "const". This allows macro removal of the qualifiers when compiling for SIMH 3.x. */ #ifndef CONST #define CONST const #endif /* Length specific integer declarations */ #if defined (VMS) #include <ints.h> #else typedef signed char int8; typedef signed short int16; typedef signed int int32; typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint32; #endif typedef int t_stat; /* status */ typedef int t_bool; /* boolean */ /* 64b integers */ #if defined (__GNUC__) /* GCC */ typedef signed long long t_int64; typedef unsigned long long t_uint64; #elif defined (_WIN32) /* Windows */ typedef signed __int64 t_int64; typedef unsigned __int64 t_uint64; #elif (defined (__ALPHA) || defined (__ia64)) && defined (VMS) /* 64b VMS */ typedef signed __int64 t_int64; typedef unsigned __int64 t_uint64; #elif defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */ typedef signed long t_int64; typedef unsigned long t_uint64; #else /* default */ #define t_int64 signed long long #define t_uint64 unsigned long long #endif /* end 64b */ #ifndef INT64_C #define INT64_C(x) x ## LL #endif #if defined (USE_INT64) /* 64b data */ typedef t_int64 t_svalue; /* signed value */ typedef t_uint64 t_value; /* value */ #else /* 32b data */ typedef int32 t_svalue; typedef uint32 t_value; #endif /* end 64b data */ #if defined (USE_INT64) && defined (USE_ADDR64) /* 64b address */ typedef t_uint64 t_addr; #define T_ADDR_W 64 #define T_ADDR_FMT LL_FMT #else /* 32b address */ typedef uint32 t_addr; #define T_ADDR_W 32 #define T_ADDR_FMT "" #endif /* end 64b address */ #if defined (_WIN32) #define vsnprintf _vsnprintf #endif #if defined (__DECC) && defined (__VMS) && (defined (__VAX) || (__CRTL_VER <= 70311000)) #define NO_vsnprintf #endif #if defined( NO_vsnprintf) #define STACKBUFSIZE 16384 #else #define STACKBUFSIZE 2048 #endif #if defined (_WIN32) /* Actually, a GCC issue */ #define LL_FMT "I64" #else #define LL_FMT "ll" #endif #if defined (VMS) && (defined (__ia64) || defined (__ALPHA)) #define HAVE_GLOB #endif #if defined (__linux) || defined (VMS) || defined (__APPLE__) #define HAVE_C99_STRFTIME 1 #endif #if defined (_WIN32) #define NULL_DEVICE "NUL:" #elif defined (_VMS) #define NULL_DEVICE "NL:" #else #define NULL_DEVICE "/dev/null" #endif /* Stubs for inlining */ #if defined(_MSC_VER) #define SIM_INLINE _inline #define SIM_NOINLINE _declspec (noinline) #elif defined(__GNUC__) #define SIM_INLINE inline #define SIM_NOINLINE __attribute__ ((noinline)) #else #define SIM_INLINE #define SIM_NOINLINE #endif /* Storage class modifier for weak link definition for sim_vm_init() */ #if defined(__cplusplus) #if defined(__GNUC__) #define WEAK __attribute__((weak)) #elif defined(_MSC_VER) #define WEAK __declspec(selectany) #else #define WEAK extern #endif #else #define WEAK #endif /* System independent definitions */ #define FLIP_SIZE (1 << 16) /* flip buf size */ #if !defined (PATH_MAX) /* usually in limits */ #define PATH_MAX 512 #endif #if (PATH_MAX >= 128) #define CBUFSIZE (128 + PATH_MAX) /* string buf size */ #else #define CBUFSIZE 256 #endif /* Breakpoint spaces definitions */ #define SIM_BKPT_N_SPC (1 << (32 - SIM_BKPT_V_SPC)) /* max number spaces */ #define SIM_BKPT_V_SPC (BRK_TYP_MAX + 1) /* location in arg */ /* Extended switch definitions (bits >= 26) */ #define SIM_SW_HIDE (1u << 26) /* enable hiding */ #define SIM_SW_REST (1u << 27) /* attach/restore */ #define SIM_SW_REG (1u << 28) /* register value */ #define SIM_SW_STOP (1u << 29) /* stop message */ #define SIM_SW_SHUT (1u << 30) /* shutdown */ /* Simulator status codes 0 ok 1 - (SCPE_BASE - 1) simulator specific SCPE_BASE - n general */ #define SCPE_OK 0 /* normal return */ #define SCPE_BASE 64 /* base for messages */ #define SCPE_NXM (SCPE_BASE + 0) /* nxm */ #define SCPE_UNATT (SCPE_BASE + 1) /* no file */ #define SCPE_IOERR (SCPE_BASE + 2) /* I/O error */ #define SCPE_CSUM (SCPE_BASE + 3) /* loader cksum */ #define SCPE_FMT (SCPE_BASE + 4) /* loader format */ #define SCPE_NOATT (SCPE_BASE + 5) /* not attachable */ #define SCPE_OPENERR (SCPE_BASE + 6) /* open error */ #define SCPE_MEM (SCPE_BASE + 7) /* alloc error */ #define SCPE_ARG (SCPE_BASE + 8) /* argument error */ #define SCPE_STEP (SCPE_BASE + 9) /* step expired */ #define SCPE_UNK (SCPE_BASE + 10) /* unknown command */ #define SCPE_RO (SCPE_BASE + 11) /* read only */ #define SCPE_INCOMP (SCPE_BASE + 12) /* incomplete */ #define SCPE_STOP (SCPE_BASE + 13) /* sim stopped */ #define SCPE_EXIT (SCPE_BASE + 14) /* sim exit */ #define SCPE_TTIERR (SCPE_BASE + 15) /* console tti err */ #define SCPE_TTOERR (SCPE_BASE + 16) /* console tto err */ #define SCPE_EOF (SCPE_BASE + 17) /* end of file */ #define SCPE_REL (SCPE_BASE + 18) /* relocation error */ #define SCPE_NOPARAM (SCPE_BASE + 19) /* no parameters */ #define SCPE_ALATT (SCPE_BASE + 20) /* already attached */ #define SCPE_TIMER (SCPE_BASE + 21) /* hwre timer err */ #define SCPE_SIGERR (SCPE_BASE + 22) /* signal err */ #define SCPE_TTYERR (SCPE_BASE + 23) /* tty setup err */ #define SCPE_SUB (SCPE_BASE + 24) /* subscript err */ #define SCPE_NOFNC (SCPE_BASE + 25) /* func not imp */ #define SCPE_UDIS (SCPE_BASE + 26) /* unit disabled */ #define SCPE_NORO (SCPE_BASE + 27) /* rd only not ok */ #define SCPE_INVSW (SCPE_BASE + 28) /* invalid switch */ #define SCPE_MISVAL (SCPE_BASE + 29) /* missing value */ #define SCPE_2FARG (SCPE_BASE + 30) /* too few arguments */ #define SCPE_2MARG (SCPE_BASE + 31) /* too many arguments */ #define SCPE_NXDEV (SCPE_BASE + 32) /* nx device */ #define SCPE_NXUN (SCPE_BASE + 33) /* nx unit */ #define SCPE_NXREG (SCPE_BASE + 34) /* nx register */ #define SCPE_NXPAR (SCPE_BASE + 35) /* nx parameter */ #define SCPE_NEST (SCPE_BASE + 36) /* nested DO */ #define SCPE_IERR (SCPE_BASE + 37) /* internal error */ #define SCPE_MTRLNT (SCPE_BASE + 38) /* tape rec lnt error */ #define SCPE_LOST (SCPE_BASE + 39) /* Telnet conn lost */ #define SCPE_TTMO (SCPE_BASE + 40) /* Telnet conn timeout */ #define SCPE_STALL (SCPE_BASE + 41) /* Telnet conn stall */ #define SCPE_AFAIL (SCPE_BASE + 42) /* assert failed */ #define SCPE_INVREM (SCPE_BASE + 43) /* invalid remote console command */ #define SCPE_NOTATT (SCPE_BASE + 44) /* not attached */ #define SCPE_EXPECT (SCPE_BASE + 45) /* expect matched */ #define SCPE_REMOTE (SCPE_BASE + 46) /* remote console command */ #define SCPE_MAX_ERR (SCPE_BASE + 47) /* Maximum SCPE Error Value */ #define SCPE_KFLAG 0x1000 /* tti data flag */ #define SCPE_BREAK 0x2000 /* tti break flag */ #define SCPE_NOMESSAGE 0x10000000 /* message display supression flag */ #define SCPE_BARE_STATUS(stat) ((stat) & ~(SCPE_NOMESSAGE|SCPE_KFLAG|SCPE_BREAK)) /* Print value format codes */ #define PV_RZRO 0 /* right, zero fill */ #define PV_RSPC 1 /* right, space fill */ #define PV_RCOMMA 2 /* right, space fill. Comma separate every 3 */ #define PV_LEFT 3 /* left justify */ /* Default timing parameters */ #define KBD_POLL_WAIT 5000 /* keyboard poll */ #define KBD_MAX_WAIT 500000 #define SERIAL_IN_WAIT 100 /* serial in time */ #define SERIAL_OUT_WAIT 100 /* serial output */ #define NOQUEUE_WAIT 1000000 /* min check time */ #define KBD_LIM_WAIT(x) (((x) > KBD_MAX_WAIT)? KBD_MAX_WAIT: (x)) #define KBD_WAIT(w,s) ((w)? w: KBD_LIM_WAIT (s)) /* Convert switch letter to bit mask */ #define SWMASK(x) (1u << (((int) (x)) - ((int) 'A'))) /* String match - at least one character required */ #define MATCH_CMD(ptr,cmd) ((NULL == (ptr)) || (!*(ptr)) || sim_strncasecmp ((ptr), (cmd), strlen (ptr))) /* End of Linked List/Queue value */ /* Chosen for 2 reasons: */ /* 1 - to not be NULL, this allowing the NULL value to */ /* indicate inclusion on a list */ /* and */ /* 2 - to not be a valid/possible pointer (alignment) */ #define QUEUE_LIST_END ((UNIT *)1) /* Typedefs for principal structures */ typedef struct DEVICE DEVICE; typedef struct UNIT UNIT; typedef struct REG REG; typedef struct CTAB CTAB; typedef struct C1TAB C1TAB; typedef struct SHTAB SHTAB; typedef struct MTAB MTAB; typedef struct SCHTAB SCHTAB; typedef struct BRKTAB BRKTAB; typedef struct BRKTYPTAB BRKTYPTAB; typedef struct EXPTAB EXPTAB; typedef struct EXPECT EXPECT; typedef struct SEND SEND; typedef struct DEBTAB DEBTAB; typedef struct FILEREF FILEREF; typedef struct MEMFILE MEMFILE; typedef struct BITFIELD BITFIELD; typedef t_stat (*ACTIVATE_API)(UNIT *unit, int32 interval); /* Device data structure */ struct DEVICE { const char *name; /* name */ UNIT *units; /* units */ REG *registers; /* registers */ MTAB *modifiers; /* modifiers */ uint32 numunits; /* #units */ uint32 aradix; /* address radix */ uint32 awidth; /* address width */ uint32 aincr; /* addr increment */ uint32 dradix; /* data radix */ uint32 dwidth; /* data width */ t_stat (*examine)(t_value *v, t_addr a, UNIT *up, int32 sw); /* examine routine */ t_stat (*deposit)(t_value v, t_addr a, UNIT *up, int32 sw); /* deposit routine */ t_stat (*reset)(DEVICE *dp); /* reset routine */ t_stat (*boot)(int32 u, DEVICE *dp); /* boot routine */ t_stat (*attach)(UNIT *up, CONST char *cp); /* attach routine */ t_stat (*detach)(UNIT *up); /* detach routine */ void *ctxt; /* context */ uint32 flags; /* flags */ uint32 dctrl; /* debug control */ DEBTAB *debflags; /* debug flags */ t_stat (*msize)(UNIT *up, int32 v, CONST char *cp, void *dp); /* mem size routine */ char *lname; /* logical name */ t_stat (*help)(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); /* help */ t_stat (*attach_help)(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); /* attach help */ void *help_ctx; /* Context available to help routines */ const char *(*description)(DEVICE *dptr); /* Device Description */ BRKTYPTAB *brk_types; /* Breakpoint types */ }; /* Device flags */ #define DEV_V_DIS 0 /* dev disabled */ #define DEV_V_DISABLE 1 /* dev disable-able */ #define DEV_V_DYNM 2 /* mem size dynamic */ #define DEV_V_DEBUG 3 /* debug capability */ #define DEV_V_TYPE 4 /* Attach type */ #define DEV_S_TYPE 3 /* Width of Type Field */ #define DEV_V_SECTORS 7 /* Unit Capacity is in 512byte sectors */ #define DEV_V_DONTAUTO 8 /* Do not auto detach already attached units */ #define DEV_V_FLATHELP 9 /* Use traditional (unstructured) help */ #define DEV_V_NOSAVE 10 /* Don't save device state */ #define DEV_V_UF_31 12 /* user flags, V3.1 */ #define DEV_V_UF 16 /* user flags */ #define DEV_V_RSV 31 /* reserved */ #define DEV_DIS (1 << DEV_V_DIS) /* device is currently disabled */ #define DEV_DISABLE (1 << DEV_V_DISABLE) /* device can be set enabled or disabled */ #define DEV_DYNM (1 << DEV_V_DYNM) /* device requires call on msize routine to change memory size */ #define DEV_DEBUG (1 << DEV_V_DEBUG) /* device supports SET DEBUG command */ #define DEV_SECTORS (1 << DEV_V_SECTORS) /* capacity is 512 byte sectors */ #define DEV_DONTAUTO (1 << DEV_V_DONTAUTO) /* Do not auto detach already attached units */ #define DEV_FLATHELP (1 << DEV_V_FLATHELP) /* Use traditional (unstructured) help */ #define DEV_NOSAVE (1 << DEV_V_NOSAVE) /* Don't save device state */ #define DEV_NET 0 /* Deprecated - meaningless */ #define DEV_TYPEMASK (((1 << DEV_S_TYPE) - 1) << DEV_V_TYPE) #define DEV_DISK (1 << DEV_V_TYPE) /* sim_disk Attach */ #define DEV_TAPE (2 << DEV_V_TYPE) /* sim_tape Attach */ #define DEV_MUX (3 << DEV_V_TYPE) /* sim_tmxr Attach */ #define DEV_ETHER (4 << DEV_V_TYPE) /* Ethernet Device */ #define DEV_DISPLAY (5 << DEV_V_TYPE) /* Display Device */ #define DEV_TYPE(dptr) ((dptr)->flags & DEV_TYPEMASK) #define DEV_UFMASK_31 (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF_31) - 1)) #define DEV_UFMASK (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF) - 1)) #define DEV_RFLAGS (DEV_UFMASK|DEV_DIS) /* restored flags */ /* Unit data structure Parts of the unit structure are device specific, that is, they are not referenced by the simulator control package and can be freely used by device simulators. Fields starting with 'buf', and flags starting with 'UF', are device specific. The definitions given here are for a typical sequential device. */ struct UNIT { UNIT *next; /* next active */ t_stat (*action)(UNIT *up); /* action routine */ char *filename; /* open file name */ FILE *fileref; /* file reference */ void *filebuf; /* memory buffer */ uint32 hwmark; /* high water mark */ int32 time; /* time out */ uint32 flags; /* flags */ uint32 dynflags; /* dynamic flags */ t_addr capac; /* capacity */ t_addr pos; /* file position */ void (*io_flush)(UNIT *up); /* io flush routine */ uint32 iostarttime; /* I/O start time */ int32 buf; /* buffer */ int32 wait; /* wait */ int32 u3; /* device specific */ int32 u4; /* device specific */ int32 u5; /* device specific */ int32 u6; /* device specific */ void *up7; /* device specific */ void *up8; /* device specific */ void *tmxr; /* TMXR linkage */ t_bool (*cancel)(UNIT *); double usecs_remaining; /* time balance for long delays */ #ifdef SIM_ASYNCH_IO void (*a_check_completion)(UNIT *); t_bool (*a_is_active)(UNIT *); UNIT *a_next; /* next asynch active */ int32 a_event_time; ACTIVATE_API a_activate_call; /* Asynchronous Polling control */ /* These fields should only be referenced when holding the sim_tmxr_poll_lock */ t_bool a_polling_now; /* polling active flag */ int32 a_poll_waiter_count; /* count of polling threads */ /* waiting for this unit */ /* Asynchronous Timer control */ double a_due_time; /* due time for timer event */ double a_due_gtime; /* due time (in instructions) for timer event */ double a_usec_delay; /* time delay for timer event */ #endif }; /* Unit flags */ #define UNIT_V_UF_31 12 /* dev spec, V3.1 */ #define UNIT_V_UF 16 /* device specific */ #define UNIT_V_RSV 31 /* reserved!! */ #define UNIT_ATTABLE 0000001 /* attachable */ #define UNIT_RO 0000002 /* read only */ #define UNIT_FIX 0000004 /* fixed capacity */ #define UNIT_SEQ 0000010 /* sequential */ #define UNIT_ATT 0000020 /* attached */ #define UNIT_BINK 0000040 /* K = power of 2 */ #define UNIT_BUFABLE 0000100 /* bufferable */ #define UNIT_MUSTBUF 0000200 /* must buffer */ #define UNIT_BUF 0000400 /* buffered */ #define UNIT_ROABLE 0001000 /* read only ok */ #define UNIT_DISABLE 0002000 /* disable-able */ #define UNIT_DIS 0004000 /* disabled */ #define UNIT_IDLE 0040000 /* idle eligible */ /* Unused/meaningless flags */ #define UNIT_TEXT 0000000 /* text mode - no effect */ #define UNIT_UFMASK_31 (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF_31) - 1)) #define UNIT_UFMASK (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF) - 1)) #define UNIT_RFLAGS (UNIT_UFMASK|UNIT_DIS) /* restored flags */ /* Unit dynamic flags (dynflags) */ /* These flags are only set dynamically */ #define UNIT_ATTMULT 0000001 /* Allow multiple attach commands */ #define UNIT_TM_POLL 0000002 /* TMXR Polling unit */ #define UNIT_NO_FIO 0000004 /* fileref is NOT a FILE * */ #define UNIT_DISK_CHK 0000010 /* disk data debug checking (sim_disk) */ #define UNIT_TMR_UNIT 0000020 /* Unit registered as a calibrated timer */ #define UNIT_V_DF_TAPE 5 /* Bit offset for Tape Density reservation */ #define UNIT_S_DF_TAPE 3 /* Bits Reserved for Tape Density */ struct BITFIELD { const char *name; /* field name */ uint32 offset; /* starting bit */ uint32 width; /* width */ const char **valuenames; /* map of values to strings */ const char *format; /* value format string */ }; /* Register data structure */ struct REG { CONST char *name; /* name */ void *loc; /* location */ uint32 radix; /* radix */ uint32 width; /* width */ uint32 offset; /* starting bit */ uint32 depth; /* save depth */ const char *desc; /* description */ BITFIELD *fields; /* bit fields */ uint32 flags; /* flags */ uint32 qptr; /* circ q ptr */ size_t str_size; /* structure size */ }; /* Register flags */ #define REG_FMT 00003 /* see PV_x */ #define REG_RO 00004 /* read only */ #define REG_HIDDEN 00010 /* hidden */ #define REG_NZ 00020 /* must be non-zero */ #define REG_UNIT 00040 /* in unit struct */ #define REG_STRUCT 00100 /* in structure array */ #define REG_CIRC 00200 /* circular array */ #define REG_VMIO 00400 /* use VM data print/parse */ #define REG_VMAD 01000 /* use VM addr print/parse */ #define REG_FIT 02000 /* fit access to size */ #define REG_HRO (REG_RO | REG_HIDDEN) /* hidden, read only */ #define REG_V_UF 16 /* device specific */ #define REG_UFMASK (~((1u << REG_V_UF) - 1)) /* user flags mask */ #define REG_VMFLAGS (REG_VMIO | REG_UFMASK) /* call VM routine if any of these are set */ /* Command tables, base and alternate formats */ struct CTAB { const char *name; /* name */ t_stat (*action)(int32 flag, CONST char *cptr); /* action routine */ int32 arg; /* argument */ const char *help; /* help string/structured locator */ const char *help_base; /* structured help base*/ void (*message)(const char *unechoed_cmdline, t_stat stat); /* message printing routine */ }; struct C1TAB { const char *name; /* name */ t_stat (*action)(DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr);/* action routine */ int32 arg; /* argument */ const char *help; /* help string */ }; struct SHTAB { const char *name; /* name */ t_stat (*action)(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); int32 arg; /* argument */ const char *help; /* help string */ }; /* Modifier table - only extended entries have disp, reg, or flags */ struct MTAB { uint32 mask; /* mask */ uint32 match; /* match */ const char *pstring; /* print string */ const char *mstring; /* match string */ t_stat (*valid)(UNIT *up, int32 v, CONST char *cp, void *dp); /* validation routine */ t_stat (*disp)(FILE *st, UNIT *up, int32 v, CONST void *dp); /* display routine */ void *desc; /* value descriptor */ /* REG * if MTAB_VAL */ /* int * if not */ const char *help; /* help string */ }; /* mtab mask flag bits */ /* NOTE: MTAB_VALR and MTAB_VALO are only used to display help */ #define MTAB_XTD (1u << UNIT_V_RSV) /* ext entry flag */ #define MTAB_VDV (0001 | MTAB_XTD) /* valid for dev */ #define MTAB_VUN (0002 | MTAB_XTD) /* valid for unit */ #define MTAB_VALR (0004 | MTAB_XTD) /* takes a value (required) */ #define MTAB_VALO (0010 | MTAB_XTD) /* takes a value (optional) */ #define MTAB_NMO (0020 | MTAB_XTD) /* only if named */ #define MTAB_NC (0040 | MTAB_XTD) /* no UC conversion */ #define MTAB_QUOTE (0100 | MTAB_XTD) /* quoted string */ #define MTAB_SHP (0200 | MTAB_XTD) /* show takes parameter */ #define MODMASK(mptr,flag) (((mptr)->mask & (uint32)(flag)) == (uint32)(flag))/* flag mask test */ /* Search table */ struct SCHTAB { int32 logic; /* logical operator */ int32 boolop; /* boolean operator */ uint32 count; /* value count in mask and comp arrays */ t_value *mask; /* mask for logical */ t_value *comp; /* comparison for boolean */ }; /* Breakpoint table */ struct BRKTAB { t_addr addr; /* address */ uint32 typ; /* mask of types */ #define BRK_TYP_USR_TYPES ((1 << ('Z'-'A'+1)) - 1)/* all types A-Z */ #define BRK_TYP_DYN_STEPOVER (SWMASK ('Z'+1)) #define BRK_TYP_DYN_USR (SWMASK ('Z'+2)) #define BRK_TYP_DYN_ALL (BRK_TYP_DYN_USR|BRK_TYP_DYN_STEPOVER) /* Mask of All Dynamic types */ #define BRK_TYP_TEMP (SWMASK ('Z'+3)) /* Temporary (one-shot) */ #define BRK_TYP_MAX (('Z'-'A')+3) /* Maximum breakpoint type */ int32 cnt; /* proceed count */ char *act; /* action string */ double time_fired[SIM_BKPT_N_SPC]; /* instruction count when match occurred */ BRKTAB *next; /* list with same address value */ }; /* Breakpoint table */ struct BRKTYPTAB { uint32 btyp; /* type mask */ const char *desc; /* description */ }; #define BRKTYPE(typ,descrip) {SWMASK(typ), descrip} /* Expect rule */ struct EXPTAB { uint8 *match; /* match string */ uint32 size; /* match string size */ char *match_pattern; /* match pattern for format */ int32 cnt; /* proceed count */ int32 switches; /* flags */ #define EXP_TYP_PERSIST (SWMASK ('P')) /* rule persists after match, default is once a rule matches, it is removed */ #define EXP_TYP_CLEARALL (SWMASK ('C')) /* clear all rules after matching this rule, default is to once a rule matches, it is removed */ #define EXP_TYP_REGEX (SWMASK ('R')) /* rule pattern is a regular expression */ #define EXP_TYP_REGEX_I (SWMASK ('I')) /* regular expression pattern matching should be case independent */ #define EXP_TYP_TIME (SWMASK ('T')) /* halt delay is in microseconds instead of instructions */ #if defined(USE_REGEX) regex_t regex; /* compiled regular expression */ #endif char *act; /* action string */ }; /* Expect Context */ struct EXPECT { DEVICE *dptr; /* Device (for Debug) */ uint32 dbit; /* Debugging Bit */ EXPTAB *rules; /* match rules */ int32 size; /* count of match rules */ uint32 after; /* delay before halting */ uint8 *buf; /* buffer of output data which has produced */ uint32 buf_ins; /* buffer insertion point for the next output data */ uint32 buf_size; /* buffer size */ }; /* Send Context */ struct SEND { uint32 delay; /* instruction delay between sent data */ #define SEND_DEFAULT_DELAY 1000 /* default delay instruction count */ DEVICE *dptr; /* Device (for Debug) */ uint32 dbit; /* Debugging Bit */ uint32 after; /* instruction delay before sending any data */ double next_time; /* execution time when next data can be sent */ uint8 *buffer; /* buffer */ size_t bufsize; /* buffer size */ int32 insoff; /* insert offset */ int32 extoff; /* extra offset */ }; /* Debug table */ struct DEBTAB { const char *name; /* control name */ uint32 mask; /* control bit */ const char *desc; /* description */ }; /* Deprecated Debug macros. Use sim_debug() */ #define DEBUG_PRS(d) (sim_deb && d.dctrl) #define DEBUG_PRD(d) (sim_deb && d->dctrl) #define DEBUG_PRI(d,m) (sim_deb && (d.dctrl & (m))) #define DEBUG_PRJ(d,m) (sim_deb && ((d)->dctrl & (m))) #define SIM_DBG_EVENT 0x10000 #define SIM_DBG_ACTIVATE 0x20000 #define SIM_DBG_AIO_QUEUE 0x40000 /* Open File Reference */ struct FILEREF { char name[CBUFSIZE]; /* file name */ FILE *file; /* file handle */ int32 refcount; /* reference count */ }; struct MEMFILE { char *buf; /* buffered data */ size_t size; /* size */ size_t pos; /* data used */ }; /* The following macros exist to help populate structure contents They are dependent on the declaration order of the fields of the structures they exist to populate. */ #define UDATA(act,fl,cap) NULL,act,NULL,NULL,NULL,0,0,(fl),0,(cap),0,NULL,0,0 #if defined (__STDC__) || defined (_WIN32) /* Variants which depend on how macro arguments are convered to strings */ /* Generic Register declaration for all fields. If the register structure is extended, this macro will be retained and a new macro will be provided that populates the new register structure */ #define REGDATA(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \ #nm, &(loc), (rdx), (wd), (off), (dep), (desc), (flds), (fl), (qptr), (siz) /* Right Justified Octal Register Data */ #define ORDATA(nm,loc,wd) #nm, &(loc), 8, (wd), 0, 1, NULL, NULL /* Right Justified Decimal Register Data */ #define DRDATA(nm,loc,wd) #nm, &(loc), 10, (wd), 0, 1, NULL, NULL /* Right Justified Hexadecimal Register Data */ #define HRDATA(nm,loc,wd) #nm, &(loc), 16, (wd), 0, 1, NULL, NULL /* Right Justified Binary Register Data */ #define BINRDATA(nm,loc,wd) #nm, &(loc), 2, (wd), 0, 1, NULL, NULL /* One-bit binary flag at an arbitrary offset in a 32-bit word Register */ #define FLDATA(nm,loc,pos) #nm, &(loc), 2, 1, (pos), 1, NULL, NULL /* Arbitrary location and Radix Register */ #define GRDATA(nm,loc,rdx,wd,pos) #nm, &(loc), (rdx), (wd), (pos), 1, NULL, NULL /* Arrayed register whose data is kept in a standard C array Register */ #define BRDATA(nm,loc,rdx,wd,dep) #nm, (loc), (rdx), (wd), 0, (dep), NULL, NULL /* Same as above, but with additional description initializer */ #define ORDATAD(nm,loc,wd,desc) #nm, &(loc), 8, (wd), 0, 1, (desc), NULL #define DRDATAD(nm,loc,wd,desc) #nm, &(loc), 10, (wd), 0, 1, (desc), NULL #define HRDATAD(nm,loc,wd,desc) #nm, &(loc), 16, (wd), 0, 1, (desc), NULL #define BINRDATAD(nm,loc,wd,desc) #nm, &(loc), 2, (wd), 0, 1, (desc), NULL #define FLDATAD(nm,loc,pos,desc) #nm, &(loc), 2, 1, (pos), 1, (desc), NULL #define GRDATAD(nm,loc,rdx,wd,pos,desc) #nm, &(loc), (rdx), (wd), (pos), 1, (desc), NULL #define BRDATAD(nm,loc,rdx,wd,dep,desc) #nm, (loc), (rdx), (wd), 0, (dep), (desc), NULL /* Same as above, but with additional description initializer, and bitfields */ #define ORDATADF(nm,loc,wd,desc,flds) #nm, &(loc), 8, (wd), 0, 1, (desc), (flds) #define DRDATADF(nm,loc,wd,desc,flds) #nm, &(loc), 10, (wd), 0, 1, (desc), (flds) #define HRDATADF(nm,loc,wd,desc,flds) #nm, &(loc), 16, (wd), 0, 1, (desc), (flds) #define BINRDATADF(nm,loc,wd) #nm, &(loc), 2, (wd), 0, 1, NULL, NULL #define FLDATADF(nm,loc,pos,desc,flds) #nm, &(loc), 2, 1, (pos), 1, (desc), (flds) #define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) #nm, &(loc), (rdx), (wd), (pos), 1, (desc), (flds) #define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) #nm, (loc), (rdx), (wd), 0, (dep), (desc), (flds) #define BIT(nm) {#nm, 0xffffffff, 1} /* Single Bit definition */ #define BITNC {"", 0xffffffff, 1} /* Don't care Bit definition */ #define BITF(nm,sz) {#nm, 0xffffffff, sz} /* Bit Field definition */ #define BITNCF(sz) {"", 0xffffffff, sz} /* Don't care Bit Field definition */ #define BITFFMT(nm,sz,fmt) {#nm, 0xffffffff, sz, NULL, #fmt}/* Bit Field definition with Output format */ #define BITFNAM(nm,sz,names) {#nm, 0xffffffff, sz, names} /* Bit Field definition with value->name map */ #else /* For non-STD-C compiler which can't stringify macro arguments with # */ #define REGDATA(nm,loc,rdx,wd,off,dep,desc,flds,fl,qptr,siz) \ "nm", &(loc), (rdx), (wd), (off), (dep), (desc), (flds), (fl), (qptr), (siz) #define ORDATA(nm,loc,wd) "nm", &(loc), 8, (wd), 0, 1, NULL, NULL #define DRDATA(nm,loc,wd) "nm", &(loc), 10, (wd), 0, 1, NULL, NULL #define HRDATA(nm,loc,wd) "nm", &(loc), 16, (wd), 0, 1, NULL, NULL #define BINRDATA(nm,loc,wd) "nm", &(loc), 2, (wd), 0, 1, NULL, NULL #define FLDATA(nm,loc,pos) "nm", &(loc), 2, 1, (pos), 1, NULL, NULL #define GRDATA(nm,loc,rdx,wd,pos) "nm", &(loc), (rdx), (wd), (pos), 1, NULL, NULL #define BRDATA(nm,loc,rdx,wd,dep) "nm", (loc), (rdx), (wd), 0, (dep), NULL, NULL #define ORDATAD(nm,loc,wd,desc) "nm", &(loc), 8, (wd), 0, 1, (desc), NULL #define DRDATAD(nm,loc,wd,desc) "nm", &(loc), 10, (wd), 0, 1, (desc), NULL #define HRDATAD(nm,loc,wd,desc) "nm", &(loc), 16, (wd), 0, 1, (desc), NULL #define BINRDATAD(nm,loc,wd,desc) "nm", &(loc), 2, (wd), 0, 1, (desc), NULL #define FLDATAD(nm,loc,pos,desc) "nm", &(loc), 2, 1, (pos), 1, (desc), NULL #define GRDATAD(nm,loc,rdx,wd,pos,desc) "nm", &(loc), (rdx), (wd), (pos), 1, (desc), NULL #define BRDATAD(nm,loc,rdx,wd,dep,desc) "nm", (loc), (rdx), (wd), 0, (dep), (desc), NULL #define ORDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 8, (wd), 0, 1, (desc), (flds) #define DRDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 10, (wd), 0, 1, (desc), (flds) #define HRDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 16, (wd), 0, 1, (desc), (flds) #define BINRDATADF(nm,loc,wd,desc,flds) "nm", &(loc), 2, (wd), 0, 1, (desc), (flds) #define FLDATADF(nm,loc,pos,desc,flds) "nm", &(loc), 2, 1, (pos), 1, (desc), (flds) #define GRDATADF(nm,loc,rdx,wd,pos,desc,flds) "nm", &(loc), (rdx), (wd), (pos), 1, (desc), (flds) #define BRDATADF(nm,loc,rdx,wd,dep,desc,flds) "nm", (loc), (rdx), (wd), 0, (dep), (desc), (flds) #define BIT(nm) {"nm", 0xffffffff, 1} /* Single Bit definition */ #define BITNC {"", 0xffffffff, 1} /* Don't care Bit definition */ #define BITF(nm,sz) {"nm", 0xffffffff, sz} /* Bit Field definition */ #define BITNCF(sz) {"", 0xffffffff, sz} /* Don't care Bit Field definition */ #define BITFFMT(nm,sz,fmt) {"nm", 0xffffffff, sz, NULL, "fmt"}/* Bit Field definition with Output format */ #define BITFNAM(nm,sz,names) {"nm", 0xffffffff, sz, names} /* Bit Field definition with value->name map */ #endif #define ENDBITS {NULL} /* end of bitfield list */ /* Arrayed register whose data is part of the UNIT structure */ #define URDATA(nm,loc,rdx,wd,off,dep,fl) \ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),NULL,NULL,((fl) | REG_UNIT),0,0) /* Arrayed register whose data is part of an arbitrary structure */ #define STRDATA(nm,loc,rdx,wd,off,dep,siz,fl) \ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),NULL,NULL,((fl) | REG_STRUCT),0,(siz)) /* Same as above, but with additional description initializer */ #define URDATAD(nm,loc,rdx,wd,off,dep,fl,desc) \ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),NULL,((fl) | REG_UNIT),0,0) #define STRDATAD(nm,loc,rdx,wd,off,dep,siz,fl,desc) \ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),NULL,((fl) | REG_STRUCT),0,(siz)) /* Same as above, but with additional description initializer, and bitfields */ #define URDATADF(nm,loc,rdx,wd,off,dep,fl,desc,flds) \ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),(flds),((fl) | REG_UNIT),0,0) #define STRDATADF(nm,loc,rdx,wd,off,dep,siz,fl,desc,flds) \ REGDATA(nm,(loc),(rdx),(wd),(off),(dep),(desc),(flds),((fl) | REG_STRUCT),0,(siz)) /* Function prototypes */ #include "scp.h" #include "sim_console.h" #include "sim_timer.h" #include "sim_fio.h" /* Macro to ALWAYS execute the specified expression and fail if it evaluates to false. */ /* This replaces any references to "assert()" which should never be invoked */ /* with an expression which causes side effects (i.e. must be executed for */ /* the program to work correctly) */ #define ASSURE(_Expression) while (!(_Expression)) {fprintf(stderr, "%s failed at %s line %d\n", #_Expression, __FILE__, __LINE__); \ sim_printf("%s failed at %s line %d\n", #_Expression, __FILE__, __LINE__); \ abort();} /* Asynch/Threaded I/O support */ #if defined (SIM_ASYNCH_IO) #include <pthread.h> #define SIM_ASYNCH_CLOCKS 1 extern pthread_mutex_t sim_asynch_lock; extern pthread_cond_t sim_asynch_wake; extern pthread_mutex_t sim_timer_lock; extern pthread_cond_t sim_timer_wake; extern t_bool sim_timer_event_canceled; extern int32 sim_tmxr_poll_count; extern pthread_cond_t sim_tmxr_poll_cond; extern pthread_mutex_t sim_tmxr_poll_lock; extern pthread_t sim_asynch_main_threadid; extern UNIT * volatile sim_asynch_queue; extern volatile t_bool sim_idle_wait; extern int32 sim_asynch_check; extern int32 sim_asynch_latency; extern int32 sim_asynch_inst_latency; /* Thread local storage */ #if defined(__GNUC__) && !defined(__APPLE__) && !defined(__hpux) && !defined(__OpenBSD__) && !defined(_AIX) #define AIO_TLS __thread #elif defined(_MSC_VER) #define AIO_TLS __declspec(thread) #else /* Other compiler environment, then don't worry about thread local storage. */ /* It is primarily used only used in debugging messages */ #define AIO_TLS #endif #define AIO_QUEUE_CHECK(que, lock) \ do { \ UNIT *_cptr; \ if (lock) \ pthread_mutex_lock (lock); \ for (_cptr = que; \ (_cptr != QUEUE_LIST_END); \ _cptr = _cptr->next) \ if (!_cptr->next) { \ if (sim_deb) { \ sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Corruption detected\n");\ fclose(sim_deb); \ } \ sim_printf("Queue Corruption detected\n"); \ abort(); \ } \ if (lock) \ pthread_mutex_unlock (lock); \ } while (0) #define AIO_MAIN_THREAD (pthread_equal ( pthread_self(), sim_asynch_main_threadid )) #define AIO_LOCK \ pthread_mutex_lock(&sim_asynch_lock) #define AIO_UNLOCK \ pthread_mutex_unlock(&sim_asynch_lock) #define AIO_IS_ACTIVE(uptr) (((uptr)->a_is_active ? (uptr)->a_is_active (uptr) : FALSE) || ((uptr)->a_next)) #if defined(SIM_ASYNCH_MUX) #define AIO_CANCEL(uptr) \ if (((uptr)->dynflags & UNIT_TM_POLL) && \ !((uptr)->next) && !((uptr)->a_next)) { \ (uptr)->a_polling_now = FALSE; \ sim_tmxr_poll_count -= (uptr)->a_poll_waiter_count; \ (uptr)->a_poll_waiter_count = 0; \ } #endif /* defined(SIM_ASYNCH_MUX) */ #if !defined(AIO_CANCEL) #define AIO_CANCEL(uptr) #endif /* !defined(AIO_CANCEL) */ #define AIO_EVENT_BEGIN(uptr) \ do { \ int __was_poll = uptr->dynflags & UNIT_TM_POLL #define AIO_EVENT_COMPLETE(uptr, reason) \ if (__was_poll) { \ pthread_mutex_lock (&sim_tmxr_poll_lock); \ uptr->a_polling_now = FALSE; \ if (uptr->a_poll_waiter_count) { \ sim_tmxr_poll_count -= uptr->a_poll_waiter_count; \ uptr->a_poll_waiter_count = 0; \ if (0 == sim_tmxr_poll_count) \ pthread_cond_broadcast (&sim_tmxr_poll_cond); \ } \ pthread_mutex_unlock (&sim_tmxr_poll_lock); \ } \ AIO_UPDATE_QUEUE; \ } while (0) #if defined(__DECC_VER) #include <builtins> #if defined(__IA64) #define USE_AIO_INTRINSICS 1 #endif #endif #if defined(_WIN32) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) #define USE_AIO_INTRINSICS 1 #endif /* Provide a way to test both Intrinsic and Lock based queue manipulations */ /* when both are available on a particular platform */ #if defined(DONT_USE_AIO_INTRINSICS) && defined(USE_AIO_INTRINSICS) #undef USE_AIO_INTRINSICS #endif #ifdef USE_AIO_INTRINSICS /* This approach uses intrinsics to manage access to the link list head */ /* sim_asynch_queue. This implementation is a completely lock free design */ /* which avoids the potential ABA issues. */ #define AIO_QUEUE_MODE "Lock free asynchronous event queue access" #define AIO_INIT \ do { \ int tmr; \ sim_asynch_main_threadid = pthread_self(); \ /* Empty list/list end uses the point value (void *)1. \ This allows NULL in an entry's a_next pointer to \ indicate that the entry is not currently in any list */ \ sim_asynch_queue = QUEUE_LIST_END; \ for (tmr=0; tmr<SIM_NTIMERS; tmr++) \ sim_clock_cosched_queue[tmr] = QUEUE_LIST_END; \ } while (0) #define AIO_CLEANUP \ do { \ pthread_mutex_destroy(&sim_asynch_lock); \ pthread_cond_destroy(&sim_asynch_wake); \ pthread_mutex_destroy(&sim_timer_lock); \ pthread_cond_destroy(&sim_timer_wake); \ pthread_mutex_destroy(&sim_tmxr_poll_lock); \ pthread_cond_destroy(&sim_tmxr_poll_cond); \ } while (0) #ifdef _WIN32 #elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8) #define InterlockedCompareExchangePointer(Destination, Exchange, Comparand) __sync_val_compare_and_swap(Destination, Comparand, Exchange) #elif defined(__DECC_VER) #define InterlockedCompareExchangePointer(Destination, Exchange, Comparand) (void *)((int32)_InterlockedCompareExchange64(Destination, Exchange, Comparand)) #else #error "Implementation of function InterlockedCompareExchangePointer() is needed to build with USE_AIO_INTRINSICS" #endif #define AIO_ILOCK AIO_LOCK #define AIO_IUNLOCK AIO_UNLOCK #define AIO_QUEUE_VAL (UNIT *)(InterlockedCompareExchangePointer((void * volatile *)&sim_asynch_queue, (void *)sim_asynch_queue, NULL)) #define AIO_QUEUE_SET(val, queue) (UNIT *)(InterlockedCompareExchangePointer((void * volatile *)&sim_asynch_queue, (void *)val, queue)) #define AIO_UPDATE_QUEUE sim_aio_update_queue () #define AIO_ACTIVATE(caller, uptr, event_time) \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ sim_aio_activate ((ACTIVATE_API)caller, uptr, event_time); \ return SCPE_OK; \ } else (void)0 #else /* !USE_AIO_INTRINSICS */ /* This approach uses a pthread mutex to manage access to the link list */ /* head sim_asynch_queue. It will always work, but may be slower than the */ /* lock free approach when using USE_AIO_INTRINSICS */ #define AIO_QUEUE_MODE "Lock based asynchronous event queue access" #define AIO_INIT \ do { \ int tmr; \ pthread_mutexattr_t attr; \ \ pthread_mutexattr_init (&attr); \ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ pthread_mutex_init (&sim_asynch_lock, &attr); \ pthread_mutexattr_destroy (&attr); \ sim_asynch_main_threadid = pthread_self(); \ /* Empty list/list end uses the point value (void *)1. \ This allows NULL in an entry's a_next pointer to \ indicate that the entry is not currently in any list */ \ sim_asynch_queue = QUEUE_LIST_END; \ for (tmr=0; tmr<SIM_NTIMERS; tmr++) \ sim_clock_cosched_queue[tmr] = QUEUE_LIST_END; \ } while (0) #define AIO_CLEANUP \ do { \ pthread_mutex_destroy(&sim_asynch_lock); \ pthread_cond_destroy(&sim_asynch_wake); \ pthread_mutex_destroy(&sim_timer_lock); \ pthread_cond_destroy(&sim_timer_wake); \ pthread_mutex_destroy(&sim_tmxr_poll_lock); \ pthread_cond_destroy(&sim_tmxr_poll_cond); \ } while (0) #define AIO_ILOCK AIO_LOCK #define AIO_IUNLOCK AIO_UNLOCK #define AIO_QUEUE_VAL sim_asynch_queue #define AIO_QUEUE_SET(val, queue) (sim_asynch_queue = val) #define AIO_UPDATE_QUEUE sim_aio_update_queue () #define AIO_ACTIVATE(caller, uptr, event_time) \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch event for %s after %d instructions\n", sim_uname(uptr), event_time);\ AIO_LOCK; \ if (uptr->a_next) { /* already queued? */ \ uptr->a_activate_call = sim_activate_abs; \ } else { \ uptr->a_next = sim_asynch_queue; \ uptr->a_event_time = event_time; \ uptr->a_activate_call = (ACTIVATE_API)&caller; \ sim_asynch_queue = uptr; \ } \ if (sim_idle_wait) { \ sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(uptr), event_time);\ pthread_cond_signal (&sim_asynch_wake); \ } \ AIO_UNLOCK; \ sim_asynch_check = 0; \ return SCPE_OK; \ } else (void)0 #endif /* USE_AIO_INTRINSICS */ #define AIO_VALIDATE if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) {sim_printf("Improper thread context for operation\n"); abort();} #define AIO_CHECK_EVENT \ if (0 > --sim_asynch_check) { \ AIO_UPDATE_QUEUE; \ sim_asynch_check = sim_asynch_inst_latency; \ } else (void)0 #define AIO_SET_INTERRUPT_LATENCY(instpersec) \ do { \ sim_asynch_inst_latency = (int32)((((double)(instpersec))*sim_asynch_latency)/1000000000);\ if (sim_asynch_inst_latency == 0) \ sim_asynch_inst_latency = 1; \ } while (0) #else /* !SIM_ASYNCH_IO */ #define AIO_QUEUE_MODE "Asynchronous I/O is not available" #define AIO_UPDATE_QUEUE #define AIO_ACTIVATE(caller, uptr, event_time) #define AIO_VALIDATE #define AIO_CHECK_EVENT #define AIO_INIT #define AIO_MAIN_THREAD TRUE #define AIO_LOCK #define AIO_UNLOCK #define AIO_CLEANUP #define AIO_EVENT_BEGIN(uptr) #define AIO_EVENT_COMPLETE(uptr, reason) #define AIO_IS_ACTIVE(uptr) FALSE #define AIO_CANCEL(uptr) #define AIO_SET_INTERRUPT_LATENCY(instpersec) #define AIO_TLS #endif /* SIM_ASYNCH_IO */ #ifdef __cplusplus } #endif #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 | /* sim_disk.c: simulator disk support library Copyright (c) 2011, Mark Pizzolato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of Mark Pizzolato shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Mark Pizzolato. This is the place which hides processing of various disk formats, as well as OS-specific direct hardware access. 25-Jan-11 MP Initial Implemementation Public routines: sim_disk_attach attach disk unit sim_disk_detach detach disk unit sim_disk_attach_help help routine for attaching disks sim_disk_rdsect read disk sectors sim_disk_rdsect_a read disk sectors asynchronously sim_disk_wrsect write disk sectors sim_disk_wrsect_a write disk sectors asynchronously sim_disk_unload unload or detach a disk as needed sim_disk_reset reset unit sim_disk_wrp TRUE if write protected sim_disk_isavailable TRUE if available for I/O sim_disk_size get disk size sim_disk_set_fmt set disk format sim_disk_show_fmt show disk format sim_disk_set_capac set disk capacity sim_disk_show_capac show disk capacity sim_disk_set_async enable asynchronous operation sim_disk_clr_async disable asynchronous operation sim_disk_data_trace debug support Internal routines: sim_os_disk_open_raw platform specific open raw device sim_os_disk_close_raw platform specific close raw device sim_os_disk_size_raw platform specific raw device size sim_os_disk_unload_raw platform specific disk unload/eject sim_os_disk_rdsect platform specific read sectors sim_os_disk_wrsect platform specific write sectors sim_vhd_disk_open platform independent open virtual disk file sim_vhd_disk_create platform independent create virtual disk file sim_vhd_disk_create_diff platform independent create differencing virtual disk file sim_vhd_disk_close platform independent close virtual disk file sim_vhd_disk_size platform independent virtual disk size sim_vhd_disk_rdsect platform independent read virtual disk sectors sim_vhd_disk_wrsect platform independent write virtual disk sectors */ #define _FILE_OFFSET_BITS 64 /* 64 bit file offset for raw I/O operations */ #include "sim_defs.h" #include "sim_disk.h" #include "sim_ether.h" #include <ctype.h> #include <sys/stat.h> #ifdef _WIN32 #include <windows.h> #endif #if defined SIM_ASYNCH_IO #include <pthread.h> #endif struct disk_context { DEVICE *dptr; /* Device for unit (access to debug flags) */ uint32 dbit; /* debugging bit */ uint32 sector_size; /* Disk Sector Size (of the pseudo disk) */ uint32 capac_factor; /* Units of Capacity (2 = word, 1 = byte) */ uint32 xfer_element_size; /* Disk Bus Transfer size (1 - byte, 2 - word, 4 - longword) */ uint32 storage_sector_size;/* Sector size of the containing storage */ uint32 removable; /* Removable device flag */ uint32 auto_format; /* Format determined dynamically */ #if defined _WIN32 HANDLE disk_handle; /* OS specific Raw device handle */ #endif #if defined SIM_ASYNCH_IO int asynch_io; /* Asynchronous Interrupt scheduling enabled */ int asynch_io_latency; /* instructions to delay pending interrupt */ pthread_mutex_t lock; pthread_t io_thread; /* I/O Thread Id */ pthread_mutex_t io_lock; pthread_cond_t io_cond; pthread_cond_t io_done; pthread_cond_t startup_cond; int io_dop; uint8 *buf; t_seccnt *rsects; t_seccnt sects; t_lba lba; DISK_PCALLBACK callback; t_stat io_status; #endif }; #define disk_ctx up8 /* Field in Unit structure which points to the disk_context */ #if defined SIM_ASYNCH_IO #define AIO_CALLSETUP \ struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; \ \ if ((!callback) || !ctx->asynch_io) #define AIO_CALL(op, _lba, _buf, _rsects, _sects, _callback) \ if (ctx->asynch_io) { \ struct disk_context *ctx = \ (struct disk_context *)uptr->disk_ctx; \ \ pthread_mutex_lock (&ctx->io_lock); \ \ sim_debug (ctx->dbit, ctx->dptr, \ "sim_disk AIO_CALL(op=%d, unit=%d, lba=0x%X, sects=%d)\n",\ op, (int)(uptr-ctx->dptr->units), _lba, _sects);\ \ if (ctx->callback) \ abort(); /* horrible mistake, stop */ \ ctx->io_dop = op; \ ctx->lba = _lba; \ ctx->buf = _buf; \ ctx->sects = _sects; \ ctx->rsects = _rsects; \ ctx->callback = _callback; \ pthread_cond_signal (&ctx->io_cond); \ pthread_mutex_unlock (&ctx->io_lock); \ } \ else \ if (_callback) \ (_callback) (uptr, r); #define DOP_DONE 0 /* close */ #define DOP_RSEC 1 /* sim_disk_rdsect_a */ #define DOP_WSEC 2 /* sim_disk_wrsect_a */ #define DOP_IAVL 3 /* sim_disk_isavailable_a */ static void * _disk_io(void *arg) { UNIT* volatile uptr = (UNIT*)arg; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; /* Boost Priority for this I/O thread vs the CPU instruction execution thread which in general won't be readily yielding the processor when this thread needs to run */ sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); sim_debug (ctx->dbit, ctx->dptr, "_disk_io(unit=%d) starting\n", (int)(uptr-ctx->dptr->units)); pthread_mutex_lock (&ctx->io_lock); pthread_cond_signal (&ctx->startup_cond); /* Signal we're ready to go */ while (ctx->asynch_io) { pthread_cond_wait (&ctx->io_cond, &ctx->io_lock); if (ctx->io_dop == DOP_DONE) break; pthread_mutex_unlock (&ctx->io_lock); switch (ctx->io_dop) { case DOP_RSEC: ctx->io_status = sim_disk_rdsect (uptr, ctx->lba, ctx->buf, ctx->rsects, ctx->sects); break; case DOP_WSEC: ctx->io_status = sim_disk_wrsect (uptr, ctx->lba, ctx->buf, ctx->rsects, ctx->sects); break; case DOP_IAVL: ctx->io_status = sim_disk_isavailable (uptr); break; } pthread_mutex_lock (&ctx->io_lock); ctx->io_dop = DOP_DONE; pthread_cond_signal (&ctx->io_done); sim_activate (uptr, ctx->asynch_io_latency); } pthread_mutex_unlock (&ctx->io_lock); sim_debug (ctx->dbit, ctx->dptr, "_disk_io(unit=%d) exiting\n", (int)(uptr-ctx->dptr->units)); return NULL; } /* This routine is called in the context of the main simulator thread before processing events for any unit. It is only called when an asynchronous thread has called sim_activate() to activate a unit. The job of this routine is to put the unit in proper condition to digest what may have occurred in the asynchrconous thread. Since disk processing only handles a single I/O at a time to a particular disk device (due to using stdio for the SimH Disk format and stdio doesn't have an atomic seek+(read|write) operation), we have the opportunity to possibly detect improper attempts to issue multiple concurrent I/O requests. */ static void _disk_completion_dispatch (UNIT *uptr) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; DISK_PCALLBACK callback = ctx->callback; sim_debug (ctx->dbit, ctx->dptr, "_disk_completion_dispatch(unit=%d, dop=%d, callback=%p)\n", (int)(uptr-ctx->dptr->units), ctx->io_dop, ctx->callback); if (ctx->io_dop != DOP_DONE) abort(); /* horribly wrong, stop */ if (ctx->callback && ctx->io_dop == DOP_DONE) { ctx->callback = NULL; callback (uptr, ctx->io_status); } } static t_bool _disk_is_active (UNIT *uptr) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; if (ctx) { sim_debug (ctx->dbit, ctx->dptr, "_disk_is_active(unit=%d, dop=%d)\n", (int)(uptr-ctx->dptr->units), ctx->io_dop); return (ctx->io_dop != DOP_DONE); } return FALSE; } static t_bool _disk_cancel (UNIT *uptr) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; if (ctx) { sim_debug (ctx->dbit, ctx->dptr, "_disk_cancel(unit=%d, dop=%d)\n", (int)(uptr-ctx->dptr->units), ctx->io_dop); if (ctx->asynch_io) { pthread_mutex_lock (&ctx->io_lock); while (ctx->io_dop != DOP_DONE) pthread_cond_wait (&ctx->io_done, &ctx->io_lock); pthread_mutex_unlock (&ctx->io_lock); } } return FALSE; } #else #define AIO_CALLSETUP #define AIO_CALL(op, _lba, _buf, _rsects, _sects, _callback) \ if (_callback) \ (_callback) (uptr, r); #endif /* Forward declarations */ static t_stat sim_vhd_disk_implemented (void); static FILE *sim_vhd_disk_open (const char *rawdevicename, const char *openmode); static FILE *sim_vhd_disk_create (const char *szVHDPath, t_offset desiredsize); static FILE *sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath); static FILE *sim_vhd_disk_merge (const char *szVHDPath, char **ParentVHD); static int sim_vhd_disk_close (FILE *f); static void sim_vhd_disk_flush (FILE *f); static t_offset sim_vhd_disk_size (FILE *f); static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects); static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects); static t_stat sim_vhd_disk_clearerr (UNIT *uptr); static t_stat sim_vhd_disk_set_dtype (FILE *f, const char *dtype); static const char *sim_vhd_disk_get_dtype (FILE *f); static t_stat sim_os_disk_implemented_raw (void); static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode); static int sim_os_disk_close_raw (FILE *f); static void sim_os_disk_flush_raw (FILE *f); static t_offset sim_os_disk_size_raw (FILE *f); static t_stat sim_os_disk_unload_raw (FILE *f); static t_bool sim_os_disk_isavailable_raw (FILE *f); static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects); static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects); static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable); static t_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec); static char *HostPathToVhdPath (const char *szHostPath, char *szVhdPath, size_t VhdPathSize); static char *VhdPathToHostPath (const char *szVhdPath, char *szHostPath, size_t HostPathSize); struct sim_disk_fmt { const char *name; /* name */ int32 uflags; /* unit flags */ int32 fmtval; /* Format type value */ t_stat (*impl_fnc)(void); /* Implemented Test Function */ }; static struct sim_disk_fmt fmts[DKUF_N_FMT] = { { "SIMH", 0, DKUF_F_STD, NULL}, { "RAW", 0, DKUF_F_RAW, sim_os_disk_implemented_raw}, { "VHD", 0, DKUF_F_VHD, sim_vhd_disk_implemented}, { NULL, 0, 0} }; /* Set disk format */ t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { uint32 f; if (uptr == NULL) return SCPE_IERR; if (cptr == NULL) return SCPE_ARG; for (f = 0; f < DKUF_N_FMT && fmts[f].name; f++) { if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) { if ((fmts[f].impl_fnc) && (fmts[f].impl_fnc() != SCPE_OK)) return SCPE_NOFNC; uptr->flags = (uptr->flags & ~DKUF_FMT) | (fmts[f].fmtval << DKUF_V_FMT) | fmts[f].uflags; return SCPE_OK; } } return SCPE_ARG; } /* Show disk format */ t_stat sim_disk_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { int32 f = DK_GET_FMT (uptr); size_t i; for (i = 0; i < DKUF_N_FMT; i++) if (fmts[i].fmtval == f) { fprintf (st, "%s format", fmts[i].name); return SCPE_OK; } fprintf (st, "invalid format"); return SCPE_OK; } /* Set disk capacity */ t_stat sim_disk_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { t_offset cap; t_stat r; DEVICE *dptr = find_dev_from_unit (uptr); if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; cap = (t_offset) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r); if (r != SCPE_OK) return SCPE_ARG; uptr->capac = (t_addr)((cap * ((t_offset) 1000000))/((dptr->flags & DEV_SECTORS) ? 512 : 1)); return SCPE_OK; } /* Show disk capacity */ t_stat sim_disk_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { const char *cap_units = "B"; DEVICE *dptr = find_dev_from_unit (uptr); t_offset capac = ((t_offset)uptr->capac)*((dptr->flags & DEV_SECTORS) ? 512 : 1); if ((dptr->dwidth / dptr->aincr) == 16) cap_units = "W"; if (capac) { if (capac >= (t_offset) 1000000) fprintf (st, "capacity=%dM%s", (uint32) (capac / ((t_offset) 1000000)), cap_units); else if (uptr->capac >= (t_addr) 1000) fprintf (st, "capacity=%dK%s", (uint32) (capac / ((t_offset) 1000)), cap_units); else fprintf (st, "capacity=%d%s", (uint32) capac, cap_units); } else fprintf (st, "undefined capacity"); return SCPE_OK; } /* Test for available */ t_bool sim_disk_isavailable (UNIT *uptr) { if (!(uptr->flags & UNIT_ATT)) /* attached? */ return FALSE; switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* SIMH format */ return TRUE; case DKUF_F_VHD: /* VHD format */ return TRUE; break; case DKUF_F_RAW: /* Raw Physical Disk Access */ return sim_os_disk_isavailable_raw (uptr->fileref); break; default: return FALSE; } } t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback) { t_bool r = FALSE; AIO_CALLSETUP r = sim_disk_isavailable (uptr); AIO_CALL(DOP_IAVL, 0, NULL, NULL, 0, callback); return r; } /* Test for write protect */ t_bool sim_disk_wrp (UNIT *uptr) { return (uptr->flags & DKUF_WRP)? TRUE: FALSE; } /* Get Disk size */ t_offset sim_disk_size (UNIT *uptr) { switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* SIMH format */ return sim_fsize_ex (uptr->fileref); case DKUF_F_VHD: /* VHD format */ return sim_vhd_disk_size (uptr->fileref); break; case DKUF_F_RAW: /* Raw Physical Disk Access */ return sim_os_disk_size_raw (uptr->fileref); break; default: return (t_offset)-1; } } /* Enable asynchronous operation */ t_stat sim_disk_set_async (UNIT *uptr, int latency) { #if !defined(SIM_ASYNCH_IO) char *msg = "Disk: can't operate asynchronously\r\n"; sim_printf ("%s", msg); return SCPE_NOFNC; #else struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; pthread_attr_t attr; sim_debug (ctx->dbit, ctx->dptr, "sim_disk_set_async(unit=%d)\n", (int)(uptr-ctx->dptr->units)); ctx->asynch_io = sim_asynch_enabled; ctx->asynch_io_latency = latency; if (ctx->asynch_io) { pthread_mutex_init (&ctx->io_lock, NULL); pthread_cond_init (&ctx->io_cond, NULL); pthread_cond_init (&ctx->io_done, NULL); pthread_cond_init (&ctx->startup_cond, NULL); pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_mutex_lock (&ctx->io_lock); pthread_create (&ctx->io_thread, &attr, _disk_io, (void *)uptr); pthread_attr_destroy(&attr); pthread_cond_wait (&ctx->startup_cond, &ctx->io_lock); /* Wait for thread to stabilize */ pthread_mutex_unlock (&ctx->io_lock); pthread_cond_destroy (&ctx->startup_cond); } uptr->a_check_completion = _disk_completion_dispatch; uptr->a_is_active = _disk_is_active; uptr->cancel = _disk_cancel; return SCPE_OK; #endif } /* Disable asynchronous operation */ t_stat sim_disk_clr_async (UNIT *uptr) { #if !defined(SIM_ASYNCH_IO) return SCPE_NOFNC; #else struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; /* make sure device exists */ if (!ctx) return SCPE_UNATT; sim_debug (ctx->dbit, ctx->dptr, "sim_disk_clr_async(unit=%d)\n", (int)(uptr-ctx->dptr->units)); if (ctx->asynch_io) { pthread_mutex_lock (&ctx->io_lock); ctx->asynch_io = 0; pthread_cond_signal (&ctx->io_cond); pthread_mutex_unlock (&ctx->io_lock); pthread_join (ctx->io_thread, NULL); pthread_mutex_destroy (&ctx->io_lock); pthread_cond_destroy (&ctx->io_cond); pthread_cond_destroy (&ctx->io_done); } return SCPE_OK; #endif } /* Read Sectors */ static t_stat _sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) { t_offset da; uint32 err, tbc; size_t i; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects); da = ((t_offset)lba) * ctx->sector_size; tbc = sects * ctx->sector_size; if (sectsread) *sectsread = 0; err = sim_fseeko (uptr->fileref, da, SEEK_SET); /* set pos */ if (!err) { i = sim_fread (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref); if (i < tbc/ctx->xfer_element_size) /* fill */ memset (&buf[i*ctx->xfer_element_size], 0, tbc-(i*ctx->xfer_element_size)); err = ferror (uptr->fileref); if ((!err) && (sectsread)) *sectsread = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size); } return err; } t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) { t_stat r; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; t_seccnt sread = 0; sim_debug (ctx->dbit, ctx->dptr, "sim_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects); if ((sects == 1) && /* Single sector reads */ (lba >= (uptr->capac*ctx->capac_factor)/(ctx->sector_size/((ctx->dptr->flags & DEV_SECTORS) ? 512 : 1)))) {/* beyond the end of the disk */ memset (buf, '\0', ctx->sector_size); /* are bad block management efforts - zero buffer */ if (sectsread) *sectsread = 1; return SCPE_OK; /* return success */ } if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) || /* Sector Aligned & whole sector transfers */ ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) && (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) { switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* SIMH format */ return _sim_disk_rdsect (uptr, lba, buf, sectsread, sects); case DKUF_F_VHD: /* VHD format */ r = sim_vhd_disk_rdsect (uptr, lba, buf, &sread, sects); break; case DKUF_F_RAW: /* Raw Physical Disk Access */ r = sim_os_disk_rdsect (uptr, lba, buf, &sread, sects); break; default: return SCPE_NOFNC; } if (sectsread) *sectsread = sread; if (r != SCPE_OK) return r; sim_buf_swap_data (buf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size); return r; } else { /* Unaligned and/or partial sector transfers */ uint8 *tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size); t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */ t_lba tlba = lba & ~(sspsts - 1); t_seccnt tsects = sects + (lba - tlba); tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1); if (sectsread) *sectsread = 0; if (tbuf == NULL) return SCPE_MEM; switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* SIMH format */ r = _sim_disk_rdsect (uptr, tlba, tbuf, &sread, tsects); break; case DKUF_F_VHD: /* VHD format */ r = sim_vhd_disk_rdsect (uptr, tlba, tbuf, &sread, tsects); if (r == SCPE_OK) sim_buf_swap_data (tbuf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size); break; case DKUF_F_RAW: /* Raw Physical Disk Access */ r = sim_os_disk_rdsect (uptr, tlba, tbuf, &sread, tsects); if (r == SCPE_OK) sim_buf_swap_data (tbuf, ctx->xfer_element_size, (sread * ctx->sector_size) / ctx->xfer_element_size); break; default: free (tbuf); return SCPE_NOFNC; } if (r == SCPE_OK) { memcpy (buf, tbuf + ((lba - tlba) * ctx->sector_size), sects * ctx->sector_size); if (sectsread) { *sectsread = sread - (lba - tlba); if (*sectsread > sects) *sectsread = sects; } } free (tbuf); return r; } } t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback) { t_stat r = SCPE_OK; AIO_CALLSETUP r = sim_disk_rdsect (uptr, lba, buf, sectsread, sects); AIO_CALL(DOP_RSEC, lba, buf, sectsread, sects, callback); return r; } /* Write Sectors */ static t_stat _sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) { t_offset da; uint32 err, tbc; size_t i; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; sim_debug (ctx->dbit, ctx->dptr, "_sim_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects); da = ((t_offset)lba) * ctx->sector_size; tbc = sects * ctx->sector_size; if (sectswritten) *sectswritten = 0; err = sim_fseeko (uptr->fileref, da, SEEK_SET); /* set pos */ if (!err) { i = sim_fwrite (buf, ctx->xfer_element_size, tbc/ctx->xfer_element_size, uptr->fileref); err = ferror (uptr->fileref); if ((!err) && (sectswritten)) *sectswritten = (t_seccnt)((i*ctx->xfer_element_size+ctx->sector_size-1)/ctx->sector_size); } return err; } t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; uint32 f = DK_GET_FMT (uptr); t_stat r; uint8 *tbuf = NULL; sim_debug (ctx->dbit, ctx->dptr, "sim_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects); if (uptr->dynflags & UNIT_DISK_CHK) { DEVICE *dptr = find_dev_from_unit (uptr); uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */ t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(ctx->sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1))); t_lba sect; for (sect = 0; sect < sects; sect++) { t_lba offset; t_bool sect_error = FALSE; for (offset = 0; offset < ctx->sector_size; offset += sizeof(uint32)) { if (*((uint32 *)&buf[sect*ctx->sector_size + offset]) != (uint32)(lba + sect)) { sect_error = TRUE; break; } } if (sect_error) { uint32 save_dctrl = dptr->dctrl; FILE *save_sim_deb = sim_deb; sim_printf ("\n%s%d: Write Address Verification Error on lbn %d(0x%X) of %d(0x%X).\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(lba+sect), (int)(lba+sect), (int)total_sectors, (int)total_sectors); dptr->dctrl = 0xFFFFFFFF; sim_deb = save_sim_deb ? save_sim_deb : stdout; sim_disk_data_trace (uptr, buf+sect*ctx->sector_size, lba+sect, ctx->sector_size, "Found", TRUE, 1); dptr->dctrl = save_dctrl; sim_deb = save_sim_deb; } } } if (f == DKUF_F_STD) return _sim_disk_wrsect (uptr, lba, buf, sectswritten, sects); if ((0 == (ctx->sector_size & (ctx->storage_sector_size - 1))) || /* Sector Aligned & whole sector transfers */ ((0 == ((lba*ctx->sector_size) & (ctx->storage_sector_size - 1))) && (0 == ((sects*ctx->sector_size) & (ctx->storage_sector_size - 1))))) { if (sim_end || (ctx->xfer_element_size == sizeof (char))) switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_VHD: /* VHD format */ return sim_vhd_disk_wrsect (uptr, lba, buf, sectswritten, sects); case DKUF_F_RAW: /* Raw Physical Disk Access */ return sim_os_disk_wrsect (uptr, lba, buf, sectswritten, sects); default: return SCPE_NOFNC; } tbuf = (uint8*) malloc (sects * ctx->sector_size); if (NULL == tbuf) return SCPE_MEM; sim_buf_copy_swapped (tbuf, buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size); switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_VHD: /* VHD format */ r = sim_vhd_disk_wrsect (uptr, lba, tbuf, sectswritten, sects); break; case DKUF_F_RAW: /* Raw Physical Disk Access */ r = sim_os_disk_wrsect (uptr, lba, tbuf, sectswritten, sects); break; default: r = SCPE_NOFNC; break; } } else { /* Unaligned and/or partial sector transfers */ t_lba sspsts = ctx->storage_sector_size/ctx->sector_size; /* sim sectors in a storage sector */ t_lba tlba = lba & ~(sspsts - 1); t_seccnt tsects = sects + (lba - tlba); tbuf = (uint8*) malloc (sects*ctx->sector_size + 2*ctx->storage_sector_size); tsects = (tsects + (sspsts - 1)) & ~(sspsts - 1); if (sectswritten) *sectswritten = 0; if (tbuf == NULL) return SCPE_MEM; /* Partial Sector writes require a read-modify-write sequence for the partial sectors */ if ((lba & (sspsts - 1)) || (sects < sspsts)) switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_VHD: /* VHD format */ sim_vhd_disk_rdsect (uptr, tlba, tbuf, NULL, sspsts); break; case DKUF_F_RAW: /* Raw Physical Disk Access */ sim_os_disk_rdsect (uptr, tlba, tbuf, NULL, sspsts); break; default: r = SCPE_NOFNC; break; } if ((tsects > sspsts) && ((sects + lba - tlba) & (sspsts - 1))) switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_VHD: /* VHD format */ sim_vhd_disk_rdsect (uptr, tlba + tsects - sspsts, tbuf + (tsects - sspsts) * ctx->sector_size, NULL, sspsts); break; case DKUF_F_RAW: /* Raw Physical Disk Access */ sim_os_disk_rdsect (uptr, tlba + tsects - sspsts, tbuf + (tsects - sspsts) * ctx->sector_size, NULL, sspsts); break; default: r = SCPE_NOFNC; break; } sim_buf_copy_swapped (tbuf + (lba & (sspsts - 1)) * ctx->sector_size, buf, ctx->xfer_element_size, (sects * ctx->sector_size) / ctx->xfer_element_size); switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_VHD: /* VHD format */ r = sim_vhd_disk_wrsect (uptr, tlba, tbuf, sectswritten, tsects); break; case DKUF_F_RAW: /* Raw Physical Disk Access */ r = sim_os_disk_wrsect (uptr, tlba, tbuf, sectswritten, tsects); break; default: r = SCPE_NOFNC; break; } if ((r == SCPE_OK) && sectswritten) { *sectswritten -= (lba - tlba); if (*sectswritten > sects) *sectswritten = sects; } } free (tbuf); return r; } t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback) { t_stat r = SCPE_OK; AIO_CALLSETUP r = sim_disk_wrsect (uptr, lba, buf, sectswritten, sects); AIO_CALL(DOP_WSEC, lba, buf, sectswritten, sects, callback); return r; } t_stat sim_disk_unload (UNIT *uptr) { switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* Simh */ case DKUF_F_VHD: /* VHD format */ return sim_disk_detach (uptr); case DKUF_F_RAW: /* Raw Physical Disk Access */ return sim_os_disk_unload_raw (uptr->fileref); /* remove/eject disk */ break; default: return SCPE_NOFNC; } } /* This routine is called when the simulator stops and any time the asynch mode is changed (enabled or disabled) */ static void _sim_disk_io_flush (UNIT *uptr) { uint32 f = DK_GET_FMT (uptr); #if defined (SIM_ASYNCH_IO) struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; sim_disk_clr_async (uptr); if (sim_asynch_enabled) sim_disk_set_async (uptr, ctx->asynch_io_latency); #endif switch (f) { /* case on format */ case DKUF_F_STD: /* Simh */ fflush (uptr->fileref); break; case DKUF_F_VHD: /* Virtual Disk */ sim_vhd_disk_flush (uptr->fileref); break; case DKUF_F_RAW: /* Physical */ sim_os_disk_flush_raw (uptr->fileref); break; } } static t_stat _err_return (UNIT *uptr, t_stat stat) { free (uptr->filename); uptr->filename = NULL; free (uptr->disk_ctx); uptr->disk_ctx = NULL; return stat; } #pragma pack(push,1) typedef struct _ODS2_HomeBlock { uint32 hm2_l_homelbn; uint32 hm2_l_alhomelbn; uint32 hm2_l_altidxlbn; uint8 hm2_b_strucver; uint8 hm2_b_struclev; uint16 hm2_w_cluster; uint16 hm2_w_homevbn; uint16 hm2_w_alhomevbn; uint16 hm2_w_altidxvbn; uint16 hm2_w_ibmapvbn; uint32 hm2_l_ibmaplbn; uint32 hm2_l_maxfiles; uint16 hm2_w_ibmapsize; uint16 hm2_w_resfiles; uint16 hm2_w_devtype; uint16 hm2_w_rvn; uint16 hm2_w_setcount; uint16 hm2_w_volchar; uint32 hm2_l_volowner; uint32 hm2_l_reserved; uint16 hm2_w_protect; uint16 hm2_w_fileprot; uint16 hm2_w_reserved; uint16 hm2_w_checksum1; uint32 hm2_q_credate[2]; uint8 hm2_b_window; uint8 hm2_b_lru_lim; uint16 hm2_w_extend; uint32 hm2_q_retainmin[2]; uint32 hm2_q_retainmax[2]; uint32 hm2_q_revdate[2]; uint8 hm2_r_min_class[20]; uint8 hm2_r_max_class[20]; uint8 hm2_r_reserved[320]; uint32 hm2_l_serialnum; uint8 hm2_t_strucname[12]; uint8 hm2_t_volname[12]; uint8 hm2_t_ownername[12]; uint8 hm2_t_format[12]; uint16 hm2_w_reserved2; uint16 hm2_w_checksum2; } ODS2_HomeBlock; typedef struct _ODS2_FileHeader { uint8 fh2_b_idoffset; uint8 fh2_b_mpoffset; uint8 fh2_b_acoffset; uint8 fh2_b_rsoffset; uint16 fh2_w_seg_num; uint16 fh2_w_structlev; uint16 fh2_w_fid[3]; uint16 fh2_w_ext_fid[3]; uint16 fh2_w_recattr[16]; uint32 fh2_l_filechar; uint16 fh2_w_remaining[228]; } ODS2_FileHeader; typedef union _ODS2_Retreval { struct { unsigned fm2___fill : 14; /* type specific data */ unsigned fm2_v_format : 2; /* format type code */ } fm2_r_word0_bits; struct { unsigned fm2_v_exact : 1; /* exact placement specified */ unsigned fm2_v_oncyl : 1; /* on cylinder allocation desired */ unsigned fm2___fill : 10; unsigned fm2_v_lbn : 1; /* use LBN of next map pointer */ unsigned fm2_v_rvn : 1; /* place on specified RVN */ unsigned fm2_v_format0 : 2; } fm2_r_map_bits0; struct { unsigned fm2_b_count1 : 8; /* low byte described below */ unsigned fm2_v_highlbn1 : 6; /* high order LBN */ unsigned fm2_v_format1 : 2; unsigned fm2_w_lowlbn1 : 16; /* low order LBN */ } fm2_r_map_bits1; struct { struct { unsigned fm2_v_count2 : 14; /* count field */ unsigned fm2_v_format2 : 2; unsigned fm2_l_lowlbn2 : 16; /* low order LBN */ } fm2_r_map2_long0; uint16 fm2_l_highlbn2; /* high order LBN */ } fm2_r_map_bits2; struct { struct { unsigned fm2_v_highcount3 : 14; /* low order count field */ unsigned fm2_v_format3 : 2; unsigned fm2_w_lowcount3 : 16; /* high order count field */ } fm2_r_map3_long0; uint32 fm2_l_lbn3; } fm2_r_map_bits3; } ODS2_Retreval; typedef struct _ODS2_StorageControlBlock { uint8 scb_b_strucver; /* 1 */ uint8 scb_b_struclev; /* 2 */ uint16 scb_w_cluster; uint32 scb_l_volsize; uint32 scb_l_blksize; uint32 scb_l_sectors; uint32 scb_l_tracks; uint32 scb_l_cylinder; uint32 scb_l_status; uint32 scb_l_status2; uint16 scb_w_writecnt; uint8 scb_t_volockname[12]; uint32 scb_q_mounttime[2]; uint16 scb_w_backrev; uint32 scb_q_genernum[2]; uint8 scb_b_reserved[446]; uint16 scb_w_checksum; } ODS2_SCB; #pragma pack(pop) static uint16 ODS2Checksum (void *Buffer, uint16 WordCount) { int i; uint16 Sum = 0; uint16 CheckSum = 0; uint16 *Buf = (uint16 *)Buffer; for (i=0; i<WordCount; i++) CheckSum += Buf[i]; return CheckSum; } static t_offset get_filesystem_size (UNIT *uptr) { DEVICE *dptr; t_addr saved_capac; t_offset temp_capac = 512 * (t_offset)0xFFFFFFFFu; /* Make sure we can access the largest sector */ uint32 capac_factor; ODS2_HomeBlock Home; ODS2_FileHeader Header; ODS2_Retreval *Retr; ODS2_SCB Scb; uint16 CheckSum1, CheckSum2; uint32 ScbLbn; t_offset ret_val = (t_offset)-1; if ((dptr = find_dev_from_unit (uptr)) == NULL) return ret_val; capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* save capacity units (word: 2, byte: 1) */ saved_capac = uptr->capac; uptr->capac = (t_addr)(temp_capac/(capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))); if (sim_disk_rdsect (uptr, 1, (uint8 *)&Home, NULL, 1)) goto Return_Cleanup; CheckSum1 = ODS2Checksum (&Home, (uint16)((((char *)&Home.hm2_w_checksum1)-((char *)&Home.hm2_l_homelbn))/2)); CheckSum2 = ODS2Checksum (&Home, (uint16)((((char *)&Home.hm2_w_checksum2)-((char *)&Home.hm2_l_homelbn))/2)); if ((Home.hm2_l_homelbn == 0) || (Home.hm2_l_alhomelbn == 0) || (Home.hm2_l_altidxlbn == 0) || ((Home.hm2_b_struclev != 2) && (Home.hm2_b_struclev != 5)) || (Home.hm2_b_strucver == 0) || (Home.hm2_w_cluster == 0) || (Home.hm2_w_homevbn == 0) || (Home.hm2_w_alhomevbn == 0) || (Home.hm2_w_ibmapvbn == 0) || (Home.hm2_l_ibmaplbn == 0) || (Home.hm2_w_resfiles >= Home.hm2_l_maxfiles) || (Home.hm2_w_ibmapsize == 0) || (Home.hm2_w_resfiles < 5) || (Home.hm2_w_checksum1 != CheckSum1) || (Home.hm2_w_checksum2 != CheckSum2)) goto Return_Cleanup; if (sim_disk_rdsect (uptr, Home.hm2_l_ibmaplbn+Home.hm2_w_ibmapsize+1, (uint8 *)&Header, NULL, 1)) goto Return_Cleanup; CheckSum1 = ODS2Checksum (&Header, 255); if (CheckSum1 != *(((uint16 *)&Header)+255)) /* Verify Checksum on BITMAP.SYS file header */ goto Return_Cleanup; Retr = (ODS2_Retreval *)(((uint16*)(&Header))+Header.fh2_b_mpoffset); /* The BitMap File has a single extent, which may be preceeded by a placement descriptor */ if (Retr->fm2_r_word0_bits.fm2_v_format == 0) Retr = (ODS2_Retreval *)(((uint16 *)Retr)+1); /* skip placement descriptor */ switch (Retr->fm2_r_word0_bits.fm2_v_format) { case 1: ScbLbn = (Retr->fm2_r_map_bits1.fm2_v_highlbn1<<16)+Retr->fm2_r_map_bits1.fm2_w_lowlbn1; break; case 2: ScbLbn = (Retr->fm2_r_map_bits2.fm2_l_highlbn2<<16)+Retr->fm2_r_map_bits2.fm2_r_map2_long0.fm2_l_lowlbn2; break; case 3: ScbLbn = Retr->fm2_r_map_bits3.fm2_l_lbn3; break; } Retr = (ODS2_Retreval *)(((uint16 *)Retr)+Retr->fm2_r_word0_bits.fm2_v_format+1); if (sim_disk_rdsect (uptr, ScbLbn, (uint8 *)&Scb, NULL, 1)) goto Return_Cleanup; CheckSum1 = ODS2Checksum (&Scb, 255); if (CheckSum1 != *(((uint16 *)&Scb)+255)) /* Verify Checksum on Storage Control Block */ goto Return_Cleanup; if ((Scb.scb_w_cluster != Home.hm2_w_cluster) || (Scb.scb_b_strucver != Home.hm2_b_strucver) || (Scb.scb_b_struclev != Home.hm2_b_struclev)) goto Return_Cleanup; if (!sim_quiet) { sim_printf ("%s%d: '%s' Contains ODS%d File system:\n", sim_dname (dptr), (int)(uptr-dptr->units), uptr->filename, Home.hm2_b_struclev); sim_printf ("%s%d: Volume Name: %12.12s ", sim_dname (dptr), (int)(uptr-dptr->units), Home.hm2_t_volname); sim_printf ("Format: %12.12s ", Home.hm2_t_format); sim_printf ("SectorsInVolume: %d\n", Scb.scb_l_volsize); } ret_val = ((t_offset)Scb.scb_l_volsize) * 512; Return_Cleanup: uptr->capac = saved_capac; return ret_val; } t_stat sim_disk_attach (UNIT *uptr, const char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize, uint32 dbit, const char *dtype, uint32 pdp11tracksize, int completion_delay) { struct disk_context *ctx; DEVICE *dptr; char tbuf[4*CBUFSIZE]; FILE *(*open_function)(const char *filename, const char *mode) = sim_fopen; FILE *(*create_function)(const char *filename, t_offset desiredsize) = NULL; t_offset (*size_function)(FILE *file); t_stat (*storage_function)(FILE *file, uint32 *sector_size, uint32 *removable) = NULL; t_bool created = FALSE, copied = FALSE; t_bool auto_format = FALSE; t_offset capac, filesystem_capac; if (uptr->flags & UNIT_DIS) /* disabled? */ return SCPE_UDIS; if (!(uptr->flags & UNIT_ATTABLE)) /* not attachable? */ return SCPE_NOATT; if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT; if (sim_switches & SWMASK ('F')) { /* format spec? */ char gbuf[CBUFSIZE]; cptr = get_glyph (cptr, gbuf, 0); /* get spec */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; if (sim_disk_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) return sim_messagef (SCPE_ARG, "Invalid Disk Format: %s\n", gbuf); sim_switches = sim_switches & ~(SWMASK ('F')); /* Record Format specifier already processed */ auto_format = TRUE; } if (sim_switches & SWMASK ('D')) { /* create difference disk? */ char gbuf[CBUFSIZE]; FILE *vhd; sim_switches = sim_switches & ~(SWMASK ('D')); cptr = get_glyph_nc (cptr, gbuf, 0); /* get spec */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; vhd = sim_vhd_disk_create_diff (gbuf, cptr); if (vhd) { sim_vhd_disk_close (vhd); return sim_disk_attach (uptr, gbuf, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize, completion_delay); } return sim_messagef (SCPE_ARG, "Unable to create differencing VHD: %s\n", gbuf); } if (sim_switches & SWMASK ('C')) { /* create vhd disk & copy contents? */ char gbuf[CBUFSIZE]; FILE *vhd; int saved_sim_switches = sim_switches; int32 saved_sim_quiet = sim_quiet; uint32 capac_factor; t_stat r; sim_switches = sim_switches & ~(SWMASK ('C')); cptr = get_glyph_nc (cptr, gbuf, 0); /* get spec */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; sim_switches |= SWMASK ('R') | SWMASK ('E'); sim_quiet = TRUE; /* First open the source of the copy operation */ r = sim_disk_attach (uptr, cptr, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize, completion_delay); sim_quiet = saved_sim_quiet; if (r != SCPE_OK) { sim_switches = saved_sim_switches; return sim_messagef (r, "Can't open source VHD: %s\n", cptr); } if (!sim_quiet) { sim_printf ("%s%d: creating new virtual disk '%s'\n", sim_dname (dptr), (int)(uptr-dptr->units), gbuf); } capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */ vhd = sim_vhd_disk_create (gbuf, ((t_offset)uptr->capac)*capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)); if (!vhd) { return sim_messagef (r, "%s%d: can't create virtual disk '%s'\n", sim_dname (dptr), (int)(uptr-dptr->units), gbuf); } else { uint8 *copy_buf = (uint8*) malloc (1024*1024); t_lba lba; t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size); t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1))); t_seccnt sects = sectors_per_buffer; if (!copy_buf) { sim_vhd_disk_close(vhd); remove (gbuf); return SCPE_MEM; } for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) { if (!sim_quiet) sim_printf ("%s%d: Copied %dMB. %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors)); sects = sectors_per_buffer; if (lba + sects > total_sectors) sects = total_sectors - lba; r = sim_disk_rdsect (uptr, lba, copy_buf, NULL, sects); if (r == SCPE_OK) { uint32 saved_unit_flags = uptr->flags; FILE *save_unit_fileref = uptr->fileref; sim_disk_set_fmt (uptr, 0, "VHD", NULL); uptr->fileref = vhd; r = sim_disk_wrsect (uptr, lba, copy_buf, NULL, sects); uptr->fileref = save_unit_fileref; uptr->flags = saved_unit_flags; } } if (!sim_quiet) { if (r == SCPE_OK) sim_printf ("\n%s%d: Copied %dMB. Done.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_offset)lba*sector_size)/1000000)); else sim_printf ("\n%s%d: Error copying: %s.\n", sim_dname (dptr), (int)(uptr-dptr->units), sim_error_text (r)); } if ((r == SCPE_OK) && (sim_switches & SWMASK ('V'))) { uint8 *verify_buf = (uint8*) malloc (1024*1024); if (!verify_buf) { sim_vhd_disk_close(vhd); remove (gbuf); free (copy_buf); return SCPE_MEM; } for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) { if (!sim_quiet) sim_printf ("%s%d: Verified %dMB. %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors)); sects = sectors_per_buffer; if (lba + sects > total_sectors) sects = total_sectors - lba; r = sim_disk_rdsect (uptr, lba, copy_buf, NULL, sects); if (r == SCPE_OK) { uint32 saved_unit_flags = uptr->flags; FILE *save_unit_fileref = uptr->fileref; sim_disk_set_fmt (uptr, 0, "VHD", NULL); uptr->fileref = vhd; r = sim_disk_rdsect (uptr, lba, verify_buf, NULL, sects); uptr->fileref = save_unit_fileref; uptr->flags = saved_unit_flags; if (r == SCPE_OK) { if (0 != memcmp (copy_buf, verify_buf, 1024*1024)) r = SCPE_IOERR; } } } if (!sim_quiet) { if (r == SCPE_OK) sim_printf ("\n%s%d: Verified %dMB. Done.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(((t_offset)lba*sector_size)/1000000)); else { t_lba i; uint32 save_dctrl = dptr->dctrl; FILE *save_sim_deb = sim_deb; for (i = 0; i < (1024*1024/sector_size); ++i) if (0 != memcmp (copy_buf+i*sector_size, verify_buf+i*sector_size, sector_size)) break; sim_printf ("\n%s%d: Verification Error on lbn %d.\n", sim_dname (dptr), (int)(uptr-dptr->units), lba+i); dptr->dctrl = 0xFFFFFFFF; sim_deb = stdout; sim_disk_data_trace (uptr, copy_buf+i*sector_size, lba+i, sector_size, "Expected", TRUE, 1); sim_disk_data_trace (uptr, verify_buf+i*sector_size, lba+i, sector_size, "Found", TRUE, 1); dptr->dctrl = save_dctrl; sim_deb = save_sim_deb; } } free (verify_buf); } free (copy_buf); sim_vhd_disk_close (vhd); sim_disk_detach (uptr); if (r == SCPE_OK) { created = TRUE; copied = TRUE; tbuf[sizeof(tbuf)-1] = '\0'; strncpy (tbuf, gbuf, sizeof(tbuf)-1); cptr = tbuf; sim_disk_set_fmt (uptr, 0, "VHD", NULL); sim_switches = saved_sim_switches; } else return r; /* fall through and open/return the newly created & copied vhd */ } } else if (sim_switches & SWMASK ('M')) { /* merge difference disk? */ char gbuf[CBUFSIZE], *Parent = NULL; FILE *vhd; sim_switches = sim_switches & ~(SWMASK ('M')); get_glyph_nc (cptr, gbuf, 0); /* get spec */ vhd = sim_vhd_disk_merge (gbuf, &Parent); if (vhd) { t_stat r; sim_vhd_disk_close (vhd); r = sim_disk_attach (uptr, Parent, sector_size, xfer_element_size, dontautosize, dbit, dtype, pdp11tracksize, completion_delay); free (Parent); return r; } return SCPE_ARG; } switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* SIMH format */ if (NULL == (uptr->fileref = sim_vhd_disk_open (cptr, "rb"))) { if (errno == EBADF) /* VHD but broken */ return SCPE_OPENERR; open_function = sim_fopen; size_function = sim_fsize_ex; break; } sim_disk_set_fmt (uptr, 0, "VHD", NULL); /* set file format to VHD */ sim_vhd_disk_close (uptr->fileref); /* close vhd file*/ auto_format = TRUE; uptr->fileref = NULL; /* Fall through to normal VHD processing */ case DKUF_F_VHD: /* VHD format */ open_function = sim_vhd_disk_open; create_function = sim_vhd_disk_create; size_function = sim_vhd_disk_size; break; case DKUF_F_RAW: /* Raw Physical Disk Access */ open_function = sim_os_disk_open_raw; size_function = sim_os_disk_size_raw; storage_function = sim_os_disk_info_raw; break; default: return SCPE_IERR; } uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char));/* alloc name buf */ uptr->disk_ctx = ctx = (struct disk_context *)calloc(1, sizeof(struct disk_context)); if ((uptr->filename == NULL) || (uptr->disk_ctx == NULL)) return _err_return (uptr, SCPE_MEM); strncpy (uptr->filename, cptr, CBUFSIZE); /* save name */ ctx->sector_size = (uint32)sector_size; /* save sector_size */ ctx->capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* save capacity units (word: 2, byte: 1) */ ctx->xfer_element_size = (uint32)xfer_element_size; /* save xfer_element_size */ ctx->dptr = dptr; /* save DEVICE pointer */ ctx->dbit = dbit; /* save debug bit */ sim_debug (ctx->dbit, ctx->dptr, "sim_disk_attach(unit=%d,filename='%s')\n", (int)(uptr-ctx->dptr->units), uptr->filename); ctx->auto_format = auto_format; /* save that we auto selected format */ ctx->storage_sector_size = (uint32)sector_size; /* Default */ if ((sim_switches & SWMASK ('R')) || /* read only? */ ((uptr->flags & UNIT_RO) != 0)) { if (((uptr->flags & UNIT_ROABLE) == 0) && /* allowed? */ ((uptr->flags & UNIT_RO) == 0)) return _err_return (uptr, SCPE_NORO); /* no, error */ uptr->fileref = open_function (cptr, "rb"); /* open rd only */ if (uptr->fileref == NULL) /* open fail? */ return _err_return (uptr, SCPE_OPENERR); /* yes, error */ uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ if (!sim_quiet) { sim_printf ("%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units)); } } else { /* normal */ uptr->fileref = open_function (cptr, "rb+"); /* open r/w */ if (uptr->fileref == NULL) { /* open fail? */ if ((errno == EROFS) || (errno == EACCES)) { /* read only? */ if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ return _err_return (uptr, SCPE_NORO); /* no error */ uptr->fileref = open_function (cptr, "rb"); /* open rd only */ if (uptr->fileref == NULL) /* open fail? */ return _err_return (uptr, SCPE_OPENERR);/* yes, error */ uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ if (!sim_quiet) sim_printf ("%s%d: unit is read only\n", sim_dname (dptr), (int)(uptr-dptr->units)); } else { /* doesn't exist */ if (sim_switches & SWMASK ('E')) /* must exist? */ return _err_return (uptr, SCPE_OPENERR); /* yes, error */ if (create_function) uptr->fileref = create_function (cptr, ((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1));/* create new file */ else uptr->fileref = open_function (cptr, "wb+");/* open new file */ if (uptr->fileref == NULL) /* open fail? */ return _err_return (uptr, SCPE_OPENERR);/* yes, error */ if (!sim_quiet) sim_printf ("%s%d: creating new file\n", sim_dname (dptr), (int)(uptr-dptr->units)); created = TRUE; } } /* end if null */ } /* end else */ if (DK_GET_FMT (uptr) == DKUF_F_VHD) { if ((created) && dtype) sim_vhd_disk_set_dtype (uptr->fileref, dtype); if (dtype && strcmp (dtype, sim_vhd_disk_get_dtype (uptr->fileref))) { char cmd[32]; sprintf (cmd, "%s%d %s", dptr->name, (int)(uptr-dptr->units), sim_vhd_disk_get_dtype (uptr->fileref)); set_cmd (0, cmd); } } uptr->flags = uptr->flags | UNIT_ATT; uptr->pos = 0; /* Get Device attributes if they are available */ if (storage_function) storage_function (uptr->fileref, &ctx->storage_sector_size, &ctx->removable); if ((created) && (!copied)) { t_stat r = SCPE_OK; uint8 *secbuf = (uint8 *)calloc (1, ctx->sector_size); /* alloc temp sector buf */ /* On a newly created disk, we write a zero sector to the last and the first sectors. This serves 3 purposes: 1) it avoids strange allocation delays writing newly allocated storage at the end of the disk during simulator operation 2) it allocates storage for the whole disk at creation time to avoid strange failures which may happen during simulator execution if the containing disk is full 3) it leaves a Sinh Format disk at the intended size so it may subsequently be autosized with the correct size. */ if (secbuf == NULL) r = SCPE_MEM; if (r == SCPE_OK) r = sim_disk_wrsect (uptr, (t_lba)(((((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)) - ctx->sector_size)/ctx->sector_size), secbuf, NULL, 1); /* Write Last Sector */ if (r == SCPE_OK) r = sim_disk_wrsect (uptr, (t_lba)(0), secbuf, NULL, 1); /* Write First Sector */ free (secbuf); if (r != SCPE_OK) { sim_disk_detach (uptr); /* report error now */ remove (cptr); /* remove the create file */ return SCPE_OPENERR; } if (sim_switches & SWMASK ('I')) { /* Initialize To Sector Address */ uint8 *init_buf = (uint8*) malloc (1024*1024); t_lba lba, sect; uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */ t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size); t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1))); t_seccnt sects = sectors_per_buffer; if (!init_buf) { sim_disk_detach (uptr); /* report error now */ remove (cptr); return SCPE_MEM; } for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) { sects = sectors_per_buffer; if (lba + sects > total_sectors) sects = total_sectors - lba; for (sect = 0; sect < sects; sect++) { t_lba offset; for (offset = 0; offset < sector_size; offset += sizeof(uint32)) *((uint32 *)&init_buf[sect*sector_size + offset]) = (uint32)(lba + sect); } r = sim_disk_wrsect (uptr, lba, init_buf, NULL, sects); if (r != SCPE_OK) { free (init_buf); sim_disk_detach (uptr); /* report error now */ remove (cptr); /* remove the create file */ return SCPE_OPENERR; } if (!sim_quiet) sim_printf ("%s%d: Initialized To Sector Address %dMB. %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors)); } if (!sim_quiet) sim_printf ("%s%d: Initialized To Sector Address %dMB. 100%% complete.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000)); } if (pdp11tracksize) sim_disk_pdp11_bad_block (uptr, pdp11tracksize); } if (sim_switches & SWMASK ('K')) { t_stat r = SCPE_OK; t_lba lba, sect; uint32 capac_factor = ((dptr->dwidth / dptr->aincr) == 16) ? 2 : 1; /* capacity units (word: 2, byte: 1) */ t_seccnt sectors_per_buffer = (t_seccnt)((1024*1024)/sector_size); t_lba total_sectors = (t_lba)((uptr->capac*capac_factor)/(sector_size/((dptr->flags & DEV_SECTORS) ? 512 : 1))); t_seccnt sects = sectors_per_buffer; uint8 *verify_buf = (uint8*) malloc (1024*1024); if (!verify_buf) { sim_disk_detach (uptr); /* report error now */ return SCPE_MEM; } for (lba = 0; (lba < total_sectors) && (r == SCPE_OK); lba += sects) { sects = sectors_per_buffer; if (lba + sects > total_sectors) sects = total_sectors - lba; r = sim_disk_rdsect (uptr, lba, verify_buf, NULL, sects); if (r == SCPE_OK) { for (sect = 0; sect < sects; sect++) { t_lba offset; t_bool sect_error = FALSE; for (offset = 0; offset < sector_size; offset += sizeof(uint32)) { if (*((uint32 *)&verify_buf[sect*sector_size + offset]) != (uint32)(lba + sect)) { sect_error = TRUE; break; } } if (sect_error) { uint32 save_dctrl = dptr->dctrl; FILE *save_sim_deb = sim_deb; sim_printf ("\n%s%d: Verification Error on lbn %d(0x%X) of %d(0x%X).\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)(lba+sect), (int)(lba+sect), (int)total_sectors, (int)total_sectors); dptr->dctrl = 0xFFFFFFFF; sim_deb = stdout; sim_disk_data_trace (uptr, verify_buf+sect*sector_size, lba+sect, sector_size, "Found", TRUE, 1); dptr->dctrl = save_dctrl; sim_deb = save_sim_deb; } } } if (!sim_quiet) sim_printf ("%s%d: Verified containing Sector Address %dMB. %d%% complete.\r", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000), (int)((((float)lba)*100)/total_sectors)); } if (!sim_quiet) sim_printf ("%s%d: Verified containing Sector Address %dMB. 100%% complete.\n", sim_dname (dptr), (int)(uptr-dptr->units), (int)((((float)lba)*sector_size)/1000000)); free (verify_buf); uptr->dynflags |= UNIT_DISK_CHK; } filesystem_capac = get_filesystem_size (uptr); capac = size_function (uptr->fileref); if (capac && (capac != (t_offset)-1)) { if (dontautosize) { t_addr saved_capac = uptr->capac; if ((filesystem_capac != (t_offset)-1) && (filesystem_capac > (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)))) { if (!sim_quiet) { uptr->capac = (t_addr)(filesystem_capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))); sim_printf ("%s%d: The file system on the disk %s is larger than simulated device (%s > ", sim_dname (dptr), (int)(uptr-dptr->units), cptr, sprint_capac (dptr, uptr)); uptr->capac = saved_capac; sim_printf ("%s)\n", sprint_capac (dptr, uptr)); } sim_disk_detach (uptr); return SCPE_OPENERR; } if ((capac < (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))) && (DKUF_F_STD != DK_GET_FMT (uptr))) { if (!sim_quiet) { uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))); sim_printf ("%s%d: non expandable disk %s is smaller than simulated device (%s < ", sim_dname (dptr), (int)(uptr-dptr->units), cptr, sprint_capac (dptr, uptr)); uptr->capac = saved_capac; sim_printf ("%s)\n", sprint_capac (dptr, uptr)); } sim_disk_detach (uptr); return SCPE_OPENERR; } } else { if ((filesystem_capac != (t_offset)-1) && (filesystem_capac > capac)) capac = filesystem_capac; if ((capac != (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))) || (DKUF_F_STD != DK_GET_FMT (uptr))) uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))); } } #if defined (SIM_ASYNCH_IO) sim_disk_set_async (uptr, completion_delay); #endif uptr->io_flush = _sim_disk_io_flush; return SCPE_OK; } t_stat sim_disk_detach (UNIT *uptr) { struct disk_context *ctx; int (*close_function)(FILE *f); FILE *fileref; t_bool auto_format; if ((uptr == NULL) || !(uptr->flags & UNIT_ATT)) return SCPE_NOTATT; ctx = (struct disk_context *)uptr->disk_ctx; fileref = uptr->fileref; sim_debug (ctx->dbit, ctx->dptr, "sim_disk_detach(unit=%d,filename='%s')\n", (int)(uptr-ctx->dptr->units), uptr->filename); switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* Simh */ close_function = fclose; break; case DKUF_F_VHD: /* Virtual Disk */ close_function = sim_vhd_disk_close; break; case DKUF_F_RAW: /* Physical */ close_function = sim_os_disk_close_raw; break; default: return SCPE_IERR; } if (!(uptr->flags & UNIT_ATTABLE)) /* attachable? */ return SCPE_NOATT; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (NULL == find_dev_from_unit (uptr)) return SCPE_OK; auto_format = ctx->auto_format; if (uptr->io_flush) uptr->io_flush (uptr); /* flush buffered data */ sim_disk_clr_async (uptr); uptr->flags &= ~(UNIT_ATT | UNIT_RO); uptr->dynflags &= ~(UNIT_NO_FIO | UNIT_DISK_CHK); free (uptr->filename); uptr->filename = NULL; uptr->fileref = NULL; free (uptr->disk_ctx); uptr->disk_ctx = NULL; uptr->io_flush = NULL; if (auto_format) sim_disk_set_fmt (uptr, 0, "SIMH", NULL); /* restore file format */ if (close_function (fileref) == EOF) return SCPE_IOERR; return SCPE_OK; } t_stat sim_disk_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { fprintf (st, "%s Disk Attach Help\n\n", dptr->name); fprintf (st, "Disk container files can be one of 3 different types:\n\n"); fprintf (st, " SIMH A disk is an unstructured binary file of the size appropriate\n"); fprintf (st, " for the disk drive being simulated\n"); fprintf (st, " VHD Virtual Disk format which is described in the \"Microsoft\n"); fprintf (st, " Virtual Hard Disk (VHD) Image Format Specification\". The\n"); fprintf (st, " VHD implementation includes support for 1) Fixed (Preallocated)\n"); fprintf (st, " disks, 2) Dynamically Expanding disks, and 3) Differencing disks.\n"); fprintf (st, " RAW platform specific access to physical disk or CDROM drives\n\n"); fprintf (st, "Virtual (VHD) Disks supported conform to \"Virtual Hard Disk Image Format\n"); fprintf (st, "Specification\", Version 1.0 October 11, 2006.\n"); fprintf (st, "Dynamically expanding disks never change their \"Virtual Size\", but they don't\n"); fprintf (st, "consume disk space on the containing storage until the virtual sectors in the\n"); fprintf (st, "disk are actually written to (i.e. a 2GB Dynamic disk container file with only\n"); fprintf (st, "30MB of data will initially be about 30MB in size and this size will grow up to\n"); fprintf (st, "2GB as different sectors are written to. The VHD format contains metadata\n"); fprintf (st, "which describes the drive size and the simh device type in use when the VHD\n"); fprintf (st, "was created. This metadata is therefore available whenever that VHD is\n"); fprintf (st, "attached to an emulated disk device in the future so the device type and\n"); fprintf (st, "size can be automatically be configured.\n\n"); if (0 == (uptr-dptr->units)) { if (dptr->numunits > 1) { uint32 i; for (i=0; i < dptr->numunits; ++i) if (dptr->units[i].flags & UNIT_ATTABLE) fprintf (st, " sim> ATTACH {switches} %s%d diskfile\n", dptr->name, i); } else fprintf (st, " sim> ATTACH {switches} %s diskfile\n", dptr->name); } else fprintf (st, " sim> ATTACH {switches} %s diskfile\n\n", dptr->name); fprintf (st, "\n%s attach command switches\n", dptr->name); fprintf (st, " -R Attach Read Only.\n"); fprintf (st, " -E Must Exist (if not specified an attempt to create the indicated\n"); fprintf (st, " disk container will be attempted).\n"); fprintf (st, " -F Open the indicated disk container in a specific format (default\n"); fprintf (st, " is to autodetect VHD defaulting to simh if the indicated\n"); fprintf (st, " container is not a VHD).\n"); fprintf (st, " -I Initialize newly created disk so that each sector contains its\n"); fprintf (st, " sector address\n"); fprintf (st, " -K Verify that the disk contents contain the sector address in each\n"); fprintf (st, " sector. Whole disk checked at attach time and each sector is\n"); fprintf (st, " checked when written.\n"); fprintf (st, " -C Create a VHD and copy its contents from another disk (simh, VHD,\n"); fprintf (st, " or RAW format). Add a -V switch to verify a copy operation.\n"); fprintf (st, " -V Perform a verification pass to confirm successful data copy\n"); fprintf (st, " operation.\n"); fprintf (st, " -X When creating a VHD, create a fixed sized VHD (vs a Dynamically\n"); fprintf (st, " expanding one).\n"); fprintf (st, " -D Create a Differencing VHD (relative to an already existing VHD\n"); fprintf (st, " disk)\n"); fprintf (st, " -M Merge a Differencing VHD into its parent VHD disk\n"); fprintf (st, " -O Override consistency checks when attaching differencing disks\n"); fprintf (st, " which have unexpected parent disk GUID or timestamps\n\n"); fprintf (st, " -U Fix inconsistencies which are overridden by the -O switch\n"); fprintf (st, " -Y Answer Yes to prompt to overwrite last track (on disk create)\n"); fprintf (st, " -N Answer No to prompt to overwrite last track (on disk create)\n"); fprintf (st, "Examples:\n"); fprintf (st, " sim> show rq\n"); fprintf (st, " RQ, address=20001468-2000146B*, no vector, 4 units\n"); fprintf (st, " RQ0, 159MB, not attached, write enabled, RD54, autosize, SIMH format\n"); fprintf (st, " RQ1, 159MB, not attached, write enabled, RD54, autosize, SIMH format\n"); fprintf (st, " RQ2, 159MB, not attached, write enabled, RD54, autosize, SIMH format\n"); fprintf (st, " RQ3, 409KB, not attached, write enabled, RX50, autosize, SIMH format\n"); fprintf (st, " sim> atta rq0 RA81.vhd\n"); fprintf (st, " sim> show rq0\n"); fprintf (st, " RQ0, 456MB, attached to RA81.vhd, write enabled, RA81, autosize, VHD format\n"); fprintf (st, " sim> set rq2 ra92\n"); fprintf (st, " sim> att rq2 -f vhd RA92.vhd\n"); fprintf (st, " RQ2: creating new file\n"); fprintf (st, " sim> sho rq2\n"); fprintf (st, " RQ2, 1505MB, attached to RA92.vhd, write enabled, RA92, autosize, VHD format\n"); fprintf (st, " sim> ! dir RA92.vhd\n"); fprintf (st, " Volume in drive H is New Volume\n"); fprintf (st, " Volume Serial Number is F8DE-510C\n\n"); fprintf (st, " Directory of H:\\Data\n\n"); fprintf (st, " 04/14/2011 12:57 PM 5,120 RA92.vhd\n"); fprintf (st, " 1 File(s) 5,120 bytes\n"); fprintf (st, " sim> atta rq3 -d RA92-1-Diff.vhd RA92.vhd\n"); fprintf (st, " sim> atta rq3 -c RA92-1.vhd RA92.vhd\n"); fprintf (st, " RQ3: creating new virtual disk 'RA92-1.vhd'\n"); fprintf (st, " RQ3: Copied 1505MB. 99%% complete.\n"); fprintf (st, " RQ3: Copied 1505MB. Done.\n"); fprintf (st, " sim> sh rq3\n"); fprintf (st, " RQ3, 1505MB, attached to RA92-1.vhd, write enabled, RA92, autosize, VHD format\n"); fprintf (st, " sim> ! dir RA92*\n"); fprintf (st, " Volume in drive H is New Volume\n"); fprintf (st, " Volume Serial Number is F8DE-510C\n\n"); fprintf (st, " Directory of H:\\Data\n\n"); fprintf (st, " 04/14/2011 01:12 PM 5,120 RA92-1.vhd\n"); fprintf (st, " 04/14/2011 12:58 PM 5,120 RA92.vhd\n"); fprintf (st, " 2 File(s) 10,240 bytes\n"); fprintf (st, " sim> sho rq2\n"); fprintf (st, " RQ2, 1505MB, not attached, write enabled, RA92, autosize, VHD format\n"); fprintf (st, " sim> set rq2 ra81\n"); fprintf (st, " sim> set rq2 noauto\n"); fprintf (st, " sim> sho rq2\n"); fprintf (st, " RQ2, 456MB, not attached, write enabled, RA81, noautosize, VHD format\n"); fprintf (st, " sim> set rq2 format=simh\n"); fprintf (st, " sim> sho rq2\n"); fprintf (st, " RQ2, 456MB, not attached, write enabled, RA81, noautosize, SIMH format\n"); fprintf (st, " sim> atta rq2 -c RA81-Copy.vhd VMS055.dsk\n"); fprintf (st, " RQ2: creating new virtual disk 'RA81-Copy.vhd'\n"); fprintf (st, " RQ2: Copied 456MB. 99%% complete.\n"); fprintf (st, " RQ2: Copied 456MB. Done.\n"); fprintf (st, " sim> sho rq2\n"); fprintf (st, " RQ2, 456MB, attached to RA81-Copy.vhd, write enabled, RA81, noautosize, VHD format\n"); return SCPE_OK; } t_bool sim_disk_vhd_support (void) { return SCPE_OK == sim_vhd_disk_implemented (); } t_bool sim_disk_raw_support (void) { return SCPE_OK == sim_os_disk_implemented_raw (); } t_stat sim_disk_reset (UNIT *uptr) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; sim_debug (ctx->dbit, ctx->dptr, "sim_disk_reset(unit=%d)\n", (int)(uptr-ctx->dptr->units)); _sim_disk_io_flush(uptr); AIO_VALIDATE; AIO_UPDATE_QUEUE; return SCPE_OK; } t_stat sim_disk_perror (UNIT *uptr, const char *msg) { int saved_errno = errno; if (!(uptr->flags & UNIT_ATTABLE)) /* not attachable? */ return SCPE_NOATT; switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* SIMH format */ case DKUF_F_VHD: /* VHD format */ case DKUF_F_RAW: /* Raw Physical Disk Access */ perror (msg); sim_printf ("%s %s: %s\n", sim_uname(uptr), msg, strerror(saved_errno)); default: ; } return SCPE_OK; } t_stat sim_disk_clearerr (UNIT *uptr) { if (!(uptr->flags & UNIT_ATTABLE)) /* not attachable? */ return SCPE_NOATT; switch (DK_GET_FMT (uptr)) { /* case on format */ case DKUF_F_STD: /* SIMH format */ clearerr (uptr->fileref); break; case DKUF_F_VHD: /* VHD format */ sim_vhd_disk_clearerr (uptr); break; default: ; } return SCPE_OK; } /* Factory bad block table creation routine This routine writes a DEC standard 144 compliant bad block table on the last track of the specified unit as described in: EL-00144_B_DEC_STD_144_Disk_Standard_for_Recording_and_Handling_Bad_Sectors_Nov76.pdf The bad block table consists of 10 repetitions of the same table, formatted as follows: words 0-1 pack id number words 2-3 cylinder/sector/surface specifications : words n-n+1 end of table (-1,-1) Inputs: uptr = pointer to unit sec = number of sectors per surface Outputs: sta = status code */ static t_stat sim_disk_pdp11_bad_block (UNIT *uptr, int32 sec) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; int32 i; t_addr da; int32 wds = ctx->sector_size/sizeof (uint16); uint16 *buf; DEVICE *dptr; char *namebuf, *c; uint32 packid; if ((sec < 2) || (wds < 16)) return SCPE_ARG; if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT; if (uptr->flags & UNIT_RO) return SCPE_RO; if (ctx->capac_factor != 2) /* Must be Word oriented Capacity */ return SCPE_IERR; if (!get_yn ("Overwrite last track? [N]", FALSE)) return SCPE_OK; if ((buf = (uint16 *) malloc (wds * sizeof (uint16))) == NULL) return SCPE_MEM; namebuf = uptr->filename; if ((c = strrchr (namebuf, '/'))) namebuf = c+1; if ((c = strrchr (namebuf, '\\'))) namebuf = c+1; if ((c = strrchr (namebuf, ']'))) namebuf = c+1; packid = eth_crc32(0, namebuf, strlen (namebuf)); buf[0] = (uint16)packid; buf[1] = (uint16)(packid >> 16) & 0x7FFF; /* Make sure MSB is clear */ buf[2] = buf[3] = 0; for (i = 4; i < wds; i++) buf[i] = 0177777u; da = (uptr->capac*((dptr->flags & DEV_SECTORS) ? 512 : 1)) - (sec * wds); for (i = 0; (i < sec) && (i < 10); i++, da += wds) if (sim_disk_wrsect (uptr, (t_lba)(da/wds), (uint8 *)buf, NULL, 1)) { free (buf); return SCPE_IOERR; } free (buf); return SCPE_OK; } void sim_disk_data_trace(UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; if (sim_deb && (ctx->dptr->dctrl & reason)) { char pos[32]; sprintf (pos, "lbn: %08X ", (unsigned int)lba); sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), pos, len, txt, reason); } } /* OS Specific RAW Disk I/O support */ #if defined _WIN32 static void _set_errno_from_status (DWORD dwStatus) { switch (dwStatus) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_INVALID_DRIVE: case ERROR_NO_MORE_FILES: case ERROR_BAD_NET_NAME: case ERROR_BAD_NETPATH: case ERROR_BAD_PATHNAME: case ERROR_FILENAME_EXCED_RANGE: errno = ENOENT; return; case ERROR_INVALID_ACCESS: case ERROR_INVALID_DATA: case ERROR_INVALID_FUNCTION: case ERROR_INVALID_PARAMETER: case ERROR_NEGATIVE_SEEK: errno = EINVAL; return; case ERROR_ARENA_TRASHED: case ERROR_NOT_ENOUGH_MEMORY: case ERROR_INVALID_BLOCK: case ERROR_NOT_ENOUGH_QUOTA: errno = ENOMEM; return; case ERROR_TOO_MANY_OPEN_FILES: errno = EMFILE; return; case ERROR_ACCESS_DENIED: case ERROR_CURRENT_DIRECTORY: case ERROR_LOCK_VIOLATION: case ERROR_NETWORK_ACCESS_DENIED: case ERROR_CANNOT_MAKE: case ERROR_FAIL_I24: case ERROR_DRIVE_LOCKED: case ERROR_SEEK_ON_DEVICE: case ERROR_NOT_LOCKED: case ERROR_LOCK_FAILED: errno = EACCES; return; case ERROR_ALREADY_EXISTS: case ERROR_FILE_EXISTS: errno = EEXIST; return; case ERROR_INVALID_HANDLE: case ERROR_INVALID_TARGET_HANDLE: case ERROR_DIRECT_ACCESS_HANDLE: errno = EBADF; return; case ERROR_DIR_NOT_EMPTY: errno = ENOTEMPTY; return; case ERROR_BAD_ENVIRONMENT: errno = E2BIG; return; case ERROR_BAD_FORMAT: errno = ENOEXEC; return; case ERROR_NOT_SAME_DEVICE: errno = EXDEV; return; case ERROR_BROKEN_PIPE: errno = EPIPE; return; case ERROR_DISK_FULL: errno = ENOSPC; return; case ERROR_WAIT_NO_CHILDREN: case ERROR_CHILD_NOT_COMPLETE: errno = ECHILD; return; case ERROR_NO_PROC_SLOTS: case ERROR_MAX_THRDS_REACHED: case ERROR_NESTING_NOT_ALLOWED: errno = EAGAIN; return; } if ((dwStatus >= ERROR_WRITE_PROTECT) && (dwStatus <= ERROR_SHARING_BUFFER_EXCEEDED)) { errno = EACCES; return; } if ((dwStatus >= ERROR_INVALID_STARTING_CODESEG) && (dwStatus <= ERROR_INFLOOP_IN_RELOC_CHAIN)) { errno = ENOEXEC; return; } errno = EINVAL; } #if defined(__GNUC__) #include <ddk/ntddstor.h> #include <ddk/ntdddisk.h> #else #include <winioctl.h> #endif #if defined(__cplusplus) extern "C" { #endif WINBASEAPI BOOL WINAPI GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER lpFileSize); #if defined(__cplusplus) } #endif struct _device_type { int32 Type; const char *desc; } DeviceTypes[] = { {FILE_DEVICE_8042_PORT, "8042_PORT"}, {FILE_DEVICE_ACPI, "ACPI"}, {FILE_DEVICE_BATTERY, "BATTERY"}, {FILE_DEVICE_BEEP, "BEEP"}, #ifdef FILE_DEVICE_BLUETOOTH {FILE_DEVICE_BLUETOOTH, "BLUETOOTH"}, #endif {FILE_DEVICE_BUS_EXTENDER, "BUS_EXTENDER"}, {FILE_DEVICE_CD_ROM, "CD_ROM"}, {FILE_DEVICE_CD_ROM_FILE_SYSTEM, "CD_ROM_FILE_SYSTEM"}, {FILE_DEVICE_CHANGER, "CHANGER"}, {FILE_DEVICE_CONTROLLER, "CONTROLLER"}, #ifdef FILE_DEVICE_CRYPT_PROVIDER {FILE_DEVICE_CRYPT_PROVIDER, "CRYPT_PROVIDER"}, #endif {FILE_DEVICE_DATALINK, "DATALINK"}, {FILE_DEVICE_DFS, "DFS"}, {FILE_DEVICE_DFS_FILE_SYSTEM, "DFS_FILE_SYSTEM"}, {FILE_DEVICE_DFS_VOLUME, "DFS_VOLUME"}, {FILE_DEVICE_DISK, "DISK"}, {FILE_DEVICE_DISK_FILE_SYSTEM, "DISK_FILE_SYSTEM"}, {FILE_DEVICE_DVD, "DVD"}, {FILE_DEVICE_FILE_SYSTEM, "FILE_SYSTEM"}, #ifdef FILE_DEVICE_FIPS {FILE_DEVICE_FIPS, "FIPS"}, #endif {FILE_DEVICE_FULLSCREEN_VIDEO, "FULLSCREEN_VIDEO"}, #ifdef FILE_DEVICE_INFINIBAND {FILE_DEVICE_INFINIBAND, "INFINIBAND"}, #endif {FILE_DEVICE_INPORT_PORT, "INPORT_PORT"}, {FILE_DEVICE_KEYBOARD, "KEYBOARD"}, {FILE_DEVICE_KS, "KS"}, {FILE_DEVICE_KSEC, "KSEC"}, {FILE_DEVICE_MAILSLOT, "MAILSLOT"}, {FILE_DEVICE_MASS_STORAGE, "MASS_STORAGE"}, {FILE_DEVICE_MIDI_IN, "MIDI_IN"}, {FILE_DEVICE_MIDI_OUT, "MIDI_OUT"}, {FILE_DEVICE_MODEM, "MODEM"}, {FILE_DEVICE_MOUSE, "MOUSE"}, {FILE_DEVICE_MULTI_UNC_PROVIDER, "MULTI_UNC_PROVIDER"}, {FILE_DEVICE_NAMED_PIPE, "NAMED_PIPE"}, {FILE_DEVICE_NETWORK, "NETWORK"}, {FILE_DEVICE_NETWORK_BROWSER, "NETWORK_BROWSER"}, {FILE_DEVICE_NETWORK_FILE_SYSTEM, "NETWORK_FILE_SYSTEM"}, {FILE_DEVICE_NETWORK_REDIRECTOR, "NETWORK_REDIRECTOR"}, {FILE_DEVICE_NULL, "NULL"}, {FILE_DEVICE_PARALLEL_PORT, "PARALLEL_PORT"}, {FILE_DEVICE_PHYSICAL_NETCARD, "PHYSICAL_NETCARD"}, {FILE_DEVICE_PRINTER, "PRINTER"}, {FILE_DEVICE_SCANNER, "SCANNER"}, {FILE_DEVICE_SCREEN, "SCREEN"}, {FILE_DEVICE_SERENUM, "SERENUM"}, {FILE_DEVICE_SERIAL_MOUSE_PORT, "SERIAL_MOUSE_PORT"}, {FILE_DEVICE_SERIAL_PORT, "SERIAL_PORT"}, {FILE_DEVICE_SMARTCARD, "SMARTCARD"}, {FILE_DEVICE_SMB, "SMB"}, {FILE_DEVICE_SOUND, "SOUND"}, {FILE_DEVICE_STREAMS, "STREAMS"}, {FILE_DEVICE_TAPE, "TAPE"}, {FILE_DEVICE_TAPE_FILE_SYSTEM, "TAPE_FILE_SYSTEM"}, {FILE_DEVICE_TERMSRV, "TERMSRV"}, {FILE_DEVICE_TRANSPORT, "TRANSPORT"}, {FILE_DEVICE_UNKNOWN, "UNKNOWN"}, {FILE_DEVICE_VDM, "VDM"}, {FILE_DEVICE_VIDEO, "VIDEO"}, {FILE_DEVICE_VIRTUAL_DISK, "VIRTUAL_DISK"}, #ifdef FILE_DEVICE_VMBUS {FILE_DEVICE_VMBUS, "VMBUS"}, #endif {FILE_DEVICE_WAVE_IN, "WAVE_IN"}, {FILE_DEVICE_WAVE_OUT, "WAVE_OUT"}, #ifdef FILE_DEVICE_WPD {FILE_DEVICE_WPD, "WPD"}, #endif {0, NULL}}; static const char *_device_type_name (int DeviceType) { int i; for (i=0; DeviceTypes[i].desc; i++) if (DeviceTypes[i].Type == DeviceType) return DeviceTypes[i].desc; return "Unknown"; } static t_stat sim_os_disk_implemented_raw (void) { return sim_toffset_64 ? SCPE_OK : SCPE_NOFNC; } static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode) { HANDLE Handle; DWORD DesiredAccess = 0; if (strchr (openmode, 'r')) DesiredAccess |= GENERIC_READ; if (strchr (openmode, 'w') || strchr (openmode, '+')) DesiredAccess |= GENERIC_WRITE; /* SCP Command Line parsing replaces \\ with \ presuming this is an escape sequence. This only affecdts RAW device names and UNC paths. We handle the RAW device name case here by prepending paths beginning with \.\ with an extra \. */ if (!memcmp ("\\.\\", rawdevicename, 3)) { char *tmpname = (char *)malloc (2 + strlen (rawdevicename)); if (tmpname == NULL) return NULL; *tmpname = '\\'; strcpy (tmpname + 1, rawdevicename); Handle = CreateFileA (tmpname, DesiredAccess, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS|FILE_FLAG_WRITE_THROUGH, NULL); free (tmpname); if (Handle != INVALID_HANDLE_VALUE) return (FILE *)Handle; } Handle = CreateFileA (rawdevicename, DesiredAccess, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS|FILE_FLAG_WRITE_THROUGH, NULL); if (Handle == INVALID_HANDLE_VALUE) { _set_errno_from_status (GetLastError ()); return NULL; } return (FILE *)Handle; } static int sim_os_disk_close_raw (FILE *f) { if (!CloseHandle ((HANDLE)f)) { _set_errno_from_status (GetLastError ()); return EOF; } return 0; } static void sim_os_disk_flush_raw (FILE *f) { FlushFileBuffers ((HANDLE)f); } static t_offset sim_os_disk_size_raw (FILE *Disk) { DWORD IoctlReturnSize; LARGE_INTEGER Size; if (GetFileSizeEx((HANDLE)Disk, &Size)) return (t_offset)(Size.QuadPart); #ifdef IOCTL_STORAGE_READ_CAPACITY if (1) { STORAGE_READ_CAPACITY S; ZeroMemory (&S, sizeof (S)); S.Version = sizeof (STORAGE_READ_CAPACITY); if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ IOCTL_STORAGE_READ_CAPACITY, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ (LPVOID) &S, /* output buffer */ (DWORD) sizeof(S), /* size of output buffer */ (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ return (t_offset)(S.DiskLength.QuadPart); } #endif #ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY_EX if (1) { DISK_GEOMETRY_EX G; ZeroMemory (&G, sizeof (G)); if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ (LPVOID) &G, /* output buffer */ (DWORD) sizeof(G), /* size of output buffer */ (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ return (t_offset)(G.DiskSize.QuadPart); } #endif #ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY if (1) { DISK_GEOMETRY G; if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ IOCTL_DISK_GET_DRIVE_GEOMETRY, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ (LPVOID) &G, /* output buffer */ (DWORD) sizeof(G), /* size of output buffer */ (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ return (t_offset)(G.Cylinders.QuadPart*G.TracksPerCylinder*G.SectorsPerTrack*G.BytesPerSector); } #endif _set_errno_from_status (GetLastError ()); return (t_offset)-1; } static t_stat sim_os_disk_unload_raw (FILE *Disk) { #ifdef IOCTL_STORAGE_EJECT_MEDIA DWORD BytesReturned; uint32 Removable = FALSE; sim_os_disk_info_raw (Disk, NULL, &Removable); if (Removable) { if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ IOCTL_STORAGE_EJECT_MEDIA, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ NULL, /* lpOutBuffer */ 0, /* nOutBufferSize */ (LPDWORD) &BytesReturned, /* number of bytes returned */ (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ _set_errno_from_status (GetLastError ()); return SCPE_IOERR; } } return SCPE_OK; #else return SCPE_NOFNC; #endif } static t_bool sim_os_disk_isavailable_raw (FILE *Disk) { #ifdef IOCTL_STORAGE_EJECT_MEDIA DWORD BytesReturned; uint32 Removable = FALSE; sim_os_disk_info_raw (Disk, NULL, &Removable); if (Removable) { if (!DeviceIoControl((HANDLE)Disk, /* handle to disk */ IOCTL_STORAGE_CHECK_VERIFY, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ NULL, /* lpOutBuffer */ 0, /* nOutBufferSize */ (LPDWORD) &BytesReturned, /* number of bytes returned */ (LPOVERLAPPED) NULL)) { /* OVERLAPPED structure */ _set_errno_from_status (GetLastError ()); return FALSE; } } #endif return TRUE; } static t_stat sim_os_disk_info_raw (FILE *Disk, uint32 *sector_size, uint32 *removable) { DWORD IoctlReturnSize; STORAGE_DEVICE_NUMBER Device; ZeroMemory (&Device, sizeof (Device)); if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ IOCTL_STORAGE_GET_DEVICE_NUMBER, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ (LPVOID) &Device, /* output buffer */ (DWORD) sizeof(Device), /* size of output buffer */ (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ sim_printf ("Device OK - Type: %s, Number: %d\n", _device_type_name (Device.DeviceType), (int)Device.DeviceNumber); if (sector_size) *sector_size = 512; if (removable) *removable = 0; #ifdef IOCTL_STORAGE_READ_CAPACITY if (1) { STORAGE_READ_CAPACITY S; ZeroMemory (&S, sizeof (S)); S.Version = sizeof (STORAGE_READ_CAPACITY); if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ IOCTL_STORAGE_READ_CAPACITY, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ (LPVOID) &S, /* output buffer */ (DWORD) sizeof(S), /* size of output buffer */ (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ if (sector_size) *sector_size = S.BlockLength; } #endif #ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY_EX if (1) { DISK_GEOMETRY_EX G; ZeroMemory (&G, sizeof (G)); if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ (LPVOID) &G, /* output buffer */ (DWORD) sizeof(G), /* size of output buffer */ (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ if (sector_size) *sector_size = G.Geometry.BytesPerSector; } #endif #ifdef IOCTL_DISK_GET_DRIVE_GEOMETRY if (1) { DISK_GEOMETRY G; if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ IOCTL_DISK_GET_DRIVE_GEOMETRY, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ (LPVOID) &G, /* output buffer */ (DWORD) sizeof(G), /* size of output buffer */ (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ if (sector_size) *sector_size = G.BytesPerSector; } #endif #ifdef IOCTL_STORAGE_GET_HOTPLUG_INFO if (1) { STORAGE_HOTPLUG_INFO H; ZeroMemory (&H, sizeof (H)); if (DeviceIoControl((HANDLE)Disk, /* handle to volume */ IOCTL_STORAGE_GET_HOTPLUG_INFO, /* dwIoControlCode */ NULL, /* lpInBuffer */ 0, /* nInBufferSize */ (LPVOID) &H, /* output buffer */ (DWORD) sizeof(H), /* size of output buffer */ (LPDWORD) &IoctlReturnSize, /* number of bytes returned */ (LPOVERLAPPED) NULL)) /* OVERLAPPED structure */ if (removable) *removable = H.MediaRemovable; } #endif if (removable && *removable) sim_printf ("Removable Device\n"); return SCPE_OK; } static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) { OVERLAPPED pos; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; long long addr; sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects); addr = ((long long)lba) * ctx->sector_size; memset (&pos, 0, sizeof (pos)); pos.Offset = (DWORD)addr; pos.OffsetHigh = (DWORD)(addr >> 32); if (ReadFile ((HANDLE)(uptr->fileref), buf, sects * ctx->sector_size, (LPDWORD)sectsread, &pos)) { if (sectsread) *sectsread /= ctx->sector_size; return SCPE_OK; } _set_errno_from_status (GetLastError ()); return SCPE_IOERR; } static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) { OVERLAPPED pos; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; long long addr; sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects); addr = ((long long)lba) * ctx->sector_size; memset (&pos, 0, sizeof (pos)); pos.Offset = (DWORD)addr; pos.OffsetHigh = (DWORD)(addr >> 32); if (WriteFile ((HANDLE)(uptr->fileref), buf, sects * ctx->sector_size, (LPDWORD)sectswritten, &pos)) { if (sectswritten) *sectswritten /= ctx->sector_size; return SCPE_OK; } _set_errno_from_status (GetLastError ()); return SCPE_IOERR; } #elif defined (__linux) || defined (__linux__) || defined (__sun) || defined (__sun__) || defined (__hpux) || defined (_AIX) #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> static t_stat sim_os_disk_implemented_raw (void) { return sim_toffset_64 ? SCPE_OK : SCPE_NOFNC; } static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode) { int mode = 0; if (strchr (openmode, 'r') && (strchr (openmode, '+') || strchr (openmode, 'w'))) mode = O_RDWR; else if (strchr (openmode, 'r')) mode = O_RDONLY; #ifdef O_LARGEFILE mode |= O_LARGEFILE; #endif #ifdef O_DSYNC mode |= O_DSYNC; #endif return (FILE *)((long)open (rawdevicename, mode, 0)); } static int sim_os_disk_close_raw (FILE *f) { return close ((int)((long)f)); } static void sim_os_disk_flush_raw (FILE *f) { fsync ((int)((long)f)); } static t_offset sim_os_disk_size_raw (FILE *f) { t_offset pos, size; pos = (t_offset)lseek ((int)((long)f), (off_t)0, SEEK_CUR); size = (t_offset)lseek ((int)((long)f), (off_t)0, SEEK_END); lseek ((int)((long)f), (off_t)pos, SEEK_SET); return size; } static t_stat sim_os_disk_unload_raw (FILE *f) { return SCPE_IOERR; } static t_bool sim_os_disk_isavailable_raw (FILE *Disk) { return TRUE; } static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; off_t addr; ssize_t bytesread; sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_rdsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects); addr = ((off_t)lba) * ctx->sector_size; bytesread = pread((int)((long)uptr->fileref), buf, sects * ctx->sector_size, addr); if (bytesread < 0) { if (sectsread) *sectsread = 0; return SCPE_IOERR; } if (sectsread) *sectsread = bytesread / ctx->sector_size; return SCPE_OK; } static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) { struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; off_t addr; ssize_t byteswritten; sim_debug (ctx->dbit, ctx->dptr, "sim_os_disk_wrsect(unit=%d, lba=0x%X, sects=%d)\n", (int)(uptr-ctx->dptr->units), lba, sects); addr = ((off_t)lba) * ctx->sector_size; byteswritten = pwrite((int)((long)uptr->fileref), buf, sects * ctx->sector_size, addr); if (byteswritten < 0) { if (sectswritten) *sectswritten = 0; return SCPE_IOERR; } if (sectswritten) *sectswritten = byteswritten / ctx->sector_size; return SCPE_OK; } static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable) { if (sector_size) *sector_size = 512; if (removable) *removable = 0; return SCPE_OK; } #else /*============================================================================*/ /* Non-implemented versions */ /*============================================================================*/ static t_stat sim_os_disk_implemented_raw (void) { return SCPE_NOFNC; } static FILE *sim_os_disk_open_raw (const char *rawdevicename, const char *openmode) { return NULL; } static int sim_os_disk_close_raw (FILE *f) { return EOF; } static void sim_os_disk_flush_raw (FILE *f) { } static t_offset sim_os_disk_size_raw (FILE *f) { return (t_offset)-1; } static t_stat sim_os_disk_unload_raw (FILE *f) { return SCPE_NOFNC; } static t_bool sim_os_disk_isavailable_raw (FILE *Disk) { return FALSE; } static t_stat sim_os_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) { return SCPE_NOFNC; } static t_stat sim_os_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) { return SCPE_NOFNC; } static t_stat sim_os_disk_info_raw (FILE *f, uint32 *sector_size, uint32 *removable) { return SCPE_NOFNC; } #endif /* OS Independent Disk Virtual Disk (VHD) I/O support */ #if (defined (VMS) && !(defined (__ALPHA) || defined (__ia64))) #define DONT_DO_VHD_SUPPORT /* VAX/VMS compilers don't have 64 bit integers */ #endif #if defined (DONT_DO_VHD_SUPPORT) /*============================================================================*/ /* Non-implemented version */ /* This is only for hody systems which don't have 64 bit integer types */ /*============================================================================*/ static t_stat sim_vhd_disk_implemented (void) { return SCPE_NOFNC; } static FILE *sim_vhd_disk_open (const char *vhdfilename, const char *openmode) { return NULL; } static FILE *sim_vhd_disk_merge (const char *szVHDPath, char **ParentVHD) { return NULL; } static FILE *sim_vhd_disk_create (const char *szVHDPath, t_offset desiredsize) { return NULL; } static FILE *sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath) { return NULL; } static int sim_vhd_disk_close (FILE *f) { return -1; } static void sim_vhd_disk_flush (FILE *f) { } static t_offset sim_vhd_disk_size (FILE *f) { return (t_offset)-1; } static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) { return SCPE_IOERR; } static t_stat sim_vhd_disk_clearerr (UNIT *uptr) { return SCPE_IOERR; } static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) { return SCPE_IOERR; } static t_stat sim_vhd_disk_set_dtype (FILE *f, const char *dtype) { return SCPE_NOFNC; } static const char *sim_vhd_disk_get_dtype (FILE *f) { return NULL; } #else /*++ This code follows the details specified in the "Virtual Hard Disk Image Format Specification", Version 1.0 October 11, 2006. This format specification is available for anyone to implement under the "Microsoft Open Specification Promise" described at: http://www.microsoft.com/interop/osp/default.mspx. --*/ typedef t_uint64 uint64; typedef t_int64 int64; typedef struct _VHD_Footer { /* Cookies are used to uniquely identify the original creator of the hard disk image. The values are case-sensitive. Microsoft uses the "conectix" string to identify this file as a hard disk image created by Microsoft Virtual Server, Virtual PC, and predecessor products. The cookie is stored as an eight-character ASCII string with the "c" in the first byte, the "o" in the second byte, and so on. */ char Cookie[8]; /* This is a bit field used to indicate specific feature support. The following table displays the list of features. Any fields not listed are reserved. Feature Value: No features enabled 0x00000000 Temporary 0x00000001 Reserved 0x00000002 No features enabled. The hard disk image has no special features enabled in it. Temporary. This bit is set if the current disk is a temporary disk. A temporary disk designation indicates to an application that this disk is a candidate for deletion on shutdown. Reserved. This bit must always be set to 1. All other bits are also reserved and should be set to 0. */ uint32 Features; /* This field is divided into a major/minor version and matches the version of the specification used in creating the file. The most-significant two bytes are for the major version. The least-significant two bytes are the minor version. This must match the file format specification. For the current specification, this field must be initialized to 0x00010000. The major version will be incremented only when the file format is modified in such a way that it is no longer compatible with older versions of the file format. */ uint32 FileFormatVersion; /* This field holds the absolute byte offset, from the beginning of the file, to the next structure. This field is used for dynamic disks and differencing disks, but not fixed disks. For fixed disks, this field should be set to 0xFFFFFFFF. */ uint64 DataOffset; /* This field stores the creation time of a hard disk image. This is the number of seconds since January 1, 2000 12:00:00 AM in UTC/GMT. */ uint32 TimeStamp; /* This field is used to document which application created the hard disk. The field is a left-justified text field. It uses a single-byte character set. If the hard disk is created by Microsoft Virtual PC, "vpc " is written in this field. If the hard disk image is created by Microsoft Virtual Server, then "vs " is written in this field. Other applications should use their own unique identifiers. */ char CreatorApplication[4]; /* This field holds the major/minor version of the application that created the hard disk image. Virtual Server 2004 sets this value to 0x00010000 and Virtual PC 2004 sets this to 0x00050000. */ uint32 CreatorVersion; /* This field stores the type of host operating system this disk image is created on. Host OS type Value Windows 0x5769326B (Wi2k) Macintosh 0x4D616320 (Mac ) */ uint8 CreatorHostOS[4]; /* This field stores the size of the hard disk in bytes, from the perspective of the virtual machine, at creation time. This field is for informational purposes. */ uint64 OriginalSize; /* This field stores the current size of the hard disk, in bytes, from the perspective of the virtual machine. This value is same as the original size when the hard disk is created. This value can change depending on whether the hard disk is expanded. */ uint64 CurrentSize; /* This field stores the cylinder, heads, and sectors per track value for the hard disk. Disk Geometry field Size (bytes) Cylinder 2 Heads 1 Sectors per track/cylinder 1 When a hard disk is configured as an ATA hard disk, the CHS values (that is, Cylinder, Heads, Sectors per track) are used by the ATA controller to determine the size of the disk. When the user creates a hard disk of a certain size, the size of the hard disk image in the virtual machine is smaller than that created by the user. This is because CHS value calculated from the hard disk size is rounded down. The pseudo-code for the algorithm used to determine the CHS values can be found in the appendix of this document. */ uint32 DiskGeometry; /* Disk Type field Value None 0 Reserved (deprecated) 1 Fixed hard disk 2 Dynamic hard disk 3 Differencing hard disk 4 Reserved (deprecated) 5 Reserved (deprecated) 6 */ uint32 DiskType; /* This field holds a basic checksum of the hard disk footer. It is just a one's complement of the sum of all the bytes in the footer without the checksum field. If the checksum verification fails, the Virtual PC and Virtual Server products will instead use the header. If the checksum in the header also fails, the file should be assumed to be corrupt. The pseudo-code for the algorithm used to determine the checksum can be found in the appendix of this document. */ uint32 Checksum; /* Every hard disk has a unique ID stored in the hard disk. This is used to identify the hard disk. This is a 128-bit universally unique identifier (UUID). This field is used to associate a parent hard disk image with its differencing hard disk image(s). */ uint8 UniqueID[16]; /* This field holds a one-byte flag that describes whether the system is in saved state. If the hard disk is in the saved state the value is set to 1. Operations such as compaction and expansion cannot be performed on a hard disk in a saved state. */ uint8 SavedState; /* This field contains zeroes. It is 427 bytes in size. */ uint8 Reserved1[11]; /* This field is an extension to the VHD spec and includes a simh drive type name as a nul terminated string. */ uint8 DriveType[16]; /* This field contains zeroes. It is 400 bytes in size. */ uint8 Reserved[400]; } VHD_Footer; /* For dynamic and differencing disk images, the "Data Offset" field within the image footer points to a secondary structure that provides additional information about the disk image. The dynamic disk header should appear on a sector (512-byte) boundary. */ typedef struct _VHD_DynamicDiskHeader { /* This field holds the value "cxsparse". This field identifies the header. */ char Cookie[8]; /* This field contains the absolute byte offset to the next structure in the hard disk image. It is currently unused by existing formats and should be set to 0xFFFFFFFF. */ uint64 DataOffset; /* This field stores the absolute byte offset of the Block Allocation Table (BAT) in the file. */ uint64 TableOffset; /* This field stores the version of the dynamic disk header. The field is divided into Major/Minor version. The least-significant two bytes represent the minor version, and the most-significant two bytes represent the major version. This must match with the file format specification. For this specification, this field must be initialized to 0x00010000. The major version will be incremented only when the header format is modified in such a way that it is no longer compatible with older versions of the product. */ uint32 HeaderVersion; /* This field holds the maximum entries present in the BAT. This should be equal to the number of blocks in the disk (that is, the disk size divided by the block size). */ uint32 MaxTableEntries; /* A block is a unit of expansion for dynamic and differencing hard disks. It is stored in bytes. This size does not include the size of the block bitmap. It is only the size of the data section of the block. The sectors per block must always be a power of two. The default value is 0x00200000 (indicating a block size of 2 MB). */ uint32 BlockSize; /* This field holds a basic checksum of the dynamic header. It is a one's complement of the sum of all the bytes in the header without the checksum field. If the checksum verification fails the file should be assumed to be corrupt. */ uint32 Checksum; /* This field is used for differencing hard disks. A differencing hard disk stores a 128-bit UUID of the parent hard disk. For more information, see "Creating Differencing Hard Disk Images" later in this paper. */ uint8 ParentUniqueID[16]; /* This field stores the modification time stamp of the parent hard disk. This is the number of seconds since January 1, 2000 12:00:00 AM in UTC/GMT. */ uint32 ParentTimeStamp; /* This field should be set to zero. */ uint32 Reserved0; /* This field contains a Unicode string (UTF-16) of the parent hard disk filename. */ char ParentUnicodeName[512]; /* These entries store an absolute byte offset in the file where the parent locator for a differencing hard disk is stored. This field is used only for differencing disks and should be set to zero for dynamic disks. */ struct VHD_ParentLocator { /* The platform code describes which platform-specific format is used for the file locator. For Windows, a file locator is stored as a path (for example. "c:\disksimages\ParentDisk.vhd"). On a Macintosh system, the file locator is a binary large object (blob) that contains an "alias." The parent locator table is used to support moving hard disk images across platforms. Some current platform codes include the following: Platform Code Description None (0x0) Wi2r (0x57693272) [deprecated] Wi2k (0x5769326B) [deprecated] W2ru (0x57327275) Unicode pathname (UTF-16) on Windows relative to the differencing disk pathname. W2ku (0x57326B75) Absolute Unicode (UTF-16) pathname on Windows. Mac (0x4D616320) (Mac OS alias stored as a blob) MacX(0x4D616358) A file URL with UTF-8 encoding conforming to RFC 2396. */ uint8 PlatformCode[4]; /* This field stores the number of 512-byte sectors needed to store the parent hard disk locator. */ uint32 PlatformDataSpace; /* This field stores the actual length of the parent hard disk locator in bytes. */ uint32 PlatformDataLength; /* This field must be set to zero. */ uint32 Reserved; /* This field stores the absolute file offset in bytes where the platform specific file locator data is stored. */ uint64 PlatformDataOffset; /* This field stores the absolute file offset in bytes where the platform specific file locator data is stored. */ } ParentLocatorEntries[8]; /* This must be initialized to zeroes. */ char Reserved[256]; } VHD_DynamicDiskHeader; #define VHD_BAT_FREE_ENTRY (0xFFFFFFFF) #define VHD_DATA_BLOCK_ALIGNMENT ((uint64)4096) /* Optimum when underlying storage has 4k sectors */ #define VHD_DT_Fixed 2 /* Fixed hard disk */ #define VHD_DT_Dynamic 3 /* Dynamic hard disk */ #define VHD_DT_Differencing 4 /* Differencing hard disk */ static uint32 NtoHl(uint32 value); static uint64 NtoHll(uint64 value); typedef struct VHD_IOData *VHDHANDLE; static t_stat ReadFilePosition(FILE *File, void *buf, size_t bufsize, size_t *bytesread, uint64 position) { uint32 err = sim_fseeko (File, (t_offset)position, SEEK_SET); size_t i; if (bytesread) *bytesread = 0; if (!err) { i = fread (buf, 1, bufsize, File); err = ferror (File); if ((!err) && bytesread) *bytesread = i; } return (err ? SCPE_IOERR : SCPE_OK); } static t_stat WriteFilePosition(FILE *File, void *buf, size_t bufsize, size_t *byteswritten, uint64 position) { uint32 err = sim_fseeko (File, (t_offset)position, SEEK_SET); size_t i; if (byteswritten) *byteswritten = 0; if (!err) { i = fwrite (buf, 1, bufsize, File); err = ferror (File); if ((!err) && byteswritten) *byteswritten = i; } return (err ? SCPE_IOERR : SCPE_OK); } static uint32 CalculateVhdFooterChecksum(void *data, size_t size) { uint32 sum = 0; uint8 *c = (uint8 *)data; while (size--) sum += *c++; return ~sum; } #if defined(_WIN32) || defined (__ALPHA) || defined (__ia64) || defined (VMS) #ifndef __BYTE_ORDER__ #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ #endif #endif #ifndef __BYTE_ORDER__ #define __BYTE_ORDER__ UNKNOWN #endif #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ static uint32 NtoHl(uint32 value) { uint8 *l = (uint8 *)&value; return l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); } static uint64 NtoHll(uint64 value) { uint8 *l = (uint8 *)&value; uint64 highresult = l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); uint32 lowresult = l[7] | (l[6]<<8) | (l[5]<<16) | (l[4]<<24); return (highresult << 32) | lowresult; } #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ static uint32 NtoHl(uint32 value) { return value; } static uint64 NtoHll(uint64 value) { return value; } #else static uint32 NtoHl(uint32 value) { uint8 *l = (uint8 *)&value; if (sim_end) return l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); return value; } static uint64 NtoHll(uint64 value) { uint8 *l = (uint8 *)&value; if (sim_end) { uint64 highresult = l[3] | (l[2]<<8) | (l[1]<<16) | (l[0]<<24); uint32 lowresult = l[7] | (l[6]<<8) | (l[5]<<16) | (l[4]<<24); return (highresult << 32) | lowresult; } return value; } #endif static int GetVHDFooter(const char *szVHDPath, VHD_Footer *sFooter, VHD_DynamicDiskHeader *sDynamic, uint32 **aBAT, uint32 *ModifiedTimeStamp, char *szParentVHDPath, size_t ParentVHDPathSize) { FILE *File = NULL; uint64 position; uint32 sum, saved_sum; int Return = 0; VHD_Footer sHeader; struct stat statb; if (sFooter) memset(sFooter, '\0', sizeof(*sFooter)); if (sDynamic) memset(sDynamic, '\0', sizeof(*sDynamic)); if (aBAT) *aBAT = NULL; File = sim_fopen (szVHDPath, "rb"); if (!File) { Return = errno; goto Return_Cleanup; } if (ModifiedTimeStamp) { if (fstat (fileno (File), &statb)) { Return = errno; goto Return_Cleanup; } else *ModifiedTimeStamp = NtoHl ((uint32)(statb.st_mtime-946684800)); } position = sim_fsize_ex (File); if (((int64)position) == -1) { Return = errno; goto Return_Cleanup; } position -= sizeof(*sFooter); if (ReadFilePosition(File, sFooter, sizeof(*sFooter), NULL, position)) { Return = errno; goto Return_Cleanup; } saved_sum = NtoHl(sFooter->Checksum); sFooter->Checksum = 0; sum = CalculateVhdFooterChecksum(sFooter, sizeof(*sFooter)); sFooter->Checksum = NtoHl(saved_sum); if ((sum != saved_sum) || (memcmp("conectix", sFooter->Cookie, sizeof(sFooter->Cookie)))) { Return = EINVAL; /* File Corrupt */ goto Return_Cleanup; } if (ReadFilePosition(File, &sHeader, sizeof(sHeader), NULL, (uint64)0)) { Return = errno; goto Return_Cleanup; } if ((NtoHl(sFooter->DiskType) != VHD_DT_Dynamic) && (NtoHl(sFooter->DiskType) != VHD_DT_Differencing) && (NtoHl(sFooter->DiskType) != VHD_DT_Fixed)) { Return = EINVAL; /* File Corrupt */ goto Return_Cleanup; } if (((NtoHl(sFooter->DiskType) == VHD_DT_Dynamic) || (NtoHl(sFooter->DiskType) == VHD_DT_Differencing)) && memcmp(sFooter, &sHeader, sizeof(sHeader))) { Return = EINVAL; /* File Corrupt */ goto Return_Cleanup; } if ((sDynamic) && ((NtoHl(sFooter->DiskType) == VHD_DT_Dynamic) || (NtoHl(sFooter->DiskType) == VHD_DT_Differencing))) { if (ReadFilePosition(File, sDynamic, sizeof (*sDynamic), NULL, NtoHll (sFooter->DataOffset))) { Return = errno; goto Return_Cleanup; } saved_sum = NtoHl (sDynamic->Checksum); sDynamic->Checksum = 0; sum = CalculateVhdFooterChecksum (sDynamic, sizeof(*sDynamic)); sDynamic->Checksum = NtoHl (saved_sum); if ((sum != saved_sum) || (memcmp ("cxsparse", sDynamic->Cookie, sizeof (sDynamic->Cookie)))) { Return = errno; goto Return_Cleanup; } if (aBAT) { *aBAT = (uint32*) malloc(512*((sizeof(**aBAT)*NtoHl(sDynamic->MaxTableEntries)+511)/512)); if (ReadFilePosition(File, *aBAT, sizeof (**aBAT)*NtoHl(sDynamic->MaxTableEntries), NULL, NtoHll (sDynamic->TableOffset))) { Return = EINVAL; /* File Corrupt */ goto Return_Cleanup; } } if (szParentVHDPath && ParentVHDPathSize) { VHD_Footer sParentFooter; memset (szParentVHDPath, '\0', ParentVHDPathSize); if (NtoHl (sFooter->DiskType) == VHD_DT_Differencing) { size_t i, j; for (j=0; j<8; ++j) { uint8 *Pdata; uint32 PdataSize; char ParentName[512]; char CheckPath[512]; uint32 ParentModificationTime; if ('\0' == sDynamic->ParentLocatorEntries[j].PlatformCode[0]) continue; memset (ParentName, '\0', sizeof(ParentName)); memset (CheckPath, '\0', sizeof(CheckPath)); PdataSize = NtoHl(sDynamic->ParentLocatorEntries[j].PlatformDataSpace); Pdata = (uint8*) calloc (1, PdataSize+2); if (!Pdata) continue; if (ReadFilePosition(File, Pdata, PdataSize, NULL, NtoHll (sDynamic->ParentLocatorEntries[j].PlatformDataOffset))) { free (Pdata); continue; } for (i=0; i<NtoHl(sDynamic->ParentLocatorEntries[j].PlatformDataLength); i+=2) if ((Pdata[i] == '\0') && (Pdata[i+1] == '\0')) { ParentName[i/2] = '\0'; break; } else ParentName[i/2] = Pdata[i] ? Pdata[i] : Pdata[i+1]; free (Pdata); if (0 == memcmp (sDynamic->ParentLocatorEntries[j].PlatformCode, "W2ku", 4)) strncpy (CheckPath, ParentName, sizeof (CheckPath)-1); else if (0 == memcmp (sDynamic->ParentLocatorEntries[j].PlatformCode, "W2ru", 4)) { const char *c; if ((c = strrchr (szVHDPath, '\\'))) memcpy (CheckPath, szVHDPath, c-szVHDPath+1); strncpy (CheckPath+strlen(CheckPath), ParentName, sizeof (CheckPath)-(strlen (CheckPath)+1)); } VhdPathToHostPath (CheckPath, CheckPath, sizeof (CheckPath)); if (0 == GetVHDFooter(CheckPath, &sParentFooter, NULL, NULL, &ParentModificationTime, NULL, 0)) { if ((0 == memcmp (sDynamic->ParentUniqueID, sParentFooter.UniqueID, sizeof (sParentFooter.UniqueID))) && ((sDynamic->ParentTimeStamp == ParentModificationTime) || ((NtoHl(sDynamic->ParentTimeStamp)-NtoHl(ParentModificationTime)) == 3600) || (sim_switches & SWMASK ('O')))) strncpy (szParentVHDPath, CheckPath, ParentVHDPathSize); else { if (0 != memcmp (sDynamic->ParentUniqueID, sParentFooter.UniqueID, sizeof (sParentFooter.UniqueID))) sim_printf ("Error Invalid Parent VHD '%s' for Differencing VHD: %s\n", CheckPath, szVHDPath); else sim_printf ("Error Parent VHD '%s' has been modified since Differencing VHD: %s was created\n", CheckPath, szVHDPath); Return = EINVAL; /* File Corrupt/Invalid */ } break; } else { struct stat statb; if (0 == stat (CheckPath, &statb)) { sim_printf ("Parent VHD '%s' corrupt for Differencing VHD: %s\n", CheckPath, szVHDPath); Return = EBADF; /* File Corrupt/Invalid */ break; } } } if (!*szParentVHDPath) { if (Return != EINVAL) /* File Not Corrupt? */ sim_printf ("Missing Parent VHD for Differencing VHD: %s\n", szVHDPath); Return = EBADF; } } } } Return_Cleanup: if (File) fclose(File); if (aBAT && (0 != Return)) { free (*aBAT); *aBAT = NULL; } return errno = Return; } struct VHD_IOData { VHD_Footer Footer; VHD_DynamicDiskHeader Dynamic; uint32 *BAT; FILE *File; char ParentVHDPath[512]; struct VHD_IOData *Parent; }; static t_stat sim_vhd_disk_implemented (void) { return SCPE_OK; } static t_stat sim_vhd_disk_set_dtype (FILE *f, const char *dtype) { VHDHANDLE hVHD = (VHDHANDLE)f; int Status = 0; memset (hVHD->Footer.DriveType, '\0', sizeof hVHD->Footer.DriveType); memcpy (hVHD->Footer.DriveType, dtype, ((1+strlen (dtype)) < sizeof (hVHD->Footer.DriveType)) ? (1+strlen (dtype)) : sizeof (hVHD->Footer.DriveType)); hVHD->Footer.Checksum = 0; hVHD->Footer.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Footer, sizeof(hVHD->Footer))); if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Fixed) { if (WriteFilePosition(hVHD->File, &hVHD->Footer, sizeof(hVHD->Footer), NULL, NtoHll (hVHD->Footer.CurrentSize))) Status = errno; goto Cleanup_Return; } else { uint64 position; position = sim_fsize_ex (hVHD->File); if (((int64)position) == -1) { Status = errno; goto Cleanup_Return; } position -= sizeof(hVHD->Footer); /* Update both copies on a dynamic disk */ if (WriteFilePosition(hVHD->File, &hVHD->Footer, sizeof(hVHD->Footer), NULL, (uint64)0)) { Status = errno; goto Cleanup_Return; } if (WriteFilePosition(hVHD->File, &hVHD->Footer, sizeof(hVHD->Footer), NULL, position)) { Status = errno; goto Cleanup_Return; } } Cleanup_Return: if (Status) return SCPE_IOERR; return SCPE_OK; } static const char *sim_vhd_disk_get_dtype (FILE *f) { VHDHANDLE hVHD = (VHDHANDLE)f; return (char *)(&hVHD->Footer.DriveType[0]); } static FILE *sim_vhd_disk_open (const char *szVHDPath, const char *DesiredAccess) { VHDHANDLE hVHD = (VHDHANDLE) calloc (1, sizeof(*hVHD)); int NeedUpdate = FALSE; int Status; if (!hVHD) return (FILE *)hVHD; Status = GetVHDFooter (szVHDPath, &hVHD->Footer, &hVHD->Dynamic, &hVHD->BAT, NULL, hVHD->ParentVHDPath, sizeof (hVHD->ParentVHDPath)); if (Status) goto Cleanup_Return; if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Differencing) { uint32 ParentModifiedTimeStamp; VHD_Footer ParentFooter; VHD_DynamicDiskHeader ParentDynamic; hVHD->Parent = (VHDHANDLE)sim_vhd_disk_open (hVHD->ParentVHDPath, "rb"); if (!hVHD->Parent) { Status = errno; goto Cleanup_Return; } Status = GetVHDFooter (hVHD->ParentVHDPath, &ParentFooter, &ParentDynamic, NULL, &ParentModifiedTimeStamp, NULL, 0); if (Status) goto Cleanup_Return; if ((0 != memcmp (hVHD->Dynamic.ParentUniqueID, ParentFooter.UniqueID, sizeof (ParentFooter.UniqueID))) || (ParentModifiedTimeStamp != hVHD->Dynamic.ParentTimeStamp)) { if (sim_switches & SWMASK ('O')) { /* OVERRIDE consistency checks? */ if ((sim_switches & SWMASK ('U')) && /* FIX (UPDATE) consistency checks AND */ (strchr (DesiredAccess, '+'))) { /* open for write/update? */ memcpy (hVHD->Dynamic.ParentUniqueID, ParentFooter.UniqueID, sizeof (ParentFooter.UniqueID)); hVHD->Dynamic.ParentTimeStamp = ParentModifiedTimeStamp; hVHD->Dynamic.Checksum = 0; hVHD->Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Dynamic, sizeof(hVHD->Dynamic))); NeedUpdate = TRUE; } } else { Status = EBADF; goto Cleanup_Return; } } } if (hVHD->Footer.SavedState) { Status = EAGAIN; /* Busy */ goto Cleanup_Return; } hVHD->File = sim_fopen (szVHDPath, DesiredAccess); if (!hVHD->File) { Status = errno; goto Cleanup_Return; } Cleanup_Return: if (Status) { sim_vhd_disk_close ((FILE *)hVHD); hVHD = NULL; } else { if (NeedUpdate) { /* Update Differencing Disk Header? */ if (WriteFilePosition(hVHD->File, &hVHD->Dynamic, sizeof (hVHD->Dynamic), NULL, NtoHll (hVHD->Footer.DataOffset))) { sim_vhd_disk_close ((FILE *)hVHD); hVHD = NULL; } } } errno = Status; return (FILE *)hVHD; } static t_stat WriteVirtualDiskSectors(VHDHANDLE hVHD, uint8 *buf, t_seccnt sects, t_seccnt *sectswritten, uint32 SectorSize, t_lba lba); static FILE *sim_vhd_disk_merge (const char *szVHDPath, char **ParentVHD) { VHDHANDLE hVHD = (VHDHANDLE) calloc (1, sizeof(*hVHD)); VHDHANDLE Parent = NULL; int Status; uint32 SectorSize, SectorsPerBlock, BlockSize, BlockNumber, BitMapBytes, BitMapSectors, BlocksToMerge, NeededBlock; uint64 BlockOffset; size_t BytesRead; t_seccnt SectorsWritten; void *BlockData = NULL; if (!hVHD) return (FILE *)hVHD; if (0 != (Status = GetVHDFooter (szVHDPath, &hVHD->Footer, &hVHD->Dynamic, &hVHD->BAT, NULL, hVHD->ParentVHDPath, sizeof (hVHD->ParentVHDPath)))) goto Cleanup_Return; if (NtoHl (hVHD->Footer.DiskType) != VHD_DT_Differencing) { Status = EINVAL; goto Cleanup_Return; } if (hVHD->Footer.SavedState) { Status = EAGAIN; /* Busy */ goto Cleanup_Return; } SectorSize = 512; BlockSize = NtoHl (hVHD->Dynamic.BlockSize); BlockData = malloc (BlockSize*SectorSize); if (NULL == BlockData) { Status = errno; goto Cleanup_Return; } Parent = (VHDHANDLE)sim_vhd_disk_open (hVHD->ParentVHDPath, "rb+"); if (!Parent) { Status = errno; goto Cleanup_Return; } hVHD->File = sim_fopen (szVHDPath, "rb"); if (!hVHD->File) { Status = errno; goto Cleanup_Return; } SectorsPerBlock = NtoHl (hVHD->Dynamic.BlockSize)/SectorSize; BitMapBytes = (7+(NtoHl (hVHD->Dynamic.BlockSize)/SectorSize))/8; BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize; for (BlockNumber=BlocksToMerge=0; BlockNumber< NtoHl (hVHD->Dynamic.MaxTableEntries); ++BlockNumber) { if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) continue; ++BlocksToMerge; } if (!sim_quiet) sim_printf ("Merging %s\ninto %s\n", szVHDPath, hVHD->ParentVHDPath); for (BlockNumber=NeededBlock=0; BlockNumber < NtoHl (hVHD->Dynamic.MaxTableEntries); ++BlockNumber) { uint32 BlockSectors = SectorsPerBlock; if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) continue; ++NeededBlock; BlockOffset = SectorSize*((uint64)(NtoHl (hVHD->BAT[BlockNumber]) + BitMapSectors)); if ((BlockNumber*SectorsPerBlock + BlockSectors) > ((uint64)NtoHll (hVHD->Footer.CurrentSize))/SectorSize) BlockSectors = (uint32)(((uint64)NtoHll (hVHD->Footer.CurrentSize))/SectorSize - (BlockNumber*SectorsPerBlock)); if (ReadFilePosition(hVHD->File, BlockData, SectorSize*BlockSectors, &BytesRead, BlockOffset)) break; if (WriteVirtualDiskSectors (Parent, (uint8*)BlockData, BlockSectors, &SectorsWritten, SectorSize, SectorsPerBlock*BlockNumber)) break; if (!sim_quiet) sim_printf ("Merged %dMB. %d%% complete.\r", (int)((((float)NeededBlock)*SectorsPerBlock)*SectorSize/1000000), (int)((((float)NeededBlock)*100)/BlocksToMerge)); hVHD->BAT[BlockNumber] = VHD_BAT_FREE_ENTRY; } if (BlockNumber < NtoHl (hVHD->Dynamic.MaxTableEntries)) { Status = errno; } else { Status = 0; if (!sim_quiet) sim_printf ("Merged %dMB. 100%% complete.\n", (int)((((float)NeededBlock)*SectorsPerBlock)*SectorSize/1000000)); fclose (hVHD->File); hVHD->File = NULL; remove (szVHDPath); *ParentVHD = (char*) malloc (strlen (hVHD->ParentVHDPath)+1); strcpy (*ParentVHD, hVHD->ParentVHDPath); } Cleanup_Return: free (BlockData); if (hVHD->File) fclose (hVHD->File); if (Status) { free (hVHD->BAT); free (hVHD); hVHD = NULL; sim_vhd_disk_close ((FILE *)Parent); } else { free (hVHD->BAT); free (hVHD); hVHD = Parent; } errno = Status; return (FILE *)hVHD; } static int sim_vhd_disk_close (FILE *f) { VHDHANDLE hVHD = (VHDHANDLE)f; if (NULL != hVHD) { if (hVHD->Parent) sim_vhd_disk_close ((FILE *)hVHD->Parent); free (hVHD->BAT); if (hVHD->File) { fflush (hVHD->File); fclose (hVHD->File); } free (hVHD); return 0; } return -1; } static void sim_vhd_disk_flush (FILE *f) { VHDHANDLE hVHD = (VHDHANDLE)f; if ((NULL != hVHD) && (hVHD->File)) fflush (hVHD->File); } static t_offset sim_vhd_disk_size (FILE *f) { VHDHANDLE hVHD = (VHDHANDLE)f; return (t_offset)(NtoHll (hVHD->Footer.CurrentSize)); } #include <stdlib.h> #include <time.h> static void _rand_uuid_gen (void *uuidaddr) { int i; uint8 *b = (uint8 *)uuidaddr; uint32 timenow = (uint32)time (NULL); memcpy (uuidaddr, &timenow, sizeof (timenow)); srand ((unsigned)timenow); for (i=4; i<16; i++) { b[i] = (uint8)rand(); } } #if defined (_WIN32) static void uuid_gen (void *uuidaddr) { static RPC_STATUS (RPC_ENTRY *UuidCreate_c) (void *); if (!UuidCreate_c) { HINSTANCE hDll; hDll = LoadLibraryA("rpcrt4.dll"); UuidCreate_c = (RPC_STATUS (RPC_ENTRY *) (void *))GetProcAddress(hDll, "UuidCreate"); } if (UuidCreate_c) UuidCreate_c(uuidaddr); else _rand_uuid_gen (uuidaddr); } #elif defined (HAVE_DLOPEN) #include <dlfcn.h> static void uuid_gen (void *uuidaddr) { void (*uuid_generate_c) (void *) = NULL; void *handle; #define S__STR_QUOTE(tok) #tok #define S__STR(tok) S__STR_QUOTE(tok) handle = dlopen("libuuid." S__STR(HAVE_DLOPEN), RTLD_NOW|RTLD_GLOBAL); if (handle) uuid_generate_c = (void (*)(void *))((size_t)dlsym(handle, "uuid_generate")); if (uuid_generate_c) uuid_generate_c(uuidaddr); else _rand_uuid_gen (uuidaddr); if (handle) dlclose(handle); } #else static void uuid_gen (void *uuidaddr) { _rand_uuid_gen (uuidaddr); } #endif static VHDHANDLE CreateVirtualDisk(const char *szVHDPath, uint32 SizeInSectors, uint32 BlockSize, t_bool bFixedVHD) { VHD_Footer Footer; VHD_DynamicDiskHeader Dynamic; uint32 *BAT = NULL; time_t now; uint32 i; FILE *File = NULL; uint32 Status = 0; uint32 BytesPerSector = 512; uint64 SizeInBytes = ((uint64)SizeInSectors)*BytesPerSector; uint64 TableOffset; uint32 MaxTableEntries; VHDHANDLE hVHD = NULL; if (SizeInBytes > ((uint64)(1024*1024*1024))*2040) { Status = EFBIG; goto Cleanup_Return; } File = sim_fopen (szVHDPath, "rb"); if (File) { fclose (File); File = NULL; Status = EEXIST; goto Cleanup_Return; } File = sim_fopen (szVHDPath, "wb"); if (!File) { Status = errno; goto Cleanup_Return; } memset (&Footer, 0, sizeof(Footer)); memcpy (Footer.Cookie, "conectix", 8); Footer.Features = NtoHl (0x00000002);; Footer.FileFormatVersion = NtoHl (0x00010000);; Footer.DataOffset = NtoHll (bFixedVHD ? ((long long)-1) : (long long)(sizeof(Footer))); time (&now); Footer.TimeStamp = NtoHl ((uint32)(now-946684800)); memcpy (Footer.CreatorApplication, "simh", 4); Footer.CreatorVersion = NtoHl (0x00040000); memcpy (Footer.CreatorHostOS, "Wi2k", 4); Footer.OriginalSize = NtoHll (SizeInBytes); Footer.CurrentSize = NtoHll (SizeInBytes); uuid_gen (Footer.UniqueID); Footer.DiskType = NtoHl (bFixedVHD ? VHD_DT_Fixed : VHD_DT_Dynamic); Footer.DiskGeometry = NtoHl (0xFFFF10FF); if (1) { /* CHS Calculation */ uint32 totalSectors = (uint32)(SizeInBytes/BytesPerSector);/* Total data sectors present in the disk image */ uint32 cylinders; /* Number of cylinders present on the disk */ uint32 heads; /* Number of heads present on the disk */ uint32 sectorsPerTrack; /* Sectors per track on the disk */ uint32 cylinderTimesHeads; /* Cylinders x heads */ if (totalSectors > 65535 * 16 * 255) totalSectors = 65535 * 16 * 255; if (totalSectors >= 65535 * 16 * 63) { sectorsPerTrack = 255; heads = 16; cylinderTimesHeads = totalSectors / sectorsPerTrack; } else { sectorsPerTrack = 17; cylinderTimesHeads = totalSectors / sectorsPerTrack; heads = (cylinderTimesHeads + 1023) / 1024; if (heads < 4) heads = 4; if (cylinderTimesHeads >= (heads * 1024) || heads > 16) { sectorsPerTrack = 31; heads = 16; cylinderTimesHeads = totalSectors / sectorsPerTrack; } if (cylinderTimesHeads >= (heads * 1024)) { sectorsPerTrack = 63; heads = 16; cylinderTimesHeads = totalSectors / sectorsPerTrack; } } cylinders = cylinderTimesHeads / heads; Footer.DiskGeometry = NtoHl ((cylinders<<16)|(heads<<8)|sectorsPerTrack); } Footer.Checksum = NtoHl (CalculateVhdFooterChecksum(&Footer, sizeof(Footer))); if (bFixedVHD) { if (WriteFilePosition(File, &Footer, sizeof(Footer), NULL, SizeInBytes)) Status = errno; goto Cleanup_Return; } /* Dynamic Disk */ memset (&Dynamic, 0, sizeof(Dynamic)); memcpy (Dynamic.Cookie, "cxsparse", 8); Dynamic.DataOffset = NtoHll ((uint64)0xFFFFFFFFFFFFFFFFLL); TableOffset = NtoHll(Footer.DataOffset)+sizeof(Dynamic); Dynamic.TableOffset = NtoHll (TableOffset); Dynamic.HeaderVersion = NtoHl (0x00010000); if (0 == BlockSize) BlockSize = 2*1024*1024; Dynamic.BlockSize = NtoHl (BlockSize); MaxTableEntries = (uint32)((SizeInBytes+BlockSize-1)/BlockSize); Dynamic.MaxTableEntries = NtoHl (MaxTableEntries); Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum(&Dynamic, sizeof(Dynamic))); BAT = (uint32*) malloc (BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector)); memset (BAT, 0, BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector)); for (i=0; i<MaxTableEntries; ++i) BAT[i] = VHD_BAT_FREE_ENTRY; if (WriteFilePosition(File, &Footer, sizeof(Footer), NULL, 0)) { Status = errno; goto Cleanup_Return; } if (WriteFilePosition(File, &Dynamic, sizeof(Dynamic), NULL, NtoHll(Footer.DataOffset))) { Status = errno; goto Cleanup_Return; } if (WriteFilePosition(File, BAT, BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector), NULL, NtoHll(Dynamic.TableOffset))) { Status = errno; goto Cleanup_Return; } if (WriteFilePosition(File, &Footer, sizeof(Footer), NULL, NtoHll(Dynamic.TableOffset)+BytesPerSector*((MaxTableEntries*sizeof(*BAT)+BytesPerSector-1)/BytesPerSector))) { Status = errno; goto Cleanup_Return; } Cleanup_Return: free (BAT); if (File) fclose (File); if (Status) { if (Status != EEXIST) remove (szVHDPath); } else { hVHD = (VHDHANDLE)sim_vhd_disk_open (szVHDPath, "rb+"); if (!hVHD) Status = errno; } errno = Status; return hVHD; } #if defined(__CYGWIN__) || defined(VMS) || defined(__APPLE__) || defined(__linux) || defined(__linux__) || defined(__unix__) #include <unistd.h> #endif static void ExpandToFullPath (const char *szFileSpec, char *szFullFileSpecBuffer, size_t BufferSize) { char *c; #ifdef _WIN32 for (c = strchr (szFullFileSpecBuffer, '/'); c; c = strchr (szFullFileSpecBuffer, '/')) *c = '\\'; GetFullPathNameA (szFileSpec, (DWORD)BufferSize, szFullFileSpecBuffer, NULL); for (c = strchr (szFullFileSpecBuffer, '\\'); c; c = strchr (szFullFileSpecBuffer, '\\')) *c = '/'; #else char buffer[PATH_MAX]; char *wd = getcwd(buffer, PATH_MAX); if ((szFileSpec[0] != '/') || (strchr (szFileSpec, ':'))) snprintf (szFullFileSpecBuffer, BufferSize, "%s/%s", wd, szFileSpec); else strncpy (szFullFileSpecBuffer, szFileSpec, BufferSize); if ((c = strstr (szFullFileSpecBuffer, "]/"))) memcpy (c+1, c+2, strlen(c+2)+1); memset (szFullFileSpecBuffer + strlen (szFullFileSpecBuffer), 0, BufferSize - strlen (szFullFileSpecBuffer)); #endif } static char * HostPathToVhdPath (const char *szHostPath, char *szVhdPath, size_t VhdPathSize) { char *c, *d; strncpy (szVhdPath, szHostPath, VhdPathSize-1); if ((szVhdPath[1] == ':') && islower(szVhdPath[0])) szVhdPath[0] = toupper(szVhdPath[0]); szVhdPath[VhdPathSize-1] = '\0'; if ((c = strrchr (szVhdPath, ']'))) { *c = '\0'; if (!(d = strchr (szVhdPath, '['))) return d; *d = '/'; while ((d = strchr (d, '.'))) *d = '/'; *c = '/'; } while ((c = strchr (szVhdPath, '/'))) *c = '\\'; for (c = strstr (szVhdPath, "\\.\\"); c; c = strstr (szVhdPath, "\\.\\")) memcpy (c, c+2, strlen(c+2)+1); for (c = strstr (szVhdPath, "\\\\"); c; c = strstr (szVhdPath, "\\\\")) memcpy (c, c+1, strlen(c+1)+1); while ((c = strstr (szVhdPath, "\\..\\"))) { *c = '\0'; d = strrchr (szVhdPath, '\\'); if (d) memcpy (d, c+3, strlen(c+3)+1); else return d; } memset (szVhdPath + strlen (szVhdPath), 0, VhdPathSize - strlen (szVhdPath)); return szVhdPath; } static char * VhdPathToHostPath (const char *szVhdPath, char *szHostPath, size_t HostPathSize) { char *c; char *d = szHostPath; strncpy (szHostPath, szVhdPath, HostPathSize-1); szHostPath[HostPathSize-1] = '\0'; #if defined(VMS) c = strchr (szVhdPath, ':'); if (*(c+1) != '\\') return NULL; *(c+1) = '['; d = strrchr (c+2, '\\'); if (d) { *d = ']'; while ((d = strrchr (c+2, '\\'))) *d = '.'; } else return NULL; #else while ((c = strchr (d, '\\'))) *c = '/'; #endif memset (szHostPath + strlen (szHostPath), 0, HostPathSize - strlen (szHostPath)); return szHostPath; } static VHDHANDLE CreateDifferencingVirtualDisk(const char *szVHDPath, const char *szParentVHDPath) { uint32 BytesPerSector = 512; VHDHANDLE hVHD = NULL; VHD_Footer ParentFooter; VHD_DynamicDiskHeader ParentDynamic; uint32 ParentTimeStamp; uint32 Status = 0; char *RelativeParentVHDPath = NULL; char *FullParentVHDPath = NULL; char *RelativeParentVHDPathUnicode = NULL; char *FullParentVHDPathUnicode = NULL; char *FullVHDPath = NULL; char *TempPath = NULL; size_t i, RelativeMatch, UpDirectories, LocatorsWritten = 0; int64 LocatorPosition; if ((Status = GetVHDFooter (szParentVHDPath, &ParentFooter, &ParentDynamic, NULL, &ParentTimeStamp, NULL, 0))) goto Cleanup_Return; hVHD = CreateVirtualDisk (szVHDPath, (uint32)(NtoHll(ParentFooter.CurrentSize)/BytesPerSector), NtoHl(ParentDynamic.BlockSize), FALSE); if (!hVHD) { Status = errno; goto Cleanup_Return; } LocatorPosition = ((sizeof (hVHD->Footer) + BytesPerSector - 1)/BytesPerSector + (sizeof (hVHD->Dynamic) + BytesPerSector - 1)/BytesPerSector)*BytesPerSector; hVHD->Dynamic.Checksum = 0; RelativeParentVHDPath = (char*) calloc (1, BytesPerSector+2); FullParentVHDPath = (char*) calloc (1, BytesPerSector+2); RelativeParentVHDPathUnicode = (char*) calloc (1, BytesPerSector+2); FullParentVHDPathUnicode = (char*) calloc (1, BytesPerSector+2); FullVHDPath = (char*) calloc (1, BytesPerSector+2); TempPath = (char*) calloc (1, BytesPerSector+2); ExpandToFullPath (szParentVHDPath, TempPath, BytesPerSector); HostPathToVhdPath (TempPath, FullParentVHDPath, BytesPerSector); for (i=0; i < strlen (FullParentVHDPath); i++) hVHD->Dynamic.ParentUnicodeName[i*2+1] = FullParentVHDPath[i]; /* Big Endian Unicode */ for (i=0; i < strlen (FullParentVHDPath); i++) FullParentVHDPathUnicode[i*2] = FullParentVHDPath[i]; /* Little Endian Unicode */ ExpandToFullPath (szVHDPath, TempPath, BytesPerSector); HostPathToVhdPath (TempPath, FullVHDPath, BytesPerSector); for (i=0, RelativeMatch=UpDirectories=0; i<strlen(FullVHDPath); i++) if (FullVHDPath[i] == '\\') { if (memcmp (FullVHDPath, FullParentVHDPath, i+1)) ++UpDirectories; else RelativeMatch = i; } if (RelativeMatch) { char UpDir[4] = "..\\"; UpDir[2] = FullParentVHDPath[RelativeMatch]; if (UpDirectories) for (i=0; i<UpDirectories; i++) strcpy (RelativeParentVHDPath+strlen (RelativeParentVHDPath), UpDir); else strcpy (RelativeParentVHDPath+strlen (RelativeParentVHDPath), UpDir+1); strcpy (RelativeParentVHDPath+strlen (RelativeParentVHDPath), &FullParentVHDPath[RelativeMatch+1]); } for (i=0; i < strlen(RelativeParentVHDPath); i++) RelativeParentVHDPathUnicode[i*2] = RelativeParentVHDPath[i]; hVHD->Dynamic.ParentTimeStamp = ParentTimeStamp; memcpy (hVHD->Dynamic.ParentUniqueID, ParentFooter.UniqueID, sizeof (hVHD->Dynamic.ParentUniqueID)); /* There are two potential parent locators on current vhds */ memcpy (hVHD->Dynamic.ParentLocatorEntries[0].PlatformCode, "W2ku", 4); hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataSpace = NtoHl (BytesPerSector); hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataLength = NtoHl ((uint32)(2*strlen(FullParentVHDPath))); hVHD->Dynamic.ParentLocatorEntries[0].Reserved = 0; hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); ++LocatorsWritten; if (RelativeMatch) { memcpy (hVHD->Dynamic.ParentLocatorEntries[1].PlatformCode, "W2ru", 4); hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataSpace = NtoHl (BytesPerSector); hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataLength = NtoHl ((uint32)(2*strlen(RelativeParentVHDPath))); hVHD->Dynamic.ParentLocatorEntries[1].Reserved = 0; hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataOffset = NtoHll (LocatorPosition+LocatorsWritten*BytesPerSector); ++LocatorsWritten; } hVHD->Dynamic.TableOffset = NtoHll (((LocatorPosition+LocatorsWritten*BytesPerSector + VHD_DATA_BLOCK_ALIGNMENT - 1)/VHD_DATA_BLOCK_ALIGNMENT)*VHD_DATA_BLOCK_ALIGNMENT); hVHD->Dynamic.Checksum = 0; hVHD->Dynamic.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Dynamic, sizeof(hVHD->Dynamic))); hVHD->Footer.Checksum = 0; hVHD->Footer.DiskType = NtoHl (VHD_DT_Differencing); memcpy (hVHD->Footer.DriveType, ParentFooter.DriveType, sizeof (hVHD->Footer.DriveType)); hVHD->Footer.Checksum = NtoHl (CalculateVhdFooterChecksum (&hVHD->Footer, sizeof(hVHD->Footer))); if (WriteFilePosition (hVHD->File, &hVHD->Footer, sizeof (hVHD->Footer), NULL, 0)) { Status = errno; goto Cleanup_Return; } if (WriteFilePosition (hVHD->File, &hVHD->Dynamic, sizeof (hVHD->Dynamic), NULL, NtoHll (hVHD->Footer.DataOffset))) { Status = errno; goto Cleanup_Return; } if (WriteFilePosition (hVHD->File, hVHD->BAT, BytesPerSector*((NtoHl (hVHD->Dynamic.MaxTableEntries)*sizeof(*hVHD->BAT)+BytesPerSector-1)/BytesPerSector), NULL, NtoHll (hVHD->Dynamic.TableOffset))) { Status = errno; goto Cleanup_Return; } if (WriteFilePosition (hVHD->File, &hVHD->Footer, sizeof (hVHD->Footer), NULL, NtoHll (hVHD->Dynamic.TableOffset)+BytesPerSector*((NtoHl (hVHD->Dynamic.MaxTableEntries)*sizeof(*hVHD->BAT)+BytesPerSector-1)/BytesPerSector))) { Status = errno; goto Cleanup_Return; } if (hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataLength) if (WriteFilePosition (hVHD->File, FullParentVHDPathUnicode, BytesPerSector, NULL, NtoHll (hVHD->Dynamic.ParentLocatorEntries[0].PlatformDataOffset))) { Status = errno; goto Cleanup_Return; } if (hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataLength) if (WriteFilePosition (hVHD->File, RelativeParentVHDPathUnicode, BytesPerSector, NULL, NtoHll (hVHD->Dynamic.ParentLocatorEntries[1].PlatformDataOffset))) { Status = errno; goto Cleanup_Return; } Cleanup_Return: free (RelativeParentVHDPath); free (FullParentVHDPath); free (RelativeParentVHDPathUnicode); free (FullParentVHDPathUnicode); free (FullVHDPath); free (TempPath); sim_vhd_disk_close ((FILE *)hVHD); hVHD = NULL; if (Status) { if ((EEXIST != Status) && (ENOENT != Status)) remove (szVHDPath); } else { hVHD = (VHDHANDLE)sim_vhd_disk_open (szVHDPath, "rb+"); if (!hVHD) Status = errno; } errno = Status; return hVHD; } static FILE *sim_vhd_disk_create (const char *szVHDPath, t_offset desiredsize) { return (FILE *)CreateVirtualDisk (szVHDPath, (uint32)(desiredsize/512), 0, (sim_switches & SWMASK ('X'))); } static FILE *sim_vhd_disk_create_diff (const char *szVHDPath, const char *szParentVHDPath) { return (FILE *)CreateDifferencingVirtualDisk (szVHDPath, szParentVHDPath); } static t_stat ReadVirtualDiskSectors(VHDHANDLE hVHD, uint8 *buf, t_seccnt sects, t_seccnt *sectsread, uint32 SectorSize, t_lba lba) { uint64 BlockOffset = ((uint64)lba)*SectorSize; uint32 BlocksRead = 0; uint32 SectorsInRead; size_t BytesRead = 0; if (!hVHD || (hVHD->File == NULL)) { errno = EBADF; return SCPE_IOERR; } if ((BlockOffset + sects*SectorSize) > (uint64)NtoHll (hVHD->Footer.CurrentSize)) { errno = ERANGE; return SCPE_IOERR; } if (NtoHl (hVHD->Footer.DiskType) == VHD_DT_Fixed) { if (ReadFilePosition(hVHD->File, buf, sects*SectorSize, &BytesRead, BlockOffset)) { if (sectsread) *sectsread = (t_seccnt)(BytesRead/SectorSize); return SCPE_IOERR; } if (sectsread) *sectsread /= SectorSize; return SCPE_OK; } /* We are now dealing with a Dynamically expanding or differencing disk */ while (sects) { uint32 SectorsPerBlock = NtoHl (hVHD->Dynamic.BlockSize)/SectorSize; uint64 BlockNumber = lba/SectorsPerBlock; uint32 BitMapBytes = (7+(NtoHl (hVHD->Dynamic.BlockSize)/SectorSize))/8; uint32 BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize; SectorsInRead = SectorsPerBlock - lba%SectorsPerBlock; if (SectorsInRead > sects) SectorsInRead = sects; if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) { if (!hVHD->Parent) memset (buf, 0, SectorSize*SectorsInRead); else { if (ReadVirtualDiskSectors(hVHD->Parent, buf, SectorsInRead, NULL, SectorSize, lba)) { if (sectsread) *sectsread = BlocksRead; return FALSE; } } } else { BlockOffset = SectorSize*((uint64)(NtoHl (hVHD->BAT[BlockNumber]) + lba%SectorsPerBlock + BitMapSectors)); if (ReadFilePosition(hVHD->File, buf, SectorsInRead*SectorSize, NULL, BlockOffset)) { if (sectsread) *sectsread = BlocksRead; return SCPE_IOERR; } } sects -= SectorsInRead; buf = (uint8 *)(((char *)buf) + SectorSize*SectorsInRead); lba += SectorsInRead; BlocksRead += SectorsInRead; } if (sectsread) *sectsread = BlocksRead; return SCPE_OK; } static t_stat sim_vhd_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects) { VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; return ReadVirtualDiskSectors(hVHD, buf, sects, sectsread, ctx->sector_size, lba); } static t_stat sim_vhd_disk_clearerr (UNIT *uptr) { VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref; clearerr (hVHD->File); return SCPE_OK; } static t_bool BufferIsZeros(void *Buffer, size_t BufferSize) { size_t i; char *c = (char *)Buffer; for (i=0; i<BufferSize; ++i) if (c[i]) return FALSE; return TRUE; } static t_stat WriteVirtualDiskSectors(VHDHANDLE hVHD, uint8 *buf, t_seccnt sects, t_seccnt *sectswritten, uint32 SectorSize, t_lba lba) { uint64 BlockOffset = ((uint64)lba)*SectorSize; uint32 BlocksWritten = 0; uint32 SectorsInWrite; size_t BytesWritten = 0; if (!hVHD || !hVHD->File) { errno = EBADF; return SCPE_IOERR; } if ((BlockOffset + sects*SectorSize) > (uint64)NtoHll(hVHD->Footer.CurrentSize)) { errno = ERANGE; return SCPE_IOERR; } if (NtoHl(hVHD->Footer.DiskType) == VHD_DT_Fixed) { if (WriteFilePosition(hVHD->File, buf, sects*SectorSize, &BytesWritten, BlockOffset)) { if (sectswritten) *sectswritten = (t_seccnt)(BytesWritten/SectorSize); return SCPE_IOERR; } if (sectswritten) *sectswritten /= SectorSize; return SCPE_OK; } /* We are now dealing with a Dynamically expanding or differencing disk */ while (sects) { uint32 SectorsPerBlock = NtoHl(hVHD->Dynamic.BlockSize)/SectorSize; uint64 BlockNumber = lba/SectorsPerBlock; uint32 BitMapBytes = (7+(NtoHl(hVHD->Dynamic.BlockSize)/SectorSize))/8; uint32 BitMapSectors = (BitMapBytes+SectorSize-1)/SectorSize; if (BlockNumber >= NtoHl(hVHD->Dynamic.MaxTableEntries)) { if (sectswritten) *sectswritten = BlocksWritten; return SCPE_EOF; } SectorsInWrite = 1; if (hVHD->BAT[BlockNumber] == VHD_BAT_FREE_ENTRY) { uint8 *BitMap = NULL; uint32 BitMapBufferSize = VHD_DATA_BLOCK_ALIGNMENT; uint8 *BitMapBuffer = NULL; void *BlockData = NULL; uint8 *BATUpdateBufferAddress; uint32 BATUpdateBufferSize; uint64 BATUpdateStorageAddress; if (!hVHD->Parent && BufferIsZeros(buf, SectorSize)) goto IO_Done; /* Need to allocate a new Data Block. */ BlockOffset = sim_fsize_ex (hVHD->File); if (((int64)BlockOffset) == -1) return SCPE_IOERR; if (BitMapSectors*SectorSize > BitMapBufferSize) BitMapBufferSize = BitMapSectors*SectorSize; BitMapBuffer = (uint8 *)calloc(1, BitMapBufferSize + SectorSize*SectorsPerBlock); if (BitMapBufferSize > BitMapSectors*SectorSize) BitMap = BitMapBuffer + BitMapBufferSize-BitMapBytes; else BitMap = BitMapBuffer; memset(BitMap, 0xFF, BitMapBytes); BlockOffset -= sizeof(hVHD->Footer); if (0 == (BlockOffset & ~(VHD_DATA_BLOCK_ALIGNMENT-1))) { // Already aligned, so use padded BitMapBuffer if (WriteFilePosition(hVHD->File, BitMapBuffer, BitMapBufferSize + SectorSize*SectorsPerBlock, NULL, BlockOffset)) { free (BitMapBuffer); return SCPE_IOERR; } BlockOffset += BitMapBufferSize; } else { // align the data portion of the block to the desired alignment // compute the address of the data portion of the block BlockOffset += BitMapSectors*SectorSize; // round up this address to the desired alignment BlockOffset += VHD_DATA_BLOCK_ALIGNMENT-1; BlockOffset &= ~(VHD_DATA_BLOCK_ALIGNMENT-1); BlockOffset -= BitMapSectors*SectorSize; if (WriteFilePosition(hVHD->File, BitMap, SectorSize * (BitMapSectors + SectorsPerBlock), NULL, BlockOffset)) { free (BitMapBuffer); return SCPE_IOERR; } BlockOffset += BitMapSectors*SectorSize; } free(BitMapBuffer); BitMapBuffer = BitMap = NULL; /* the BAT block address is the beginning of the block bitmap */ BlockOffset -= BitMapSectors*SectorSize; hVHD->BAT[BlockNumber] = NtoHl((uint32)(BlockOffset/SectorSize)); BlockOffset += SectorSize * (SectorsPerBlock + BitMapSectors); if (WriteFilePosition(hVHD->File, &hVHD->Footer, sizeof(hVHD->Footer), NULL, BlockOffset)) goto Fatal_IO_Error; /* Since a large VHD can have a pretty large BAT, and we've only changed one longword bat entry in the current BAT, we write just the aligned sector which contains the updated BAT entry */ BATUpdateBufferAddress = (uint8 *)hVHD->BAT - (size_t)NtoHll(hVHD->Dynamic.TableOffset) + (size_t)((((size_t)&hVHD->BAT[BlockNumber]) - (size_t)hVHD->BAT + (size_t)NtoHll(hVHD->Dynamic.TableOffset)) & ~(VHD_DATA_BLOCK_ALIGNMENT-1)); /* If the starting of the BAT isn't on a VHD_DATA_BLOCK_ALIGNMENT boundary and we've just updated a BAT entry early in the array, the buffer computed address might be before the start of the BAT table. If so, only write the BAT data needed */ if (BATUpdateBufferAddress < (uint8 *)hVHD->BAT) { BATUpdateBufferAddress = (uint8 *)hVHD->BAT; BATUpdateBufferSize = (uint32)((((size_t)&hVHD->BAT[BlockNumber]) - (size_t)hVHD->BAT) + 512) & ~511; BATUpdateStorageAddress = NtoHll(hVHD->Dynamic.TableOffset); } else { BATUpdateBufferSize = VHD_DATA_BLOCK_ALIGNMENT; BATUpdateStorageAddress = NtoHll(hVHD->Dynamic.TableOffset) + BATUpdateBufferAddress - ((uint8 *)hVHD->BAT); } /* If the total BAT is smaller than one VHD_DATA_BLOCK_ALIGNMENT, then be sure to only write out the BAT data */ if ((size_t)(BATUpdateBufferAddress - (uint8 *)hVHD->BAT + BATUpdateBufferSize) > 512*((sizeof(*hVHD->BAT)*NtoHl(hVHD->Dynamic.MaxTableEntries) + 511)/512)) BATUpdateBufferSize = (uint32)(512*((sizeof(*hVHD->BAT)*NtoHl(hVHD->Dynamic.MaxTableEntries) + 511)/512) - (BATUpdateBufferAddress - ((uint8 *)hVHD->BAT))); if (WriteFilePosition(hVHD->File, BATUpdateBufferAddress, BATUpdateBufferSize, NULL, BATUpdateStorageAddress)) goto Fatal_IO_Error; if (hVHD->Parent) { /* Need to populate data block contents from parent VHD */ uint32 BlockSectors = SectorsPerBlock; BlockData = malloc(SectorsPerBlock*SectorSize); if (((lba/SectorsPerBlock)*SectorsPerBlock + BlockSectors) > ((uint64)NtoHll (hVHD->Footer.CurrentSize))/SectorSize) BlockSectors = (uint32)(((uint64)NtoHll (hVHD->Footer.CurrentSize))/SectorSize - (lba/SectorsPerBlock)*SectorsPerBlock); if (ReadVirtualDiskSectors(hVHD->Parent, (uint8*) BlockData, BlockSectors, NULL, SectorSize, (lba/SectorsPerBlock)*SectorsPerBlock)) goto Fatal_IO_Error; if (WriteVirtualDiskSectors(hVHD, (uint8*) BlockData, BlockSectors, NULL, SectorSize, (lba/SectorsPerBlock)*SectorsPerBlock)) goto Fatal_IO_Error; free(BlockData); } continue; Fatal_IO_Error: free (BitMap); free (BlockData); fclose (hVHD->File); hVHD->File = NULL; return SCPE_IOERR; } else { BlockOffset = 512*((uint64)(NtoHl(hVHD->BAT[BlockNumber]) + lba%SectorsPerBlock + BitMapSectors)); SectorsInWrite = SectorsPerBlock - lba%SectorsPerBlock; if (SectorsInWrite > sects) SectorsInWrite = sects; if (WriteFilePosition(hVHD->File, buf, SectorsInWrite*SectorSize, NULL, BlockOffset)) { if (sectswritten) *sectswritten = BlocksWritten; return SCPE_IOERR; } } IO_Done: sects -= SectorsInWrite; buf = (uint8 *)(((char *)buf) + SectorsInWrite*SectorSize); lba += SectorsInWrite; BlocksWritten += SectorsInWrite; } if (sectswritten) *sectswritten = BlocksWritten; return SCPE_OK; } static t_stat sim_vhd_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects) { VHDHANDLE hVHD = (VHDHANDLE)uptr->fileref; struct disk_context *ctx = (struct disk_context *)uptr->disk_ctx; return WriteVirtualDiskSectors(hVHD, buf, sects, sectswritten, ctx->sector_size, lba); } #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | /* sim_disk.h: simulator disk support library definitions Copyright (c) 2011, Mark Pizzolato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of Robert M Supnik and Mark Pizzolato shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik and Mark Pizzolato. 25-Jan-11 MP Initial Implemementation */ #ifndef SIM_DISK_H_ #define SIM_DISK_H_ 0 #ifdef __cplusplus extern "C" { #endif /* SIMH/Disk format */ typedef uint32 t_seccnt; /* disk sector count */ typedef uint32 t_lba; /* disk logical block address */ /* Unit flags */ #define DKUF_V_WLK (UNIT_V_UF + 0) /* write locked */ #define DKUF_V_FMT (UNIT_V_UF + 1) /* disk file format */ #define DKUF_W_FMT 2 /* 2b of formats */ #define DKUF_N_FMT (1u << DKUF_W_FMT) /* number of formats */ #define DKUF_M_FMT ((1u << DKUF_W_FMT) - 1) #define DKUF_F_STD 0 /* SIMH format */ #define DKUF_F_RAW 1 /* Raw Physical Disk Access */ #define DKUF_F_VHD 2 /* VHD format */ #define DKUF_V_UF (DKUF_V_FMT + DKUF_W_FMT) #define DKUF_WLK (1u << DKUF_V_WLK) #define DKUF_FMT (DKUF_M_FMT << DKUF_V_FMT) #define DKUF_WRP (DKUF_WLK | UNIT_RO) #define DK_F_STD (DKUF_F_STD << DKUF_V_FMT) #define DK_F_RAW (DKUF_F_RAW << DKUF_V_FMT) #define DK_F_VHD (DKUF_F_VHD << DKUF_V_FMT) #define DK_GET_FMT(u) (((u)->flags >> DKUF_V_FMT) & DKUF_M_FMT) /* Return status codes */ #define DKSE_OK 0 /* no error */ typedef void (*DISK_PCALLBACK)(UNIT *unit, t_stat status); /* Prototypes */ t_stat sim_disk_attach (UNIT *uptr, const char *cptr, size_t sector_size, size_t xfer_element_size, t_bool dontautosize, uint32 debugbit, const char *drivetype, uint32 pdp11_tracksize, int completion_delay); t_stat sim_disk_detach (UNIT *uptr); t_stat sim_disk_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); t_stat sim_disk_rdsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects); t_stat sim_disk_rdsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectsread, t_seccnt sects, DISK_PCALLBACK callback); t_stat sim_disk_wrsect (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects); t_stat sim_disk_wrsect_a (UNIT *uptr, t_lba lba, uint8 *buf, t_seccnt *sectswritten, t_seccnt sects, DISK_PCALLBACK callback); t_stat sim_disk_unload (UNIT *uptr); t_stat sim_disk_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat sim_disk_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat sim_disk_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat sim_disk_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat sim_disk_set_asynch (UNIT *uptr, int latency); t_stat sim_disk_clr_asynch (UNIT *uptr); t_stat sim_disk_reset (UNIT *uptr); t_stat sim_disk_perror (UNIT *uptr, const char *msg); t_stat sim_disk_clearerr (UNIT *uptr); t_bool sim_disk_isavailable (UNIT *uptr); t_bool sim_disk_isavailable_a (UNIT *uptr, DISK_PCALLBACK callback); t_bool sim_disk_wrp (UNIT *uptr); t_offset sim_disk_size (UNIT *uptr); t_bool sim_disk_vhd_support (void); t_bool sim_disk_raw_support (void); void sim_disk_data_trace (UNIT *uptr, const uint8 *data, size_t lba, size_t len, const char* txt, int detail, uint32 reason); #ifdef __cplusplus } #endif #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 | /* sim_ether.c: OS-dependent network routines ------------------------------------------------------------------------------ Copyright (c) 2002-2007, David T. Hittner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the author shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. ------------------------------------------------------------------------------ This ethernet simulation is based on the PCAP and WinPcap packages. PCAP/WinPcap was chosen as the basis for network code since it is the most "universal" of the various network packages available. Using this style has allowed rapid network development for the major SIMH platforms. Developing a network package specifically for SIMH was rejected due to the time required; the advantage would be a more easily compiled and integrated code set. There are various problems associated with use of ethernet networking, which would be true regardless of the network package used, since there are no universally accepted networking methods. The most serious of these is getting the proper networking package loaded onto the system, since most environments do not come with the network interface packages loaded. The second most serious network issue relates to security. The network simulation needs to simulate operating system level functionality (packet driving). However, the host network programming interfaces tend to operate at the user level of functionality, so getting to the full functionality of the network interface usually requires that the person executing the network code be a privileged user of the host system. See the PCAP/WinPcap documentation for the appropriate host platform if unprivileged use of networking is needed - there may be known workarounds. Define one of the two macros below to enable networking: USE_NETWORK - Create statically linked network code USE_SHARED - Create dynamically linked network code ------------------------------------------------------------------------------ Supported/Tested Platforms: Windows(NT,2K,XP,2K3,Vista,Win7) WinPcap V3.0+ Linux libpcap at least 0.9 OpenBSD,FreeBSD,NetBSD libpcap at least 0.9 MAC OS/X libpcap at least 0.9 Solaris Sparc libpcap at least 0.9 Solaris Intel libpcap at least 0.9 AIX ?? HP/UX ?? Compaq Tru64 Unix ?? VMS Alpha/Itanium VMS only, needs VMS libpcap WinPcap is available from: http://winpcap.polito.it/ libpcap for VMS is available from: http://simh.trailing-edge.com/sources/vms-pcap.zip libpcap for other Unix platforms is available at: NOTE: As of the release of this version of sim_ether.c ALL current *nix platforms ship with a sufficiently new version of libpcap, and ALL provide a libpcap-dev package for developing libpcap based applications. The OS vendor supplied version of libpcap AND the libpcap-dev components are preferred for proper operation of both simh AND other applications on the host system which use libpcap. Current Version: http://www.tcpdump.org/daily/libpcap-current.tar.gz Released Version: http://www.tcpdump.org/release/ When necessary (see NOTE above about vendor supplied libpcap), we've gotten the tarball, unpacked, built and installed it with: gzip -dc libpcap-current.tar.gz | tar xvf - cd libpcap-directory-name ./configure make make install Note: The "make install" step generally will have to be done as root. This will install libpcap in /usr/local/lib and /usr/local/include The current simh makefile will do the right thing to locate and reference the OS provided libpcap or the one just installed. Note: Building for the platforms indicated above, with the indicated libpcap, should automatically leverage the appropriate mechanisms contained here. Things are structured so that it is likely to work for any other as yet untested platform. If it works for you, please let the author know so we can update the table above. If it doesn't work, then the following #define variables can influence the operation on an untested platform. USE_BPF - Determines if this code leverages a libpcap/WinPcap provided bpf packet filtering facility. All tested environments have bpf facilities that work the way we need them to. However a new one might not. undefine this variable to let this code do its own filtering. USE_SETNONBLOCK - Specifies whether the libpcap environment's non-blocking semantics are to be leveraged. This helps to manage the varying behaviours of the kernel packet facilities leveraged by libpcap. USE_READER_THREAD - Specifies that packet reading should be done in the context of a separate thread. The Posix threading APIs are used. This option is less efficient than the default non-threaded approach, but it exists since some platforms don't want to work with nonblocking libpcap semantics. OpenBSD and NetBSD either don't have pthread APIs available, or they are too buggy to be useful. Using the threaded approach may require special compile and/or link time switches (i.e. -lpthread or -pthread, etc.) Consult the documentation for your platform as needed. Although this may be 'less efficient' than the non-threaded approach, the efficiency is an overall system efficiency not necessarily a simulator efficiency. This means that work is removed from the thread executing simulated instructions so the simulated system will most likely run faster (given that modern host CPUs are multi-core and have someplace to do this work in parallel). MUST_DO_SELECT - Specifies that, when USE_READER_THREAD is active, select() should be used to determine when available packets are ready for reading. Otherwise, we depend on the libpcap/kernel packet timeout specified on pcap_open_live. If USE_READER_THREAD is not set, then MUST_DO_SELECT is irrelevant HAVE_TAP_NETWORK - Specifies that support for tap networking should be included. This can be leveraged, along with OS bridging capabilities to share a single LAN interface. This allows device names of the form tap:tap0 to be specified at open time. This functionality is only useful/needed on *nix platforms since native sharing of Windows NIC devices works with no external magic. HAVE_VDE_NETWORK - Specifies that support for vde networking should be included. This can be leveraged, along with OS bridging capabilities to share a single LAN interface. It also can allow a simulator to have useful networking functionality when running without root access. This allows device names of the form vde:/tmp/switch to be specified at open time. This functionality is only available on *nix platforms since the vde api isn't available on Windows. HAVE_SLIRP_NETWORK- Specifies that support for SLiRP networking should be included. This can be leveraged to provide User Mode IP NAT connectivity for simulators. NEED_PCAP_SENDPACKET - Specifies that you are using an older version of libpcap which doesn't provide a pcap_sendpacket API. NOTE: Changing these defines is done in either sim_ether.h OR on the global compiler command line which builds all of the modules included in a simulator. ------------------------------------------------------------------------------ Modification history: 30-Mar-12 MP Added host NIC address determination on supported VMS platforms 01-Mar-12 MP Made host NIC address determination on *nix platforms more robust. 01-Mar-12 MP Added host NIC address determination work when building under Cygwin 01-Mar-12 AGN Add conditionals for Cygwin dynamic loading of wpcap.dll 01-Mar-12 AGN Specify the full /usr/lib for dlopen under Apple Mac OS X. 17-Nov-11 MP Added dynamic loading of libpcap on *nix platforms 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking 29-Oct-11 MP Added support for integrated Tap networking interfaces on OSX 12-Aug-11 MP Cleaned up payload length determination Fixed race condition detecting reflections when threaded reading and writing is enabled 18-Apr-11 MP Fixed race condition with self loopback packets in multithreaded environments 09-Jan-11 MP Fixed missing crc data when USE_READER_THREAD is defined and crc's are needed (only the pdp11_xu) 16-Dec-10 MP added priority boost for read and write threads when USE_READER_THREAD does I/O in separate threads. This helps throughput since it allows these I/O bound threads to preempt the main thread (which is executing simulated instructions). 09-Dec-10 MP allowed more flexible parsing of MAC address strings 09-Dec-10 MP Added support to determine if network address conflicts exist 07-Dec-10 MP Reworked DECnet self detection to the more general approach of loopback self when a Physical Address is being set. 04-Dec-10 MP Changed eth_write to do nonblocking writes when USE_READER_THREAD is defined. 20-Aug-10 TVO Fix for Mac OSX 10.6 17-Jun-10 MP Fixed bug in the AUTODIN II hash filtering. 14-Jun-10 MP Added support for integrated Tap networking interfaces on BSD platforms. 13-Jun-10 MP Added support for integrated Tap networking interfaces on Linux platforms. 31-May-10 MP Added support for more TOE (TCP Offload Engine) features for IPv4 network traffic from the host and/or from hosts on the LAN. These new TOE features are: LSO (Large Send Offload) and Jumbo packet fragmentation support. These features allow a simulated network device to support traffic when a host leverages a NIC's Large Send Offload capabilities to fregment and/or segment outgoing network traffic. Additionally a simulated network device can reasonably exist on a LAN which is configured to use Jumbo frames. 21-May-10 MP Added functionality to fixup IP header checksums to accomodate packets from a host with a NIC which has TOE (TCP Offload Engine) enabled which is expected to implement the checksum computations in hardware. Since we catch packets before they arrive at the NIC the expected checksum insertions haven't been performed yet. This processing is only done for packets sent from the hoat to the guest we're supporting. In general this will be a relatively small number of packets so it is done for all IP frame packets coming from the hoat to the guest. In order to make the determination of packets specifically arriving from the host we need to know the hardware MAC address of the host NIC. Currently determining a NIC's MAC address is relatively easy on Windows. The non-windows code works on linux and may work on other *nix platforms either as is or with slight modifications. The code, as implemented, only messes with this activity if the host interface MAC address can be determined. 20-May-10 MP Added general support to deal with receiving packets smaller than ETH_MIN_PACKET in length. These come from packets looped back by some bridging mechanism and need to be padded to the minimum frame size. A real NIC won't pass us any packets like that. This fix belongs here since this layer is responsible for interfacing to they physical layer devices, AND it belongs here to get CRC processing right. 05-Mar-08 MP Added optional multicast filtering support for doing LANCE style AUTODIN II based hashed filtering. 07-Feb-08 MP Added eth_show_dev to display ethernet state Changed the return value from eth_read to return whether or not a packet was read. No existing callers used or checked constant return value that previously was being supplied. 29-Jan-08 MP Added eth_set_async to provide a mechanism (when USE_READER_THREAD is enabled) to allow packet reception to dynamically update the simulator event queue and potentially avoid polling for I/O. This provides a minimal overhead (no polling) maximal responsiveness for network activities. 29-Jan-08 MP Properly sequenced activities in eth_close to avoid a race condition when USE_READER_THREAD is enabled. 25-Jan-08 MP Changed the following when USE_READER_THREAD is enabled: - Fixed bug when the simulated device doesn't need crc in packet data which is read. - Added call to pcap_setmintocopy to minimize packet delivery latencies. - Added ethq_destroy and used it to avoid a memory leak in eth_close. - Properly cleaned up pthread mutexes in eth_close. Migrated to using sim_os_ms_sleep for a delay instead of a call to select(). Fixed the bpf filter used when no traffic is to be matched. Reworked eth_add_packet_crc32 implementation to avoid an extra buffer copy while reading packets. Fixedup #ifdef's relating to USE_SHARED so that setting USE_SHARED or USE_NETWORK will build a working network environment. 23-Jan-08 MP Reworked eth_packet_trace and eth_packet_trace_ex to allow only output ethernet header+crc and provide a mechanism for the simulated device to display full packet data debugging. 17-May-07 DTH Fixed non-ethernet device removal loop (from Naoki Hamada) 15-May-07 DTH Added dynamic loading of wpcap.dll; Corrected exceed max index bug in ethX lookup 04-May-07 DTH Corrected failure to look up ethernet device names in the registry on Windows XP x64 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) 02-Jun-06 JDB Fixed compiler warning for incompatible sscanf parameter 15-Dec-05 DTH Patched eth_host_devices [remove non-ethernet devices] (from Mark Pizzolato and Galen Tackett, 08-Jun-05) Patched eth_open [tun fix](from Antal Ritter, 06-Oct-05) 30-Nov-05 DTH Added option to regenerate CRC on received packets; some ethernet devices need to pass it on to the simulation, and by the time libpcap/winpcap gets the packet, the host OS network layer has already stripped CRC out of the packet 01-Dec-04 DTH Added Windows user-defined adapter names (from Timothe Litt) 25-Mar-04 MP Revised comments and minor #defines to deal with updated libpcap which now provides pcap_sendpacket on all platforms. 04-Feb-04 MP Returned success/fail status from eth_write to support determining if the current libpcap connection can successfully write packets. Added threaded approach to reading packets since this works better on some platforms (solaris intel) than the inconsistently implemented non-blocking read approach. 04-Feb-04 DTH Converted ETH_DEBUG to sim_debug 13-Jan-04 MP tested and fixed on OpenBSD, NetBS and FreeBSD. 09-Jan-04 MP removed the BIOCSHDRCMPLT ioctl() for OS/X 05-Jan-04 DTH Added eth_mac_scan 30-Dec-03 DTH Cleaned up queue routines, added no network support message 26-Dec-03 DTH Added ethernet show and queue functions from pdp11_xq 15-Dec-03 MP polished generic libpcap support. 05-Dec-03 DTH Genericized eth_devices() and #ifdefs 03-Dec-03 MP Added Solaris support 02-Dec-03 DTH Corrected decnet fix to use reflection counting 01-Dec-03 DTH Added BPF source filtering and reflection counting 28-Nov-03 DTH Rewrote eth_devices using universal pcap_findalldevs() 25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code 19-Nov-03 MP Fixed BPF functionality on Linux/BSD. 17-Nov-03 DTH Added xBSD simplification 14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code 13-Nov-03 DTH Merged in __FreeBSD__ support 21-Oct-03 MP Added enriched packet dumping for debugging 20-Oct-03 MP Added support for multiple ethernet devices on VMS 20-Sep-03 Ankan Add VMS support (Alpha only) 29-Sep-03 MP Changed separator character in eth_fmt_mac to be ":" to format ethernet addresses the way the BPF compile engine wants to see them. Added BPF support to filter packets Added missing printf in eth_close 07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection. 06-Jun-03 MP Fixed formatting of Ethernet Protocol Type in eth_packet_trace 30-May-03 DTH Changed WIN32 to _WIN32 for consistency 07-Mar-03 MP Fixed Linux implementation of PacketGetAdapterNames to also work on Red Hat 6.2-sparc and Debian 3.0r1-sparc. 03-Mar-03 MP Changed logging to be consistent on stdout and sim_log 01-Feb-03 MP Changed type of local variables in eth_packet_trace to conform to the interface needs of eth_mac_fmt wich produces char data instead of unsigned char data. Suggested by the DECC compiler. 15-Jan-03 DTH Corrected PacketGetAdapterNames parameter2 datatype 26-Dec-02 DTH Merged Mark Pizzolato's enhancements with main source Added networking documentation Changed _DEBUG to ETH_DEBUG 20-Dec-02 MP Added display of packet CRC to the eth_packet_trace. This helps distinguish packets with identical lengths and protocols. 05-Dec-02 MP With the goal of draining the input buffer more rapidly changed eth_read to call pcap_dispatch repeatedly until either a timeout returns nothing or a packet allowed by the filter is seen. This more closely reflects how the pcap layer will work when the filtering is actually done by a bpf filter. 31-Oct-02 DTH Added USE_NETWORK conditional Reworked not attached test Added OpenBSD support (from Federico Schwindt) Added ethX detection simplification (from Megan Gentry) Removed sections of temporary code Added parameter validation 23-Oct-02 DTH Beta 5 released 22-Oct-02 DTH Added all_multicast and promiscuous support Fixed not attached behavior 21-Oct-02 DTH Added NetBSD support (from Jason Thorpe) Patched buffer size to make sure entire packet is read in Made 'ethX' check characters passed as well as length Corrected copyright again 16-Oct-02 DTH Beta 4 released Corrected copyright 09-Oct-02 DTH Beta 3 released Added pdp11 write acceleration (from Patrick Caulfield) 08-Oct-02 DTH Beta 2 released Integrated with 2.10-0p4 Added variable vector and copyrights 04-Oct-02 DTH Added linux support (from Patrick Caulfield) 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 24-Sep-02 DTH Finished eth_devices, eth_getname 18-Sep-02 DTH Callbacks implemented 13-Sep-02 DTH Basic packet read/write written 20-Aug-02 DTH Created Sim_Ether for O/S independant ethernet implementation ------------------------------------------------------------------------------ */ #include <ctype.h> #include "sim_ether.h" #include "sim_sock.h" #include "sim_timer.h" #if defined(_WIN32) #include <direct.h> #else #include <unistd.h> #endif /* Internal routines - forward declarations */ static int _eth_get_system_id (char *buf, size_t buf_size); /*============================================================================*/ /* OS-independant ethernet routines */ /*============================================================================*/ t_stat eth_mac_scan (ETH_MAC* mac, const char* strmac) { return eth_mac_scan_ex (mac, strmac, NULL); } t_stat eth_mac_scan_ex (ETH_MAC* mac, const char* strmac, UNIT *uptr) { unsigned int a[6], g[6]; FILE *f; char filebuf[64] = ""; uint32 i; static const ETH_MAC zeros = {0,0,0,0,0,0}; static const ETH_MAC ones = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; ETH_MAC newmac; struct { uint32 bits; char system_id[37]; char cwd[PATH_MAX]; char file[PATH_MAX]; ETH_MAC base_mac; char uname[64]; char sim[128]; } state; CONST char *cptr, *tptr; uint32 data; /* Allow generated MAC address */ /* XX:XX:XX:XX:XX:XX{/bits{>file}} */ /* bits (if specified) must be from 16 thru 48 */ memset (&state, 0, sizeof(state)); _eth_get_system_id (state.system_id, sizeof(state.system_id)); strncpy (state.sim, sim_name, sizeof(state.sim)); getcwd (state.cwd, sizeof(state.cwd)); if (uptr) strncpy (state.uname, sim_uname (uptr), sizeof(state.uname)); cptr = strchr (strmac, '>'); if (cptr) { strncpy (state.file, cptr + 1, sizeof(state.file)); if ((f = fopen (state.file, "r"))) { filebuf[sizeof(filebuf)-1] = '\0'; fgets (filebuf, sizeof(filebuf)-1, f); strmac = filebuf; fclose (f); strcpy (state.file, ""); /* avoid saving */ } } cptr = strchr (strmac, '/'); if (cptr) { state.bits = (uint32)strtotv (cptr + 1, &tptr, 10); if ((state.bits < 16) || (state.bits > 48)) return sim_messagef (SCPE_ARG, "Invalid MAC address bits specifier '%d'. Valid values are from 16 thru 48\n", state.bits); } else state.bits = 48; data = eth_crc32 (0, (void *)&state, sizeof(state)); for (i=g[0]=g[1]=0; i<4; i++) g[i+2] = (data >> (i << 3)) & 0xFF; if ((6 != sscanf(strmac, "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])) && (6 != sscanf(strmac, "%x.%x.%x.%x.%x.%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5])) && (6 != sscanf(strmac, "%x-%x-%x-%x-%x-%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]))) return sim_messagef (SCPE_ARG, "Invalid MAC address format: '%s'\n", strmac); for (i=0; i<6; i++) if (a[i] > 0xFF) return sim_messagef (SCPE_ARG, "Invalid MAC address byte value: %02X\n", a[i]); else { uint32 mask, shift; state.base_mac[i] = a[i]; if (((i + 1) << 3) < state.bits) shift = 0; else shift = ((i + 1) << 3) - state.bits; mask = 0xFF << shift; newmac[i] = (unsigned char)((a[i] & mask) | (g[i] & ~mask)); } /* final check - mac cannot be broadcast or multicast address */ if (!memcmp(newmac, zeros, sizeof(ETH_MAC)) || /* broadcast */ !memcmp(newmac, ones, sizeof(ETH_MAC)) || /* broadcast */ (newmac[0] & 0x01) /* multicast */ ) return sim_messagef (SCPE_ARG, "Can't use Broadcast or MultiCast address as interface MAC address\n"); /* new mac is OK */ /* optionally save */ if (state.file[0]) { /* Save File specified? */ f = fopen (state.file, "w"); if (f == NULL) return sim_messagef (SCPE_ARG, "Can't open MAC address configuration file '%s'.\n", state.file); eth_mac_fmt (&newmac, filebuf); fprintf (f, "%s/48\n", filebuf); fprintf (f, "system-id: %s\n", state.system_id); fprintf (f, "directory: %s\n", state.cwd); fprintf (f, "simulator: %s\n", state.sim); fprintf (f, "device: %s\n", state.uname); fprintf (f, "file: %s\n", state.file); eth_mac_fmt (&state.base_mac, filebuf); fprintf (f, "base-mac: %s\n", filebuf); fprintf (f, "specified: %d bits\n", state.bits); fprintf (f, "generated: %d bits\n", 48-state.bits); fclose (f); } /* copy into passed mac */ memcpy (*mac, newmac, sizeof(ETH_MAC)); return SCPE_OK; } void eth_mac_fmt(ETH_MAC* const mac, char* buff) { const uint8* m = (const uint8*) mac; sprintf(buff, "%02X:%02X:%02X:%02X:%02X:%02X", m[0], m[1], m[2], m[3], m[4], m[5]); return; } static const uint32 crcTable[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len) { const uint32 mask = 0xFFFFFFFF; const unsigned char* buf = (const unsigned char*)vbuf; crc ^= mask; while (len > 8) { crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; len -= 8; } while (0 != len--) crc = (crc >> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; return(crc ^ mask); } int eth_get_packet_crc32_data(const uint8 *msg, int len, uint8 *crcdata) { int crc_len; if (len <= ETH_MAX_PACKET) { uint32 crc = eth_crc32(0, msg, len); /* calculate CRC */ uint32 ncrc = htonl(crc); /* CRC in network order */ int size = sizeof(ncrc); /* size of crc field */ memcpy(crcdata, &ncrc, size); /* append crc to packet */ crc_len = len + size; /* set packet crc length */ } else { crc_len = 0; /* appending crc would destroy packet */ } return crc_len; } int eth_add_packet_crc32(uint8 *msg, int len) { int crc_len; if (len <= ETH_MAX_PACKET) { crc_len = eth_get_packet_crc32_data(msg, len, &msg[len]);/* append crc to packet */ } else { crc_len = 0; /* appending crc would destroy packet */ } return crc_len; } void eth_setcrc(ETH_DEV* dev, int need_crc) { dev->need_crc = need_crc; } void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, const char* txt, int detail, uint32 reason) { if (dev->dptr->dctrl & reason) { char src[20]; char dst[20]; const unsigned short* proto = (const unsigned short*) &msg[12]; uint32 crc = eth_crc32(0, msg, len); eth_mac_fmt((ETH_MAC*)msg, dst); eth_mac_fmt((ETH_MAC*)(msg+6), src); sim_debug(reason, dev->dptr, "%s dst: %s src: %s proto: 0x%04X len: %d crc: %X\n", txt, dst, src, ntohs(*proto), len, crc); if (detail) { int i, same, group, sidx, oidx; char outbuf[80], strbuf[18]; static const char hex[] = "0123456789ABCDEF"; for (i=same=0; i<len; i += 16) { if ((i > 0) && (0 == memcmp(&msg[i], &msg[i-16], 16))) { ++same; continue; } if (same > 0) { sim_debug(reason, dev->dptr, "%04X thru %04X same as above\n", i-(16*same), i-1); same = 0; } group = (((len - i) > 16) ? 16 : (len - i)); for (sidx=oidx=0; sidx<group; ++sidx) { outbuf[oidx++] = ' '; outbuf[oidx++] = hex[(msg[i+sidx]>>4)&0xf]; outbuf[oidx++] = hex[msg[i+sidx]&0xf]; if (isprint(msg[i+sidx])) strbuf[sidx] = msg[i+sidx]; else strbuf[sidx] = '.'; } outbuf[oidx] = '\0'; strbuf[sidx] = '\0'; sim_debug(reason, dev->dptr, "%04X%-48s %s\n", i, outbuf, strbuf); } if (same > 0) { sim_debug(reason, dev->dptr, "%04X thru %04X same as above\n", i-(16*same), len-1); } } } } void eth_packet_trace(ETH_DEV* dev, const uint8 *msg, int len, const char* txt) { eth_packet_trace_ex(dev, msg, len, txt, 0, dev->dbit); } void eth_packet_trace_detail(ETH_DEV* dev, const uint8 *msg, int len, const char* txt) { eth_packet_trace_ex(dev, msg, len, txt, 1 , dev->dbit); } const char* eth_getname(int number, char* name, char *desc) { ETH_LIST list[ETH_MAX_DEVICE]; int count = eth_devices(ETH_MAX_DEVICE, list); if ((number < 0) || (count <= number)) return NULL; if (list[number].eth_api != ETH_API_PCAP) { sim_printf ("Eth: Pcap capable device not found. You may need to run as root\n"); return NULL; } strcpy(name, list[number].name); strcpy(desc, list[number].desc); return name; } const char* eth_getname_bydesc(const char* desc, char* name, char *ndesc) { ETH_LIST list[ETH_MAX_DEVICE]; int count = eth_devices(ETH_MAX_DEVICE, list); int i; size_t j=strlen(desc); for (i=0; i<count; i++) { int found = 1; size_t k = strlen(list[i].desc); if (j != k) continue; for (k=0; k<j; k++) if (tolower(list[i].desc[k]) != tolower(desc[k])) found = 0; if (found == 0) continue; /* found a case-insensitive description match */ strcpy(name, list[i].name); strcpy(ndesc, list[i].desc); return name; } /* not found */ return NULL; } char* eth_getname_byname(const char* name, char* temp, char *desc) { ETH_LIST list[ETH_MAX_DEVICE]; int count = eth_devices(ETH_MAX_DEVICE, list); size_t n; int i, found; found = 0; n = strlen(name); for (i=0; i<count && !found; i++) { if ((n == strlen(list[i].name)) && (sim_strncasecmp(name, list[i].name, n) == 0)) { found = 1; strcpy(temp, list[i].name); /* only case might be different */ strcpy(desc, list[i].desc); } } return (found ? temp : NULL); } char* eth_getdesc_byname(char* name, char* temp) { ETH_LIST list[ETH_MAX_DEVICE]; int count = eth_devices(ETH_MAX_DEVICE, list); size_t n; int i, found; found = 0; n = strlen(name); for (i=0; i<count && !found; i++) { if ((n == strlen(list[i].name)) && (sim_strncasecmp(name, list[i].name, n) == 0)) { found = 1; strcpy(temp, list[i].desc); } } return (found ? temp : NULL); } void eth_zero(ETH_DEV* dev) { /* set all members to NULL OR 0 */ memset(dev, 0, sizeof(ETH_DEV)); dev->reflections = -1; /* not established yet */ } static char* (*p_pcap_lib_version) (void); static ETH_DEV **eth_open_devices = NULL; static int eth_open_device_count = 0; static t_bool eth_show_active = FALSE; #if defined (USE_NETWORK) || defined (USE_SHARED) static void _eth_add_to_open_list (ETH_DEV* dev) { eth_open_devices = (ETH_DEV**)realloc(eth_open_devices, (eth_open_device_count+1)*sizeof(*eth_open_devices)); eth_open_devices[eth_open_device_count++] = dev; } static void _eth_remove_from_open_list (ETH_DEV* dev) { int i, j; for (i=0; i<eth_open_device_count; ++i) if (eth_open_devices[i] == dev) { for (j=i+1; j<eth_open_device_count; ++j) eth_open_devices[j-1] = eth_open_devices[j]; --eth_open_device_count; break; } } #endif t_stat eth_show (FILE* st, UNIT* uptr, int32 val, CONST void* desc) { ETH_LIST list[ETH_MAX_DEVICE]; int number; eth_show_active = TRUE; number = eth_devices(ETH_MAX_DEVICE, list); fprintf(st, "ETH devices:\n"); if (number == -1) fprintf(st, " network support not available in simulator\n"); else if (number == 0) fprintf(st, " no network devices are available\n"); else { size_t min, len; int i; for (i=0, min=0; i<number; i++) if ((len = strlen(list[i].name)) > min) min = len; for (i=0; i<number; i++) fprintf(st," eth%d\t%-*s (%s)\n", i, (int)min, list[i].name, list[i].desc); } if (p_pcap_lib_version) { fprintf(st, "%s\n", p_pcap_lib_version()); } if (eth_open_device_count) { int i; char desc[ETH_DEV_DESC_MAX], *d; fprintf(st,"Open ETH Devices:\n"); for (i=0; i<eth_open_device_count; i++) { d = eth_getdesc_byname(eth_open_devices[i]->name, desc); if (d) fprintf(st, " %-7s%s (%s)\n", eth_open_devices[i]->dptr->name, eth_open_devices[i]->dptr->units[0].filename, d); else fprintf(st, " %-7s%s\n", eth_open_devices[i]->dptr->name, eth_open_devices[i]->dptr->units[0].filename); eth_show_dev (st, eth_open_devices[i]); } } eth_show_active = FALSE; return SCPE_OK; } t_stat eth_show_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char *desc) { return eth_show (st, uptr, val, NULL); } t_stat ethq_init(ETH_QUE* que, int max) { /* create dynamic queue if it does not exist */ if (!que->item) { que->item = (struct eth_item *) calloc(max, sizeof(struct eth_item)); if (!que->item) { /* failed to allocate memory */ sim_printf("EthQ: failed to allocate dynamic queue[%d]\r\n", max); return SCPE_MEM; }; que->max = max; }; ethq_clear(que); return SCPE_OK; } t_stat ethq_destroy(ETH_QUE* que) { /* release dynamic queue if it exists */ ethq_clear(que); que->max = 0; if (que->item) { free(que->item); que->item = NULL; }; return SCPE_OK; } void ethq_clear(ETH_QUE* que) { int i; /* free up any extended packets */ for (i=0; i<que->max; ++i) if (que->item[i].packet.oversize) { free (que->item[i].packet.oversize); que->item[i].packet.oversize = NULL; } /* clear packet array */ memset(que->item, 0, sizeof(struct eth_item) * que->max); /* clear rest of structure */ que->count = que->head = que->tail = 0; } void ethq_remove(ETH_QUE* que) { struct eth_item* item = &que->item[que->head]; if (que->count) { if (item->packet.oversize) free (item->packet.oversize); memset(item, 0, sizeof(struct eth_item)); if (++que->head == que->max) que->head = 0; que->count--; } } void ethq_insert_data(ETH_QUE* que, int32 type, const uint8 *data, int used, size_t len, size_t crc_len, const uint8 *crc_data, int32 status) { struct eth_item* item; /* if queue empty, set pointers to beginning */ if (!que->count) { que->head = 0; que->tail = -1; } /* find new tail of the circular queue */ if (++que->tail == que->max) que->tail = 0; if (++que->count > que->max) { que->count = que->max; /* lose oldest packet */ if (++que->head == que->max) que->head = 0; que->loss++; } if (que->count > que->high) que->high = que->count; /* set information in (new) tail item */ item = &que->item[que->tail]; item->type = type; item->packet.len = len; item->packet.used = used; item->packet.crc_len = crc_len; if (len <= sizeof (item->packet.msg)) { memcpy(item->packet.msg, data, ((len > crc_len) ? len : crc_len)); if (crc_data && (crc_len > len)) memcpy(&item->packet.msg[len], crc_data, ETH_CRC_SIZE); } else { item->packet.oversize = (uint8 *)realloc (item->packet.oversize, ((len > crc_len) ? len : crc_len)); memcpy(item->packet.oversize, data, ((len > crc_len) ? len : crc_len)); if (crc_data && (crc_len > len)) memcpy(&item->packet.oversize[len], crc_data, ETH_CRC_SIZE); } item->packet.status = status; } void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) { ethq_insert_data(que, type, pack->oversize ? pack->oversize : pack->msg, pack->used, pack->len, pack->crc_len, NULL, status); } /*============================================================================*/ /* Non-implemented versions */ /*============================================================================*/ #if !defined (USE_NETWORK) && !defined (USE_SHARED) const char *eth_capabilities(void) {return "no Ethernet";} t_stat eth_open(ETH_DEV* dev, const char* name, DEVICE* dptr, uint32 dbit) {return SCPE_NOFNC;} t_stat eth_close (ETH_DEV* dev) {return SCPE_NOFNC;} t_stat eth_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { fprintf (st, "%s attach help\n\n", dptr->name); fprintf (st, "This simulator was not built with ethernet device support\n"); return SCPE_OK; } t_stat eth_check_address_conflict (ETH_DEV* dev, ETH_MAC* const mac) {return SCPE_NOFNC;} t_stat eth_set_throttle (ETH_DEV* dev, uint32 time, uint32 burst, uint32 delay) {return SCPE_NOFNC;} t_stat eth_set_async (ETH_DEV *dev, int latency) {return SCPE_NOFNC;} t_stat eth_clr_async (ETH_DEV *dev) {return SCPE_NOFNC;} t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) {return SCPE_NOFNC;} int eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) {return SCPE_NOFNC;} t_stat eth_filter (ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous) {return SCPE_NOFNC;} t_stat eth_filter_hash (ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous, ETH_MULTIHASH* const hash) {return SCPE_NOFNC;} int eth_devices (int max, ETH_LIST* dev) {return -1;} void eth_show_dev (FILE* st, ETH_DEV* dev) {} static int _eth_get_system_id (char *buf, size_t buf_size) {memset (buf, 0, buf_size); return 0;} #else /* endif unimplemented */ const char *eth_capabilities(void) { return "Ethernet Packet transports" #if defined (HAVE_PCAP_NETWORK) ":PCAP" #endif #if defined (HAVE_TAP_NETWORK) ":TAP" #endif #if defined (HAVE_VDE_NETWORK) ":VDE" #endif #if defined (HAVE_SLIRP_NETWORK) ":NAT" #endif ":UDP"; } #if (defined (xBSD) || defined (__APPLE__)) && (defined (HAVE_TAP_NETWORK) || defined (HAVE_PCAP_NETWORK)) #include <sys/ioctl.h> #include <net/bpf.h> #endif #if defined (HAVE_PCAP_NETWORK) /*============================================================================*/ /* WIN32, Linux, and xBSD routines use WinPcap and libpcap packages */ /* OpenVMS Alpha uses a WinPcap port and an associated execlet */ /*============================================================================*/ #include <pcap.h> #include <string.h> #else struct pcap_pkthdr { uint32 caplen; /* length of portion present */ uint32 len; /* length this packet (off wire) */ }; #define PCAP_ERRBUF_SIZE 256 typedef void * pcap_t; /* Pseudo Type to avoid compiler errors */ #define DLT_EN10MB 1 /* Dummy Value to avoid compiler errors */ #endif /* HAVE_PCAP_NETWORK */ #ifdef HAVE_TAP_NETWORK #if defined(__linux) || defined(__linux__) #include <sys/ioctl.h> #include <net/if.h> #include <linux/if_tun.h> #elif defined(HAVE_BSDTUNTAP) #include <sys/types.h> #include <net/if_types.h> #include <net/if.h> #else /* We don't know how to do this on the current platform */ #undef HAVE_TAP_NETWORK #endif #endif /* HAVE_TAP_NETWORK */ #ifdef HAVE_VDE_NETWORK #ifdef __cplusplus extern "C" { #endif #include <libvdeplug.h> #ifdef __cplusplus } #endif #endif /* HAVE_VDE_NETWORK */ #ifdef HAVE_SLIRP_NETWORK #include "sim_slirp.h" #endif /* HAVE_SLIRP_NETWORK */ /* Allows windows to look up user-defined adapter names */ #if defined(_WIN32) #include <winreg.h> #endif #ifdef HAVE_DLOPEN #include <dlfcn.h> #endif #if defined(USE_SHARED) && (defined(_WIN32) || defined(HAVE_DLOPEN)) /* Dynamic DLL loading technique and modified source comes from Etherial/WireShark capture_pcap.c */ /* Dynamic DLL load variables */ #ifdef _WIN32 static HINSTANCE hLib = NULL; /* handle to DLL */ #else static void *hLib = 0; /* handle to Library */ #endif static int lib_loaded = 0; /* 0=not loaded, 1=loaded, 2=library load failed, 3=Func load failed */ static const char* lib_name = #if defined(_WIN32) || defined(__CYGWIN__) "wpcap.dll"; #elif defined(__APPLE__) "/usr/lib/libpcap.A.dylib"; #else #define __STR_QUOTE(tok) #tok #define __STR(tok) __STR_QUOTE(tok) "libpcap." __STR(HAVE_DLOPEN); #endif static const char* no_pcap = #if defined(_WIN32) || defined(__CYGWIN__) "wpcap load failure"; #else "libpcap load failure"; #endif /* define pointers to pcap functions needed */ static void (*p_pcap_close) (pcap_t *); static int (*p_pcap_compile) (pcap_t *, struct bpf_program *, const char *, int, bpf_u_int32); static int (*p_pcap_datalink) (pcap_t *); static int (*p_pcap_dispatch) (pcap_t *, int, pcap_handler, u_char *); static int (*p_pcap_findalldevs) (pcap_if_t **, char *); static void (*p_pcap_freealldevs) (pcap_if_t *); static void (*p_pcap_freecode) (struct bpf_program *); static char* (*p_pcap_geterr) (pcap_t *); static int (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, char *); static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *); #ifdef _WIN32 static int (*p_pcap_setmintocopy) (pcap_t* handle, int); static HANDLE (*p_pcap_getevent) (pcap_t *); #else #ifdef MUST_DO_SELECT static int (*p_pcap_get_selectable_fd) (pcap_t *); #endif static int (*p_pcap_fileno) (pcap_t *); #endif static int (*p_pcap_sendpacket) (pcap_t* handle, const u_char* msg, int len); static int (*p_pcap_setfilter) (pcap_t *, struct bpf_program *); static int (*p_pcap_setnonblock)(pcap_t* a, int nonblock, char *errbuf); /* load function pointer from DLL */ typedef int (*_func)(); static void load_function(const char* function, _func* func_ptr) { #ifdef _WIN32 *func_ptr = (_func)((size_t)GetProcAddress(hLib, function)); #else *func_ptr = (_func)((size_t)dlsym(hLib, function)); #endif if (*func_ptr == 0) { sim_printf ("Eth: Failed to find function '%s' in %s\r\n", function, lib_name); lib_loaded = 3; } } static void try_load_function(const char* function, _func* func_ptr) { #ifdef _WIN32 *func_ptr = (_func)((size_t)GetProcAddress(hLib, function)); #else *func_ptr = (_func)((size_t)dlsym(hLib, function)); #endif } /* load wpcap.dll as required */ int load_pcap(void) { switch(lib_loaded) { case 0: /* not loaded */ /* attempt to load DLL */ #ifdef _WIN32 if (1) { BOOL(WINAPI *p_SetDllDirectory)(LPCTSTR); UINT(WINAPI *p_GetSystemDirectory)(LPTSTR lpBuffer, UINT uSize); p_SetDllDirectory = (BOOL(WINAPI *)(LPCTSTR)) GetProcAddress(GetModuleHandle("kernel32.dll"), "SetDllDirectoryA"); p_GetSystemDirectory = (UINT(WINAPI *)(LPTSTR, UINT)) GetProcAddress(GetModuleHandle("kernel32.dll"), "GetSystemDirectoryA"); if (p_SetDllDirectory && p_GetSystemDirectory) { char npcap_path[512] = ""; if (p_GetSystemDirectory (npcap_path, sizeof(npcap_path) - 7)) strcat (npcap_path, "\\Npcap"); if (p_SetDllDirectory(npcap_path)) hLib = LoadLibraryA(lib_name); p_SetDllDirectory (NULL); } if (hLib == NULL) hLib = LoadLibraryA(lib_name); } #else hLib = dlopen(lib_name, RTLD_NOW); #endif if (hLib == 0) { /* failed to load DLL */ sim_printf ("Eth: Failed to load %s\r\n", lib_name); #ifdef _WIN32 sim_printf ("Eth: You must install Npcap or WinPcap 4.x to use networking\r\n"); #else sim_printf ("Eth: You must install libpcap to use networking\r\n"); #endif lib_loaded = 2; break; } else { /* library loaded OK */ lib_loaded = 1; } /* load required functions; sets dll_load=3 on error */ load_function("pcap_close", (_func *) &p_pcap_close); load_function("pcap_compile", (_func *) &p_pcap_compile); load_function("pcap_datalink", (_func *) &p_pcap_datalink); load_function("pcap_dispatch", (_func *) &p_pcap_dispatch); load_function("pcap_findalldevs", (_func *) &p_pcap_findalldevs); load_function("pcap_freealldevs", (_func *) &p_pcap_freealldevs); load_function("pcap_freecode", (_func *) &p_pcap_freecode); load_function("pcap_geterr", (_func *) &p_pcap_geterr); load_function("pcap_lookupnet", (_func *) &p_pcap_lookupnet); load_function("pcap_open_live", (_func *) &p_pcap_open_live); #ifdef _WIN32 load_function("pcap_setmintocopy", (_func *) &p_pcap_setmintocopy); load_function("pcap_getevent", (_func *) &p_pcap_getevent); #else #ifdef MUST_DO_SELECT load_function("pcap_get_selectable_fd", (_func *) &p_pcap_get_selectable_fd); #endif load_function("pcap_fileno", (_func *) &p_pcap_fileno); #endif load_function("pcap_sendpacket", (_func *) &p_pcap_sendpacket); load_function("pcap_setfilter", (_func *) &p_pcap_setfilter); load_function("pcap_setnonblock", (_func *) &p_pcap_setnonblock); load_function("pcap_lib_version", (_func *) &p_pcap_lib_version); if ((lib_loaded == 1) && (!eth_show_active)) { /* log successful load */ sim_printf("%s\n", p_pcap_lib_version()); } break; default: /* loaded or failed */ break; } return (lib_loaded == 1) ? 1 : 0; } /* define functions with dynamic revectoring */ void pcap_close(pcap_t* a) { if (load_pcap() != 0) { p_pcap_close(a); } } /* Some platforms's pcap.h have an ancient declaration of pcap_compile which doesn't have a const in the bpf string argument */ #if !defined (BPF_CONST_STRING) int pcap_compile(pcap_t* a, struct bpf_program* b, char* c, int d, bpf_u_int32 e) { #else int pcap_compile(pcap_t* a, struct bpf_program* b, const char* c, int d, bpf_u_int32 e) { #endif if (load_pcap() != 0) { return p_pcap_compile(a, b, c, d, e); } else { return 0; } } int pcap_datalink(pcap_t* a) { if (load_pcap() != 0) { return p_pcap_datalink(a); } else { return 0; } } int pcap_dispatch(pcap_t* a, int b, pcap_handler c, u_char* d) { if (load_pcap() != 0) { return p_pcap_dispatch(a, b, c, d); } else { return 0; } } int pcap_findalldevs(pcap_if_t** a, char* b) { if (load_pcap() != 0) { return p_pcap_findalldevs(a, b); } else { *a = 0; strcpy(b, no_pcap); return -1; } } void pcap_freealldevs(pcap_if_t* a) { if (load_pcap() != 0) { p_pcap_freealldevs(a); } } void pcap_freecode(struct bpf_program* a) { if (load_pcap() != 0) { p_pcap_freecode(a); } } char* pcap_geterr(pcap_t* a) { if (load_pcap() != 0) { return p_pcap_geterr(a); } else { return (char*) 0; } } int pcap_lookupnet(const char* a, bpf_u_int32* b, bpf_u_int32* c, char* d) { if (load_pcap() != 0) { return p_pcap_lookupnet(a, b, c, d); } else { return 0; } } pcap_t* pcap_open_live(const char* a, int b, int c, int d, char* e) { if (load_pcap() != 0) { return p_pcap_open_live(a, b, c, d, e); } else { return (pcap_t*) 0; } } #ifdef _WIN32 int pcap_setmintocopy(pcap_t* a, int b) { if (load_pcap() != 0) { return p_pcap_setmintocopy(a, b); } else { return 0; } } HANDLE pcap_getevent(pcap_t* a) { if (load_pcap() != 0) { return p_pcap_getevent(a); } else { return (HANDLE) 0; } } #else #ifdef MUST_DO_SELECT int pcap_get_selectable_fd(pcap_t* a) { if (load_pcap() != 0) { return p_pcap_get_selectable_fd(a); } else { return 0; } } #endif int pcap_fileno(pcap_t * a) { if (load_pcap() != 0) { return p_pcap_fileno(a); } else { return 0; } } #endif int pcap_sendpacket(pcap_t* a, const u_char* b, int c) { if (load_pcap() != 0) { return p_pcap_sendpacket(a, b, c); } else { return 0; } } int pcap_setfilter(pcap_t* a, struct bpf_program* b) { if (load_pcap() != 0) { return p_pcap_setfilter(a, b); } else { return 0; } } int pcap_setnonblock(pcap_t* a, int nonblock, char *errbuf) { if (load_pcap() != 0) { return p_pcap_setnonblock(a, nonblock, errbuf); } else { return 0; } } #endif /* defined(USE_SHARED) && (defined(_WIN32) || defined(HAVE_DLOPEN)) */ /* Some platforms have always had pcap_sendpacket */ #if defined(_WIN32) || defined(__VMS) #define HAS_PCAP_SENDPACKET 1 #else /* The latest libpcap and WinPcap all have pcap_sendpacket */ #if !defined (NEED_PCAP_SENDPACKET) #define HAS_PCAP_SENDPACKET 1 #endif #endif #if !defined (HAS_PCAP_SENDPACKET) /* libpcap has no function to write a packet, so we need to implement pcap_sendpacket() for compatibility with the WinPcap base code. Return value: 0=Success, -1=Failure */ int pcap_sendpacket(pcap_t* handle, const u_char* msg, int len) { #if defined (__linux) || defined (__linux__) return (send(pcap_fileno(handle), msg, len, 0) == len)? 0 : -1; #else return (write(pcap_fileno(handle), msg, len) == len)? 0 : -1; #endif /* linux */ } #endif /* !HAS_PCAP_SENDPACKET */ #if defined(_WIN32) || defined(__CYGWIN__) /* extracted from WinPcap's Packet32.h */ struct _PACKET_OID_DATA { uint32 Oid; ///< OID code. See the Microsoft DDK documentation or the file ntddndis.h ///< for a complete list of valid codes. uint32 Length; ///< Length of the data field uint8 Data[1]; ///< variable-lenght field that contains the information passed to or received ///< from the adapter. }; typedef struct _PACKET_OID_DATA PACKET_OID_DATA, *PPACKET_OID_DATA; typedef void **LPADAPTER; #define OID_802_3_CURRENT_ADDRESS 0x01010102 /* Extracted from ntddmdis.h */ static int pcap_mac_if_win32(const char *AdapterName, unsigned char MACAddress[6]) { LPADAPTER lpAdapter; PPACKET_OID_DATA OidData; int Status; int ReturnValue; #ifdef _WIN32 HMODULE hDll; /* handle to DLL */ #else static void *hDll = NULL; /* handle to Library */ typedef int BOOLEAN; #endif LPADAPTER (*p_PacketOpenAdapter)(const char *AdapterName); void (*p_PacketCloseAdapter)(LPADAPTER lpAdapter); int (*p_PacketRequest)(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData); #ifdef _WIN32 hDll = LoadLibraryA("packet.dll"); p_PacketOpenAdapter = (LPADAPTER (*)(const char *AdapterName))GetProcAddress(hDll, "PacketOpenAdapter"); p_PacketCloseAdapter = (void (*)(LPADAPTER lpAdapter))GetProcAddress(hDll, "PacketCloseAdapter"); p_PacketRequest = (int (*)(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData))GetProcAddress(hDll, "PacketRequest"); #else hDll = dlopen("packet.dll", RTLD_NOW); p_PacketOpenAdapter = (LPADAPTER (*)(const char *AdapterName))dlsym(hDll, "PacketOpenAdapter"); p_PacketCloseAdapter = (void (*)(LPADAPTER lpAdapter))dlsym(hDll, "PacketCloseAdapter"); p_PacketRequest = (int (*)(LPADAPTER AdapterObject,BOOLEAN Set,PPACKET_OID_DATA OidData))dlsym(hDll, "PacketRequest"); #endif /* Open the selected adapter */ lpAdapter = p_PacketOpenAdapter(AdapterName); if (!lpAdapter || (*lpAdapter == (void *)-1)) { #ifdef _WIN32 FreeLibrary(hDll); #else dlclose(hDll); #endif return -1; } /* Allocate a buffer to get the MAC adress */ OidData = (PACKET_OID_DATA *)malloc(6 + sizeof(PACKET_OID_DATA)); if (OidData == NULL) { p_PacketCloseAdapter(lpAdapter); #ifdef _WIN32 FreeLibrary(hDll); #else dlclose(hDll); #endif return -1; } /* Retrieve the adapter MAC querying the NIC driver */ OidData->Oid = OID_802_3_CURRENT_ADDRESS; OidData->Length = 6; memset(OidData->Data, 0, 6); Status = p_PacketRequest(lpAdapter, FALSE, OidData); if(Status) { memcpy(MACAddress, OidData->Data, 6); ReturnValue = 0; } else ReturnValue = -1; free(OidData); p_PacketCloseAdapter(lpAdapter); #ifdef _WIN32 FreeLibrary(hDll); #else dlclose(hDll); #endif return ReturnValue; } #endif /* defined(_WIN32) || defined(__CYGWIN__) */ #if defined (__VMS) && !defined(__VAX) #include <descrip.h> #include <iodef.h> #include <ssdef.h> #include <starlet.h> #include <stdio.h> #include <stsdef.h> #include <nmadef.h> static int pcap_mac_if_vms(const char *AdapterName, unsigned char MACAddress[6]) { char VMS_Device[16]; $DESCRIPTOR(Device, VMS_Device); unsigned short iosb[4]; unsigned short *w; unsigned char *pha = NULL; unsigned char *hwa = NULL; int tmpval; int status; unsigned short characteristics[512]; long chardesc[] = {sizeof(characteristics), (long)&characteristics}; unsigned short chan; #pragma member_alignment save #pragma nomember_alignment static struct { short fmt; long val_fmt; short pty; long val_pty; short pad; long val_pad; } setup = { NMA$C_PCLI_FMT, NMA$C_LINFM_ETH, NMA$C_PCLI_PTY, 0x0090, NMA$C_PCLI_PAD, NMA$C_STATE_OFF, }; #pragma member_alignment restore long setupdesc[] = {sizeof(setup), (long)&setup}; /* Convert Interface Name to VMS Device Name */ /* This is a name shuffle */ /* WE0 becomes EWA0: */ /* SE1 becomes ESB0: */ /* XE0 becomes EXA0: */ tmpval = (int)(AdapterName[2]-'0'); if ((tmpval < 0) || (tmpval > 25)) return -1; VMS_Device[0] = toupper(AdapterName[1]); VMS_Device[1] = toupper(AdapterName[0]); VMS_Device[2] = 'A' + tmpval; VMS_Device[3] = '0'; VMS_Device[4] = '\0'; VMS_Device[5] = '\0'; Device.dsc$w_length = strlen(VMS_Device); if (!$VMS_STATUS_SUCCESS( sys$assign (&Device, &chan, 0, 0, 0) )) return -1; status = sys$qiow (0, chan, IO$_SETMODE|IO$M_CTRL|IO$M_STARTUP, &iosb, 0, 0, 0, &setupdesc, 0, 0, 0, 0); if ((!$VMS_STATUS_SUCCESS(status)) || (!$VMS_STATUS_SUCCESS(iosb[0]))) { sys$dassgn(chan); return -1; } status = sys$qiow (0, chan, IO$_SENSEMODE|IO$M_CTRL, &iosb, 0, 0, 0, &chardesc, 0, 0, 0, 0); sys$dassgn(chan); if ((!$VMS_STATUS_SUCCESS(status)) || (!$VMS_STATUS_SUCCESS(iosb[0]))) return -1; for (w=characteristics; w < &characteristics[iosb[1]]; ) { if ((((*w)&0xFFF) == NMA$C_PCLI_HWA) && (6 == *(w+1))) hwa = (unsigned char *)(w + 2); if ((((*w)&0xFFF) == NMA$C_PCLI_PHA) && (6 == *(w+1))) pha = (unsigned char *)(w + 2); if (((*w)&0x1000) == 0) w += 3; /* Skip over Longword Parameter */ else w += (2 + ((1 + *(w+1))/2)); /* Skip over String Parameter */ } if (pha != NULL) /* Prefer Physical Address */ memcpy(MACAddress, pha, 6); else if (hwa != NULL) /* Fallback to Hardware Address */ memcpy(MACAddress, hwa, 6); else return -1; return 0; } #endif /* defined (__VMS) && !defined(__VAX) */ static void eth_get_nic_hw_addr(ETH_DEV* dev, const char *devname) { memset(&dev->host_nic_phy_hw_addr, 0, sizeof(dev->host_nic_phy_hw_addr)); dev->have_host_nic_phy_addr = 0; if (dev->eth_api != ETH_API_PCAP) return; #if defined(_WIN32) || defined(__CYGWIN__) if (!pcap_mac_if_win32(devname, dev->host_nic_phy_hw_addr)) dev->have_host_nic_phy_addr = 1; #elif defined (__VMS) && !defined(__VAX) if (!pcap_mac_if_vms(devname, dev->host_nic_phy_hw_addr)) dev->have_host_nic_phy_addr = 1; #elif !defined(__CYGWIN__) && !defined(__VMS) if (1) { char command[1024]; FILE *f; int i; const char *patterns[] = { "grep [0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]", "egrep [0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]:[0-9a-fA-F]?[0-9a-fA-F]", NULL}; memset(command, 0, sizeof(command)); for (i=0; patterns[i] && (0 == dev->have_host_nic_phy_addr); ++i) { snprintf(command, sizeof(command)-1, "ifconfig %s | %s >NIC.hwaddr", devname, patterns[i]); (void)system(command); if (NULL != (f = fopen("NIC.hwaddr", "r"))) { while (0 == dev->have_host_nic_phy_addr) { if (fgets(command, sizeof(command)-1, f)) { char *p1, *p2; p1 = strchr(command, ':'); while (p1) { p2 = strchr(p1+1, ':'); if (p2 <= p1+3) { unsigned int mac_bytes[6]; if (6 == sscanf(p1-2, "%02x:%02x:%02x:%02x:%02x:%02x", &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], &mac_bytes[4], &mac_bytes[5])) { dev->host_nic_phy_hw_addr[0] = mac_bytes[0]; dev->host_nic_phy_hw_addr[1] = mac_bytes[1]; dev->host_nic_phy_hw_addr[2] = mac_bytes[2]; dev->host_nic_phy_hw_addr[3] = mac_bytes[3]; dev->host_nic_phy_hw_addr[4] = mac_bytes[4]; dev->host_nic_phy_hw_addr[5] = mac_bytes[5]; dev->have_host_nic_phy_addr = 1; } break; } p1 = p2; } } else break; } fclose(f); remove("NIC.hwaddr"); } } } #endif } #if defined(__APPLE__) #include <uuid/uuid.h> #include <unistd.h> static int _eth_get_system_id (char *buf, size_t buf_size) { static struct timespec wait = {5, 0}; /* 5 seconds */ static uuid_t uuid; memset (buf, 0, buf_size); if (buf_size < 37) return -1; if (gethostuuid (uuid, &wait)) memset (uuid, 0, sizeof(uuid)); uuid_unparse_lower(uuid, buf); return 0; } #elif defined(_WIN32) static int _eth_get_system_id (char *buf, size_t buf_size) { LONG status; DWORD reglen, regtype; HKEY reghnd; memset (buf, 0, buf_size); #ifndef KEY_WOW64_64KEY #define KEY_WOW64_64KEY (0x0100) #endif if ((status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Cryptography", 0, KEY_QUERY_VALUE|KEY_WOW64_64KEY, ®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) #include <pthread.h> static void * _eth_reader(void *arg) { ETH_DEV* volatile dev = (ETH_DEV*)arg; int status = 0; int sel_ret = 0; int do_select = 0; SOCKET select_fd = 0; #if defined (_WIN32) HANDLE hWait = (dev->eth_api == ETH_API_PCAP) ? pcap_getevent ((pcap_t*)dev->handle) : NULL; #endif switch (dev->eth_api) { case ETH_API_PCAP: #if defined (HAVE_PCAP_NETWORK) #if defined (MUST_DO_SELECT) do_select = 1; select_fd = pcap_get_selectable_fd((pcap_t *)dev->handle); #endif #endif break; case ETH_API_TAP: case ETH_API_VDE: case ETH_API_UDP: case ETH_API_NAT: do_select = 1; select_fd = dev->fd_handle; break; } sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); /* Boost Priority for this I/O thread vs the CPU instruction execution thread which, in general, won't be readily yielding the processor when this thread needs to run */ sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); while (dev->handle) { #if defined (_WIN32) if (dev->eth_api == ETH_API_PCAP) { if (WAIT_OBJECT_0 == WaitForSingleObject (hWait, 250)) sel_ret = 1; } if ((dev->eth_api == ETH_API_UDP) || (dev->eth_api == ETH_API_NAT)) #endif /* _WIN32 */ if (1) { if (do_select) { #ifdef HAVE_SLIRP_NETWORK if (dev->eth_api == ETH_API_NAT) { sel_ret = sim_slirp_select ((SLIRP*)dev->handle, 250); } else #endif { fd_set setl; struct timeval timeout; FD_ZERO(&setl); FD_SET(select_fd, &setl); timeout.tv_sec = 0; timeout.tv_usec = 250*1000; sel_ret = select(1+select_fd, &setl, NULL, NULL, &timeout); } } else sel_ret = 1; if (sel_ret < 0 && errno != EINTR) break; } if (sel_ret > 0) { if (!dev->handle) break; /* dispatch read request queue available packets */ switch (dev->eth_api) { #ifdef HAVE_PCAP_NETWORK case ETH_API_PCAP: status = pcap_dispatch ((pcap_t*)dev->handle, -1, &_eth_callback, (u_char*)dev); break; #endif #ifdef HAVE_TAP_NETWORK case ETH_API_TAP: if (1) { struct pcap_pkthdr header; int len; u_char buf[ETH_MAX_JUMBO_FRAME]; memset(&header, 0, sizeof(header)); len = read(dev->fd_handle, buf, sizeof(buf)); if (len > 0) { status = 1; header.caplen = header.len = len; _eth_callback((u_char *)dev, &header, buf); } else { if (len < 0) status = -1; else status = 0; } } break; #endif /* HAVE_TAP_NETWORK */ #ifdef HAVE_VDE_NETWORK case ETH_API_VDE: if (1) { struct pcap_pkthdr header; int len; u_char buf[ETH_MAX_JUMBO_FRAME]; memset(&header, 0, sizeof(header)); len = vde_recv((VDECONN *)dev->handle, buf, sizeof(buf), 0); if (len > 0) { status = 1; header.caplen = header.len = len; _eth_callback((u_char *)dev, &header, buf); } else { if (len < 0) status = -1; else status = 0; } } break; #endif /* HAVE_VDE_NETWORK */ #ifdef HAVE_SLIRP_NETWORK case ETH_API_NAT: sim_slirp_dispatch ((SLIRP*)dev->handle); status = 1; break; #endif /* HAVE_SLIRP_NETWORK */ case ETH_API_UDP: if (1) { struct pcap_pkthdr header; int len; u_char buf[ETH_MAX_JUMBO_FRAME]; memset(&header, 0, sizeof(header)); len = (int)sim_read_sock (select_fd, (char *)buf, (int32)sizeof(buf)); if (len > 0) { status = 1; header.caplen = header.len = len; _eth_callback((u_char *)dev, &header, buf); } else { if (len < 0) status = -1; else status = 0; } } break; } if ((status > 0) && (dev->asynch_io)) { int wakeup_needed; pthread_mutex_lock (&dev->lock); wakeup_needed = (dev->read_queue.count != 0); pthread_mutex_unlock (&dev->lock); if (wakeup_needed) { sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); } } if (status < 0) { ++dev->receive_packet_errors; _eth_error (dev, "_eth_reader"); if (dev->handle) { /* Still attached? */ #if defined (_WIN32) hWait = (dev->eth_api == ETH_API_PCAP) ? pcap_getevent ((pcap_t*)dev->handle) : NULL; #endif if (do_select) { select_fd = dev->fd_handle; #if !defined (_WIN32) && defined(HAVE_PCAP_NETWORK) if (dev->eth_api == ETH_API_PCAP) select_fd = pcap_get_selectable_fd((pcap_t *)dev->handle); #endif } } } } } sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n"); return NULL; } static void * _eth_writer(void *arg) { ETH_DEV* volatile dev = (ETH_DEV*)arg; ETH_WRITE_REQUEST *request; /* Boost Priority for this I/O thread vs the CPU instruction execution thread which in general won't be readily yielding the processor when this thread needs to run */ sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); sim_debug(dev->dbit, dev->dptr, "Writer Thread Starting\n"); pthread_mutex_lock (&dev->writer_lock); while (dev->handle) { pthread_cond_wait (&dev->writer_cond, &dev->writer_lock); while (NULL != (request = dev->write_requests)) { /* Pull buffer off request list */ dev->write_requests = request->next; pthread_mutex_unlock (&dev->writer_lock); if (dev->throttle_delay != ETH_THROT_DISABLED_DELAY) { uint32 packet_delta_time = sim_os_msec() - dev->throttle_packet_time; dev->throttle_events <<= 1; dev->throttle_events += (packet_delta_time < dev->throttle_time) ? 1 : 0; if ((dev->throttle_events & dev->throttle_mask) == dev->throttle_mask) { sim_os_ms_sleep (dev->throttle_delay); ++dev->throttle_count; } dev->throttle_packet_time = sim_os_msec(); } dev->write_status = _eth_write(dev, &request->packet, NULL); pthread_mutex_lock (&dev->writer_lock); /* Put buffer on free buffer list */ request->next = dev->write_buffers; dev->write_buffers = request; } } pthread_mutex_unlock (&dev->writer_lock); sim_debug(dev->dbit, dev->dptr, "Writer Thread Exiting\n"); return NULL; } #endif t_stat eth_set_async (ETH_DEV *dev, int latency) { #if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) char *msg = "Eth: can't operate asynchronously, must poll\r\n"; sim_printf ("%s", msg); return SCPE_NOFNC; #else int wakeup_needed; dev->asynch_io = 1; dev->asynch_io_latency = latency; pthread_mutex_lock (&dev->lock); wakeup_needed = (dev->read_queue.count != 0); pthread_mutex_unlock (&dev->lock); if (wakeup_needed) { sim_debug(dev->dbit, dev->dptr, "Queueing automatic poll\n"); sim_activate_abs (dev->dptr->units, dev->asynch_io_latency); } #endif return SCPE_OK; } t_stat eth_clr_async (ETH_DEV *dev) { #if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) return SCPE_NOFNC; #else /* make sure device exists */ if (!dev) return SCPE_UNATT; dev->asynch_io = 0; return SCPE_OK; #endif } t_stat eth_set_throttle (ETH_DEV* dev, uint32 time, uint32 burst, uint32 delay) { if (!dev) return SCPE_IERR; dev->throttle_time = time; dev->throttle_burst = burst; dev->throttle_delay = delay; dev->throttle_mask = (1 << dev->throttle_burst) - 1; return SCPE_OK; } static t_stat _eth_open_port(char *savname, int *eth_api, void **handle, SOCKET *fd_handle, char errbuf[PCAP_ERRBUF_SIZE], char *bpf_filter, void *opaque, DEVICE *dptr, uint32 dbit) { int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; if (bufsz < ETH_MAX_JUMBO_FRAME) bufsz = ETH_MAX_JUMBO_FRAME; /* Enable handling of jumbo frames */ *eth_api = 0; *handle = NULL; *fd_handle = 0; /* attempt to connect device */ memset(errbuf, 0, PCAP_ERRBUF_SIZE); if (0 == strncmp("tap:", savname, 4)) { int tun = -1; /* TUN/TAP Socket */ int on = 1; const char *devname = savname + 4; while (isspace(*devname)) ++devname; #if defined(HAVE_TAP_NETWORK) if (!strcmp(savname, "tap:tapN")) { sim_printf ("Eth: Must specify actual tap device name (i.e. tap:tap0)\r\n"); return SCPE_OPENERR | SCPE_NOMESSAGE; } #endif #if (defined(__linux) || defined(__linux__)) && defined(HAVE_TAP_NETWORK) if ((tun = open("/dev/net/tun", O_RDWR)) >= 0) { struct ifreq ifr; /* Interface Requests */ memset(&ifr, 0, sizeof(ifr)); /* Set up interface flags */ strcpy(ifr.ifr_name, devname); ifr.ifr_flags = IFF_TAP|IFF_NO_PI; /* Send interface requests to TUN/TAP driver. */ if (ioctl(tun, TUNSETIFF, &ifr) >= 0) { if (ioctl(tun, FIONBIO, &on)) { strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); close(tun); } else { *fd_handle = tun; strcpy(savname, ifr.ifr_name); } } else strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); } else strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); #elif defined(HAVE_BSDTUNTAP) && defined(HAVE_TAP_NETWORK) if (1) { char dev_name[64] = ""; snprintf(dev_name, sizeof(dev_name)-1, "/dev/%s", devname); dev_name[sizeof(dev_name)-1] = '\0'; if ((tun = open(dev_name, O_RDWR)) >= 0) { if (ioctl(tun, FIONBIO, &on)) { strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); close(tun); } else { *fd_handle = tun; strcpy(savname, devname); } #if defined (__APPLE__) if (1) { struct ifreq ifr; int s; memset (&ifr, 0, sizeof(ifr)); ifr.ifr_addr.sa_family = AF_INET; strncpy(ifr.ifr_name, savname, sizeof(ifr.ifr_name)); if ((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) >= 0) { ifr.ifr_flags |= IFF_UP; if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr)) { strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); close(tun); } } close(s); } } #endif } else strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); } #else strncpy(errbuf, "No support for tap: devices", PCAP_ERRBUF_SIZE-1); #endif /* !defined(__linux) && !defined(HAVE_BSDTUNTAP) */ if (0 == errbuf[0]) { *eth_api = ETH_API_TAP; *handle = (void *)1; /* Flag used to indicated open */ } } else { /* !tap: */ if (0 == strncmp("vde:", savname, 4)) { #if defined(HAVE_VDE_NETWORK) struct vde_open_args voa; const char *devname = savname + 4; memset(&voa, 0, sizeof(voa)); if (!strcmp(savname, "vde:vdedevice")) { sim_printf ("Eth: Must specify actual vde device name (i.e. vde:/tmp/switch)\r\n"); return SCPE_OPENERR | SCPE_NOMESSAGE; } while (isspace(*devname)) ++devname; if (!(*handle = (void*) vde_open((char *)devname, (char *)"simh", &voa))) strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); else { *eth_api = ETH_API_VDE; *fd_handle = vde_datafd((VDECONN*)(*handle)); } #else strncpy(errbuf, "No support for vde: network devices", PCAP_ERRBUF_SIZE-1); #endif /* defined(HAVE_VDE_NETWORK) */ } else { /* !vde: */ if (0 == strncmp("nat:", savname, 4)) { #if defined(HAVE_SLIRP_NETWORK) const char *devname = savname + 4; while (isspace(*devname)) ++devname; if (!(*handle = (void*) sim_slirp_open(devname, opaque, &_slirp_callback, dptr, dbit))) strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); else { *eth_api = ETH_API_NAT; *fd_handle = 0; } #else strncpy(errbuf, "No support for nat: network devices", PCAP_ERRBUF_SIZE-1); #endif /* defined(HAVE_SLIRP_NETWORK) */ } else { /* not nat: */ if (0 == strncmp("udp:", savname, 4)) { char localport[CBUFSIZE], host[CBUFSIZE], port[CBUFSIZE]; char hostport[2*CBUFSIZE]; const char *devname = savname + 4; if (!strcmp(savname, "udp:sourceport:remotehost:remoteport")) { sim_printf ("Eth: Must specify actual udp host and ports(i.e. udp:1224:somehost.com:2234)\r\n"); return SCPE_OPENERR | SCPE_NOMESSAGE; } while (isspace(*devname)) ++devname; if (SCPE_OK != sim_parse_addr_ex (devname, host, sizeof(host), "localhost", port, sizeof(port), localport, sizeof(localport), NULL)) return SCPE_OPENERR; if (localport[0] == '\0') strcpy (localport, port); sprintf (hostport, "%s:%s", host, port); if ((SCPE_OK == sim_parse_addr (hostport, NULL, 0, NULL, NULL, 0, NULL, "localhost")) && (0 == strcmp (localport, port))) { sim_printf ("Eth: Must specify different udp localhost ports\r\n"); return SCPE_OPENERR | SCPE_NOMESSAGE; } *fd_handle = sim_connect_sock_ex (localport, hostport, NULL, NULL, SIM_SOCK_OPT_DATAGRAM); if (INVALID_SOCKET == *fd_handle) return SCPE_OPENERR; *eth_api = ETH_API_UDP; *handle = (void *)1; /* Flag used to indicated open */ } else { /* not udp:, so attempt to open the parameter as if it were an explicit device name */ #if defined(HAVE_PCAP_NETWORK) *handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); if (!*handle) { /* can't open device */ sim_printf ("Eth: pcap_open_live error - %s\r\n", errbuf); return SCPE_OPENERR | SCPE_NOMESSAGE; } *eth_api = ETH_API_PCAP; #if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__) /* Tell the kernel that the header is fully-formed when it gets it. This is required in order to fake the src address. */ if (1) { int one = 1; ioctl(pcap_fileno(*handle), BIOCSHDRCMPLT, &one); } #endif /* xBSD */ #if defined(_WIN32) pcap_setmintocopy ((pcap_t*)(*handle), 0); #endif #if !defined (USE_READER_THREAD) #ifdef USE_SETNONBLOCK /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ if (pcap_setnonblock (*handle, 1, errbuf) == -1) { sim_printf ("Eth: Failed to set non-blocking: %s\r\n", errbuf); } #endif #if defined (__APPLE__) if (1) { /* Deliver packets immediately, needed for OS X 10.6.2 and later * (Snow-Leopard). * See this thread on libpcap and Mac Os X 10.6 Snow Leopard on * the tcpdump mailinglist: http://seclists.org/tcpdump/2010/q1/110 */ int v = 1; ioctl(pcap_fileno(*handle), BIOCIMMEDIATE, &v); } #endif /* defined (__APPLE__) */ #endif /* !defined (USE_READER_THREAD) */ #else strncpy (errbuf, "Unknown or unsupported network device", PCAP_ERRBUF_SIZE-1); #endif /* defined(HAVE_PCAP_NETWORK) */ } /* not udp:, so attempt to open the parameter as if it were an explicit device name */ } /* !nat: */ } /* !vde: */ } /* !tap: */ if (errbuf[0]) return SCPE_OPENERR; #ifdef USE_BPF if (bpf_filter && (*eth_api == ETH_API_PCAP)) { struct bpf_program bpf; int status; bpf_u_int32 bpf_subnet, bpf_netmask; if (pcap_lookupnet(savname, &bpf_subnet, &bpf_netmask, errbuf)<0) bpf_netmask = 0; /* compile filter string */ if ((status = pcap_compile((pcap_t*)(*handle), &bpf, bpf_filter, 1, bpf_netmask)) < 0) { sprintf(errbuf, "%s", pcap_geterr((pcap_t*)(*handle))); sim_printf("Eth: pcap_compile error: %s\r\n", errbuf); /* show erroneous BPF string */ sim_printf ("Eth: BPF string is: |%s|\r\n", bpf_filter); } else { /* apply compiled filter string */ if ((status = pcap_setfilter((pcap_t*)(*handle), &bpf)) < 0) { sprintf(errbuf, "%s", pcap_geterr((pcap_t*)(*handle))); sim_printf("Eth: pcap_setfilter error: %s\r\n", errbuf); } else { #ifdef USE_SETNONBLOCK /* set file non-blocking */ status = pcap_setnonblock ((pcap_t*)(*handle), 1, errbuf); #endif /* USE_SETNONBLOCK */ } pcap_freecode(&bpf); } } #endif /* USE_BPF */ return SCPE_OK; } t_stat eth_open(ETH_DEV* dev, const char* name, DEVICE* dptr, uint32 dbit) { t_stat r; int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; char errbuf[PCAP_ERRBUF_SIZE]; char temp[1024], desc[1024] = ""; const char* savname = name; char namebuf[4*CBUFSIZE]; int num; if (bufsz < ETH_MAX_JUMBO_FRAME) bufsz = ETH_MAX_JUMBO_FRAME; /* Enable handling of jumbo frames */ /* initialize device */ eth_zero(dev); /* translate name of type "ethX" to real device name */ if ((strlen(name) == 4) && (tolower(name[0]) == 'e') && (tolower(name[1]) == 't') && (tolower(name[2]) == 'h') && isdigit(name[3]) ) { num = atoi(&name[3]); savname = eth_getname(num, temp, desc); if (savname == NULL) /* didn't translate */ return SCPE_OPENERR; } else { /* are they trying to use device description? */ savname = eth_getname_bydesc(name, temp, desc); if (savname == NULL) { /* didn't translate */ /* probably is not ethX and has no description */ savname = eth_getname_byname(name, temp, desc); if (savname == NULL) {/* didn't translate */ savname = name; desc[0] = '\0'; /* no description */ } } } namebuf[sizeof(namebuf)-1] = '\0'; strncpy (namebuf, savname, sizeof(namebuf)-1); savname = namebuf; r = _eth_open_port(namebuf, &dev->eth_api, &dev->handle, &dev->fd_handle, errbuf, NULL, (void *)dev, dptr, dbit); if (errbuf[0]) { sim_printf ("Eth: open error - %s\r\n", errbuf); return SCPE_OPENERR | SCPE_NOMESSAGE; } if (r != SCPE_OK) return r; if (!strcmp (desc, "No description available")) strcpy (desc, ""); sim_printf ("Eth: opened OS device %s%s%s\r\n", savname, desc[0] ? " - " : "", desc); /* get the NIC's hardware MAC address */ eth_get_nic_hw_addr(dev, savname); /* save name of device */ dev->name = (char *)malloc(strlen(savname)+1); strcpy(dev->name, savname); /* save debugging information */ dev->dptr = dptr; dev->dbit = dbit; #if defined (USE_READER_THREAD) if (1) { pthread_attr_t attr; ethq_init (&dev->read_queue, 200); /* initialize FIFO queue */ pthread_mutex_init (&dev->lock, NULL); pthread_mutex_init (&dev->writer_lock, NULL); pthread_mutex_init (&dev->self_lock, NULL); pthread_cond_init (&dev->writer_cond, NULL); pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); #if defined(__hpux) { /* libpcap needs sizeof(long) * 8192 bytes on the stack */ size_t stack_size; const size_t min_stack_size = sizeof(long) * 8192 * 3 / 2; if (!pthread_attr_getstacksize(&attr, &stack_size) && stack_size < min_stack_size) { pthread_attr_setstacksize(&attr, min_stack_size); } } #endif /* defined(__hpux) */ pthread_create (&dev->reader_thread, &attr, _eth_reader, (void *)dev); pthread_create (&dev->writer_thread, &attr, _eth_writer, (void *)dev); pthread_attr_destroy(&attr); } #endif /* defined (USE_READER_THREAD */ _eth_add_to_open_list (dev); return SCPE_OK; } static t_stat _eth_close_port(int eth_api, pcap_t *pcap, SOCKET pcap_fd) { switch (eth_api) { #ifdef HAVE_PCAP_NETWORK case ETH_API_PCAP: pcap_close(pcap); break; #endif #ifdef HAVE_TAP_NETWORK case ETH_API_TAP: close(pcap_fd); break; #endif #ifdef HAVE_VDE_NETWORK case ETH_API_VDE: vde_close((VDECONN*)pcap); break; #endif #ifdef HAVE_SLIRP_NETWORK case ETH_API_NAT: sim_slirp_close((SLIRP*)pcap); break; #endif case ETH_API_UDP: sim_close_sock(pcap_fd); break; } return SCPE_OK; } t_stat eth_close(ETH_DEV* dev) { pcap_t *pcap; SOCKET pcap_fd; /* make sure device exists */ if (!dev) return SCPE_UNATT; /* close the device */ pcap_fd = dev->fd_handle; /* save handle to possibly close later */ pcap = (pcap_t *)dev->handle; dev->handle = NULL; dev->fd_handle = 0; dev->have_host_nic_phy_addr = 0; #if defined (USE_READER_THREAD) pthread_join (dev->reader_thread, NULL); pthread_mutex_destroy (&dev->lock); pthread_cond_signal (&dev->writer_cond); pthread_join (dev->writer_thread, NULL); pthread_mutex_destroy (&dev->self_lock); pthread_mutex_destroy (&dev->writer_lock); pthread_cond_destroy (&dev->writer_cond); if (1) { ETH_WRITE_REQUEST *buffer; while (NULL != (buffer = dev->write_buffers)) { dev->write_buffers = buffer->next; free(buffer); } while (NULL != (buffer = dev->write_requests)) { dev->write_requests = buffer->next; free(buffer); } } ethq_destroy (&dev->read_queue); /* release FIFO queue */ #endif _eth_close_port (dev->eth_api, pcap, pcap_fd); sim_printf ("Eth: closed %s\r\n", dev->name); /* clean up the mess */ free(dev->name); free(dev->bpf_filter); eth_zero(dev); _eth_remove_from_open_list (dev); return SCPE_OK; } t_stat eth_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { fprintf (st, "%s attach help\n\n", dptr->name); fprintf (st, " sim> SHOW ETHERNET\n"); fprintf (st, " libpcap version 1.0.0\n"); fprintf (st, " ETH devices:\n"); fprintf (st, " eth0 en0 (No description available)\n"); #if defined(HAVE_TAP_NETWORK) fprintf (st, " eth1 tap:tapN (Integrated Tun/Tap support)\n"); #endif #if defined(HAVE_SLIRP_NETWORK) fprintf (st, " eth2 vde:device (Integrated VDE support)\n"); #endif #if defined(HAVE_SLIRP_NETWORK) fprintf (st, " eth3 nat:{optional-nat-parameters} (Integrated NAT (SLiRP) support)\n"); #endif fprintf (st, " eth4 udp:sourceport:remotehost:remoteport (Integrated UDP bridge support)\n"); fprintf (st, " sim> ATTACH %s eth0\n\n", dptr->name); fprintf (st, "or equivalently:\n\n"); fprintf (st, " sim> ATTACH %s en0\n\n", dptr->name); #if defined(HAVE_SLIRP_NETWORK) sim_slirp_attach_help (st, dptr, uptr, flag, cptr); #endif return SCPE_OK; } static int _eth_rand_byte() { static int rand_initialized = 0; if (!rand_initialized) srand((unsigned int)sim_os_msec()); return (rand() & 0xFF); } t_stat eth_check_address_conflict (ETH_DEV* dev, ETH_MAC* const mac) { ETH_PACK send, recv; t_stat status; uint32 i; int responses = 0; uint32 offset, function; char mac_string[32]; eth_mac_fmt(mac, mac_string); sim_debug(dev->dbit, dev->dptr, "Determining Address Conflict for MAC address: %s\n", mac_string); /* The process of checking address conflicts is used in two ways: 1) to determine the behavior of the currently running packet delivery facility regarding whether it may receive copies of every packet sent (and how many). 2) to verify if a MAC address which this facility is planning to use as the source address of packets is already in use by some other node on the local network Case #1, doesn't require (and explicitly doesn't want) any interaction or response from other systems on the LAN so therefore no considerations regarding switch packet forwarding are important. Meanwhile, Case #2 does require responses from other components on the LAN to provide useful functionality. The original designers of this mechanism did this when essentially all LANs were single collision domains (i.e. ALL nodes which might be affected by an address conflict were physically present on a single Ethernet cable which might have been extended by a couple of repeaters). Since that time, essentially no networks are single collision domains. Thick and thinwire Ethernet cables don't exist and very few networks even have hubs. Today, essentially all LANs are deployed using one or more layers of network switches. In a switched LAN environment, the switches on the LAN "learn" which ports on the LAN source traffic from which MAC addresses and then forward traffic destined for particular MAC address to the appropriate ports. If a particular MAC address is already in use somewhere on the LAN, then the switches "know" where it is. The host based test using the loopback protocol is poorly designed to detect this condition. This test is performed by the host first changing the device's Physical MAC address to the address which is to be tested, and then sending a loopback packet FROM AND TO this MAC address with a loopback reply to be sent by a system which may be currently using the MAC address. If no reply is received, then the MAC address is presumed to be unused. The sending of this packet will result in its delivery to the right system since the switch port/MAC address tables know where to deliver packets destined to this MAC address, however the response it generates won't be delivered to the system performing the test since the switches on the LAN won't know about the local port being the right target for packets with this MAC address. A better test design to detect these conflicts would be for the testing system to send a loopback packet FROM the current physical MAC address (BEFORE changing it) TO the MAC address being tested with the loopback response coming to the current physical MAC address of the device. If a response is received, then the address is in use and the attempt to change the device's MAC address should fail. Since we can't change the software running in these simulators to implement this better conflict detection approach, we can still "do the right thing" in the sim_ether layer. We're already handling the loopback test packets specially since we always had to avoid receiving the packets which were being sent, but needed to allow for the incoming loopback packets to be properly dealt with. We can extend this current special handling to change outgoing "loopback to self" packets to have source AND loopback destination addresses in the packets to be the host NIC's physical address. The switch network will already know the correct MAC/port relationship for the host NIC's physical address, so loopback response packets will be delivered as needed. Code in _eth_write and _eth_callback provide the special handling to perform the described loopback packet adjustments, and code in eth_filter_hash makes sure that the loopback response packets are received. */ /* build a loopback forward request packet */ memset (&send, 0, sizeof(ETH_PACK)); send.len = ETH_MIN_PACKET; /* minimum packet size */ for (i=0; i<send.len; i++) send.msg[i] = _eth_rand_byte(); memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */ memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */ send.msg[12] = 0x90; /* loopback packet type */ send.msg[13] = 0; send.msg[14] = 0; /* Offset */ send.msg[15] = 0; send.msg[16] = 2; /* Forward */ send.msg[17] = 0; memcpy(&send.msg[18], mac, sizeof(ETH_MAC)); /* Forward Destination */ send.msg[24] = 1; /* Reply */ send.msg[25] = 0; eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); /* send the packet */ status = _eth_write (dev, &send, NULL); if (status != SCPE_OK) { const char *msg; msg = (dev->eth_api == ETH_API_PCAP) ? "Eth: Error Transmitting packet: %s\r\n" "You may need to run as root, or install a libpcap version\r\n" "which is at least 0.9 from your OS vendor or www.tcpdump.org\r\n" : "Eth: Error Transmitting packet: %s\r\n" "You may need to run as root.\r\n"; sim_printf(msg, strerror(errno)); return status; } sim_os_ms_sleep (300); /* time for a conflicting host to respond */ eth_packet_trace_detail (dev, send.msg, send.len, "Sent-Address-Check"); /* empty the read queue and count the responses */ do { memset (&recv, 0, sizeof(ETH_PACK)); status = eth_read (dev, &recv, NULL); eth_packet_trace_detail (dev, recv.msg, recv.len, "Recv-Address-Check"); offset = 16 + (recv.msg[14] | (recv.msg[15] << 8)); function = 0; if ((offset+2) < recv.len) function = recv.msg[offset] | (recv.msg[offset+1] << 8); if (((0 == memcmp(send.msg+12, recv.msg+12, 2)) && /* Protocol Match */ (function == 1) && /* Function is Reply */ (0 == memcmp(&send.msg[offset], &recv.msg[offset], send.len-offset))) || /* Content Match */ (0 == memcmp(send.msg, recv.msg, send.len))) /* Packet Match (Reflection) */ responses++; } while (recv.len > 0); sim_debug(dev->dbit, dev->dptr, "Address Conflict = %d\n", responses); return responses; } t_stat eth_reflect(ETH_DEV* dev) { /* Test with an address no NIC should have. */ /* We do this to avoid reflections from the wire, */ /* in the event that a simulated NIC has a MAC address conflict. */ static ETH_MAC mac = {0xfe,0xff,0xff,0xff,0xff,0xfe}; sim_debug(dev->dbit, dev->dptr, "Determining Reflections...\n"); dev->reflections = 0; dev->reflections = eth_check_address_conflict (dev, &mac); sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections); return dev->reflections; } static void _eth_error(ETH_DEV* dev, const char* where) { char msg[64]; const char *netname = ""; time_t now; time(&now); sim_printf ("%s", asctime(localtime(&now))); switch (dev->eth_api) { case ETH_API_PCAP: netname = "pcap"; break; case ETH_API_TAP: netname = "tap"; break; case ETH_API_VDE: netname = "vde"; break; case ETH_API_UDP: netname = "udp"; break; case ETH_API_NAT: netname = "nat"; break; } sprintf(msg, "%s(%s): ", where, netname); switch (dev->eth_api) { #if defined(HAVE_PCAP_NETWORK) case ETH_API_PCAP: sim_printf ("%s%s\n", msg, pcap_geterr ((pcap_t*)dev->handle)); break; #endif default: sim_err_sock (INVALID_SOCKET, msg); break; } #ifdef USE_READER_THREAD pthread_mutex_lock (&dev->lock); ++dev->error_waiting_threads; if (!dev->error_needs_reset) dev->error_needs_reset = (((dev->transmit_packet_errors + dev->receive_packet_errors)%ETH_ERROR_REOPEN_THRESHOLD) == 0); pthread_mutex_unlock (&dev->lock); #else dev->error_needs_reset = (((dev->transmit_packet_errors + dev->receive_packet_errors)%ETH_ERROR_REOPEN_THRESHOLD) == 0); #endif /* Limit errors to 1 per second (per invoking thread (reader and writer)) */ sim_os_sleep (1); /* When all of the threads which can reference this ETH_DEV object are simultaneously waiting in this routine, we have the potential to close and reopen the network connection. We do this after ETH_ERROR_REOPEN_THRESHOLD total errors have occurred. In practice could be as frequently as once every ETH_ERROR_REOPEN_THRESHOLD/2 seconds, but normally would be about once every 1.5*ETH_ERROR_REOPEN_THRESHOLD seconds (ONLY when the error condition exists). */ #ifdef USE_READER_THREAD pthread_mutex_lock (&dev->lock); if ((dev->error_waiting_threads == 2) && (dev->error_needs_reset)) { #else if (dev->error_needs_reset) { #endif char errbuf[PCAP_ERRBUF_SIZE]; t_stat r; _eth_close_port(dev->eth_api, (pcap_t *)dev->handle, dev->fd_handle); sim_os_sleep (ETH_ERROR_REOPEN_PAUSE); r = _eth_open_port(dev->name, &dev->eth_api, &dev->handle, &dev->fd_handle, errbuf, dev->bpf_filter, (void *)dev, dev->dptr, dev->dbit); dev->error_needs_reset = FALSE; if (r == SCPE_OK) sim_printf ("%s ReOpened: %s \n", msg, dev->name); else sim_printf ("%s ReOpen Attempt Failed: %s - %s\n", msg, dev->name, errbuf); ++dev->error_reopen_count; } #ifdef USE_READER_THREAD --dev->error_waiting_threads; pthread_mutex_unlock (&dev->lock); #endif } static t_stat _eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { int status = 1; /* default to failure */ /* make sure device exists */ if ((!dev) || (dev->eth_api == ETH_API_NONE)) return SCPE_UNATT; /* make sure packet exists */ if (!packet) return SCPE_ARG; /* make sure packet is acceptable length */ if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { int loopback_self_frame = LOOPBACK_SELF_FRAME(packet->msg, packet->msg); int loopback_physical_response = LOOPBACK_PHYSICAL_RESPONSE(dev, packet->msg); eth_packet_trace (dev, packet->msg, packet->len, "writing"); /* record sending of loopback packet (done before actual send to avoid race conditions with receiver) */ if (loopback_self_frame || loopback_physical_response) { /* Direct loopback responses to the host physical address since our physical address may not have been learned yet. */ if (loopback_self_frame && dev->have_host_nic_phy_addr) { memcpy(&packet->msg[6], dev->host_nic_phy_hw_addr, sizeof(ETH_MAC)); memcpy(&packet->msg[18], dev->host_nic_phy_hw_addr, sizeof(ETH_MAC)); eth_packet_trace (dev, packet->msg, packet->len, "writing-fixed"); } #ifdef USE_READER_THREAD pthread_mutex_lock (&dev->self_lock); #endif dev->loopback_self_sent += dev->reflections; dev->loopback_self_sent_total++; #ifdef USE_READER_THREAD pthread_mutex_unlock (&dev->self_lock); #endif } /* dispatch write request (synchronous; no need to save write info to dev) */ switch (dev->eth_api) { #ifdef HAVE_PCAP_NETWORK case ETH_API_PCAP: status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); break; #endif #ifdef HAVE_TAP_NETWORK case ETH_API_TAP: status = (((int)packet->len == write(dev->fd_handle, (void *)packet->msg, packet->len)) ? 0 : -1); break; #endif #ifdef HAVE_VDE_NETWORK case ETH_API_VDE: status = vde_send((VDECONN*)dev->handle, (void *)packet->msg, packet->len, 0); if ((status == (int)packet->len) || (status == 0)) status = 0; else if ((status == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) status = 0; else status = 1; break; #endif #ifdef HAVE_SLIRP_NETWORK case ETH_API_NAT: status = sim_slirp_send((SLIRP*)dev->handle, (char *)packet->msg, (size_t)packet->len, 0); if ((status == (int)packet->len) || (status == 0)) status = 0; else status = 1; break; #endif case ETH_API_UDP: status = (((int32)packet->len == sim_write_sock (dev->fd_handle, (char *)packet->msg, (int32)packet->len)) ? 0 : -1); break; } ++dev->packets_sent; /* basic bookkeeping */ /* On error, correct loopback bookkeeping */ if ((status != 0) && loopback_self_frame) { #ifdef USE_READER_THREAD pthread_mutex_lock (&dev->self_lock); #endif dev->loopback_self_sent -= dev->reflections; dev->loopback_self_sent_total--; #ifdef USE_READER_THREAD pthread_mutex_unlock (&dev->self_lock); #endif } if (status != 0) { ++dev->transmit_packet_errors; _eth_error (dev, "_eth_write"); } } /* if packet->len */ /* call optional write callback function */ if (routine) (routine)(status); return ((status == 0) ? SCPE_OK : SCPE_IOERR); } t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { #ifdef USE_READER_THREAD ETH_WRITE_REQUEST *request; int write_queue_size = 1; /* make sure device exists */ if ((!dev) || (dev->eth_api == ETH_API_NONE)) return SCPE_UNATT; /* Get a buffer */ pthread_mutex_lock (&dev->writer_lock); if (NULL != (request = dev->write_buffers)) dev->write_buffers = request->next; pthread_mutex_unlock (&dev->writer_lock); if (NULL == request) request = (ETH_WRITE_REQUEST *)malloc(sizeof(*request)); /* Copy buffer contents */ request->packet.len = packet->len; request->packet.used = packet->used; request->packet.status = packet->status; request->packet.crc_len = packet->crc_len; memcpy(request->packet.msg, packet->msg, packet->len); /* Insert buffer at the end of the write list (to make sure that */ /* packets make it to the wire in the order they were presented here) */ pthread_mutex_lock (&dev->writer_lock); request->next = NULL; if (dev->write_requests) { ETH_WRITE_REQUEST *last_request = dev->write_requests; ++write_queue_size; while (last_request->next) { last_request = last_request->next; ++write_queue_size; } last_request->next = request; } else dev->write_requests = request; if (write_queue_size > dev->write_queue_peak) dev->write_queue_peak = write_queue_size; pthread_mutex_unlock (&dev->writer_lock); /* Awaken writer thread to perform actual write */ pthread_cond_signal (&dev->writer_cond); /* Return with a status from some prior write */ if (routine) (routine)(dev->write_status); return dev->write_status; #else return _eth_write(dev, packet, routine); #endif } static int _eth_hash_lookup(ETH_MULTIHASH hash, const u_char* data) { int key = 0x3f & (eth_crc32(0, data, 6) >> 26); key ^= 0x3f; return (hash[key>>3] & (1 << (key&0x7))); } #if 0 static int _eth_hash_validate(ETH_MAC *MultiCastList, int count, ETH_MULTIHASH hash) { ETH_MULTIHASH lhash; int i; memset(lhash, 0, sizeof(lhash)); for (i=0; i<count; ++i) { int key = 0x3f & (eth_crc32(0, MultiCastList[i], 6) >> 26); key ^= 0x3F; printf("MAC: %02X:%02X:%02X:%02X:%02X:%02X Key: %X, Byte: %X, Val: %X\n", MultiCastList[i][0], MultiCastList[i][1], MultiCastList[i][2], MultiCastList[i][3], MultiCastList[i][4], MultiCastList[i][5], key, key>>3, (1 << (key&0x7))); lhash[key>>3] |= (1 << (key&0x7)); } if (memcmp(hash, lhash, sizeof(lhash))) { printf("Inconsistent Computed Hash:\n"); printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7]); printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", lhash[0], lhash[1], lhash[2], lhash[3], lhash[4], lhash[5], lhash[6], lhash[7]); } else { printf("Should be: %02X %02X %02X %02X %02X %02X %02X %02X\n", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7]); printf("Was: %02X %02X %02X %02X %02X %02X %02X %02X\n", lhash[0], lhash[1], lhash[2], lhash[3], lhash[4], lhash[5], lhash[6], lhash[7]); } return 0; } static void _eth_test_multicast_hash() { ETH_MAC tMacs[] = { {0xAB, 0x00, 0x04, 0x01, 0xAC, 0x10}, {0xAB, 0x00, 0x00, 0x04, 0x00, 0x00}, {0x09, 0x00, 0x2B, 0x00, 0x00, 0x0F}, {0x09, 0x00, 0x2B, 0x02, 0x01, 0x04}, {0x09, 0x00, 0x2B, 0x02, 0x01, 0x07}, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x01, 0x00, 0x5E, 0x00, 0x00, 0x01}}; ETH_MULTIHASH thash = {0x01, 0x40, 0x00, 0x00, 0x48, 0x88, 0x40, 0x00}; _eth_hash_validate(tMacs, sizeof(tMacs)/sizeof(tMacs[0]), thash); } #endif /* The IP header */ struct IPHeader { uint8 verhlen; /* Version & Header Length in dwords */ #define IP_HLEN(IP) (((IP)->verhlen&0xF)<<2) /* Header Length in Bytes */ #define IP_VERSION(IP) ((((IP)->verhlen)>>4)&0xF) /* IP Version */ uint8 tos; /* Type of service */ uint16 total_len; /* Length of the packet in dwords */ uint16 ident; /* unique identifier */ uint16 flags; /* Fragmentation Flags */ #define IP_DF_FLAG (0x4000) #define IP_MF_FLAG (0x2000) #define IP_OFFSET_MASK (0x1FFF) #define IP_FRAG_DF(IP) (ntohs(((IP)->flags))&IP_DF_FLAG) #define IP_FRAG_MF(IP) (ntohs(((IP)->flags))&IP_MF_FLAG) #define IP_FRAG_OFFSET(IP) (ntohs(((IP)->flags))&IP_OFFSET_MASK) uint8 ttl; /* Time to live */ uint8 proto; /* Protocol number (TCP, UDP etc) */ uint16 checksum; /* IP checksum */ uint32 source_ip; /* Source Address */ uint32 dest_ip; /* Destination Address */ }; /* ICMP header */ struct ICMPHeader { uint8 type; /* ICMP packet type */ uint8 code; /* Type sub code */ uint16 checksum; /* ICMP Checksum */ uint32 otherstuff[1];/* optional data */ }; struct UDPHeader { uint16 source_port; uint16 dest_port; uint16 length; /* The length of the entire UDP datagram, including both header and Data fields. */ uint16 checksum; }; struct TCPHeader { uint16 source_port; uint16 dest_port; uint32 sequence_number; uint32 acknowledgement_number; uint16 data_offset_and_flags; #define TCP_DATA_OFFSET(TCP) ((ntohs((TCP)->data_offset_and_flags)>>12)<<2) #define TCP_CWR_FLAG (0x80) #define TCP_ECR_FLAG (0x40) #define TCP_URG_FLAG (0x20) #define TCP_ACK_FLAG (0x10) #define TCP_PSH_FLAG (0x08) #define TCP_RST_FLAG (0x04) #define TCP_SYN_FLAG (0x02) #define TCP_FIN_FLAG (0x01) #define TCP_FLAGS_MASK (0xFFF) uint16 window; uint16 checksum; uint16 urgent; uint16 otherstuff[1]; /* The rest of the packet */ }; #ifndef IPPROTO_TCP #define IPPROTO_TCP 6 /* tcp */ #endif #ifndef IPPROTO_UDP #define IPPROTO_UDP 17 /* user datagram protocol */ #endif #ifndef IPPROTO_ICMP #define IPPROTO_ICMP 1 /* control message protocol */ #endif static uint16 ip_checksum(uint16 *buffer, int size) { unsigned long cksum = 0; /* Sum all the words together, adding the final byte if size is odd */ while (size > 1) { cksum += *buffer++; size -= sizeof(*buffer); } if (size) { uint16 endword; uint8 *endbytes = (uint8 *)&endword; endbytes[0] = *((uint8 *)buffer); endbytes[1] = 0; cksum += endword; } /* Do a little shuffling */ cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >> 16); /* Return the bitwise complement of the resulting mishmash */ return (uint16)(~cksum); } static uint16 pseudo_checksum(uint16 len, uint16 proto, uint16 *src_addr, uint16 *dest_addr, uint8 *buff) { uint32 sum; /* Sum the data first */ sum = 0xffff&(~ip_checksum((uint16 *)buff, len)); /* add the pseudo header which contains the IP source and destinationn addresses */ sum += src_addr[0]; sum += src_addr[1]; sum += dest_addr[0]; sum += dest_addr[1]; /* and the protocol number and the length of the UDP packet */ sum = sum + htons(proto) + htons(len); /* Do a little shuffling */ sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); /* Return the bitwise complement of the resulting mishmash */ return (uint16)(~sum); } static void _eth_fix_ip_jumbo_offload(ETH_DEV* dev, u_char* msg, int len) { const unsigned short* proto = (const unsigned short*) &msg[12]; struct IPHeader *IP; struct TCPHeader *TCP = NULL; struct UDPHeader *UDP; struct ICMPHeader *ICMP; uint16 orig_checksum; uint16 payload_len; uint16 mtu_payload; uint16 ip_flags; uint16 frag_offset; struct pcap_pkthdr header; uint16 orig_tcp_flags; /* Only interested in IP frames */ if (ntohs(*proto) != 0x0800) { ++dev->jumbo_dropped; /* Non IP Frames are dropped */ return; } IP = (struct IPHeader *)&msg[14]; if (IP_VERSION(IP) != 4) { ++dev->jumbo_dropped; /* Non IPv4 jumbo frames are dropped */ return; } if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) { ++dev->jumbo_dropped; /* Bogus header length frames are dropped */ return; } if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) { ++dev->jumbo_dropped; /* Previously fragmented, but currently jumbo sized frames are dropped */ return; } switch (IP->proto) { case IPPROTO_UDP: UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); if (ntohs(UDP->length) > (len-IP_HLEN(IP))) { ++dev->jumbo_dropped; /* Bogus UDP packet length (packet contained length exceeds packet size) frames are dropped */ return; } if (UDP->checksum == 0) break; /* UDP Checksums are disabled */ orig_checksum = UDP->checksum; UDP->checksum = 0; UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); if (orig_checksum != UDP->checksum) eth_packet_trace (dev, msg, len, "reading jumbo UDP header Checksum Fixed"); break; case IPPROTO_ICMP: ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); orig_checksum = ICMP->checksum; ICMP->checksum = 0; ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); if (orig_checksum != ICMP->checksum) eth_packet_trace (dev, msg, len, "reading jumbo ICMP header Checksum Fixed"); break; case IPPROTO_TCP: TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); if ((TCP_DATA_OFFSET(TCP) > (len-IP_HLEN(IP))) || (TCP_DATA_OFFSET(TCP) < 20)) { ++dev->jumbo_dropped; /* Bogus TCP packet header length (packet contained length exceeds packet size) frames are dropped */ return; } /* We don't do anything with the TCP checksum since we're going to resegment the TCP data below */ break; default: ++dev->jumbo_dropped; /* We onlt handle UDP, ICMP and TCP jumbo frames others are dropped */ return; } /* Reasonable Checksums are now in the jumbo packet, but we've got to actually */ /* deliver ONLY standard sized ethernet frames. Our job here is to now act as */ /* a router might have to and fragment these IPv4 frames as they are delivered */ /* into the virtual NIC. We do this by walking down the packet and dispatching */ /* a chunk at a time recomputing an appropriate header for each chunk. For */ /* datagram oriented protocols (UDP and ICMP) this is done by simple packet */ /* fragmentation. For TCP this is done by breaking large packets into separate */ /* TCP packets. */ memset(&header, 0, sizeof(header)); switch (IP->proto) { case IPPROTO_UDP: case IPPROTO_ICMP: ++dev->jumbo_fragmented; /* When we're performing LSO (Large Send Offload), we're given a 'template' header which may not include a value being populated in the IP header length (which is only 16 bits). We process as payload everything which isn't known header data. */ payload_len = (uint16)(len - (14 + IP_HLEN(IP))); mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP)); frag_offset = 0; while (payload_len > 0) { ip_flags = frag_offset; if (payload_len > mtu_payload) { ip_flags |= IP_MF_FLAG; IP->total_len = htons(((mtu_payload>>3)<<3) + IP_HLEN(IP)); } else { IP->total_len = htons(payload_len + IP_HLEN(IP)); } IP->flags = htons(ip_flags); IP->checksum = 0; IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); header.caplen = header.len = 14 + ntohs(IP->total_len); eth_packet_trace (dev, ((u_char *)IP)-14, header.len, "reading Datagram fragment"); #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET if (1) { /* Debugging is easier if we read packets directly with pcap (i.e. we can use Wireshark to verify packet contents) we don't want to do this all the time for 2 reasons: 1) sending through pcap involves kernel transitions and 2) if the current system reflects sent packets, the recieving side will receive and process 2 copies of any packets sent this way. */ ETH_PACK pkt; memset(&pkt, 0, sizeof(pkt)); memcpy(pkt.msg, ((u_char *)IP)-14, header.len); pkt.len = header.len; _eth_write(dev, &pkt, NULL); } #else _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); #endif payload_len -= (ntohs(IP->total_len) - IP_HLEN(IP)); frag_offset += (ntohs(IP->total_len) - IP_HLEN(IP))>>3; if (payload_len > 0) { /* Move the MAC and IP headers down to just prior to the next payload segment */ memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP)); IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - IP_HLEN(IP)); } } break; case IPPROTO_TCP: ++dev->jumbo_fragmented; eth_packet_trace_ex (dev, ((u_char *)IP)-14, len, "Fragmenting Jumbo TCP segment", 1, dev->dbit); TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); orig_tcp_flags = ntohs(TCP->data_offset_and_flags); /* When we're performing LSO (Large Send Offload), we're given a 'template' header which may not include a value being populated in the IP header length (which is only 16 bits). We process as payload everything which isn't known header data. */ payload_len = (uint16)(len - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); mtu_payload = ETH_MIN_JUMBO_FRAME - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); while (payload_len > 0) { if (payload_len > mtu_payload) { TCP->data_offset_and_flags = htons(orig_tcp_flags&~(TCP_PSH_FLAG|TCP_FIN_FLAG|TCP_RST_FLAG)); IP->total_len = htons(mtu_payload + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); } else { TCP->data_offset_and_flags = htons(orig_tcp_flags); IP->total_len = htons(payload_len + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); } IP->checksum = 0; IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); TCP->checksum = 0; TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); header.caplen = header.len = 14 + ntohs(IP->total_len); eth_packet_trace_ex (dev, ((u_char *)IP)-14, header.len, "reading TCP segment", 1, dev->dbit); #if ETH_MIN_JUMBO_FRAME < ETH_MAX_PACKET if (1) { /* Debugging is easier if we read packets directly with pcap (i.e. we can use Wireshark to verify packet contents) we don't want to do this all the time for 2 reasons: 1) sending through pcap involves kernel transitions and 2) if the current system reflects sent packets, the recieving side will receive and process 2 copies of any packets sent this way. */ ETH_PACK pkt; memset(&pkt, 0, sizeof(pkt)); memcpy(pkt.msg, ((u_char *)IP)-14, header.len); pkt.len = header.len; _eth_write(dev, &pkt, NULL); } #else _eth_callback((u_char *)dev, &header, ((u_char *)IP)-14); #endif payload_len -= (ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); if (payload_len > 0) { /* Move the MAC, IP and TCP headers down to just prior to the next payload segment */ memcpy(((u_char *)IP) + ntohs(IP->total_len) - (14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)), ((u_char *)IP) - 14, 14 + IP_HLEN(IP) + TCP_DATA_OFFSET(TCP)); IP = (struct IPHeader *)(((u_char *)IP) + ntohs(IP->total_len) - (IP_HLEN(IP) + TCP_DATA_OFFSET(TCP))); TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); TCP->sequence_number = htonl(mtu_payload + ntohl(TCP->sequence_number)); } } break; } } static void _eth_fix_ip_xsum_offload(ETH_DEV* dev, const u_char* msg, int len) { const unsigned short* proto = (const unsigned short*) &msg[12]; struct IPHeader *IP; struct TCPHeader *TCP; struct UDPHeader *UDP; struct ICMPHeader *ICMP; uint16 orig_checksum; /* Only need to process locally originated packets */ if ((!dev->have_host_nic_phy_addr) || (memcmp(msg+6, dev->host_nic_phy_hw_addr, 6))) return; /* Only interested in IP frames */ if (ntohs(*proto) != 0x0800) return; IP = (struct IPHeader *)&msg[14]; if (IP_VERSION(IP) != 4) return; /* Only interested in IPv4 frames */ if ((IP_HLEN(IP) > len) || (ntohs(IP->total_len) > len)) return; /* Bogus header length */ orig_checksum = IP->checksum; IP->checksum = 0; IP->checksum = ip_checksum((uint16 *)IP, IP_HLEN(IP)); if (orig_checksum != IP->checksum) eth_packet_trace (dev, msg, len, "reading IP header Checksum Fixed"); if (IP_FRAG_OFFSET(IP) || IP_FRAG_MF(IP)) return; /* Insufficient data to compute payload checksum */ switch (IP->proto) { case IPPROTO_UDP: UDP = (struct UDPHeader *)(((char *)IP)+IP_HLEN(IP)); if (ntohs(UDP->length) > (len-IP_HLEN(IP))) return; /* packet contained length exceeds packet size */ if (UDP->checksum == 0) return; /* UDP Checksums are disabled */ orig_checksum = UDP->checksum; UDP->checksum = 0; UDP->checksum = pseudo_checksum(ntohs(UDP->length), IPPROTO_UDP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)UDP); if (orig_checksum != UDP->checksum) eth_packet_trace (dev, msg, len, "reading UDP header Checksum Fixed"); break; case IPPROTO_TCP: TCP = (struct TCPHeader *)(((char *)IP)+IP_HLEN(IP)); orig_checksum = TCP->checksum; TCP->checksum = 0; TCP->checksum = pseudo_checksum(ntohs(IP->total_len)-IP_HLEN(IP), IPPROTO_TCP, (uint16 *)(&IP->source_ip), (uint16 *)(&IP->dest_ip), (uint8 *)TCP); if (orig_checksum != TCP->checksum) eth_packet_trace (dev, msg, len, "reading TCP header Checksum Fixed"); break; case IPPROTO_ICMP: ICMP = (struct ICMPHeader *)(((char *)IP)+IP_HLEN(IP)); orig_checksum = ICMP->checksum; ICMP->checksum = 0; ICMP->checksum = ip_checksum((uint16 *)ICMP, ntohs(IP->total_len)-IP_HLEN(IP)); if (orig_checksum != ICMP->checksum) eth_packet_trace (dev, msg, len, "reading ICMP header Checksum Fixed"); break; } } static int _eth_process_loopback (ETH_DEV* dev, const u_char* data, uint32 len) { int protocol = data[12] | (data[13] << 8); ETH_PACK response; uint32 offset, function; if (protocol != 0x0090) /* !ethernet loopback */ return 0; if (LOOPBACK_REFLECTION_TEST_PACKET(dev, data)) return 0; /* Ignore reflection check packet */ offset = 16 + (data[14] | (data[15] << 8)); if (offset >= len) return 0; function = data[offset] | (data[offset+1] << 8); if (function != 2) /*forward*/ return 0; /* The only packets we should be responding to are ones which we received due to them being directed to our physical MAC address, OR the Broadcast address OR to a Multicast address we're listening to (we may receive others if we're in promiscuous mode, but shouldn't respond to them) */ if ((0 == (data[0]&1)) && /* Multicast or Broadcast */ (0 != memcmp(dev->filter_address[0], data, sizeof(ETH_MAC)))) return 0; /* Attempts to forward to multicast or broadcast addresses are explicitly ignored by consuming the packet and doing nothing else */ if (data[offset+2]&1) return 1; eth_packet_trace (dev, data, len, "rcvd"); sim_debug(dev->dbit, dev->dptr, "_eth_process_loopback()\n"); /* create forward response packet */ memset(&response, 0, sizeof(response)); response.len = len; memcpy(response.msg, data, len); memcpy(&response.msg[0], &response.msg[offset+2], sizeof(ETH_MAC)); memcpy(&response.msg[6], dev->filter_address[0], sizeof(ETH_MAC)); offset += 8 - 16; /* Account for the Ethernet Header and Offset value in this number */ response.msg[14] = offset & 0xFF; response.msg[15] = (offset >> 8) & 0xFF; /* send response packet */ eth_write(dev, &response, NULL); eth_packet_trace(dev, response.msg, response.len, ((function == 1) ? "loopbackreply" : "loopbackforward")); ++dev->loopback_packets_processed; return 1; } static void _eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) { ETH_DEV* dev = (ETH_DEV*) info; int to_me; int from_me = 0; int i; int bpf_used; if (LOOPBACK_PHYSICAL_RESPONSE(dev, data)) { u_char *datacopy = (u_char *)malloc(header->len); /* Since we changed the outgoing loopback packet to have the physical MAC address of the host's interface instead of the programmatically set physical address of this pseudo device, we restore parts of the modified packet back as needed */ memcpy(datacopy, data, header->len); memcpy(datacopy, dev->physical_addr, sizeof(ETH_MAC)); memcpy(datacopy+18, dev->physical_addr, sizeof(ETH_MAC)); _eth_callback(info, header, datacopy); free(datacopy); return; } switch (dev->eth_api) { case ETH_API_PCAP: #ifdef USE_BPF bpf_used = 1; to_me = 1; /* AUTODIN II hash mode? */ if ((dev->hash_filter) && (data[0] & 0x01) && (!dev->promiscuous) && (!dev->all_multicast)) to_me = _eth_hash_lookup(dev->hash, data); break; #endif /* USE_BPF */ case ETH_API_TAP: case ETH_API_VDE: case ETH_API_UDP: case ETH_API_NAT: bpf_used = 0; to_me = 0; eth_packet_trace (dev, data, header->len, "received"); for (i = 0; i < dev->addr_count; i++) { if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; } /* all multicast mode? */ if (dev->all_multicast && (data[0] & 0x01)) to_me = 1; /* promiscuous mode? */ if (dev->promiscuous) to_me = 1; /* AUTODIN II hash mode? */ if ((dev->hash_filter) && (!to_me) && (data[0] & 0x01)) to_me = _eth_hash_lookup(dev->hash, data); break; default: bpf_used = to_me = 0; /* Should NEVER happen */ abort(); break; } /* detect reception of loopback packet to our physical address */ if ((LOOPBACK_SELF_FRAME(dev->physical_addr, data)) || (LOOPBACK_PHYSICAL_REFLECTION(dev, data))) { #ifdef USE_READER_THREAD pthread_mutex_lock (&dev->self_lock); #endif dev->loopback_self_rcvd_total++; /* lower reflection count - if already zero, pass it on */ if (dev->loopback_self_sent > 0) { eth_packet_trace (dev, data, header->len, "ignored"); dev->loopback_self_sent--; to_me = 0; } else if (!bpf_used) from_me = 0; #ifdef USE_READER_THREAD pthread_mutex_unlock (&dev->self_lock); #endif } if (bpf_used ? to_me : (to_me && !from_me)) { if (header->len > ETH_MIN_JUMBO_FRAME) { if (header->len <= header->caplen) {/* Whole Frame captured? */ u_char *datacopy = (u_char *)malloc(header->len); memcpy(datacopy, data, header->len); _eth_fix_ip_jumbo_offload(dev, datacopy, header->len); free(datacopy); } else ++dev->jumbo_truncated; return; } if (_eth_process_loopback(dev, data, header->len)) return; #if defined (USE_READER_THREAD) if (1) { int crc_len = 0; uint8 crc_data[4]; uint32 len = header->len; u_char *moved_data = NULL; if (header->len < ETH_MIN_PACKET) { /* Pad runt packets before CRC append */ moved_data = (u_char *)malloc(ETH_MIN_PACKET); memcpy(moved_data, data, len); memset(moved_data + len, 0, ETH_MIN_PACKET-len); len = ETH_MIN_PACKET; data = moved_data; } /* If necessary, fix IP header checksums for packets originated locally */ /* but were presumed to be traversing a NIC which was going to handle that task */ /* This must be done before any needed CRC calculation */ _eth_fix_ip_xsum_offload(dev, (const u_char*)data, len); if (dev->need_crc) crc_len = eth_get_packet_crc32_data(data, len, crc_data); eth_packet_trace (dev, data, len, "rcvqd"); pthread_mutex_lock (&dev->lock); ethq_insert_data(&dev->read_queue, ETH_ITM_NORMAL, data, 0, len, crc_len, crc_data, 0); ++dev->packets_received; pthread_mutex_unlock (&dev->lock); free(moved_data); } #else /* !USE_READER_THREAD */ /* set data in passed read packet */ dev->read_packet->len = header->len; memcpy(dev->read_packet->msg, data, header->len); /* Handle runt case and pad with zeros. */ /* The real NIC won't hand us runts from the wire, BUT we may be getting */ /* some packets looped back before they actually traverse the wire */ /* (by an internal bridge device for instance) */ if (header->len < ETH_MIN_PACKET) { memset(&dev->read_packet->msg[header->len], 0, ETH_MIN_PACKET-header->len); dev->read_packet->len = ETH_MIN_PACKET; } /* If necessary, fix IP header checksums for packets originated by the local host */ /* but were presumed to be traversing a NIC which was going to handle that task */ /* This must be done before any needed CRC calculation */ _eth_fix_ip_xsum_offload(dev, dev->read_packet->msg, dev->read_packet->len); if (dev->need_crc) dev->read_packet->crc_len = eth_add_packet_crc32(dev->read_packet->msg, dev->read_packet->len); else dev->read_packet->crc_len = 0; eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading"); ++dev->packets_received; /* call optional read callback function */ if (dev->read_callback) (dev->read_callback)(0); #endif } } int eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) { int status; /* make sure device exists */ if ((!dev) || (dev->eth_api == ETH_API_NONE)) return 0; /* make sure packet exists */ if (!packet) return 0; packet->len = 0; #if !defined (USE_READER_THREAD) /* set read packet */ dev->read_packet = packet; /* set optional callback routine */ dev->read_callback = routine; /* dispatch read request to either receive a filtered packet or timeout */ do { switch (dev->eth_api) { #ifdef HAVE_PCAP_NETWORK case ETH_API_PCAP: status = pcap_dispatch((pcap_t*)dev->handle, 1, &_eth_callback, (u_char*)dev); break; #endif #ifdef HAVE_TAP_NETWORK case ETH_API_TAP: if (1) { struct pcap_pkthdr header; int len; u_char buf[ETH_MAX_JUMBO_FRAME]; memset(&header, 0, sizeof(header)); len = read(dev->fd_handle, buf, sizeof(buf)); if (len > 0) { status = 1; header.caplen = header.len = len; _eth_callback((u_char *)dev, &header, buf); } else { if (len < 0) status = -1; else status = 0; } } break; #endif /* HAVE_TAP_NETWORK */ #ifdef HAVE_VDE_NETWORK case ETH_API_VDE: if (1) { struct pcap_pkthdr header; int len; u_char buf[ETH_MAX_JUMBO_FRAME]; memset(&header, 0, sizeof(header)); len = vde_recv((VDECONN*)dev->handle, buf, sizeof(buf), 0); if (len > 0) { status = 1; header.caplen = header.len = len; _eth_callback((u_char *)dev, &header, buf); } else { if (len < 0) status = -1; else status = 0; } } break; #endif /* HAVE_VDE_NETWORK */ case ETH_API_UDP: if (1) { struct pcap_pkthdr header; int len; u_char buf[ETH_MAX_JUMBO_FRAME]; memset(&header, 0, sizeof(header)); len = (int)sim_read_sock (dev->fd_handle, (char *)buf, (int32)sizeof(buf)); if (len > 0) { status = 1; header.caplen = header.len = len; _eth_callback((u_char *)dev, &header, buf); } else { if (len < 0) status = -1; else status = 0; } } break; } } while ((status > 0) && (0 == packet->len)); if (status < 0) { ++dev->receive_packet_errors; _eth_error (dev, "eth_reader"); } #else /* USE_READER_THREAD */ status = 0; pthread_mutex_lock (&dev->lock); if (dev->read_queue.count > 0) { ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; packet->len = item->packet.len; packet->crc_len = item->packet.crc_len; memcpy(packet->msg, item->packet.msg, ((packet->len > packet->crc_len) ? packet->len : packet->crc_len)); status = 1; ethq_remove(&dev->read_queue); } pthread_mutex_unlock (&dev->lock); if ((status) && (routine)) routine(0); #endif return status; } t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous) { return eth_filter_hash(dev, addr_count, addresses, all_multicast, promiscuous, NULL); } t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous, ETH_MULTIHASH* const hash) { int i; char buf[116+66*ETH_FILTER_MAX]; char errbuf[PCAP_ERRBUF_SIZE]; char mac[20]; char* buf2; t_stat status; #ifdef USE_BPF struct bpf_program bpf; #endif /* make sure device exists */ if (!dev) return SCPE_UNATT; /* filter count OK? */ if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX)) return SCPE_ARG; else if (!addresses) return SCPE_ARG; /* test reflections. This is done early in this routine since eth_reflect */ /* calls eth_filter recursively and thus changes the state of the device. */ if (dev->reflections == -1) status = eth_reflect(dev); /* set new filter addresses */ for (i = 0; i < addr_count; i++) memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); dev->addr_count = addr_count; /* store other flags */ dev->all_multicast = all_multicast; dev->promiscuous = promiscuous; /* store multicast hash data */ dev->hash_filter = (hash != NULL); if (hash) { memcpy(dev->hash, hash, sizeof(*hash)); sim_debug(dev->dbit, dev->dptr, "Multicast Hash: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n", dev->hash[0], dev->hash[1], dev->hash[2], dev->hash[3], dev->hash[4], dev->hash[5], dev->hash[6], dev->hash[7]); } /* print out filter information if debugging */ if (dev->dptr->dctrl & dev->dbit) { sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); for (i = 0; i < addr_count; i++) { char mac[20]; eth_mac_fmt(&dev->filter_address[i], mac); sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac); } if (dev->all_multicast) { sim_debug(dev->dbit, dev->dptr, "All Multicast\n"); } if (dev->promiscuous) { sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); } } /* setup BPF filters and other fields to minimize packet delivery */ strcpy(buf, ""); /* construct destination filters - since the real ethernet interface was set into promiscuous mode by eth_open(), we need to filter out the packets that our simulated interface doesn't want. */ if (!dev->promiscuous) { for (i = 0; i < addr_count; i++) { eth_mac_fmt(&dev->filter_address[i], mac); if (!strstr(buf, mac)) /* eliminate duplicates */ sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "((", mac); } if (dev->all_multicast || dev->hash_filter) sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : "(("); if (strlen(buf) > 0) sprintf(&buf[strlen(buf)], ")"); } /* construct source filters - this prevents packets from being reflected back by systems where WinPcap and libpcap cause packet reflections. Note that some systems do not reflect packets at all. This *assumes* that the simulated NIC will not send out packets with multicast source fields. */ if ((addr_count > 0) && (dev->reflections > 0)) { if (strlen(buf) > 0) sprintf(&buf[strlen(buf)], " and "); sprintf (&buf[strlen(buf)], "not ("); buf2 = &buf[strlen(buf)]; for (i = 0; i < addr_count; i++) { if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */ eth_mac_fmt(&dev->filter_address[i], mac); if (!strstr(buf2, mac)) /* eliminate duplicates */ sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac); } sprintf (&buf[strlen(buf)], ")"); if (1 == strlen(buf2)) { /* all addresses were multicast? */ buf[strlen(buf)-6] = '\0'; /* Remove "not ()" */ if (strlen(buf) > 0) buf[strlen(buf)-5] = '\0'; /* remove " and " */ } } if (strlen(buf) > 0) sprintf(&buf[strlen(buf)], ")"); /* When changing the Physical Address on a LAN interface, VMS sends out a loopback packet with the source and destination addresses set to the same value as the Physical Address which is being setup. This packet is designed to find and help diagnose MAC address conflicts (which also include DECnet address conflicts). Normally, this packet would not be seen by the sender, only by the other machine that has the same Physical Address (or possibly DECnet address). If the ethernet subsystem is reflecting packets, the network startup will fail to start if it sees the reflected packet, since it thinks another system is using this Physical Address (or DECnet address). We have to let these packets through, so that if another machine has the same Physical Address (or DECnet address) that we can detect it. Both eth_write() and _eth_callback() help by checking the reflection count - eth_write() adds the reflection count to dev->loopback_self_sent, and _eth_callback() check the value - if the dev->loopback_self_sent count is zero, then the packet has come from another machine with the same address, and needs to be passed on to the simulated machine. */ memset(dev->physical_addr, 0, sizeof(ETH_MAC)); dev->loopback_self_sent = 0; /* check for physical address in filters */ if ((addr_count) && (dev->reflections > 0)) { for (i = 0; i < addr_count; i++) { if (dev->filter_address[i][0]&1) continue; /* skip all multicast addresses */ eth_mac_fmt(&dev->filter_address[i], mac); if (strcmp(mac, "00:00:00:00:00:00") != 0) { memcpy(dev->physical_addr, &dev->filter_address[i], sizeof(ETH_MAC)); /* let packets through where dst and src are the same as our physical address */ sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac); if (dev->have_host_nic_phy_addr) { eth_mac_fmt(&dev->host_nic_phy_hw_addr, mac); sprintf(&buf[strlen(buf)], " or ((ether dst %s) and (ether proto 0x9000))", mac); } break; } } } if ((0 == strlen(buf)) && (!dev->promiscuous)) /* Empty filter means match nothing */ strcpy(buf, "ether host fe:ff:ff:ff:ff:ff"); /* this should be a good match nothing filter */ sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); /* get netmask, which is a required argument for compiling. The value, in our case isn't actually interesting since the filters we generate aren't referencing IP fields, networks or values */ #ifdef USE_BPF if (dev->eth_api == ETH_API_PCAP) { bpf_u_int32 bpf_subnet, bpf_netmask; if (pcap_lookupnet(dev->name, &bpf_subnet, &bpf_netmask, errbuf)<0) bpf_netmask = 0; /* compile filter string */ if ((status = pcap_compile((pcap_t*)dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { sprintf(errbuf, "%s", pcap_geterr((pcap_t*)dev->handle)); sim_printf("Eth: pcap_compile error: %s\r\n", errbuf); /* show erroneous BPF string */ sim_printf ("Eth: BPF string is: |%s|\r\n", buf); } else { /* apply compiled filter string */ if ((status = pcap_setfilter((pcap_t*)dev->handle, &bpf)) < 0) { sprintf(errbuf, "%s", pcap_geterr((pcap_t*)dev->handle)); sim_printf("Eth: pcap_setfilter error: %s\r\n", errbuf); } else { /* Save BPF filter string */ dev->bpf_filter = (char *)realloc(dev->bpf_filter, 1 + strlen(buf)); strcpy (dev->bpf_filter, buf); #ifdef USE_SETNONBLOCK /* set file non-blocking */ status = pcap_setnonblock (dev->handle, 1, errbuf); #endif /* USE_SETNONBLOCK */ } pcap_freecode(&bpf); } #ifdef USE_READER_THREAD pthread_mutex_lock (&dev->lock); ethq_clear (&dev->read_queue); /* Empty FIFO Queue when filter list changes */ pthread_mutex_unlock (&dev->lock); #endif } #endif /* USE_BPF */ return SCPE_OK; } /* The libpcap provided API pcap_findalldevs() on most platforms, will leverage the getifaddrs() API if it is available in preference to alternate platform specific methods of determining the interface list. A limitation of getifaddrs() is that it returns only interfaces which have associated addresses. This may not include all of the interesting interfaces that we are interested in since a host may have dedicated interfaces for a simulator, which is otherwise unused by the host. One could hand craft the the build of libpcap to specifically use alternate methods to implement pcap_findalldevs(). However, this can get tricky, and would then result in a sort of deviant libpcap. This routine exists to allow platform specific code to validate and/or extend the set of available interfaces to include any that are not returned by pcap_findalldevs. */ int eth_host_devices(int used, int max, ETH_LIST* list) { pcap_t* conn = NULL; int i, j, datalink = 0; char errbuf[PCAP_ERRBUF_SIZE]; for (i=0; i<used; ++i) { /* Cull any non-ethernet interface types */ #if defined(HAVE_PCAP_NETWORK) conn = pcap_open_live(list[i].name, ETH_MAX_PACKET, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); if (NULL != conn) datalink = pcap_datalink(conn), pcap_close(conn); list[i].eth_api = ETH_API_PCAP; #endif if ((NULL == conn) || (datalink != DLT_EN10MB)) { for (j=i; j<used-1; ++j) list[j] = list[j+1]; --used; --i; } } /* for */ #if defined(_WIN32) /* replace device description with user-defined adapter name (if defined) */ for (i=0; i<used; i++) { char regkey[2048]; unsigned char regval[2048]; LONG status; DWORD reglen, regtype; HKEY reghnd; /* These registry keys don't seem to exist for all devices, so we simply ignore errors. */ /* Windows XP x64 registry uses wide characters by default, so we force use of narrow characters by using the 'A'(ANSI) version of RegOpenKeyEx. This could cause some problems later, if this code is internationalized. Ideally, the pcap lookup will return wide characters, and we should use them to build a wide registry key, rather than hardcoding the string as we do here. */ if (list[i].name[strlen( "\\Device\\NPF_" )] == '{') { sprintf( regkey, "SYSTEM\\CurrentControlSet\\Control\\Network\\" "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s\\Connection", list[i].name+ strlen( "\\Device\\NPF_" ) ); if ((status = RegOpenKeyExA (HKEY_LOCAL_MACHINE, regkey, 0, KEY_QUERY_VALUE, ®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"); sprintf(list[used].desc, "%s", "Integrated VDE support"); list[used].eth_api = ETH_API_VDE; ++used; } #endif #ifdef HAVE_SLIRP_NETWORK if (used < max) { sprintf(list[used].name, "%s", "nat:{optional-nat-parameters}"); sprintf(list[used].desc, "%s", "Integrated NAT (SLiRP) support"); list[used].eth_api = ETH_API_NAT; ++used; } #endif if (used < max) { sprintf(list[used].name, "%s", "udp:sourceport:remotehost:remoteport"); sprintf(list[used].desc, "%s", "Integrated UDP bridge support"); list[used].eth_api = ETH_API_UDP; ++used; } return used; } int eth_devices(int max, ETH_LIST* list) { int i = 0; char errbuf[PCAP_ERRBUF_SIZE]; #ifndef DONT_USE_PCAP_FINDALLDEVS pcap_if_t* alldevs; pcap_if_t* dev; memset(list, 0, max*sizeof(*list)); errbuf[0] = '\0'; /* retrieve the device list */ if (pcap_findalldevs(&alldevs, errbuf) == -1) { sim_printf ("Eth: error in pcap_findalldevs: %s\r\n", errbuf); } else { /* copy device list into the passed structure */ for (i=0, dev=alldevs; dev && (i < max); dev=dev->next, ++i) { if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; strncpy(list[i].name, dev->name, sizeof(list[i].name)-1); if (dev->description) strncpy(list[i].desc, dev->description, sizeof(list[i].desc)-1); else strncpy(list[i].desc, "No description available", sizeof(list[i].desc)-1); } /* free device list */ pcap_freealldevs(alldevs); } #endif /* Add any host specific devices and/or validate those already found */ i = eth_host_devices(i, max, list); /* If no devices were found and an error message was left in the buffer, display it */ if ((i == 0) && (errbuf[0])) { sim_printf ("Eth: pcap_findalldevs warning: %s\r\n", errbuf); } /* return device count */ return i; } void eth_show_dev (FILE *st, ETH_DEV* dev) { fprintf(st, "Ethernet Device:\n"); if (!dev) { fprintf(st, "-- Not Attached\n"); return; } fprintf(st, " Name: %s\n", dev->name); fprintf(st, " Reflections: %d\n", dev->reflections); fprintf(st, " Self Loopbacks Sent: %d\n", dev->loopback_self_sent_total); fprintf(st, " Self Loopbacks Rcvd: %d\n", dev->loopback_self_rcvd_total); if (dev->have_host_nic_phy_addr) { char hw_mac[20]; eth_mac_fmt(&dev->host_nic_phy_hw_addr, hw_mac); fprintf(st, " Host NIC Address: %s\n", hw_mac); } if (dev->jumbo_dropped) fprintf(st, " Jumbo Dropped: %d\n", dev->jumbo_dropped); if (dev->jumbo_fragmented) fprintf(st, " Jumbo Fragmented: %d\n", dev->jumbo_fragmented); if (dev->jumbo_truncated) fprintf(st, " Jumbo Truncated: %d\n", dev->jumbo_truncated); if (dev->packets_sent) fprintf(st, " Packets Sent: %d\n", dev->packets_sent); if (dev->transmit_packet_errors) fprintf(st, " Send Packet Errors: %d\n", dev->transmit_packet_errors); if (dev->packets_received) fprintf(st, " Packets Received: %d\n", dev->packets_received); if (dev->receive_packet_errors) fprintf(st, " Read Packet Errors: %d\n", dev->receive_packet_errors); if (dev->error_reopen_count) fprintf(st, " Error ReOpen Count: %d\n", dev->error_reopen_count); if (dev->loopback_packets_processed) fprintf(st, " Loopback Packets: %d\n", dev->loopback_packets_processed); #if defined(USE_READER_THREAD) fprintf(st, " Asynch Interrupts: %s\n", dev->asynch_io?"Enabled":"Disabled"); if (dev->asynch_io) fprintf(st, " Interrupt Latency: %d uSec\n", dev->asynch_io_latency); if (dev->throttle_count) fprintf(st, " Throttle Delays: %d\n", dev->throttle_count); fprintf(st, " Read Queue: Count: %d\n", dev->read_queue.count); fprintf(st, " Read Queue: High: %d\n", dev->read_queue.high); fprintf(st, " Read Queue: Loss: %d\n", dev->read_queue.loss); fprintf(st, " Peak Write Queue Size: %d\n", dev->write_queue_peak); #endif if (dev->bpf_filter) fprintf(st, " BPF Filter: %s\n", dev->bpf_filter); #if defined(HAVE_SLIRP_NETWORK) if (dev->eth_api == ETH_API_NAT) sim_slirp_show ((SLIRP *)dev->handle, st); #endif } #endif /* USE_NETWORK */ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | /* sim_ether.h: OS-dependent network information ------------------------------------------------------------------------------ Copyright (c) 2002-2005, David T. Hittner Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the author shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. ------------------------------------------------------------------------------ Modification history: 01-Mar-12 AGN Cygwin doesn't have non-blocking pcap I/O pcap (it uses WinPcap) 17-Nov-11 MP Added dynamic loading of libpcap on *nix platforms 30-Oct-11 MP Added support for vde (Virtual Distributed Ethernet) networking 18-Apr-11 MP Fixed race condition with self loopback packets in multithreaded environments 09-Dec-10 MP Added support to determine if network address conflicts exist 07-Dec-10 MP Reworked DECnet self detection to the more general approach of loopback self when any Physical Address is being set. 04-Dec-10 MP Changed eth_write to do nonblocking writes when USE_READER_THREAD is defined. 07-Feb-08 MP Added eth_show_dev to display ethernet state 28-Jan-08 MP Added eth_set_async 23-Jan-08 MP Added eth_packet_trace_ex and ethq_destroy 30-Nov-05 DTH Added CRC length to packet and more field comments 04-Feb-04 DTH Added debugging information 14-Jan-04 MP Generalized BSD support issues 05-Jan-04 DTH Added eth_mac_scan 26-Dec-03 DTH Added ethernet show and queue functions from pdp11_xq 23-Dec-03 DTH Added status to packet 01-Dec-03 DTH Added reflections, tweaked decnet fix items 25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code 14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code 07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection. 05-Jun-03 DTH Added used to struct eth_packet 01-Feb-03 MP Changed some uint8 strings to char* to reflect usage 22-Oct-02 DTH Added all_multicast and promiscuous support 21-Oct-02 DTH Corrected copyright again 16-Oct-02 DTH Fixed copyright 08-Oct-02 DTH Integrated with 2.10-0p4, added variable vector and copyrights 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 15-Aug-02 DTH Started XQ simulation ------------------------------------------------------------------------------ */ #ifndef SIM_ETHER_H #define SIM_ETHER_H #include "sim_defs.h" #include "sim_sock.h" #ifdef __cplusplus extern "C" { #endif /* make common BSD code a bit easier to read in this file */ /* OS/X seems to define and compile using one of these BSD types */ #if defined(__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__) #define xBSD 1 #endif #if !defined(__FreeBSD__) && !defined(_WIN32) && !defined(VMS) && !defined(__CYGWIN__) && !defined(__APPLE__) #define USE_SETNONBLOCK 1 #endif /* cygwin dowsn't have the right features to use the threaded network I/O */ #if defined(__CYGWIN__) || defined(__ZAURUS__) // psco added check for Zaurus platform #define DONT_USE_READER_THREAD #endif #if ((((defined(__sun) || defined(__sun__)) && defined(__i386__)) || defined(__linux)) && !defined(DONT_USE_READER_THREAD)) #define USE_READER_THREAD 1 #endif #if defined(DONT_USE_READER_THREAD) #undef USE_READER_THREAD #endif /* make common winpcap code a bit easier to read in this file */ #if defined(_WIN32) || defined(VMS) || defined(__CYGWIN__) #define PCAP_READ_TIMEOUT -1 #else #define PCAP_READ_TIMEOUT 1 #endif /* set related values to have correct relationships */ #if defined (USE_READER_THREAD) #if defined (USE_SETNONBLOCK) #undef USE_SETNONBLOCK #endif /* USE_SETNONBLOCK */ #undef PCAP_READ_TIMEOUT #define PCAP_READ_TIMEOUT 15 #if (!defined (xBSD) && !defined(_WIN32) && !defined(VMS) && !defined(__CYGWIN__)) || defined (HAVE_TAP_NETWORK) || defined (HAVE_VDE_NETWORK) #define MUST_DO_SELECT 1 #endif #endif /* USE_READER_THREAD */ /* give priority to USE_NETWORK over USE_SHARED */ #if defined(USE_NETWORK) && defined(USE_SHARED) #undef USE_SHARED #endif /* USE_SHARED only works on Windows or if HAVE_DLOPEN */ #if defined(USE_SHARED) && !defined(_WIN32) && !defined(HAVE_DLOPEN) #undef USE_SHARED #endif /* USE_SHARED implies shared pcap, so force HAVE_PCAP_NETWORK */ #if defined(USE_SHARED) && !defined(HAVE_PCAP_NETWORK) #define HAVE_PCAP_NETWORK 1 #endif /* USE_BPF is defined to let this code leverage the libpcap/OS kernel provided BPF packet filtering. This generally will enhance performance. It may not be available in some environments and/or it may not work correctly, so undefining this will still provide working code here. */ #if defined(HAVE_PCAP_NETWORK) #define USE_BPF 1 #if defined (_WIN32) && !defined (BPF_CONST_STRING) #define BPF_CONST_STRING 1 #endif #else #define DONT_USE_PCAP_FINDALLDEVS 1 #endif #if defined (USE_READER_THREAD) #include <pthread.h> #endif /* structure declarations */ #define ETH_PROMISC 1 /* promiscuous mode = true */ #define ETH_TIMEOUT -1 /* read timeout in milliseconds (immediate) */ #define ETH_FILTER_MAX 20 /* maximum address filters */ #define ETH_DEV_NAME_MAX 256 /* maximum device name size */ #define ETH_DEV_DESC_MAX 256 /* maximum device description size */ #define ETH_MIN_PACKET 60 /* minimum ethernet packet size */ #define ETH_MAX_PACKET 1514 /* maximum ethernet packet size */ #define ETH_MAX_JUMBO_FRAME 65536 /* maximum ethernet jumbo frame size (or Offload Segment Size) */ #define ETH_MAX_DEVICE 20 /* maximum ethernet devices */ #define ETH_CRC_SIZE 4 /* ethernet CRC size */ #define ETH_FRAME_SIZE (ETH_MAX_PACKET+ETH_CRC_SIZE) /* ethernet maximum frame size */ #define ETH_MIN_JUMBO_FRAME ETH_MAX_PACKET /* Threshold size for Jumbo Frame Processing */ #define LOOPBACK_SELF_FRAME(phy_mac, msg) \ (((msg)[12] == 0x90) && ((msg)[13] == 0x00) && /* Ethernet Loopback */ \ ((msg)[16] == 0x02) && ((msg)[17] == 0x00) && /* Forward Function */ \ ((msg)[24] == 0x01) && ((msg)[25] == 0x00) && /* Next Function - Reply */ \ (memcmp(phy_mac, (msg), 6) == 0) && /* Ethernet Destination */ \ (memcmp(phy_mac, (msg)+6, 6) == 0) && /* Ethernet Source */ \ (memcmp(phy_mac, (msg)+18, 6) == 0)) /* Forward Address */ #define LOOPBACK_PHYSICAL_RESPONSE(dev, msg) \ ((dev->have_host_nic_phy_addr) && \ ((msg)[12] == 0x90) && ((msg)[13] == 0x00) && /* Ethernet Loopback */ \ ((msg)[14] == 0x08) && ((msg)[15] == 0x00) && /* Skipcount - 8 */ \ ((msg)[16] == 0x02) && ((msg)[17] == 0x00) && /* Last Function - Forward */ \ ((msg)[24] == 0x01) && ((msg)[25] == 0x00) && /* Function - Reply */ \ (memcmp(dev->host_nic_phy_hw_addr, (msg)+18, 6) == 0) && /* Forward Address - Host MAC */\ (memcmp(dev->host_nic_phy_hw_addr, (msg), 6) == 0) && /* Ethernet Source - Host MAC */\ (memcmp(dev->physical_addr, (msg)+6, 6) == 0)) /* Ethernet Source */ #define LOOPBACK_PHYSICAL_REFLECTION(dev, msg) \ ((dev->have_host_nic_phy_addr) && \ ((msg)[12] == 0x90) && ((msg)[13] == 0x00) && /* Ethernet Loopback */ \ ((msg)[16] == 0x02) && ((msg)[17] == 0x00) && /* Forward Function */ \ ((msg)[24] == 0x01) && ((msg)[25] == 0x00) && /* Next Function - Reply */ \ (memcmp(dev->host_nic_phy_hw_addr, (msg)+6, 6) == 0) && /* Ethernet Source - Host MAC */\ (memcmp(dev->host_nic_phy_hw_addr, (msg)+18, 6) == 0)) /* Forward Address - Host MAC */ #define LOOPBACK_REFLECTION_TEST_PACKET(dev, msg) \ ((dev->have_host_nic_phy_addr) && \ ((msg)[12] == 0x90) && ((msg)[13] == 0x00) && /* Ethernet Loopback */ \ ((msg)[14] == 0x00) && ((msg)[15] == 0x00) && /* Skipcount - 0 */ \ ((msg)[16] == 0x02) && ((msg)[17] == 0x00) && /* Forward Function */ \ ((msg)[24] == 0x01) && ((msg)[25] == 0x00) && /* Next Function - Reply */ \ ((msg)[00] == 0xFE) && ((msg)[01] == 0xFF) && /* Ethernet Destination - Reflection Test MAC */\ ((msg)[02] == 0xFF) && ((msg)[03] == 0xFF) && \ ((msg)[04] == 0xFF) && ((msg)[05] == 0xFE) && \ (memcmp(dev->host_nic_phy_hw_addr, (msg)+6, 6) == 0)) /* Ethernet Source - Host MAC */ struct eth_packet { uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */ uint8 *oversize; /* oversized frame (message) */ uint32 len; /* packet length without CRC */ uint32 used; /* bytes processed (used in packet chaining) */ int status; /* transmit/receive status */ uint32 crc_len; /* packet length with CRC */ }; struct eth_item { int type; /* receive (0=setup, 1=loopback, 2=normal) */ #define ETH_ITM_SETUP 0 #define ETH_ITM_LOOPBACK 1 #define ETH_ITM_NORMAL 2 struct eth_packet packet; }; struct eth_queue { int max; int count; int head; int tail; int loss; int high; struct eth_item* item; }; struct eth_list { char name[ETH_DEV_NAME_MAX]; char desc[ETH_DEV_DESC_MAX]; int eth_api; }; typedef int ETH_BOOL; typedef unsigned char ETH_MAC[6]; typedef unsigned char ETH_MULTIHASH[8]; typedef struct eth_packet ETH_PACK; typedef void (*ETH_PCALLBACK)(int status); typedef struct eth_list ETH_LIST; typedef struct eth_queue ETH_QUE; typedef struct eth_item ETH_ITEM; struct eth_write_request { struct eth_write_request *next; ETH_PACK packet; }; typedef struct eth_write_request ETH_WRITE_REQUEST; struct eth_device { char* name; /* name of ethernet device */ void* handle; /* handle of implementation-specific device */ SOCKET fd_handle; /* fd to kernel device (where needed) */ char* bpf_filter; /* bpf filter currently in effect */ int eth_api; /* Designator for which API is being used to move packets */ #define ETH_API_NONE 0 /* No API in use yet */ #define ETH_API_PCAP 1 /* Pcap API in use */ #define ETH_API_TAP 2 /* tun/tap API in use */ #define ETH_API_VDE 3 /* VDE API in use */ #define ETH_API_UDP 4 /* UDP API in use */ #define ETH_API_NAT 5 /* NAT (SLiRP) API in use */ ETH_PCALLBACK read_callback; /* read callback function */ ETH_PCALLBACK write_callback; /* write callback function */ ETH_PACK* read_packet; /* read packet */ ETH_MAC filter_address[ETH_FILTER_MAX]; /* filtering addresses */ int addr_count; /* count of filtering addresses */ ETH_BOOL promiscuous; /* promiscuous mode flag */ ETH_BOOL all_multicast; /* receive all multicast messages */ ETH_BOOL hash_filter; /* filter using AUTODIN II multicast hash */ ETH_MULTIHASH hash; /* AUTODIN II multicast hash */ int32 loopback_self_sent; /* loopback packets sent but not seen */ int32 loopback_self_sent_total; /* total loopback packets sent */ int32 loopback_self_rcvd_total; /* total loopback packets seen */ ETH_MAC physical_addr; /* physical address of interface */ int32 have_host_nic_phy_addr; /* flag indicating that the host_nic_phy_hw_addr is valid */ ETH_MAC host_nic_phy_hw_addr; /* MAC address of the attached NIC */ uint32 jumbo_fragmented; /* Giant IPv4 Frames Fragmented */ uint32 jumbo_dropped; /* Giant Frames Dropped */ uint32 jumbo_truncated; /* Giant Frames too big for capture buffer - Dropped */ uint32 packets_sent; /* Total Packets Sent */ uint32 packets_received; /* Total Packets Received */ uint32 loopback_packets_processed; /* Total Loopback Packets Processed */ uint32 transmit_packet_errors; /* Total Send Packet Errors */ uint32 receive_packet_errors; /* Total Read Packet Errors */ int32 error_waiting_threads; /* Count of threads currently waiting after an error */ ETH_BOOL error_needs_reset; /* Flag indicating to force reset */ #define ETH_ERROR_REOPEN_THRESHOLD 10 /* Attempt ReOpen after 20 send/receive errors */ #define ETH_ERROR_REOPEN_PAUSE 4 /* Seconds to pause between closing and reopening LAN */ uint32 error_reopen_count; /* Count of ReOpen Attempts */ DEVICE* dptr; /* device ethernet is attached to */ uint32 dbit; /* debugging bit */ int reflections; /* packet reflections on interface */ int need_crc; /* device needs CRC (Cyclic Redundancy Check) */ /* Throttling control parameters: */ uint32 throttle_time; /* ms burst time window */ #define ETH_THROT_DEFAULT_TIME 5 /* 5ms Default burst time window */ uint32 throttle_burst; /* packets passed with throttle_time which trigger throttling */ #define ETH_THROT_DEFAULT_BURST 4 /* 4 Packet burst in time window */ uint32 throttle_delay; /* ms to delay when throttling. 0 disables throttling */ #define ETH_THROT_DISABLED_DELAY 0 /* 0 Delay disables throttling */ #define ETH_THROT_DEFAULT_DELAY 10 /* 10ms Delay during burst */ /* Throttling state variables: */ uint32 throttle_mask; /* match test for threshold detection (1 << throttle_burst) - 1 */ uint32 throttle_events; /* keeps track of packet arrival values */ uint32 throttle_packet_time; /* time last packet was transmitted */ uint32 throttle_count; /* Total Throttle Delays */ #if defined (USE_READER_THREAD) int asynch_io; /* Asynchronous Interrupt scheduling enabled */ int asynch_io_latency; /* instructions to delay pending interrupt */ ETH_QUE read_queue; pthread_mutex_t lock; pthread_t reader_thread; /* Reader Thread Id */ pthread_t writer_thread; /* Writer Thread Id */ pthread_mutex_t writer_lock; pthread_mutex_t self_lock; pthread_cond_t writer_cond; ETH_WRITE_REQUEST *write_requests; int write_queue_peak; ETH_WRITE_REQUEST *write_buffers; t_stat write_status; #endif }; typedef struct eth_device ETH_DEV; /* prototype declarations*/ t_stat eth_open (ETH_DEV* dev, const char* name, /* open ethernet interface */ DEVICE* dptr, uint32 dbit); t_stat eth_close (ETH_DEV* dev); /* close ethernet interface */ t_stat eth_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, /* write sychronous packet; */ ETH_PCALLBACK routine); /* callback when done */ int eth_read (ETH_DEV* dev, ETH_PACK* packet, /* read single packet; */ ETH_PCALLBACK routine); /* callback when done*/ t_stat eth_filter (ETH_DEV* dev, int addr_count, /* set filter on incoming packets */ ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous); t_stat eth_filter_hash (ETH_DEV* dev, int addr_count, /* set filter on incoming packets with AUTODIN II based hash */ ETH_MAC* const addresses, ETH_BOOL all_multicast, ETH_BOOL promiscuous, ETH_MULTIHASH* const hash); t_stat eth_check_address_conflict (ETH_DEV* dev, ETH_MAC* const address); int eth_devices (int max, ETH_LIST* dev); /* get ethernet devices on host */ void eth_setcrc (ETH_DEV* dev, int need_crc); /* enable/disable CRC mode */ t_stat eth_set_async (ETH_DEV* dev, int latency); /* set read behavior to be async */ t_stat eth_clr_async (ETH_DEV* dev); /* set read behavior to be not async */ t_stat eth_set_throttle (ETH_DEV* dev, uint32 time, uint32 burst, uint32 delay); /* set transmit throttle parameters */ uint32 eth_crc32(uint32 crc, const void* vbuf, size_t len); /* Compute Ethernet Autodin II CRC for buffer */ void eth_packet_trace (ETH_DEV* dev, const uint8 *msg, int len, const char* txt); /* trace ethernet packet header+crc */ void eth_packet_trace_ex (ETH_DEV* dev, const uint8 *msg, int len, const char* txt, int detail, uint32 reason); /* trace ethernet packet */ t_stat eth_show (FILE* st, UNIT* uptr, /* show ethernet devices */ int32 val, CONST void* desc); t_stat eth_show_devices (FILE* st, DEVICE *dptr, /* show ethernet devices */ UNIT* uptr, int32 val, CONST char* desc); void eth_show_dev (FILE*st, ETH_DEV* dev); /* show ethernet device state */ void eth_mac_fmt (ETH_MAC* const add, char* buffer); /* format ethernet mac address */ t_stat eth_mac_scan (ETH_MAC* mac, const char* strmac); /* scan string for mac, put in mac */ t_stat eth_mac_scan_ex (ETH_MAC* mac, /* scan string for mac, put in mac */ const char* strmac, UNIT *uptr);/* for specified unit */ t_stat ethq_init (ETH_QUE* que, int max); /* initialize FIFO queue */ void ethq_clear (ETH_QUE* que); /* clear FIFO queue */ void ethq_remove (ETH_QUE* que); /* remove item from FIFO queue */ void ethq_insert (ETH_QUE* que, int32 type, /* insert item into FIFO queue */ ETH_PACK* packet, int32 status); void ethq_insert_data(ETH_QUE* que, int32 type, /* insert item into FIFO queue */ const uint8 *data, int used, size_t len, size_t crc_len, const uint8 *crc_data, int32 status); t_stat ethq_destroy(ETH_QUE* que); /* release FIFO queue */ const char *eth_capabilities(void); #ifdef __cplusplus } #endif #endif /* _SIM_ETHER_H */ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 | /* sim_fio.c: simulator file I/O library Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 03-Jun-11 MP Simplified VMS 64b support and made more portable 02-Feb-11 MP Added sim_fsize_ex and sim_fsize_name_ex returning t_addr Added export of sim_buf_copy_swapped and sim_buf_swap_data 28-Jun-07 RMS Added VMS IA64 support (from Norm Lastovica) 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) 15-May-06 RMS Added sim_fsize_name 21-Apr-06 RMS Added FreeBSD large file support (from Mark Martinec) 19-Nov-05 RMS Added OS/X large file support (from Peter Schorn) 16-Aug-05 RMS Fixed C++ declaration and cast problems 17-Jul-04 RMS Fixed bug in optimized sim_fread (reported by Scott Bailey) 26-May-04 RMS Optimized sim_fread (suggested by John Dundas) 02-Jan-04 RMS Split out from SCP This library includes: sim_finit - initialize package sim_fopen - open file sim_fread - endian independent read (formerly fxread) sim_write - endian independent write (formerly fxwrite) sim_fseek - conditionally extended (>32b) seek ( sim_fseeko - extended seek (>32b if available) sim_fsize - get file size sim_fsize_name - get file size of named file sim_fsize_ex - get file size as a t_offset sim_fsize_name_ex - get file size as a t_offset of named file sim_buf_copy_swapped - copy data swapping elements along the way sim_buf_swap_data - swap data elements inplace in buffer sim_shmem_open create or attach to a shared memory region sim_shmem_close close a shared memory region sim_fopen and sim_fseek are OS-dependent. The other routines are not. sim_fsize is always a 32b routine (it is used only with small capacity random access devices like fixed head disks and DECtapes). */ #include "sim_defs.h" t_bool sim_end; /* TRUE = little endian, FALSE = big endian */ t_bool sim_taddr_64; /* t_addr is > 32b and Large File Support available */ t_bool sim_toffset_64; /* Large File (>2GB) file I/O Support available */ #if defined(fprintf) /* Make sure to only use the C rtl stream I/O routines */ #undef fprintf #undef fputs #undef fputc #endif /* OS-independent, endian independent binary I/O package For consistency, all binary data read and written by the simulator is stored in little endian data order. That is, in a multi-byte data item, the bytes are written out right to left, low order byte to high order byte. On a big endian host, data is read and written from high byte to low byte. Consequently, data written on a little endian system must be byte reversed to be usable on a big endian system, and vice versa. These routines are analogs of the standard C runtime routines fread and fwrite. If the host is little endian, or the data items are size char, then the calls are passed directly to fread or fwrite. Otherwise, these routines perform the necessary byte swaps. Sim_fread swaps in place, sim_fwrite uses an intermediate buffer. */ int32 sim_finit (void) { union {int32 i; char c[sizeof (int32)]; } end_test; end_test.i = 1; /* test endian-ness */ sim_end = (end_test.c[0] != 0); sim_toffset_64 = (sizeof(t_offset) > sizeof(int32)); /* Large File (>2GB) support */ sim_taddr_64 = sim_toffset_64 && (sizeof(t_addr) > sizeof(int32)); return sim_end; } void sim_buf_swap_data (void *bptr, size_t size, size_t count) { uint32 j; int32 k; unsigned char by, *sptr, *dptr; if (sim_end || (count == 0) || (size == sizeof (char))) return; for (j = 0, dptr = sptr = (unsigned char *) bptr; /* loop on items */ j < count; j++) { for (k = (int32)(size - 1); k >= (((int32) size + 1) / 2); k--) { by = *sptr; /* swap end-for-end */ *sptr++ = *(dptr + k); *(dptr + k) = by; } sptr = dptr = dptr + size; /* next item */ } } size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr) { size_t c; if ((size == 0) || (count == 0)) /* check arguments */ return 0; c = fread (bptr, size, count, fptr); /* read buffer */ if (sim_end || (size == sizeof (char)) || (c == 0)) /* le, byte, or err? */ return c; /* done */ sim_buf_swap_data (bptr, size, count); return c; } void sim_buf_copy_swapped (void *dbuf, const void *sbuf, size_t size, size_t count) { size_t j; int32 k; const unsigned char *sptr = (const unsigned char *)sbuf; unsigned char *dptr = (unsigned char *)dbuf; if (sim_end || (size == sizeof (char))) { memcpy (dptr, sptr, size * count); return; } for (j = 0; j < count; j++) { /* loop on items */ for (k = (int32)(size - 1); k >= 0; k--) *(dptr + k) = *sptr++; dptr = dptr + size; } } size_t sim_fwrite (const void *bptr, size_t size, size_t count, FILE *fptr) { size_t c, nelem, nbuf, lcnt, total; int32 i; const unsigned char *sptr; unsigned char *sim_flip; if ((size == 0) || (count == 0)) /* check arguments */ return 0; if (sim_end || (size == sizeof (char))) /* le or byte? */ return fwrite (bptr, size, count, fptr); /* done */ sim_flip = (unsigned char *)malloc(FLIP_SIZE); if (!sim_flip) return 0; nelem = FLIP_SIZE / size; /* elements in buffer */ nbuf = count / nelem; /* number buffers */ lcnt = count % nelem; /* count in last buf */ if (lcnt) nbuf = nbuf + 1; else lcnt = nelem; total = 0; sptr = (const unsigned char *) bptr; /* init input ptr */ for (i = (int32)nbuf; i > 0; i--) { /* loop on buffers */ c = (i == 1)? lcnt: nelem; sim_buf_copy_swapped (sim_flip, sptr, size, c); sptr = sptr + size * count; c = fwrite (sim_flip, size, c, fptr); if (c == 0) { free(sim_flip); return total; } total = total + c; } free(sim_flip); return total; } /* Forward Declaration */ t_offset sim_ftell (FILE *st); /* Get file size */ t_offset sim_fsize_ex (FILE *fp) { t_offset pos, sz; if (fp == NULL) return 0; pos = sim_ftell (fp); sim_fseek (fp, 0, SEEK_END); sz = sim_ftell (fp); sim_fseeko (fp, pos, SEEK_SET); return sz; } t_offset sim_fsize_name_ex (const char *fname) { FILE *fp; t_offset sz; if ((fp = sim_fopen (fname, "rb")) == NULL) return 0; sz = sim_fsize_ex (fp); fclose (fp); return sz; } uint32 sim_fsize_name (const char *fname) { return (uint32)(sim_fsize_name_ex (fname)); } uint32 sim_fsize (FILE *fp) { return (uint32)(sim_fsize_ex (fp)); } /* OS-dependent routines */ /* Optimized file open */ FILE *sim_fopen (const char *file, const char *mode) { #if defined (VMS) return fopen (file, mode, "ALQ=32", "DEQ=4096", "MBF=6", "MBC=127", "FOP=cbt,tef", "ROP=rah,wbh", "CTX=stm"); #elif (defined (__linux) || defined (__linux__) || defined (__hpux) || defined (_AIX)) && !defined (DONT_DO_LARGEFILE) return fopen64 (file, mode); #else return fopen (file, mode); #endif } #if !defined (DONT_DO_LARGEFILE) /* 64b VMS */ #if ((defined (__ALPHA) || defined (__ia64)) && defined (VMS) && (__DECC_VER >= 60590001)) || \ ((defined(__sun) || defined(__sun__)) && defined(_LARGEFILE_SOURCE)) #define S_SIM_IO_FSEEK_EXT_ 1 int sim_fseeko (FILE *st, t_offset offset, int whence) { return fseeko (st, (off_t)offset, whence); } t_offset sim_ftell (FILE *st) { return (t_offset)(ftello (st)); } #endif /* Alpha UNIX - natively 64b */ #if defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */ #define S_SIM_IO_FSEEK_EXT_ 1 int sim_fseeko (FILE *st, t_offset offset, int whence) { return fseek (st, offset, whence); } t_offset sim_ftell (FILE *st) { return (t_offset)(ftell (st)); } #endif /* Windows */ #if defined (_WIN32) #define S_SIM_IO_FSEEK_EXT_ 1 #include <sys/stat.h> int sim_fseeko (FILE *st, t_offset offset, int whence) { fpos_t fileaddr; struct _stati64 statb; switch (whence) { case SEEK_SET: fileaddr = (fpos_t)offset; break; case SEEK_END: if (_fstati64 (_fileno (st), &statb)) return (-1); fileaddr = statb.st_size + offset; break; case SEEK_CUR: if (fgetpos (st, &fileaddr)) return (-1); fileaddr = fileaddr + offset; break; default: errno = EINVAL; return (-1); } return fsetpos (st, &fileaddr); } t_offset sim_ftell (FILE *st) { fpos_t fileaddr; if (fgetpos (st, &fileaddr)) return (-1); return (t_offset)fileaddr; } #endif /* end Windows */ /* Linux */ #if defined (__linux) || defined (__linux__) || defined (__hpux) || defined (_AIX) #define S_SIM_IO_FSEEK_EXT_ 1 int sim_fseeko (FILE *st, t_offset xpos, int origin) { return fseeko64 (st, (off64_t)xpos, origin); } t_offset sim_ftell (FILE *st) { return (t_offset)(ftello64 (st)); } #endif /* end Linux with LFS */ /* Apple OS/X */ #if defined (__APPLE__) || defined (__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__) || defined (__CYGWIN__) #define S_SIM_IO_FSEEK_EXT_ 1 int sim_fseeko (FILE *st, t_offset xpos, int origin) { return fseeko (st, (off_t)xpos, origin); } t_offset sim_ftell (FILE *st) { return (t_offset)(ftello (st)); } #endif /* end Apple OS/X */ #endif /* !DONT_DO_LARGEFILE */ /* Default: no OS-specific routine has been defined */ #if !defined (S_SIM_IO_FSEEK_EXT_) int sim_fseeko (FILE *st, t_offset xpos, int origin) { return fseek (st, (long) xpos, origin); } t_offset sim_ftell (FILE *st) { return (t_offset)(ftell (st)); } #endif int sim_fseek (FILE *st, t_addr offset, int whence) { return sim_fseeko (st, (t_offset)offset, whence); } #if defined(_WIN32) #include <io.h> int sim_set_fsize (FILE *fptr, t_addr size) { return _chsize(_fileno(fptr), (long)size); } int sim_set_fifo_nonblock (FILE *fptr) { return -1; } struct SHMEM { HANDLE hMapping; size_t shm_size; void *shm_base; }; t_stat sim_shmem_open (const char *name, size_t size, SHMEM **shmem, void **addr) { *shmem = (SHMEM *)calloc (1, sizeof(**shmem)); if (*shmem == NULL) return SCPE_MEM; (*shmem)->hMapping = INVALID_HANDLE_VALUE; (*shmem)->shm_size = size; (*shmem)->shm_base = NULL; (*shmem)->hMapping = CreateFileMappingA (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)size, name); if ((*shmem)->hMapping == INVALID_HANDLE_VALUE) { sim_shmem_close (*shmem); *shmem = NULL; return SCPE_OPENERR; } (*shmem)->shm_base = MapViewOfFile ((*shmem)->hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0); if ((*shmem)->shm_base == NULL) { sim_shmem_close (*shmem); *shmem = NULL; return SCPE_OPENERR; } *addr = (*shmem)->shm_base; return SCPE_OK; } void sim_shmem_close (SHMEM *shmem) { if (shmem == NULL) return; if (shmem->shm_base != NULL) UnmapViewOfFile (shmem->shm_base); if (shmem->hMapping != INVALID_HANDLE_VALUE) CloseHandle (shmem->hMapping); free (shmem); } #else /* !defined(_WIN32) */ #include <unistd.h> int sim_set_fsize (FILE *fptr, t_addr size) { return ftruncate(fileno(fptr), (off_t)size); } #include <sys/stat.h> #include <fcntl.h> int sim_set_fifo_nonblock (FILE *fptr) { struct stat stbuf; if (!fptr || fstat (fileno(fptr), &stbuf)) return -1; #if defined(S_IFIFO) && defined(O_NONBLOCK) if ((stbuf.st_mode & S_IFIFO)) { int flags = fcntl(fileno(fptr), F_GETFL, 0); return fcntl(fileno(fptr), F_SETFL, flags | O_NONBLOCK); } #endif return -1; } #include <sys/mman.h> struct SHMEM { int shm_fd; size_t shm_size; void *shm_base; }; t_stat sim_shmem_open (const char *name, size_t size, SHMEM **shmem, void **addr) { #ifdef HAVE_SHM_OPEN *shmem = (SHMEM *)calloc (1, sizeof(**shmem)); *addr = NULL; if (*shmem == NULL) return SCPE_MEM; (*shmem)->shm_base = MAP_FAILED; (*shmem)->shm_size = size; (*shmem)->shm_fd = shm_open (name, O_RDWR, 0); if ((*shmem)->shm_fd == -1) { (*shmem)->shm_fd = shm_open (name, O_CREAT | O_RDWR, 0660); if ((*shmem)->shm_fd == -1) { sim_shmem_close (*shmem); *shmem = NULL; return SCPE_OPENERR; } if (ftruncate((*shmem)->shm_fd, size)) { sim_shmem_close (*shmem); *shmem = NULL; return SCPE_OPENERR; } } else { struct stat statb; if ((fstat ((*shmem)->shm_fd, &statb)) || (statb.st_size != (*shmem)->shm_size)) { sim_shmem_close (*shmem); *shmem = NULL; return SCPE_OPENERR; } } (*shmem)->shm_base = mmap(NULL, (*shmem)->shm_size, PROT_READ | PROT_WRITE, MAP_SHARED, (*shmem)->shm_fd, 0); if ((*shmem)->shm_base == MAP_FAILED) { sim_shmem_close (*shmem); *shmem = NULL; return SCPE_OPENERR; } *addr = (*shmem)->shm_base; return SCPE_OK; #else return SCPE_NOFNC; #endif } void sim_shmem_close (SHMEM *shmem) { if (shmem == NULL) return; if (shmem->shm_base != MAP_FAILED) munmap (shmem->shm_base, shmem->shm_size); if (shmem->shm_fd != -1) close (shmem->shm_fd); free (shmem); } #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | /* sim_fio.h: simulator file I/O library headers Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 02-Feb-11 MP Added sim_fsize_ex and sim_fsize_name_ex returning t_addr Added export of sim_buf_copy_swapped and sim_buf_swap_data 15-May-06 RMS Added sim_fsize_name 16-Aug-05 RMS Fixed C++ declaration and cast problems 02-Jan-04 RMS Split out from SCP */ #ifndef SIM_FIO_H_ #define SIM_FIO_H_ 0 #ifdef __cplusplus extern "C" { #endif #define FLIP_SIZE (1 << 16) /* flip buf size */ #define fxread(a,b,c,d) sim_fread (a, b, c, d) #define fxwrite(a,b,c,d) sim_fwrite (a, b, c, d) int32 sim_finit (void); #if (defined (__linux) || defined (__linux__) || defined (__hpux) || defined (_AIX) || \ (defined (VMS) && (defined (__ALPHA) || defined (__ia64)) && (__DECC_VER >= 60590001)) || \ ((defined(__sun) || defined(__sun__)) && defined(_LARGEFILE_SOURCE)) || \ defined (_WIN32) || defined (__APPLE__) || defined (__CYGWIN__) || \ defined (__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__)) && !defined (DONT_DO_LARGEFILE) typedef t_int64 t_offset; #else typedef int32 t_offset; #if !defined (DONT_DO_LARGEFILE) #define DONT_DO_LARGEFILE 1 #endif #endif FILE *sim_fopen (const char *file, const char *mode); int sim_fseek (FILE *st, t_addr offset, int whence); int sim_fseeko (FILE *st, t_offset offset, int whence); int sim_set_fsize (FILE *fptr, t_addr size); int sim_set_fifo_nonblock (FILE *fptr); size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr); size_t sim_fwrite (const void *bptr, size_t size, size_t count, FILE *fptr); uint32 sim_fsize (FILE *fptr); uint32 sim_fsize_name (const char *fname); t_offset sim_ftell (FILE *st); t_offset sim_fsize_ex (FILE *fptr); t_offset sim_fsize_name_ex (const char *fname); void sim_buf_swap_data (void *bptr, size_t size, size_t count); void sim_buf_copy_swapped (void *dptr, const void *bptr, size_t size, size_t count); typedef struct SHMEM SHMEM; t_stat sim_shmem_open (const char *name, size_t size, SHMEM **shmem, void **addr); void sim_shmem_close (SHMEM *shmem); extern t_bool sim_taddr_64; /* t_addr is > 32b and Large File Support available */ extern t_bool sim_toffset_64; /* Large File (>2GB) file I/O support */ extern t_bool sim_end; /* TRUE = little endian, FALSE = big endian */ #ifdef __cplusplus } #endif #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 | /* sim_frontpanel.h: simulator frontpanel API definitions Copyright (c) 2015, Mark Pizzolato Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL MARK PIZZOLATO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Mark Pizzolato shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Mark Pizzolato. 15-Jan-15 MP Initial implementation 01-Apr-15 MP Added register indirect, mem_examine and mem_deposit 03-Apr-15 MP Added logic to pass simulator startup messages in panel error text if the connection to the simulator shuts down while it is starting. 04-Apr-15 MP Added mount and dismount routines to connect and disconnect removable media This module defines interface between a front panel application and a simh simulator. Facilities provide ways to gather information from and to observe and control the state of a simulator. Any application which wants to use this API needs to: 1) include this file in the application code 2) compile sim_frontpanel.c and sim_sock.c from the top level directory of the simh source. 3) link the sim_frontpanel and sim_sock object modules and libpthreads into the application. 4) Use a simh simulator built from the same version of simh that the sim_frontpanel and sim_sock modules came from. */ #ifndef SIM_FRONTPANEL_H_ #define SIM_FRONTPANEL_H_ 0 #ifdef __cplusplus extern "C" { #endif #include <stdlib.h> #if !defined(__VAX) /* Unsupported platform */ #define SIM_FRONTPANEL_VERSION 2 /** sim_panel_start_simulator A starts a simulator with a particular configuration sim_path the path to the simulator binary sim_config the configuration to run the simulator with device_panel_count the number of sub panels for connected devices Note 1: - The path specified must be either a fully specified path or it could be merey the simulator name if the simulator binary is located in the current PATH. - The simulator binary must be built from the same version simh source code that the frontpanel API was acquired fron (the API and the simh framework must speak the same language) Note 2: - Configuration file specified should contain device setup statements (enable, disable, CPU types and attach commands). It should not start a simulator running. */ typedef struct PANEL PANEL; PANEL * sim_panel_start_simulator (const char *sim_path, const char *sim_config, size_t device_panel_count); PANEL * sim_panel_start_simulator_debug (const char *sim_path, const char *sim_config, size_t device_panel_count, const char *debug_file); /** sim_panel_add_device_panel - creates a sub panel associated with a specific simulator panel simulator_panel the simulator panel to connect to device_name the simulator's name for the device */ PANEL * sim_panel_add_device_panel (PANEL *simulator_panel, const char *device_name); /** sim_panel_destroy to shutdown a panel or sub panel. Note: destroying a simulator panel will also destroy any related sub panels */ int sim_panel_destroy (PANEL *panel); /** The frontpanel API exposes the state of a simulator via access to simh register variables that the simulator and its devices define. These registers certainly include any architecturally described registers (PC, PSL, SP, etc.), but also include anything else the simulator uses as internal state to implement the running simulator. The registers that a particular frontpanel application mught need access to are described by the application by calling: sim_panel_add_register sim_panel_add_register_array and sim_panel_add_register_indirect name the name the simulator knows this register by device_name the device this register is part of. Defaults to the device of the panel (in a device panel) or the default device in the simulator (usually the CPU). element_count number of elements in the register array size the size (in local storage) of the buffer which will receive the data in the simulator's register addr a pointer to the location of the buffer which will be loaded with the data in the simulator's register */ int sim_panel_add_register (PANEL *panel, const char *name, const char *device_name, size_t size, void *addr); int sim_panel_add_register_array (PANEL *panel, const char *name, const char *device_name, size_t element_count, size_t size, void *addr); int sim_panel_add_register_indirect (PANEL *panel, const char *name, const char *device_name, size_t size, void *addr); /** A panel application has a choice of two different methods of getting the values contained in the set of registers it has declared interest in via the sim_panel_add_register API. 1) The values can be polled (when ever it is desired) by calling sim_panel_get_registers(). 2) The panel can call sim_panel_set_display_callback() to specify a callback routine and a periodic rate that the callback routine should be called. The panel API will make a best effort to deliver the current register state at the desired rate. Note 1: The buffers described in a panel's register set will be dynamically revised as soon as data is available from the simulator. The callback routine merely serves as a notification that a complete register set has arrived. Note 2: The callback routine should, in general, not run for a long time or frontpanel interactions with the simulator may be disrupted. Setting a flag, signaling an event or posting a message are reasonable activities to perform in a callback routine. */ int sim_panel_get_registers (PANEL *panel, unsigned long long *simulation_time); /** */ typedef void (*PANEL_DISPLAY_PCALLBACK)(PANEL *panel, unsigned long long simulation_time, void *context); int sim_panel_set_display_callback (PANEL *panel, PANEL_DISPLAY_PCALLBACK callback, void *context, int callbacks_per_second); /** When a front panel application needs to change the running state of a simulator one of the following routines should be called: sim_panel_exec_halt - Stop instruction execution sim_panel_exec_boot - Boot a simulator from a specific device sim_panel_exec_run - Start/Resume a simulator running instructions sim_panel_exec_step - Have a simulator execute a single step */ int sim_panel_exec_halt (PANEL *panel); int sim_panel_exec_boot (PANEL *panel, const char *device); int sim_panel_exec_run (PANEL *panel); int sim_panel_exec_step (PANEL *panel); /** When a front panel application wants to describe conditions that should stop instruction execution an execution or an output breakpoint should be used. To established or clear a breakpoint, one of the following routines should be called: sim_panel_break_set - Establish a simulation breakpoint sim_panel_break_clear - Cancel/Delete a previously defined breakpoint sim_panel_break_output_set - Establish a simulator output breakpoint sim_panel_break_output_clear - Cancel/Delete a previously defined output breakpoint Note: Any breakpoint switches/flags must be located at the beginning of the condition string */ int sim_panel_break_set (PANEL *panel, const char *condition); int sim_panel_break_clear (PANEL *panel, const char *condition); int sim_panel_break_output_set (PANEL *panel, const char *condition); int sim_panel_break_output_clear (PANEL *panel, const char *condition); /** When a front panel application needs to change or access memory or a register one of the following routines should be called: sim_panel_gen_examine - Examine register or memory sim_panel_gen_deposit - Deposit to register or memory sim_panel_mem_examine - Examine memory location sim_panel_mem_deposit - Deposit to memory location sim_panel_set_register_value - Deposit to a register or memory location */ /** sim_panel_gen_examine name_or_addr the name the simulator knows this register by size the size (in local storage) of the buffer which will receive the data returned when examining the simulator value a pointer to the buffer which will be loaded with the data returned when examining the simulator */ int sim_panel_gen_examine (PANEL *panel, const char *name_or_addr, size_t size, void *value); /** sim_panel_gen_deposit name_or_addr the name the simulator knows this register by size the size (in local storage) of the buffer which contains the data to be deposited into the simulator value a pointer to the buffer which contains the data to be deposited into the simulator */ int sim_panel_gen_deposit (PANEL *panel, const char *name_or_addr, size_t size, const void *value); /** sim_panel_mem_examine addr_size the size (in local storage) of the buffer which contains the memory address of the data to be examined in the simulator addr a pointer to the buffer containing the memory address of the data to be examined in the simulator value_size the size (in local storage) of the buffer which will receive the data returned when examining the simulator value a pointer to the buffer which will be loaded with the data returned when examining the simulator */ int sim_panel_mem_examine (PANEL *panel, size_t addr_size, const void *addr, size_t value_size, void *value); /** sim_panel_mem_deposit addr_size the size (in local storage) of the buffer which contains the memory address of the data to be deposited into the simulator addr a pointer to the buffer containing the memory address of the data to be deposited into the simulator value_size the size (in local storage) of the buffer which will contains the data to be deposited into the simulator value a pointer to the buffer which contains the data to be deposited into the simulator */ int sim_panel_mem_deposit (PANEL *panel, size_t addr_size, const void *addr, size_t value_size, const void *value); /** sim_panel_set_register_value name the name of a simulator register or a memory address which is to receive a new value value the new value in character string form. The string must be in the native/natural radix that the simulator uses when referencing that register */ int sim_panel_set_register_value (PANEL *panel, const char *name, const char *value); /** When a front panel application may needs to change the media in a simulated removable media device one of the following routines should be called: sim_panel_mount - mounts the indicated media file on a device sim_panel_dismount - dismounts the currently mounted media file from a device */ /** sim_panel_mount device the name of a simulator device/unit switches any switches appropriate for the desire attach path the path on the local system to be attached */ int sim_panel_mount (PANEL *panel, const char *device, const char *switches, const char *path); /** sim_panel_dismount device the name of a simulator device/unit */ int sim_panel_dismount (PANEL *panel, const char *device); typedef enum { Halt, /* Simulation is halted (instructions not being executed) */ Run, /* Simulation is executing instructions */ Error /* Panel simulator is in an error state and should be */ /* closed (destroyed). sim_panel_get_error might help */ /* explain why */ } OperationalState; OperationalState sim_panel_get_state (PANEL *panel); /** All APIs routines which return an int return 0 for success and -1 for an error. An API which returns an error (-1), will not change the panel state. sim_panel_get_error - the details of the most recent error sim_panel_clear_error - clears the error buffer */ const char *sim_panel_get_error (void); void sim_panel_clear_error (void); /** The panek<->simulator wire protocol can be traced if protocol problems arise. sim_panel_set_debug_file - Specifies the log file to record debug traffic sim_panel_set_debug_mode - Specifies the debug detail to be recorded sim_panel_flush_debug - Flushes debug output to disk */ void sim_panel_set_debug_file (PANEL *panel, const char *debug_file); #define DBG_XMT 1 /* Transmit Data */ #define DBG_RCV 2 /* Receive Data */ void sim_panel_set_debug_mode (PANEL *panel, int debug_bits); void sim_panel_flush_debug (PANEL *panel); #endif /* !defined(__VAX) */ #ifdef __cplusplus } #endif #endif /* SIM_FRONTPANEL_H_ */ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 | /* sim_serial.c: OS-dependent serial port routines Copyright (c) 2008, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the author shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. The author gratefully acknowledges the assistance of Holger Veit with the UNIX-specific code and testing. 07-Oct-08 JDB [serial] Created file This module provides OS-dependent routines to access serial ports on the host machine. The terminal multiplexer library uses these routines to provide serial connections to simulated terminal interfaces. Currently, the module supports Windows and UNIX. Use on other systems returns error codes indicating that the functions failed, inhibiting serial port support in SIMH. The following routines are provided: sim_open_serial open a serial port sim_config_serial change baud rate and character framing configuration sim_control_serial manipulate and/or return the modem bits on a serial port sim_read_serial read from a serial port sim_write_serial write to a serial port sim_close_serial close a serial port sim_show_serial shows the available host serial ports The calling sequences are as follows: SERHANDLE sim_open_serial (char *name) -------------------------------------- The serial port referenced by the OS-dependent "name" is opened. If the open is successful, and "name" refers to a serial port on the host system, then a handle to the port is returned. If not, then the value INVALID_HANDLE is returned. t_stat sim_config_serial (SERHANDLE port, const char *config) ------------------------------------------------------------- The baud rate and framing parameters (character size, parity, and number of stop bits) of the serial port associated with "port" are set. If any "config" field value is unsupported by the host system, or if the combination of values (e.g., baud rate and number of stop bits) is unsupported, SCPE_ARG is returned. If the configuration is successful, SCPE_OK is returned. sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits) ------------------------------------------------------------------------------------------------- The DTR and RTS line of the serial port is set or cleared as indicated in the respective bits_to_set or bits_to_clear parameters. If the incoming_bits parameter is not NULL, then the modem status bits DCD, RNG, DSR and CTS are returned. If unreasonable or nonsense bits_to_set or bits_to_clear bits are specified, then the return status is SCPE_ARG; If an error occurs, SCPE_IOERR is returned. int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk) ---------------------------------------------------------------------------- A non-blocking read is issued for the serial port indicated by "port" to get at most "count" bytes into the string "buffer". If a serial line break was detected during the read, the variable pointed to by "brk" is set to 1. If the read is successful, the actual number of characters read is returned. If no characters were available, then the value 0 is returned. If an error occurs, then the value -1 is returned. int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count) ------------------------------------------------------------------ A write is issued to the serial port indicated by "port" to put "count" characters from "buffer". If the write is successful, the actual number of characters written is returned. If an error occurs, then the value -1 is returned. void sim_close_serial (SERHANDLE port) -------------------------------------- The serial port indicated by "port" is closed. int sim_serial_devices (int max, SERIAL_LIST* list) --------------------------------------------------- enumerates the available host serial ports t_stat sim_show_serial (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, const void* desc) --------------------------------- displays the available host serial ports */ #include "sim_defs.h" #include "sim_serial.h" #include "sim_tmxr.h" #include <ctype.h> #define SER_DEV_NAME_MAX 256 /* maximum device name size */ #define SER_DEV_DESC_MAX 256 /* maximum device description size */ #define SER_DEV_CONFIG_MAX 64 /* maximum device config size */ #define SER_MAX_DEVICE 64 /* maximum serial devices */ typedef struct serial_list { char name[SER_DEV_NAME_MAX]; char desc[SER_DEV_DESC_MAX]; } SERIAL_LIST; typedef struct serial_config { /* serial port configuration */ uint32 baudrate; /* baud rate */ uint32 charsize; /* character size in bits */ char parity; /* parity (N/O/E/M/S) */ uint32 stopbits; /* 0/1/2 stop bits (0 implies 1.5) */ } SERCONFIG; static int sim_serial_os_devices (int max, SERIAL_LIST* list); static SERHANDLE sim_open_os_serial (char *name); static void sim_close_os_serial (SERHANDLE port); static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config); static struct open_serial_device { SERHANDLE port; TMLN *line; char name[SER_DEV_NAME_MAX]; char config[SER_DEV_CONFIG_MAX]; } *serial_open_devices = NULL; static int serial_open_device_count = 0; static struct open_serial_device *_get_open_device (SERHANDLE port) { int i; for (i=0; i<serial_open_device_count; ++i) if (serial_open_devices[i].port == port) return &serial_open_devices[i]; return NULL; } static struct open_serial_device *_get_open_device_byname (const char *name) { int i; for (i=0; i<serial_open_device_count; ++i) if (0 == strcmp(name, serial_open_devices[i].name)) return &serial_open_devices[i]; return NULL; } static struct open_serial_device *_serial_add_to_open_list (SERHANDLE port, TMLN *line, const char *name, const char *config) { serial_open_devices = (struct open_serial_device *)realloc(serial_open_devices, (++serial_open_device_count)*sizeof(*serial_open_devices)); memset(&serial_open_devices[serial_open_device_count-1], 0, sizeof(serial_open_devices[serial_open_device_count-1])); serial_open_devices[serial_open_device_count-1].port = port; serial_open_devices[serial_open_device_count-1].line = line; strncpy(serial_open_devices[serial_open_device_count-1].name, name, sizeof(serial_open_devices[serial_open_device_count-1].name)-1); if (config) strncpy(serial_open_devices[serial_open_device_count-1].config, config, sizeof(serial_open_devices[serial_open_device_count-1].config)); return &serial_open_devices[serial_open_device_count-1]; } static void _serial_remove_from_open_list (SERHANDLE port) { int i, j; for (i=0; i<serial_open_device_count; ++i) if (serial_open_devices[i].port == port) { for (j=i+1; j<serial_open_device_count; ++j) serial_open_devices[j-1] = serial_open_devices[j]; --serial_open_device_count; break; } } /* Generic error message handler. This routine should be called for unexpected errors. Some error returns may be expected, e.g., a "file not found" error from an "open" routine. These should return appropriate status codes to the caller, allowing SCP to print an error message if desired, rather than printing this generic error message. */ static void sim_error_serial (const char *routine, int error) { sim_printf ("Serial: %s fails with error %d\n", routine, error); return; } /* Used when sorting a list of serial port names */ static int _serial_name_compare (const void *pa, const void *pb) { const SERIAL_LIST *a = (const SERIAL_LIST *)pa; const SERIAL_LIST *b = (const SERIAL_LIST *)pb; return strcmp(a->name, b->name); } static int sim_serial_devices (int max, SERIAL_LIST *list) { int i, j, ports = sim_serial_os_devices(max, list); /* Open ports may not show up in the list returned by sim_serial_os_devices so we add the open ports to the list removing duplicates before sorting the resulting list */ for (i=0; i<serial_open_device_count; ++i) { for (j=0; j<ports; ++j) if (0 == strcmp(serial_open_devices[i].name, list[j].name)) break; if (j<ports) continue; if (ports >= max) break; strcpy(list[ports].name, serial_open_devices[i].name); strcpy(list[ports].desc, serial_open_devices[i].config); ++ports; } if (ports) /* Order the list returned alphabetically by the port name */ qsort (list, ports, sizeof(list[0]), _serial_name_compare); return ports; } static char* sim_serial_getname (int number, char* name) { SERIAL_LIST list[SER_MAX_DEVICE]; int count = sim_serial_devices(SER_MAX_DEVICE, list); if (count <= number) return NULL; strcpy(name, list[number].name); return name; } static char* sim_serial_getname_bydesc (char* desc, char* name) { SERIAL_LIST list[SER_MAX_DEVICE]; int count = sim_serial_devices(SER_MAX_DEVICE, list); int i; size_t j=strlen(desc); for (i=0; i<count; i++) { int found = 1; size_t k = strlen(list[i].desc); if (j != k) continue; for (k=0; k<j; k++) if (tolower(list[i].desc[k]) != tolower(desc[k])) found = 0; if (found == 0) continue; /* found a case-insensitive description match */ strcpy(name, list[i].name); return name; } /* not found */ return NULL; } static char* sim_serial_getname_byname (char* name, char* temp) { SERIAL_LIST list[SER_MAX_DEVICE]; int count = sim_serial_devices(SER_MAX_DEVICE, list); size_t n; int i, found; found = 0; n = strlen(name); for (i=0; i<count && !found; i++) { if ((n == strlen(list[i].name)) && (sim_strncasecmp(name, list[i].name, n) == 0)) { found = 1; strcpy(temp, list[i].name); /* only case might be different */ } } return (found ? temp : NULL); } char* sim_serial_getdesc_byname (char* name, char* temp) { SERIAL_LIST list[SER_MAX_DEVICE]; int count = sim_serial_devices(SER_MAX_DEVICE, list); size_t n; int i, found; found = 0; n = strlen(name); for (i=0; i<count && !found; i++) { if ((n == strlen(list[i].name)) && (sim_strncasecmp(name, list[i].name, n) == 0)) { found = 1; strcpy(temp, list[i].desc); } } return (found ? temp : NULL); } t_stat sim_show_serial (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc) { SERIAL_LIST list[SER_MAX_DEVICE]; int number = sim_serial_devices(SER_MAX_DEVICE, list); fprintf(st, "Serial devices:\n"); if (number == -1) fprintf(st, " serial support not available in simulator\n"); else if (number == 0) fprintf(st, " no serial devices are available\n"); else { size_t min, len; int i; for (i=0, min=0; i<number; i++) if ((len = strlen(list[i].name)) > min) min = len; for (i=0; i<number; i++) fprintf(st," ser%d\t%-*s%s%s%s\n", i, (int)min, list[i].name, list[i].desc[0] ? " (" : "", list[i].desc, list[i].desc[0] ? ")" : ""); } if (serial_open_device_count) { int i; char desc[SER_DEV_DESC_MAX], *d; fprintf(st,"Open Serial Devices:\n"); for (i=0; i<serial_open_device_count; i++) { d = sim_serial_getdesc_byname(serial_open_devices[i].name, desc); fprintf(st, " %s\tLn%02d %s%s%s%s\tConfig: %s\n", serial_open_devices[i].line->mp->dptr->name, (int)(serial_open_devices[i].line->mp->ldsc-serial_open_devices[i].line), serial_open_devices[i].line->destination, d ? " {" : "", d ? d : "", d ? ")" : "", serial_open_devices[i].line->serconfig); } } return SCPE_OK; } SERHANDLE sim_open_serial (char *name, TMLN *lp, t_stat *stat) { char temp1[1024], devname [1024]; char *savname = name; SERHANDLE port = INVALID_HANDLE; CONST char *config; t_stat status; config = get_glyph_nc (name, devname, ';'); /* separate port name from optional config params */ if ((config == NULL) || (*config == '\0')) config = "9600-8N1"; if (stat) *stat = SCPE_OK; /* translate name of type "serX" to real device name */ if ((strlen(devname) <= 5) && (tolower(devname[0]) == 's') && (tolower(devname[1]) == 'e') && (tolower(devname[2]) == 'r') && (isdigit(devname[3])) && (isdigit(devname[4]) || (devname[4] == '\0')) ) { int num = atoi(&devname[3]); savname = sim_serial_getname(num, temp1); if (savname == NULL) { /* didn't translate */ if (stat) *stat = SCPE_OPENERR; return INVALID_HANDLE; } } else { /* are they trying to use device description? */ savname = sim_serial_getname_bydesc(devname, temp1); if (savname == NULL) { /* didn't translate */ /* probably is not serX and has no description */ savname = sim_serial_getname_byname(devname, temp1); if (savname == NULL) /* didn't translate */ savname = devname; } } if (_get_open_device_byname (savname)) { if (stat) *stat = SCPE_OPENERR; return INVALID_HANDLE; } port = sim_open_os_serial (savname); if (port == INVALID_HANDLE) { if (stat) *stat = SCPE_OPENERR; return port; } status = sim_config_serial (port, config); /* set serial configuration */ if (status != SCPE_OK) { /* port configuration error? */ sim_close_serial (port); /* close the port */ if (stat) *stat = status; port = INVALID_HANDLE; /* report error */ } if ((port != INVALID_HANDLE) && (*config) && (lp)) { lp->serconfig = (char *)realloc (lp->serconfig, 1 + strlen (config)); strcpy (lp->serconfig, config); } if (port != INVALID_HANDLE) _serial_add_to_open_list (port, lp, savname, config); return port; } void sim_close_serial (SERHANDLE port) { sim_close_os_serial (port); _serial_remove_from_open_list (port); } t_stat sim_config_serial (SERHANDLE port, CONST char *sconfig) { CONST char *pptr; CONST char *sptr, *tptr; SERCONFIG config = { 0 }; t_bool arg_error = FALSE; t_stat r; struct open_serial_device *dev; if ((sconfig == NULL) || (*sconfig == '\0')) sconfig = "9600-8N1"; /* default settings */ pptr = sconfig; config.baudrate = (uint32)strtotv (pptr, &sptr, 10); /* parse baud rate */ arg_error = (pptr == sptr); /* check for bad argument */ if (*sptr) /* separator present? */ sptr++; /* skip it */ config.charsize = (uint32)strtotv (sptr, &tptr, 10); /* parse character size */ arg_error = arg_error || (sptr == tptr); /* check for bad argument */ if (*tptr) /* parity character present? */ config.parity = (char)toupper (*tptr++); /* save parity character */ config.stopbits = (uint32)strtotv (tptr, &sptr, 10); /* parse number of stop bits */ arg_error = arg_error || (tptr == sptr); /* check for bad argument */ if (arg_error) /* bad conversions? */ return SCPE_ARG; /* report argument error */ if (strcmp (sptr, ".5") == 0) /* 1.5 stop bits requested? */ config.stopbits = 0; /* code request */ r = sim_config_os_serial (port, config); dev = _get_open_device (port); if (dev) { dev->line->serconfig = (char *)realloc (dev->line->serconfig, 1 + strlen (sconfig)); strcpy (dev->line->serconfig, sconfig); } return r; } #if defined (_WIN32) /* Windows serial implementation */ /* Enumerate the available serial ports. The serial port names are extracted from the appropriate place in the windows registry (HKLM\HARDWARE\DEVICEMAP\SERIALCOMM\). The resulting list is sorted alphabetically by device name (COMn). The device description is set to the OS internal name for the COM device. */ struct SERPORT { HANDLE hPort; DWORD dwEvtMask; OVERLAPPED oReadSync; OVERLAPPED oWriteReady; OVERLAPPED oWriteSync; }; static int sim_serial_os_devices (int max, SERIAL_LIST* list) { int ports = 0; HKEY hSERIALCOMM; memset(list, 0, max*sizeof(*list)); if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &hSERIALCOMM) == ERROR_SUCCESS) { DWORD dwIndex = 0; DWORD dwType; DWORD dwValueNameSize = sizeof(list[ports].desc); DWORD dwDataSize = sizeof(list[ports].name); /* Enumerate all the values underneath HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM */ while (RegEnumValueA(hSERIALCOMM, dwIndex, list[ports].desc, &dwValueNameSize, NULL, &dwType, (BYTE *)list[ports].name, &dwDataSize) == ERROR_SUCCESS) { /* String values with non-zero size are the interesting ones */ if ((dwType == REG_SZ) && (dwDataSize > 0)) { if (ports < max) ++ports; else break; } /* Besure to clear the working entry before trying again */ memset(list[ports].name, 0, sizeof(list[ports].name)); memset(list[ports].desc, 0, sizeof(list[ports].desc)); dwValueNameSize = sizeof(list[ports].desc); dwDataSize = sizeof(list[ports].name); ++dwIndex; } RegCloseKey(hSERIALCOMM); } return ports; } /* Open a serial port. The serial port designated by "name" is opened, and the handle to the port is returned. If an error occurs, INVALID_HANDLE is returned instead. After opening, the port is configured with the default communication parameters established by the system, and the timeouts are set for immediate return on a read request to enable polling. Implementation notes: 1. We call "GetDefaultCommConfig" to obtain the default communication parameters for the specified port. If the name does not refer to a communications port (serial or parallel), the function fails. 2. There is no way to limit "CreateFile" just to serial ports, so we must check after the port is opened. The "GetCommState" routine will return an error if the handle does not refer to a serial port. 3. Calling "GetDefaultCommConfig" for a serial port returns a structure containing a DCB. This contains the default parameters. However, some of the DCB fields are not set correctly, so we cannot use this directly in a call to "SetCommState". Instead, we must copy the fields of interest to a DCB retrieved from a call to "GetCommState". */ static SERHANDLE sim_open_os_serial (char *name) { HANDLE hPort; SERHANDLE port; DCB dcb; COMMCONFIG commdefault; DWORD error; DWORD commsize = sizeof (commdefault); COMMTIMEOUTS cto; if (!GetDefaultCommConfig (name, &commdefault, &commsize)) { /* get default comm parameters */ error = GetLastError (); /* function failed; get error */ if (error != ERROR_INVALID_PARAMETER) /* not a communications port name? */ sim_error_serial ("GetDefaultCommConfig", (int) error); /* no, so report unexpected error */ return INVALID_HANDLE; /* indicate bad port name */ } hPort = CreateFile (name, GENERIC_READ | GENERIC_WRITE, /* open the port */ 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); if (hPort == INVALID_HANDLE_VALUE) { /* open failed? */ error = GetLastError (); /* get error code */ if ((error != ERROR_FILE_NOT_FOUND) && /* bad filename? */ (error != ERROR_ACCESS_DENIED)) /* already open? */ sim_error_serial ("CreateFile", (int) error); /* no, so report unexpected error */ return INVALID_HANDLE; /* indicate bad port name */ } port = (SERHANDLE)calloc (1, sizeof(*port)); /* instantiate the SERHANDLE */ port->hPort = hPort; if (!GetCommState (port->hPort, &dcb)) { /* get the current comm parameters */ error = GetLastError (); /* function failed; get error */ if (error != ERROR_INVALID_PARAMETER) /* not a serial port name? */ sim_error_serial ("GetCommState", (int) error); /* no, so report unexpected error */ sim_close_os_serial (port); /* close port */ return INVALID_HANDLE; /* and indicate bad port name */ } dcb.BaudRate = commdefault.dcb.BaudRate; /* copy default parameters of interest */ dcb.Parity = commdefault.dcb.Parity; dcb.ByteSize = commdefault.dcb.ByteSize; dcb.StopBits = commdefault.dcb.StopBits; dcb.fOutX = commdefault.dcb.fOutX; dcb.fInX = commdefault.dcb.fInX; dcb.fDtrControl = DTR_CONTROL_DISABLE; /* disable DTR initially until poll connects */ if (!SetCommState (port->hPort, &dcb)) { /* configure the port with default parameters */ sim_error_serial ("SetCommState", /* function failed; report unexpected error */ (int) GetLastError ()); sim_close_os_serial (port); /* close port */ return INVALID_HANDLE; /* and indicate failure to caller */ } cto.ReadIntervalTimeout = MAXDWORD; /* set port to return immediately on read */ cto.ReadTotalTimeoutMultiplier = 0; /* i.e., to enable polling */ cto.ReadTotalTimeoutConstant = 0; cto.WriteTotalTimeoutMultiplier = 0; cto.WriteTotalTimeoutConstant = 0; if (!SetCommTimeouts (port->hPort, &cto)) { /* configure port timeouts */ sim_error_serial ("SetCommTimeouts", /* function failed; report unexpected error */ (int) GetLastError ()); sim_close_os_serial (port); /* close port */ return INVALID_HANDLE; /* and indicate failure to caller */ } /* Create an event object for use by WaitCommEvent. */ port->oWriteReady.hEvent = CreateEvent(NULL, /* default security attributes */ TRUE, /* manual-reset event */ TRUE, /* signaled */ NULL); /* no name */ if (port->oWriteReady.hEvent == NULL) { sim_error_serial ("CreateEvent", /* function failed; report unexpected error */ (int) GetLastError ()); sim_close_os_serial (port); /* close port */ return INVALID_HANDLE; /* and indicate failure to caller */ } port->oReadSync.hEvent = CreateEvent(NULL, /* default security attributes */ TRUE, /* manual-reset event */ FALSE, /* not signaled */ NULL); /* no name */ if (port->oReadSync.hEvent == NULL) { sim_error_serial ("CreateEvent", /* function failed; report unexpected error */ (int) GetLastError ()); sim_close_os_serial (port); /* close port */ return INVALID_HANDLE; /* and indicate failure to caller */ } port->oWriteSync.hEvent = CreateEvent(NULL, /* default security attributes */ TRUE, /* manual-reset event */ FALSE, /* not signaled */ NULL); /* no name */ if (port->oWriteSync.hEvent == NULL) { sim_error_serial ("CreateEvent", /* function failed; report unexpected error */ (int) GetLastError ()); sim_close_os_serial (port); /* close port */ return INVALID_HANDLE; /* and indicate failure to caller */ } if (!SetCommMask (port->hPort, EV_TXEMPTY)) { sim_error_serial ("SetCommMask", /* function failed; report unexpected error */ (int) GetLastError ()); sim_close_os_serial (port); /* close port */ return INVALID_HANDLE; /* and indicate failure to caller */ } return port; /* return port handle on success */ } /* Configure a serial port. Port parameters are configured as specified in the "config" structure. If "config" contains an invalid configuration value, or if the host system rejects the configuration (e.g., by requesting an unsupported combination of character size and stop bits), SCPE_ARG is returned to the caller. If an unexpected error occurs, SCPE_IOERR is returned. If the configuration succeeds, SCPE_OK is returned. Implementation notes: 1. We do not enable input parity checking, as the multiplexer library has no way of communicating parity errors back to the target simulator. 2. A zero value for the "stopbits" field of the "config" structure implies 1.5 stop bits. */ static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) { static const struct { char parity; BYTE parity_code; } parity_map [] = { { 'E', EVENPARITY }, { 'M', MARKPARITY }, { 'N', NOPARITY }, { 'O', ODDPARITY }, { 'S', SPACEPARITY } }; static const int32 parity_count = sizeof (parity_map) / sizeof (parity_map [0]); DCB dcb; DWORD error; int32 i; if (!GetCommState (port->hPort, &dcb)) { /* get the current comm parameters */ sim_error_serial ("GetCommState", /* function failed; report unexpected error */ (int) GetLastError ()); return SCPE_IOERR; /* return failure status */ } dcb.BaudRate = config.baudrate; /* assign baud rate */ if (config.charsize >= 5 && config.charsize <= 8) /* character size OK? */ dcb.ByteSize = (BYTE)config.charsize; /* assign character size */ else return SCPE_ARG; /* not a valid size */ for (i = 0; i < parity_count; i++) /* assign parity */ if (config.parity == parity_map [i].parity) { /* match mapping value? */ dcb.Parity = parity_map [i].parity_code; /* assign corresponding code */ break; } if (i == parity_count) /* parity assigned? */ return SCPE_ARG; /* not a valid parity specifier */ if (config.stopbits == 1) /* assign stop bits */ dcb.StopBits = ONESTOPBIT; else if (config.stopbits == 2) dcb.StopBits = TWOSTOPBITS; else if (config.stopbits == 0) /* 0 implies 1.5 stop bits */ dcb.StopBits = ONE5STOPBITS; else return SCPE_ARG; /* not a valid number of stop bits */ if (!SetCommState (port->hPort, &dcb)) { /* set the configuration */ error = GetLastError (); /* check for error */ if (error == ERROR_INVALID_PARAMETER) /* invalid configuration? */ return SCPE_ARG; /* report as argument error */ sim_error_serial ("SetCommState", (int) error); /* function failed; report unexpected error */ return SCPE_IOERR; /* return failure status */ } return SCPE_OK; /* return success status */ } /* Control a serial port. The DTR and RTS line of the serial port is set or cleared as indicated in the respective bits_to_set or bits_to_clear parameters. If the incoming_bits parameter is not NULL, then the modem status bits DCD, RNG, DSR and CTS are returned. If unreasonable or nonsense bits_to_set or bits_to_clear bits are specified, then the return status is SCPE_ARG; If an error occurs, SCPE_IOERR is returned. */ t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits) { if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits */ (bits_to_clear & ~(TMXR_MDM_OUTGOING)) || (bits_to_set & bits_to_clear)) /* and can't set and clear the same bits */ return SCPE_ARG; if (bits_to_set&TMXR_MDM_DTR) if (!EscapeCommFunction (port->hPort, SETDTR)) { sim_error_serial ("EscapeCommFunction", (int) GetLastError ()); return SCPE_IOERR; } if (bits_to_clear&TMXR_MDM_DTR) if (!EscapeCommFunction (port->hPort, CLRDTR)) { sim_error_serial ("EscapeCommFunction", (int) GetLastError ()); return SCPE_IOERR; } if (bits_to_set&TMXR_MDM_RTS) if (!EscapeCommFunction (port->hPort, SETRTS)) { sim_error_serial ("EscapeCommFunction", (int) GetLastError ()); return SCPE_IOERR; } if (bits_to_clear&TMXR_MDM_RTS) if (!EscapeCommFunction (port->hPort, CLRRTS)) { sim_error_serial ("EscapeCommFunction", (int) GetLastError ()); return SCPE_IOERR; } if (incoming_bits) { DWORD ModemStat; if (GetCommModemStatus (port->hPort, &ModemStat)) { sim_error_serial ("GetCommModemStatus", (int) GetLastError ()); return SCPE_IOERR; } *incoming_bits = ((ModemStat&MS_CTS_ON) ? TMXR_MDM_CTS : 0) | ((ModemStat&MS_DSR_ON) ? TMXR_MDM_DSR : 0) | ((ModemStat&MS_RING_ON) ? TMXR_MDM_RNG : 0) | ((ModemStat&MS_RLSD_ON) ? TMXR_MDM_DCD : 0); } return SCPE_OK; } /* Read from a serial port. The port is checked for available characters. If any are present, they are copied to the passed buffer, and the count of characters is returned. If no characters are available, 0 is returned. If an error occurs, -1 is returned. If a BREAK is detected on the communications line, the corresponding flag in the "brk" array is set. Implementation notes: 1. The "ClearCommError" function will set the CE_BREAK flag in the returned errors value if a BREAK has occurred. However, we do not know where in the serial stream it happened, as CE_BREAK isn't associated with a specific character. Because the "brk" array does want a flag associated with a specific character, we guess at the proper location by setting the "brk" entry corresponding to the first NUL in the character stream. If no NUL is present, then the "brk" entry associated with the first character is set. */ int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk) { DWORD read; DWORD commerrors; COMSTAT cs; char *bptr; if (!ClearCommError (port->hPort, &commerrors, &cs)) { /* get the comm error flags */ sim_error_serial ("ClearCommError", /* function failed; report unexpected error */ (int) GetLastError ()); return -1; /* return failure to caller */ } if (!ReadFile (port->hPort, (LPVOID) buffer, /* read any available characters */ (DWORD) count, &read, &port->oReadSync)) { sim_error_serial ("ReadFile", /* function failed; report unexpected error */ (int) GetLastError ()); return -1; /* return failure to caller */ } if (commerrors & CE_BREAK) { /* was a BREAK detected? */ bptr = (char *) memchr (buffer, 0, read); /* search for the first NUL in the buffer */ if (bptr) /* was one found? */ brk = brk + (bptr - buffer); /* calculate corresponding position */ *brk = 1; /* set the BREAK flag */ } return read; /* return the number of characters read */ } /* Write to a serial port. "Count" characters are written from "buffer" to the serial port. The actual number of characters written to the port is returned. If an error occurred on writing, -1 is returned. */ int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count) { if (WaitForSingleObject (port->oWriteReady.hEvent, 0) == WAIT_TIMEOUT) return 0; if ((!WriteFile (port->hPort, (LPVOID) buffer, /* write the buffer to the serial port */ (DWORD) count, NULL, &port->oWriteSync)) && (GetLastError () != ERROR_IO_PENDING)) { sim_error_serial ("WriteFile", /* function failed; report unexpected error */ (int) GetLastError ()); return -1; /* return failure to caller */ } if ((!WaitCommEvent (port->hPort, &port->dwEvtMask, &port->oWriteReady)) && (GetLastError () != ERROR_IO_PENDING)) { sim_error_serial ("WaitCommEvent", /* function failed; report unexpected error */ (int) GetLastError ()); return -1; /* return failure to caller */ } return count; /* return number of characters written/queued */ } /* Close a serial port. The serial port is closed. Errors are ignored. */ static void sim_close_os_serial (SERHANDLE port) { if (port->oWriteReady.hEvent) CloseHandle (port->oWriteReady.hEvent); /* close the event handle */ if (port->oReadSync.hEvent) CloseHandle (port->oReadSync.hEvent); /* close the event handle */ if (port->oWriteSync.hEvent) CloseHandle (port->oWriteSync.hEvent); /* close the event handle */ if (port->hPort) CloseHandle (port->hPort); /* close the port */ free (port); } #elif defined (__unix__) || defined(__APPLE__) || defined(__hpux) struct SERPORT { int port; }; #if defined(__linux) || defined(__linux__) #include <dirent.h> #include <libgen.h> #include <unistd.h> #include <sys/stat.h> #endif /* __linux__ */ /* UNIX implementation */ /* Enumerate the available serial ports. The serial port names generated by attempting to open /dev/ttyS0 thru /dev/ttyS63 and /dev/ttyUSB0 thru /dev/ttyUSB63 and /dev/tty.serial0 thru /dev/tty.serial63. Ones we can open and are ttys (as determined by isatty()) are added to the list. The list is sorted alphabetically by device name. */ static int sim_serial_os_devices (int max, SERIAL_LIST* list) { int i; int port; int ports = 0; memset(list, 0, max*sizeof(*list)); #if defined(__linux) || defined(__linux__) if (1) { struct dirent **namelist; struct stat st; i = scandir("/sys/class/tty/", &namelist, NULL, NULL); while (i--) { if (strcmp(namelist[i]->d_name, ".") && strcmp(namelist[i]->d_name, "..")) { char path[1024], devicepath[1024], driverpath[1024]; sprintf (path, "/sys/class/tty/%s", namelist[i]->d_name); sprintf (devicepath, "/sys/class/tty/%s/device", namelist[i]->d_name); sprintf (driverpath, "/sys/class/tty/%s/device/driver", namelist[i]->d_name); if ((lstat(devicepath, &st) == 0) && S_ISLNK(st.st_mode)) { char buffer[1024]; memset (buffer, 0, sizeof(buffer)); if (readlink(driverpath, buffer, sizeof(buffer)) > 0) { sprintf (list[ports].name, "/dev/%s", basename (path)); port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */ if (port != -1) { /* open OK? */ if (isatty (port)) /* is device a TTY? */ ++ports; close (port); } } } } free (namelist[i]); } free (namelist); } #elif defined(__hpux) for (i=0; (ports < max) && (i < 64); ++i) { sprintf (list[ports].name, "/dev/tty%dp%d", i/8, i%8); port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */ if (port != -1) { /* open OK? */ if (isatty (port)) /* is device a TTY? */ ++ports; close (port); } } #else /* Non Linux/HP-UX, just try some well known device names */ for (i=0; (ports < max) && (i < 64); ++i) { sprintf (list[ports].name, "/dev/ttyS%d", i); port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */ if (port != -1) { /* open OK? */ if (isatty (port)) /* is device a TTY? */ ++ports; close (port); } } for (i=0; (ports < max) && (i < 64); ++i) { sprintf (list[ports].name, "/dev/ttyUSB%d", i); port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */ if (port != -1) { /* open OK? */ if (isatty (port)) /* is device a TTY? */ ++ports; close (port); } } for (i=1; (ports < max) && (i < 64); ++i) { sprintf (list[ports].name, "/dev/tty.serial%d", i); port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */ if (port != -1) { /* open OK? */ if (isatty (port)) /* is device a TTY? */ ++ports; close (port); } } for (i=0; (ports < max) && (i < 64); ++i) { sprintf (list[ports].name, "/dev/tty%02d", i); port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */ if (port != -1) { /* open OK? */ if (isatty (port)) /* is device a TTY? */ ++ports; close (port); } } for (i=0; (ports < max) && (i < 8); ++i) { sprintf (list[ports].name, "/dev/ttyU%d", i); port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */ if (port != -1) { /* open OK? */ if (isatty (port)) /* is device a TTY? */ ++ports; close (port); } } #endif return ports; } /* Open a serial port. The serial port designated by "name" is opened, and the handle to the port is returned. If an error occurs, INVALID_HANDLE is returned instead. After opening, the port is configured to "raw" mode. Implementation notes: 1. We use a non-blocking open to allow for polling during reads. 2. There is no way to limit "open" just to serial ports, so we must check after the port is opened. We do this with a combination of "isatty" and "tcgetattr". 3. We configure with PARMRK set and IGNBRK and BRKINT cleared. This will mark a communication line BREAK condition in the input stream with the three-character sequence \377 \000 \000. This is detected during reading. */ static SERHANDLE sim_open_os_serial (char *name) { static const tcflag_t i_clear = IGNBRK | /* ignore BREAK */ BRKINT | /* signal on BREAK */ INPCK | /* enable parity checking */ ISTRIP | /* strip character to 7 bits */ INLCR | /* map NL to CR */ IGNCR | /* ignore CR */ ICRNL | /* map CR to NL */ IXON | /* enable XON/XOFF output control */ IXOFF; /* enable XON/XOFF input control */ static const tcflag_t i_set = PARMRK | /* mark parity errors and line breaks */ IGNPAR; /* ignore parity errors */ static const tcflag_t o_clear = OPOST; /* post-process output */ static const tcflag_t o_set = 0; static const tcflag_t c_clear = HUPCL; /* hang up line on last close */ static const tcflag_t c_set = CREAD | /* enable receiver */ CLOCAL; /* ignore modem status lines */ static const tcflag_t l_clear = ISIG | /* enable signals */ ICANON | /* canonical input */ ECHO | /* echo characters */ ECHOE | /* echo ERASE as an error correcting backspace */ ECHOK | /* echo KILL */ ECHONL | /* echo NL */ NOFLSH | /* disable flush after interrupt */ TOSTOP | /* send SIGTTOU for background output */ IEXTEN; /* enable extended functions */ static const tcflag_t l_set = 0; int port; SERHANDLE serport; struct termios tio; port = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */ if (port == -1) { /* open failed? */ if (errno != ENOENT && errno != EACCES) /* file not found or can't open? */ sim_error_serial ("open", errno); /* no, so report unexpected error */ return INVALID_HANDLE; /* indicate failure to caller */ } if (!isatty (port)) { /* is device a TTY? */ close (port); /* no, so close it */ return INVALID_HANDLE; /* and return failure to caller */ } if (tcgetattr (port, &tio)) { /* get the terminal attributes */ sim_error_serial ("tcgetattr", errno); /* function failed; report unexpected error */ close (port); /* close the port */ return INVALID_HANDLE; /* and return failure to caller */ } // which of these methods is best? #if 1 tio.c_iflag = (tio.c_iflag & ~i_clear) | i_set; /* configure the serial line for raw mode */ tio.c_oflag = (tio.c_oflag & ~o_clear) | o_set; tio.c_cflag = (tio.c_cflag & ~c_clear) | c_set; tio.c_lflag = (tio.c_lflag & ~l_clear) | l_set; #ifdef VMIN tio.c_cc[VMIN] = 1; #endif #ifdef VTIME tio.c_cc[VTIME] = 0; #endif #elif 0 tio.c_iflag &= ~(IGNBRK | BRKINT | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF); tio.c_iflag |= PARMRK | IGNPAR; tio.c_oflag &= ~(OPOST); tio.c_cflag &= ~(HUPCL); tio.c_cflag |= CREAD | CLOCAL; tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | NOFLSH | TOSTOP | IEXTEN); #elif 0 tio.c_iflag = PARMRK | IGNPAR; tio.c_oflag = 0; tio.c_cflag = tio.c_cflag | CLOCAL | CREAD; tio.c_lflag = 0; #endif if (tcsetattr (port, TCSANOW, &tio)) { /* set the terminal attributes */ sim_error_serial ("tcsetattr", errno); /* function failed; report unexpected error */ close (port); /* close the port */ return INVALID_HANDLE; /* and return failure to caller */ } serport = (SERHANDLE)calloc (1, sizeof(*serport)); serport->port = port; return serport; /* return port fd for success */ } /* Configure a serial port. Port parameters are configured as specified in the "config" structure. If "config" contains an invalid configuration value, or if the host system rejects the configuration (e.g., by requesting an unsupported combination of character size and stop bits), SCPE_ARG is returned to the caller. If an unexpected error occurs, SCPE_IOERR is returned. If the configuration succeeds, SCPE_OK is returned. Implementation notes: 1. 1.5 stop bits is not a supported configuration. */ static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) { struct termios tio; int32 i; static const struct { uint32 rate; speed_t rate_code; } baud_map [] = { { 50, B50 }, { 75, B75 }, { 110, B110 }, { 134, B134 }, { 150, B150 }, { 200, B200 }, { 300, B300 }, { 600, B600 }, { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, { 4800, B4800 }, { 9600, B9600 }, { 19200, B19200 }, { 38400, B38400 }, { 57600, B57600 }, { 115200, B115200 } }; static const int32 baud_count = sizeof (baud_map) / sizeof (baud_map [0]); static const tcflag_t charsize_map [4] = { CS5, CS6, CS7, CS8 }; if (tcgetattr (port->port, &tio)) { /* get the current configuration */ sim_error_serial ("tcgetattr", errno); /* function failed; report unexpected error */ return SCPE_IOERR; /* return failure status */ } for (i = 0; i < baud_count; i++) /* assign baud rate */ if (config.baudrate == baud_map [i].rate) { /* match mapping value? */ cfsetispeed(&tio, baud_map [i].rate_code); /* set input rate */ cfsetospeed(&tio, baud_map [i].rate_code); /* set output rate */ break; } if (i == baud_count) /* baud rate assigned? */ return SCPE_ARG; /* invalid rate specified */ if ((config.charsize >= 5) && (config.charsize <= 8)) /* character size OK? */ tio.c_cflag = (tio.c_cflag & ~CSIZE) | /* replace character size code */ charsize_map [config.charsize - 5]; else return SCPE_ARG; /* not a valid size */ switch (config.parity) { /* assign parity */ case 'E': tio.c_cflag = (tio.c_cflag & ~PARODD) | PARENB; /* set for even parity */ break; case 'N': tio.c_cflag = tio.c_cflag & ~PARENB; /* set for no parity */ break; case 'O': tio.c_cflag = tio.c_cflag | PARODD | PARENB; /* set for odd parity */ break; default: return SCPE_ARG; /* not a valid parity specifier */ } if (config.stopbits == 1) /* one stop bit? */ tio.c_cflag = tio.c_cflag & ~CSTOPB; /* clear two-bits flag */ else if (config.stopbits == 2) /* two stop bits? */ tio.c_cflag = tio.c_cflag | CSTOPB; /* set two-bits flag */ else /* some other number? */ return SCPE_ARG; /* not a valid number of stop bits */ if (tcsetattr (port->port, TCSAFLUSH, &tio)) { /* set the new configuration */ sim_error_serial ("tcsetattr", errno); /* function failed; report unexpected error */ return SCPE_IERR; /* return failure status */ } return SCPE_OK; /* configuration set successfully */ } /* Control a serial port. The DTR and RTS line of the serial port is set or cleared as indicated in the respective bits_to_set or bits_to_clear parameters. If the incoming_bits parameter is not NULL, then the modem status bits DCD, RNG, DSR and CTS are returned. If unreasonable or nonsense bits_to_set or bits_to_clear bits are specified, then the return status is SCPE_ARG; If an error occurs, SCPE_IOERR is returned. */ t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits) { int bits; if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits */ (bits_to_clear & ~(TMXR_MDM_OUTGOING)) || (bits_to_set & bits_to_clear)) /* and can't set and clear the same bits */ return SCPE_ARG; if (bits_to_set) { bits = ((bits_to_set&TMXR_MDM_DTR) ? TIOCM_DTR : 0) | ((bits_to_set&TMXR_MDM_RTS) ? TIOCM_RTS : 0); if (ioctl (port->port, TIOCMBIS, &bits)) { /* set the desired bits */ sim_error_serial ("ioctl", errno); /* report unexpected error */ return SCPE_IOERR; /* return failure status */ } } if (bits_to_clear) { bits = ((bits_to_clear&TMXR_MDM_DTR) ? TIOCM_DTR : 0) | ((bits_to_clear&TMXR_MDM_RTS) ? TIOCM_RTS : 0); if (ioctl (port->port, TIOCMBIC, &bits)) { /* clear the desired bits */ sim_error_serial ("ioctl", errno); /* report unexpected error */ return SCPE_IOERR; /* return failure status */ } } if (incoming_bits) { if (ioctl (port->port, TIOCMGET, &bits)) { /* get the modem bits */ sim_error_serial ("ioctl", errno); /* report unexpected error */ return SCPE_IOERR; /* return failure status */ } *incoming_bits = ((bits&TIOCM_CTS) ? TMXR_MDM_CTS : 0) | ((bits&TIOCM_DSR) ? TMXR_MDM_DSR : 0) | ((bits&TIOCM_RNG) ? TMXR_MDM_RNG : 0) | ((bits&TIOCM_CAR) ? TMXR_MDM_DCD : 0); } return SCPE_OK; } /* Read from a serial port. The port is checked for available characters. If any are present, they are copied to the passed buffer, and the count of characters is returned. If no characters are available, 0 is returned. If an error occurs, -1 is returned. If a BREAK is detected on the communications line, the corresponding flag in the "brk" array is set. Implementation notes: 1. A character with a framing or parity error is indicated in the input stream by the three-character sequence \377 \000 \ccc, where "ccc" is the bad character. A communications line BREAK is indicated by the sequence \377 \000 \000. A received \377 character is indicated by the two-character sequence \377 \377. If we find any of these sequences, they are replaced by the single intended character by sliding the succeeding characters backward by one or two positions. If a BREAK sequence was encountered, the corresponding location in the "brk" array is determined, and the flag is set. Note that there may be multiple sequences in the buffer. */ int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk) { int read_count; char *bptr, *cptr; int32 remaining; read_count = read (port->port, (void *) buffer, (size_t) count);/* read from the serial port */ if (read_count == -1) /* read error? */ if (errno == EAGAIN) /* no characters available? */ return 0; /* return 0 to indicate */ else /* some other problem */ sim_error_serial ("read", errno); /* report unexpected error */ else { /* read succeeded */ cptr = buffer; /* point at start of buffer */ remaining = read_count - 1; /* stop search one char from end of string */ while (remaining > 0 && /* still characters to search? */ (bptr = (char*)memchr (cptr, '\377', remaining))) {/* search for start of PARMRK sequence */ remaining = remaining - (bptr - cptr) - 1; /* calc characters remaining */ if (*(bptr + 1) == '\377') { /* is it a \377 \377 sequence? */ memmove (bptr + 1, bptr + 2, remaining); /* slide string backward to leave first \377 */ remaining = remaining - 1; /* drop remaining count */ read_count = read_count - 1; /* and read count by char eliminated */ } else if (remaining > 0 && *(bptr + 1) == '\0') { /* is it a \377 \000 \ccc sequence? */ memmove (bptr, bptr + 2, remaining); /* slide string backward to leave \ccc */ remaining = remaining - 2; /* drop remaining count */ read_count = read_count - 2; /* and read count by chars eliminated */ if (*bptr == '\0') /* is it a BREAK sequence? */ *(brk + (bptr - buffer)) = 1; /* set corresponding BREAK flag */ } cptr = bptr + 1; /* point at remainder of string */ } } return (int32) read_count; /* return the number of characters read */ } /* Write to a serial port. "Count" characters are written from "buffer" to the serial port. The actual number of characters written to the port is returned. If an error occurred on writing, -1 is returned. */ int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count) { int written; written = write (port->port, (void *) buffer, (size_t) count);/* write the buffer to the serial port */ if (written == -1) { if (errno == EWOULDBLOCK) written = 0; /* not an error, but nothing written */ #if defined(EAGAIN) else if (errno == EAGAIN) written = 0; /* not an error, but nothing written */ #endif else /* unexpected error? */ sim_error_serial ("write", errno); /* report it */ } return (int32) written; /* return number of characters written */ } /* Close a serial port. The serial port is closed. Errors are ignored. */ static void sim_close_os_serial (SERHANDLE port) { close (port->port); /* close the port */ free (port); } #elif defined (VMS) /* VMS implementation */ #if defined(__VAX) #define sys$assign SYS$ASSIGN #define sys$qiow SYS$QIOW #define sys$dassgn SYS$DASSGN #define sys$device_scan SYS$DEVICE_SCAN #define sys$getdviw SYS$GETDVIW #endif #include <descrip.h> #include <ttdef.h> #include <tt2def.h> #include <iodef.h> #include <ssdef.h> #include <dcdef.h> #include <dvsdef.h> #include <dvidef.h> #include <starlet.h> #include <unistd.h> typedef struct { unsigned short sense_count; unsigned char sense_first_char; unsigned char sense_reserved; unsigned int stat; unsigned int stat2; } SENSE_BUF; typedef struct { unsigned short status; unsigned short count; unsigned int dev_status; } IOSB; typedef struct { unsigned short buffer_size; unsigned short item_code; void *buffer_address; void *return_length_address; } ITEM; struct SERPORT { uint32 port; IOSB write_iosb; }; /* Enumerate the available serial ports. The serial port names generated by attempting to open /dev/ttyS0 thru /dev/ttyS53 and /dev/ttyUSB0 thru /dev/ttyUSB0. Ones we can open and are ttys (as determined by isatty()) are added to the list. The list is sorted alphabetically by device name. */ static int sim_serial_os_devices (int max, SERIAL_LIST* list) { $DESCRIPTOR (wild, "*"); char devstr[sizeof(list[0].name)]; $DESCRIPTOR (device, devstr); int ports; IOSB iosb; uint32 status; uint32 devsts; #define UCB$M_TEMPLATE 0x2000 /* Device is a template device */ #define UCB$M_ONLINE 0x0010 /* Device is online */ uint32 devtype; uint32 devdepend; #define DEV$M_RTM 0x20000000 uint32 devnamlen = 0; t_bool done = FALSE; uint32 context[2]; uint32 devclass = DC$_TERM; /* Only interested in terminal devices */ ITEM select_items[] = { {sizeof (devclass), DVS$_DEVCLASS, &devclass, NULL}, { 0, 0, NULL, NULL}}; ITEM valid_items[] = { { sizeof (devsts), DVI$_STS, &devsts, NULL}, { sizeof(devstr), DVI$_DEVNAM, devstr, &devnamlen}, { sizeof(devtype), DVI$_DEVTYPE, &devtype, NULL}, { sizeof(devdepend), DVI$_DEVDEPEND, &devdepend, NULL}, { 0, 0, NULL, NULL}}; memset(context, 0, sizeof(context)); memset(devstr, 0, sizeof(devstr)); memset(list, 0, max*sizeof(*list)); for (ports=0; (ports < max); ++ports) { device.dsc$w_length = sizeof (devstr) - 1; status = sys$device_scan (&device, &device.dsc$w_length, &wild, select_items, &context); switch (status) { case SS$_NOSUCHDEV: case SS$_NOMOREDEV: done = TRUE; break; default: if (0 == (status&1)) done = TRUE; else { status = sys$getdviw (0, 0, &device, valid_items, &iosb, NULL, 0, NULL); if (status == SS$_NORMAL) status = iosb.status; if (status != SS$_NORMAL) { done = TRUE; break; } device.dsc$w_length = devnamlen; if ((0 == (devsts & UCB$M_TEMPLATE)) && (0 != (devsts & UCB$M_ONLINE)) && (0 == (devdepend & DEV$M_RTM))) { devstr[device.dsc$w_length] = '\0'; strcpy (list[ports].name, devstr); while (list[ports].name[0] == '_') strcpy (list[ports].name, list[ports].name+1); } else --ports; } break; } if (done) break; } return ports; } /* Open a serial port. The serial port designated by "name" is opened, and the handle to the port is returned. If an error occurs, INVALID_HANDLE is returned instead. After opening, the port is configured to "raw" mode. Implementation notes: 1. We use a non-blocking open to allow for polling during reads. 2. There is no way to limit "open" just to serial ports, so we must check after the port is opened. We do this with sys$getdvi. */ static SERHANDLE sim_open_os_serial (char *name) { uint32 status; uint32 chan = 0; IOSB iosb; $DESCRIPTOR (devnam, name); uint32 devclass; ITEM items[] = { {sizeof (devclass), DVI$_DEVCLASS, &devclass, NULL}, { 0, 0, NULL, NULL}}; SENSE_BUF start_mode = { 0 }; SENSE_BUF run_mode = { 0 }; SERHANDLE port; devnam.dsc$w_length = strlen (devnam.dsc$a_pointer); status = sys$assign (&devnam, &chan, 0, 0); if (status != SS$_NORMAL) return INVALID_HANDLE; status = sys$getdviw (0, chan, NULL, items, &iosb, NULL, 0, NULL); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL) || (devclass != DC$_TERM)) { sys$dassgn (chan); return INVALID_HANDLE; } status = sys$qiow (0, chan, IO$_SENSEMODE, &iosb, 0, 0, &start_mode, sizeof (start_mode), 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) { sys$dassgn (chan); return INVALID_HANDLE; } run_mode = start_mode; run_mode.stat = start_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC | TT$M_HALFDUP); run_mode.stat2 = start_mode.stat2 | TT2$M_PASTHRU; status = sys$qiow (0, chan, IO$_SETMODE, &iosb, 0, 0, &run_mode, sizeof (run_mode), 0, 0, 0, 0); if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) { sys$dassgn (chan); return INVALID_HANDLE; } port = (SERHANDLE)calloc (1, sizeof(*port)); port->port = chan; port->write_iosb.status = 1; return port; /* return channel for success */ } /* Configure a serial port. Port parameters are configured as specified in the "config" structure. If "config" contains an invalid configuration value, or if the host system rejects the configuration (e.g., by requesting an unsupported combination of character size and stop bits), SCPE_ARG is returned to the caller. If an unexpected error occurs, SCPE_IOERR is returned. If the configuration succeeds, SCPE_OK is returned. Implementation notes: 1. 1.5 stop bits is not a supported configuration. */ static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) { int32 i; SENSE_BUF sense; uint32 status, speed, parity, charsize, stopbits; IOSB iosb; static const struct { uint32 rate; uint32 rate_code; } baud_map [] = { { 50, TT$C_BAUD_50 }, { 75, TT$C_BAUD_75 }, { 110, TT$C_BAUD_110 }, { 134, TT$C_BAUD_134 }, { 150, TT$C_BAUD_150 }, { 300, TT$C_BAUD_300 }, { 600, TT$C_BAUD_600 }, { 1200, TT$C_BAUD_1200 }, { 1800, TT$C_BAUD_1800 }, { 2000, TT$C_BAUD_2000 }, { 2400, TT$C_BAUD_2400 }, { 3600, TT$C_BAUD_3600 }, { 4800, TT$C_BAUD_4800 }, { 7200, TT$C_BAUD_7200 }, { 9600, TT$C_BAUD_9600 }, { 19200, TT$C_BAUD_19200 }, { 38400, TT$C_BAUD_38400 }, { 57600, TT$C_BAUD_57600 }, { 76800, TT$C_BAUD_76800 }, { 115200, TT$C_BAUD_115200} }; static const int32 baud_count = sizeof (baud_map) / sizeof (baud_map [0]); status = sys$qiow (0, port->port, IO$_SENSEMODE, &iosb, 0, 0, &sense, sizeof(sense), 0, NULL, 0, 0); if (status == SS$_NORMAL) status = iosb.status; if (status != SS$_NORMAL) { sim_error_serial ("config-SENSEMODE", status); /* report unexpected error */ return SCPE_IOERR; } for (i = 0; i < baud_count; i++) /* assign baud rate */ if (config.baudrate == baud_map [i].rate) { /* match mapping value? */ speed = baud_map [i].rate_code << 8 | /* set input rate */ baud_map [i].rate_code; /* set output rate */ break; } if (i == baud_count) /* baud rate assigned? */ return SCPE_ARG; /* invalid rate specified */ if (config.charsize >= 5 && config.charsize <= 8) /* character size OK? */ charsize = TT$M_ALTFRAME | config.charsize; /* set character size */ else return SCPE_ARG; /* not a valid size */ switch (config.parity) { /* assign parity */ case 'E': parity = TT$M_ALTRPAR | TT$M_PARITY; /* set for even parity */ break; case 'N': parity = TT$M_ALTRPAR; /* set for no parity */ break; case 'O': parity = TT$M_ALTRPAR | TT$M_PARITY | TT$M_ODD; /* set for odd parity */ break; default: return SCPE_ARG; /* not a valid parity specifier */ } switch (config.stopbits) { case 1: /* one stop bit? */ stopbits = 0; break; case 2: /* two stop bits? */ if ((speed & 0xff) <= TT$C_BAUD_150) { /* Only valid for */ stopbits = TT$M_TWOSTOP; /* speeds 150baud or less */ break; } default: return SCPE_ARG; /* not a valid number of stop bits */ } status = sys$qiow (0, port->port, IO$_SETMODE, &iosb, 0, 0, &sense, sizeof (sense), speed, 0, parity | charsize | stopbits, 0); if (status == SS$_NORMAL) status = iosb.status; if (status != SS$_NORMAL) { sim_error_serial ("config-SETMODE", status); /* report unexpected error */ return SCPE_IOERR; } return SCPE_OK; /* configuration set successfully */ } /* Control a serial port. The DTR and RTS line of the serial port is set or cleared as indicated in the respective bits_to_set or bits_to_clear parameters. If the incoming_bits parameter is not NULL, then the modem status bits DCD, RNG, DSR and CTS are returned. If unreasonable or nonsense bits_to_set or bits_to_clear bits are specified, then the return status is SCPE_ARG; If an error occurs, SCPE_IOERR is returned. */ t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits) { uint32 status; IOSB iosb; uint32 bits[2] = {0, 0}; if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits */ (bits_to_clear & ~(TMXR_MDM_OUTGOING)) || (bits_to_set & bits_to_clear)) /* and can't set and clear the same bits */ return SCPE_ARG; if (bits_to_set) bits[0] |= (((bits_to_set&TMXR_MDM_DTR) ? TT$M_DS_DTR : 0) | ((bits_to_set&TMXR_MDM_RTS) ? TT$M_DS_RTS : 0)) << 16; if (bits_to_clear) bits[0] |= (((bits_to_clear&TMXR_MDM_DTR) ? TT$M_DS_DTR : 0) | ((bits_to_clear&TMXR_MDM_RTS) ? TT$M_DS_RTS : 0)) << 24; if (bits_to_set || bits_to_clear) { status = sys$qiow (0, port->port, IO$_SETMODE|IO$M_SET_MODEM|IO$M_MAINT, &iosb, 0, 0, bits, 0, 0, 0, 0, 0); if (status == SS$_NORMAL) status = iosb.status; if (status != SS$_NORMAL) { sim_error_serial ("control-SETMODE", status); /* report unexpected error */ return SCPE_IOERR; } } if (incoming_bits) { uint32 modem; status = sys$qiow (0, port->port, IO$_SENSEMODE|IO$M_RD_MODEM, &iosb, 0, 0, bits, 0, 0, 0, 0, 0); if (status == SS$_NORMAL) status = iosb.status; if (status != SS$_NORMAL) { sim_error_serial ("control-SENSEMODE", status); /* report unexpected error */ return SCPE_IOERR; } modem = bits[0] >> 16; *incoming_bits = ((modem&TT$M_DS_CTS) ? TMXR_MDM_CTS : 0) | ((modem&TT$M_DS_DSR) ? TMXR_MDM_DSR : 0) | ((modem&TT$M_DS_RING) ? TMXR_MDM_RNG : 0) | ((modem&TT$M_DS_CARRIER) ? TMXR_MDM_DCD : 0); } return SCPE_OK; } /* Read from a serial port. The port is checked for available characters. If any are present, they are copied to the passed buffer, and the count of characters is returned. If no characters are available, 0 is returned. If an error occurs, -1 is returned. If a BREAK is detected on the communications line, the corresponding flag in the "brk" array is set. Implementation notes: 1. A character with a framing or parity error is indicated in the input stream by the three-character sequence \377 \000 \ccc, where "ccc" is the bad character. A communications line BREAK is indicated by the sequence \377 \000 \000. A received \377 character is indicated by the two-character sequence \377 \377. If we find any of these sequences, they are replaced by the single intended character by sliding the succeeding characters backward by one or two positions. If a BREAK sequence was encountered, the corresponding location in the "brk" array is determined, and the flag is set. Note that there may be multiple sequences in the buffer. */ int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk) { int read_count = 0; uint32 status; static uint32 term[2] = {0, 0}; unsigned char buf[4]; IOSB iosb; SENSE_BUF sense; status = sys$qiow (0, port->port, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb, 0, 0, &sense, 8, 0, term, 0, 0); if (status == SS$_NORMAL) status = iosb.status; if (status != SS$_NORMAL) { sim_error_serial ("read", status); /* report unexpected error */ return -1; } if (sense.sense_count == 0) /* no characters available? */ return 0; /* return 0 to indicate */ status = sys$qiow (0, port->port, IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, &iosb, 0, 0, buffer, (count < sense.sense_count) ? count : sense.sense_count, 0, term, 0, 0); if (status == SS$_NORMAL) status = iosb.status; if (status != SS$_NORMAL) { sim_error_serial ("read", status); /* report unexpected error */ return -1; } return (int32)iosb.count; /* return the number of characters read */ } /* Write to a serial port. "Count" characters are written from "buffer" to the serial port. The actual number of characters written to the port is returned. If an error occurred on writing, -1 is returned. */ int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count) { uint32 status; if (port->write_iosb.status == 0) /* Prior write not done yet? */ return 0; status = sys$qio (0, port->port, IO$_WRITELBLK | IO$M_NOFORMAT, &port->write_iosb, 0, 0, buffer, count, 0, 0, 0, 0); if (status != SS$_NORMAL) { sim_error_serial ("write", status); /* report unexpected error */ return -1; } return (int32)count; /* return number of characters written */ } /* Close a serial port. The serial port is closed. Errors are ignored. */ static void sim_close_os_serial (SERHANDLE port) { sys$dassgn (port->port); /* close the port */ free (port); } #else /* Non-implemented stubs */ /* Enumerate the available serial ports. */ static int sim_serial_os_devices (int max, SERIAL_LIST* list) { return 0; } /* Open a serial port */ static SERHANDLE sim_open_os_serial (char *name) { return INVALID_HANDLE; } /* Configure a serial port */ static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) { return SCPE_IERR; } /* Control a serial port */ t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits) { return SCPE_NOFNC; } /* Read from a serial port */ int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk) { return -1; } /* Write to a serial port */ int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count) { return -1; } /* Close a serial port */ static void sim_close_os_serial (SERHANDLE port) { } #endif /* end else !implemented */ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | /* sim_serial.h: OS-dependent serial port routines header file Copyright (c) 2008, J. David Bryan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the author shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. 07-Oct-08 JDB [serial] Created file */ #ifndef SIM_SERIAL_H_ #define SIM_SERIAL_H_ 0 #ifdef __cplusplus extern "C" { #endif #ifndef SIMH_SERHANDLE_DEFINED #define SIMH_SERHANDLE_DEFINED 0 typedef struct SERPORT *SERHANDLE; #endif /* SERHANDLE_DEFINED */ #if defined (_WIN32) /* Windows definitions */ /* We need the basic Win32 definitions, but including "windows.h" also includes "winsock.h" as well. However, "sim_sock.h" explicitly includes "winsock2.h," and this file cannot coexist with "winsock.h". So we set a guard definition that prevents "winsock.h" from being included. */ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include <windows.h> #if !defined(INVALID_HANDLE) #define INVALID_HANDLE (SERHANDLE)INVALID_HANDLE_VALUE #endif /* !defined(INVALID_HANDLE) */ #elif defined (__unix__) || defined (__APPLE__) || defined (__hpux) /* UNIX definitions */ #include <fcntl.h> #ifdef __hpux #include <sys/modem.h> #endif #include <termios.h> #include <unistd.h> #include <sys/ioctl.h> #if !defined(INVALID_HANDLE) #define INVALID_HANDLE ((SERHANDLE)(void *)-1) #endif /* !defined(INVALID_HANDLE) */ #elif defined (VMS) /* VMS definitions */ #if !defined(INVALID_HANDLE) #define INVALID_HANDLE ((SERHANDLE)(void *)-1) #endif /* !defined(INVALID_HANDLE) */ #else /* Non-implemented definitions */ #if !defined(INVALID_HANDLE) #define INVALID_HANDLE ((SERHANDLE)(void *)-1) #endif /* !defined(INVALID_HANDLE) */ #endif /* OS variants */ /* Common definitions */ /* Global routines */ #include "sim_tmxr.h" /* need TMLN definition and modem definitions */ extern SERHANDLE sim_open_serial (char *name, TMLN *lp, t_stat *status); extern t_stat sim_config_serial (SERHANDLE port, CONST char *config); extern t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits); extern int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk); extern int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count); extern void sim_close_serial (SERHANDLE port); extern t_stat sim_show_serial (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc); #ifdef __cplusplus } #endif #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 | /* sim_sock.c: OS-dependent socket routines Copyright (c) 2001-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 15-Oct-12 MP Added definitions needed to detect possible tcp connect failures 25-Sep-12 MP Reworked for RFC3493 interfaces supporting IPv6 and IPv4 22-Jun-10 RMS Fixed types in sim_accept_conn (from Mark Pizzolato) 19-Nov-05 RMS Added conditional for OpenBSD (from Federico G. Schwindt) 16-Aug-05 RMS Fixed spurious SIGPIPE signal error in Unix 14-Apr-05 RMS Added WSAEINPROGRESS test (from Tim Riker) 09-Jan-04 RMS Fixed typing problem in Alpha Unix (found by Tim Chapman) 17-Apr-03 RMS Fixed non-implemented version of sim_close_sock (found by Mark Pizzolato) 17-Dec-02 RMS Added sim_connect_socket, sim_create_socket 08-Oct-02 RMS Revised for .NET compatibility 22-Aug-02 RMS Changed calling sequence for sim_accept_conn 22-May-02 RMS Added OS2 EMX support from Holger Veit 06-Feb-02 RMS Added VMS support from Robert Alan Byer 16-Sep-01 RMS Added Macintosh support from Peter Schorn 02-Sep-01 RMS Fixed UNIX bugs found by Mirian Lennox and Tom Markson */ #ifdef __cplusplus extern "C" { #endif #include "sim_sock.h" #include <signal.h> #include <stdio.h> #include <stdlib.h> #if defined(AF_INET6) && defined(_WIN32) #include <ws2tcpip.h> #endif #ifdef HAVE_DLOPEN #include <dlfcn.h> #endif #ifndef WSAAPI #define WSAAPI #endif #if defined(SHUT_RDWR) && !defined(SD_BOTH) #define SD_BOTH SHUT_RDWR #endif #ifndef NI_MAXHOST #define NI_MAXHOST 1025 #endif /* OS dependent routines sim_master_sock create master socket sim_connect_sock connect a socket to a remote destination sim_connect_sock_ex connect a socket to a remote destination sim_accept_conn accept connection sim_read_sock read from socket sim_write_sock write from socket sim_close_sock close socket sim_setnonblock set socket non-blocking */ /* First, all the non-implemented versions */ #if defined (__OS2__) && !defined (__EMX__) void sim_init_sock (void) { } void sim_cleanup_sock (void) { } SOCKET sim_master_sock_ex (const char *hostport, int *parse_status, int opt_flags) { return INVALID_SOCKET; } SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, int opt_flags) { return INVALID_SOCKET; } SOCKET sim_accept_conn (SOCKET master, char **connectaddr); { return INVALID_SOCKET; } int sim_read_sock (SOCKET sock, char *buf, int nbytes) { return -1; } int sim_write_sock (SOCKET sock, char *msg, int nbytes) { return 0; } void sim_close_sock (SOCKET sock) { return; } #else /* endif unimpl */ /* UNIX, Win32, Macintosh, VMS, OS2 (Berkeley socket) routines */ static struct sock_errors { int value; const char *text; } sock_errors[] = { {WSAEWOULDBLOCK, "Operation would block"}, {WSAENAMETOOLONG, "File name too long"}, {WSAEINPROGRESS, "Operation now in progress "}, {WSAETIMEDOUT, "Connection timed out"}, {WSAEISCONN, "Transport endpoint is already connected"}, {WSAECONNRESET, "Connection reset by peer"}, {WSAECONNREFUSED, "Connection refused"}, {WSAECONNABORTED, "Connection aborted"}, {WSAEHOSTUNREACH, "No route to host"}, {WSAEADDRINUSE, "Address already in use"}, #if defined (WSAEAFNOSUPPORT) {WSAEAFNOSUPPORT, "Address family not supported by protocol"}, #endif {WSAEACCES, "Permission denied"}, {0, NULL} }; const char *sim_get_err_sock (const char *emsg) { int err = WSAGetLastError (); int i; static char err_buf[512]; for (i=0; (sock_errors[i].text) && (sock_errors[i].value != err); i++) ; if (sock_errors[i].value == err) sprintf (err_buf, "Sockets: %s error %d - %s\n", emsg, err, sock_errors[i].text); else #if defined(_WIN32) sprintf (err_buf, "Sockets: %s error %d\n", emsg, err); #else sprintf (err_buf, "Sockets: %s error %d - %s\n", emsg, err, strerror(err)); #endif return err_buf; } SOCKET sim_err_sock (SOCKET s, const char *emsg) { sim_printf ("%s", sim_get_err_sock (emsg)); if (s != INVALID_SOCKET) { int err = WSAGetLastError (); sim_close_sock (s); WSASetLastError (err); /* Retain Original socket error value */ } return INVALID_SOCKET; } typedef void (WSAAPI *freeaddrinfo_func) (struct addrinfo *ai); static freeaddrinfo_func p_freeaddrinfo; typedef int (WSAAPI *getaddrinfo_func) (const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **res); static getaddrinfo_func p_getaddrinfo; #if defined(VMS) typedef size_t socklen_t; #if !defined(EAI_OVERFLOW) #define EAI_OVERFLOW EAI_FAIL #endif #endif #if defined(__hpux) #if !defined(EAI_OVERFLOW) #define EAI_OVERFLOW EAI_FAIL #endif #endif typedef int (WSAAPI *getnameinfo_func) (const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags); static getnameinfo_func p_getnameinfo; static void WSAAPI s_freeaddrinfo (struct addrinfo *ai) { struct addrinfo *a, *an; for (a=ai; a != NULL; a=an) { an = a->ai_next; free (a->ai_canonname); free (a->ai_addr); free (a); } } static int WSAAPI s_getaddrinfo (const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **res) { struct hostent *he; struct servent *se = NULL; struct sockaddr_in *sin; struct addrinfo *result = NULL; struct addrinfo *ai, *lai = NULL; struct addrinfo dhints; struct in_addr ipaddr; struct in_addr *fixed[2]; struct in_addr **ips = NULL; struct in_addr **ip; const char *cname = NULL; int port = 0; // Validate parameters if ((hostname == NULL) && (service == NULL)) return EAI_NONAME; if (hints) { if ((hints->ai_family != PF_INET) && (hints->ai_family != PF_UNSPEC)) return EAI_FAMILY; switch (hints->ai_socktype) { default: return EAI_SOCKTYPE; case SOCK_DGRAM: case SOCK_STREAM: case 0: break; } } else { hints = &dhints; memset(&dhints, 0, sizeof(dhints)); dhints.ai_family = PF_UNSPEC; } if (service) { char *c; port = strtoul(service, &c, 10); if ((port == 0) || (*c != '\0')) { switch (hints->ai_socktype) { case SOCK_DGRAM: se = getservbyname(service, "udp"); break; case SOCK_STREAM: case 0: se = getservbyname(service, "tcp"); break; } if (NULL == se) return EAI_SERVICE; port = se->s_port; } } if (hostname) { if ((0xffffffff != (ipaddr.s_addr = inet_addr(hostname))) || (0 == strcmp("255.255.255.255", hostname))) { fixed[0] = &ipaddr; fixed[1] = NULL; } else { if ((0xffffffff != (ipaddr.s_addr = inet_addr(hostname))) || (0 == strcmp("255.255.255.255", hostname))) { fixed[0] = &ipaddr; fixed[1] = NULL; if ((hints->ai_flags & AI_CANONNAME) && !(hints->ai_flags & AI_NUMERICHOST)) { he = gethostbyaddr((char *)&ipaddr, 4, AF_INET); if (NULL != he) cname = he->h_name; else cname = hostname; } ips = fixed; } else { if (hints->ai_flags & AI_NUMERICHOST) return EAI_NONAME; he = gethostbyname(hostname); if (he) { ips = (struct in_addr **)he->h_addr_list; if (hints->ai_flags & AI_CANONNAME) cname = he->h_name; } else { switch (h_errno) { case HOST_NOT_FOUND: case NO_DATA: return EAI_NONAME; case TRY_AGAIN: return EAI_AGAIN; default: return EAI_FAIL; } } } } } else { if (hints->ai_flags & AI_PASSIVE) ipaddr.s_addr = htonl(INADDR_ANY); else ipaddr.s_addr = htonl(INADDR_LOOPBACK); fixed[0] = &ipaddr; fixed[1] = NULL; ips = fixed; } for (ip=ips; (ip != NULL) && (*ip != NULL); ++ip) { ai = (struct addrinfo *)calloc(1, sizeof(*ai)); if (NULL == ai) { s_freeaddrinfo(result); return EAI_MEMORY; } ai->ai_family = PF_INET; ai->ai_socktype = hints->ai_socktype; ai->ai_protocol = hints->ai_protocol; ai->ai_addr = NULL; ai->ai_addrlen = sizeof(struct sockaddr_in); ai->ai_canonname = NULL; ai->ai_next = NULL; ai->ai_addr = (struct sockaddr *)calloc(1, sizeof(struct sockaddr_in)); if (NULL == ai->ai_addr) { free(ai); s_freeaddrinfo(result); return EAI_MEMORY; } sin = (struct sockaddr_in *)ai->ai_addr; sin->sin_family = PF_INET; sin->sin_port = (unsigned short)port; memcpy(&sin->sin_addr, *ip, sizeof(sin->sin_addr)); if (NULL == result) result = ai; else lai->ai_next = ai; lai = ai; } if (cname) { result->ai_canonname = (char *)calloc(1, strlen(cname)+1); if (NULL == result->ai_canonname) { s_freeaddrinfo(result); return EAI_MEMORY; } strcpy(result->ai_canonname, cname); } *res = result; return 0; } #ifndef EAI_OVERFLOW #define EAI_OVERFLOW WSAENAMETOOLONG #endif static int WSAAPI s_getnameinfo (const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags) { struct hostent *he; struct servent *se = NULL; const struct sockaddr_in *sin = (const struct sockaddr_in *)sa; if (sin->sin_family != PF_INET) return EAI_FAMILY; if ((NULL == host) && (NULL == serv)) return EAI_NONAME; if ((serv) && (servlen > 0)) { if (flags & NI_NUMERICSERV) se = NULL; else if (flags & NI_DGRAM) se = getservbyport(sin->sin_port, "udp"); else se = getservbyport(sin->sin_port, "tcp"); if (se) { if (servlen <= strlen(se->s_name)) return EAI_OVERFLOW; strcpy(serv, se->s_name); } else { char buf[16]; sprintf(buf, "%d", ntohs(sin->sin_port)); if (servlen <= strlen(buf)) return EAI_OVERFLOW; strcpy(serv, buf); } } if ((host) && (hostlen > 0)) { if (flags & NI_NUMERICHOST) he = NULL; else he = gethostbyaddr((const char *)&sin->sin_addr, 4, AF_INET); if (he) { if (hostlen < strlen(he->h_name)+1) return EAI_OVERFLOW; strcpy(host, he->h_name); } else { if (flags & NI_NAMEREQD) return EAI_NONAME; if (hostlen < strlen(inet_ntoa(sin->sin_addr))+1) return EAI_OVERFLOW; strcpy(host, inet_ntoa(sin->sin_addr)); } } return 0; } #if defined(_WIN32) || defined(__CYGWIN__) #if !defined(IPV6_V6ONLY) /* Older XP environments may not define IPV6_V6ONLY */ #define IPV6_V6ONLY 27 /* Treat wildcard bind as AF_INET6-only. */ #endif /* Dynamic DLL load variables */ #ifdef _WIN32 static HINSTANCE hLib = 0; /* handle to DLL */ #else static void *hLib = NULL; /* handle to Library */ #endif static int lib_loaded = 0; /* 0=not loaded, 1=loaded, 2=library load failed, 3=Func load failed */ static const char* lib_name = "Ws2_32.dll"; /* load function pointer from DLL */ typedef int (*_func)(); static void load_function(const char* function, _func* func_ptr) { #ifdef _WIN32 *func_ptr = (_func)GetProcAddress(hLib, function); #else *func_ptr = (_func)dlsym(hLib, function); #endif if (*func_ptr == 0) { sim_printf ("Sockets: Failed to find function '%s' in %s\r\n", function, lib_name); lib_loaded = 3; } } /* load Ws2_32.dll as required */ int load_ws2(void) { switch(lib_loaded) { case 0: /* not loaded */ /* attempt to load DLL */ #ifdef _WIN32 hLib = LoadLibraryA(lib_name); #else hLib = dlopen(lib_name, RTLD_NOW); #endif if (hLib == 0) { /* failed to load DLL */ sim_printf ("Sockets: Failed to load %s\r\n", lib_name); lib_loaded = 2; break; } else { /* library loaded OK */ lib_loaded = 1; } /* load required functions; sets dll_load=3 on error */ load_function("getaddrinfo", (_func *) &p_getaddrinfo); load_function("getnameinfo", (_func *) &p_getnameinfo); load_function("freeaddrinfo", (_func *) &p_freeaddrinfo); if (lib_loaded != 1) { /* unsuccessful load, connect stubs */ p_getaddrinfo = (getaddrinfo_func)s_getaddrinfo; p_getnameinfo = (getnameinfo_func)s_getnameinfo; p_freeaddrinfo = (freeaddrinfo_func)s_freeaddrinfo; } break; default: /* loaded or failed */ break; } return (lib_loaded == 1) ? 1 : 0; } #endif /* OS independent routines sim_parse_addr parse a hostname/ipaddress from port and apply defaults and optionally validate an address match */ /* sim_parse_addr host:port Presumption is that the input, if it doesn't contain a ':' character is a port specifier. If the host field contains one or more colon characters (i.e. it is an IPv6 address), the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format) Inputs: cptr = pointer to input string default_host = optional pointer to default host if none specified host_len = length of host buffer default_port = optional pointer to default port if none specified port_len = length of port buffer validate_addr = optional name/addr which is checked to be equivalent to the host result of parsing the other input. This address would usually be returned by sim_accept_conn. Outputs: host = pointer to buffer for IP address (may be NULL), 0 = none port = pointer to buffer for IP port (may be NULL), 0 = none result = status (0 on complete success or -1 if parsing can't happen due to bad syntax, a value is out of range, a result can't fit into a result buffer, a service name doesn't exist, or a validation name doesn't match the parsed host) */ int sim_parse_addr (const char *cptr, char *host, size_t host_len, const char *default_host, char *port, size_t port_len, const char *default_port, const char *validate_addr) { char gbuf[CBUFSIZE], default_pbuf[CBUFSIZE]; const char *hostp; char *portp; char *endc; unsigned long portval; if ((host != NULL) && (host_len != 0)) memset (host, 0, host_len); if ((port != NULL) && (port_len != 0)) memset (port, 0, port_len); if ((cptr == NULL) || (*cptr == 0)) { if (((default_host == NULL) || (*default_host == 0)) || ((default_port == NULL) || (*default_port == 0))) return -1; if ((host == NULL) || (port == NULL)) return -1; /* no place */ if ((strlen(default_host) >= host_len) || (strlen(default_port) >= port_len)) return -1; /* no room */ strcpy (host, default_host); strcpy (port, default_port); return 0; } memset (default_pbuf, 0, sizeof(default_pbuf)); if (default_port) strncpy (default_pbuf, default_port, sizeof(default_pbuf)-1); gbuf[sizeof(gbuf)-1] = '\0'; strncpy (gbuf, cptr, sizeof(gbuf)-1); hostp = gbuf; /* default addr */ portp = NULL; if ((portp = strrchr (gbuf, ':')) && /* x:y? split */ (NULL == strchr (portp, ']'))) { *portp++ = 0; if (*portp == '\0') portp = default_pbuf; } else { /* No colon in input */ portp = gbuf; /* Input is the port specifier */ hostp = (const char *)default_host; /* host is defaulted if provided */ } if (portp != NULL) { portval = strtoul(portp, &endc, 10); if ((*endc == '\0') && ((portval == 0) || (portval > 65535))) return -1; /* numeric value too big */ if (*endc != '\0') { struct servent *se = getservbyname(portp, "tcp"); if (se == NULL) return -1; /* invalid service name */ } } if (port) /* port wanted? */ if (portp != NULL) { if (strlen(portp) >= port_len) return -1; /* no room */ else strcpy (port, portp); } if (hostp != NULL) { if (']' == hostp[strlen(hostp)-1]) { if ('[' != hostp[0]) return -1; /* invalid domain literal */ /* host may be the const default_host so move to temp buffer before modifying */ strncpy(gbuf, hostp+1, sizeof(gbuf)-1); /* remove brackets from domain literal host */ gbuf[strlen(gbuf)-1] = '\0'; hostp = gbuf; } } if (host) { /* host wanted? */ if (hostp != NULL) { if (strlen(hostp) >= host_len) return -1; /* no room */ else if (('\0' != hostp[0]) || (default_host == NULL)) strcpy (host, hostp); else if (strlen(default_host) >= host_len) return -1; /* no room */ else strcpy (host, default_host); } else { if (default_host) { if (strlen(default_host) >= host_len) return -1; /* no room */ else strcpy (host, default_host); } } } if (validate_addr) { struct addrinfo *ai_host, *ai_validate, *ai, *aiv; int status; if (hostp == NULL) return -1; if (p_getaddrinfo(hostp, NULL, NULL, &ai_host)) return -1; if (p_getaddrinfo(validate_addr, NULL, NULL, &ai_validate)) { p_freeaddrinfo (ai_host); return -1; } status = -1; for (ai = ai_host; ai != NULL; ai = ai->ai_next) { for (aiv = ai_validate; aiv != NULL; aiv = aiv->ai_next) { if ((ai->ai_addrlen == aiv->ai_addrlen) && (ai->ai_family == aiv->ai_family) && (0 == memcmp (ai->ai_addr, aiv->ai_addr, ai->ai_addrlen))) { status = 0; break; } } } if (status != 0) { /* be generous and allow successful validations against variations of localhost addresses */ if (((0 == strcmp("127.0.0.1", hostp)) && (0 == strcmp("::1", validate_addr))) || ((0 == strcmp("127.0.0.1", validate_addr)) && (0 == strcmp("::1", hostp)))) status = 0; } p_freeaddrinfo (ai_host); p_freeaddrinfo (ai_validate); return status; } return 0; } /* sim_parse_addr_ex localport:host:port Presumption is that the input, if it doesn't contain a ':' character is a port specifier. If the host field contains one or more colon characters (i.e. it is an IPv6 address), the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format) llll:w.x.y.z:rrrr llll:name.domain.com:rrrr llll::rrrr rrrr w.x.y.z:rrrr [w.x.y.z]:rrrr name.domain.com:rrrr Inputs: cptr = pointer to input string default_host = optional pointer to default host if none specified host_len = length of host buffer default_port = optional pointer to default port if none specified port_len = length of port buffer Outputs: host = pointer to buffer for IP address (may be NULL), 0 = none port = pointer to buffer for IP port (may be NULL), 0 = none localport = pointer to buffer for local IP port (may be NULL), 0 = none result = status (SCPE_OK on complete success or SCPE_ARG if parsing can't happen due to bad syntax, a value is out of range, a result can't fit into a result buffer, a service name doesn't exist, or a validation name doesn't match the parsed host) */ int sim_parse_addr_ex (const char *cptr, char *host, size_t hostlen, const char *default_host, char *port, size_t port_len, char *localport, size_t localport_len, const char *default_port) { const char *hostp; if ((localport != NULL) && (localport_len != 0)) memset (localport, 0, localport_len); hostp = strchr (cptr, ':'); if ((hostp != NULL) && ((hostp[1] == '[') || (NULL != strchr (hostp+1, ':')))) { if ((localport != NULL) && (localport_len != 0)) { localport_len -= 1; if (localport_len > (size_t)(hostp-cptr)) localport_len = (size_t)(hostp-cptr); memcpy (localport, cptr, localport_len); } return sim_parse_addr (hostp+1, host, hostlen, default_host, port, port_len, default_port, NULL); } return sim_parse_addr (cptr, host, hostlen, default_host, port, port_len, default_port, NULL); } void sim_init_sock (void) { #if defined (_WIN32) int err; WORD wVersionRequested; WSADATA wsaData; wVersionRequested = MAKEWORD (2, 2); err = WSAStartup (wVersionRequested, &wsaData); /* start Winsock */ if (err != 0) sim_printf ("Winsock: startup error %d\n", err); #if defined(AF_INET6) load_ws2 (); #endif /* endif AF_INET6 */ #else /* Use native addrinfo APIs */ #if defined(AF_INET6) p_getaddrinfo = (getaddrinfo_func)getaddrinfo; p_getnameinfo = (getnameinfo_func)getnameinfo; p_freeaddrinfo = (freeaddrinfo_func)freeaddrinfo; #else /* Native APIs not available, connect stubs */ p_getaddrinfo = (getaddrinfo_func)s_getaddrinfo; p_getnameinfo = (getnameinfo_func)s_getnameinfo; p_freeaddrinfo = (freeaddrinfo_func)s_freeaddrinfo; #endif /* endif AF_INET6 */ #endif /* endif _WIN32 */ #if defined (SIGPIPE) signal (SIGPIPE, SIG_IGN); /* no pipe signals */ #endif } void sim_cleanup_sock (void) { #if defined (_WIN32) WSACleanup (); #endif } #if defined (_WIN32) /* Windows */ static int sim_setnonblock (SOCKET sock) { unsigned long non_block = 1; return ioctlsocket (sock, FIONBIO, &non_block); /* set nonblocking */ } #elif defined (VMS) /* VMS */ static int sim_setnonblock (SOCKET sock) { int non_block = 1; return ioctl (sock, FIONBIO, &non_block); /* set nonblocking */ } #else /* Mac, Unix, OS/2 */ static int sim_setnonblock (SOCKET sock) { int fl, sta; fl = fcntl (sock, F_GETFL,0); /* get flags */ if (fl == -1) return SOCKET_ERROR; sta = fcntl (sock, F_SETFL, fl | O_NONBLOCK); /* set nonblock */ if (sta == -1) return SOCKET_ERROR; #if !defined (macintosh) && !defined (__EMX__) && \ !defined (__HAIKU__) /* Unix only */ sta = fcntl (sock, F_SETOWN, getpid()); /* set ownership */ if (sta == -1) return SOCKET_ERROR; #endif return 0; } #endif /* endif !Win32 && !VMS */ static int sim_setnodelay (SOCKET sock) { int nodelay = 1; int sta; /* disable Nagle algorithm */ sta = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)); if (sta == -1) return SOCKET_ERROR; #if defined(TCP_NODELAYACK) /* disable delayed ack algorithm */ sta = setsockopt (sock, IPPROTO_TCP, TCP_NODELAYACK, (char *)&nodelay, sizeof(nodelay)); if (sta == -1) return SOCKET_ERROR; #endif #if defined(TCP_QUICKACK) /* disable delayed ack algorithm */ sta = setsockopt (sock, IPPROTO_TCP, TCP_QUICKACK, (char *)&nodelay, sizeof(nodelay)); if (sta == -1) return SOCKET_ERROR; #endif return sta; } static SOCKET sim_create_sock (int af, int opt_flags) { SOCKET newsock; int err; newsock = socket (af, ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? SOCK_DGRAM : SOCK_STREAM), 0);/* create socket */ if (newsock == INVALID_SOCKET) { /* socket error? */ err = WSAGetLastError (); #if defined(WSAEAFNOSUPPORT) if (err == WSAEAFNOSUPPORT) /* expected error, just return */ return newsock; #endif return sim_err_sock (newsock, "socket"); /* report error and return */ } return newsock; } /* Some platforms and/or network stacks have varying support for listening on an IPv6 socket and receiving connections from both IPv4 and IPv6 client connections. This is known as IPv4-Mapped. Some platforms claim such support (i.e. some Windows versions), but it doesn't work in all cases. */ SOCKET sim_master_sock_ex (const char *hostport, int *parse_status, int opt_flags) { SOCKET newsock = INVALID_SOCKET; int sta; char host[CBUFSIZE], port[CBUFSIZE]; int r; struct addrinfo hints; struct addrinfo *result = NULL, *preferred; r = sim_parse_addr (hostport, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL); if (parse_status) *parse_status = r; if (r) return newsock; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; hints.ai_protocol = IPPROTO_TCP; hints.ai_socktype = SOCK_STREAM; if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &result)) { if (parse_status) *parse_status = -1; return newsock; } preferred = result; #ifdef IPV6_V6ONLY /* When we can create a dual stack socket, be sure to find the IPv6 addrinfo to bind to. */ for (; preferred != NULL; preferred = preferred->ai_next) { if (preferred->ai_family == AF_INET6) break; } if (preferred == NULL) preferred = result; #endif retry: newsock = sim_create_sock (preferred->ai_family, 0); /* create socket */ if (newsock == INVALID_SOCKET) { /* socket error? */ #ifndef IPV6_V6ONLY if (preferred->ai_next) { preferred = preferred->ai_next; goto retry; } #else if ((preferred->ai_family == AF_INET6) && (preferred != result)) { preferred = result; goto retry; } #endif p_freeaddrinfo(result); return newsock; } #ifdef IPV6_V6ONLY if (preferred->ai_family == AF_INET6) { int off = 0; sta = setsockopt (newsock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&off, sizeof(off)); } #endif if (opt_flags & SIM_SOCK_OPT_REUSEADDR) { int on = 1; sta = setsockopt (newsock, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); } #if defined (SO_EXCLUSIVEADDRUSE) else { int on = 1; sta = setsockopt (newsock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&on, sizeof(on)); } #endif sta = bind (newsock, preferred->ai_addr, preferred->ai_addrlen); p_freeaddrinfo(result); if (sta == SOCKET_ERROR) /* bind error? */ return sim_err_sock (newsock, "bind"); if (!(opt_flags & SIM_SOCK_OPT_BLOCKING)) { sta = sim_setnonblock (newsock); /* set nonblocking */ if (sta == SOCKET_ERROR) /* fcntl error? */ return sim_err_sock (newsock, "fcntl"); } sta = listen (newsock, 1); /* listen on socket */ if (sta == SOCKET_ERROR) /* listen error? */ return sim_err_sock (newsock, "listen"); return newsock; /* got it! */ } SOCKET sim_connect_sock_ex (const char *sourcehostport, const char *hostport, const char *default_host, const char *default_port, int opt_flags) { SOCKET newsock = INVALID_SOCKET; int sta; char host[CBUFSIZE], port[CBUFSIZE]; struct addrinfo hints; struct addrinfo *result = NULL, *source = NULL; if (sim_parse_addr (hostport, host, sizeof(host), default_host, port, sizeof(port), default_port, NULL)) return INVALID_SOCKET; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_protocol = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? IPPROTO_UDP : IPPROTO_TCP); hints.ai_socktype = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? SOCK_DGRAM : SOCK_STREAM); if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &result)) return INVALID_SOCKET; if (sourcehostport) { /* Validate the local/source side address which we'll bind to */ if (sim_parse_addr (sourcehostport, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL)) { p_freeaddrinfo (result); return INVALID_SOCKET; } memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = result->ai_family; /* Same family as connect destination */ hints.ai_protocol = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? IPPROTO_UDP : IPPROTO_TCP); hints.ai_socktype = ((opt_flags & SIM_SOCK_OPT_DATAGRAM) ? SOCK_DGRAM : SOCK_STREAM); if (p_getaddrinfo(host[0] ? host : NULL, port[0] ? port : NULL, &hints, &source)) { p_freeaddrinfo (result); return INVALID_SOCKET; } newsock = sim_create_sock (result->ai_family, opt_flags & SIM_SOCK_OPT_DATAGRAM);/* create socket */ if (newsock == INVALID_SOCKET) { /* socket error? */ p_freeaddrinfo (result); p_freeaddrinfo (source); return newsock; } sta = bind (newsock, source->ai_addr, source->ai_addrlen); p_freeaddrinfo(source); source = NULL; if (sta == SOCKET_ERROR) { /* bind error? */ p_freeaddrinfo (result); return sim_err_sock (newsock, "bind"); } } if (newsock == INVALID_SOCKET) { /* socket error? */ newsock = sim_create_sock (result->ai_family, opt_flags & SIM_SOCK_OPT_DATAGRAM);/* create socket */ if (newsock == INVALID_SOCKET) { /* socket error? */ p_freeaddrinfo (result); return newsock; } } if (!(opt_flags & SIM_SOCK_OPT_BLOCKING)) { sta = sim_setnonblock (newsock); /* set nonblocking */ if (sta == SOCKET_ERROR) { /* fcntl error? */ p_freeaddrinfo (result); return sim_err_sock (newsock, "fcntl"); } } if ((!(opt_flags & SIM_SOCK_OPT_DATAGRAM)) && (opt_flags & SIM_SOCK_OPT_NODELAY)) { sta = sim_setnodelay (newsock); /* set nodelay */ if (sta == SOCKET_ERROR) { /* setsock error? */ p_freeaddrinfo (result); return sim_err_sock (newsock, "setnodelay"); } } if (!(opt_flags & SIM_SOCK_OPT_DATAGRAM)) { int keepalive = 1; /* enable TCP Keep Alives */ sta = setsockopt (newsock, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(keepalive)); if (sta == -1) return sim_err_sock (newsock, "setsockopt KEEPALIVE"); } sta = connect (newsock, result->ai_addr, result->ai_addrlen); p_freeaddrinfo (result); if (sta == SOCKET_ERROR) { if (opt_flags & SIM_SOCK_OPT_BLOCKING) { if ((WSAGetLastError () == WSAETIMEDOUT) || /* expected errors after a connect failure */ (WSAGetLastError () == WSAEHOSTUNREACH) || (WSAGetLastError () == WSAECONNREFUSED) || (WSAGetLastError () == WSAECONNABORTED) || (WSAGetLastError () == WSAECONNRESET)) { sim_close_sock (newsock); newsock = INVALID_SOCKET; } else return sim_err_sock (newsock, "connect"); } else /* Non Blocking case won't return errors until some future read */ if ((WSAGetLastError () != WSAEWOULDBLOCK) && (WSAGetLastError () != WSAEINPROGRESS)) return sim_err_sock (newsock, "connect"); } return newsock; /* got it! */ } SOCKET sim_accept_conn_ex (SOCKET master, char **connectaddr, int opt_flags) { int sta = 0, err; int keepalive = 1; #if defined (macintosh) || defined (__linux) || defined (__linux__) || \ defined (__APPLE__) || defined (__OpenBSD__) || \ defined(__NetBSD__) || defined(__FreeBSD__) || \ (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \ defined (__HAIKU__) socklen_t size; #elif defined (_WIN32) || defined (__EMX__) || \ (defined (__ALPHA) && defined (__unix__)) || \ defined (__hpux) int size; #else size_t size; #endif SOCKET newsock; struct sockaddr_storage clientname; if (master == 0) /* not attached? */ return INVALID_SOCKET; size = sizeof (clientname); memset (&clientname, 0, sizeof(clientname)); newsock = accept (master, (struct sockaddr *) &clientname, &size); if (newsock == INVALID_SOCKET) { /* error? */ err = WSAGetLastError (); if (err != WSAEWOULDBLOCK) sim_err_sock(newsock, "accept"); return INVALID_SOCKET; } if (connectaddr != NULL) { *connectaddr = (char *)calloc(1, NI_MAXHOST+1); #ifdef AF_INET6 p_getnameinfo((struct sockaddr *)&clientname, size, *connectaddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (0 == memcmp("::ffff:", *connectaddr, 7)) /* is this a IPv4-mapped IPv6 address? */ memmove(*connectaddr, 7+*connectaddr, /* prefer bare IPv4 address */ strlen(*connectaddr) - 7 + 1); /* length to include terminating \0 */ #else strcpy(*connectaddr, inet_ntoa(((struct sockaddr_in *)&connectaddr)->s_addr)); #endif } if (!(opt_flags & SIM_SOCK_OPT_BLOCKING)) { sta = sim_setnonblock (newsock); /* set nonblocking */ if (sta == SOCKET_ERROR) /* fcntl error? */ return sim_err_sock (newsock, "fcntl"); } if ((opt_flags & SIM_SOCK_OPT_NODELAY)) { sta = sim_setnodelay (newsock); /* set nonblocking */ if (sta == SOCKET_ERROR) /* setsockopt error? */ return sim_err_sock (newsock, "setnodelay"); } /* enable TCP Keep Alives */ sta = setsockopt (newsock, SOL_SOCKET, SO_KEEPALIVE, (char *)&keepalive, sizeof(keepalive)); if (sta == -1) return sim_err_sock (newsock, "setsockopt KEEPALIVE"); return newsock; } int sim_check_conn (SOCKET sock, int rd) { fd_set rw_set, er_set; fd_set *rw_p = &rw_set; fd_set *er_p = &er_set; struct timeval zero; struct sockaddr_storage peername; #if defined (macintosh) || defined (__linux) || defined (__linux__) || \ defined (__APPLE__) || defined (__OpenBSD__) || \ defined(__NetBSD__) || defined(__FreeBSD__) || \ (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \ defined (__HAIKU__) socklen_t peernamesize = (socklen_t)sizeof(peername); #elif defined (_WIN32) || defined (__EMX__) || \ (defined (__ALPHA) && defined (__unix__)) || \ defined (__hpux) int peernamesize = (int)sizeof(peername); #else size_t peernamesize = sizeof(peername); #endif memset (&zero, 0, sizeof(zero)); FD_ZERO (rw_p); FD_ZERO (er_p); FD_SET (sock, rw_p); FD_SET (sock, er_p); if (rd) select ((int) sock + 1, rw_p, NULL, er_p, &zero); else select ((int) sock + 1, NULL, rw_p, er_p, &zero); if (FD_ISSET (sock, er_p)) return -1; if (FD_ISSET (sock, rw_p)) { if (0 == getpeername (sock, (struct sockaddr *)&peername, &peernamesize)) return 1; else return -1; } return 0; } static int _sim_getaddrname (struct sockaddr *addr, size_t addrsize, char *hostnamebuf, char *portnamebuf) { #if defined (macintosh) || defined (__linux) || defined (__linux__) || \ defined (__APPLE__) || defined (__OpenBSD__) || \ defined(__NetBSD__) || defined(__FreeBSD__) || \ (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \ defined (__HAIKU__) socklen_t size = (socklen_t)addrsize; #elif defined (_WIN32) || defined (__EMX__) || \ (defined (__ALPHA) && defined (__unix__)) || \ defined (__hpux) int size = (int)addrsize; #else size_t size = addrsize; #endif int ret = 0; #ifdef AF_INET6 *hostnamebuf = '\0'; *portnamebuf = '\0'; ret = p_getnameinfo(addr, size, hostnamebuf, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (0 == memcmp("::ffff:", hostnamebuf, 7)) /* is this a IPv4-mapped IPv6 address? */ memmove(hostnamebuf, 7+hostnamebuf, /* prefer bare IPv4 address */ strlen(hostnamebuf) + 7 - 1); /* length to include terminating \0 */ if (!ret) ret = p_getnameinfo(addr, size, NULL, 0, portnamebuf, NI_MAXSERV, NI_NUMERICSERV); #else strcpy(hostnamebuf, inet_ntoa(((struct sockaddr_in *)addr)->s_addr)); sprintf(portnamebuf, "%d", (int)ntohs(((struct sockaddr_in *)addr)->s_port))); #endif return ret; } int sim_getnames_sock (SOCKET sock, char **socknamebuf, char **peernamebuf) { struct sockaddr_storage sockname, peername; #if defined (macintosh) || defined (__linux) || defined (__linux__) || \ defined (__APPLE__) || defined (__OpenBSD__) || \ defined(__NetBSD__) || defined(__FreeBSD__) || \ (defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)) || \ defined (__HAIKU__) socklen_t socknamesize = (socklen_t)sizeof(sockname); socklen_t peernamesize = (socklen_t)sizeof(peername); #elif defined (_WIN32) || defined (__EMX__) || \ (defined (__ALPHA) && defined (__unix__)) || \ defined (__hpux) int socknamesize = (int)sizeof(sockname); int peernamesize = (int)sizeof(peername); #else size_t socknamesize = sizeof(sockname); size_t peernamesize = sizeof(peername); #endif char hostbuf[NI_MAXHOST+1]; char portbuf[NI_MAXSERV+1]; if (socknamebuf) *socknamebuf = (char *)calloc(1, NI_MAXHOST+NI_MAXSERV+4); if (peernamebuf) *peernamebuf = (char *)calloc(1, NI_MAXHOST+NI_MAXSERV+4); getsockname (sock, (struct sockaddr *)&sockname, &socknamesize); getpeername (sock, (struct sockaddr *)&peername, &peernamesize); if (socknamebuf != NULL) { _sim_getaddrname ((struct sockaddr *)&sockname, (size_t)socknamesize, hostbuf, portbuf); sprintf(*socknamebuf, "[%s]:%s", hostbuf, portbuf); } if (peernamebuf != NULL) { _sim_getaddrname ((struct sockaddr *)&peername, (size_t)peernamesize, hostbuf, portbuf); sprintf(*peernamebuf, "[%s]:%s", hostbuf, portbuf); } return 0; } int sim_read_sock (SOCKET sock, char *buf, int nbytes) { int rbytes, err; rbytes = recv (sock, buf, nbytes, 0); if (rbytes == 0) /* disconnect */ return -1; if (rbytes == SOCKET_ERROR) { err = WSAGetLastError (); if (err == WSAEWOULDBLOCK) /* no data */ return 0; #if defined(EAGAIN) if (err == EAGAIN) /* no data */ return 0; #endif if ((err != WSAETIMEDOUT) && /* expected errors after a connect failure */ (err != WSAEHOSTUNREACH) && (err != WSAECONNREFUSED) && (err != WSAECONNABORTED) && (err != WSAECONNRESET) && (err != WSAEINTR)) /* or a close of a blocking read */ sim_err_sock (INVALID_SOCKET, "read"); return -1; } return rbytes; } int sim_write_sock (SOCKET sock, const char *msg, int nbytes) { int err, sbytes = send (sock, msg, nbytes, 0); if (sbytes == SOCKET_ERROR) { err = WSAGetLastError (); if (err == WSAEWOULDBLOCK) /* no data */ return 0; #if defined(EAGAIN) if (err == EAGAIN) /* no data */ return 0; #endif } return sbytes; } void sim_close_sock (SOCKET sock) { shutdown(sock, SD_BOTH); closesocket (sock); } #endif /* end else !implemented */ #ifdef __cplusplus } #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 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 |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 | /* sim_tape.c: simulator tape support library Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. Ultimately, this will be a place to hide processing of various tape formats, as well as OS-specific direct hardware access. 23-Jan-12 MP Added support for Logical EOT detection while positioning 05-Feb-11 MP Refactored to prepare for SIM_ASYNC_IO support Added higher level routines: sim_tape_wreomrw - erase remainder of tape & rewind sim_tape_sprecsf - skip records sim_tape_spfilef - skip files sim_tape_sprecsr - skip records rev sim_tape_spfiler - skip files rev sim_tape_position - general purpose position These routines correspond to natural tape operations and will align better when physical tape support is included here. 08-Jun-08 JDB Fixed signed/unsigned warning in sim_tape_set_fmt 23-Jan-07 JDB Fixed backspace over gap at BOT 22-Jan-07 RMS Fixed bug in P7B format read reclnt rev (found by Rich Cornwell) 15-Dec-06 RMS Added support for small capacity tapes 30-Aug-06 JDB Added erase gap support 14-Feb-06 RMS Added variable tape capacity 23-Jan-06 JDB Fixed odd-byte-write problem in sim_tape_wrrecf 17-Dec-05 RMS Added write support for Paul Pierce 7b format 16-Aug-05 RMS Fixed C++ declaration and cast problems 02-May-05 RMS Added support for Pierce 7b format 28-Jul-04 RMS Fixed bug in writing error records (found by Dave Bryan) RMS Fixed incorrect error codes (found by Dave Bryan) 05-Jan-04 RMS Revised for file I/O library 25-Apr-03 RMS Added extended file support 28-Mar-03 RMS Added E11 and TPC format support Public routines: sim_tape_attach attach tape unit sim_tape_detach detach tape unit sim_tape_attach_help help routine for attaching tapes sim_tape_rdrecf read tape record forward sim_tape_rdrecr read tape record reverse sim_tape_wrrecf write tape record forward sim_tape_sprecf space tape record forward sim_tape_sprecr space tape record reverse sim_tape_wrtmk write tape mark sim_tape_wreom erase remainder of tape sim_tape_wreomrw erase remainder of tape & rewind sim_tape_wrgap write erase gap sim_tape_sprecsf space records forward sim_tape_spfilef space files forward sim_tape_sprecsr space records reverse sim_tape_spfiler space files reverse sim_tape_position generalized position sim_tape_rewind rewind sim_tape_reset reset unit sim_tape_bot TRUE if at beginning of tape sim_tape_eot TRUE if at or beyond end of tape sim_tape_wrp TRUE if write protected sim_tape_set_fmt set tape format sim_tape_show_fmt show tape format sim_tape_set_capac set tape capacity sim_tape_show_capac show tape capacity sim_tape_set_dens set tape density sim_tape_show_dens show tape density sim_tape_set_async enable asynchronous operation sim_tape_clr_async disable asynchronous operation */ #include "sim_defs.h" #include "sim_tape.h" #include <ctype.h> #if defined SIM_ASYNCH_IO #include <pthread.h> #endif struct sim_tape_fmt { const char *name; /* name */ int32 uflags; /* unit flags */ t_addr bot; /* bot test */ }; static struct sim_tape_fmt fmts[MTUF_N_FMT] = { { "SIMH", 0, sizeof (t_mtrlnt) - 1 }, { "E11", 0, sizeof (t_mtrlnt) - 1 }, { "TPC", UNIT_RO, sizeof (t_tpclnt) - 1 }, { "P7B", 0, 0 }, /* { "TPF", UNIT_RO, 0 }, */ { NULL, 0, 0 } }; static const uint32 bpi [] = { /* tape density table, indexed by MT_DENS constants */ 0, /* 0 = MT_DENS_NONE -- density not set */ 200, /* 1 = MT_DENS_200 -- 200 bpi NRZI */ 556, /* 2 = MT_DENS_556 -- 556 bpi NRZI */ 800, /* 3 = MT_DENS_800 -- 800 bpi NRZI */ 1600, /* 4 = MT_DENS_1600 -- 1600 bpi PE */ 6250 /* 5 = MT_DENS_6250 -- 6250 bpi GCR */ }; #define BPI_COUNT (sizeof (bpi) / sizeof (bpi [0])) /* count of density table entries */ static t_stat sim_tape_ioerr (UNIT *uptr); static t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat); static uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map, uint32 mapsize); static t_stat sim_tape_simh_check (UNIT *uptr); static t_stat sim_tape_e11_check (UNIT *uptr); static t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map); static void sim_tape_data_trace (UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason); struct tape_context { DEVICE *dptr; /* Device for unit (access to debug flags) */ uint32 dbit; /* debugging bit for trace */ uint32 auto_format; /* Format determined dynamically */ #if defined SIM_ASYNCH_IO int asynch_io; /* Asynchronous Interrupt scheduling enabled */ int asynch_io_latency; /* instructions to delay pending interrupt */ pthread_mutex_t lock; pthread_t io_thread; /* I/O Thread Id */ pthread_mutex_t io_lock; pthread_cond_t io_cond; pthread_cond_t io_done; pthread_cond_t startup_cond; int io_top; uint8 *buf; uint32 *bc; uint32 *fc; uint32 vbc; uint32 max; uint32 gaplen; uint32 bpi; uint32 *objupdate; TAPE_PCALLBACK callback; t_stat io_status; #endif }; #define tape_ctx up8 /* Field in Unit structure which points to the tape_context */ #if defined SIM_ASYNCH_IO #define AIO_CALLSETUP \ struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; \ \ if (ctx == NULL) \ return sim_messagef (SCPE_IERR, "Bad Attach\n"); \ if ((!callback) || !ctx->asynch_io) #define AIO_CALL(op, _buf, _bc, _fc, _max, _vbc, _gaplen, _bpi, _obj, _callback)\ if (ctx->asynch_io) { \ struct tape_context *ctx = \ (struct tape_context *)uptr->tape_ctx; \ \ pthread_mutex_lock (&ctx->io_lock); \ \ sim_debug (ctx->dbit, ctx->dptr, \ "sim_tape AIO_CALL(op=%d, unit=%d)\n", op, (int)(uptr-ctx->dptr->units));\ \ if (ctx->callback) \ abort(); /* horrible mistake, stop */ \ ctx->io_top = op; \ ctx->buf = _buf; \ ctx->bc = _bc; \ ctx->fc = _fc; \ ctx->max = _max; \ ctx->vbc = _vbc; \ ctx->gaplen = _gaplen; \ ctx->bpi = _bpi; \ ctx->objupdate = _obj; \ ctx->callback = _callback; \ pthread_cond_signal (&ctx->io_cond); \ pthread_mutex_unlock (&ctx->io_lock); \ } \ else \ if (_callback) \ (_callback) (uptr, r); #define TOP_DONE 0 /* close */ #define TOP_RDRF 1 /* sim_tape_rdrecf_a */ #define TOP_RDRR 2 /* sim_tape_rdrecr_a */ #define TOP_WREC 3 /* sim_tape_wrrecf_a */ #define TOP_WTMK 4 /* sim_tape_wrtmk_a */ #define TOP_WEOM 5 /* sim_tape_wreom_a */ #define TOP_WEMR 6 /* sim_tape_wreomrw_a */ #define TOP_WGAP 7 /* sim_tape_wrgap_a */ #define TOP_SPRF 8 /* sim_tape_sprecf_a */ #define TOP_SRSF 9 /* sim_tape_sprecsf_a */ #define TOP_SPRR 10 /* sim_tape_sprecr_a */ #define TOP_SRSR 11 /* sim_tape_sprecsr_a */ #define TOP_SPFF 12 /* sim_tape_spfilef */ #define TOP_SFRF 13 /* sim_tape_spfilebyrecf */ #define TOP_SPFR 14 /* sim_tape_spfiler */ #define TOP_SFRR 15 /* sim_tape_spfilebyrecr */ #define TOP_RWND 16 /* sim_tape_rewind_a */ #define TOP_POSN 17 /* sim_tape_position_a */ static void * _tape_io(void *arg) { UNIT* volatile uptr = (UNIT*)arg; struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; /* Boost Priority for this I/O thread vs the CPU instruction execution thread which in general won't be readily yielding the processor when this thread needs to run */ sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); sim_debug (ctx->dbit, ctx->dptr, "_tape_io(unit=%d) starting\n", (int)(uptr-ctx->dptr->units)); pthread_mutex_lock (&ctx->io_lock); pthread_cond_signal (&ctx->startup_cond); /* Signal we're ready to go */ while (1) { pthread_cond_wait (&ctx->io_cond, &ctx->io_lock); if (ctx->io_top == TOP_DONE) break; pthread_mutex_unlock (&ctx->io_lock); switch (ctx->io_top) { case TOP_RDRF: ctx->io_status = sim_tape_rdrecf (uptr, ctx->buf, ctx->bc, ctx->max); break; case TOP_RDRR: ctx->io_status = sim_tape_rdrecr (uptr, ctx->buf, ctx->bc, ctx->max); break; case TOP_WREC: ctx->io_status = sim_tape_wrrecf (uptr, ctx->buf, ctx->vbc); break; case TOP_WTMK: ctx->io_status = sim_tape_wrtmk (uptr); break; case TOP_WEOM: ctx->io_status = sim_tape_wreom (uptr); break; case TOP_WEMR: ctx->io_status = sim_tape_wreomrw (uptr); break; case TOP_WGAP: ctx->io_status = sim_tape_wrgap (uptr, ctx->gaplen); break; case TOP_SPRF: ctx->io_status = sim_tape_sprecf (uptr, ctx->bc); break; case TOP_SRSF: ctx->io_status = sim_tape_sprecsf (uptr, ctx->vbc, ctx->bc); break; case TOP_SPRR: ctx->io_status = sim_tape_sprecr (uptr, ctx->bc); break; case TOP_SRSR: ctx->io_status = sim_tape_sprecsr (uptr, ctx->vbc, ctx->bc); break; case TOP_SPFF: ctx->io_status = sim_tape_spfilef (uptr, ctx->vbc, ctx->bc); break; case TOP_SFRF: ctx->io_status = sim_tape_spfilebyrecf (uptr, ctx->vbc, ctx->bc, ctx->fc, ctx->max); break; case TOP_SPFR: ctx->io_status = sim_tape_spfiler (uptr, ctx->vbc, ctx->bc); break; case TOP_SFRR: ctx->io_status = sim_tape_spfilebyrecr (uptr, ctx->vbc, ctx->bc, ctx->fc); break; case TOP_RWND: ctx->io_status = sim_tape_rewind (uptr); break; case TOP_POSN: ctx->io_status = sim_tape_position (uptr, ctx->vbc, ctx->gaplen, ctx->bc, ctx->bpi, ctx->fc, ctx->objupdate); break; } pthread_mutex_lock (&ctx->io_lock); ctx->io_top = TOP_DONE; pthread_cond_signal (&ctx->io_done); sim_activate (uptr, ctx->asynch_io_latency); } pthread_mutex_unlock (&ctx->io_lock); sim_debug (ctx->dbit, ctx->dptr, "_tape_io(unit=%d) exiting\n", (int)(uptr-ctx->dptr->units)); return NULL; } /* This routine is called in the context of the main simulator thread before processing events for any unit. It is only called when an asynchronous thread has called sim_activate() to activate a unit. The job of this routine is to put the unit in proper condition to digest what may have occurred in the asynchronous thread. Since tape processing only handles a single I/O at a time to a particular tape device, we have the opportunity to possibly detect improper attempts to issue multiple concurrent I/O requests. */ static void _tape_completion_dispatch (UNIT *uptr) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; TAPE_PCALLBACK callback = ctx->callback; sim_debug (ctx->dbit, ctx->dptr, "_tape_completion_dispatch(unit=%d, top=%d, callback=%p)\n", (int)(uptr-ctx->dptr->units), ctx->io_top, ctx->callback); if (ctx->io_top != TOP_DONE) abort(); /* horribly wrong, stop */ if (ctx->callback && ctx->io_top == TOP_DONE) { ctx->callback = NULL; callback (uptr, ctx->io_status); } } static t_bool _tape_is_active (UNIT *uptr) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; if (ctx) { sim_debug (ctx->dbit, ctx->dptr, "_tape_is_active(unit=%d, top=%d)\n", (int)(uptr-ctx->dptr->units), ctx->io_top); return (ctx->io_top != TOP_DONE); } return FALSE; } static t_bool _tape_cancel (UNIT *uptr) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; if (ctx) { sim_debug (ctx->dbit, ctx->dptr, "_tape_cancel(unit=%d, top=%d)\n", (int)(uptr-ctx->dptr->units), ctx->io_top); if (ctx->asynch_io) { pthread_mutex_lock (&ctx->io_lock); while (ctx->io_top != TOP_DONE) pthread_cond_wait (&ctx->io_done, &ctx->io_lock); pthread_mutex_unlock (&ctx->io_lock); } } return FALSE; } #else #define AIO_CALLSETUP \ if (uptr->tape_ctx == NULL) \ return sim_messagef (SCPE_IERR, "Bad Attach\n"); #define AIO_CALL(op, _buf, _fc, _bc, _max, _vbc, _gaplen, _bpi, _obj, _callback) \ if (_callback) \ (_callback) (uptr, r); #endif /* Enable asynchronous operation */ t_stat sim_tape_set_async (UNIT *uptr, int latency) { #if !defined(SIM_ASYNCH_IO) sim_printf ("Tape: can't operate asynchronously\r\n"); return SCPE_NOFNC; #else struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; pthread_attr_t attr; ctx->asynch_io = sim_asynch_enabled; ctx->asynch_io_latency = latency; if (ctx->asynch_io) { pthread_mutex_init (&ctx->io_lock, NULL); pthread_cond_init (&ctx->io_cond, NULL); pthread_cond_init (&ctx->io_done, NULL); pthread_cond_init (&ctx->startup_cond, NULL); pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_mutex_lock (&ctx->io_lock); pthread_create (&ctx->io_thread, &attr, _tape_io, (void *)uptr); pthread_attr_destroy(&attr); pthread_cond_wait (&ctx->startup_cond, &ctx->io_lock); /* Wait for thread to stabilize */ pthread_mutex_unlock (&ctx->io_lock); pthread_cond_destroy (&ctx->startup_cond); } uptr->a_check_completion = _tape_completion_dispatch; uptr->a_is_active = _tape_is_active; uptr->cancel = _tape_cancel; return SCPE_OK; #endif } /* Disable asynchronous operation */ t_stat sim_tape_clr_async (UNIT *uptr) { #if !defined(SIM_ASYNCH_IO) return SCPE_NOFNC; #else struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; /* make sure device exists */ if (!ctx) return SCPE_UNATT; if (ctx->asynch_io) { pthread_mutex_lock (&ctx->io_lock); ctx->asynch_io = 0; pthread_cond_signal (&ctx->io_cond); pthread_mutex_unlock (&ctx->io_lock); pthread_join (ctx->io_thread, NULL); pthread_mutex_destroy (&ctx->io_lock); pthread_cond_destroy (&ctx->io_cond); pthread_cond_destroy (&ctx->io_done); } return SCPE_OK; #endif } /* This routine is called when the simulator stops and any time the asynch mode is changed (enabled or disabled) */ static void _sim_tape_io_flush (UNIT *uptr) { #if defined (SIM_ASYNCH_IO) struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; sim_tape_clr_async (uptr); if (sim_asynch_enabled) sim_tape_set_async (uptr, ctx->asynch_io_latency); #endif fflush (uptr->fileref); } /* Attach tape unit */ t_stat sim_tape_attach (UNIT *uptr, CONST char *cptr) { DEVICE *dptr; if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT; return sim_tape_attach_ex (uptr, cptr, (dptr->flags & DEV_DEBUG) ? 0xFFFFFFFF : 0, 0); } t_stat sim_tape_attach_ex (UNIT *uptr, const char *cptr, uint32 dbit, int completion_delay) { struct tape_context *ctx; uint32 objc; DEVICE *dptr; char gbuf[CBUFSIZE]; t_stat r; t_bool auto_format = FALSE; if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT; if (sim_switches & SWMASK ('F')) { /* format spec? */ cptr = get_glyph (cptr, gbuf, 0); /* get spec */ if (*cptr == 0) /* must be more */ return SCPE_2FARG; if (sim_tape_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) return sim_messagef (SCPE_ARG, "Invalid Tape Format: %s\n", gbuf); sim_switches = sim_switches & ~(SWMASK ('F')); /* Record Format specifier already processed */ auto_format = TRUE; } if (MT_GET_FMT (uptr) == MTUF_F_TPC) sim_switches |= SWMASK ('R'); /* Force ReadOnly attach for TPC tapes */ r = attach_unit (uptr, (CONST char *)cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return sim_messagef (r, "Can't open tape image: %s\n", cptr); switch (MT_GET_FMT (uptr)) { /* case on format */ case MTUF_F_STD: /* SIMH */ if (SCPE_OK != sim_tape_simh_check (uptr)) { sim_tape_detach (uptr); return SCPE_FMT; /* yes, complain */ } break; case MTUF_F_E11: /* E11 */ if (SCPE_OK != sim_tape_e11_check (uptr)) { sim_tape_detach (uptr); return SCPE_FMT; /* yes, complain */ } break; case MTUF_F_TPC: /* TPC */ objc = sim_tape_tpc_map (uptr, NULL, 0); /* get # objects */ if (objc == 0) { /* tape empty? */ sim_tape_detach (uptr); return SCPE_FMT; /* yes, complain */ } uptr->filebuf = calloc (objc + 1, sizeof (t_addr)); if (uptr->filebuf == NULL) { /* map allocated? */ sim_tape_detach (uptr); return SCPE_MEM; /* no, complain */ } uptr->hwmark = objc + 1; /* save map size */ sim_tape_tpc_map (uptr, (t_addr *) uptr->filebuf, objc);/* fill map */ break; default: break; } uptr->tape_ctx = ctx = (struct tape_context *)calloc(1, sizeof(struct tape_context)); ctx->dptr = dptr; /* save DEVICE pointer */ ctx->dbit = dbit; /* save debug bit */ ctx->auto_format = auto_format; /* save that we auto selected format */ sim_tape_rewind (uptr); #if defined (SIM_ASYNCH_IO) sim_tape_set_async (uptr, completion_delay); #endif uptr->io_flush = _sim_tape_io_flush; return SCPE_OK; } /* Detach tape unit */ t_stat sim_tape_detach (UNIT *uptr) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint32 f = MT_GET_FMT (uptr); t_stat r; t_bool auto_format = FALSE; if ((ctx == NULL) || (uptr == NULL) || !(uptr->flags & UNIT_ATT)) return SCPE_IERR; if (uptr->io_flush) uptr->io_flush (uptr); /* flush buffered data */ if (ctx) auto_format = ctx->auto_format; sim_tape_clr_async (uptr); r = detach_unit (uptr); /* detach unit */ if (r != SCPE_OK) return r; switch (f) { /* case on format */ case MTUF_F_TPC: /* TPC */ if (uptr->filebuf) /* free map */ free (uptr->filebuf); uptr->filebuf = NULL; uptr->hwmark = 0; break; default: break; } sim_tape_rewind (uptr); free (uptr->tape_ctx); uptr->tape_ctx = NULL; uptr->io_flush = NULL; if (auto_format) /* format was determined or specified at attach time? */ sim_tape_set_fmt (uptr, 0, "SIMH", NULL); /* restore default format */ return SCPE_OK; } t_stat sim_tape_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { fprintf (st, "%s Tape Attach Help\n\n", dptr->name); if (0 == (uptr-dptr->units)) { if (dptr->numunits > 1) { uint32 i; for (i=0; i < dptr->numunits; ++i) if (dptr->units[i].flags & UNIT_ATTABLE) fprintf (st, " sim> ATTACH {switches} %s%d tapefile\n\n", dptr->name, i); } else fprintf (st, " sim> ATTACH {switches} %s tapefile\n\n", dptr->name); } else fprintf (st, " sim> ATTACH {switches} %s tapefile\n\n", dptr->name); fprintf (st, "Attach command switches\n"); fprintf (st, " -R Attach Read Only.\n"); fprintf (st, " -E Must Exist (if not specified an attempt to create the indicated\n"); fprintf (st, " virtual tape will be attempted).\n"); fprintf (st, " -F Open the indicated tape container in a specific format (default\n"); fprintf (st, " is SIMH, alternatives are E11, TPC and P7B)\n"); return SCPE_OK; } static void sim_tape_data_trace(UNIT *uptr, const uint8 *data, size_t len, const char* txt, int detail, uint32 reason) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; if (ctx == NULL) return; if (sim_deb && (ctx->dptr->dctrl & reason)) sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), "", len, txt, reason); } /* Read record length forward (internal routine) Inputs: uptr = pointer to tape unit bc = pointer to returned record length Outputs: status = operation status exit condition tape position ------------------ ----------------------------------------------------- unit unattached unchanged read error unchanged, PNU set end of file/medium updated if a gap precedes, else unchanged and PNU set tape mark updated tape runaway updated data record updated, sim_fread will read record forward This routine is called to set up a record read or spacing in the forward direction. On return, status is MTSE_OK and the tape is positioned at the first data byte if a record was encountered, or status is an MTSE error code giving the reason that the operation did not succeed and the tape position is as indicated above. The ANSI standards for magnetic tape recording (X3.32, X3.39, and X3.54) and the equivalent ECMA standard (ECMA-62) specify a maximum erase gap length of 25 feet (7.6 meters). While gaps of any length may be written, gaps longer than this are non-standard and may indicate that an unrecorded or erased tape is being read. If the tape density has been set via a previous "sim_tape_set_dens" call, then the length is monitored when skipping over erase gaps. If the length reaches 25 feet, motion is terminated, and MTSE_RUNAWAY status is returned. Runaway status is also returned if an end-of-medium marker or the physical end of file is encountered while spacing over a gap; however, MTSE_EOM is returned if the tape is positioned at the EOM on entry. If the density has not been set, then a gap of any length is skipped, and MTSE_RUNAWAY status is never returned. In effect, erase gaps present in the tape image file will be transparent to the caller. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format. Because gaps may be partially overwritten with data records, gap metadata must be examined marker-by-marker. To reduce the number of file read calls, a buffer of metadata elements is used. The buffer size is initially established at 256 elements but may be set to any size desired. To avoid a large read for the typical case where an erase gap is not present, the first read is of a single metadatum marker. If that is a gap marker, then additional buffered reads are performed. See the notes at "sim_tape_wrgap" regarding the erase gap implementation. Implementation notes: 1. For programming convenience, erase gap processing is performed for both SIMH standard and E11 tape formats, although the latter will never contain erase gaps, as the "sim_tape_wrgap" call takes no action for the E11 format. 2. The "feof" call cannot return a non-zero value on the first pass through the loop, because the "sim_fseek" call resets the internal end-of-file indicator. Subsequent passes only occur if an erase gap is present, so a non-zero return indicates an EOF was seen while reading through a gap. 3. The dynamic start/stop test of the HP 3000 magnetic tape diagnostic heavily exercises the erase gap scanning code. Sample test execution times for various buffer sizes on a 2 GHz host platform are: buffer size execution time (elements) (CPU seconds) ----------- -------------- 1 7200 32 783 128 237 256 203 512 186 1024 171 4. Because an erase gap may precede the logical end-of-medium, represented either by the physical end-of-file or by an EOM marker, the "position not updated" flag is set only if the tape is positioned at the EOM when the routine is entered. If at least one gap marker precedes the EOM, then the PNU flag is not set. This ensures that a backspace-and-retry sequence will work correctly in both cases. */ static t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint8 c; t_bool all_eof; uint32 f = MT_GET_FMT (uptr); t_mtrlnt sbc; t_tpclnt tpcbc; t_mtrlnt buffer [256]; /* local tape buffer */ uint32 bufcntr, bufcap; /* buffer counter and capacity */ int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */ t_stat r = MTSE_OK; MT_CLR_PNU (uptr); /* clear the position-not-updated flag */ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */ return MTSE_UNATT; /* then quit with an error */ if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set the initial tape position */ switch (f) { /* the read method depends on the tape format */ case MTUF_F_STD: case MTUF_F_E11: runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set the largest legal gap size in bytes */ if (runaway_counter == 0) { /* if tape density has not been not set */ sizeof_gap = 0; /* then disable runaway detection */ runaway_counter = INT_MAX; /* to allow gaps of any size */ } else /* otherwise */ sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */ bufcntr = 0; /* force an initial read */ bufcap = 0; /* but of just one metadata marker */ do { /* loop until a record, gap, or error is seen */ if (bufcntr == bufcap) { /* if the buffer is empty then refill it */ if (feof (uptr->fileref)) { /* if we hit the EOF while reading a gap */ if (sizeof_gap > 0) /* then if detection is enabled */ r = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ r = MTSE_EOM; /* as the end-of-medium */ break; } else if (bufcap == 0) /* otherwise if this is the initial read */ bufcap = 1; /* then start with just one marker */ else /* otherwise reset the capacity */ bufcap = sizeof (buffer) /* to the full size of the buffer */ / sizeof (buffer [0]); bufcap = sim_fread (buffer, /* fill the buffer */ sizeof (t_mtrlnt), /* with tape metadata */ bufcap, uptr->fileref); if (ferror (uptr->fileref)) { /* if a file I/O error occurred */ if (bufcntr == 0) /* then if this is the initial read */ MT_SET_PNU (uptr); /* then set position not updated */ r = sim_tape_ioerr (uptr); /* report the error and quit */ break; } else if (bufcap == 0 /* otherwise if positioned at the physical EOF */ || buffer [0] == MTR_EOM) /* or at the logical EOM */ if (bufcntr == 0) { /* then if this is the initial read */ MT_SET_PNU (uptr); /* then set position not updated */ r = MTSE_EOM; /* and report the end-of-medium and quit */ break; } else { /* otherwise some gap has already been skipped */ if (sizeof_gap > 0) /* so if detection is enabled */ r = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ r = MTSE_EOM; /* as the end-of-medium */ break; } else /* otherwise reset the index */ bufcntr = 0; /* to the start of the buffer */ } *bc = buffer [bufcntr++]; /* store the metadata marker value */ if (*bc == MTR_EOM) { /* if an end-of-medium marker is seen */ if (sizeof_gap > 0) /* then if detection is enabled */ r = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ r = MTSE_EOM; /* as the end-of-medium */ break; } uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* space over the marker */ if (*bc == MTR_TMK) { /* if the value is a tape mark */ r = MTSE_TMK; /* then quit with tape mark status */ break; } else if (*bc == MTR_GAP) /* otherwise if the value is a full gap */ runaway_counter -= sizeof_gap; /* then decrement the gap counter */ else if (*bc == MTR_FHGAP) { /* otherwise if the value if a half gap */ uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2; /* then back up */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* to resync */ bufcntr = bufcap; /* mark the buffer as invalid to force a read */ *bc = MTR_GAP; /* reset the marker */ runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */ } else { /* otherwise it's a record marker */ if (bufcntr < bufcap) /* if the position is within the buffer */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* then seek to the data area */ sbc = MTR_L (*bc); /* extract the record length */ uptr->pos = uptr->pos + sizeof (t_mtrlnt) /* position to the start */ + (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */ } } while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */ if (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */ r = MTSE_RUNAWAY; /* then report it */ break; /* otherwise the operation succeeded */ case MTUF_F_TPC: sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); *bc = tpcbc; /* save rec lnt */ if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); /* pos not upd */ return sim_tape_ioerr (uptr); } if (feof (uptr->fileref)) { /* eof? */ MT_SET_PNU (uptr); /* pos not upd */ r = MTSE_EOM; break; } uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */ if (tpcbc == TPC_TMK) /* tape mark? */ r = MTSE_TMK; uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */ break; case MTUF_F_P7B: for (sbc = 0, all_eof = 1; ; sbc++) { /* loop thru record */ sim_fread (&c, sizeof (uint8), 1, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); /* pos not upd */ return sim_tape_ioerr (uptr); } if (feof (uptr->fileref)) { /* eof? */ if (sbc == 0) /* no data? eom */ return MTSE_EOM; break; /* treat like eor */ } if ((sbc != 0) && (c & P7B_SOR)) /* next record? */ break; if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0; } *bc = sbc; /* save rec lnt */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ uptr->pos = uptr->pos + sbc; /* spc over record */ if (all_eof) /* tape mark? */ r = MTSE_TMK; break; default: return MTSE_FMT; } sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos); return r; } /* Read record length reverse (internal routine) Inputs: uptr = pointer to tape unit bc = pointer to returned record length Outputs: status = operation status exit condition tape position ------------------ ------------------------------------------- unit unattached unchanged beginning of tape unchanged read error unchanged end of file unchanged end of medium updated tape mark updated tape runaway updated data record updated, sim_fread will read record forward This routine is called to set up a record read or spacing in the reverse direction. On return, status is MTSE_OK and the tape is positioned at the first data byte if a record was encountered, or status is an MTSE error code giving the reason that the operation did not succeed and the tape position is as indicated above. See the notes at "sim_tape_rdlntf" and "sim_tape_wrgap" regarding tape runaway and the erase gap implementation, respectively. */ static t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint8 c; t_bool all_eof; uint32 f = MT_GET_FMT (uptr); t_addr ppos; t_mtrlnt sbc; t_tpclnt tpcbc; t_mtrlnt buffer [256]; /* local tape buffer */ uint32 bufcntr, bufcap; /* buffer counter and capacity */ int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */ t_stat r = MTSE_OK; MT_CLR_PNU (uptr); /* clear the position-not-updated flag */ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */ return MTSE_UNATT; /* then quit with an error */ if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ if (sim_tape_bot (uptr)) /* if the unit is positioned at the BOT */ return MTSE_BOT; /* then reading backward is not possible */ switch (f) { /* the read method depends on the tape format */ case MTUF_F_STD: case MTUF_F_E11: runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set largest legal gap size in bytes */ if (runaway_counter == 0) { /* if tape density has not been not set */ sizeof_gap = 0; /* then disable runaway detection */ runaway_counter = INT_MAX; /* to allow gaps of any size */ } else /* otherwise */ sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */ bufcntr = 0; /* force an initial read */ bufcap = 1; /* but of just one metadata marker */ do { /* loop until a record, gap, or error seen */ if (bufcntr == 0) { /* if the buffer is empty then refill it */ if (sim_tape_bot (uptr)) { /* if the search has backed into the BOT */ r = MTSE_BOT; /* then quit with an error */ break; } else if (uptr->pos < sizeof (buffer)) /* if less than a full buffer remains */ bufcap = (uint32) uptr->pos /* then reduce the capacity accordingly */ / sizeof (t_mtrlnt); sim_fseek (uptr->fileref, /* seek back to the location */ uptr->pos - bufcap * sizeof (t_mtrlnt), /* corresponding to the start */ SEEK_SET); /* of the buffer */ bufcntr = sim_fread (buffer, sizeof (t_mtrlnt), /* fill the buffer */ bufcap, uptr->fileref); /* with tape metadata */ if (ferror (uptr->fileref)) { /* if a file I/O error occurred */ MT_SET_PNU (uptr); /* then set position not updated */ r = sim_tape_ioerr (uptr); /* report the error and quit */ break; } else /* otherwise reset the capacity */ bufcap = sizeof (buffer) /* to the full size of the buffer */ / sizeof (buffer [0]); } *bc = buffer [--bufcntr]; /* store the metadata marker value */ uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* backspace over the marker */ if (*bc == MTR_TMK) { /* if the marker is a tape mark */ r = MTSE_TMK; /* then quit with tape mark status */ break; } else if (*bc == MTR_GAP) /* otherwise if the marker is a full gap */ runaway_counter -= sizeof_gap; /* then decrement the gap counter */ else if ((*bc & MTR_M_RHGAP) == MTR_RHGAP /* otherwise if the marker */ || *bc == MTR_RRGAP) { /* is a half gap */ uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* then position forward to resync */ bufcntr = 0; /* mark the buffer as invalid to force a read */ *bc = MTR_GAP; /* reset the marker */ runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */ } else { /* otherwise it's a record marker */ sbc = MTR_L (*bc); /* extract the record length */ uptr->pos = uptr->pos - sizeof (t_mtrlnt) /* position to the start */ - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */ sim_fseek (uptr->fileref, /* seek to the data area */ uptr->pos + sizeof (t_mtrlnt), SEEK_SET); } } while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */ if (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */ r = MTSE_RUNAWAY; /* then report it */ break; /* otherwise the operation succeeded */ case MTUF_F_TPC: ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */ sim_fseek (uptr->fileref, ppos, SEEK_SET); /* position */ sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); *bc = tpcbc; /* save rec lnt */ if (ferror (uptr->fileref)) /* error? */ return sim_tape_ioerr (uptr); if (feof (uptr->fileref)) { /* eof? */ r = MTSE_EOM; break; } uptr->pos = ppos; /* spc over record */ if (*bc == MTR_TMK) { /* tape mark? */ r = MTSE_TMK; break; } sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET); break; case MTUF_F_P7B: for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) { sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET); sim_fread (&c, sizeof (uint8), 1, uptr->fileref); if (ferror (uptr->fileref)) /* error? */ return sim_tape_ioerr (uptr); if (feof (uptr->fileref)) { /* eof? */ r = MTSE_EOM; break; } if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0; if (c & P7B_SOR) /* start of record? */ break; } uptr->pos = uptr->pos - sbc; /* update position */ *bc = sbc; /* save rec lnt */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ if (all_eof) /* tape mark? */ r = MTSE_TMK; break; default: return MTSE_FMT; } sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos); return r; } /* Read record forward Inputs: uptr = pointer to tape unit buf = pointer to buffer bc = pointer to returned record length max = maximum record size Outputs: status = operation status exit condition position unit unattached unchanged read error unchanged, PNU set end of file/medium unchanged, PNU set invalid record unchanged, PNU set tape mark updated data record updated data record error updated */ t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint32 f = MT_GET_FMT (uptr); t_mtrlnt i, tbc, rbc; t_addr opos; t_stat st; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecf(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max); opos = uptr->pos; /* old position */ if (MTSE_OK != (st = sim_tape_rdlntf (uptr, &tbc))) /* read rec lnt */ return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) { /* rec out of range? */ MT_SET_PNU (uptr); uptr->pos = opos; return MTSE_INVRL; } i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */ if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); uptr->pos = opos; return sim_tape_ioerr (uptr); } for ( ; i < rbc; i++) /* fill with 0's */ buf[i] = 0; if (f == MTUF_F_P7B) /* p7b? strip SOR */ buf[0] = buf[0] & P7B_DPAR; sim_tape_data_trace(uptr, buf, rbc, "Record Read", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR); return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); } t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback) { t_stat r = SCPE_OK; AIO_CALLSETUP r = sim_tape_rdrecf (uptr, buf, bc, max); AIO_CALL(TOP_RDRF, buf, bc, NULL, max, 0, 0, 0, NULL, callback); return r; } /* Read record reverse Inputs: uptr = pointer to tape unit buf = pointer to buffer bc = pointer to returned record length max = maximum record size Outputs: status = operation status exit condition position unit unattached unchanged read error unchanged end of file unchanged end of medium updated invalid record unchanged tape mark updated data record updated data record error updated */ t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint32 f = MT_GET_FMT (uptr); t_mtrlnt i, rbc, tbc; t_stat st; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecr(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max); if (MTSE_OK != (st = sim_tape_rdlntr (uptr, &tbc))) /* read rec lnt */ return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) /* rec out of range? */ return MTSE_INVRL; i = (t_mtrlnt)sim_fread (buf, sizeof (uint8), rbc, uptr->fileref);/* read record */ if (ferror (uptr->fileref)) /* error? */ return sim_tape_ioerr (uptr); for ( ; i < rbc; i++) /* fill with 0's */ buf[i] = 0; if (f == MTUF_F_P7B) /* p7b? strip SOR */ buf[0] = buf[0] & P7B_DPAR; sim_tape_data_trace(uptr, buf, rbc, "Record Read Reverse", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR); return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); } t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback) { t_stat r = SCPE_OK; AIO_CALLSETUP r = sim_tape_rdrecr (uptr, buf, bc, max); AIO_CALL(TOP_RDRR, buf, bc, NULL, max, 0, 0, 0, NULL, callback); return r; } /* Write record forward Inputs: uptr = pointer to tape unit buf = pointer to buffer bc = record length Outputs: status = operation status exit condition position unit unattached unchanged write protect unchanged write error unchanged, PNU set data record updated */ t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint32 f = MT_GET_FMT (uptr); t_mtrlnt sbc; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrrecf(unit=%d, buf=%p, bc=%d)\n", (int)(uptr-ctx->dptr->units), buf, bc); sim_tape_data_trace(uptr, buf, bc, "Record Write", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR); MT_CLR_PNU (uptr); sbc = MTR_L (bc); if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return MTSE_UNATT; if (sim_tape_wrp (uptr)) /* write prot? */ return MTSE_WRP; if (sbc == 0) /* nothing to do? */ return MTSE_OK; sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */ switch (f) { /* case on format */ case MTUF_F_STD: /* standard */ sbc = MTR_L ((bc + 1) & ~1); /* pad odd length */ case MTUF_F_E11: /* E11 */ sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref); sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref); sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); return sim_tape_ioerr (uptr); } uptr->pos = uptr->pos + sbc + (2 * sizeof (t_mtrlnt)); /* move tape */ break; case MTUF_F_P7B: /* Pierce 7B */ buf[0] = buf[0] | P7B_SOR; /* mark start of rec */ sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref); sim_fwrite (buf, sizeof (uint8), 1, uptr->fileref); /* delimit rec */ if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); return sim_tape_ioerr (uptr); } uptr->pos = uptr->pos + sbc; /* move tape */ break; } sim_tape_data_trace(uptr, buf, sbc, "Record Written", ctx->dptr->dctrl & MTSE_DBG_DAT, MTSE_DBG_STR); return MTSE_OK; } t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback) { t_stat r = SCPE_OK; AIO_CALLSETUP r = sim_tape_wrrecf (uptr, buf, bc); AIO_CALL(TOP_WREC, buf, 0, NULL, 0, bc, 0, 0, NULL, callback); return r; } /* Write metadata forward (internal routine) */ static t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; MT_CLR_PNU (uptr); if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ return MTSE_UNATT; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ if (sim_tape_wrp (uptr)) /* write prot? */ return MTSE_WRP; sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */ sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); return sim_tape_ioerr (uptr); } sim_debug (MTSE_DBG_STR, ctx->dptr, "wr_lnt: lnt: %d, pos: %" T_ADDR_FMT "u\n", dat, uptr->pos); uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* move tape */ return MTSE_OK; } /* Write tape mark */ t_stat sim_tape_wrtmk (UNIT *uptr) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrtmk(unit=%d)\n", (int)(uptr-ctx->dptr->units)); if (MT_GET_FMT (uptr) == MTUF_F_P7B) { /* P7B? */ uint8 buf = P7B_EOF; /* eof mark */ return sim_tape_wrrecf (uptr, &buf, 1); /* write char */ } return sim_tape_wrdata (uptr, MTR_TMK); } t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_wrtmk (uptr); AIO_CALL(TOP_WTMK, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); return r; } /* Write end of medium */ t_stat sim_tape_wreom (UNIT *uptr) { t_stat result; struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreom(unit=%d)\n", (int)(uptr-ctx->dptr->units)); if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */ return MTSE_FMT; result = sim_tape_wrdata (uptr, MTR_EOM); /* write the EOM marker */ uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* restore original tape position */ MT_SET_PNU (uptr); /* indicate that position was not updated */ return result; } t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_wreom (uptr); AIO_CALL(TOP_WEOM, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); return r; } /* Write end of medium-rewind */ t_stat sim_tape_wreomrw (UNIT *uptr) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat r; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wreomrw(unit=%d)\n", (int)(uptr-ctx->dptr->units)); if (MT_GET_FMT (uptr) == MTUF_F_P7B) /* cant do P7B */ return MTSE_FMT; r = sim_tape_wrdata (uptr, MTR_EOM); if (r == MTSE_OK) r = sim_tape_rewind (uptr); return r; } t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_wreomrw (uptr); AIO_CALL(TOP_WEMR, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); return r; } /* Write erase gap Inputs: uptr = pointer to tape unit gaplen = length of gap in tenths of an inch Outputs: status = operation status exit condition position ------------------ ------------------ unit unattached unchanged unsupported format unchanged write protected unchanged read error unchanged, PNU set write error unchanged, PNU set gap written updated An erase gap is represented in the tape image file by a special metadata value. This value is chosen so that it is still recognizable even if it has been "cut in half" by a subsequent data overwrite that does not end on a metadatum-sized boundary. In addition, a range of metadata values are reserved for detection in the reverse direction. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format. This implementation supports erasing gaps in the middle of a populated tape image and will always produce a valid image. It also produces valid images when overwriting gaps with data records, with one exception: a data write that leaves only two bytes of gap remaining will produce an invalid tape. This limitation is deemed acceptable, as it is analogous to the existing limitation that data records cannot overwrite other data records without producing an invalid tape. Because SIMH tape images do not carry physical parameters (e.g., recording density), overwriting a tape image file containing gap metadata is problematic if the density setting is not the same as that used during recording. There is no way to establish a gap of a certain length unequivocally in an image file, so this implementation establishes a gap of a certain number of bytes that reflect the desired gap length at the tape density in bits per inch used during writing. To write an erase gap, the implementation uses one of two approaches, depending on whether or not the current tape position is at EOM. Erasing at EOM presents no special difficulties; gap metadata markers are written for the prescribed number of bytes. If the tape is not at EOM, then erasing must take into account the existing record structure to ensure that a valid tape image is maintained. The general approach is to erase for the nominal number of bytes but to increase that length, if necessary, to ensure that a partially overwritten data record at the end of the gap can be altered to maintain validity. Because the smallest legal tape record requires space for two metadata markers plus two data bytes, an erasure that would leave less than that is increased to consume the entire record. Otherwise, the final record is truncated appropriately by rewriting the leading and trailing length words appropriately. When reading in either direction, gap metadata markers are ignored (skipped) until a record length header, EOF marker, EOM marker, or physical EOF is encountered. Thus, tape images containing gap metadata are transparent to the calling simulator (unless tape runaway support is enabled -- see the notes at "sim_tape_rdlntf" for details). The permissibility of data record lengths that are not multiples of the metadatum size presents a difficulty when reading. If such an "odd length" record is written over a gap, half of a metadata marker will exist immediately after the trailing record length. This condition is detected when reading forward by the appearance of a "reversed" marker. The value appears reversed because the value is made up of half of one marker and half of the next. This is handled by seeking forward two bytes to resync (the stipulation above that the overwrite cannot leave only two bytes of gap means that at least one "whole" metadata marker will follow). Reading in reverse presents a more complex problem, because half of the marker is from the preceding trailing record length marker and therefore could be any of a range of values. However, that range is restricted by the SIMH tape specification requirement that record length metadata values must have bits 30:24 set to zero. This allows unambiguous detection of the condition. The value chosen for gap metadata and the values reserved for "half-gap" detection are: 0xFFFFFFFE - primary gap value 0xFFFEFFFF - reserved (indicates half-gap in forward reads) 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads) 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads) If the tape density has been set via a previous sim_tape_set_dens call, and the tape format is set to SIMH format, then this routine will write a gap of the appropriate size. If the density has not been set, then no action will be taken, and either MTSE_IOERR or MTSE_OK status will be returned, depending on whether SIMH or another format is selected, respectively. A simulator that calls this routine must set the density beforehand; failure to do so is an error. However, calling while another format is enabled is OK and is treated as a no-operation. This allows a device simulator that supports writing erase gaps to use the same code without worrying about the tape format currently selected by the user. */ t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; t_mtrlnt meta, sbc, new_len, rec_size; t_addr gap_pos = uptr->pos; uint32 file_size, marker_count, tape_density; int32 gap_needed; uint32 gap_alloc = 0; /* gap currently allocated from the tape */ const uint32 format = MT_GET_FMT (uptr); /* tape format */ const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */ const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */ if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen); MT_CLR_PNU (uptr); if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */ return MTSE_UNATT; /* then we cannot proceed */ else if (sim_tape_wrp (uptr)) /* otherwise if the unit is write protected */ return MTSE_WRP; /* then we cannot write */ tape_density = bpi [MT_DENS (uptr->dynflags)]; /* get the density of the tape */ if (format != MTUF_F_STD) /* if erase gaps aren't supported by the format */ return MTSE_OK; /* then take no action */ else if (tape_density == 0) /* otherwise if the density is not set */ return MTSE_IOERR; /* then report an I/O error */ else /* otherwise */ gap_needed = (gaplen * tape_density) / 10; /* determine the gap size needed in bytes */ file_size = sim_fsize (uptr->fileref); /* get file size */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ /* Read tape records and allocate to gap until amount required is consumed. Read next metadatum from tape: - EOF or EOM: allocate remainder of bytes needed. - TMK or GAP: allocate sizeof(metadatum) bytes. - Reverse GAP: allocate sizeof(metadatum) / 2 bytes. - Data record: see below. Loop until bytes needed = 0. */ do { sim_fread (&meta, meta_size, 1, uptr->fileref); /* read metadatum */ if (ferror (uptr->fileref)) { /* read error? */ uptr->pos = gap_pos; /* restore original position */ MT_SET_PNU (uptr); /* position not updated */ return sim_tape_ioerr (uptr); /* translate error */ } else uptr->pos = uptr->pos + meta_size; /* move tape over datum */ if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */ gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ gap_needed = 0; } else if ((meta == MTR_GAP) || (meta == MTR_TMK)) { /* gap or tape mark? */ gap_alloc = gap_alloc + meta_size; /* allocate marker space */ gap_needed = gap_needed - meta_size; /* reduce requirement */ } else if (meta == MTR_FHGAP) { /* half gap? */ uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */ gap_needed = gap_needed - meta_size / 2; /* reduce requirement */ } else if (uptr->pos + MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */ gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */ gap_needed = 0; /* allocate remainder */ } /* Allocate a data record: - Determine record size in bytes (including metadata) - If record size - bytes needed < smallest allowed record size, allocate entire record to gap, else allocate needed amount and truncate data record to reflect remainder. */ else { /* data record */ sbc = MTR_L (meta); /* get record data length */ rec_size = ((sbc + 1) & ~1) + meta_size * 2; /* overall size in bytes */ if (rec_size < gap_needed + min_rec_size) { /* rec too small? */ uptr->pos = uptr->pos - meta_size + rec_size; /* position past record */ sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */ gap_alloc = gap_alloc + rec_size; /* allocate record */ gap_needed = gap_needed - rec_size; /* reduce requirement */ } else { /* record size OK */ uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */ new_len = MTR_F (meta) | (sbc - gap_needed); /* truncate to new len */ st = sim_tape_wrdata (uptr, new_len); /* write new rec len */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } uptr->pos = uptr->pos + sbc - gap_needed; /* position to end of data */ st = sim_tape_wrdata (uptr, new_len); /* write new rec len */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ gap_needed = 0; } } } while (gap_needed > 0); uptr->pos = gap_pos; /* reposition to gap start */ if (gap_alloc & (meta_size - 1)) { /* gap size "odd?" */ st = sim_tape_wrdata (uptr, MTR_FHGAP); /* write half gap marker */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } uptr->pos = uptr->pos - meta_size / 2; /* realign position */ gap_alloc = gap_alloc - 2; /* decrease gap to write */ } marker_count = gap_alloc / meta_size; /* count of gap markers */ do { st = sim_tape_wrdata (uptr, MTR_GAP); /* write gap markers */ if (st != MTSE_OK) { /* write OK? */ uptr->pos = gap_pos; /* restore orig pos */ return st; /* PNU was set by wrdata */ } } while (--marker_count > 0); return MTSE_OK; } t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_wrgap (uptr, gaplen); AIO_CALL(TOP_RDRR, NULL, NULL, NULL, 0, 0, gaplen, 0, NULL, callback); return r; } /* Space record forward Inputs: uptr = pointer to tape unit bc = pointer to size of record skipped Outputs: status = operation status exit condition position unit unattached unchanged read error unchanged, PNU set end of file/medium unchanged, PNU set tape mark updated data record updated data record error updated */ t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecf(unit=%d)\n", (int)(uptr-ctx->dptr->units)); st = sim_tape_rdlntf (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_sprecf (uptr, bc); AIO_CALL(TOP_SPRF, NULL, bc, NULL, 0, 0, 0, 0, NULL, callback); return r; } /* Space records forward Inputs: uptr = pointer to tape unit count = count of records to skip skipped = pointer to number of records actually skipped Outputs: status = operation status exit condition position unit unattached unchanged read error unchanged, PNU set end of file/medium unchanged, PNU set tape mark updated data record updated data record error updated */ t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; t_mtrlnt tbc; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsf(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count); *skipped = 0; while (*skipped < count) { /* loopo */ st = sim_tape_sprecf (uptr, &tbc); /* spc rec */ if (st != MTSE_OK) return st; *skipped = *skipped + 1; /* # recs skipped */ } return MTSE_OK; } t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_sprecsf (uptr, count, skipped); AIO_CALL(TOP_SRSF, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); return r; } /* Space record reverse Inputs: uptr = pointer to tape unit bc = pointer to size of records skipped Outputs: status = operation status exit condition position unit unattached unchanged beginning of tape unchanged read error unchanged end of file unchanged end of medium updated tape mark updated data record updated */ t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecr(unit=%d)\n", (int)(uptr-ctx->dptr->units)); if (MT_TST_PNU (uptr)) { MT_CLR_PNU (uptr); *bc = 0; return MTSE_OK; } st = sim_tape_rdlntr (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_sprecr (uptr, bc); AIO_CALL(TOP_SPRR, NULL, bc, NULL, 0, 0, 0, 0, NULL, callback); return r; } /* Space records reverse Inputs: uptr = pointer to tape unit count = count of records to skip skipped = pointer to number of records actually skipped Outputs: status = operation status exit condition position unit unattached unchanged beginning of tape unchanged read error unchanged end of file unchanged end of medium updated tape mark updated data record updated */ t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; t_mtrlnt tbc; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecsr(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count); *skipped = 0; while (*skipped < count) { /* loopo */ st = sim_tape_sprecr (uptr, &tbc); /* spc rec rev */ if (st != MTSE_OK) return st; *skipped = *skipped + 1; /* # recs skipped */ } return MTSE_OK; } t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_sprecsr (uptr, count, skipped); AIO_CALL(TOP_SRSR, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); return r; } /* Space files forward by record Inputs: uptr = pointer to tape unit count = count of files to skip skipped = pointer to number of files actually skipped recsskipped = pointer to number of records skipped check_leot = flag to detect and stop skip between two successive tape marks Outputs: status = operation status exit condition position unit unattached unchanged read error unchanged, PNU set end of file/medium unchanged, PNU set tape mark updated data record updated data record error updated */ t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; t_bool last_tapemark = FALSE; uint32 filerecsskipped; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecf(unit=%d, count=%d, check_leot=%d)\n", (int)(uptr-ctx->dptr->units), count, check_leot); if (check_leot) { t_mtrlnt rbc; st = sim_tape_rdlntr (uptr, &rbc); last_tapemark = (MTSE_TMK == st); if ((st == MTSE_OK) || (st == MTSE_TMK)) sim_tape_rdlntf (uptr, &rbc); } *skipped = 0; *recsskipped = 0; while (*skipped < count) { /* loopo */ while (1) { st = sim_tape_sprecsf (uptr, 0x1ffffff, &filerecsskipped);/* spc recs */ *recsskipped += filerecsskipped; if (st != MTSE_OK) break; } if (st == MTSE_TMK) { *skipped = *skipped + 1; /* # files skipped */ if (check_leot && (filerecsskipped == 0) && last_tapemark) { uint32 filefileskipped; sim_tape_spfilebyrecr (uptr, 1, &filefileskipped, &filerecsskipped); *skipped = *skipped - 1; /* adjust # files skipped */ return MTSE_LEOT; } last_tapemark = TRUE; } else return st; } return MTSE_OK; } t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_spfilebyrecf (uptr, count, skipped, recsskipped, check_leot); AIO_CALL(TOP_SFRF, NULL, skipped, recsskipped, check_leot, count, 0, 0, NULL, callback); return r; } /* Space files forward Inputs: uptr = pointer to tape unit count = count of files to skip skipped = pointer to number of files actually skipped Outputs: status = operation status exit condition position unit unattached unchanged read error unchanged, PNU set end of file/medium unchanged, PNU set tape mark updated data record updated data record error updated */ t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint32 totalrecsskipped; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilef(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count); return sim_tape_spfilebyrecf (uptr, count, skipped, &totalrecsskipped, FALSE); } t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_spfilef (uptr, count, skipped); AIO_CALL(TOP_SPFF, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); return r; } /* Space files reverse by record Inputs: uptr = pointer to tape unit count = count of files to skip skipped = pointer to number of files actually skipped recsskipped = pointer to number of records skipped Outputs: status = operation status exit condition position unit unattached unchanged beginning of tape unchanged read error unchanged end of file unchanged end of medium updated tape mark updated data record updated */ t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat st; uint32 filerecsskipped; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecr(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count); *skipped = 0; *recsskipped = 0; while (*skipped < count) { /* loopo */ while (1) { st = sim_tape_sprecsr (uptr, 0x1ffffff, &filerecsskipped);/* spc recs rev */ *recsskipped += filerecsskipped; if (st != MTSE_OK) break; } if (st == MTSE_TMK) *skipped = *skipped + 1; /* # files skipped */ else return st; } return MTSE_OK; } t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_spfilebyrecr (uptr, count, skipped, recsskipped); AIO_CALL(TOP_SPFR, NULL, skipped, recsskipped, 0, count, 0, 0, NULL, callback); return r; } /* Space files reverse Inputs: uptr = pointer to tape unit count = count of files to skip skipped = pointer to number of files actually skipped Outputs: status = operation status exit condition position unit unattached unchanged beginning of tape unchanged read error unchanged end of file unchanged end of medium updated tape mark updated data record updated */ t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; uint32 totalrecsskipped; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfiler(unit=%d, count=%d)\n", (int)(uptr-ctx->dptr->units), count); return sim_tape_spfilebyrecr (uptr, count, skipped, &totalrecsskipped); } t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_spfiler (uptr, count, skipped); AIO_CALL(TOP_SPFR, NULL, skipped, NULL, 0, count, 0, 0, NULL, callback); return r; } /* Rewind tape */ t_stat sim_tape_rewind (UNIT *uptr) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; if (uptr->flags & UNIT_ATT) { if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n");/* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rewind(unit=%d)\n", (int)(uptr-ctx->dptr->units)); } uptr->pos = 0; MT_CLR_PNU (uptr); return MTSE_OK; } t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_rewind (uptr); AIO_CALL(TOP_RWND, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); return r; } /* Position Tape */ t_stat sim_tape_position (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; t_stat r = MTSE_OK; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_position(unit=%d, flags=0x%X, recs=%d, files=%d)\n", (int)(uptr-ctx->dptr->units), flags, recs, files); *recsskipped = *filesskipped = *objectsskipped = 0; if (flags & MTPOS_M_REW) r = sim_tape_rewind (uptr); if (r != MTSE_OK) return r; if (flags & MTPOS_M_OBJ) { uint32 objs = recs; uint32 skipped; uint32 objsremaining = objs; while (*objectsskipped < objs) { /* loopo */ if (flags & MTPOS_M_REV) /* reverse? */ r = sim_tape_sprecsr (uptr, objsremaining, &skipped); else r = sim_tape_sprecsf (uptr, objsremaining, &skipped); objsremaining = objsremaining - (skipped + ((r == MTSE_TMK) ? 1 : 0)); if ((r == MTSE_TMK) || (r == MTSE_OK)) *objectsskipped = *objectsskipped + skipped + ((r == MTSE_TMK) ? 1 : 0); else return r; } r = MTSE_OK; } else { uint32 fileskiprecs; if (flags & MTPOS_M_REV) /* reverse? */ r = sim_tape_spfilebyrecr (uptr, files, filesskipped, &fileskiprecs); else r = sim_tape_spfilebyrecf (uptr, files, filesskipped, &fileskiprecs, (flags & MTPOS_M_DLE)); if (r != MTSE_OK) return r; if (flags & MTPOS_M_REV) /* reverse? */ r = sim_tape_sprecsr (uptr, recs, recsskipped); else r = sim_tape_sprecsf (uptr, recs, recsskipped); if (r == MTSE_TMK) *filesskipped = *filesskipped + 1; *objectsskipped = fileskiprecs + *filesskipped + *recsskipped; } return r; } t_stat sim_tape_position_a (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; AIO_CALLSETUP r = sim_tape_position (uptr, flags, recs, recsskipped, files, filesskipped, objectsskipped); AIO_CALL(TOP_POSN, NULL, recsskipped, filesskipped, 0, flags, recs, files, objectsskipped, callback); return r; } /* Reset tape */ t_stat sim_tape_reset (UNIT *uptr) { struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; MT_CLR_PNU (uptr); if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; if (ctx == NULL) /* if not properly attached? */ return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_reset(unit=%d)\n", (int)(uptr-ctx->dptr->units)); _sim_tape_io_flush(uptr); AIO_VALIDATE; AIO_UPDATE_QUEUE; return SCPE_OK; } /* Test for BOT */ t_bool sim_tape_bot (UNIT *uptr) { uint32 f = MT_GET_FMT (uptr); return (uptr->pos <= fmts[f].bot)? TRUE: FALSE; } /* Test for end of tape */ t_bool sim_tape_eot (UNIT *uptr) { return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE; } /* Test for write protect */ t_bool sim_tape_wrp (UNIT *uptr) { return ((uptr->flags & MTUF_WRP) || (MT_GET_FMT (uptr) == MTUF_F_TPC))? TRUE: FALSE; } /* Process I/O error */ static t_stat sim_tape_ioerr (UNIT *uptr) { sim_printf ("%s: Magtape library I/O error: %s\n", sim_uname (uptr), strerror (errno)); clearerr (uptr->fileref); return MTSE_IOERR; } /* Set tape format */ t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { uint32 f; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; if (uptr == NULL) return SCPE_IERR; if (cptr == NULL) return SCPE_ARG; for (f = 0; f < MTUF_N_FMT; f++) { if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) { uptr->flags = (uptr->flags & ~MTUF_FMT) | (f << MTUF_V_FMT) | fmts[f].uflags; return SCPE_OK; } } return SCPE_ARG; } /* Show tape format */ t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { int32 f = MT_GET_FMT (uptr); if (fmts[f].name) fprintf (st, "%s format", fmts[f].name); else fprintf (st, "invalid format"); return SCPE_OK; } /* Map a TPC format tape image */ static uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map, uint32 mapsize) { t_addr tpos, leot; t_addr tape_size; t_tpclnt bc, last_bc = 0xFFFF; uint32 had_double_tape_mark = 0; size_t i; uint32 objc, sizec; uint32 *countmap = NULL; uint8 *recbuf = NULL; DEVICE *dptr = find_dev_from_unit (uptr); if ((uptr == NULL) || (uptr->fileref == NULL)) return 0; countmap = (uint32 *)calloc (65536, sizeof(*countmap)); recbuf = (uint8 *)malloc (65536); tape_size = (t_addr)sim_fsize (uptr->fileref); sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape_size: %" T_ADDR_FMT "u\n", tape_size); for (objc = 0, sizec = 0, tpos = 0;; ) { sim_fseek (uptr->fileref, tpos, SEEK_SET); i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref); if (i == 0) /* past or at eof? */ break; if (countmap[bc] == 0) sizec++; ++countmap[bc]; if (map && (objc < mapsize)) map[objc] = tpos; if (bc) { sim_debug (MTSE_DBG_STR, dptr, "tpc_map: %d byte count at pos: %" T_ADDR_FMT "u\n", bc, tpos); if (sim_deb && (dptr->dctrl & MTSE_DBG_STR)) { sim_fread (recbuf, 1, bc, uptr->fileref); sim_data_trace(dptr, uptr, ((dptr->dctrl & MTSE_DBG_DAT) ? recbuf : NULL), "", bc, "Data Record", MTSE_DBG_STR); } } else sim_debug (MTSE_DBG_STR, dptr, "tpc_map: tape mark at pos: %" T_ADDR_FMT "u\n", tpos); objc++; tpos = tpos + ((bc + 1) & ~1) + sizeof (t_tpclnt); if ((bc == 0) && (last_bc == 0)) { /* double tape mark? */ had_double_tape_mark = objc; leot = tpos; } last_bc = bc; } sim_debug (MTSE_DBG_STR, dptr, "tpc_map: objc: %u, different record sizes: %u\n", objc, sizec); for (i=0; i<65535; i++) { if (countmap[i]) { if (i == 0) sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u tape marks\n", countmap[i]); else sim_debug (MTSE_DBG_STR, dptr, "tpc_map: summary - %u %d byte record%s\n", countmap[i], (int)i, (countmap[i] > 1) ? "s" : ""); } } if (((last_bc != 0xffff) && (tpos > tape_size) && (!had_double_tape_mark)) || (!had_double_tape_mark) || ((objc == countmap[0]) && (countmap[0] != 2))) { /* Unreasonable format? */ if (last_bc != 0xffff) sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR unexpected EOT byte count: %d\n", last_bc); if (tpos > tape_size) sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR next record position %" T_ADDR_FMT "u beyond EOT: %" T_ADDR_FMT "u\n", tpos, tape_size); if (objc == countmap[0]) sim_debug (MTSE_DBG_STR, dptr, "tpc_map: ERROR tape cnly contains tape marks\n"); free (countmap); free (recbuf); return 0; } if ((last_bc != 0xffff) && (tpos > tape_size)) { sim_debug (MTSE_DBG_STR, dptr, "tpc_map: WARNING unexpected EOT byte count: %d, double tape mark before %" T_ADDR_FMT "u provides logical EOT\n", last_bc, leot); objc = had_double_tape_mark; tpos = leot; } if (map) map[objc] = tpos; sim_debug (MTSE_DBG_STR, dptr, "tpc_map: OK objc: %d\n", objc); free (countmap); free (recbuf); return objc; } /* Check the basic structure of a SIMH format tape image */ static t_stat sim_tape_simh_check (UNIT *uptr) { return SCPE_OK; } /* Check the basic structure of a E11 format tape image */ static t_stat sim_tape_e11_check (UNIT *uptr) { return SCPE_OK; } /* Find the preceding record in a TPC file */ static t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map) { uint32 lo, hi, p; if (map == NULL) return 0; lo = 0; hi = uptr->hwmark - 1; do { p = (lo + hi) >> 1; if (uptr->pos == map[p]) return ((p == 0)? map[p]: map[p - 1]); else if (uptr->pos < map[p]) hi = p - 1; else lo = p + 1; } while (lo <= hi); return ((p == 0)? map[p]: map[p - 1]); } /* Set tape capacity */ t_stat sim_tape_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { t_addr cap; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG; if (uptr->flags & UNIT_ATT) return SCPE_ALATT; cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r); if (r != SCPE_OK) return SCPE_ARG; uptr->capac = cap * ((t_addr) 1000000); return SCPE_OK; } /* Show tape capacity */ t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { if (uptr->capac) { if (uptr->capac >= (t_addr) 1000000) fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000))); else { if (uptr->capac >= (t_addr) 1000) fprintf (st, "capacity=%dKB", (uint32) (uptr->capac / ((t_addr) 1000))); else fprintf (st, "capacity=%dB", (uint32) uptr->capac); } } else fprintf (st, "unlimited capacity"); return SCPE_OK; } /* Set the tape density. Set the density of the specified tape unit either to the value supplied or to the value represented by the supplied character string. If "desc" is NULL, then "val" must be set to one of the MT_DENS_* constants in sim_tape.h other than MT_DENS_NONE; the supplied value is used as the tape density, and the character string is ignored. Otherwise, "desc" must point at an int32 value containing a set of allowed densities constructed as a bitwise OR of the appropriate MT_*_VALID values. In this case, the string pointed to by "cptr" will be parsed for a decimal value corresponding to the desired density in bits per inch and validated against the set of allowed values. In either case, SCPE_ARG is returned if the density setting is not valid or allowed. If the setting is OK, the new density is set into the unit structure, and SCPE_OK is returned. */ t_stat sim_tape_set_dens (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { uint32 density, new_bpi; t_stat result = SCPE_OK; if (uptr == NULL) /* if the unit pointer is null */ return SCPE_IERR; /* then the caller has screwed up */ else if (desc == NULL) /* otherwise if a validation set was not supplied */ if (val > 0 && val < (int32) BPI_COUNT) /* then if a valid density code was supplied */ uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK) /* then insert the code */ | (val << UNIT_V_DF_TAPE); /* in the unit flags */ else /* otherwise the code is invalid */ return SCPE_ARG; /* so report a bad argument */ else { /* otherwise a validation set was supplied */ if (cptr == NULL || *cptr == 0) /* but if no value is present */ return SCPE_MISVAL; /* then report a missing value */ new_bpi = (uint32) get_uint (cptr, 10, UINT_MAX, &result); /* convert the string value */ if (result != SCPE_OK) /* if the conversion failed */ result = SCPE_ARG; /* then report a bad argument */ else for (density = 0; density < BPI_COUNT; density++) /* otherwise validate the density */ if (new_bpi == bpi [density] /* if it matches a value in the list */ && ((1 << density) & *(const int32 *) desc)) { /* and it's an allowed value */ uptr->dynflags = (uptr->dynflags & ~MTVF_DENS_MASK) /* then store the index of the value */ | density << UNIT_V_DF_TAPE; /* in the unit flags */ return SCPE_OK; /* and return success */ } result = SCPE_ARG; /* if no match, then report a bad argument */ } return result; /* return the result of the operation */ } /* Show the tape density */ t_stat sim_tape_show_dens (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { uint32 tape_density; if (uptr == NULL) /* if the unit pointer is null */ return SCPE_IERR; /* then the caller has screwed up */ else { /* otherwise get the density */ tape_density = bpi [MT_DENS (uptr->dynflags)]; /* of the tape from the unit flags */ if (tape_density) /* if it's set */ fprintf (st, "density=%d bpi", tape_density); /* then report it */ else /* otherwise */ fprintf (st, "density not set"); /* it was never set by the caller */ } return SCPE_OK; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | /* sim_tape.h: simulator tape support library definitions Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 23-Jan-12 MP Added support for Logical EOT detection while positioning 05-Feb-11 MP Add Asynch I/O support 30-Aug-06 JDB Added erase gap support 14-Feb-06 RMS Added variable tape capacity 17-Dec-05 RMS Added write support for Paul Pierce 7b format 02-May-05 RMS Added support for Paul Pierce 7b format */ #ifndef SIM_TAPE_H_ #define SIM_TAPE_H_ 0 #ifdef __cplusplus extern "C" { #endif /* SIMH/E11 tape format */ typedef uint32 t_mtrlnt; /* magtape rec lnt */ #define MTR_TMK 0x00000000 /* tape mark */ #define MTR_EOM 0xFFFFFFFF /* end of medium */ #define MTR_GAP 0xFFFFFFFE /* primary gap */ #define MTR_RRGAP 0xFFFFFFFF /* reverse read half gap */ #define MTR_FHGAP 0xFFFEFFFF /* fwd half gap (overwrite) */ #define MTR_RHGAP 0xFFFF0000 /* rev half gap (overwrite) */ #define MTR_M_RHGAP (~0x000080FF) /* range mask for rev gap */ #define MTR_MAXLEN 0x00FFFFFF /* max len is 24b */ #define MTR_ERF 0x80000000 /* error flag */ #define MTR_F(x) ((x) & MTR_ERF) /* record error flg */ #define MTR_L(x) ((x) & ~MTR_ERF) /* record length */ /* TPC tape format */ typedef uint16 t_tpclnt; /* magtape rec lnt */ /* P7B tape format */ #define P7B_SOR 0x80 /* start of record */ #define P7B_PAR 0x40 /* parity */ #define P7B_DATA 0x3F /* data */ #define P7B_DPAR (P7B_PAR|P7B_DATA) /* data and parity */ #define P7B_EOF 0x0F /* eof character */ #define TPC_TMK 0x0000 /* tape mark */ /* Unit flags */ #define MTUF_V_PNU (UNIT_V_UF + 0) /* position not upd */ #define MTUF_V_WLK (UNIT_V_UF + 1) /* write locked */ #define MTUF_V_FMT (UNIT_V_UF + 2) /* tape file format */ #define MTUF_W_FMT 3 /* 3b of formats */ #define MTUF_N_FMT (1u << MTUF_W_FMT) /* number of formats */ #define MTUF_M_FMT ((1u << MTUF_W_FMT) - 1) #define MTUF_F_STD 0 /* SIMH format */ #define MTUF_F_E11 1 /* E11 format */ #define MTUF_F_TPC 2 /* TPC format */ #define MTUF_F_P7B 3 /* P7B format */ #define MUTF_F_TDF 4 /* TDF format */ #define MTUF_V_UF (MTUF_V_FMT + MTUF_W_FMT) #define MTUF_PNU (1u << MTUF_V_PNU) #define MTUF_WLK (1u << MTUF_V_WLK) #define MTUF_FMT (MTUF_M_FMT << MTUF_V_FMT) #define MTUF_WRP (MTUF_WLK | UNIT_RO) #define MT_F_STD (MTUF_F_STD << MTUF_V_FMT) #define MT_F_E11 (MTUF_F_E11 << MTUF_V_FMT) #define MT_F_TPC (MTUF_F_TPC << MTUF_V_FMT) #define MT_F_P7B (MTUF_F_P7B << MTUF_V_FMT) #define MT_F_TDF (MTUF_F_TDF << MTUF_V_FMT) #define MT_SET_PNU(u) (u)->flags = (u)->flags | MTUF_PNU #define MT_CLR_PNU(u) (u)->flags = (u)->flags & ~MTUF_PNU #define MT_TST_PNU(u) ((u)->flags & MTUF_PNU) #define MT_GET_FMT(u) (((u)->flags >> MTUF_V_FMT) & MTUF_M_FMT) /* sim_tape_position Position Flags */ #define MTPOS_V_REW 3 #define MTPOS_M_REW (1u << MTPOS_V_REW) /* Rewind First */ #define MTPOS_V_REV 2 #define MTPOS_M_REV (1u << MTPOS_V_REV) /* Reverse Direction */ #define MTPOS_V_OBJ 1 #define MTPOS_M_OBJ (1u << MTPOS_V_OBJ) /* Objects vs Records/Files */ #define MTPOS_V_DLE 4 #define MTPOS_M_DLE (1u << MTPOS_V_DLE) /* Detect LEOT */ /* Tape density values */ #define MT_DENS_NONE 0 /* density not set */ #define MT_DENS_200 1 /* 200 bpi NRZI */ #define MT_DENS_556 2 /* 556 bpi NRZI */ #define MT_DENS_800 3 /* 800 bpi NRZI */ #define MT_DENS_1600 4 /* 1600 bpi PE */ #define MT_DENS_6250 5 /* 6250 bpi GCR */ #define MTVF_DENS_MASK (((1u << UNIT_S_DF_TAPE) - 1) << UNIT_V_DF_TAPE) #define MT_DENS(f) (((f) & MTVF_DENS_MASK) >> UNIT_V_DF_TAPE) #define MT_NONE_VALID (1u << MT_DENS_NONE) /* density not set is valid */ #define MT_200_VALID (1u << MT_DENS_200) /* 200 bpi is valid */ #define MT_556_VALID (1u << MT_DENS_556) /* 556 bpi is valid */ #define MT_800_VALID (1u << MT_DENS_800) /* 800 bpi is valid */ #define MT_1600_VALID (1u << MT_DENS_1600) /* 1600 bpi is valid */ #define MT_6250_VALID (1u << MT_DENS_6250) /* 6250 bpi is valid */ /* Return status codes */ #define MTSE_OK 0 /* no error */ #define MTSE_TMK 1 /* tape mark */ #define MTSE_UNATT 2 /* unattached */ #define MTSE_IOERR 3 /* IO error */ #define MTSE_INVRL 4 /* invalid rec lnt */ #define MTSE_FMT 5 /* invalid format */ #define MTSE_BOT 6 /* beginning of tape */ #define MTSE_EOM 7 /* end of medium */ #define MTSE_RECE 8 /* error in record */ #define MTSE_WRP 9 /* write protected */ #define MTSE_LEOT 10 /* Logical End Of Tape */ #define MTSE_RUNAWAY 11 /* tape runaway */ typedef void (*TAPE_PCALLBACK)(UNIT *unit, t_stat status); /* Tape Internal Debug flags */ #define MTSE_DBG_DAT 0x0400000 /* Debug Data */ #define MTSE_DBG_POS 0x0800000 /* Debug Positioning activities */ #define MTSE_DBG_STR 0x1000000 /* Debug Tape Structure */ /* Prototypes */ t_stat sim_tape_attach_ex (UNIT *uptr, const char *cptr, uint32 dbit, int completion_delay); t_stat sim_tape_attach (UNIT *uptr, CONST char *cptr); t_stat sim_tape_detach (UNIT *uptr); t_stat sim_tape_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); t_stat sim_tape_rdrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback); t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); t_stat sim_tape_rdrecr_a (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max, TAPE_PCALLBACK callback); t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc); t_stat sim_tape_wrrecf_a (UNIT *uptr, uint8 *buf, t_mtrlnt bc, TAPE_PCALLBACK callback); t_stat sim_tape_wrtmk (UNIT *uptr); t_stat sim_tape_wrtmk_a (UNIT *uptr, TAPE_PCALLBACK callback); t_stat sim_tape_wreom (UNIT *uptr); t_stat sim_tape_wreom_a (UNIT *uptr, TAPE_PCALLBACK callback); t_stat sim_tape_wreomrw (UNIT *uptr); t_stat sim_tape_wreomrw_a (UNIT *uptr, TAPE_PCALLBACK callback); t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen); t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback); t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc); t_stat sim_tape_sprecf_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback); t_stat sim_tape_sprecsf (UNIT *uptr, uint32 count, uint32 *skipped); t_stat sim_tape_sprecsf_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); t_stat sim_tape_spfilef (UNIT *uptr, uint32 count, uint32 *skipped); t_stat sim_tape_spfilef_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); t_stat sim_tape_spfilebyrecf (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot); t_stat sim_tape_spfilebyrecf_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, t_bool check_leot, TAPE_PCALLBACK callback); t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc); t_stat sim_tape_sprecr_a (UNIT *uptr, t_mtrlnt *bc, TAPE_PCALLBACK callback); t_stat sim_tape_sprecsr (UNIT *uptr, uint32 count, uint32 *skipped); t_stat sim_tape_sprecsr_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); t_stat sim_tape_spfiler (UNIT *uptr, uint32 count, uint32 *skipped); t_stat sim_tape_spfiler_a (UNIT *uptr, uint32 count, uint32 *skipped, TAPE_PCALLBACK callback); t_stat sim_tape_spfilebyrecr (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped); t_stat sim_tape_spfilebyrecr_a (UNIT *uptr, uint32 count, uint32 *skipped, uint32 *recsskipped, TAPE_PCALLBACK callback); t_stat sim_tape_rewind (UNIT *uptr); t_stat sim_tape_rewind_a (UNIT *uptr, TAPE_PCALLBACK callback); t_stat sim_tape_position (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recskipped, uint32 files, uint32 *fileskipped, uint32 *objectsskipped); t_stat sim_tape_position_a (UNIT *uptr, uint32 flags, uint32 recs, uint32 *recsskipped, uint32 files, uint32 *filesskipped, uint32 *objectsskipped, TAPE_PCALLBACK callback); t_stat sim_tape_reset (UNIT *uptr); t_bool sim_tape_bot (UNIT *uptr); t_bool sim_tape_wrp (UNIT *uptr); t_bool sim_tape_eot (UNIT *uptr); t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat sim_tape_set_capac (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat sim_tape_set_dens (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat sim_tape_show_dens (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat sim_tape_set_asynch (UNIT *uptr, int latency); t_stat sim_tape_clr_asynch (UNIT *uptr); #ifdef __cplusplus } #endif #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 | /* sim_timer.c: simulator timer library Copyright (c) 1993-2010, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 21-Oct-11 MP Fixed throttling in several ways: - Sleep for the observed clock tick size while throttling - Recompute the throttling wait once every 10 seconds to account for varying instruction mixes during different phases of a simulator execution or to accommodate the presence of other load on the host system. - Each of the pre-existing throttling modes (Kcps, Mcps, and %) all compute the appropriate throttling interval dynamically. These dynamic computations assume that 100% of the host CPU is dedicated to the current simulator during this computation. This assumption may not always be true and under certain conditions may never provide a way to correctly determine the appropriate throttling wait. An additional throttling mode has been added which allows the simulator operator to explicitly state the desired throttling wait parameters. These are specified by: SET THROT insts/delay where 'insts' is the number of instructions to execute before sleeping for 'delay' milliseconds. 22-Apr-11 MP Fixed Asynch I/O support to reasonably account cycles when an idle wait is terminated by an external event 05-Jan-11 MP Added Asynch I/O support 29-Dec-10 MP Fixed clock resolution determination for Unix platforms 22-Sep-08 RMS Added "stability threshold" for idle routine 27-May-08 RMS Fixed bug in Linux idle routines (from Walter Mueller) 18-Jun-07 RMS Modified idle to exclude counted delays 22-Mar-07 RMS Added sim_rtcn_init_all 17-Oct-06 RMS Added idle support (based on work by Mark Pizzolato) Added throttle support 16-Aug-05 RMS Fixed C++ declaration and cast problems 02-Jan-04 RMS Split out from SCP This library includes the following routines: sim_timer_init - initialize timing system sim_rtc_init - initialize calibration sim_rtc_calb - calibrate clock sim_idle - virtual machine idle sim_os_msec - return elapsed time in msec sim_os_sleep - sleep specified number of seconds sim_os_ms_sleep - sleep specified number of milliseconds sim_idle_ms_sleep - sleep specified number of milliseconds or until awakened by an asynchronous event sim_timespec_diff subtract two timespec values sim_timer_activate_after schedule unit for specific time sim_timer_activate_time determine activation time sim_timer_activate_time_usecs determine activation time in usecs sim_rom_read_with_delay delay for default or specified delay sim_get_rom_delay_factor get current or initialize 1usec delay factor sim_set_rom_delay_factor set specific delay factor The calibration, idle, and throttle routines are OS-independent; the _os_ routines are not. */ #define NOT_MUX_USING_CODE /* sim_tmxr library provider or agnostic */ #include "sim_defs.h" #include <ctype.h> #include <math.h> #define SIM_INTERNAL_CLK (SIM_NTIMERS+(1<<30)) #define SIM_INTERNAL_UNIT sim_internal_timer_unit #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif #ifndef MAX #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #endif uint32 sim_idle_ms_sleep (unsigned int msec); /* MS_MIN_GRANULARITY exists here so that timing behavior for hosts systems */ /* with slow clock ticks can be assessed and tested without actually having */ /* that slow a clock tick on the development platform */ //#define MS_MIN_GRANULARITY 20 /* Uncomment to simulate 20ms host tick size.*/ /* some Solaris and BSD hosts come this way */ #if defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) uint32 real_sim_idle_ms_sleep (unsigned int msec); uint32 real_sim_os_msec (void); uint32 real_sim_os_ms_sleep (unsigned int msec); static uint32 real_sim_os_sleep_min_ms = 0; static uint32 real_sim_os_sleep_inc_ms = 0; uint32 sim_idle_ms_sleep (unsigned int msec) { uint32 real_start = real_sim_os_msec (); uint32 start = (real_start / MS_MIN_GRANULARITY) * MS_MIN_GRANULARITY; uint32 tick_left; if (msec == 0) return 0; if (real_start == start) tick_left = 0; else tick_left = MS_MIN_GRANULARITY - (real_start - start); if (msec <= tick_left) real_sim_idle_ms_sleep (tick_left); else real_sim_idle_ms_sleep (((msec + MS_MIN_GRANULARITY - 1) / MS_MIN_GRANULARITY) * MS_MIN_GRANULARITY); return (sim_os_msec () - start); } uint32 sim_os_msec (void) { return (real_sim_os_msec ()/MS_MIN_GRANULARITY)*MS_MIN_GRANULARITY; } uint32 sim_os_ms_sleep (unsigned int msec) { msec = MS_MIN_GRANULARITY*((msec+MS_MIN_GRANULARITY-1)/MS_MIN_GRANULARITY); return real_sim_os_ms_sleep (msec); } #endif /* defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) */ t_bool sim_idle_enab = FALSE; /* global flag */ volatile t_bool sim_idle_wait = FALSE; /* global flag */ static int32 sim_calb_tmr = -1; /* the system calibrated timer */ static int32 sim_calb_tmr_last = -1; /* shadow value when at sim> prompt */ static double sim_inst_per_sec_last = 0; /* shadow value when at sim> prompt */ static uint32 sim_idle_rate_ms = 0; static uint32 sim_os_sleep_min_ms = 0; static uint32 sim_os_sleep_inc_ms = 0; static uint32 sim_os_clock_resoluton_ms = 0; static uint32 sim_os_tick_hz = 0; static uint32 sim_idle_stable = SIM_IDLE_STDFLT; static uint32 sim_idle_calib_pct = 0; static uint32 sim_rom_delay = 0; static uint32 sim_throt_ms_start = 0; static uint32 sim_throt_ms_stop = 0; static uint32 sim_throt_type = 0; static uint32 sim_throt_val = 0; static uint32 sim_throt_state = SIM_THROT_STATE_INIT; static double sim_throt_cps; static double sim_throt_inst_start; static uint32 sim_throt_sleep_time = 0; static int32 sim_throt_wait = 0; static UNIT *sim_clock_unit[SIM_NTIMERS+1] = {NULL}; UNIT * volatile sim_clock_cosched_queue[SIM_NTIMERS+1] = {NULL}; static int32 sim_cosched_interval[SIM_NTIMERS+1]; static t_bool sim_catchup_ticks = TRUE; #if defined (SIM_ASYNCH_CLOCKS) && !defined (SIM_ASYNCH_IO) #undef SIM_ASYNCH_CLOCKS #endif t_bool sim_asynch_timer = FALSE; #if defined (SIM_ASYNCH_CLOCKS) UNIT * volatile sim_wallclock_queue = QUEUE_LIST_END; UNIT * volatile sim_wallclock_entry = NULL; #endif #define sleep1Samples 100 static uint32 _compute_minimum_sleep (void) { uint32 i, tot, tim; sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); #if defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) real_sim_idle_ms_sleep (1); /* Start sampling on a tick boundary */ for (i = 0, tot = 0; i < sleep1Samples; i++) tot += real_sim_idle_ms_sleep (1); tim = tot / sleep1Samples; /* Truncated average */ real_sim_os_sleep_min_ms = tim; real_sim_idle_ms_sleep (1); /* Start sampling on a tick boundary */ for (i = 0, tot = 0; i < sleep1Samples; i++) tot += real_sim_idle_ms_sleep (real_sim_os_sleep_min_ms + 1); tim = tot / sleep1Samples; /* Truncated average */ real_sim_os_sleep_inc_ms = tim - real_sim_os_sleep_min_ms; #endif /* defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) */ sim_idle_ms_sleep (1); /* Start sampling on a tick boundary */ for (i = 0, tot = 0; i < sleep1Samples; i++) tot += sim_idle_ms_sleep (1); tim = tot / sleep1Samples; /* Truncated average */ sim_os_sleep_min_ms = tim; sim_idle_ms_sleep (1); /* Start sampling on a tick boundary */ for (i = 0, tot = 0; i < sleep1Samples; i++) tot += sim_idle_ms_sleep (sim_os_sleep_min_ms + 1); tim = tot / sleep1Samples; /* Truncated average */ sim_os_sleep_inc_ms = tim - sim_os_sleep_min_ms; sim_os_set_thread_priority (PRIORITY_NORMAL); return sim_os_sleep_min_ms; } #if defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) #define sim_idle_ms_sleep real_sim_idle_ms_sleep #define sim_os_msec real_sim_os_msec #define sim_os_ms_sleep real_sim_os_ms_sleep #endif /* defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) */ #if defined(SIM_ASYNCH_IO) uint32 sim_idle_ms_sleep (unsigned int msec) { uint32 start_time = sim_os_msec(); struct timespec done_time; t_bool timedout = FALSE; clock_gettime(CLOCK_REALTIME, &done_time); done_time.tv_sec += (msec/1000); done_time.tv_nsec += 1000000*(msec%1000); if (done_time.tv_nsec > 1000000000) { done_time.tv_sec += done_time.tv_nsec/1000000000; done_time.tv_nsec = done_time.tv_nsec%1000000000; } pthread_mutex_lock (&sim_asynch_lock); sim_idle_wait = TRUE; if (!pthread_cond_timedwait (&sim_asynch_wake, &sim_asynch_lock, &done_time)) sim_asynch_check = 0; /* force check of asynch queue now */ else timedout = TRUE; sim_idle_wait = FALSE; pthread_mutex_unlock (&sim_asynch_lock); if (!timedout) { AIO_UPDATE_QUEUE; } return sim_os_msec() - start_time; } #else uint32 sim_idle_ms_sleep (unsigned int msec) { return sim_os_ms_sleep (msec); } #endif /* Mark the need for the sim_os_set_thread_priority routine, */ /* allowing the feature and/or platform dependent code to provide it */ #define NEED_THREAD_PRIORITY /* If we've got pthreads support then use pthreads mechanisms */ #if defined(USE_READER_THREAD) #undef NEED_THREAD_PRIORITY #if defined(_WIN32) /* On Windows there are several potentially disjoint threading APIs */ /* in use (base win32 pthreads, libSDL provided threading, and direct */ /* calls to beginthreadex), so go directly to the Win32 threading APIs */ /* to manage thread priority */ t_stat sim_os_set_thread_priority (int below_normal_above) { const static int val[3] = {THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL}; if ((below_normal_above < -1) || (below_normal_above > 1)) return SCPE_ARG; SetThreadPriority (GetCurrentThread(), val[1 + below_normal_above]); return SCPE_OK; } #else /* Native pthreads priority implementation */ t_stat sim_os_set_thread_priority (int below_normal_above) { int sched_policy, min_prio, max_prio; struct sched_param sched_priority; if ((below_normal_above < -1) || (below_normal_above > 1)) return SCPE_ARG; pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); min_prio = sched_get_priority_min(sched_policy); max_prio = sched_get_priority_max(sched_policy); switch (below_normal_above) { case PRIORITY_BELOW_NORMAL: sched_priority.sched_priority = min_prio; break; case PRIORITY_NORMAL: sched_priority.sched_priority = (max_prio + min_prio) / 2; break; case PRIORITY_ABOVE_NORMAL: sched_priority.sched_priority = max_prio; break; } pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); return SCPE_OK; } #endif #endif /* defined(USE_READER_THREAD) */ /* OS-dependent timer and clock routines */ /* VMS */ #if defined (VMS) #if defined (__VAX) #define sys$gettim SYS$GETTIM #define sys$setimr SYS$SETIMR #define lib$emul LIB$EMUL #define sys$waitfr SYS$WAITFR #define lib$subx LIB$SUBX #define lib$ediv LIB$EDIV #endif #include <starlet.h> #include <lib$routines.h> #include <unistd.h> const t_bool rtc_avail = TRUE; uint32 sim_os_msec (void) { uint32 quo, htod, tod[2]; int32 i; sys$gettim (tod); /* time 0.1usec */ /* To convert to msec, must divide a 64b quantity by 10000. This is actually done by dividing the 96b quantity 0'time by 10000, producing 64b of quotient, the high 32b of which are discarded. This can probably be done by a clever multiply... */ quo = htod = 0; for (i = 0; i < 64; i++) { /* 64b quo */ htod = (htod << 1) | ((tod[1] >> 31) & 1); /* shift divd */ tod[1] = (tod[1] << 1) | ((tod[0] >> 31) & 1); tod[0] = tod[0] << 1; quo = quo << 1; /* shift quo */ if (htod >= 10000) { /* divd work? */ htod = htod - 10000; /* subtract */ quo = quo | 1; /* set quo bit */ } } return quo; } void sim_os_sleep (unsigned int sec) { sleep (sec); return; } uint32 sim_os_ms_sleep_init (void) { return _compute_minimum_sleep (); } uint32 sim_os_ms_sleep (unsigned int msec) { uint32 stime = sim_os_msec (); uint32 qtime[2]; int32 nsfactor = -10000; static int32 zero = 0; lib$emul (&msec, &nsfactor, &zero, qtime); sys$setimr (2, qtime, 0, 0); sys$waitfr (2); return sim_os_msec () - stime; } #ifdef NEED_CLOCK_GETTIME int clock_gettime(int clk_id, struct timespec *tp) { uint32 secs, ns, tod[2], unixbase[2] = {0xd53e8000, 0x019db1de}; if (clk_id != CLOCK_REALTIME) return -1; sys$gettim (tod); /* time 0.1usec */ lib$subx(tod, unixbase, tod); /* convert to unix base */ lib$ediv(&10000000, tod, &secs, &ns); /* isolate seconds & 100ns parts */ tp->tv_sec = secs; tp->tv_nsec = ns*100; return 0; } #endif /* CLOCK_REALTIME */ #elif defined (_WIN32) /* Win32 routines */ const t_bool rtc_avail = TRUE; uint32 sim_os_msec (void) { return timeGetTime (); } void sim_os_sleep (unsigned int sec) { Sleep (sec * 1000); return; } void sim_timer_exit (void) { timeEndPeriod (sim_idle_rate_ms); return; } uint32 sim_os_ms_sleep_init (void) { TIMECAPS timers; if (timeGetDevCaps (&timers, sizeof (timers)) != TIMERR_NOERROR) return 0; if (timers.wPeriodMin == 0) return 0; if (timeBeginPeriod (timers.wPeriodMin) != TIMERR_NOERROR) return 0; atexit (sim_timer_exit); /* return measured actual minimum sleep time */ return _compute_minimum_sleep (); } uint32 sim_os_ms_sleep (unsigned int msec) { uint32 stime = sim_os_msec(); Sleep (msec); return sim_os_msec () - stime; } #if defined(NEED_CLOCK_GETTIME) int clock_gettime(int clk_id, struct timespec *tp) { t_uint64 now, unixbase; if (clk_id != CLOCK_REALTIME) return -1; unixbase = 116444736; unixbase *= 1000000000; GetSystemTimeAsFileTime((FILETIME*)&now); now -= unixbase; tp->tv_sec = (long)(now/10000000); tp->tv_nsec = (now%10000000)*100; return 0; } #endif #elif defined (__OS2__) /* OS/2 routines, from Bruce Ray */ const t_bool rtc_avail = FALSE; uint32 sim_os_msec (void) { return 0; } void sim_os_sleep (unsigned int sec) { return; } uint32 sim_os_ms_sleep_init (void) { return 0; } uint32 sim_os_ms_sleep (unsigned int msec) { return 0; } /* Metrowerks CodeWarrior Macintosh routines, from Ben Supnik */ #elif defined (__MWERKS__) && defined (macintosh) #include <Timer.h> #include <Mactypes.h> #include <sioux.h> #include <unistd.h> #include <siouxglobals.h> #define NANOS_PER_MILLI 1000000 #define MILLIS_PER_SEC 1000 const t_bool rtc_avail = TRUE; uint32 sim_os_msec (void) { unsigned long long micros; UnsignedWide macMicros; unsigned long millis; Microseconds (&macMicros); micros = *((unsigned long long *) &macMicros); millis = micros / 1000LL; return (uint32) millis; } void sim_os_sleep (unsigned int sec) { sleep (sec); return; } uint32 sim_os_ms_sleep_init (void) { return _compute_minimum_sleep (); } uint32 sim_os_ms_sleep (unsigned int milliseconds) { uint32 stime = sim_os_msec (); struct timespec treq; treq.tv_sec = milliseconds / MILLIS_PER_SEC; treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; (void) nanosleep (&treq, NULL); return sim_os_msec () - stime; } #if defined(NEED_CLOCK_GETTIME) int clock_gettime(int clk_id, struct timespec *tp) { struct timeval cur; if (clk_id != CLOCK_REALTIME) return -1; gettimeofday (&cur, NULL); tp->tv_sec = cur.tv_sec; tp->tv_nsec = cur.tv_usec*1000; return 0; } #endif #else /* UNIX routines */ #include <time.h> #include <sys/time.h> #include <unistd.h> #define NANOS_PER_MILLI 1000000 #define MILLIS_PER_SEC 1000 const t_bool rtc_avail = TRUE; uint32 sim_os_msec (void) { struct timeval cur; struct timezone foo; uint32 msec; gettimeofday (&cur, &foo); msec = (((uint32) cur.tv_sec) * 1000) + (((uint32) cur.tv_usec) / 1000); return msec; } void sim_os_sleep (unsigned int sec) { sleep (sec); return; } uint32 sim_os_ms_sleep_init (void) { return _compute_minimum_sleep (); } #if !defined(_POSIX_SOURCE) #ifdef NEED_CLOCK_GETTIME typedef int clockid_t; int clock_gettime(clockid_t clk_id, struct timespec *tp) { struct timeval cur; struct timezone foo; if (clk_id != CLOCK_REALTIME) return -1; gettimeofday (&cur, &foo); tp->tv_sec = cur.tv_sec; tp->tv_nsec = cur.tv_usec*1000; return 0; } #endif /* CLOCK_REALTIME */ #endif /* !defined(_POSIX_SOURCE) && defined(SIM_ASYNCH_IO) */ uint32 sim_os_ms_sleep (unsigned int milliseconds) { uint32 stime = sim_os_msec (); struct timespec treq; treq.tv_sec = milliseconds / MILLIS_PER_SEC; treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; (void) nanosleep (&treq, NULL); return sim_os_msec () - stime; } #if defined(NEED_THREAD_PRIORITY) #undef NEED_THREAD_PRIORITY #include <sys/time.h> #include <sys/resource.h> t_stat sim_os_set_thread_priority (int below_normal_above) { if ((below_normal_above < -1) || (below_normal_above > 1)) return SCPE_ARG; errno = 0; switch (below_normal_above) { case PRIORITY_BELOW_NORMAL: if ((getpriority (PRIO_PROCESS, 0) <= 0) && /* at or above normal pri? */ (errno == 0)) setpriority (PRIO_PROCESS, 0, 10); break; case PRIORITY_NORMAL: if (getpriority (PRIO_PROCESS, 0) != 0) /* at or above normal pri? */ setpriority (PRIO_PROCESS, 0, 0); break; case PRIORITY_ABOVE_NORMAL: if ((getpriority (PRIO_PROCESS, 0) <= 0) && /* at or above normal pri? */ (errno == 0)) setpriority (PRIO_PROCESS, 0, -10); break; } return SCPE_OK; } #endif /* defined(NEED_THREAD_PRIORITY) */ #endif /* If one hasn't been provided yet, then just stub it */ #if defined(NEED_THREAD_PRIORITY) t_stat sim_os_set_thread_priority (int below_normal_above) { return SCPE_OK; } #endif #if defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) /* Make sure to use the substitute routines */ #undef sim_idle_ms_sleep #undef sim_os_msec #undef sim_os_ms_sleep #endif /* defined(MS_MIN_GRANULARITY) && (MS_MIN_GRANULARITY != 1) */ /* diff = min - sub */ void sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub) { /* move the minuend value to the difference and operate there. */ *diff = *min; /* Borrow as needed for the nsec value */ while (sub->tv_nsec > diff->tv_nsec) { --diff->tv_sec; diff->tv_nsec += 1000000000; } diff->tv_nsec -= sub->tv_nsec; diff->tv_sec -= sub->tv_sec; /* Normalize the result */ while (diff->tv_nsec > 1000000000) { ++diff->tv_sec; diff->tv_nsec -= 1000000000; } } /* Forward declarations */ static double _timespec_to_double (struct timespec *time); static void _double_to_timespec (struct timespec *time, double dtime); static t_bool _rtcn_tick_catchup_check (int32 tmr, int32 time); static void _rtcn_configure_calibrated_clock (int32 newtmr); static t_bool _sim_coschedule_cancel (UNIT *uptr); static t_bool _sim_wallclock_cancel (UNIT *uptr); static t_bool _sim_wallclock_is_active (UNIT *uptr); t_stat sim_timer_show_idle_mode (FILE* st, UNIT* uptr, int32 val, CONST void * desc); #if defined(SIM_ASYNCH_CLOCKS) static int sim_timespec_compare (struct timespec *a, struct timespec *b) { while (a->tv_nsec > 1000000000) { a->tv_nsec -= 1000000000; ++a->tv_sec; } while (b->tv_nsec > 1000000000) { b->tv_nsec -= 1000000000; ++b->tv_sec; } if (a->tv_sec < b->tv_sec) return -1; if (a->tv_sec > b->tv_sec) return 1; if (a->tv_nsec < b->tv_nsec) return -1; if (a->tv_nsec > b->tv_nsec) return 1; else return 0; } #endif /* defined(SIM_ASYNCH_CLOCKS) */ /* OS independent clock calibration package */ static int32 rtc_ticks[SIM_NTIMERS+1] = { 0 }; /* ticks */ static uint32 rtc_hz[SIM_NTIMERS+1] = { 0 }; /* tick rate */ static uint32 rtc_last_hz[SIM_NTIMERS+1] = { 0 }; /* prior tick rate */ static uint32 rtc_rtime[SIM_NTIMERS+1] = { 0 }; /* real time */ static uint32 rtc_vtime[SIM_NTIMERS+1] = { 0 }; /* virtual time */ static double rtc_gtime[SIM_NTIMERS+1] = { 0 }; /* instruction time */ static uint32 rtc_nxintv[SIM_NTIMERS+1] = { 0 }; /* next interval */ static int32 rtc_based[SIM_NTIMERS+1] = { 0 }; /* base delay */ static int32 rtc_currd[SIM_NTIMERS+1] = { 0 }; /* current delay */ static int32 rtc_initd[SIM_NTIMERS+1] = { 0 }; /* initial delay */ static uint32 rtc_elapsed[SIM_NTIMERS+1] = { 0 }; /* sec since init */ static uint32 rtc_calibrations[SIM_NTIMERS+1] = { 0 }; /* calibration count */ static double rtc_clock_skew_max[SIM_NTIMERS+1] = { 0 }; /* asynchronous max skew */ static double rtc_clock_start_gtime[SIM_NTIMERS+1] = { 0 };/* reference instruction time for clock */ static double rtc_clock_tick_size[SIM_NTIMERS+1] = { 0 }; /* 1/hz */ static uint32 rtc_calib_initializations[SIM_NTIMERS+1] = { 0 };/* Initialization Count */ static double rtc_calib_tick_time[SIM_NTIMERS+1] = { 0 }; /* ticks time */ static double rtc_calib_tick_time_tot[SIM_NTIMERS+1] = { 0 };/* ticks time - total*/ static uint32 rtc_calib_ticks_acked[SIM_NTIMERS+1] = { 0 };/* ticks Acked */ static uint32 rtc_calib_ticks_acked_tot[SIM_NTIMERS+1] = { 0 };/* ticks Acked - total */ static uint32 rtc_clock_ticks[SIM_NTIMERS+1] = { 0 };/* ticks delivered since catchup base */ static uint32 rtc_clock_ticks_tot[SIM_NTIMERS+1] = { 0 };/* ticks delivered since catchup base - total */ static double rtc_clock_init_base_time[SIM_NTIMERS+1] = { 0 };/* reference time for clock initialization */ static double rtc_clock_tick_start_time[SIM_NTIMERS+1] = { 0 };/* reference time when ticking started */ static double rtc_clock_catchup_base_time[SIM_NTIMERS+1] = { 0 };/* reference time for catchup ticks */ static uint32 rtc_clock_catchup_ticks[SIM_NTIMERS+1] = { 0 };/* Record of catchups */ static uint32 rtc_clock_catchup_ticks_tot[SIM_NTIMERS+1] = { 0 };/* Record of catchups - total */ static t_bool rtc_clock_catchup_pending[SIM_NTIMERS+1] = { 0 };/* clock tick catchup pending */ static t_bool rtc_clock_catchup_eligible[SIM_NTIMERS+1] = { 0 };/* clock tick catchup eligible */ static uint32 rtc_clock_time_idled[SIM_NTIMERS+1] = { 0 };/* total time idled */ static uint32 rtc_clock_time_idled_last[SIM_NTIMERS+1] = { 0 };/* total time idled */ static uint32 rtc_clock_calib_skip_idle[SIM_NTIMERS+1] = { 0 };/* Calibrations skipped due to idling */ static uint32 rtc_clock_calib_gap2big[SIM_NTIMERS+1] = { 0 };/* Calibrations skipped Gap Too Big */ static uint32 rtc_clock_calib_backwards[SIM_NTIMERS+1] = { 0 };/* Calibrations skipped Clock Running Backwards */ static uint32 sim_idle_cyc_ms = 0; /* Cycles per millisecond while not idling */ UNIT sim_timer_units[SIM_NTIMERS+1]; /* Clock assist units */ /* one for each timer and one for an internal */ /* clock if no clocks are registered. */ UNIT sim_internal_timer_unit; /* Internal calibration timer */ UNIT sim_throttle_unit; /* one for throttle */ t_stat sim_throt_svc (UNIT *uptr); t_stat sim_timer_tick_svc (UNIT *uptr); #define DBG_IDL TIMER_DBG_IDLE /* idling */ #define DBG_QUE TIMER_DBG_QUEUE /* queue activities */ #define DBG_MUX TIMER_DBG_MUX /* tmxr queue activities */ #define DBG_TRC 0x008 /* tracing */ #define DBG_CAL 0x010 /* calibration activities */ #define DBG_TIM 0x020 /* timer thread activities */ #define DBG_THR 0x040 /* throttle activities */ #define DBG_ACK 0x080 /* interrupt acknowledgement activities */ #define DBG_CHK 0x100 /* check scheduled activation time*/ DEBTAB sim_timer_debug[] = { {"TRACE", DBG_TRC, "Trace routine calls"}, {"IDLE", DBG_IDL, "Idling activities"}, {"QUEUE", DBG_QUE, "Event queuing activities"}, {"IACK", DBG_ACK, "interrupt acknowledgement activities"}, {"CALIB", DBG_CAL, "Calibration activities"}, {"TIME", DBG_TIM, "Activation and scheduling activities"}, {"THROT", DBG_THR, "Throttling activities"}, {"MUX", DBG_MUX, "Tmxr scheduling activities"}, {"CHECK", DBG_CHK, "Check scheduled activation time"}, {0} }; /* Forward device declarations */ extern DEVICE sim_timer_dev; extern DEVICE sim_throttle_dev; void sim_rtcn_init_all (void) { int32 tmr; for (tmr = 0; tmr <= SIM_NTIMERS; tmr++) if (rtc_initd[tmr] != 0) sim_rtcn_init (rtc_initd[tmr], tmr); return; } int32 sim_rtcn_init (int32 time, int32 tmr) { return sim_rtcn_init_unit (NULL, time, tmr); } int32 sim_rtcn_init_unit (UNIT *uptr, int32 time, int32 tmr) { if (time == 0) time = 1; if (tmr == SIM_INTERNAL_CLK) tmr = SIM_NTIMERS; else { if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return time; } /* * If we'd previously succeeded in calibrating a tick value, then use that * delay as a better default to setup when we're re-initialized. * Re-initializing happens on any boot or after any breakpoint/continue. */ if (rtc_currd[tmr]) time = rtc_currd[tmr]; if (!uptr) uptr = sim_clock_unit[tmr]; sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_init_unit(unit=%s, time=%d, tmr=%d)\n", sim_uname(uptr), time, tmr); if (uptr) { if (!sim_clock_unit[tmr]) sim_register_clock_unit_tmr (uptr, tmr); } rtc_clock_start_gtime[tmr] = sim_gtime(); rtc_rtime[tmr] = sim_os_msec (); rtc_vtime[tmr] = rtc_rtime[tmr]; rtc_nxintv[tmr] = 1000; rtc_ticks[tmr] = 0; rtc_last_hz[tmr] = rtc_hz[tmr]; rtc_hz[tmr] = 0; rtc_based[tmr] = time; rtc_currd[tmr] = time; rtc_initd[tmr] = time; rtc_elapsed[tmr] = 0; rtc_calibrations[tmr] = 0; rtc_clock_ticks_tot[tmr] += rtc_clock_ticks[tmr]; rtc_clock_ticks[tmr] = 0; rtc_calib_tick_time_tot[tmr] += rtc_calib_tick_time[tmr]; rtc_calib_tick_time[tmr] = 0; rtc_clock_catchup_pending[tmr] = FALSE; rtc_clock_catchup_eligible[tmr] = FALSE; rtc_clock_catchup_ticks_tot[tmr] += rtc_clock_catchup_ticks[tmr]; rtc_clock_catchup_ticks[tmr] = 0; rtc_calib_ticks_acked_tot[tmr] += rtc_calib_ticks_acked[tmr]; rtc_calib_ticks_acked[tmr] = 0; ++rtc_calib_initializations[tmr]; rtc_clock_init_base_time[tmr] = sim_timenow_double (); _rtcn_configure_calibrated_clock (tmr); return time; } int32 sim_rtcn_calb (int32 ticksper, int32 tmr) { uint32 new_rtime, delta_rtime, last_idle_pct; int32 delta_vtime; double new_gtime; int32 new_currd; int32 itmr; if (tmr == SIM_INTERNAL_CLK) tmr = SIM_NTIMERS; else { if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return 10000; } if (rtc_hz[tmr] != ticksper) { /* changing tick rate? */ if (rtc_hz[tmr] == 0) rtc_clock_tick_start_time[tmr] = sim_timenow_double (); rtc_last_hz[tmr] = rtc_hz[tmr]; rtc_hz[tmr] = ticksper; _rtcn_configure_calibrated_clock (tmr); if (ticksper != 0) { rtc_clock_tick_size[tmr] = 1.0 / ticksper; rtc_currd[tmr] = (int32)(sim_timer_inst_per_sec () / ticksper); } } if (ticksper == 0) /* running? */ return 10000; if (sim_clock_unit[tmr] == NULL) { /* Not using TIMER units? */ rtc_clock_ticks[tmr] += 1; rtc_calib_tick_time[tmr] += rtc_clock_tick_size[tmr]; } if (rtc_clock_catchup_pending[tmr]) { /* catchup tick? */ ++rtc_clock_catchup_ticks[tmr]; /* accumulating which were catchups */ rtc_clock_catchup_pending[tmr] = FALSE; } rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */ if (rtc_ticks[tmr] < ticksper) /* 1 sec yet? */ return rtc_currd[tmr]; rtc_ticks[tmr] = 0; /* reset ticks */ rtc_elapsed[tmr] = rtc_elapsed[tmr] + 1; /* count sec */ if (sim_throt_type != SIM_THROT_NONE) { rtc_gtime[tmr] = sim_gtime(); /* save instruction time */ rtc_currd[tmr] = (int32)(sim_throt_cps / ticksper); /* use throttle calibration */ ++rtc_calibrations[tmr]; /* count calibrations */ sim_debug (DBG_CAL, &sim_timer_dev, "using throttle calibrated value - result: %d\n", rtc_currd[tmr]); return rtc_currd[tmr]; } if (!rtc_avail) /* no timer? */ return rtc_currd[tmr]; if (sim_calb_tmr != tmr) { rtc_currd[tmr] = (int32)(sim_timer_inst_per_sec()/ticksper); sim_debug (DBG_CAL, &sim_timer_dev, "calibrated calibrated tmr=%d against system tmr=%d, tickper=%d (result: %d)\n", tmr, sim_calb_tmr, ticksper, rtc_currd[tmr]); return rtc_currd[tmr]; } new_rtime = sim_os_msec (); /* wall time */ ++rtc_calibrations[tmr]; /* count calibrations */ sim_debug (DBG_TRC, &sim_timer_dev, "sim_rtcn_calb(ticksper=%d, tmr=%d)\n", ticksper, tmr); if (new_rtime < rtc_rtime[tmr]) { /* time running backwards? */ /* This happens when the value returned by sim_os_msec wraps (as an uint32) */ /* Wrapping will happen initially sometime before a simulator has been running */ /* for 49 days approximately every 49 days thereafter. */ ++rtc_clock_calib_backwards[tmr]; /* Count statistic */ sim_debug (DBG_CAL, &sim_timer_dev, "time running backwards - OldTime: %u, NewTime: %u, result: %d\n", rtc_rtime[tmr], new_rtime, rtc_currd[tmr]); rtc_rtime[tmr] = new_rtime; /* reset wall time */ return rtc_currd[tmr]; /* can't calibrate */ } delta_rtime = new_rtime - rtc_rtime[tmr]; /* elapsed wtime */ rtc_rtime[tmr] = new_rtime; /* adv wall time */ rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */ if (delta_rtime > 30000) { /* gap too big? */ /* This simulator process has somehow been suspended for a significant */ /* amount of time. This will certainly happen if the host system has */ /* slept or hibernated. It also might happen when a simulator */ /* developer stops the simulator at a breakpoint (a process, not simh */ /* breakpoint). To accomodate this, we set the calibration state to */ /* ignore what happened and proceed from here. */ ++rtc_clock_calib_gap2big[tmr]; /* Count statistic */ rtc_vtime[tmr] = rtc_rtime[tmr]; /* sync virtual and real time */ rtc_nxintv[tmr] = 1000; /* reset next interval */ rtc_gtime[tmr] = sim_gtime(); /* save instruction time */ sim_debug (DBG_CAL, &sim_timer_dev, "gap too big: delta = %d - result: %d\n", delta_rtime, rtc_currd[tmr]); return rtc_currd[tmr]; /* can't calibr */ } if (delta_rtime == 0) /* avoid divide by zero */ last_idle_pct = 0; /* force calibration */ else last_idle_pct = MIN(100, (uint32)(100.0 * (((double)(rtc_clock_time_idled[tmr] - rtc_clock_time_idled_last[tmr])) / ((double)delta_rtime)))); rtc_clock_time_idled_last[tmr] = rtc_clock_time_idled[tmr]; if (last_idle_pct > (100 - sim_idle_calib_pct)) { rtc_rtime[tmr] = new_rtime; /* save wall time */ rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */ rtc_gtime[tmr] = sim_gtime(); /* save instruction time */ ++rtc_clock_calib_skip_idle[tmr]; sim_debug (DBG_CAL, &sim_timer_dev, "skipping calibration due to idling (%d%%) - result: %d\n", last_idle_pct, rtc_currd[tmr]); return rtc_currd[tmr]; /* avoid calibrating idle checks */ } new_gtime = sim_gtime(); if ((last_idle_pct == 0) && (delta_rtime != 0)) sim_idle_cyc_ms = (uint32)((new_gtime - rtc_gtime[tmr]) / delta_rtime); if (sim_asynch_timer) { /* An asynchronous clock, merely needs to divide the number of */ /* instructions actually executed by the clock rate. */ new_currd = (int32)((new_gtime - rtc_gtime[tmr])/ticksper); /* avoid excessive swings in the calibrated result */ if (new_currd > 10*rtc_currd[tmr]) /* don't swing big too fast */ new_currd = 10*rtc_currd[tmr]; else if (new_currd < rtc_currd[tmr]/10) /* don't swing small too fast */ new_currd = rtc_currd[tmr]/10; rtc_currd[tmr] = new_currd; rtc_gtime[tmr] = new_gtime; /* save instruction time */ sim_debug (DBG_CAL, &sim_timer_dev, "asynch calibration result: %d\n", rtc_currd[tmr]); return rtc_currd[tmr]; /* calibrated result */ } rtc_gtime[tmr] = new_gtime; /* save instruction time */ /* This self regulating algorithm depends directly on the assumption */ /* that this routine is called back after processing the number of */ /* instructions which was returned the last time it was called. */ if (delta_rtime == 0) /* gap too small? */ rtc_based[tmr] = rtc_based[tmr] * ticksper; /* slew wide */ else rtc_based[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / ((double) delta_rtime));/* new base rate */ delta_vtime = rtc_vtime[tmr] - rtc_rtime[tmr]; /* gap */ if (delta_vtime > SIM_TMAX) /* limit gap */ delta_vtime = SIM_TMAX; else if (delta_vtime < -SIM_TMAX) delta_vtime = -SIM_TMAX; rtc_nxintv[tmr] = 1000 + delta_vtime; /* next wtime */ rtc_currd[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / 1000.0); /* next delay */ if (rtc_based[tmr] <= 0) /* never negative or zero! */ rtc_based[tmr] = 1; if (rtc_currd[tmr] <= 0) /* never negative or zero! */ rtc_currd[tmr] = 1; sim_debug (DBG_CAL, &sim_timer_dev, "calibrated tmr=%d, tickper=%d (base=%d, nxintv=%u, result: %d)\n", tmr, ticksper, rtc_based[tmr], rtc_nxintv[tmr], rtc_currd[tmr]); /* Adjust calibration for other timers which depend on this timer's calibration */ for (itmr=0; itmr<=SIM_NTIMERS; itmr++) if ((itmr != tmr) && (rtc_hz[itmr] != 0)) rtc_currd[itmr] = (rtc_currd[tmr] * ticksper) / rtc_hz[itmr]; AIO_SET_INTERRUPT_LATENCY(rtc_currd[tmr] * ticksper); /* set interrrupt latency */ return rtc_currd[tmr]; } /* Prior interfaces - default to timer 0 */ int32 sim_rtc_init (int32 time) { return sim_rtcn_init (time, 0); } int32 sim_rtc_calb (int32 ticksper) { return sim_rtcn_calb (ticksper, 0); } /* sim_timer_init - get minimum sleep time available on this host */ t_bool sim_timer_init (void) { int tmr; uint32 clock_start, clock_last, clock_now; sim_debug (DBG_TRC, &sim_timer_dev, "sim_timer_init()\n"); for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { sim_timer_units[tmr].action = &sim_timer_tick_svc; sim_timer_units[tmr].flags = UNIT_DIS | UNIT_IDLE; } SIM_INTERNAL_UNIT.flags = UNIT_IDLE; sim_register_internal_device (&sim_timer_dev); /* Register Clock Assist device */ sim_throttle_unit.action = &sim_throt_svc; sim_register_clock_unit_tmr (&SIM_INTERNAL_UNIT, SIM_INTERNAL_CLK); sim_idle_enab = FALSE; /* init idle off */ sim_idle_rate_ms = sim_os_ms_sleep_init (); /* get OS timer rate */ sim_set_rom_delay_factor (sim_get_rom_delay_factor ()); /* initialize ROM delay factor */ clock_last = clock_start = sim_os_msec (); sim_os_clock_resoluton_ms = 1000; do { uint32 clock_diff; clock_now = sim_os_msec (); clock_diff = clock_now - clock_last; if ((clock_diff > 0) && (clock_diff < sim_os_clock_resoluton_ms)) sim_os_clock_resoluton_ms = clock_diff; clock_last = clock_now; } while (clock_now < clock_start + 100); sim_os_tick_hz = 1000/(sim_os_clock_resoluton_ms * (sim_idle_rate_ms/sim_os_clock_resoluton_ms)); return (sim_idle_rate_ms != 0); } /* sim_timer_idle_capable - tell if the host is Idle capable and what the host OS tick size is */ t_bool sim_timer_idle_capable (uint32 *host_ms_sleep_1, uint32 *host_tick_ms) { if (host_tick_ms) *host_tick_ms = sim_os_clock_resoluton_ms; if (host_ms_sleep_1) *host_ms_sleep_1 = sim_os_sleep_min_ms; return (sim_idle_rate_ms != 0); } /* sim_show_timers - show running timer information */ t_stat sim_show_timers (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc) { int tmr, clocks; struct timespec now; time_t time_t_now; int32 calb_tmr = (sim_calb_tmr == -1) ? sim_calb_tmr_last : sim_calb_tmr; double inst_per_sec = sim_timer_inst_per_sec (); fprintf (st, "Minimum Host Sleep Time: %d ms (%dHz)\n", sim_os_sleep_min_ms, sim_os_tick_hz); if (sim_os_sleep_min_ms != sim_os_sleep_inc_ms) fprintf (st, "Minimum Host Sleep Incr Time: %d ms\n", sim_os_sleep_inc_ms); fprintf (st, "Host Clock Resolution: %d ms\n", sim_os_clock_resoluton_ms); fprintf (st, "Execution Rate: %s instructions/sec\n", sim_fmt_numeric (inst_per_sec)); if (sim_idle_enab) { fprintf (st, "Idling: Enabled\n"); fprintf (st, "Time before Idling starts: %d seconds\n", sim_idle_stable); } if (sim_throt_type != SIM_THROT_NONE) { sim_show_throt (st, NULL, uptr, val, desc); } fprintf (st, "Calibrated Timer: %s\n", (calb_tmr == -1) ? "Undetermined" : ((calb_tmr == SIM_NTIMERS) ? "Internal Timer" : (sim_clock_unit[calb_tmr] ? sim_uname(sim_clock_unit[calb_tmr]) : ""))); if (calb_tmr == SIM_NTIMERS) fprintf (st, "Catchup Ticks: %s for clocks ticking faster than %d Hz\n", sim_catchup_ticks ? "Enabled" : "Disabled", sim_os_tick_hz); if (sim_idle_calib_pct == 0) fprintf (st, "Calibration: Always\n"); else fprintf (st, "Calibration: Skipped when Idle exceeds %d%%\n", sim_idle_calib_pct); fprintf (st, "\n"); for (tmr=clocks=0; tmr<=SIM_NTIMERS; ++tmr) { if (0 == rtc_initd[tmr]) continue; if (sim_clock_unit[tmr]) { ++clocks; fprintf (st, "%s clock device is %s%s%s\n", sim_name, (tmr == SIM_NTIMERS) ? "Internal Calibrated Timer(" : "", sim_uname(sim_clock_unit[tmr]), (tmr == SIM_NTIMERS) ? ")" : ""); } fprintf (st, "%s%sTimer %d:\n", sim_asynch_timer ? "Asynchronous " : "", rtc_hz[tmr] ? "Calibrated " : "Uncalibrated ", tmr); if (rtc_hz[tmr]) { fprintf (st, " Running at: %d Hz\n", rtc_hz[tmr]); fprintf (st, " Tick Size: %s\n", sim_fmt_secs (rtc_clock_tick_size[tmr])); fprintf (st, " Ticks in current second: %d\n", rtc_ticks[tmr]); } fprintf (st, " Seconds Running: %s (%s)\n", sim_fmt_numeric ((double)rtc_elapsed[tmr]), sim_fmt_secs ((double)rtc_elapsed[tmr])); if (tmr == calb_tmr) { fprintf (st, " Calibration Opportunities: %s\n", sim_fmt_numeric ((double)rtc_calibrations[tmr])); if (sim_idle_calib_pct) fprintf (st, " Calib Skip Idle Thresh %%: %u\n", sim_idle_calib_pct); if (rtc_clock_calib_skip_idle[tmr]) fprintf (st, " Calibs Skip While Idle: %u\n", rtc_clock_calib_skip_idle[tmr]); if (rtc_clock_calib_backwards[tmr]) fprintf (st, " Calibs Skip Backwards: %u\n", rtc_clock_calib_backwards[tmr]); if (rtc_clock_calib_gap2big[tmr]) fprintf (st, " Calibs Skip Gap Too Big: %u\n", rtc_clock_calib_gap2big[tmr]); } if (rtc_gtime[tmr]) fprintf (st, " Instruction Time: %.0f\n", rtc_gtime[tmr]); if ((!sim_asynch_timer) && (sim_throt_type == SIM_THROT_NONE)) { fprintf (st, " Real Time: %u\n", rtc_rtime[tmr]); fprintf (st, " Virtual Time: %u\n", rtc_vtime[tmr]); fprintf (st, " Next Interval: %s\n", sim_fmt_numeric ((double)rtc_nxintv[tmr])); fprintf (st, " Base Tick Delay: %s\n", sim_fmt_numeric ((double)rtc_based[tmr])); fprintf (st, " Initial Insts Per Tick: %s\n", sim_fmt_numeric ((double)rtc_initd[tmr])); } fprintf (st, " Current Insts Per Tick: %s\n", sim_fmt_numeric ((double)rtc_currd[tmr])); fprintf (st, " Initializations: %d\n", rtc_calib_initializations[tmr]); fprintf (st, " Ticks: %s\n", sim_fmt_numeric ((double)(rtc_clock_ticks[tmr]))); if (rtc_clock_ticks_tot[tmr]+rtc_clock_ticks[tmr] != rtc_clock_ticks[tmr]) fprintf (st, " Total Ticks: %s\n", sim_fmt_numeric ((double)(rtc_clock_ticks_tot[tmr]+rtc_clock_ticks[tmr]))); if (rtc_clock_skew_max[tmr] != 0.0) fprintf (st, " Peak Clock Skew: %s%s\n", sim_fmt_secs (fabs(rtc_clock_skew_max[tmr])), (rtc_clock_skew_max[tmr] < 0) ? " fast" : " slow"); if (rtc_calib_ticks_acked[tmr]) fprintf (st, " Ticks Acked: %s\n", sim_fmt_numeric ((double)rtc_calib_ticks_acked[tmr])); if (rtc_calib_ticks_acked_tot[tmr]+rtc_calib_ticks_acked[tmr] != rtc_calib_ticks_acked[tmr]) fprintf (st, " Total Ticks Acked: %s\n", sim_fmt_numeric ((double)(rtc_calib_ticks_acked_tot[tmr]+rtc_calib_ticks_acked[tmr]))); if (rtc_calib_tick_time[tmr]) fprintf (st, " Tick Time: %s\n", sim_fmt_secs (rtc_calib_tick_time[tmr])); if (rtc_calib_tick_time_tot[tmr]+rtc_calib_tick_time[tmr] != rtc_calib_tick_time[tmr]) fprintf (st, " Total Tick Time: %s\n", sim_fmt_secs (rtc_calib_tick_time_tot[tmr]+rtc_calib_tick_time[tmr])); if (rtc_clock_catchup_ticks[tmr]) fprintf (st, " Catchup Ticks Sched: %s\n", sim_fmt_numeric ((double)rtc_clock_catchup_ticks[tmr])); if (rtc_clock_catchup_ticks_tot[tmr]+rtc_clock_catchup_ticks[tmr] != rtc_clock_catchup_ticks[tmr]) fprintf (st, " Total Catchup Ticks Sched: %s\n", sim_fmt_numeric ((double)(rtc_clock_catchup_ticks_tot[tmr]+rtc_clock_catchup_ticks[tmr]))); if (rtc_clock_init_base_time[tmr]) { _double_to_timespec (&now, rtc_clock_init_base_time[tmr]); time_t_now = (time_t)now.tv_sec; fprintf (st, " Initialize Base Time: %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000)); } if (rtc_clock_tick_start_time[tmr]) { _double_to_timespec (&now, rtc_clock_tick_start_time[tmr]); time_t_now = (time_t)now.tv_sec; fprintf (st, " Tick Start Time: %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000)); } clock_gettime (CLOCK_REALTIME, &now); time_t_now = (time_t)now.tv_sec; fprintf (st, " Wall Clock Time Now: %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000)); if (rtc_clock_catchup_eligible[tmr]) { _double_to_timespec (&now, rtc_clock_catchup_base_time[tmr]+rtc_calib_tick_time[tmr]); time_t_now = (time_t)now.tv_sec; fprintf (st, " Catchup Tick Time: %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000)); _double_to_timespec (&now, rtc_clock_catchup_base_time[tmr]); time_t_now = (time_t)now.tv_sec; fprintf (st, " Catchup Base Time: %8.8s.%03d\n", 11+ctime(&time_t_now), (int)(now.tv_nsec/1000000)); } if (rtc_clock_time_idled[tmr]) fprintf (st, " Total Time Idled: %s\n", sim_fmt_secs (rtc_clock_time_idled[tmr]/1000.0)); } if (clocks == 0) fprintf (st, "%s clock device is not specified, co-scheduling is unavailable\n", sim_name); return SCPE_OK; } t_stat sim_show_clock_queues (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { int tmr; #if defined (SIM_ASYNCH_CLOCKS) pthread_mutex_lock (&sim_timer_lock); if (sim_asynch_timer) { const char *tim; struct timespec due; time_t time_t_due; if (sim_wallclock_queue == QUEUE_LIST_END) fprintf (st, "%s wall clock event queue empty\n", sim_name); else { fprintf (st, "%s wall clock event queue status\n", sim_name); for (uptr = sim_wallclock_queue; uptr != QUEUE_LIST_END; uptr = uptr->a_next) { if ((dptr = find_dev_from_unit (uptr)) != NULL) { fprintf (st, " %s", sim_dname (dptr)); if (dptr->numunits > 1) fprintf (st, " unit %d", (int32) (uptr - dptr->units)); } else fprintf (st, " Unknown"); tim = sim_fmt_secs(uptr->a_usec_delay/1000000.0); _double_to_timespec (&due, uptr->a_due_time); time_t_due = (time_t)due.tv_sec; fprintf (st, " after %s due at %8.8s.%06d\n", tim, 11+ctime(&time_t_due), (int)(due.tv_nsec/1000)); } } } #endif /* SIM_ASYNCH_CLOCKS */ for (tmr=0; tmr<=SIM_NTIMERS; ++tmr) { if (sim_clock_unit[tmr] == NULL) continue; if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) { int32 accum; fprintf (st, "%s clock (%s) co-schedule event queue status\n", sim_name, sim_uname(sim_clock_unit[tmr])); accum = 0; for (uptr = sim_clock_cosched_queue[tmr]; uptr != QUEUE_LIST_END; uptr = uptr->next) { if ((dptr = find_dev_from_unit (uptr)) != NULL) { fprintf (st, " %s", sim_dname (dptr)); if (dptr->numunits > 1) fprintf (st, " unit %d", (int32) (uptr - dptr->units)); } else fprintf (st, " Unknown"); if (accum == 0) fprintf (st, " on next tick"); else fprintf (st, " after %d tick%s", accum, (accum > 1) ? "s" : ""); if (uptr->usecs_remaining) fprintf (st, " plus %.0f usecs", uptr->usecs_remaining); fprintf (st, "\n"); accum = accum + uptr->time; } } } #if defined (SIM_ASYNCH_IO) pthread_mutex_unlock (&sim_timer_lock); #endif /* SIM_ASYNCH_IO */ return SCPE_OK; } REG sim_timer_reg[] = { { DRDATAD (IDLE_CYC_MS, sim_idle_cyc_ms, 32, "Cycles Per Millisecond"), PV_RSPC|REG_RO}, { DRDATAD (ROM_DELAY, sim_rom_delay, 32, "ROM memory reference delay"), PV_RSPC|REG_RO}, { NULL } }; REG sim_throttle_reg[] = { { DRDATAD (THROT_MS_START, sim_throt_ms_start, 32, ""), PV_RSPC|REG_RO}, { DRDATAD (THROT_MS_STOP, sim_throt_ms_stop, 32, ""), PV_RSPC|REG_RO}, { DRDATAD (THROT_TYPE, sim_throt_type, 32, ""), PV_RSPC|REG_RO}, { DRDATAD (THROT_VAL, sim_throt_val, 32, ""), PV_RSPC|REG_RO}, { DRDATAD (THROT_STATE, sim_throt_state, 32, ""), PV_RSPC|REG_RO}, { DRDATAD (THROT_SLEEP_TIME, sim_throt_sleep_time, 32, ""), PV_RSPC|REG_RO}, { DRDATAD (THROT_WAIT, sim_throt_wait, 32, ""), PV_RSPC|REG_RO}, { NULL } }; /* Clear, Set and show catchup */ /* Set/Clear catchup */ t_stat sim_timer_set_catchup (int32 flag, CONST char *cptr) { if (flag) { if (!sim_catchup_ticks) sim_catchup_ticks = TRUE; } else { if (sim_catchup_ticks) sim_catchup_ticks = FALSE; } return SCPE_OK; } t_stat sim_timer_show_catchup (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { fprintf (st, "Calibrated Ticks%s", sim_catchup_ticks ? " with Catchup Ticks" : ""); return SCPE_OK; } /* Set idle calibration threshold */ t_stat sim_timer_set_idle_pct (int32 flag, CONST char *cptr) { t_stat r; int32 newpct; if (cptr == NULL) return SCPE_ARG; newpct = (int32) get_uint (cptr, 10, 100, &r); if ((r != SCPE_OK) || (newpct == (int32)(sim_idle_calib_pct))) return r; if (newpct == 0) return SCPE_ARG; sim_idle_calib_pct = (uint32)newpct; return SCPE_OK; } /* Set/Clear asynch */ t_stat sim_timer_set_async (int32 flag, CONST char *cptr) { if (flag) { if (sim_asynch_enabled && (!sim_asynch_timer)) { sim_asynch_timer = TRUE; sim_timer_change_asynch (); } } else { if (sim_asynch_timer) { sim_asynch_timer = FALSE; sim_timer_change_asynch (); } } return SCPE_OK; } static CTAB set_timer_tab[] = { #if defined (SIM_ASYNCH_CLOCKS) { "ASYNCH", &sim_timer_set_async, 1 }, { "NOASYNCH", &sim_timer_set_async, 0 }, #endif { "CATCHUP", &sim_timer_set_catchup, 1 }, { "NOCATCHUP", &sim_timer_set_catchup, 0 }, { "CALIB", &sim_timer_set_idle_pct, 0 }, { NULL, NULL, 0 } }; MTAB sim_timer_mod[] = { { 0 }, }; static t_stat sim_timer_clock_reset (DEVICE *dptr); static const char *sim_timer_description (DEVICE *dptr) { return "Clock Assist facilities"; } static const char *sim_int_timer_description (DEVICE *dptr) { return "Internal Timer"; } static const char *sim_throttle_description (DEVICE *dptr) { return "Throttle facility"; } DEVICE sim_timer_dev = { "INT-CLOCK", sim_timer_units, sim_timer_reg, sim_timer_mod, SIM_NTIMERS+1, 0, 0, 0, 0, 0, NULL, NULL, &sim_timer_clock_reset, NULL, NULL, NULL, NULL, DEV_DEBUG | DEV_NOSAVE, 0, sim_timer_debug}; DEVICE sim_int_timer_dev = { "INT-TIMER", &sim_internal_timer_unit, NULL, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, DEV_NOSAVE}; DEVICE sim_throttle_dev = { "INT-THROTTLE", &sim_throttle_unit, sim_throttle_reg, NULL, 1}; /* SET CLOCK command */ t_stat sim_set_timers (int32 arg, CONST char *cptr) { char *cvptr, gbuf[CBUFSIZE]; CTAB *ctptr; t_stat r; if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; while (*cptr != 0) { /* do all mods */ cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */ if ((cvptr = strchr (gbuf, '='))) /* = value? */ *cvptr++ = 0; get_glyph (gbuf, gbuf, 0); /* modifier to UC */ if ((ctptr = find_ctab (set_timer_tab, gbuf))) { /* match? */ r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ if (r != SCPE_OK) return r; } else return SCPE_NOPARAM; } return SCPE_OK; } /* sim_idle - idle simulator until next event or for specified interval Inputs: tmr = calibrated timer to use Must solve the linear equation ms_to_wait = w * ms_per_wait Or w = ms_to_wait / ms_per_wait */ t_bool sim_idle (uint32 tmr, int sin_cyc) { uint32 w_ms, w_idle, act_ms; int32 act_cyc; if (rtc_clock_catchup_pending[tmr]) { /* Catchup clock tick pending? */ sim_debug (DBG_CAL, &sim_timer_dev, "sim_idle(tmr=%d, sin_cyc=%d) - accelerating pending catch-up tick before idling %s\n", tmr, sin_cyc, sim_uname (sim_clock_unit[tmr])); sim_activate_abs (&sim_timer_units[tmr], 0); sim_interval -= sin_cyc; return FALSE; } if ((!sim_idle_enab) || /* idling disabled */ ((sim_clock_queue == QUEUE_LIST_END) && /* or clock queue empty? */ (!sim_asynch_timer))|| /* and not asynch? */ ((sim_clock_queue != QUEUE_LIST_END) && /* or clock queue not empty */ ((sim_clock_queue->flags & UNIT_IDLE) == 0))|| /* and event not idle-able? */ (rtc_elapsed[tmr] < sim_idle_stable)) { /* or timer not stable? */ sim_debug (DBG_IDL, &sim_timer_dev, "Can't idle: %s - elapsed: %d.%03d\n", !sim_idle_enab ? "idle disabled" : ((rtc_elapsed[tmr] < sim_idle_stable) ? "not stable" : ((sim_clock_queue != QUEUE_LIST_END) ? sim_uname (sim_clock_queue) : "")), rtc_elapsed[tmr], rtc_ticks[tmr]); sim_interval -= sin_cyc; return FALSE; } if (_rtcn_tick_catchup_check(tmr, 0)) { sim_debug (DBG_CAL, &sim_timer_dev, "sim_idle(tmr=%d, sin_cyc=%d) - rescheduling catchup tick for %s\n", tmr, sin_cyc, sim_uname (sim_clock_unit[tmr])); sim_interval -= sin_cyc; return FALSE; } /* When a simulator is in an instruction path (or under other conditions which would indicate idling), the countdown of sim_interval will not be happening at a pace which is consistent with the rate it happens when not in the 'idle capable' state. The consequence of this is that the clock calibration may produce calibrated results which vary much more than they do when not in the idle able state. Sim_idle also uses the calibrated tick size to approximate an adjustment to sim_interval to reflect the number of instructions which would have executed during the actual idle time, so consistent calibrated numbers produce better adjustments. To negate this effect, we accumulate the time actually idled here. sim_rtcn_calb compares the accumulated idle time during the most recent second and if it exceeds the percentage defined by and sim_idle_calib_pct calibration is suppressed. Thus recalibration only happens if things didn't idle too much. we also check check sim_idle_enab above so that all simulators can avoid directly checking sim_idle_enab before calling sim_idle so that all of the bookkeeping on sim_idle_idled is done here in sim_timer where it means something, while not idling when it isn't enabled. */ sim_debug (DBG_TRC, &sim_timer_dev, "sim_idle(tmr=%d, sin_cyc=%d)\n", tmr, sin_cyc); if (sim_idle_cyc_ms == 0) sim_idle_cyc_ms = (rtc_currd[tmr] * rtc_hz[tmr]) / 1000;/* cycles per msec */ if ((sim_idle_rate_ms == 0) || (sim_idle_cyc_ms == 0)) {/* not possible? */ sim_interval -= sin_cyc; sim_debug (DBG_IDL, &sim_timer_dev, "not possible idle_rate_ms=%d - cyc/ms=%d\n", sim_idle_rate_ms, sim_idle_cyc_ms); return FALSE; } w_ms = (uint32) sim_interval / sim_idle_cyc_ms; /* ms to wait */ /* When the host system has a clock tick which is less frequent than the */ /* simulated system's clock, idling will cause delays which will miss */ /* simulated clock ticks. To accomodate this, and still allow idling, if */ /* the simulator acknowledges the processing of clock ticks, then catchup */ /* ticks can be used to make up for missed ticks. */ if (rtc_clock_catchup_eligible[tmr]) w_idle = (sim_interval * 1000) / rtc_currd[tmr]; /* 1000 * pending fraction of tick */ else w_idle = (w_ms * 1000) / sim_idle_rate_ms; /* 1000 * intervals to wait */ if (w_idle < 500) { /* shorter than 1/2 the interval? */ sim_interval -= sin_cyc; sim_debug (DBG_IDL, &sim_timer_dev, "no wait\n"); return FALSE; } if (sim_clock_queue == QUEUE_LIST_END) sim_debug (DBG_IDL, &sim_timer_dev, "sleeping for %d ms - pending event in %d instructions\n", w_ms, sim_interval); else sim_debug (DBG_IDL, &sim_timer_dev, "sleeping for %d ms - pending event on %s in %d instructions\n", w_ms, sim_uname(sim_clock_queue), sim_interval); act_ms = sim_idle_ms_sleep (w_ms); /* wait */ rtc_clock_time_idled[tmr] += act_ms; act_cyc = act_ms * sim_idle_cyc_ms; act_cyc += (sim_idle_cyc_ms * sim_idle_rate_ms) / 2; /* account for half an interval's worth of cycles */ if (sim_interval > act_cyc) sim_interval = sim_interval - act_cyc; /* count down sim_interval */ else sim_interval = 0; /* or fire immediately */ if (sim_clock_queue == QUEUE_LIST_END) sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event in %d instructions\n", act_ms, sim_interval); else sim_debug (DBG_IDL, &sim_timer_dev, "slept for %d ms - pending event on %s in %d instructions\n", act_ms, sim_uname(sim_clock_queue), sim_interval); return TRUE; } /* Set idling - implicitly disables throttling */ t_stat sim_set_idle (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { t_stat r; uint32 v; if (cptr && *cptr) { v = (uint32) get_uint (cptr, 10, SIM_IDLE_STMAX, &r); if ((r != SCPE_OK) || (v < SIM_IDLE_STMIN)) return sim_messagef (SCPE_ARG, "Invalid Stability value: %s. Valid values range from %d to %d.\n", cptr, SIM_IDLE_STMIN, SIM_IDLE_STMAX); sim_idle_stable = v; } sim_idle_enab = TRUE; if (sim_throt_type != SIM_THROT_NONE) { sim_set_throt (0, NULL); sim_printf ("Throttling disabled\n"); } return SCPE_OK; } /* Clear idling */ t_stat sim_clr_idle (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { sim_idle_enab = FALSE; return SCPE_OK; } /* Show idling */ t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { if (sim_idle_enab) fprintf (st, "idle enabled"); else fprintf (st, "idle disabled"); if (sim_switches & SWMASK ('D')) fprintf (st, ", stability wait = %ds, minimum sleep resolution = %dms", sim_idle_stable, sim_os_sleep_min_ms); return SCPE_OK; } /* Throttling package */ t_stat sim_set_throt (int32 arg, CONST char *cptr) { CONST char *tptr; char c; t_value val, val2 = 0; if (arg == 0) { if ((cptr != NULL) && (*cptr != 0)) return sim_messagef (SCPE_ARG, "Unexpected NOTHROTTLE argument: %s\n", cptr); sim_throt_type = SIM_THROT_NONE; sim_throt_cancel (); } else if (sim_idle_rate_ms == 0) { return sim_messagef (SCPE_NOFNC, "Throttling is not available, Minimum OS sleep time is %dms\n", sim_os_sleep_min_ms); } else { if (*cptr == '\0') return sim_messagef (SCPE_ARG, "Missing throttle mode specification\n"); val = strtotv (cptr, &tptr, 10); if (cptr == tptr) return sim_messagef (SCPE_ARG, "Invalid throttle specification: %s\n", cptr); sim_throt_sleep_time = sim_idle_rate_ms; c = (char)toupper (*tptr++); if (c == '/') { val2 = strtotv (tptr, &tptr, 10); if ((*tptr != '\0') || (val == 0)) return sim_messagef (SCPE_ARG, "Invalid throttle delay specifier: %s\n", cptr); } if (c == 'M') sim_throt_type = SIM_THROT_MCYC; else if (c == 'K') sim_throt_type = SIM_THROT_KCYC; else if ((c == '%') && (val > 0) && (val < 100)) sim_throt_type = SIM_THROT_PCT; else if ((c == '/') && (val2 != 0)) sim_throt_type = SIM_THROT_SPC; else return sim_messagef (SCPE_ARG, "Invalid throttle specification: %s\n", cptr); if (sim_idle_enab) { sim_printf ("Idling disabled\n"); sim_clr_idle (NULL, 0, NULL, NULL); } sim_throt_val = (uint32) val; if (sim_throt_type == SIM_THROT_SPC) { if (val2 >= sim_idle_rate_ms) sim_throt_sleep_time = (uint32) val2; else { if ((sim_idle_rate_ms % val2) == 0) { sim_throt_sleep_time = sim_idle_rate_ms; sim_throt_val = (uint32) (val * (sim_idle_rate_ms / val2)); } else { sim_throt_sleep_time = sim_idle_rate_ms; sim_throt_val = (uint32) (val * (1 + (sim_idle_rate_ms / val2))); } } } } sim_register_internal_device (&sim_throttle_dev); /* Register Throttle Device */ sim_throt_cps = SIM_INITIAL_IPS; /* Initial value while correct one is determined */ return SCPE_OK; } t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr) { if (sim_idle_rate_ms == 0) fprintf (st, "Throttling: Not Available\n"); else { switch (sim_throt_type) { case SIM_THROT_MCYC: fprintf (st, "Throttle: %d megacycles\n", sim_throt_val); if (sim_throt_wait) fprintf (st, "Throttling by sleeping for: %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_wait); break; case SIM_THROT_KCYC: fprintf (st, "Throttle: %d kilocycles\n", sim_throt_val); if (sim_throt_wait) fprintf (st, "Throttling by sleeping for: %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_wait); break; case SIM_THROT_PCT: fprintf (st, "Throttle: %d%%\n", sim_throt_val); if (sim_throt_wait) fprintf (st, "Throttling by sleeping for: %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_wait); break; case SIM_THROT_SPC: fprintf (st, "Throttle: sleep %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_val); break; default: fprintf (st, "Throttling: Disabled\n"); break; } if (sim_throt_type != SIM_THROT_NONE) { if (sim_throt_state != SIM_THROT_STATE_THROTTLE) fprintf (st, "Throttle State: %s - wait: %d\n", (sim_throt_state == SIM_THROT_STATE_INIT) ? "Waiting for Init" : "Timing", sim_throt_wait); } } return SCPE_OK; } void sim_throt_sched (void) { sim_throt_state = SIM_THROT_STATE_INIT; if (sim_throt_type) sim_activate (&sim_throttle_unit, SIM_THROT_WINIT); } void sim_throt_cancel (void) { sim_cancel (&sim_throttle_unit); } /* Throttle service Throttle service has three distinct states used while dynamically determining a throttling interval: SIM_THROT_STATE_INIT take initial measurement SIM_THROT_STATE_TIME take final measurement, calculate wait values SIM_THROT_STATE_THROTTLE periodic waits to slow down the CPU */ t_stat sim_throt_svc (UNIT *uptr) { int32 tmr; uint32 delta_ms; double a_cps, d_cps; if (sim_throt_type == SIM_THROT_SPC) { /* Non dynamic? */ sim_throt_state = SIM_THROT_STATE_THROTTLE; /* force state */ sim_throt_wait = sim_throt_val; } switch (sim_throt_state) { case SIM_THROT_STATE_INIT: /* take initial reading */ sim_idle_ms_sleep (sim_idle_rate_ms); /* start on a tick boundart to calibrate */ sim_throt_ms_start = sim_os_msec (); sim_throt_inst_start = sim_gtime(); sim_throt_wait = SIM_THROT_WST; sim_throt_state = SIM_THROT_STATE_TIME; /* next state */ sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc(INIT) Starting. Values wait = %d\n", sim_throt_wait); break; /* reschedule */ case SIM_THROT_STATE_TIME: /* take final reading */ sim_throt_ms_stop = sim_os_msec (); delta_ms = sim_throt_ms_stop - sim_throt_ms_start; if (delta_ms < SIM_THROT_MSMIN) { /* not enough time? */ if (sim_throt_wait >= 100000000) { /* too many inst? */ sim_throt_state = SIM_THROT_STATE_INIT; /* fails in 32b! */ sim_printf ("Can't throttle. Host CPU is too fast with a minimum sleep time of %d ms\n", sim_idle_rate_ms); sim_set_throt (0, NULL); /* disable throttling */ return SCPE_OK; } sim_idle_ms_sleep (sim_idle_rate_ms); /* start on a tick boundart to calibrate */ sim_throt_wait = sim_throt_wait * SIM_THROT_WMUL; sim_throt_ms_start = sim_os_msec (); sim_throt_inst_start = sim_gtime(); } else { /* long enough */ a_cps = ((double) sim_throt_wait) * 1000.0 / (double) delta_ms; if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */ d_cps = (double) sim_throt_val * 1000000.0; else if (sim_throt_type == SIM_THROT_KCYC) d_cps = (double) sim_throt_val * 1000.0; else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; if (d_cps >= a_cps) { sim_throt_state = SIM_THROT_STATE_INIT; sim_printf ("Host CPU is too slow to simulate %s instructions per second\n", sim_fmt_numeric(d_cps)); sim_printf ("Throttling disabled.\n"); sim_set_throt (0, NULL); return SCPE_OK; } while (1) { sim_throt_wait = (int32) /* time between waits */ ((a_cps * d_cps * ((double) sim_throt_sleep_time)) / (1000.0 * (a_cps - d_cps))); if (sim_throt_wait >= SIM_THROT_WMIN) /* long enough? */ break; sim_throt_sleep_time += sim_os_sleep_inc_ms; sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Wait too small, increasing sleep time to %d ms. Values a_cps = %f, d_cps = %f, wait = %d\n", sim_throt_sleep_time, a_cps, d_cps, sim_throt_wait); } sim_throt_ms_start = sim_throt_ms_stop; sim_throt_inst_start = sim_gtime(); sim_throt_state = SIM_THROT_STATE_THROTTLE; sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Throttle values a_cps = %f, d_cps = %f, wait = %d, sleep = %d ms\n", a_cps, d_cps, sim_throt_wait, sim_throt_sleep_time); sim_throt_cps = d_cps; /* save the desired rate */ /* Run through all timers and adjust the calibration for each */ /* one that is running to reflect the throttle rate */ for (tmr=0; tmr<=SIM_NTIMERS; tmr++) if (rtc_hz[tmr]) { /* running? */ rtc_gtime[tmr] = sim_gtime(); /* save instruction time */ rtc_currd[tmr] = (int32)(sim_throt_cps / rtc_hz[tmr]);/* use throttle calibration */ } } break; case SIM_THROT_STATE_THROTTLE: /* throttling */ sim_idle_ms_sleep (sim_throt_sleep_time); delta_ms = sim_os_msec () - sim_throt_ms_start; if (sim_throt_type != SIM_THROT_SPC) { /* when not dynamic throttling */ if (delta_ms >= 10000) { /* recompute every 10 sec */ double delta_insts = sim_gtime() - sim_throt_inst_start; a_cps = (delta_insts * 1000.0) / (double) delta_ms; if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */ d_cps = (double) sim_throt_val * 1000000.0; else if (sim_throt_type == SIM_THROT_KCYC) d_cps = (double) sim_throt_val * 1000.0; else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; if (fabs(100.0 * (d_cps - a_cps) / a_cps) > (double)SIM_THROT_DRIFT_PCT) { sim_throt_wait = sim_throt_val; sim_throt_state = SIM_THROT_STATE_TIME;/* next state to recalibrate */ sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Recalibrating throttle based on values a_cps = %f, d_cps = %f\n", a_cps, d_cps); } sim_throt_ms_start = sim_os_msec (); sim_throt_inst_start = sim_gtime(); } } else /* record instruction rate */ sim_throt_cps = (int32)((1000.0 * sim_throt_val) / (double)delta_ms); break; } sim_activate (uptr, sim_throt_wait); /* reschedule */ return SCPE_OK; } /* Clock assist activites */ t_stat sim_timer_tick_svc (UNIT *uptr) { int32 tmr = (int32)(uptr-sim_timer_units); t_stat stat; rtc_clock_ticks[tmr] += 1; rtc_calib_tick_time[tmr] += rtc_clock_tick_size[tmr]; /* * Some devices may depend on executing during the same instruction or * immediately after the clock tick event. To satisfy this, we directly * run the clock event here and if it completes successfully, schedule any * currently coschedule units to run now. Ticks should never return a * non-success status, while co-schedule activities might, so they are * queued to run from sim_process_event */ sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_tick_svc(tmr=%d) - scheduling %s - cosched interval: %d\n", tmr, sim_uname (sim_clock_unit[tmr]), sim_cosched_interval[tmr]); if (sim_clock_unit[tmr]->action == NULL) return SCPE_IERR; stat = sim_clock_unit[tmr]->action (sim_clock_unit[tmr]); --sim_cosched_interval[tmr]; /* Countdown ticks */ if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) sim_clock_cosched_queue[tmr]->time = sim_cosched_interval[tmr]; if ((stat == SCPE_OK) && (sim_cosched_interval[tmr] <= 0) && (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END)) { if (rtc_clock_catchup_eligible[tmr]) { /* calibration started? */ struct timespec now; double skew; clock_gettime(CLOCK_REALTIME, &now); skew = (_timespec_to_double(&now) - (rtc_calib_tick_time[tmr]+rtc_clock_catchup_base_time[tmr])); if (fabs(skew) > fabs(rtc_clock_skew_max[tmr])) rtc_clock_skew_max[tmr] = skew; } do { UNIT *cptr = sim_clock_cosched_queue[tmr]; sim_clock_cosched_queue[tmr] = cptr->next; cptr->next = NULL; cptr->cancel = NULL; cptr->time = 0; if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) { sim_clock_cosched_queue[tmr]->time += sim_cosched_interval[tmr]; sim_cosched_interval[tmr] = sim_clock_cosched_queue[tmr]->time; } else sim_cosched_interval[tmr] = 0; cptr->time = 0; sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_tick_svc(tmr=%d) - coactivating %s", tmr, sim_uname (cptr)); if (cptr->usecs_remaining) { sim_debug (DBG_QUE, &sim_timer_dev, " remnant: %.0f - next %s after cosched interval: %d ticks\n", cptr->usecs_remaining, (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) ? sim_uname (sim_clock_cosched_queue[tmr]) : "", sim_cosched_interval[tmr]); sim_timer_activate_after (cptr, cptr->usecs_remaining); } else { sim_debug (DBG_QUE, &sim_timer_dev, " - next %s after cosched interval: %d ticks\n", (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) ? sim_uname (sim_clock_cosched_queue[tmr]) : "", sim_cosched_interval[tmr]); _sim_activate (cptr, 0); } } while ((sim_cosched_interval[tmr] <= 0) && (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END)); } return stat; } void sim_rtcn_get_time (struct timespec *now, int tmr) { sim_debug (DBG_CAL, &sim_timer_dev, "sim_rtcn_get_time(tmr=%d)\n", tmr); clock_gettime (CLOCK_REALTIME, now); } /* * If the host system has a relatively large clock tick (as compared to * the desired simulated hz) ticks will naturally be scheduled late and * these delays will accumulate. The net result will be unreasonably * slow ticks being delivered to the simulated system. * Additionally, when a simulator is idling and/or throttling, it will * deliberately call sim_os_ms_sleep and those sleep operations will be * variable and subject to the host system's minimum sleep resolution * which can exceed the desired sleep interval and add to the concept * of slow tick delivery to the simulated system. * We accomodate these problems and make up for lost ticks by injecting * catch-up ticks to the simulator. * * When necessary, catch-up ticks are scheduled to run under one * of two conditions: * 1) after indicated number of instructions in a call by the simulator * to sim_rtcn_tick_ack. sim_rtcn_tick_ack exists to provide a * mechanism to inform the simh timer facilities when the simulated * system has accepted the most recent clock tick interrupt. * 2) immediately when the simulator calls sim_idle * * catchup ticks are only scheduled (eligible to happen) under these * conditions after at least one tick has been acknowledged. */ /* _rtcn_tick_catchup_check - idle simulator until next event or for specified interval Inputs: tmr = calibrated timer to check/schedule time = instruction delay for next tick Returns TRUE if a catchup tick has been scheduled */ static t_bool _rtcn_tick_catchup_check (int32 tmr, int32 time) { if ((!sim_catchup_ticks) || ((tmr < 0) || (tmr >= SIM_NTIMERS))) return FALSE; if ((rtc_hz[tmr] > sim_os_tick_hz) && /* faster than host tick */ (!rtc_clock_catchup_eligible[tmr]) && /* not eligible yet? */ (time != -1)) { /* called from ack? */ rtc_clock_catchup_base_time[tmr] = sim_timenow_double(); rtc_clock_ticks_tot[tmr] += rtc_clock_ticks[tmr]; rtc_clock_ticks[tmr] = 0; rtc_calib_tick_time_tot[tmr] += rtc_calib_tick_time[tmr]; rtc_calib_tick_time[tmr] = 0.0; rtc_clock_catchup_ticks_tot[tmr] += rtc_clock_catchup_ticks[tmr]; rtc_clock_catchup_ticks[tmr] = 0; rtc_calib_ticks_acked_tot[tmr] += rtc_calib_ticks_acked[tmr]; rtc_calib_ticks_acked[tmr] = 0; rtc_clock_catchup_eligible[tmr] = TRUE; sim_debug (DBG_QUE, &sim_timer_dev, "_rtcn_tick_catchup_check() - Enabling catchup ticks for %s\n", sim_uname (sim_clock_unit[tmr])); return TRUE; } if (rtc_clock_catchup_eligible[tmr]) { double tnow = sim_timenow_double(); if (tnow > (rtc_clock_catchup_base_time[tmr] + (rtc_calib_tick_time[tmr] + rtc_clock_tick_size[tmr]))) { sim_debug (DBG_QUE, &sim_timer_dev, "_rtcn_tick_catchup_check(%d) - scheduling catchup tick for %s which is behind %s\n", time, sim_uname (sim_clock_unit[tmr]), sim_fmt_secs (tnow > (rtc_clock_catchup_base_time[tmr] + (rtc_calib_tick_time[tmr] + rtc_clock_tick_size[tmr])))); rtc_clock_catchup_pending[tmr] = TRUE; sim_activate_abs (&sim_timer_units[tmr], (time < 0) ? 0 : time); return TRUE; } } return FALSE; } t_stat sim_rtcn_tick_ack (uint32 time, int32 tmr) { if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return SCPE_TIMER; sim_debug (DBG_ACK, &sim_timer_dev, "sim_rtcn_tick_ack - for %s\n", sim_uname (sim_clock_unit[tmr])); _rtcn_tick_catchup_check (tmr, (int32)time); ++rtc_calib_ticks_acked[tmr]; return SCPE_OK; } static double _timespec_to_double (struct timespec *time) { return ((double)time->tv_sec)+(double)(time->tv_nsec)/1000000000.0; } static void _double_to_timespec (struct timespec *time, double dtime) { time->tv_sec = (time_t)floor(dtime); time->tv_nsec = (long)((dtime-floor(dtime))*1000000000.0); } double sim_timenow_double (void) { struct timespec now; clock_gettime (CLOCK_REALTIME, &now); return _timespec_to_double (&now); } #if defined(SIM_ASYNCH_CLOCKS) pthread_t sim_timer_thread; /* Wall Clock Timing Thread Id */ pthread_cond_t sim_timer_startup_cond; t_bool sim_timer_thread_running = FALSE; static void * _timer_thread(void *arg) { int sched_policy; struct sched_param sched_priority; /* Boost Priority for this I/O thread vs the CPU instruction execution thread which, in general, won't be readily yielding the processor when this thread needs to run */ pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); ++sched_priority.sched_priority; pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - starting\n"); pthread_mutex_lock (&sim_timer_lock); pthread_cond_signal (&sim_timer_startup_cond); /* Signal we're ready to go */ while (sim_asynch_timer && sim_is_running) { struct timespec start_time, stop_time; struct timespec due_time; double wait_usec; int32 inst_delay; double inst_per_sec; UNIT *uptr, *cptr, *prvptr; if (sim_wallclock_entry) { /* something to insert in queue? */ sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - timing %s for %s\n", sim_uname(sim_wallclock_entry), sim_fmt_secs (sim_wallclock_entry->a_usec_delay/1000000.0)); uptr = sim_wallclock_entry; sim_wallclock_entry = NULL; prvptr = NULL; for (cptr = sim_wallclock_queue; cptr != QUEUE_LIST_END; cptr = cptr->a_next) { if (uptr->a_due_time < cptr->a_due_time) break; prvptr = cptr; } if (prvptr == NULL) { /* insert at head */ cptr = uptr->a_next = sim_wallclock_queue; sim_wallclock_queue = uptr; } else { cptr = uptr->a_next = prvptr->a_next; /* insert at prvptr */ prvptr->a_next = uptr; } } /* determine wait time */ if (sim_wallclock_queue != QUEUE_LIST_END) { /* due time adjusted by 1/2 a minimal sleep interval */ /* the goal being to let the last fractional part of the due time */ /* be done by counting instructions */ _double_to_timespec (&due_time, sim_wallclock_queue->a_due_time-(((double)sim_idle_rate_ms)*0.0005)); } else { due_time.tv_sec = 0x7FFFFFFF; /* Sometime when 32 bit time_t wraps */ due_time.tv_nsec = 0; } clock_gettime(CLOCK_REALTIME, &start_time); wait_usec = floor(1000000.0*(_timespec_to_double (&due_time) - _timespec_to_double (&start_time))); if (sim_wallclock_queue == QUEUE_LIST_END) sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting forever\n"); else sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting for %.0f usecs until %.6f for %s\n", wait_usec, sim_wallclock_queue->a_due_time, sim_uname(sim_wallclock_queue)); if ((wait_usec <= 0.0) || (0 != pthread_cond_timedwait (&sim_timer_wake, &sim_timer_lock, &due_time))) { if (sim_wallclock_queue == QUEUE_LIST_END) /* queue empty? */ continue; /* wait again */ inst_per_sec = sim_timer_inst_per_sec (); uptr = sim_wallclock_queue; sim_wallclock_queue = uptr->a_next; uptr->a_next = NULL; /* hygiene */ clock_gettime(CLOCK_REALTIME, &stop_time); if (1 != sim_timespec_compare (&due_time, &stop_time)) inst_delay = 0; else inst_delay = (int32)(inst_per_sec*(_timespec_to_double(&due_time)-_timespec_to_double(&stop_time))); sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - slept %.0fms - activating(%s,%d)\n", 1000.0*(_timespec_to_double (&stop_time)-_timespec_to_double (&start_time)), sim_uname(uptr), inst_delay); sim_activate (uptr, inst_delay); } else {/* Something wants to adjust the queue since the wait condition was signaled */ } } pthread_mutex_unlock (&sim_timer_lock); sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - exiting\n"); return NULL; } #endif /* defined(SIM_ASYNCH_CLOCKS) */ /* In the event that there are no active clock devices, no instruction rate calibration will be performed. This is more likely on simpler simulators which don't have a full spectrum of standard devices or possibly when a clock device exists but its use is optional. Additonally, when a host system has a natural clock tick (or minimal sleep time) which is greater than the tick size that a simulator wants to run a clock at, we run this clock at the rate implied by the host system's minimal sleep time or 50Hz. To solve this we merely run an internal clock at 10Hz. */ #define CLK_TPS 10 #define CLK_INIT (SIM_INITIAL_IPS/CLK_TPS) static int32 sim_int_clk_tps; static t_stat sim_timer_clock_tick_svc (UNIT *uptr) { sim_rtcn_calb (sim_int_clk_tps, SIM_INTERNAL_CLK); sim_activate_after (uptr, 1000000/sim_int_clk_tps); /* reactivate unit */ return SCPE_OK; } /* This routine exists to assure that there is a single reliably calibrated clock properly counting instruction execution relative to time. The best way to assure reliable calibration is to use a clock which ticks no faster than the host system's clock. This is optimal so that accurate time measurements are taken. If the simulated system doesn't have a clock with an appropriate tick rate, an internal clock is run that meets this requirement, */ static void _rtcn_configure_calibrated_clock (int32 newtmr) { int32 tmr; /* Look for a timer running slower than the host system clock */ sim_int_clk_tps = MIN(CLK_TPS, sim_os_tick_hz); for (tmr=0; tmr<SIM_NTIMERS; tmr++) { if ((rtc_hz[tmr]) && (rtc_hz[tmr] <= (uint32)sim_os_tick_hz)) break; } if (tmr == SIM_NTIMERS) { /* None found? */ if ((tmr != newtmr) && (!sim_is_active (&SIM_INTERNAL_UNIT))) { if ((sim_calb_tmr != SIM_NTIMERS) &&/* not internal timer? */ (sim_calb_tmr != -1) && /* previously active? */ (!rtc_hz[sim_calb_tmr])) { /* now stopped? */ sim_debug (DBG_CAL, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Cleaning up stopped timer %s support\n", newtmr, sim_uname(sim_clock_unit[sim_calb_tmr])); /* Migrate any coscheduled devices to the standard queue */ /* with appropriate usecs_remaining reflecting their currently */ /* scheduled firing time. sim_process_event() will coschedule */ /* appropriately. */ /* temporarily restore prior hz to get correct remaining time */ rtc_hz[sim_calb_tmr] = rtc_last_hz[sim_calb_tmr]; while (sim_clock_cosched_queue[sim_calb_tmr] != QUEUE_LIST_END) { UNIT *uptr = sim_clock_cosched_queue[sim_calb_tmr]; double usecs_remaining = sim_timer_activate_time_usecs (uptr) - 1; _sim_coschedule_cancel (uptr); _sim_activate (uptr, 1); uptr->usecs_remaining = usecs_remaining; } rtc_hz[sim_calb_tmr] = 0; /* back to 0 */ if (sim_clock_unit[sim_calb_tmr]) sim_cancel (sim_clock_unit[sim_calb_tmr]); sim_cancel (&sim_timer_units[sim_calb_tmr]); } /* Start the internal timer */ sim_calb_tmr = SIM_NTIMERS; sim_debug (DBG_CAL, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Starting Internal Calibrated Timer at %dHz\n", newtmr, sim_int_clk_tps); SIM_INTERNAL_UNIT.action = &sim_timer_clock_tick_svc; SIM_INTERNAL_UNIT.flags = UNIT_IDLE; sim_register_internal_device (&sim_int_timer_dev); /* Register Internal timer device */ sim_rtcn_init_unit (&SIM_INTERNAL_UNIT, (CLK_INIT*CLK_TPS)/sim_int_clk_tps, SIM_INTERNAL_CLK); SIM_INTERNAL_UNIT.action (&SIM_INTERNAL_UNIT); /* Force tick to activate timer */ } return; } if ((tmr == newtmr) && (sim_calb_tmr == newtmr)) /* already set? */ return; if (sim_calb_tmr == SIM_NTIMERS) { /* was old the internal timer? */ sim_debug (DBG_CAL, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Stopping Internal Calibrated Timer, New Timer = %d (%dHz)\n", newtmr, tmr, rtc_hz[tmr]); rtc_initd[SIM_NTIMERS] = 0; rtc_hz[SIM_NTIMERS] = 0; sim_register_clock_unit_tmr (NULL, SIM_INTERNAL_CLK); sim_cancel (&SIM_INTERNAL_UNIT); sim_cancel (&sim_timer_units[SIM_NTIMERS]); } else { if ((sim_calb_tmr != -1) && (rtc_hz[sim_calb_tmr] == 0)) { /* Migrate any coscheduled devices to the standard queue */ /* with appropriate usecs_remaining reflecting their currently */ /* scheduled firing time. sim_process_event() will coschedule */ /* appropriately. */ /* temporarily restore prior hz to get correct remaining time */ rtc_hz[sim_calb_tmr] = rtc_last_hz[sim_calb_tmr]; while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) { UNIT *uptr = sim_clock_cosched_queue[tmr]; double usecs_remaining = sim_timer_activate_time_usecs (uptr) - 1; _sim_coschedule_cancel (uptr); _sim_activate (uptr, 1); uptr->usecs_remaining = usecs_remaining; } rtc_hz[sim_calb_tmr] = 0; /* back to 0 */ } sim_debug (DBG_CAL, &sim_timer_dev, "_rtcn_configure_calibrated_clock(newtmr=%d) - Changing Calibrated Timer from %d (%dHz) to %d (%dHz)\n", newtmr, sim_calb_tmr, rtc_hz[sim_calb_tmr], tmr, rtc_hz[tmr]); sim_calb_tmr = tmr; } sim_calb_tmr = tmr; } static t_stat sim_timer_clock_reset (DEVICE *dptr) { sim_debug (DBG_TRC, &sim_timer_dev, "sim_timer_clock_reset()\n"); _rtcn_configure_calibrated_clock (sim_calb_tmr); sim_timer_dev.description = &sim_timer_description; sim_throttle_dev.description = &sim_throttle_description; sim_int_timer_dev.description = &sim_int_timer_description; if (sim_switches & SWMASK ('P')) { sim_cancel (&SIM_INTERNAL_UNIT); sim_calb_tmr = -1; } return SCPE_OK; } void sim_start_timer_services (void) { sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services()\n"); _rtcn_configure_calibrated_clock (sim_calb_tmr); #if defined(SIM_ASYNCH_CLOCKS) pthread_mutex_lock (&sim_timer_lock); if (sim_asynch_timer) { pthread_attr_t attr; sim_debug (DBG_TRC, &sim_timer_dev, "sim_start_timer_services() - starting\n"); pthread_cond_init (&sim_timer_startup_cond, NULL); pthread_attr_init (&attr); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); pthread_create (&sim_timer_thread, &attr, _timer_thread, NULL); pthread_attr_destroy( &attr); pthread_cond_wait (&sim_timer_startup_cond, &sim_timer_lock); /* Wait for thread to stabilize */ pthread_cond_destroy (&sim_timer_startup_cond); sim_timer_thread_running = TRUE; } pthread_mutex_unlock (&sim_timer_lock); #endif } void sim_stop_timer_services (void) { int tmr; sim_debug (DBG_TRC, &sim_timer_dev, "sim_stop_timer_services()\n"); for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { int32 accum; if (sim_clock_unit[tmr]) { int32 clock_time = _sim_activate_time (&sim_timer_units[tmr]); if (clock_time < 0) clock_time = 0; /* Stop clock assist unit and make sure the clock unit has a tick queued */ sim_cancel (&sim_timer_units[tmr]); if (rtc_hz[tmr]) { sim_debug (DBG_QUE, &sim_timer_dev, "sim_stop_timer_services() - tmr=%d scheduling %s after %d\n", tmr, sim_uname (sim_clock_unit[tmr]), clock_time); _sim_activate (sim_clock_unit[tmr], clock_time); } /* Move coscheduled units to the standard event queue */ accum = 0; while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) { UNIT *cptr = sim_clock_cosched_queue[tmr]; double usecs_remaining = cptr->usecs_remaining; cptr->usecs_remaining = 0; sim_clock_cosched_queue[tmr] = cptr->next; cptr->next = NULL; cptr->cancel = NULL; accum += cptr->time; sim_debug (DBG_QUE, &sim_timer_dev, "sim_stop_timer_services() - tmr=%d scheduling %s after %d\n", tmr, sim_uname (cptr), clock_time + accum*rtc_currd[tmr]); _sim_activate (cptr, clock_time + accum*rtc_currd[tmr]); cptr->usecs_remaining = usecs_remaining; } sim_cosched_interval[tmr] = 0; } } sim_cancel (&SIM_INTERNAL_UNIT); /* Make sure Internal Timer is stopped */ sim_cancel (&sim_timer_units[SIM_NTIMERS]); sim_calb_tmr_last = sim_calb_tmr; /* Save calibrated timer value for display */ sim_inst_per_sec_last = sim_timer_inst_per_sec (); /* Save execution rate for display */ sim_calb_tmr = -1; #if defined(SIM_ASYNCH_CLOCKS) pthread_mutex_lock (&sim_timer_lock); if (sim_timer_thread_running) { sim_debug (DBG_TRC, &sim_timer_dev, "sim_stop_timer_services() - stopping\n"); pthread_cond_signal (&sim_timer_wake); pthread_mutex_unlock (&sim_timer_lock); pthread_join (sim_timer_thread, NULL); sim_timer_thread_running = FALSE; /* Any wallclock queued events are now migrated to the normal event queue */ while (sim_wallclock_queue != QUEUE_LIST_END) { UNIT *uptr = sim_wallclock_queue; double inst_delay_d = uptr->a_due_gtime - sim_gtime (); int32 inst_delay; uptr->cancel (uptr); if (inst_delay_d < 0.0) inst_delay_d = 0.0; /* Bound delay to avoid overflow. */ /* Long delays are usually canceled before they expire */ if (inst_delay_d > (double)0x7FFFFFFF) inst_delay_d = (double)0x7FFFFFFF; inst_delay = (int32)inst_delay_d; if ((inst_delay == 0) && (inst_delay_d != 0.0)) inst_delay = 1; /* Minimum non-zero delay is 1 instruction */ _sim_activate (uptr, inst_delay); /* queue it now */ } } else pthread_mutex_unlock (&sim_timer_lock); #endif } t_stat sim_timer_change_asynch (void) { #if defined(SIM_ASYNCH_CLOCKS) if (sim_asynch_enabled && sim_asynch_timer) sim_start_timer_services (); else sim_stop_timer_services (); #endif return SCPE_OK; } /* Instruction Execution rate. */ /* returns a double since it is mostly used in double expressions and to avoid overflow if/when strange timing delays might produce unexpected results */ double sim_timer_inst_per_sec (void) { double inst_per_sec = sim_inst_per_sec_last; if (sim_calb_tmr == -1) return inst_per_sec; inst_per_sec = ((double)rtc_currd[sim_calb_tmr])*rtc_hz[sim_calb_tmr]; if (0 == inst_per_sec) inst_per_sec = ((double)rtc_currd[sim_calb_tmr])*sim_int_clk_tps; return inst_per_sec; } t_stat sim_timer_activate (UNIT *uptr, int32 interval) { AIO_VALIDATE; return sim_timer_activate_after (uptr, (double)((interval * 1000000.0) / sim_timer_inst_per_sec ())); } t_stat sim_timer_activate_after (UNIT *uptr, double usec_delay) { UNIT *ouptr = uptr; int inst_delay, tmr; double inst_delay_d, inst_per_usec; t_stat stat; AIO_VALIDATE; /* If this is a clock unit, we need to schedule the related timer unit instead */ for (tmr=0; tmr<=SIM_NTIMERS; tmr++) if (sim_clock_unit[tmr] == uptr) { uptr = &sim_timer_units[tmr]; break; } if (sim_is_active (uptr)) /* already active? */ return SCPE_OK; uptr->usecs_remaining = 0; if (usec_delay <= 0.0) { sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - invalid usec value\n", sim_uname(uptr), usec_delay); return SCPE_ARG; } /* * Handle long delays by aligning with the calibrated timer's calibration * activities. Delays which would expire prior to the next calibration * are specifically scheduled directly based on the the current instruction * execution rate. Longer delays are coscheduled to fire on the first tick * after the next calibration and at that time are either scheduled directly * or re-coscheduled for the next calibration time, repeating until the total * desired time has elapsed. */ inst_per_usec = sim_timer_inst_per_sec () / 1000000.0; inst_delay_d = floor(inst_per_usec * usec_delay); inst_delay = (int32)inst_delay_d; if ((inst_delay == 0) && (usec_delay != 0)) inst_delay_d = inst_delay = 1; /* Minimum non-zero delay is 1 instruction */ if ((sim_calb_tmr != -1) && (rtc_hz[sim_calb_tmr])) { /* Calibrated Timer available? */ int32 inst_til_tick = sim_activate_time (&sim_timer_units[sim_calb_tmr]) - 1; int32 ticks_til_calib = rtc_hz[sim_calb_tmr] - rtc_ticks[sim_calb_tmr]; int32 inst_til_calib = inst_til_tick + ((ticks_til_calib - 1) * rtc_currd[sim_calb_tmr]); uint32 usecs_til_calib = (uint32)ceil(inst_til_calib / inst_per_usec); if (uptr != &sim_timer_units[sim_calb_tmr]) { /* Not scheduling calibrated timer? */ if (inst_delay_d >= (double)inst_til_calib) { /* long wait? */ stat = sim_clock_coschedule_tmr (uptr, sim_calb_tmr, ticks_til_calib - 1); uptr->usecs_remaining = (stat == SCPE_OK) ? usec_delay - usecs_til_calib : 0.0; sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - coscheduling with with calibrated timer(%d), ticks=%d, usecs_remaining=%.0f usecs, inst_til_tick=%d\n", sim_uname(uptr), usec_delay, sim_calb_tmr, ticks_til_calib, uptr->usecs_remaining, inst_til_tick); sim_debug (DBG_CHK, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - result = %.0f usecs, %.0f usecs\n", sim_uname(uptr), usec_delay, sim_timer_activate_time_usecs (ouptr), sim_timer_activate_time_usecs (uptr)); return stat; } } } /* * We're here to schedule if: * No Calibrated Timer, OR * Scheduling the Calibrated Timer OR * Short delay */ /* * Bound delay to avoid overflow. * Long delays are usually canceled before they expire, however bounding the * delay will cause sim_activate_time to return inconsistent results when * truncation has happened. */ if (inst_delay_d > (double)0x7fffffff) inst_delay_d = (double)0x7fffffff; /* Bound delay to avoid overflow. */ inst_delay = (int32)inst_delay_d; #if defined(SIM_ASYNCH_CLOCKS) if ((sim_asynch_timer) && (usec_delay > sim_idle_rate_ms*1000.0)) { double d_now = sim_timenow_double (); UNIT *cptr, *prvptr; uptr->a_usec_delay = usec_delay; uptr->a_due_time = d_now + (usec_delay / 1000000.0); uptr->a_due_gtime = sim_gtime () + (sim_timer_inst_per_sec () * (usec_delay / 1000000.0)); uptr->cancel = &_sim_wallclock_cancel; /* bind cleanup method */ uptr->a_is_active = &_sim_wallclock_is_active; if (tmr <= SIM_NTIMERS) { /* Timer Unit? */ sim_clock_unit[tmr]->cancel = &_sim_wallclock_cancel; sim_clock_unit[tmr]->a_is_active = &_sim_wallclock_is_active; } sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - queueing wallclock addition at %.6f\n", sim_uname(uptr), usec_delay, uptr->a_due_time); pthread_mutex_lock (&sim_timer_lock); for (cptr = sim_wallclock_queue, prvptr = NULL; cptr != QUEUE_LIST_END; cptr = cptr->a_next) { if (uptr->a_due_time < cptr->a_due_time) break; prvptr = cptr; } if (prvptr == NULL) { /* inserting at head */ uptr->a_next = QUEUE_LIST_END; /* Temporarily mark as active */ while (sim_wallclock_entry) { /* wait for any prior entry has been digested */ sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - queue insert entry %s busy waiting for 1ms\n", sim_uname(uptr), usec_delay, sim_uname(sim_wallclock_entry)); pthread_mutex_unlock (&sim_timer_lock); sim_os_ms_sleep (1); pthread_mutex_lock (&sim_timer_lock); } sim_wallclock_entry = uptr; pthread_mutex_unlock (&sim_timer_lock); pthread_cond_signal (&sim_timer_wake); /* wake the timer thread to deal with it */ return SCPE_OK; } else { /* inserting at prvptr */ uptr->a_next = prvptr->a_next; prvptr->a_next = uptr; pthread_mutex_unlock (&sim_timer_lock); return SCPE_OK; } } #endif stat = _sim_activate (uptr, inst_delay); /* queue it now */ uptr->usecs_remaining = ((stat == SCPE_OK) && (0.0 < (usec_delay - ceil(inst_delay / inst_per_usec) ))) ? usec_delay - floor(inst_delay / inst_per_usec) : 0.0; sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - queue addition at %d - remnant: %.0f\n", sim_uname(uptr), usec_delay, inst_delay, uptr->usecs_remaining); sim_debug (DBG_CHK, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - result = %.0f usecs, %.0f usecs\n", sim_uname(uptr), usec_delay, sim_timer_activate_time_usecs (ouptr), sim_timer_activate_time_usecs (uptr)); return stat; } /* Clock coscheduling routines */ t_stat sim_register_clock_unit_tmr (UNIT *uptr, int32 tmr) { if (tmr == SIM_INTERNAL_CLK) tmr = SIM_NTIMERS; else { if ((tmr < 0) || (tmr > SIM_NTIMERS)) return SCPE_IERR; } if (NULL == uptr) { /* deregistering? */ /* Migrate any coscheduled devices to the standard queue */ /* they will fire and subsequently requeue themselves */ while (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) { UNIT *uptr = sim_clock_cosched_queue[tmr]; double usecs_remaining = sim_timer_activate_time_usecs (uptr); _sim_coschedule_cancel (uptr); _sim_activate (uptr, 1); uptr->usecs_remaining = usecs_remaining; } if (sim_clock_unit[tmr]) { sim_cancel (sim_clock_unit[tmr]); sim_clock_unit[tmr]->dynflags &= ~UNIT_TMR_UNIT; } sim_clock_unit[tmr] = NULL; sim_cancel (&sim_timer_units[tmr]); return SCPE_OK; } if (NULL == sim_clock_unit[tmr]) sim_clock_cosched_queue[tmr] = QUEUE_LIST_END; sim_clock_unit[tmr] = uptr; uptr->dynflags |= UNIT_TMR_UNIT; sim_timer_units[tmr].flags = ((tmr == SIM_NTIMERS) ? 0 : UNIT_DIS) | (sim_clock_unit[tmr] ? UNIT_IDLE : 0); return SCPE_OK; } /* Default timer is 0, otherwise use a calibrated one if it exists */ int32 sim_rtcn_calibrated_tmr (void) { return ((rtc_currd[0] && rtc_hz[0]) ? 0 : ((sim_calb_tmr != -1) ? sim_calb_tmr : 0)); } int32 sim_rtcn_tick_size (int32 tmr) { return (rtc_currd[tmr]) ? rtc_currd[tmr] : 10000; } t_stat sim_register_clock_unit (UNIT *uptr) { return sim_register_clock_unit_tmr (uptr, 0); } t_stat sim_clock_coschedule (UNIT *uptr, int32 interval) { int32 tmr = sim_rtcn_calibrated_tmr (); int32 ticks = (interval + (sim_rtcn_tick_size (tmr)/2))/sim_rtcn_tick_size (tmr);/* Convert to ticks */ sim_debug (DBG_QUE, &sim_timer_dev, "sim_clock_coschedule(%s, interval=%d, ticks=%d)\n", sim_uname(uptr), interval, ticks); return sim_clock_coschedule_tmr (uptr, tmr, ticks); } t_stat sim_clock_coschedule_abs (UNIT *uptr, int32 interval) { sim_debug (DBG_QUE, &sim_timer_dev, "sim_clock_coschedule_abs(%s, interval=%d)\n", sim_uname(uptr), interval); sim_cancel (uptr); return sim_clock_coschedule (uptr, interval); } /* ticks - 0 means on the next tick, 1 means the second tick, etc. */ t_stat sim_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 ticks) { if (ticks < 0) return SCPE_ARG; if (sim_is_active (uptr)) { sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule_tmr(%s, tmr=%d, ticks=%d) - already active\n", sim_uname (uptr), tmr, ticks); return SCPE_OK; } if (tmr == SIM_INTERNAL_CLK) tmr = SIM_NTIMERS; else { if ((tmr < 0) || (tmr > SIM_NTIMERS)) return sim_activate (uptr, MAX(1, ticks) * 10000); } if ((NULL == sim_clock_unit[tmr]) || (rtc_hz[tmr] == 0)) { sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule_tmr(%s, tmr=%d, ticks=%d) - no clock activating after %d instructions\n", sim_uname (uptr), tmr, ticks, ticks * (rtc_currd[tmr] ? rtc_currd[tmr] : rtc_currd[sim_rtcn_calibrated_tmr ()])); return sim_activate (uptr, ticks * (rtc_currd[tmr] ? rtc_currd[tmr] : rtc_currd[sim_rtcn_calibrated_tmr ()])); } else { UNIT *cptr, *prvptr; int32 accum; if (sim_clock_cosched_queue[tmr] != QUEUE_LIST_END) sim_clock_cosched_queue[tmr]->time = sim_cosched_interval[tmr]; prvptr = NULL; accum = 0; for (cptr = sim_clock_cosched_queue[tmr]; cptr != QUEUE_LIST_END; cptr = cptr->next) { if (ticks < (accum + cptr->time)) break; accum += cptr->time; prvptr = cptr; } if (prvptr == NULL) { cptr = uptr->next = sim_clock_cosched_queue[tmr]; sim_clock_cosched_queue[tmr] = uptr; } else { cptr = uptr->next = prvptr->next; prvptr->next = uptr; } uptr->time = ticks - accum; if (cptr != QUEUE_LIST_END) cptr->time = cptr->time - uptr->time; uptr->cancel = &_sim_coschedule_cancel; /* bind cleanup method */ if (uptr == sim_clock_cosched_queue[tmr]) sim_cosched_interval[tmr] = sim_clock_cosched_queue[tmr]->time; sim_debug (DBG_QUE, &sim_timer_dev, "sim_clock_coschedule_tmr(%s, tmr=%d, ticks=%d, hz=%d) - queueing for clock co-schedule, interval now: %d\n", sim_uname (uptr), tmr, ticks, rtc_hz[tmr], sim_cosched_interval[tmr]); } return SCPE_OK; } t_stat sim_clock_coschedule_tmr_abs (UNIT *uptr, int32 tmr, int32 ticks) { sim_cancel (uptr); return sim_clock_coschedule_tmr (uptr, tmr, ticks); } /* Cancel a unit on the coschedule queue */ static t_bool _sim_coschedule_cancel (UNIT *uptr) { AIO_UPDATE_QUEUE; if (uptr->next) { /* On a queue? */ int tmr; UNIT *nptr; for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { if (sim_clock_unit[tmr]) { if (uptr == sim_clock_cosched_queue[tmr]) { nptr = sim_clock_cosched_queue[tmr] = uptr->next; uptr->next = NULL; } else { UNIT *cptr; for (cptr = sim_clock_cosched_queue[tmr]; (cptr != QUEUE_LIST_END); cptr = cptr->next) { if (cptr->next == uptr) { nptr = cptr->next = (uptr)->next; uptr->next = NULL; break; } } } if (uptr->next == NULL) { /* found? */ uptr->cancel = NULL; uptr->usecs_remaining = 0; if (nptr != QUEUE_LIST_END) nptr->time += uptr->time; sim_debug (DBG_QUE, &sim_timer_dev, "Canceled Clock Coscheduled Event for %s\n", sim_uname(uptr)); return TRUE; } } } } return FALSE; } t_bool sim_timer_is_active (UNIT *uptr) { int32 tmr; if (!(uptr->dynflags & UNIT_TMR_UNIT)) return FALSE; for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { if (sim_clock_unit[tmr] == uptr) return sim_is_active (&sim_timer_units[tmr]); } return FALSE; } t_bool sim_timer_cancel (UNIT *uptr) { int32 tmr; if (!(uptr->dynflags & UNIT_TMR_UNIT)) return SCPE_IERR; for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { if (sim_clock_unit[tmr] == uptr) return sim_cancel (&sim_timer_units[tmr]); } return SCPE_IERR; } #if defined(SIM_ASYNCH_CLOCKS) static t_bool _sim_wallclock_cancel (UNIT *uptr) { int32 tmr; t_bool b_return = FALSE; AIO_UPDATE_QUEUE; pthread_mutex_lock (&sim_timer_lock); /* If this is a clock unit, we need to cancel both this and the related timer unit */ for (tmr=0; tmr<=SIM_NTIMERS; tmr++) if (sim_clock_unit[tmr] == uptr) { uptr = &sim_timer_units[tmr]; break; } if (uptr->a_next) { UNIT *cptr; if (uptr == sim_wallclock_entry) { /* Pending on the queue? */ sim_wallclock_entry = NULL; uptr->a_next = NULL; } else { if (uptr == sim_wallclock_queue) { sim_wallclock_queue = uptr->a_next; uptr->a_next = NULL; sim_debug (DBG_QUE, &sim_timer_dev, "Canceling Timer Event for %s\n", sim_uname(uptr)); pthread_cond_signal (&sim_timer_wake); } else { for (cptr = sim_wallclock_queue; (cptr != QUEUE_LIST_END); cptr = cptr->a_next) { if (cptr->a_next == (uptr)) { cptr->a_next = (uptr)->a_next; uptr->a_next = NULL; sim_debug (DBG_QUE, &sim_timer_dev, "Canceled Timer Event for %s\n", sim_uname(uptr)); break; } } } } if (uptr->a_next == NULL) { uptr->a_due_time = uptr->a_due_gtime = uptr->a_usec_delay = 0; uptr->cancel = NULL; uptr->a_is_active = NULL; if (tmr <= SIM_NTIMERS) { /* Timer Unit? */ sim_clock_unit[tmr]->cancel = NULL; sim_clock_unit[tmr]->a_is_active = NULL; } b_return = TRUE; } } pthread_mutex_unlock (&sim_timer_lock); return b_return; } static t_bool _sim_wallclock_is_active (UNIT *uptr) { int32 tmr; if (uptr->a_next) return TRUE; /* If this is a clock unit, we need to examine the related timer unit instead */ for (tmr=0; tmr<=SIM_NTIMERS; tmr++) if (sim_clock_unit[tmr] == uptr) return (sim_timer_units[tmr].a_next != NULL); return FALSE; } #endif /* defined(SIM_ASYNCH_CLOCKS) */ int32 _sim_timer_activate_time (UNIT *uptr) { UNIT *cptr; int32 tmr; #if defined(SIM_ASYNCH_CLOCKS) if (uptr->a_is_active == &_sim_wallclock_is_active) { double d_result; pthread_mutex_lock (&sim_timer_lock); if (uptr == sim_wallclock_entry) { d_result = uptr->a_due_gtime - sim_gtime (); if (d_result < 0.0) d_result = 0.0; if (d_result > (double)0x7FFFFFFE) d_result = (double)0x7FFFFFFE; pthread_mutex_unlock (&sim_timer_lock); return ((int32)d_result) + 1; } for (cptr = sim_wallclock_queue; cptr != QUEUE_LIST_END; cptr = cptr->a_next) if (uptr == cptr) { d_result = uptr->a_due_gtime - sim_gtime (); if (d_result < 0.0) d_result = 0.0; if (d_result > (double)0x7FFFFFFE) d_result = (double)0x7FFFFFFE; pthread_mutex_unlock (&sim_timer_lock); return ((int32)d_result) + 1; } pthread_mutex_unlock (&sim_timer_lock); } if (uptr->a_next) return uptr->a_event_time + 1; #endif /* defined(SIM_ASYNCH_CLOCKS) */ if (uptr->cancel == &_sim_coschedule_cancel) { for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { int32 accum = 0; for (cptr = sim_clock_cosched_queue[tmr]; cptr != QUEUE_LIST_END; cptr = cptr->next) { if (cptr == sim_clock_cosched_queue[tmr]) { if (sim_cosched_interval[tmr] > 0) accum += sim_cosched_interval[tmr]; } else accum += cptr->time; if (cptr == uptr) return (rtc_currd[tmr] * accum) + sim_activate_time (&sim_timer_units[tmr]); } } } for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { if ((uptr == &sim_timer_units[tmr]) && (uptr->next)){ return _sim_activate_time (&sim_timer_units[tmr]); } } return -1; /* Not found. */ } double sim_timer_activate_time_usecs (UNIT *uptr) { UNIT *cptr; int32 tmr; double result = -1.0; /* If this is a clock unit, we need to return the related clock assist unit instead */ for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { if (sim_clock_unit[tmr] == uptr) { uptr = &sim_timer_units[tmr]; break; } } if (!sim_is_active (uptr)) { sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) - not active\n", sim_uname (uptr)); return result; } #if defined(SIM_ASYNCH_CLOCKS) if (uptr->a_is_active == &_sim_wallclock_is_active) { pthread_mutex_lock (&sim_timer_lock); if (uptr == sim_wallclock_entry) { result = uptr->a_due_gtime - sim_gtime (); if (result < 0.0) result = 0.0; pthread_mutex_unlock (&sim_timer_lock); result = uptr->usecs_remaining + (1000000.0 * (result / sim_timer_inst_per_sec ())) + 1; sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) wallclock_entry - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ()); return result; } for (cptr = sim_wallclock_queue; cptr != QUEUE_LIST_END; cptr = cptr->a_next) if (uptr == cptr) { result = uptr->a_due_gtime - sim_gtime (); if (result < 0.0) result = 0.0; pthread_mutex_unlock (&sim_timer_lock); result = uptr->usecs_remaining + (1000000.0 * (result / sim_timer_inst_per_sec ())) + 1; sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) wallclock - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ()); return result; } pthread_mutex_unlock (&sim_timer_lock); } if (uptr->a_next) { result = uptr->usecs_remaining + (1000000.0 * (uptr->a_event_time / sim_timer_inst_per_sec ())) + 1; sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) asynch - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ()); return result; } #endif /* defined(SIM_ASYNCH_CLOCKS) */ if (uptr->cancel == &_sim_coschedule_cancel) { for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { int32 accum = 0; for (cptr = sim_clock_cosched_queue[tmr]; cptr != QUEUE_LIST_END; cptr = cptr->next) { if (cptr == sim_clock_cosched_queue[tmr]) { if (sim_cosched_interval[tmr] > 0) accum += sim_cosched_interval[tmr]; } else accum += cptr->time; if (cptr == uptr) { result = uptr->usecs_remaining + ceil(1000000.0 * ((rtc_currd[tmr] * accum) + sim_activate_time (&sim_timer_units[tmr]) - 1) / sim_timer_inst_per_sec ()); sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) coscheduled - %.0f usecs, inst_per_sec=%.0f, tmr=%d, ticksize=%d, ticks=%d, inst_til_tick=%d\n", sim_uname (uptr), result, sim_timer_inst_per_sec (), tmr, rtc_currd[tmr], accum, sim_activate_time (&sim_timer_units[tmr]) - 1); return result; } } } } for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { if ((uptr == sim_clock_unit[tmr]) && (uptr->next)) { result = sim_clock_unit[tmr]->usecs_remaining + (1000000.0 * (sim_activate_time (&sim_timer_units[tmr]) - 1)) / sim_timer_inst_per_sec (); sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) clock - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ()); return result; } if ((uptr == &sim_timer_units[tmr]) && (uptr->next)){ result = uptr->usecs_remaining + (1000000.0 * (sim_activate_time (uptr) - 1)) / sim_timer_inst_per_sec (); sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) clock - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ()); return result; } } result = uptr->usecs_remaining + (1000000.0 * (sim_activate_time (uptr) - 1)) / sim_timer_inst_per_sec (); sim_debug (DBG_QUE, &sim_timer_dev, "sim_timer_activate_time_usecs(%s) clock - %.0f usecs, inst_per_sec=%.0f\n", sim_uname (uptr), result, sim_timer_inst_per_sec ()); return result; /* Not found. */ } /* read only memory delayed support Some simulation activities need a 'regulated' memory access time to meet timing assumptions in the code being executed. The default calibration determines a way to limit activities to 1Mhz for each call to sim_rom_read_with_delay(). If a simulator needs a different delay factor, the 1 Mhz initial value can be queried with sim_get_rom_delay_factor() and the result can be adjusted as nessary and the operating delay can be set with sim_set_rom_delay_factor(). */ SIM_NOINLINE static int32 _rom_swapb(int32 val) { return ((val << 24) & 0xff000000) | (( val << 8) & 0xff0000) | ((val >> 8) & 0xff00) | ((val >> 24) & 0xff); } static volatile int32 rom_loopval = 0; SIM_NOINLINE int32 sim_rom_read_with_delay (int32 val) { uint32 i, l = sim_rom_delay; for (i = 0; i < l; i++) rom_loopval |= (rom_loopval + val) ^ _rom_swapb (_rom_swapb (rom_loopval + val)); return val + rom_loopval; } SIM_NOINLINE uint32 sim_get_rom_delay_factor (void) { /* Calibrate the loop delay factor at startup. Do this 4 times and use the largest value computed. The goal here is to come up with a delay factor which will throttle a 6 byte delay loop running from ROM address space to execute 1 instruction per usec */ if (sim_rom_delay == 0) { uint32 i, ts, te, c = 10000, samples = 0; while (1) { c = c * 2; te = sim_os_msec(); while (te == (ts = sim_os_msec ())); /* align on ms tick */ /* This is merely a busy wait with some "work" that won't get optimized away by a good compiler. loopval always is zero. To avoid smart compilers, the loopval variable is referenced in the function arguments so that the function expression is not loop invariant. It also must be referenced by subsequent code to avoid the whole computation being eliminated. */ for (i = 0; i < c; i++) rom_loopval |= (rom_loopval + ts) ^ _rom_swapb (_rom_swapb (rom_loopval + ts)); te = sim_os_msec (); if ((te - ts) < 50) /* sample big enough? */ continue; if (sim_rom_delay < (rom_loopval + (c / (te - ts) / 1000) + 1)) sim_rom_delay = rom_loopval + (c / (te - ts) / 1000) + 1; if (++samples >= 4) break; c = c / 2; } if (sim_rom_delay < 5) sim_rom_delay = 5; } return sim_rom_delay; } void sim_set_rom_delay_factor (uint32 delay) { sim_rom_delay = delay; } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | /* sim_timer.h: simulator timer library headers Copyright (c) 1993-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. 28-Apr-07 RMS Added sim_rtc_init_all 17-Oct-06 RMS Added idle support 02-Jan-04 RMS Split out from SCP */ #ifndef SIM_TIMER_H_ #define SIM_TIMER_H_ 0 #ifdef __cplusplus extern "C" { #endif /* Pick up a struct timespec definition if it is available */ #include <time.h> #if defined(__struct_timespec_defined) #define _TIMESPEC_DEFINED #endif #if defined(SIM_ASYNCH_IO) || defined(USE_READER_THREAD) #include <pthread.h> #endif #if defined (__APPLE__) #define HAVE_STRUCT_TIMESPEC 1 /* OSX defined the structure but doesn't tell us */ #endif /* on HP-UX, CLOCK_REALTIME is enum, not preprocessor define */ #if !defined(CLOCK_REALTIME) && !defined(__hpux) #define CLOCK_REALTIME 1 #define NEED_CLOCK_GETTIME 1 #if defined(_MSC_VER) /* Visual Studio/Visual C++ */ #if _MSC_VER >= 1900 /* Visual Studio Community (2015) */ #define HAVE_STRUCT_TIMESPEC 1 #define _TIMESPEC_DEFINED 1 #endif /* _MSC_VER >= 1900 */ #endif /* defined(_MSC_VER) */ #if !defined(HAVE_STRUCT_TIMESPEC) #define HAVE_STRUCT_TIMESPEC 1 #if !defined(_TIMESPEC_DEFINED) #define _TIMESPEC_DEFINED struct timespec { time_t tv_sec; long tv_nsec; }; #endif /* !defined(_TIMESPEC_DEFINED) */ #endif /* !defined(HAVE_STRUCT_TIMESPEC) */ int clock_gettime(int clock_id, struct timespec *tp); #endif #define SIM_NTIMERS 8 /* # timers */ #define SIM_TMAX 500 /* max timer makeup */ #define SIM_INITIAL_IPS 500000 /* uncalibrated assumption */ /* about instructions per second */ #define SIM_IDLE_CAL 10 /* ms to calibrate */ #define SIM_IDLE_STMIN 2 /* min sec for stability */ #define SIM_IDLE_STDFLT 20 /* dft sec for stability */ #define SIM_IDLE_STMAX 600 /* max sec for stability */ #define SIM_THROT_WINIT 1000 /* cycles to skip */ #define SIM_THROT_WST 10000 /* initial wait */ #define SIM_THROT_WMUL 4 /* multiplier */ #define SIM_THROT_WMIN 50 /* min wait */ #define SIM_THROT_DRIFT_PCT 5 /* drift percentage for recalibrate */ #define SIM_THROT_MSMIN 10 /* min for measurement */ #define SIM_THROT_NONE 0 /* throttle parameters */ #define SIM_THROT_MCYC 1 /* MegaCycles Per Sec */ #define SIM_THROT_KCYC 2 /* KiloCycles Per Sec */ #define SIM_THROT_PCT 3 /* Max Percent of host CPU */ #define SIM_THROT_SPC 4 /* Specific periodic Delay */ #define SIM_THROT_STATE_INIT 0 /* Starting */ #define SIM_THROT_STATE_TIME 1 /* Checking Time */ #define SIM_THROT_STATE_THROTTLE 2 /* Throttling */ #define TIMER_DBG_IDLE 0x001 /* Debug Flag for Idle Debugging */ #define TIMER_DBG_QUEUE 0x002 /* Debug Flag for Asynch Queue Debugging */ #define TIMER_DBG_MUX 0x004 /* Debug Flag for Asynch Queue Debugging */ t_bool sim_timer_init (void); void sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub); double sim_timenow_double (void); int32 sim_rtcn_init (int32 time, int32 tmr); int32 sim_rtcn_init_unit (UNIT *uptr, int32 time, int32 tmr); void sim_rtcn_get_time (struct timespec *now, int tmr); t_stat sim_rtcn_tick_ack (uint32 time, int32 tmr); void sim_rtcn_init_all (void); int32 sim_rtcn_calb (int32 ticksper, int32 tmr); int32 sim_rtc_init (int32 time); int32 sim_rtc_calb (int32 ticksper); t_stat sim_set_timers (int32 arg, CONST char *cptr); t_stat sim_show_timers (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc); t_stat sim_show_clock_queues (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); t_bool sim_idle (uint32 tmr, int sin_cyc); t_stat sim_set_throt (int32 arg, CONST char *cptr); t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, CONST char *cptr); t_stat sim_set_idle (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat sim_clr_idle (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, CONST void *desc); void sim_throt_sched (void); void sim_throt_cancel (void); uint32 sim_os_msec (void); void sim_os_sleep (unsigned int sec); uint32 sim_os_ms_sleep (unsigned int msec); uint32 sim_os_ms_sleep_init (void); void sim_start_timer_services (void); void sim_stop_timer_services (void); t_stat sim_timer_change_asynch (void); t_stat sim_timer_activate (UNIT *uptr, int32 interval); t_stat sim_timer_activate_after (UNIT *uptr, double usec_delay); int32 _sim_timer_activate_time (UNIT *uptr); double sim_timer_activate_time_usecs (UNIT *uptr); t_bool sim_timer_is_active (UNIT *uptr); t_bool sim_timer_cancel (UNIT *uptr); t_stat sim_register_clock_unit (UNIT *uptr); t_stat sim_register_clock_unit_tmr (UNIT *uptr, int32 tmr); t_stat sim_clock_coschedule (UNIT *uptr, int32 interval); t_stat sim_clock_coschedule_abs (UNIT *uptr, int32 interval); t_stat sim_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 ticks); t_stat sim_clock_coschedule_tmr_abs (UNIT *uptr, int32 tmr, int32 ticks); double sim_timer_inst_per_sec (void); int32 sim_rtcn_tick_size (int32 tmr); int32 sim_rtcn_calibrated_tmr (void); t_bool sim_timer_idle_capable (uint32 *host_ms_sleep_1, uint32 *host_tick_ms); #define PRIORITY_BELOW_NORMAL -1 #define PRIORITY_NORMAL 0 #define PRIORITY_ABOVE_NORMAL 1 t_stat sim_os_set_thread_priority (int below_normal_above); uint32 sim_get_rom_delay_factor (void); void sim_set_rom_delay_factor (uint32 delay); int32 sim_rom_read_with_delay (int32 val); extern t_bool sim_idle_enab; /* idle enabled flag */ extern volatile t_bool sim_idle_wait; /* idle waiting flag */ extern t_bool sim_asynch_timer; extern DEVICE sim_timer_dev; extern UNIT * volatile sim_clock_cosched_queue[SIM_NTIMERS+1]; extern const t_bool rtc_avail; #ifdef __cplusplus } #endif #endif |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004 4005 4006 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316 4317 4318 4319 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 4335 4336 4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347 4348 4349 4350 4351 4352 4353 4354 4355 4356 4357 4358 4359 4360 4361 4362 4363 4364 4365 4366 4367 4368 4369 4370 4371 4372 4373 4374 4375 4376 4377 4378 4379 4380 4381 4382 4383 4384 4385 4386 4387 4388 4389 4390 4391 4392 4393 4394 4395 4396 4397 4398 4399 4400 4401 4402 4403 4404 4405 4406 4407 4408 4409 4410 4411 4412 4413 4414 4415 4416 4417 4418 4419 4420 4421 4422 4423 4424 4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454 4455 4456 4457 4458 4459 4460 4461 4462 4463 4464 4465 4466 4467 4468 4469 4470 4471 4472 4473 4474 4475 4476 4477 4478 4479 4480 4481 4482 4483 4484 4485 4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615 4616 4617 4618 4619 4620 4621 4622 4623 4624 4625 4626 4627 4628 4629 4630 4631 4632 4633 4634 4635 4636 4637 4638 4639 4640 4641 4642 4643 4644 4645 4646 4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663 4664 4665 4666 4667 4668 4669 4670 4671 4672 4673 4674 4675 4676 4677 4678 4679 4680 4681 4682 4683 4684 4685 4686 4687 4688 4689 4690 4691 4692 4693 4694 4695 4696 4697 4698 4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 4734 4735 4736 4737 4738 4739 4740 4741 4742 4743 4744 4745 4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 4770 4771 4772 4773 4774 4775 4776 4777 4778 4779 4780 4781 4782 4783 4784 4785 4786 4787 4788 4789 4790 4791 4792 4793 4794 4795 4796 4797 4798 4799 4800 4801 4802 4803 4804 4805 4806 4807 4808 4809 4810 4811 4812 4813 4814 4815 4816 4817 4818 4819 4820 4821 4822 4823 4824 4825 4826 4827 4828 4829 4830 4831 4832 4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845 4846 4847 4848 4849 4850 4851 4852 4853 4854 4855 4856 4857 4858 4859 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 4889 4890 4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909 4910 4911 4912 4913 4914 4915 4916 4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 4931 4932 4933 4934 4935 4936 4937 4938 4939 4940 4941 4942 4943 4944 4945 4946 4947 4948 4949 4950 4951 4952 4953 4954 4955 4956 4957 4958 4959 4960 | /* sim_tmxr.c: Telnet terminal multiplexer library Copyright (c) 2001-2011, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. Based on the original DZ11 simulator by Thord Nilson, as updated by Arthur Krewat. 12-Oct-12 MP Revised serial port support to not require changes to any code in TMXR library using code. Added support for per line listener ports and outgoing tcp connections. 02-Jun-11 MP Fixed telnet option negotiation loop with some clients Added Option Negotiation and Debugging Support 17-Jan-11 MP Added Buffered line capabilities 16-Jan-11 MP Made option negotiation more reliable 20-Nov-08 RMS Added three new standardized SHOW routines 05-Nov-08 JDB Moved logging call after connection check in tmxr_putc_ln 03-Nov-08 JDB Added TMXR null check to tmxr_find_ldsc 07-Oct-08 JDB Added initial serial port support 30-Sep-08 JDB Reverted tmxr_find_ldsc to original implementation 27-May-08 JDB Added line connection order to tmxr_poll_conn, added tmxr_set_lnorder and tmxr_show_lnorder 14-May-08 JDB Print device and line to which connection was made 11-Apr-07 JDB Worked around Telnet negotiation problem with QCTerm 16-Aug-05 RMS Fixed C++ declaration and cast problems 29-Jun-05 RMS Extended tmxr_dscln to support unit array devices Fixed bug in SET LOG/NOLOG 04-Jan-04 RMS Changed TMXR ldsc to be pointer to linedesc array Added tmxr_linemsg, circular output pointers, logging (from Mark Pizzolato) 29-Dec-03 RMS Added output stall support 01-Nov-03 RMS Cleaned up attach routine 09-Mar-03 RMS Fixed bug in SHOW CONN 22-Dec-02 RMS Fixed bugs in IAC+IAC receive and transmit sequences Added support for received break (all from by Mark Pizzolato) Fixed bug in attach 31-Oct-02 RMS Fixed bug in 8b (binary) support 22-Aug-02 RMS Added tmxr_open_master, tmxr_close_master 30-Dec-01 RMS Added tmxr_fstats, tmxr_dscln, renamed tmxr_fstatus 03-Dec-01 RMS Changed tmxr_fconns for extended SET/SHOW 20-Oct-01 RMS Fixed bugs in read logic (found by Thord Nilson). Added tmxr_rqln, tmxr_tqln This library includes: tmxr_poll_conn - poll for connection tmxr_reset_ln - reset line (drops Telnet/tcp and serial connections) tmxr_detach_ln - reset line and close per line listener and outgoing destination tmxr_getc_ln - get character for line tmxr_get_packet_ln - get packet from line tmxr_get_packet_ln_ex - get packet from line with separater byte tmxr_poll_rx - poll receive tmxr_putc_ln - put character for line tmxr_put_packet_ln - put packet on line tmxr_put_packet_ln_ex - put packet on line with separator byte tmxr_poll_tx - poll transmit tmxr_send_buffered_data - transmit buffered data tmxr_set_modem_control_passthru - enable modem control on a multiplexer tmxr_clear_modem_control_passthru - disable modem control on a multiplexer tmxr_set_get_modem_bits - set and/or get a line modem bits tmxr_set_line_loopback - enable or disable loopback mode on a line tmxr_get_line_loopback - returns the current loopback status of a line tmxr_set_line_halfduplex - enable or disable halfduplex mode on a line tmxr_get_line_halfduplex - returns the current halfduplex status of a line tmxr_set_config_line - set port speed, character size, parity and stop bits tmxr_open_master - open master connection tmxr_close_master - close master connection tmxr_attach - attach terminal multiplexor to listening port tmxr_detach - detach terminal multiplexor to listening port tmxr_attach_help - help routine for attaching multiplexer devices tmxr_set_line_unit - set the unit which polls for input for a given line tmxr_ex - (null) examine tmxr_dep - (null) deposit tmxr_msg - send message to socket tmxr_linemsg - send message to line tmxr_linemsgf - send formatted message to line tmxr_fconns - output connection status tmxr_fstats - output connection statistics tmxr_set_log - enable logging for line tmxr_set_nolog - disable logging for line tmxr_show_log - show logging status for line tmxr_dscln - disconnect line (SET routine) tmxr_rqln - number of available characters for line tmxr_tqln - number of buffered characters for line tmxr_tpqln - number of buffered packet characters for line tmxr_tpbusyln - transmit packet busy status for line tmxr_set_lnorder - set line connection order tmxr_show_lnorder - show line connection order tmxr_show_summ - show connection summary tmxr_show_cstat - show line connections or status tmxr_show_lines - show number of lines tmxr_show_open_devices - show info about all open tmxr devices All routines are OS-independent. This library supports the simulation of multiple-line terminal multiplexers. It may also be used to create single-line "multiplexers" to provide additional terminals beyond the simulation console. It may also be used to create single-line or multi-line simulated synchronous (BiSync) devices. Multiplexer lines may be connected to terminal emulators supporting the Telnet protocol via sockets, or to hardware terminals via host serial ports. Concurrent Telnet and serial connections may be mixed on a given multiplexer. When connecting via sockets, the simulated multiplexer is attached to a listening port on the host system: sim> attach MUX 23 Listening on port 23 Once attached, the listening port must be polled for incoming connections. When a connection attempt is received, it will be associated with the next multiplexer line in the user-specified line order, or with the next line in sequence if no order has been specified. Individual lines may be connected to serial ports or remote systems via TCP (telnet or not as desired), OR they may have separate listening TCP ports. Logging of Multiplexer Line output: The traffic going out multiplexer lines can be logged to files. A single line multiplexer can log it's traffic with the following command: sim> atta MUX 23,Log=LogFileName sim> atta MUX Connect=ser0,Log=LogFileName Specifying a Log value for a multi-line multiplexer is specifying a template filename. The actual file name used for each line will be the indicated filename with _n appended (n being the line number). Buffered Multiplexer Line: A Multiplexer Line Buffering has been implemented. A Buffered Line will have a copy of the last 'buffer size' bytes of output retained in a line specific buffer. The contents of this buffer will be transmitted out any new connection on that line when a new telnet session is established. This capability is most useful for the Console Telnet session. When a Console Telnet session is Buffered, a simulator will start (via BOOT CPU or whatever is appropriate for a particular simulator) without needing to have an active telnet connection. When a Telnet connection comes along for the telnet port, the contents of the saved buffer (which wraps on overflow) are presented on the telnet session as output before session traffic. This allows the connecting telnet client to see what happened before he connected since the likely reason he might be connecting to the console of a background simulator is to troubleshoot unusual behavior, the details of which may have already been sent to the console. Serial Port support: Serial ports may be specified as an operating system specific device names or using simh generic serial names. simh generic names are of the form serN, where N is from 0 thru one less than the maximum number of serial ports on the local system. The mapping of simh generic port names to OS specific names can be displayed using the following command: sim> show serial Serial devices: ser0 COM1 (\Device\Serial0) ser1 COM3 (Winachcf0) sim> attach MUX Line=2,Connect=ser0 or equivalently sim> attach MUX Line=2,Connect=COM1 An optional configuration string may be present after the port name. If present, it must be separated from the port name with a semicolon and has this form: <rate>-<charsize><parity><stopbits> where: rate = communication rate in bits per second charsize = character size in bits (5-8, including optional parity) parity = parity designator (N/E/O/M/S for no/even/odd/mark/space parity) stopbits = number of stop bits (1, 1.5, or 2) As an example: 9600-8n1 The supported rates, sizes, and parity options are host-specific. If a configuration string is not supplied, then the default of 9600-8N1 is used. An attachment to a serial port with the '-V' switch will cause a connection message to be output to the connected serial port. This will help to confirm the correct port has been connected and that the port settings are reasonable for the connected device. This would be done as: sim> attach -V MUX Connect=SerN Line specific tcp listening ports are supported. These are configured using commands of the form: sim> attach MUX Line=2,port{;notelnet} Direct computer to computer connections (Virutal Null Modem cables) may be established using the telnet protocol or via raw tcp sockets. sim> attach MUX Line=2,Connect=host:port{;notelnet} Computer to computer virtual connections can be one way (as illustrated above) or symmetric. A symmetric connection is configured by combining a one way connection with a tcp listening port on the same line: sim> attach MUX Line=2,Connect=host:port,listenport When symmetric virtual connections are configured, incoming connections on the specified listening port are checked to assure that they actually come from the specified connection destination host system. The command syntax for a single line device (MX) is: sim> attach MX port{;notelnet} sim> attach MX Connect=serN{;config} sim> attach MX Connect=COM9{;config} sim> attach MX Connect=host:port{;notelnet} The command syntax for ANY multi-line device is: sim> attach MX port{;notelnet} ; Defines the master listening port for the mux and optionally allows non-telnet (i.e. raw socket) operation for all lines. sim> attach MX Line=n,port{;notelnet} ; Defines a line specific listen port for a particular line. Each line can have a separate listen port and the mux can have its own as well. Optionally disable telnet wire protocol (i.e. raw socket) sim> attach MX Line=n,Connect=serN{;config} ; Connects line n to simh generic serial port N (port list visible with the sim> SHOW SERIAL command), the optional ";config" data specifies the speed, parity and stop bits for the connection ; DTR (and RTS) will be raised at attach time and will drop at detach/disconnect time sim> attach MX Line=n,Connect=host:port{;notelnet} ; Causes a connection to be established to the designated host:port. The actual connection will happen in a non-blocking fashion and will be completed and/or re-established by the normal tmxr_poll_conn activities All connections configured for any multiplexer device are unconfigured by: sim> detach MX ; detaches ALL connections/ports/sessions on the MUX. Console serial connections are achieved by: sim> set console serial=serN{;config} or sim> set console serial=COM2{;config} A line specific listening port (12366) can be specified by the following: sim> attach MUX Line=2,12366 A line specific remote telnet (or raw tcp) destination can be specified by the following: sim> attach MUX Line=2,Connect=remotehost:port If a connection to a remotehost:port wants a raw binary data channel (instead of a telnet session) the following would be used: sim> attach MUX Line=2,Connect=remotehost:port;notelnet A single line multiplexor can indicate any of the above line options without specifying a line number: sim> attach MUX Connect=ser0;9600-8N1 sim> attach MUX 12366 sim> attach MUX Connect=remotehost:port sim> attach MUX Connect=remotehost:port;notelnet A multiplexor can disconnect all (telnet, serial and outgoing) previous attachments with: sim> detach MUX A device emulation may choose to implement a command interface to disconnect specific individual lines. This would usually be done via a Unit Modifier table entry (MTAB) which dispatches the command "SET dev DISCONNECT[=line]" to tmxr_dscln. This will cause a telnet connection to be closed, but a serial port will normally have DTR dropped for 500ms and raised again (thus hanging up a modem on that serial port). sim> set MUX disconnect=2 A line which is connected to a serial port can be manually closed by adding the -C switch to a disconnect command. sim> set -C MUX disconnect=2 Full Modem Control serial port support. This library supports devices which wish to emulate full modem control/signalling for serial ports. Any device emulation which wishes to support this functionality for attached serial ports must call "tmxr_set_modem_control_passthru" before any call to tmxr_attach. This disables automatic DTR (&RTS) manipulation by this library. Responsibility for manipulating DTR falls on the simulated operating system. Calling tmxr_set_modem_control_passthru would usually be in a device reset routine. It may also be called by a device attach routine based on user specified options. Once support for full modem control has been declared by a device emulation for a particular TMXR device, this library will make no direct effort to manipulate modem bits while connected to serial ports. The "tmxr_set_get_modem_bits" API exists to allow the device emulation layer to query and control modem signals. The "tmxr_set_config_line" API exists to allow the device emulation layer to change port settings (baud rate, parity and stop bits). A modem_control enabled line merely passes the VM's port status bits, data and settings through to and from the serial port. The "tmxr_set_get_modem_bits" and "tmxr_set_config_line" APIs will ONLY work on a modem control enabled TMXR device. */ #define NOT_MUX_USING_CODE /* sim_tmxr library define */ #include "sim_defs.h" #include "sim_serial.h" #include "sim_sock.h" #include "sim_timer.h" #include "sim_tmxr.h" #include "scp.h" #include <ctype.h> #include <math.h> /* Telnet protocol constants - negatives are for init'ing signed char data */ /* Commands */ #define TN_IAC 0xFFu /* -1 */ /* protocol delim */ #define TN_DONT 0xFEu /* -2 */ /* dont */ #define TN_DO 0xFDu /* -3 */ /* do */ #define TN_WONT 0xFCu /* -4 */ /* wont */ #define TN_WILL 0xFBu /* -5 */ /* will */ #define TN_SB 0xFAu /* -6 */ /* sub-option negotiation */ #define TN_GA 0xF9u /* -7 */ /* go ahead */ #define TN_EL 0xF8u /* -8 */ /* erase line */ #define TN_EC 0xF7u /* -9 */ /* erase character */ #define TN_AYT 0xF6u /* -10 */ /* are you there */ #define TN_AO 0xF5u /* -11 */ /* abort output */ #define TN_IP 0xF4u /* -12 */ /* interrupt process */ #define TN_BRK 0xF3u /* -13 */ /* break */ #define TN_DATAMK 0xF2u /* -14 */ /* data mark */ #define TN_NOP 0xF1u /* -15 */ /* no operation */ #define TN_SE 0xF0u /* -16 */ /* end sub-option negot */ /* Options */ #define TN_BIN 0 /* bin */ #define TN_ECHO 1 /* echo */ #define TN_SGA 3 /* sga */ #define TN_STATUS 5 /* option status query */ #define TN_TIMING 6 /* Timing Mark */ #define TN_NAOCRD 10 /* Output Carriage-Return Disposition */ #define TN_NAOHTS 11 /* Output Horizontal Tab Stops */ #define TN_NAOHTD 12 /* Output Horizontal Tab Stop Disposition */ #define TN_NAOFFD 13 /* Output Forfeed Disposition */ #define TN_NAOVTS 14 /* Output Vertical Tab Stop */ #define TN_NAOVTD 15 /* Output Vertical Tab Stop Disposition */ #define TN_NAOLFD 16 /* Output Linefeed Disposition */ #define TN_EXTEND 17 /* Extended Ascii */ #define TN_LOGOUT 18 /* Logout */ #define TN_BM 19 /* Byte Macro */ #define TN_DET 20 /* Data Entry Terminal */ #define TN_SENDLO 23 /* Send Location */ #define TN_TERMTY 24 /* Terminal Type */ #define TN_ENDREC 25 /* Terminal Type */ #define TN_TUID 26 /* TACACS User Identification */ #define TN_OUTMRK 27 /* Output Marking */ #define TN_TTYLOC 28 /* Terminal Location Number */ #define TN_3270 29 /* 3270 Regime */ #define TN_X3PAD 30 /* X.3 PAD */ #define TN_NAWS 31 /* Negotiate About Window Size */ #define TN_TERMSP 32 /* Terminal Speed */ #define TN_TOGFLO 33 /* Remote Flow Control */ #define TN_LINE 34 /* line mode */ #define TN_XDISPL 35 /* X Display Location */ #define TN_ENVIRO 36 /* Environment */ #define TN_AUTH 37 /* Authentication */ #define TN_ENCRYP 38 /* Data Encryption */ #define TN_NEWENV 39 /* New Environment */ #define TN_TN3270 40 /* TN3270 Enhancements */ #define TN_CHARST 42 /* CHARSET */ #define TN_COMPRT 44 /* Com Port Control */ #define TN_KERMIT 47 /* KERMIT */ #define TN_CR 015 /* carriage return */ #define TN_LF 012 /* line feed */ #define TN_NUL 000 /* null */ /* Telnet line states */ #define TNS_NORM 000 /* normal */ #define TNS_IAC 001 /* IAC seen */ #define TNS_WILL 002 /* WILL seen */ #define TNS_WONT 003 /* WONT seen */ #define TNS_SKIP 004 /* skip next cmd */ #define TNS_CRPAD 005 /* CR padding */ #define TNS_DO 006 /* DO request pending rejection */ /* Telnet Option Sent Flags */ #define TNOS_DONT 001 /* Don't has been sent */ #define TNOS_WONT 002 /* Won't has been sent */ static BITFIELD tmxr_modem_bits[] = { BIT(DTR), /* Data Terminal Ready */ BIT(RTS), /* Request To Send */ BIT(DCD), /* Data Carrier Detect */ BIT(RNG), /* Ring Indicator */ BIT(CTS), /* Clear To Send */ BIT(DSR), /* Data Set Ready */ ENDBITS }; static u_char mantra[] = { /* Telnet Option Negotiation Mantra */ TN_IAC, TN_WILL, TN_LINE, TN_IAC, TN_WILL, TN_SGA, TN_IAC, TN_WILL, TN_ECHO, TN_IAC, TN_WILL, TN_BIN, TN_IAC, TN_DO, TN_BIN }; #define TMXR_GUARD ((int32)(lp->serport ? 1 : sizeof(mantra)))/* buffer guard */ /* Local routines */ static void tmxr_add_to_open_list (TMXR* mux); /* Initialize the line state. Reset the line state to represent an idle line. Note that we do not clear all of the line structure members, so a connected line remains connected after this call. Because a line break is represented by a flag in the "receive break status" array, we must zero that array in order to clear any pending break indications. */ static void tmxr_init_line (TMLN *lp) { lp->tsta = 0; /* init telnet state */ lp->xmte = 1; /* enable transmit */ lp->dstb = 0; /* default bin mode */ lp->rxbpr = lp->rxbpi = lp->rxcnt = lp->rxpcnt = 0; /* init receive indexes */ if (!lp->txbfd || lp->notelnet) /* if not buffered telnet */ lp->txbpr = lp->txbpi = lp->txcnt = lp->txpcnt = 0; /* init transmit indexes */ lp->txdrp = 0; tmxr_set_get_modem_bits (lp, 0, 0, NULL); if ((!lp->mp->buffered) && (!lp->txbfd)) { lp->txbfd = 0; lp->txbsz = TMXR_MAXBUF; lp->txb = (char *)realloc (lp->txb, lp->txbsz); lp->rxbsz = TMXR_MAXBUF; lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz); lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz); } if (lp->loopback) { lp->lpbsz = lp->rxbsz; lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz); lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0; } if (lp->rxpb) { lp->rxpboffset = lp->rxpbsize = 0; free (lp->rxpb); lp->rxpb = NULL; } if (lp->txpb) { lp->txpbsize = lp->txppsize = lp->txppoffset = 0; free (lp->txpb); lp->txpb = NULL; } memset (lp->rbr, 0, lp->rxbsz); /* clear break status array */ return; } /* Report a connection to a line. If the indicated line (lp) is speaking the telnet wire protocol, a notification of the form: Connected to the <sim> simulator <dev> device, line <n> is sent to the newly connected line. If the device has only one line, the "line <n>" part is omitted. If the device has not been defined, the "<dev> device" part is omitted. */ static void tmxr_report_connection (TMXR *mp, TMLN *lp) { int32 unwritten, psave; char cmsg[80]; char dmsg[80] = ""; char lmsg[80] = ""; char msgbuf[256] = ""; if ((!lp->notelnet) || (sim_switches & SWMASK ('V'))) { sprintf (cmsg, "\n\r\nConnected to the %s simulator ", sim_name); if (mp->dptr) { /* device defined? */ sprintf (dmsg, "%s device", /* report device name */ sim_dname (mp->dptr)); if (mp->lines > 1) /* more than one line? */ sprintf (lmsg, ", line %d", (int)(lp-mp->ldsc));/* report the line number */ } sprintf (msgbuf, "%s%s%s\r\n\n", cmsg, dmsg, lmsg); } if (!mp->buffered) { lp->txbpi = 0; /* init buf pointers */ lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf)); lp->rxcnt = lp->txcnt = lp->txdrp = 0; /* init counters */ lp->rxpcnt = lp->txpcnt = 0; } else if (lp->txcnt > lp->txbsz) lp->txbpr = (lp->txbpi + 1) % lp->txbsz; else lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf)); psave = lp->txbpi; /* save insertion pointer */ lp->txbpi = lp->txbpr; /* insert connection message */ tmxr_linemsg (lp, msgbuf); /* beginning of buffer */ lp->txbpi = psave; /* restore insertion pointer */ unwritten = tmxr_send_buffered_data (lp); /* send the message */ if (unwritten == 0) /* buffer now empty? */ lp->xmte = 1; /* reenable transmission if paused */ lp->txcnt -= (int32)strlen (msgbuf); /* adjust statistics */ return; } /* Report a disconnection to a line. A notification of the form: Disconnected from the <sim> simulator is sent to the line about to be disconnected. We do not flush the buffer here, because the disconnect routines will do that just after calling us. */ static void tmxr_report_disconnection (TMLN *lp) { if (lp->notelnet) return; tmxr_linemsgf (lp, "\r\nDisconnected from the %s simulator\r\n\n", sim_name);/* report disconnection */ return; } static int32 loop_write_ex (TMLN *lp, char *buf, int32 length, t_bool prefix_datagram) { int32 written = 0; int32 loopfree = lp->lpbsz - lp->lpbcnt; if (lp->datagram && prefix_datagram) { if ((size_t)loopfree < (size_t)(length + sizeof(length))) return written; loop_write_ex (lp, (char *)&length, sizeof(length), FALSE); } while (length) { int32 chunksize; loopfree = lp->lpbsz - lp->lpbcnt; if (loopfree == 0) break; if (loopfree < length) length = loopfree; if (lp->lpbpi >= lp->lpbpr) chunksize = lp->lpbsz - lp->lpbpi; else chunksize = lp->lpbpr - lp->lpbpi; if (chunksize > length) chunksize = length; memcpy (&lp->lpb[lp->lpbpi], buf, chunksize); buf += chunksize; length -= chunksize; written += chunksize; lp->lpbpi = (lp->lpbpi + chunksize) % lp->lpbsz; } lp->lpbcnt += written; return written; } static int32 loop_write (TMLN *lp, char *buf, int32 length) { return loop_write_ex (lp, buf, length, TRUE); } static int32 loop_read_ex (TMLN *lp, char *buf, int32 bufsize) { int32 bytesread = 0; while (bufsize > 0) { int32 chunksize; int32 loopused = lp->lpbcnt; if (loopused < bufsize) bufsize = loopused; if (loopused == 0) break; if (lp->lpbpi > lp->lpbpr) chunksize = lp->lpbpi - lp->lpbpr; else chunksize = lp->lpbsz - lp->lpbpr; if (chunksize > bufsize) chunksize = bufsize; memcpy (buf, &lp->lpb[lp->lpbpr], chunksize); buf += chunksize; bufsize -= chunksize; bytesread += chunksize; lp->lpbpr = (lp->lpbpr + chunksize) % lp->lpbsz; } lp->lpbcnt -= bytesread; return bytesread; } static int32 loop_read (TMLN *lp, char *buf, int32 bufsize) { if (lp->datagram) { int32 pktsize; if (lp->lpbcnt < (int32)sizeof(pktsize)) return 0; if ((sizeof(pktsize) != loop_read_ex (lp, (char *)&pktsize, sizeof(pktsize))) || (pktsize > bufsize)) return -1; bufsize = pktsize; } return loop_read_ex (lp, buf, bufsize); } /* Read from a line. Up to "length" characters are read into the character buffer associated with line "lp". The actual number of characters read is returned. If no characters are available, 0 is returned. If an error occurred while reading, -1 is returned. If a line break was detected on serial input, the associated receive break status flag will be set. Line break indication for Telnet connections is embedded in the Telnet protocol and must be determined externally. */ static int32 tmxr_read (TMLN *lp, int32 length) { int32 i = lp->rxbpi; if (lp->loopback) return loop_read (lp, &(lp->rxb[i]), length); if (lp->serport) /* serial port connection? */ return sim_read_serial (lp->serport, &(lp->rxb[i]), length, &(lp->rbr[i])); else /* Telnet connection */ return sim_read_sock (lp->sock, &(lp->rxb[i]), length); } /* Write to a line. Up to "length" characters are written from the character buffer associated with "lp". The actual number of characters written is returned. If an error occurred while writing, -1 is returned. */ static int32 tmxr_write (TMLN *lp, int32 length) { int32 written; int32 i = lp->txbpr; if (lp->loopback) return loop_write (lp, &(lp->txb[i]), length); if (lp->serport) { /* serial port connection? */ if (sim_gtime () < lp->txnexttime) return 0; written = sim_write_serial (lp->serport, &(lp->txb[i]), length); if (written > 0) lp->txnexttime = floor (sim_gtime () + (lp->txdelta * sim_timer_inst_per_sec ())); return written; } else { /* Telnet connection */ written = sim_write_sock (lp->sock, &(lp->txb[i]), length); if (written == SOCKET_ERROR) /* did an error occur? */ if (lp->datagram) return written; /* ignore errors on datagram sockets */ else return -1; /* return error indication */ else return written; } } /* Remove a character from the read buffer. The character at position "p" in the read buffer associated with line "lp" is removed by moving all of the following received characters down one position. The receive break status array is adjusted accordingly. */ static void tmxr_rmvrc (TMLN *lp, int32 p) { for ( ; p < lp->rxbpi; p++) { /* work from "p" through end of buffer */ lp->rxb[p] = lp->rxb[p + 1]; /* slide following character down */ lp->rbr[p] = lp->rbr[p + 1]; /* adjust break status too */ } lp->rbr[p] = 0; /* clear potential break from vacated slot */ lp->rxbpi = lp->rxbpi - 1; /* drop buffer insert index */ return; } /* Find a line descriptor indicated by unit or number. If "uptr" is NULL, then the line descriptor is determined by the line number passed in "val". If "uptr" is not NULL, then it must point to a unit associated with a line, and the line descriptor is determined by the unit number, which is derived by the position of the unit in the device's unit array. Note: This routine may be called with a UNIT that does not belong to the device indicated in the TMXR structure. That is, the multiplexer lines may belong to a device other than the one attached to the socket (the HP 2100 MUX device is one example). Therefore, we must look up the device from the unit at each call, rather than depending on the DEVICE pointer stored in the TMXR. */ static TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, const TMXR *mp) { if (mp == NULL) /* invalid multiplexer descriptor? */ return NULL; /* programming error! */ if (uptr) { /* called from SET? */ DEVICE *dptr = find_dev_from_unit (uptr); /* find device */ if (dptr == NULL) /* what?? */ return NULL; val = (int32) (uptr - dptr->units); /* implicit line # */ } if ((val < 0) || (val >= mp->lines)) /* invalid line? */ return NULL; return mp->ldsc + val; /* line descriptor */ } /* Get a line descriptor indicated by a string or unit. A pointer to the line descriptor associated with multiplexer "mp" and unit "uptr" or specified by string "cptr" is returned. If "uptr" is non-null, then the unit number within its associated device implies the line number. If "uptr" is null, then the string "cptr" is parsed for a decimal line number. If the line number is missing, malformed, or outside of the range of line numbers associated with "mp", then NULL is returned with status set to SCPE_ARG. Implementation note: 1. A return status of SCPE_IERR implies a programming error (passing an invalid pointer or an invalid unit). */ static TMLN *tmxr_get_ldsc (UNIT *uptr, const char *cptr, TMXR *mp, t_stat *status) { t_value ln; TMLN *lp = NULL; t_stat code = SCPE_OK; if (mp == NULL) /* missing mux descriptor? */ code = SCPE_IERR; /* programming error! */ else if (uptr) { /* implied line form? */ lp = tmxr_find_ldsc (uptr, mp->lines, mp); /* determine line from unit */ if (lp == NULL) /* invalid line number? */ code = SCPE_IERR; /* programming error! */ } else if (cptr == NULL) /* named line form, parameter supplied? */ code = SCPE_MISVAL; /* no, so report missing */ else { ln = get_uint (cptr, 10, mp->lines - 1, &code); /* get line number */ if (code == SCPE_OK) /* line number OK? */ lp = mp->ldsc + (int32) ln; /* use as index to determine line */ } if (status) /* return value pointer supplied? */ *status = code; /* store return status value */ return lp; /* return pointer to line descriptor */ } /* Generate the Attach string which will fully configure the multiplexer Inputs: old = pointer to the original configuration string which will be replaced *mp = pointer to multiplexer Output: a complete attach string for the current state of the multiplexer */ static char *growstring(char **string, size_t growth) { *string = (char *)realloc (*string, 1 + (*string ? strlen (*string) : 0) + growth); return *string + strlen(*string); } static char *tmxr_mux_attach_string(char *old, TMXR *mp) { char* tptr = NULL; int32 i; TMLN *lp; free (old); tptr = (char *) calloc (1, 1); if (tptr == NULL) /* no more mem? */ return tptr; if (mp->port) /* copy port */ sprintf (growstring(&tptr, 13 + strlen (mp->port)), "%s%s", mp->port, mp->notelnet ? ";notelnet" : ""); if (mp->logfiletmpl[0]) /* logfile info */ sprintf (growstring(&tptr, 7 + strlen (mp->logfiletmpl)), ",Log=%s", mp->logfiletmpl); while ((*tptr == ',') || (*tptr == ' ')) memcpy (tptr, tptr+1, strlen(tptr+1)+1); for (i=0; i<mp->lines; ++i) { char *lptr; lp = mp->ldsc + i; lptr = tmxr_line_attach_string(lp); if (lptr) { sprintf (growstring(&tptr, 10+strlen(lptr)), "%s%s", *tptr ? "," : "", lptr); free (lptr); } } if (mp->lines == 1) while ((*tptr == ',') || (*tptr == ' ')) memcpy (tptr, tptr+1, strlen(tptr+1)+1); if (*tptr == '\0') { free (tptr); tptr = NULL; } return tptr; } /* Global routines */ /* Return the Line specific attach setup currently configured for a given line Inputs: *lp = pointer to terminal line descriptor Outputs: a string which can be used to reconfigure the line, NULL if the line isn't configured Note: The returned string is dynamically allocated memory and must be freed when it is no longer needed by calling free */ char *tmxr_line_attach_string(TMLN *lp) { char* tptr = NULL; tptr = (char *) calloc (1, 1); if (tptr == NULL) /* no more mem? */ return tptr; if (lp->destination || lp->port || lp->txlogname) { if ((lp->mp->lines > 1) || (lp->port)) sprintf (growstring(&tptr, 32), "Line=%d", (int)(lp-lp->mp->ldsc)); if (lp->modem_control != lp->mp->modem_control) sprintf (growstring(&tptr, 32), ",%s", lp->modem_control ? "Modem" : "NoModem"); if (lp->txbfd && (lp->txbsz != lp->mp->buffered)) sprintf (growstring(&tptr, 32), ",Buffered=%d", lp->txbsz); if (!lp->txbfd && (lp->mp->buffered > 0)) sprintf (growstring(&tptr, 32), ",UnBuffered"); if (lp->mp->datagram != lp->datagram) sprintf (growstring(&tptr, 8), ",%s", lp->datagram ? "UDP" : "TCP"); if (lp->mp->packet != lp->packet) sprintf (growstring(&tptr, 8), ",Packet"); if (lp->port) sprintf (growstring(&tptr, 12 + strlen (lp->port)), ",%s%s", lp->port, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : ""); if (lp->destination) { if (lp->serport) { char portname[CBUFSIZE]; get_glyph_nc (lp->destination, portname, ';'); sprintf (growstring(&tptr, 25 + strlen (lp->destination)), ",Connect=%s%s%s", portname, strcmp("9600-8N1", lp->serconfig) ? ";" : "", strcmp("9600-8N1", lp->serconfig) ? lp->serconfig : ""); } else sprintf (growstring(&tptr, 25 + strlen (lp->destination)), ",Connect=%s%s", lp->destination, ((lp->mp->notelnet != lp->notelnet) && (!lp->datagram)) ? (lp->notelnet ? ";notelnet" : ";telnet") : ""); } if (lp->txlogname) sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname); if (lp->loopback) sprintf (growstring(&tptr, 12 ), ",Loopback"); } if (*tptr == '\0') { free (tptr); tptr = NULL; } return tptr; } /* Set the connection polling interval */ t_stat tmxr_connection_poll_interval (TMXR *mp, uint32 seconds) { if (0 == seconds) return SCPE_ARG; mp->poll_interval = seconds; return SCPE_OK; } /* Poll for new connection Called from unit service routine to test for new connection Inputs: *mp = pointer to terminal multiplexer descriptor Outputs: line number activated, -1 if none If a connection order is defined for the descriptor, and the first value is not -1 (indicating default order), then the order array is used to find an open line. Otherwise, a search is made of all lines in numerical sequence. */ int32 tmxr_poll_conn (TMXR *mp) { SOCKET newsock; TMLN *lp; int32 *op; int32 i, j; char *address; char msg[512]; uint32 poll_time = sim_os_msec (); if (mp->last_poll_time == 0) { /* first poll initializations */ UNIT *uptr = mp->uptr; if (!uptr) /* Attached ? */ return -1; /* No connections are possinle! */ if (mp->poll_interval == 0) /* Assure reasonable polling interval */ mp->poll_interval = TMXR_DEFAULT_CONNECT_POLL_INTERVAL; if (!(uptr->dynflags & TMUF_NOASYNCH)) { /* if asynch not disabled */ uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */ sim_cancel (uptr); } for (i=0; i < mp->lines; i++) { uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr; if (!(mp->uptr->dynflags & TMUF_NOASYNCH)) { /* if asynch not disabled */ uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */ sim_cancel (uptr); } } } if ((poll_time - mp->last_poll_time) < mp->poll_interval*1000) return -1; /* too soon to try */ srand((unsigned int)poll_time); tmxr_debug_trace (mp, "tmxr_poll_conn()"); mp->last_poll_time = poll_time; /* Check for a pending Telnet/tcp connection */ if (mp->master) { if (mp->ring_sock != INVALID_SOCKET) { /* Use currently 'ringing' socket if one is active */ newsock = mp->ring_sock; mp->ring_sock = INVALID_SOCKET; address = mp->ring_ipad; mp->ring_ipad = NULL; } else newsock = sim_accept_conn_ex (mp->master, &address, (mp->packet ? SIM_SOCK_OPT_NODELAY : 0));/* poll connect */ if (newsock != INVALID_SOCKET) { /* got a live one? */ sprintf (msg, "tmxr_poll_conn() - Connection from %s", address); tmxr_debug_connect (mp, msg); op = mp->lnorder; /* get line connection order list pointer */ i = mp->lines; /* play it safe in case lines == 0 */ ++mp->sessions; /* count the new session */ for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */ if (op && (*op >= 0) && (*op < mp->lines)) /* order list present and valid? */ i = *op++; /* get next line in list to try */ else /* no list or not used or range error */ i = j; /* get next sequential line */ lp = mp->ldsc + i; /* get pointer to line descriptor */ if ((lp->conn == FALSE) && /* is the line available? */ (lp->destination == NULL) && (lp->master == 0) && (lp->ser_connect_pending == FALSE) && (lp->modem_control ? ((lp->modembits & TMXR_MDM_DTR) != 0) : TRUE)) break; /* yes, so stop search */ } if (i >= mp->lines) { /* all busy? */ int32 ringable_count = 0; for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */ lp = mp->ldsc + j; /* get pointer to line descriptor */ if ((lp->conn == FALSE) && /* is the line available? */ (lp->destination == NULL) && (lp->master == 0) && (lp->ser_connect_pending == FALSE) && ((lp->modembits & TMXR_MDM_DTR) == 0)) { ++ringable_count; tmxr_set_get_modem_bits (lp, TMXR_MDM_RNG, 0, NULL); tmxr_debug_connect_line (lp, "tmxr_poll_conn() - Ringing line"); } } if (ringable_count > 0) { if (mp->ring_start_time == 0) { mp->ring_start_time = poll_time; mp->ring_sock = newsock; mp->ring_ipad = address; } else { if ((poll_time - mp->ring_start_time) < TMXR_MODEM_RING_TIME*1000) { mp->ring_sock = newsock; mp->ring_ipad = address; } else { /* Timeout waiting for DTR */ int ln; /* turn off pending ring signals */ for (ln = 0; ln < lp->mp->lines; ln++) { TMLN *tlp = lp->mp->ldsc + ln; if (((tlp->destination == NULL) && (tlp->master == 0)) && (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE)) tlp->modembits &= ~TMXR_MDM_RNG; } mp->ring_start_time = 0; tmxr_msg (newsock, "No answer on any connection\r\n"); tmxr_debug_connect (mp, "tmxr_poll_conn() - No Answer - All connections busy"); sim_close_sock (newsock); free (address); } } } else { tmxr_msg (newsock, "All connections busy\r\n"); tmxr_debug_connect (mp, "tmxr_poll_conn() - All connections busy"); sim_close_sock (newsock); free (address); } } else { lp = mp->ldsc + i; /* get line desc */ lp->conn = TRUE; /* record connection */ lp->sock = newsock; /* save socket */ lp->ipad = address; /* ip address */ tmxr_init_line (lp); /* init line */ lp->notelnet = mp->notelnet; /* apply mux default telnet setting */ if (!lp->notelnet) { sim_write_sock (newsock, (char *)mantra, sizeof(mantra)); tmxr_debug (TMXR_DBG_XMT, lp, "Sending", (char *)mantra, sizeof(mantra)); lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256); memset (lp->telnet_sent_opts, 0, 256); } tmxr_report_connection (mp, lp); lp->cnms = sim_os_msec (); /* time of connection */ return i; } } /* end if newsock */ } /* Look for per line listeners or outbound connecting sockets */ for (i = 0; i < mp->lines; i++) { /* check each line in sequence */ int j, r = rand(); lp = mp->ldsc + i; /* get pointer to line descriptor */ /* Check for pending serial port connection notification */ if (lp->ser_connect_pending) { lp->ser_connect_pending = FALSE; lp->conn = TRUE; return i; } /* Don't service network connections for loopbacked lines */ if (lp->loopback) continue; /* If two simulators are configured with symmetric virtual null modem cables pointing at each other, there may be a problem establishing a connection if both systems happen to be checking for the success of their connections in the exact same order. They can each observe success in their respective outgoing connections, which haven't actually been 'accept'ed on the peer end of the connection. We address this issue by checking for the success of an outgoing connection and the arrival of an incoming one in a random order. */ for (j=0; j<2; j++) switch ((j+r)&1) { case 0: if (lp->connecting) { /* connecting? */ char *sockname, *peername; switch (sim_check_conn(lp->connecting, FALSE)) { case 1: /* successful connection */ lp->conn = TRUE; /* record connection */ lp->sock = lp->connecting; /* it now looks normal */ lp->connecting = 0; lp->ipad = (char *)realloc (lp->ipad, 1+strlen (lp->destination)); strcpy (lp->ipad, lp->destination); lp->cnms = sim_os_msec (); sim_getnames_sock (lp->sock, &sockname, &peername); sprintf (msg, "tmxr_poll_conn() - Outgoing Line Connection to %s (%s->%s) established", lp->destination, sockname, peername); tmxr_debug_connect_line (lp, msg); free (sockname); free (peername); return i; case -1: /* failed connection */ sprintf (msg, "tmxr_poll_conn() - Outgoing Line Connection to %s failed", lp->destination); tmxr_debug_connect_line (lp, msg); tmxr_reset_ln (lp); /* retry */ break; } } break; case 1: if (lp->master) { /* Check for a pending Telnet/tcp connection */ while (INVALID_SOCKET != (newsock = sim_accept_conn_ex (lp->master, &address, (lp->packet ? SIM_SOCK_OPT_NODELAY : 0)))) {/* got a live one? */ char *sockname, *peername; sim_getnames_sock (newsock, &sockname, &peername); sprintf (msg, "tmxr_poll_conn() - Incoming Line Connection from %s (%s->%s)", address, peername, sockname); tmxr_debug_connect_line (lp, msg); free (sockname); free (peername); ++mp->sessions; /* count the new session */ if (lp->destination) { /* Virtual Null Modem Cable? */ char host[CBUFSIZE]; if (sim_parse_addr (lp->destination, host, sizeof(host), NULL, NULL, 0, NULL, address)) { tmxr_msg (newsock, "Rejecting connection from unexpected source\r\n"); sprintf (msg, "tmxr_poll_conn() - Rejecting line connection from: %s, Expected: %s", address, host); tmxr_debug_connect_line (lp, msg); sim_close_sock (newsock); free (address); continue; /* Try for another connection */ } if (lp->connecting) { sprintf (msg, "tmxr_poll_conn() - aborting outgoing line connection attempt to: %s", lp->destination); tmxr_debug_connect_line (lp, msg); sim_close_sock (lp->connecting); /* abort our as yet unconnnected socket */ lp->connecting = 0; } } if (lp->conn == FALSE) { /* is the line available? */ if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) { lp->conn = TRUE; /* record connection */ lp->sock = newsock; /* save socket */ lp->ipad = address; /* ip address */ tmxr_init_line (lp); /* init line */ if (!lp->notelnet) { sim_write_sock (newsock, (char *)mantra, sizeof(mantra)); tmxr_debug (TMXR_DBG_XMT, lp, "Sending", (char *)mantra, sizeof(mantra)); lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256); memset (lp->telnet_sent_opts, 0, 256); } tmxr_report_connection (mp, lp); lp->cnms = sim_os_msec (); /* time of connection */ return i; } else { tmxr_msg (newsock, "Line connection not available\r\n"); tmxr_debug_connect_line (lp, "tmxr_poll_conn() - Line connection not available"); sim_close_sock (newsock); free (address); } } else { tmxr_msg (newsock, "Line connection busy\r\n"); tmxr_debug_connect_line (lp, "tmxr_poll_conn() - Line connection busy"); sim_close_sock (newsock); free (address); } } } break; } /* Check for needed outgoing connection initiation */ if (lp->destination && (!lp->sock) && (!lp->connecting) && (!lp->serport) && (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR))) { sprintf (msg, "tmxr_poll_conn() - establishing outgoing connection to: %s", lp->destination); tmxr_debug_connect_line (lp, msg); lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (lp->mp->packet ? SIM_SOCK_OPT_NODELAY : 0)); } } return -1; /* no new connections made */ } /* Reset a line. The telnet/tcp or serial session associated with multiplexer descriptor "mp" and line descriptor "lp" is disconnected. An associated tcp socket is closed; a serial port is closed if the closeserial parameter is true, otherwise for non modem control serial lines DTR is dropped and raised again after 500ms to signal the attached serial device. */ static t_stat tmxr_reset_ln_ex (TMLN *lp, t_bool closeserial) { char msg[512]; tmxr_debug_trace_line (lp, "tmxr_reset_ln_ex()"); if (lp->txlog) fflush (lp->txlog); /* flush log */ tmxr_send_buffered_data (lp); /* send any buffered data */ sprintf (msg, "tmxr_reset_ln_ex(%s)", closeserial ? "TRUE" : "FALSE"); tmxr_debug_connect_line (lp, msg); if (lp->serport) { if (closeserial) { sim_close_serial (lp->serport); lp->serport = 0; lp->ser_connect_pending = FALSE; free (lp->destination); lp->destination = NULL; free (lp->serconfig); lp->serconfig = NULL; lp->cnms = 0; lp->xmte = 1; } else if (!lp->modem_control) { /* serial connection? */ sim_control_serial (lp->serport, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);/* drop DTR and RTS */ sim_os_ms_sleep (TMXR_DTR_DROP_TIME); sim_control_serial (lp->serport, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL);/* raise DTR and RTS */ } } else /* Telnet connection */ if (lp->sock) { sim_close_sock (lp->sock); /* close socket */ free (lp->telnet_sent_opts); lp->telnet_sent_opts = NULL; lp->sock = 0; lp->conn = FALSE; lp->cnms = 0; lp->xmte = 1; } free(lp->ipad); lp->ipad = NULL; if ((lp->destination) && (!lp->serport)) { if (lp->connecting) { sim_close_sock (lp->connecting); lp->connecting = 0; } if ((!lp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) { sprintf (msg, "tmxr_reset_ln_ex() - connecting to %s", lp->destination); tmxr_debug_connect_line (lp, msg); lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (lp->mp->packet ? SIM_SOCK_OPT_NODELAY : 0)); } } tmxr_init_line (lp); /* initialize line state */ return SCPE_OK; } t_stat tmxr_close_ln (TMLN *lp) { tmxr_debug_trace_line (lp, "tmxr_close_ln()"); tmxr_debug_connect_line (lp, "tmxr_close_ln()"); return tmxr_reset_ln_ex (lp, TRUE); } t_stat tmxr_reset_ln (TMLN *lp) { tmxr_debug_trace_line (lp, "tmxr_reset_ln()"); return tmxr_reset_ln_ex (lp, FALSE); } /* Enable modem control pass thru Inputs: none Output: none Implementation note: 1 Calling this API disables any actions on the part of this library to directly manipulate DTR (&RTS) on serial ports. 2 Calling this API enables the tmxr_set_get_modem_bits and tmxr_set_config_line APIs. */ static t_stat tmxr_clear_modem_control_passthru_state (TMXR *mp, t_bool state) { int i; if (mp->modem_control == state) return SCPE_OK; if (mp->master) return SCPE_ALATT; for (i=0; i<mp->lines; ++i) { TMLN *lp; lp = mp->ldsc + i; if ((lp->master) || (lp->sock) || (lp->connecting) || (lp->serport)) return SCPE_ALATT; } mp->modem_control = state; for (i=0; i<mp->lines; ++i) mp->ldsc[i].modem_control = state; return SCPE_OK; } t_stat tmxr_set_modem_control_passthru (TMXR *mp) { return tmxr_clear_modem_control_passthru_state (mp, TRUE); } /* Disable modem control pass thru Inputs: none Output: none Implementation note: 1 Calling this API enables this library's direct manipulation of DTR (&RTS) on serial ports. 2 Calling this API disables the tmxr_set_get_modem_bits and tmxr_set_config_line APIs. 3 This API will only change the state of the modem control processing of this library if there are no listening ports, serial ports or outgoing connecctions associated with the specified multiplexer */ t_stat tmxr_clear_modem_control_passthru (TMXR *mp) { return tmxr_clear_modem_control_passthru_state (mp, FALSE); } /* Manipulate the modem control bits of a specific line Inputs: *lp = pointer to terminal line descriptor bits_to_set TMXR_MDM_DTR and/or TMXR_MDM_RTS as desired bits_to_clear TMXR_MDM_DTR and/or TMXR_MDM_RTS as desired Output: incoming_bits if non NULL, returns the current stat of DCD, RNG, CTS and DSR along with the current state of DTR and RTS Implementation note: If a line is connected to a serial port, then these values affect and reflect the state of the serial port. If the line is connected to a network socket (or could be) then the network session state is set, cleared and/or returned. */ t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits) { int32 before_modem_bits, incoming_state; DEVICE *dptr; tmxr_debug_trace_line (lp, "tmxr_set_get_modem_bits()"); if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits */ (bits_to_clear & ~(TMXR_MDM_OUTGOING)) || (bits_to_set & bits_to_clear)) /* and can't set and clear the same bits */ return SCPE_ARG; before_modem_bits = lp->modembits; lp->modembits |= bits_to_set; lp->modembits &= ~(bits_to_clear | TMXR_MDM_INCOMING); if ((lp->sock) || (lp->serport) || (lp->loopback)) { if (lp->modembits & TMXR_MDM_DTR) { incoming_state = TMXR_MDM_DSR; if (lp->modembits & TMXR_MDM_RTS) incoming_state |= TMXR_MDM_CTS; if (lp->halfduplex) { if (incoming_state & TMXR_MDM_CTS) incoming_state |= TMXR_MDM_DCD; } else incoming_state |= TMXR_MDM_DCD; } else incoming_state = TMXR_MDM_DCD | TMXR_MDM_DSR | ((lp->modembits & TMXR_MDM_DTR) ? 0 : TMXR_MDM_RNG); } else { if (((before_modem_bits & TMXR_MDM_DTR) == 0) && /* Upward transition of DTR? */ ((lp->modembits & TMXR_MDM_DTR) != 0) && (lp->conn == FALSE) && /* Not connected */ (lp->modembits & TMXR_MDM_RNG)) { /* and Ring Signal Present */ if ((lp->destination == NULL) && (lp->master == 0) && (lp->mp && (lp->mp->ring_sock))) { int ln; lp->conn = TRUE; /* record connection */ lp->sock = lp->mp->ring_sock; /* save socket */ lp->mp->ring_sock = INVALID_SOCKET; lp->ipad = lp->mp->ring_ipad; /* ip address */ lp->mp->ring_ipad = NULL; lp->mp->ring_start_time = 0; tmxr_init_line (lp); /* init line */ lp->notelnet = lp->mp->notelnet; /* apply mux default telnet setting */ if (!lp->notelnet) { sim_write_sock (lp->sock, (char *)mantra, sizeof(mantra)); tmxr_debug (TMXR_DBG_XMT, lp, "Sending", (char *)mantra, sizeof(mantra)); lp->telnet_sent_opts = (uint8 *)realloc (lp->telnet_sent_opts, 256); memset (lp->telnet_sent_opts, 0, 256); } tmxr_report_connection (lp->mp, lp); lp->cnms = sim_os_msec (); /* time of connection */ lp->modembits &= ~TMXR_MDM_RNG; /* turn off ring on this line*/ /* turn off other pending ring signals */ for (ln = 0; ln < lp->mp->lines; ln++) { TMLN *tlp = lp->mp->ldsc + ln; if (((tlp->destination == NULL) && (tlp->master == 0)) && (tlp->modembits & TMXR_MDM_RNG) && (tlp->conn == FALSE)) tlp->modembits &= ~TMXR_MDM_RNG; } } } if ((lp->master) || (lp->mp && lp->mp->master) || (lp->port && lp->destination)) incoming_state = TMXR_MDM_DSR; else incoming_state = 0; } lp->modembits |= incoming_state; dptr = (lp->dptr ? lp->dptr : (lp->mp ? lp->mp->dptr : NULL)); if ((lp->modembits != before_modem_bits) && (sim_deb && lp->mp && dptr)) { sim_debug_bits (TMXR_DBG_MDM, dptr, tmxr_modem_bits, before_modem_bits, lp->modembits, FALSE); sim_debug (TMXR_DBG_MDM, dptr, " - Line %d - %p\n", (int)(lp-lp->mp->ldsc), lp->txb); } if (incoming_bits) *incoming_bits = lp->modembits; if (lp->mp && lp->modem_control) { /* This API ONLY works on modem_control enabled multiplexer lines */ if (bits_to_set | bits_to_clear) { /* Anything to do? */ if (lp->loopback) { if ((lp->modembits ^ before_modem_bits) & TMXR_MDM_DTR) { /* DTR changed? */ lp->ser_connect_pending = (lp->modembits & TMXR_MDM_DTR); lp->conn = !(lp->modembits & TMXR_MDM_DTR); } return SCPE_OK; } if (lp->serport) return sim_control_serial (lp->serport, bits_to_set, bits_to_clear, incoming_bits); if ((lp->sock) || (lp->connecting)) { if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) { /* drop DTR? */ if (lp->sock) tmxr_report_disconnection (lp); /* report closure */ tmxr_reset_ln (lp); } } else { if ((lp->destination) && /* Virtual Null Modem Cable */ (bits_to_set & ~before_modem_bits & /* and DTR being Raised */ TMXR_MDM_DTR)) { char msg[512]; sprintf (msg, "tmxr_set_get_modem_bits() - establishing outgoing connection to: %s", lp->destination); tmxr_debug_connect_line (lp, msg); lp->connecting = sim_connect_sock_ex (lp->datagram ? lp->port : NULL, lp->destination, "localhost", NULL, (lp->datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (lp->mp->packet ? SIM_SOCK_OPT_NODELAY : 0)); } } } return SCPE_OK; } if ((lp->sock) || (lp->connecting)) { if ((before_modem_bits & bits_to_clear & TMXR_MDM_DTR) != 0) { /* drop DTR? */ if (lp->sock) tmxr_report_disconnection (lp); /* report closure */ tmxr_reset_ln (lp); } } if ((lp->serport) && (!lp->loopback)) sim_control_serial (lp->serport, 0, 0, incoming_bits); return SCPE_INCOMP; } /* Enable or Disable loopback mode on a line Inputs: lp - the line to change enable_loopback - enable or disable flag Output: none Implementation note: 1) When enabling loopback mode, this API will disconnect any currently connected TCP or Serial session. 2) When disabling loopback mode, prior network connections and/or serial port connections will be restored. */ t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback) { if (lp->loopback == (enable_loopback != FALSE)) return SCPE_OK; /* Nothing to do */ lp->loopback = (enable_loopback != FALSE); if (lp->loopback) { lp->lpbsz = lp->rxbsz; lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz); lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0; if (!lp->conn) lp->ser_connect_pending = TRUE; } else { free (lp->lpb); lp->lpb = NULL; lp->lpbsz = 0; } return SCPE_OK; } t_bool tmxr_get_line_loopback (TMLN *lp) { return (lp->loopback != FALSE); } /* Enable or Disable halfduplex mode on a line Inputs: lp - the line to change enable_halfduplex - enable or disable flag Output: none When a network connected line is in halfduplex mode, DCD modem signal track with CTS. When not in halfduplex mode the DCD modem signal for network connected lines tracks with DSR. */ t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_halfduplex) { if (lp->halfduplex == (enable_halfduplex != FALSE)) return SCPE_OK; /* Nothing to do */ lp->halfduplex = (enable_halfduplex != FALSE); return SCPE_OK; } t_bool tmxr_get_line_halfduplex (TMLN *lp) { return (lp->halfduplex != FALSE); } t_stat tmxr_set_config_line (TMLN *lp, CONST char *config) { t_stat r; tmxr_debug_trace_line (lp, "tmxr_set_config_line()"); if (lp->serport) r = sim_config_serial (lp->serport, config); else { lp->serconfig = (char *)realloc (lp->serconfig, 1 + strlen (config)); strcpy (lp->serconfig, config); r = tmxr_set_line_speed (lp, lp->serconfig);; if (r != SCPE_OK) { free (lp->serconfig); lp->serconfig = NULL; } } if ((r == SCPE_OK) && (lp->mp) && (lp->mp->uptr)) /* Record port state for proper restore */ lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp); return r; } /* Get character from specific line Inputs: *lp = pointer to terminal line descriptor Output: valid + char, 0 if line Implementation note: 1. If a line break was detected coincident with the current character, the receive break status associated with the character is cleared, and SCPE_BREAK is ORed into the return value. */ int32 tmxr_input_pending_ln (TMLN *lp) { return (lp->rxbpi - lp->rxbpr); } int32 tmxr_getc_ln (TMLN *lp) { int32 j; t_stat val = 0; uint32 tmp; tmxr_debug_trace_line (lp, "tmxr_getc_ln()"); if ((lp->conn && lp->rcve) && /* conn & enb & */ ((!lp->rxbps) || /* (!rate limited || enough time passed)? */ (sim_gtime () >= lp->rxnexttime))) { if (!sim_send_poll_data (&lp->send, &val)) { /* injected input characters available? */ j = lp->rxbpi - lp->rxbpr; /* # input chrs */ if (j) { /* any? */ tmp = lp->rxb[lp->rxbpr]; /* get char */ val = TMXR_VALID | (tmp & 0377); /* valid + chr */ if (lp->rbr[lp->rxbpr]) { /* break? */ lp->rbr[lp->rxbpr] = 0; /* clear status */ val = val | SCPE_BREAK; /* indicate to caller */ } lp->rxbpr = lp->rxbpr + 1; /* adv pointer */ } } } /* end if conn */ if (lp->rxbpi == lp->rxbpr) /* empty? zero ptrs */ lp->rxbpi = lp->rxbpr = 0; if (lp->rxbps) { if (val) lp->rxnexttime = floor (sim_gtime () + ((lp->rxdelta * sim_timer_inst_per_sec ())/lp->rxbpsfactor)); } tmxr_debug_return(lp, val); return val; } /* Get packet from specific line Inputs: *lp = pointer to terminal line descriptor **pbuf = pointer to pointer of packet contents *psize = pointer to packet size frame_byte - byte which separates packets in the tcp stream (0 means no separation character) Output: SCPE_LOST link state lost SCPE_OK Packet returned OR no packet available Implementation notes: 1. If a packet is not yet available, then the pbuf address returned is NULL, but success (SCPE_OK) is returned */ t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize) { return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0); } t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte) { int32 c; size_t pktsize; size_t fc_size = (frame_byte ? 1 : 0); while (TMXR_VALID & (c = tmxr_getc_ln (lp))) { if (lp->rxpboffset + 3 > lp->rxpbsize) { lp->rxpbsize += 512; lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize); } if ((lp->rxpboffset == 0) && (fc_size) && (c != frame_byte)) { tmxr_debug (TMXR_DBG_PRCV, lp, "Received Unexpected Framing Byte", (char *)&lp->rxpb[lp->rxpboffset], 1); continue; } if ((lp->datagram) && (lp->rxpboffset == fc_size)) { /* Datagram packet length is provided as a part of the natural datagram delivery, for TCP lines, we read the packet length from the data stream. So, here we stuff packet size into head of packet buffer so it looks like it was delivered by TCP and the below return logic doesn't have to worry */ lp->rxpb[lp->rxpboffset++] = (uint8)(((1 + lp->rxbpi - lp->rxbpr) >> 8) & 0xFF); lp->rxpb[lp->rxpboffset++] = (uint8)((1 + lp->rxbpi - lp->rxbpr) & 0xFF); } lp->rxpb[lp->rxpboffset++] = c & 0xFF; if (lp->rxpboffset >= (2 + fc_size)) { pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size]; if (pktsize == (lp->rxpboffset - 2)) { ++lp->rxpcnt; *pbuf = &lp->rxpb[2+fc_size]; *psize = pktsize; lp->rxpboffset = 0; tmxr_debug (TMXR_DBG_PRCV, lp, "Received Packet", (char *)&lp->rxpb[2+fc_size], pktsize); return SCPE_OK; } } } *pbuf = NULL; *psize = 0; if (lp->conn) return SCPE_OK; return SCPE_LOST; } /* Poll for input Inputs: *mp = pointer to terminal multiplexer descriptor Outputs: none */ void tmxr_poll_rx (TMXR *mp) { int32 i, nbytes, j; TMLN *lp; tmxr_debug_trace (mp, "tmxr_poll_rx()"); for (i = 0; i < mp->lines; i++) { /* loop thru lines */ lp = mp->ldsc + i; /* get line desc */ if (!(lp->sock || lp->serport || lp->loopback) || !(lp->rcve)) /* skip if not connected */ continue; nbytes = 0; if (lp->rxbpi == 0) /* need input? */ nbytes = tmxr_read (lp, /* yes, read */ lp->rxbsz - TMXR_GUARD); /* leave spc for Telnet cruft */ else if (lp->tsta) /* in Telnet seq? */ nbytes = tmxr_read (lp, /* yes, read to end */ lp->rxbsz - lp->rxbpi); if (nbytes < 0) { /* line error? */ if (!lp->datagram) { /* ignore errors reading UDP sockets */ if (!lp->txbfd || lp->notelnet) lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */ tmxr_close_ln (lp); /* disconnect line */ } } else if (nbytes > 0) { /* if data rcvd */ tmxr_debug (TMXR_DBG_RCV, lp, "Received", &(lp->rxb[lp->rxbpi]), nbytes); j = lp->rxbpi; /* start of data */ lp->rxbpi = lp->rxbpi + nbytes; /* adv pointers */ lp->rxcnt = lp->rxcnt + nbytes; /* Examine new data, remove TELNET cruft before making input available */ if (!lp->notelnet) { /* Are we looking for telnet interpretation? */ for (; j < lp->rxbpi; ) { /* loop thru char */ u_char tmp = (u_char)lp->rxb[j]; /* get char */ switch (lp->tsta) { /* case tlnt state */ case TNS_NORM: /* normal */ if (tmp == TN_IAC) { /* IAC? */ lp->tsta = TNS_IAC; /* change state */ tmxr_rmvrc (lp, j); /* remove char */ break; } if ((tmp == TN_CR) && lp->dstb) /* CR, no bin */ lp->tsta = TNS_CRPAD; /* skip pad char */ j = j + 1; /* advance j */ break; case TNS_IAC: /* IAC prev */ if (tmp == TN_IAC) { /* IAC + IAC */ lp->tsta = TNS_NORM; /* treat as normal */ j = j + 1; /* advance j */ break; /* keep IAC */ } if (tmp == TN_BRK) { /* IAC + BRK? */ lp->tsta = TNS_NORM; /* treat as normal */ lp->rxb[j] = 0; /* char is null */ lp->rbr[j] = 1; /* flag break */ j = j + 1; /* advance j */ break; } switch (tmp) { case TN_WILL: /* IAC + WILL? */ lp->tsta = TNS_WILL; break; case TN_WONT: /* IAC + WONT? */ lp->tsta = TNS_WONT; break; case TN_DO: /* IAC + DO? */ lp->tsta = TNS_DO; break; case TN_DONT: /* IAC + DONT? */ lp->tsta = TNS_SKIP; /* IAC + other */ break; case TN_GA: case TN_EL: /* IAC + other 2 byte types */ case TN_EC: case TN_AYT: case TN_AO: case TN_IP: case TN_NOP: lp->tsta = TNS_NORM; /* ignore */ break; case TN_SB: /* IAC + SB sub-opt negotiation */ case TN_DATAMK: /* IAC + data mark */ case TN_SE: /* IAC + SE sub-opt end */ lp->tsta = TNS_NORM; /* ignore */ break; } tmxr_rmvrc (lp, j); /* remove char */ break; case TNS_WILL: /* IAC+WILL prev */ if ((tmp == TN_STATUS) || (tmp == TN_TIMING) || (tmp == TN_NAOCRD) || (tmp == TN_NAOHTS) || (tmp == TN_NAOHTD) || (tmp == TN_NAOFFD) || (tmp == TN_NAOVTS) || (tmp == TN_NAOVTD) || (tmp == TN_NAOLFD) || (tmp == TN_EXTEND) || (tmp == TN_LOGOUT) || (tmp == TN_BM) || (tmp == TN_DET) || (tmp == TN_SENDLO) || (tmp == TN_TERMTY) || (tmp == TN_ENDREC) || (tmp == TN_TUID) || (tmp == TN_OUTMRK) || (tmp == TN_TTYLOC) || (tmp == TN_3270) || (tmp == TN_X3PAD) || (tmp == TN_NAWS) || (tmp == TN_TERMSP) || (tmp == TN_TOGFLO) || (tmp == TN_XDISPL) || (tmp == TN_ENVIRO) || (tmp == TN_AUTH) || (tmp == TN_ENCRYP) || (tmp == TN_NEWENV) || (tmp == TN_TN3270) || (tmp == TN_CHARST) || (tmp == TN_COMPRT) || (tmp == TN_KERMIT)) { /* Reject (DONT) these 'uninteresting' options only one time to avoid loops */ if (0 == (lp->telnet_sent_opts[tmp] & TNOS_DONT)) { lp->notelnet = TRUE; /* Temporarily disable so */ tmxr_putc_ln (lp, TN_IAC); /* IAC gets injected bare */ lp->notelnet = FALSE; tmxr_putc_ln (lp, TN_DONT); tmxr_putc_ln (lp, tmp); lp->telnet_sent_opts[tmp] |= TNOS_DONT;/* Record DONT sent */ } } case TNS_WONT: /* IAC+WILL/WONT prev */ if (tmp == TN_BIN) { /* BIN? */ if (lp->tsta == TNS_WILL) { lp->dstb = 0; } else { lp->dstb = 1; } } tmxr_rmvrc (lp, j); /* remove it */ lp->tsta = TNS_NORM; /* next normal */ break; /* Negotiation with the HP terminal emulator "QCTerm" is not working. QCTerm says "WONT BIN" but sends bare CRs. RFC 854 says: Note that "CR LF" or "CR NUL" is required in both directions (in the default ASCII mode), to preserve the symmetry of the NVT model. ...The protocol requires that a NUL be inserted following a CR not followed by a LF in the data stream. Until full negotiation is implemented, we work around the problem by checking the character following the CR in non-BIN mode and strip it only if it is LF or NUL. This should not affect conforming clients. */ case TNS_CRPAD: /* only LF or NUL should follow CR */ lp->tsta = TNS_NORM; /* next normal */ if ((tmp == TN_LF) || /* CR + LF ? */ (tmp == TN_NUL)) /* CR + NUL? */ tmxr_rmvrc (lp, j); /* remove it */ break; case TNS_DO: /* pending DO request */ if ((tmp == TN_STATUS) || (tmp == TN_TIMING) || (tmp == TN_NAOCRD) || (tmp == TN_NAOHTS) || (tmp == TN_NAOHTD) || (tmp == TN_NAOFFD) || (tmp == TN_NAOVTS) || (tmp == TN_NAOVTD) || (tmp == TN_NAOLFD) || (tmp == TN_EXTEND) || (tmp == TN_LOGOUT) || (tmp == TN_BM) || (tmp == TN_DET) || (tmp == TN_SENDLO) || (tmp == TN_TERMTY) || (tmp == TN_ENDREC) || (tmp == TN_TUID) || (tmp == TN_OUTMRK) || (tmp == TN_TTYLOC) || (tmp == TN_3270) || (tmp == TN_X3PAD) || (tmp == TN_NAWS) || (tmp == TN_TERMSP) || (tmp == TN_TOGFLO) || (tmp == TN_XDISPL) || (tmp == TN_ENVIRO) || (tmp == TN_AUTH) || (tmp == TN_ENCRYP) || (tmp == TN_NEWENV) || (tmp == TN_TN3270) || (tmp == TN_CHARST) || (tmp == TN_COMPRT) || (tmp == TN_KERMIT)) { /* Reject (WONT) these 'uninteresting' options only one time to avoid loops */ if (0 == (lp->telnet_sent_opts[tmp] & TNOS_WONT)) { lp->notelnet = TRUE; /* Temporarily disable so */ tmxr_putc_ln (lp, TN_IAC); /* IAC gets injected bare */ lp->notelnet = FALSE; tmxr_putc_ln (lp, TN_WONT); tmxr_putc_ln (lp, tmp); lp->telnet_sent_opts[tmp] |= TNOS_WONT;/* Record WONT sent */ } } case TNS_SKIP: default: /* skip char */ tmxr_rmvrc (lp, j); /* remove char */ lp->tsta = TNS_NORM; /* next normal */ break; } /* end case state */ } /* end for char */ if (nbytes != (lp->rxbpi-lp->rxbpr)) { tmxr_debug (TMXR_DBG_RCV, lp, "Remaining", &(lp->rxb[lp->rxbpr]), lp->rxbpi-lp->rxbpr); } } } /* end else nbytes */ } /* end for lines */ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ lp = mp->ldsc + i; /* get line desc */ if (lp->rxbpi == lp->rxbpr) /* if buf empty, */ lp->rxbpi = lp->rxbpr = 0; /* reset pointers */ } /* end for */ return; } /* Return count of available characters for line */ int32 tmxr_rqln_bare (const TMLN *lp, t_bool speed) { if ((speed) && (lp->rxbps)) { /* consider speed and rate limiting? */ if (sim_gtime () < lp->rxnexttime) /* too soon? */ return 0; else return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz : 0)) ? 1 : 0; } return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz: 0)); } int32 tmxr_rqln (const TMLN *lp) { return tmxr_rqln_bare (lp, TRUE); } /* Store character in line buffer Inputs: *lp = pointer to line descriptor chr = character Outputs: status = ok, connection lost, or stall Implementation note: 1. If the line is not connected, SCPE_LOST is returned. */ t_stat tmxr_putc_ln (TMLN *lp, int32 chr) { if ((lp->conn == FALSE) && /* no conn & not buffered telnet? */ (!lp->txbfd || lp->notelnet)) { ++lp->txdrp; /* lost */ return SCPE_LOST; } tmxr_debug_trace_line (lp, "tmxr_putc_ln()"); #define TXBUF_AVAIL(lp) ((lp->serport ? 2: lp->txbsz) - tmxr_tqln (lp)) #define TXBUF_CHAR(lp, c) { \ lp->txb[lp->txbpi++] = (char)(c); \ lp->txbpi %= lp->txbsz; \ if (lp->txbpi == lp->txbpr) \ lp->txbpr = (1+lp->txbpr)%lp->txbsz, ++lp->txdrp; \ } if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {/* room for char (+ IAC)? */ if ((TN_IAC == (u_char) chr) && (!lp->notelnet)) /* char == IAC in telnet session? */ TXBUF_CHAR (lp, TN_IAC); /* stuff extra IAC char */ TXBUF_CHAR (lp, chr); /* buffer char & adv pointer */ if ((!lp->txbfd) && (TXBUF_AVAIL (lp) <= TMXR_GUARD))/* near full? */ lp->xmte = 0; /* disable line */ if (lp->txlog) { /* log if available */ extern TMLN *sim_oline; /* Make sure to avoid recursion */ TMLN *save_oline = sim_oline; /* when logging to a socket */ sim_oline = NULL; /* save output socket */ fputc (chr, lp->txlog); /* log to actual file */ sim_oline = save_oline; /* resture output socket */ } sim_exp_check (&lp->expect, chr); /* process expect rules as needed */ return SCPE_OK; /* char sent */ } ++lp->txdrp; lp->xmte = 0; /* no room, dsbl line */ return SCPE_STALL; /* char not sent */ } /* Store packet in line buffer Inputs: *lp = pointer to line descriptor *buf = pointer to packet data size = size of packet frame_char = inter-packet franing character (0 means no frame character) Outputs: status = ok, connection lost, or stall Implementation notea: 1. If the line is not connected, SCPE_LOST is returned. 2. If prior packet transmission still in progress, SCPE_STALL is returned and no packet data is stored. The caller must retry later. */ t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size) { return tmxr_put_packet_ln_ex (lp, buf, size, 0); } t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte) { t_stat r; size_t fc_size = (frame_byte ? 1 : 0); size_t pktlen_size = (lp->datagram ? 0 : 2); if ((!lp->conn) && (!lp->loopback)) return SCPE_LOST; if (lp->txppoffset < lp->txppsize) { tmxr_debug (TMXR_DBG_PXMT, lp, "Skipped Sending Packet - Transmit Busy", (char *)&lp->txpb[3], size); return SCPE_STALL; } if (lp->txpbsize < size + pktlen_size + fc_size) { lp->txpbsize = size + pktlen_size + fc_size; lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize); } lp->txpb[0] = frame_byte; if (!lp->datagram) { lp->txpb[0+fc_size] = (size >> 8) & 0xFF; lp->txpb[1+fc_size] = size & 0xFF; } memcpy (lp->txpb + pktlen_size + fc_size, buf, size); lp->txppsize = size + pktlen_size + fc_size; lp->txppoffset = 0; tmxr_debug (TMXR_DBG_PXMT, lp, "Sending Packet", (char *)&lp->txpb[pktlen_size+fc_size], size); ++lp->txpcnt; while ((lp->txppoffset < lp->txppsize) && (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset])))) ++lp->txppoffset; tmxr_send_buffered_data (lp); return (lp->conn || lp->loopback) ? SCPE_OK : SCPE_LOST; } /* Poll for output Inputs: *mp = pointer to terminal multiplexer descriptor Outputs: none */ void tmxr_poll_tx (TMXR *mp) { int32 i, nbytes; TMLN *lp; tmxr_debug_trace (mp, "tmxr_poll_tx()"); for (i = 0; i < mp->lines; i++) { /* loop thru lines */ lp = mp->ldsc + i; /* get line desc */ if (!lp->conn) /* skip if !conn */ continue; nbytes = tmxr_send_buffered_data (lp); /* buffered bytes */ if (nbytes == 0) { /* buf empty? enab line */ #if defined(SIM_ASYNCH_MUX) UNIT *ruptr = lp->uptr ? lp->uptr : lp->mp->uptr; if ((ruptr->dynflags & UNIT_TM_POLL) && sim_asynch_enabled && tmxr_rqln (lp)) _sim_activate (ruptr, 0); #endif lp->xmte = 1; /* enable line transmit */ } } /* end for */ return; } /* Send buffered data across network Inputs: *lp = pointer to line descriptor Outputs: returns number of bytes still buffered */ int32 tmxr_send_buffered_data (TMLN *lp) { int32 nbytes, sbytes; t_stat r; tmxr_debug_trace_line (lp, "tmxr_send_buffered_data()"); nbytes = tmxr_tqln(lp); /* avail bytes */ if (nbytes) { /* >0? write */ if (lp->txbpr < lp->txbpi) /* no wrap? */ sbytes = tmxr_write (lp, nbytes); /* write all data */ else sbytes = tmxr_write (lp, lp->txbsz - lp->txbpr);/* write to end buf */ if (sbytes >= 0) { /* ok? */ tmxr_debug (TMXR_DBG_XMT, lp, "Sent", &(lp->txb[lp->txbpr]), sbytes); lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ if (lp->txbpr >= lp->txbsz) /* wrap? */ lp->txbpr = 0; lp->txcnt = lp->txcnt + sbytes; /* update counts */ nbytes = nbytes - sbytes; if ((nbytes == 0) && (lp->datagram)) /* if Empty buffer on datagram line */ lp->txbpi = lp->txbpr = 0; /* Start next packet at beginning of buffer */ } if (sbytes < 0) { /* I/O Error? */ lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */ lp->rxpboffset = lp->txppoffset = lp->txppsize = 0;/* Drop the data we already know we can't send */ tmxr_close_ln (lp); /* close line/port on error */ return nbytes; /* done now. */ } if (nbytes && (lp->txbpr == 0)) { /* more data and wrap? */ sbytes = tmxr_write (lp, nbytes); if (sbytes > 0) { /* ok */ tmxr_debug (TMXR_DBG_XMT, lp, "Sent", lp->txb, sbytes); lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ if (lp->txbpr >= lp->txbsz) /* wrap? */ lp->txbpr = 0; lp->txcnt = lp->txcnt + sbytes; /* update counts */ nbytes = nbytes - sbytes; } } } /* end if nbytes */ while ((lp->txppoffset < lp->txppsize) && /* buffered packet data? */ (lp->txbsz > nbytes) && /* and room in xmt buffer */ (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset])))) ++lp->txppoffset; if ((nbytes == 0) && (tmxr_tqln(lp) > 0)) return tmxr_send_buffered_data (lp); return tmxr_tqln(lp) + tmxr_tpqln(lp); } /* Return count of buffered characters for line */ int32 tmxr_tqln (const TMLN *lp) { return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? lp->txbsz: 0)); } /* Return count of buffered packet characters for line */ int32 tmxr_tpqln (const TMLN *lp) { return (lp->txppsize - lp->txppoffset); } /* Return transmit packet busy status for line */ t_bool tmxr_tpbusyln (const TMLN *lp) { return (0 != (lp->txppsize - lp->txppoffset)); } static void _mux_detach_line (TMLN *lp, t_bool close_listener, t_bool close_connecting) { if (close_listener && lp->master) { sim_close_sock (lp->master); lp->master = 0; free (lp->port); lp->port = NULL; } if (lp->sock) { /* if existing tcp, drop it */ tmxr_report_disconnection (lp); /* report disconnection */ tmxr_reset_ln (lp); } if (close_connecting) { free (lp->destination); lp->destination = NULL; if (lp->connecting) { /* if existing outgoing tcp, drop it */ lp->sock = lp->connecting; lp->connecting = 0; tmxr_reset_ln (lp); } } if (lp->serport) { /* close current serial connection */ tmxr_reset_ln (lp); sim_control_serial (lp->serport, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);/* drop DTR and RTS */ sim_close_serial (lp->serport); lp->serport = 0; free (lp->serconfig); lp->serconfig = NULL; free (lp->destination); lp->destination = NULL; } tmxr_set_line_loopback (lp, FALSE); } t_stat tmxr_detach_ln (TMLN *lp) { UNIT *uptr = NULL; tmxr_debug_trace_line (lp, "tmxr_detach_ln()"); _mux_detach_line (lp, TRUE, TRUE); if (lp->mp) { if (lp->uptr) uptr = lp->uptr; else uptr = lp->mp->uptr; } if (uptr && uptr->filename) { /* Revise the unit's connect string to reflect the current attachments */ uptr->filename = tmxr_mux_attach_string (uptr->filename, lp->mp); /* No connections or listeners exist, then we're equivalent to being fully detached. We should reflect that */ if (uptr->filename == NULL) tmxr_detach (lp->mp, uptr); } return SCPE_OK; } static int32 _tmln_speed_delta (CONST char *cptr) { struct { const char *bps; int32 delta; } *spd, speeds[] = { {"50", TMLN_SPD_50_BPS}, {"75", TMLN_SPD_75_BPS}, {"110", TMLN_SPD_110_BPS}, {"134", TMLN_SPD_134_BPS}, {"150", TMLN_SPD_150_BPS}, {"300", TMLN_SPD_300_BPS}, {"600", TMLN_SPD_600_BPS}, {"1200", TMLN_SPD_1200_BPS}, {"1800", TMLN_SPD_1800_BPS}, {"2000", TMLN_SPD_2000_BPS}, {"2400", TMLN_SPD_2400_BPS}, {"3600", TMLN_SPD_3600_BPS}, {"4800", TMLN_SPD_4800_BPS}, {"7200", TMLN_SPD_7200_BPS}, {"9600", TMLN_SPD_9600_BPS}, {"19200", TMLN_SPD_19200_BPS}, {"38400", TMLN_SPD_38400_BPS}, {"57600", TMLN_SPD_57600_BPS}, {"76800", TMLN_SPD_76800_BPS}, {"115200", TMLN_SPD_115200_BPS}, {"0", 0}}; /* End of List, last valid value */ int nspeed; char speed[24]; nspeed = (uint32)strtotv (cptr, &cptr, 10); if ((*cptr != '\0') && (*cptr != '-') && (*cptr != '*')) return -1; sprintf (speed, "%d", nspeed); spd = speeds; while (1) { if (0 == strcmp(spd->bps, speed)) return spd->delta; if (spd->delta == 0) break; ++spd; } return -1; } t_stat tmxr_set_line_speed (TMLN *lp, CONST char *speed) { UNIT *uptr; CONST char *cptr; t_stat r; if (!speed || !*speed) return SCPE_2FARG; if (_tmln_speed_delta (speed) < 0) return SCPE_ARG; lp->rxbps = (uint32)strtotv (speed, &cptr, 10); if (*cptr == '*') { uint32 rxbpsfactor = (uint32) get_uint (cptr+1, 10, 32, &r); if (r != SCPE_OK) return r; lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE * rxbpsfactor; } lp->rxdelta = _tmln_speed_delta (speed); lp->rxnexttime = 0.0; uptr = lp->uptr; if ((!uptr) && (lp->mp)) uptr = lp->mp->uptr; if (uptr) uptr->wait = lp->rxdelta; if (lp->rxbpsfactor == 0.0) lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE; lp->txbps = lp->rxbps; lp->txdelta = lp->rxdelta; lp->txnexttime = lp->rxnexttime; return SCPE_OK; } /* Open a master listening socket (and all of the other variances of connections). A listening socket for the port number described by "cptr" is opened for the multiplexer associated with descriptor "mp". If the open is successful, all lines not currently otherwise connected (via serial, outgoing or direct listener) are initialized for Telnet connections. Initialization for all connection styles (MUX wide listener, per line serial, listener, outgoing, logging, buffering) are handled by this routine. */ t_stat tmxr_open_master (TMXR *mp, CONST char *cptr) { int32 i, line, nextline = -1; char tbuf[CBUFSIZE], listen[CBUFSIZE], destination[CBUFSIZE], logfiletmpl[CBUFSIZE], buffered[CBUFSIZE], hostport[CBUFSIZE], port[CBUFSIZE], option[CBUFSIZE], speed[CBUFSIZE]; SOCKET sock; SERHANDLE serport; CONST char *tptr = cptr; t_bool nolog, notelnet, listennotelnet, modem_control, loopback, datagram, packet; TMLN *lp; t_stat r = SCPE_OK; t_bool not_quiet = (!sim_quiet) && (0 == (sim_switches & SWMASK ('Q'))); if (*tptr == '\0') return SCPE_ARG; for (i = 0; i < mp->lines; i++) { /* initialize lines */ lp = mp->ldsc + i; lp->mp = mp; /* set the back pointer */ lp->modem_control = mp->modem_control; if (lp->rxbpsfactor == 0.0) lp->rxbpsfactor = TMXR_RX_BPS_UNIT_SCALE; } mp->ring_sock = INVALID_SOCKET; free (mp->ring_ipad); mp->ring_ipad = NULL; mp->ring_start_time = 0; tmxr_debug_trace (mp, "tmxr_open_master()"); while (*tptr) { line = nextline; memset(logfiletmpl, '\0', sizeof(logfiletmpl)); memset(listen, '\0', sizeof(listen)); memset(destination, '\0', sizeof(destination)); memset(buffered, '\0', sizeof(buffered)); memset(port, '\0', sizeof(port)); memset(option, '\0', sizeof(option)); memset(speed, '\0', sizeof(speed)); nolog = notelnet = listennotelnet = loopback = FALSE; datagram = mp->datagram; packet = mp->packet; if (mp->buffered) sprintf(buffered, "%d", mp->buffered); if (line != -1) notelnet = listennotelnet = mp->notelnet; modem_control = mp->modem_control; while (*tptr) { tptr = get_glyph_nc (tptr, tbuf, ','); if (!tbuf[0]) break; cptr = tbuf; if (!isdigit(*cptr)) { char gbuf[CBUFSIZE]; CONST char *init_cptr = cptr; cptr = get_glyph (cptr, gbuf, '='); if (0 == MATCH_CMD (gbuf, "LINE")) { if ((NULL == cptr) || ('\0' == *cptr)) return sim_messagef (SCPE_2FARG, "Missing Line Specifier\n"); nextline = (int32) get_uint (cptr, 10, mp->lines-1, &r); if (r) return sim_messagef (SCPE_ARG, "Invalid Line Specifier: %s\n", cptr); break; } if (0 == MATCH_CMD (gbuf, "LOG")) { if ((NULL == cptr) || ('\0' == *cptr)) return sim_messagef (SCPE_2FARG, "Missing Log Specifier\n"); strncpy(logfiletmpl, cptr, sizeof(logfiletmpl)-1); continue; } if (0 == MATCH_CMD (gbuf, "LOOPBACK")) { if ((NULL != cptr) && ('\0' != *cptr)) return sim_messagef (SCPE_2MARG, "Unexpected Loopback Specifier: %s\n", cptr); loopback = TRUE; continue; } if ((0 == MATCH_CMD (gbuf, "NOBUFFERED")) || (0 == MATCH_CMD (gbuf, "UNBUFFERED"))) { if ((NULL != cptr) && ('\0' != *cptr)) return sim_messagef (SCPE_2MARG, "Unexpected Unbuffered Specifier: %s\n", cptr); buffered[0] = '\0'; continue; } if (0 == MATCH_CMD (gbuf, "BUFFERED")) { if ((NULL == cptr) || ('\0' == *cptr)) strcpy (buffered, "32768"); else { i = (int32) get_uint (cptr, 10, 1024*1024, &r); if (r || (i == 0)) return sim_messagef (SCPE_ARG, "Invalid Buffered Specifier: %s\n", cptr); sprintf(buffered, "%d", i); } continue; } if (0 == MATCH_CMD (gbuf, "NOLOG")) { if ((NULL != cptr) && ('\0' != *cptr)) return sim_messagef (SCPE_2MARG, "Unexpected NoLog Specifier: %s\n", cptr); nolog = TRUE; continue; } if (0 == MATCH_CMD (gbuf, "NOMODEM")) { if ((NULL != cptr) && ('\0' != *cptr)) return sim_messagef (SCPE_2MARG, "Unexpected NoModem Specifier: %s\n", cptr); modem_control = FALSE; continue; } if (0 == MATCH_CMD (gbuf, "MODEM")) { if ((NULL != cptr) && ('\0' != *cptr)) return sim_messagef (SCPE_2MARG, "Unexpected Modem Specifier: %s\n", cptr); modem_control = TRUE; continue; } if ((0 == MATCH_CMD (gbuf, "DATAGRAM")) || (0 == MATCH_CMD (gbuf, "UDP"))) { if ((NULL != cptr) && ('\0' != *cptr)) return sim_messagef (SCPE_2MARG, "Unexpected Datagram Specifier: %s\n", cptr); notelnet = datagram = TRUE; continue; } if (0 == MATCH_CMD (gbuf, "PACKET")) { if ((NULL != cptr) && ('\0' != *cptr)) return sim_messagef (SCPE_2MARG, "Unexpected Packet Specifier: %s\n", cptr); packet = TRUE; continue; } if ((0 == MATCH_CMD (gbuf, "STREAM")) || (0 == MATCH_CMD (gbuf, "TCP"))) { if ((NULL != cptr) && ('\0' != *cptr)) return sim_messagef (SCPE_2MARG, "Unexpected Stream Specifier: %s\n", cptr); datagram = FALSE; continue; } if (0 == MATCH_CMD (gbuf, "CONNECT")) { if ((NULL == cptr) || ('\0' == *cptr)) return sim_messagef (SCPE_2FARG, "Missing Connect Specifier\n"); strcpy (destination, cptr); continue; } if (0 == MATCH_CMD (gbuf, "SPEED")) { if ((NULL == cptr) || ('\0' == *cptr) || (_tmln_speed_delta (cptr) < 0)) return sim_messagef (SCPE_ARG, "Invalid Speed Specifier: %s\n", (cptr ? cptr : "")); strcpy (speed, cptr); continue; } cptr = get_glyph (gbuf, port, ';'); if (sim_parse_addr (port, NULL, 0, NULL, NULL, 0, NULL, NULL)) return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port); if (cptr) { char *tptr = gbuf + (cptr - gbuf); get_glyph (cptr, tptr, 0); /* upcase this string */ if (0 == MATCH_CMD (cptr, "NOTELNET")) listennotelnet = TRUE; else if (0 == MATCH_CMD (cptr, "TELNET")) listennotelnet = FALSE; else return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", tptr); } cptr = init_cptr; } cptr = get_glyph_nc (cptr, port, ';'); sock = sim_master_sock (port, &r); /* make master socket to validate port */ if (r) return sim_messagef (SCPE_ARG, "Invalid Port Specifier: %s\n", port); if (sock == INVALID_SOCKET) /* open error */ return sim_messagef (SCPE_OPENERR, "Can't open network port: %s\n", port); sim_close_sock (sock); sim_os_ms_sleep (2); /* let the close finish (required on some platforms) */ strcpy (listen, port); cptr = get_glyph (cptr, option, ';'); if (option[0]) { if (0 == MATCH_CMD (option, "NOTELNET")) listennotelnet = TRUE; else if (0 == MATCH_CMD (option, "TELNET")) listennotelnet = FALSE; else return sim_messagef (SCPE_ARG, "Invalid Specifier: %s\n", option); } } if (destination[0]) { /* Validate destination */ serport = sim_open_serial (destination, NULL, &r); if (serport != INVALID_HANDLE) { sim_close_serial (serport); if (strchr (destination, ';') && mp->modem_control && !(sim_switches & SIM_SW_REST)) return sim_messagef (SCPE_ARG, "Serial line parameters must be set within simulated OS: %s\n", 1 + strchr (destination, ';')); } else { char *eptr; memset (hostport, '\0', sizeof(hostport)); strncpy (hostport, destination, sizeof(hostport)-1); if ((eptr = strchr (hostport, ';'))) *(eptr++) = '\0'; if (eptr) { get_glyph (eptr, eptr, 0); /* upcase this string */ if (0 == MATCH_CMD (eptr, "NOTELNET")) notelnet = TRUE; else if (0 == MATCH_CMD (eptr, "TELNET")) if (datagram) return sim_messagef (SCPE_ARG, "Telnet invalid on Datagram socket\n"); else notelnet = FALSE; else return sim_messagef (SCPE_ARG, "Unexpected specifier: %s\n", eptr); } sock = sim_connect_sock_ex (NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (packet ? SIM_SOCK_OPT_NODELAY : 0)); if (sock != INVALID_SOCKET) sim_close_sock (sock); else return sim_messagef (SCPE_ARG, "Invalid destination: %s\n", hostport); } } if (line == -1) { if (modem_control != mp->modem_control) return SCPE_ARG; if (logfiletmpl[0]) { strncpy(mp->logfiletmpl, logfiletmpl, sizeof(mp->logfiletmpl)-1); for (i = 0; i < mp->lines; i++) { lp = mp->ldsc + i; sim_close_logfile (&lp->txlogref); lp->txlog = NULL; lp->txlogname = (char *)realloc(lp->txlogname, CBUFSIZE); if (mp->lines > 1) sprintf(lp->txlogname, "%s_%d", mp->logfiletmpl, i); else strcpy (lp->txlogname, mp->logfiletmpl); r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref); if (r != SCPE_OK) { free (lp->txlogname); lp->txlogname = NULL; break; } } } mp->buffered = atoi(buffered); for (i = 0; i < mp->lines; i++) { /* initialize line buffers */ lp = mp->ldsc + i; if (mp->buffered) { lp->txbsz = mp->buffered; lp->txbfd = 1; lp->rxbsz = mp->buffered; } else { lp->txbsz = TMXR_MAXBUF; lp->txbfd = 0; lp->rxbsz = TMXR_MAXBUF; } lp->txbpi = lp->txbpr = 0; lp->txb = (char *)realloc(lp->txb, lp->txbsz); lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz); lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz); } if (nolog) { mp->logfiletmpl[0] = '\0'; for (i = 0; i < mp->lines; i++) { /* close line logs */ lp = mp->ldsc + i; free(lp->txlogname); lp->txlogname = NULL; if (lp->txlog) { sim_close_logfile (&lp->txlogref); lp->txlog = NULL; } } } if ((listen[0]) && (!datagram)) { sock = sim_master_sock (listen, &r); /* make master socket */ if (r) return sim_messagef (SCPE_ARG, "Invalid network listen port: %s\n", listen); if (sock == INVALID_SOCKET) /* open error */ return sim_messagef (SCPE_OPENERR, "Can't open network socket for listen port: %s\n", listen); if (mp->port) { /* close prior listener */ sim_close_sock (mp->master); mp->master = 0; free (mp->port); mp->port = NULL; } if (not_quiet) sim_printf ("Listening on port %s\n", listen); mp->port = (char *)realloc (mp->port, 1 + strlen (listen)); strcpy (mp->port, listen); /* save port */ mp->master = sock; /* save master socket */ mp->ring_sock = INVALID_SOCKET; free (mp->ring_ipad); mp->ring_ipad = NULL; mp->ring_start_time = 0; mp->notelnet = listennotelnet; /* save desired telnet behavior flag */ for (i = 0; i < mp->lines; i++) { /* initialize lines */ lp = mp->ldsc + i; lp->mp = mp; /* set the back pointer */ lp->packet = mp->packet; if (lp->serport) { /* serial port attached? */ tmxr_reset_ln (lp); /* close current serial connection */ sim_control_serial (lp->serport, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);/* drop DTR and RTS */ sim_close_serial (lp->serport); lp->serport = 0; free (lp->serconfig); lp->serconfig = NULL; } else { if (speed[0]) tmxr_set_line_speed (lp, speed); } tmxr_init_line (lp); /* initialize line state */ lp->sock = 0; /* clear the socket */ } } if (loopback) { if (mp->lines > 1) return sim_messagef (SCPE_ARG, "Ambiguous Loopback specification\n"); if (not_quiet) sim_printf ("Operating in loopback mode\n"); for (i = 0; i < mp->lines; i++) { lp = mp->ldsc + i; tmxr_set_line_loopback (lp, loopback); if (speed[0]) tmxr_set_line_speed (lp, speed); } } if (destination[0]) { if (mp->lines > 1) return sim_messagef (SCPE_ARG, "Ambiguous Destination specification\n"); lp = &mp->ldsc[0]; serport = sim_open_serial (destination, lp, &r); if (serport != INVALID_HANDLE) { _mux_detach_line (lp, TRUE, TRUE); if (lp->mp && lp->mp->master) { /* if existing listener, close it */ sim_close_sock (lp->mp->master); lp->mp->master = 0; free (lp->mp->port); lp->mp->port = NULL; } lp->destination = (char *)malloc(1+strlen(destination)); strcpy (lp->destination, destination); lp->mp = mp; lp->serport = serport; lp->ser_connect_pending = TRUE; lp->notelnet = TRUE; tmxr_init_line (lp); /* init the line state */ if (!lp->mp->modem_control) /* raise DTR and RTS for non modem control lines */ sim_control_serial (lp->serport, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL); lp->cnms = sim_os_msec (); /* record time of connection */ if (sim_switches & SWMASK ('V')) { /* -V flag reports connection on port */ sim_os_ms_sleep (TMXR_DTR_DROP_TIME); tmxr_report_connection (mp, lp); /* report the connection to the line */ } } else { lp->datagram = datagram; if (datagram) { if (listen[0]) { lp->port = (char *)realloc (lp->port, 1 + strlen (listen)); strcpy (lp->port, listen); /* save port */ } else return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n"); } lp->packet = packet; sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (packet ? SIM_SOCK_OPT_NODELAY : 0)); if (sock != INVALID_SOCKET) { _mux_detach_line (lp, FALSE, TRUE); lp->destination = (char *)malloc(1+strlen(hostport)); strcpy (lp->destination, hostport); lp->mp = mp; if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) { lp->connecting = sock; lp->ipad = (char *)malloc (1 + strlen (lp->destination)); strcpy (lp->ipad, lp->destination); } else sim_close_sock (sock); lp->notelnet = notelnet; tmxr_init_line (lp); /* init the line state */ if (speed[0] && (!datagram)) tmxr_set_line_speed (lp, speed); return SCPE_OK; } else return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n", datagram ? "Datagram" : "Stream", datagram ? listen : "", datagram ? "<->" : "", hostport); } } } else { /* line specific attach */ lp = &mp->ldsc[line]; lp->mp = mp; if (logfiletmpl[0]) { sim_close_logfile (&lp->txlogref); lp->txlog = NULL; lp->txlogname = (char *)realloc (lp->txlogname, 1 + strlen (logfiletmpl)); strcpy (lp->txlogname, logfiletmpl); r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref); if (r == SCPE_OK) setvbuf(lp->txlog, NULL, _IOFBF, 65536); else { free (lp->txlogname); lp->txlogname = NULL; return sim_messagef (r, "Can't open log file: %s\n", logfiletmpl); } } if (buffered[0] == '\0') { lp->rxbsz = lp->txbsz = TMXR_MAXBUF; lp->txbfd = 0; } else { lp->rxbsz = lp->txbsz = atoi(buffered); lp->txbfd = 1; } lp->txbpi = lp->txbpr = 0; lp->txb = (char *)realloc (lp->txb, lp->txbsz); lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz); lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz); lp->packet = packet; if (nolog) { free(lp->txlogname); lp->txlogname = NULL; if (lp->txlog) { sim_close_logfile (&lp->txlogref); lp->txlog = NULL; } } if ((listen[0]) && (!datagram)) { if ((mp->lines == 1) && (mp->master)) return sim_messagef (SCPE_ARG, "Single Line MUX can have either line specific OR MUS listener but NOT both\n"); sock = sim_master_sock (listen, &r); /* make master socket */ if (r) return sim_messagef (SCPE_ARG, "Invalid Listen Specification: %s\n", listen); if (sock == INVALID_SOCKET) /* open error */ return sim_messagef (SCPE_OPENERR, "Can't listen on port: %s\n", listen); _mux_detach_line (lp, TRUE, FALSE); if (not_quiet) sim_printf ("Line %d Listening on port %s\n", line, listen); lp->port = (char *)realloc (lp->port, 1 + strlen (listen)); strcpy (lp->port, listen); /* save port */ lp->master = sock; /* save master socket */ if (listennotelnet != mp->notelnet) lp->notelnet = listennotelnet; else lp->notelnet = mp->notelnet; } if (destination[0]) { serport = sim_open_serial (destination, lp, &r); if (serport != INVALID_HANDLE) { _mux_detach_line (lp, TRUE, TRUE); lp->destination = (char *)malloc(1+strlen(destination)); strcpy (lp->destination, destination); lp->serport = serport; lp->ser_connect_pending = TRUE; lp->notelnet = TRUE; tmxr_init_line (lp); /* init the line state */ if (!lp->mp->modem_control) /* raise DTR and RTS for non modem control lines */ sim_control_serial (lp->serport, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL); lp->cnms = sim_os_msec (); /* record time of connection */ if (sim_switches & SWMASK ('V')) { /* -V flag reports connection on port */ sim_os_ms_sleep (TMXR_DTR_DROP_TIME); tmxr_report_connection (mp, lp); /* report the connection to the line */ } } else { lp->datagram = datagram; if (datagram) { if (listen[0]) { lp->port = (char *)realloc (lp->port, 1 + strlen (listen)); strcpy (lp->port, listen); /* save port */ } else return sim_messagef (SCPE_ARG, "Missing listen port for Datagram socket\n"); } sock = sim_connect_sock_ex (datagram ? listen : NULL, hostport, "localhost", NULL, (datagram ? SIM_SOCK_OPT_DATAGRAM : 0) | (packet ? SIM_SOCK_OPT_NODELAY : 0)); if (sock != INVALID_SOCKET) { _mux_detach_line (lp, FALSE, TRUE); lp->destination = (char *)malloc(1+strlen(hostport)); strcpy (lp->destination, hostport); if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) { lp->connecting = sock; lp->ipad = (char *)malloc (1 + strlen (lp->destination)); strcpy (lp->ipad, lp->destination); } else sim_close_sock (sock); lp->notelnet = notelnet; tmxr_init_line (lp); /* init the line state */ } else return sim_messagef (SCPE_ARG, "Can't open %s socket on %s%s%s\n", datagram ? "Datagram" : "Stream", datagram ? listen : "", datagram ? "<->" : "", hostport); } } if (loopback) { tmxr_set_line_loopback (lp, loopback); if (not_quiet) sim_printf ("Line %d operating in loopback mode\n", line); } lp->modem_control = modem_control; if (speed[0] && (!datagram) && (!lp->serport)) tmxr_set_line_speed (lp, speed); r = SCPE_OK; } } if (r == SCPE_OK) tmxr_add_to_open_list (mp); return r; } /* Declare which unit polls for input Inputs: *mp = the mux line = the line number *uptr_poll = the unit which polls Outputs: none Implementation note: Only devices which poll on a unit different from the unit provided at MUX attach time need call this function. Calling this API is necessary for asynchronous multiplexer support and unnecessary otherwise. */ t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll) { if ((line < 0) || (line >= mp->lines)) return SCPE_ARG; mp->ldsc[line].uptr = uptr_poll; return SCPE_OK; } /* Declare which unit polls for output Inputs: *mp = the mux line = the line number *uptr_poll = the unit which polls for output Outputs: none Implementation note: Only devices which poll on a unit different from the unit provided at MUX attach time need call this function ABD different from the unit which polls for input. Calling this API is necessary for asynchronous multiplexer support and unnecessary otherwise. */ t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll) { if ((line < 0) || (line >= mp->lines)) return SCPE_ARG; mp->ldsc[line].o_uptr = uptr_poll; return SCPE_OK; } /* Declare which units are the console input and out devices Inputs: *rxuptr = the console input unit *txuptr = the console output unit Outputs: none Implementation note: This routine is exported by the tmxr library so that it gets defined to code which uses it by including sim_tmxr.h. Including sim_tmxr.h is necessary so that sim_activate is properly defined in the caller's code to actually call tmxr_activate. */ t_stat tmxr_set_console_units (UNIT *rxuptr, UNIT *txuptr) { extern TMXR sim_con_tmxr; tmxr_set_line_unit (&sim_con_tmxr, 0, rxuptr); tmxr_set_line_output_unit (&sim_con_tmxr, 0, txuptr); return SCPE_OK; } static TMXR **tmxr_open_devices = NULL; static int tmxr_open_device_count = 0; #if defined(SIM_ASYNCH_MUX) pthread_t sim_tmxr_poll_thread; /* Polling Thread Id */ #if defined(_WIN32) || defined(VMS) pthread_t sim_tmxr_serial_poll_thread; /* Serial Polling Thread Id */ pthread_cond_t sim_tmxr_serial_startup_cond; #endif pthread_mutex_t sim_tmxr_poll_lock; pthread_cond_t sim_tmxr_poll_cond; pthread_cond_t sim_tmxr_startup_cond; int32 sim_tmxr_poll_count = 0; t_bool sim_tmxr_poll_running = FALSE; static void * _tmxr_poll(void *arg) { struct timeval timeout; int timeout_usec; DEVICE *dptr = tmxr_open_devices[0]->dptr; UNIT **units = NULL; UNIT **activated = NULL; SOCKET *sockets = NULL; int wait_count = 0; /* Boost Priority for this I/O thread vs the CPU instruction execution thread which, in general, won't be readily yielding the processor when this thread needs to run */ sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - starting\n"); units = (UNIT **)calloc(FD_SETSIZE, sizeof(*units)); activated = (UNIT **)calloc(FD_SETSIZE, sizeof(*activated)); sockets = (SOCKET *)calloc(FD_SETSIZE, sizeof(*sockets)); timeout_usec = 1000000; pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_cond_signal (&sim_tmxr_startup_cond); /* Signal we're ready to go */ while (sim_asynch_enabled) { int i, j, status, select_errno; fd_set readfds, errorfds; int socket_count; SOCKET max_socket_fd; TMXR *mp; DEVICE *d; if ((tmxr_open_device_count == 0) || (!sim_is_running)) { for (j=0; j<wait_count; ++j) { d = find_dev_from_unit(activated[j]); sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Removing interest in %s. Other interest: %d\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count); --activated[j]->a_poll_waiter_count; --sim_tmxr_poll_count; } break; } /* If we started something we should wait for, let it finish before polling again */ if (wait_count) { sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - waiting for %d units\n", wait_count); pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - continuing with timeout of %dms\n", timeout_usec/1000); } FD_ZERO (&readfds); FD_ZERO (&errorfds); for (i=max_socket_fd=socket_count=0; i<tmxr_open_device_count; ++i) { mp = tmxr_open_devices[i]; if ((mp->master) && (mp->uptr->dynflags&UNIT_TM_POLL)) { units[socket_count] = mp->uptr; sockets[socket_count] = mp->master; FD_SET (mp->master, &readfds); FD_SET (mp->master, &errorfds); if (mp->master > max_socket_fd) max_socket_fd = mp->master; ++socket_count; } for (j=0; j<mp->lines; ++j) { if (mp->ldsc[j].sock) { units[socket_count] = mp->ldsc[j].uptr; if (units[socket_count] == NULL) units[socket_count] = mp->uptr; sockets[socket_count] = mp->ldsc[j].sock; FD_SET (mp->ldsc[j].sock, &readfds); FD_SET (mp->ldsc[j].sock, &errorfds); if (mp->ldsc[j].sock > max_socket_fd) max_socket_fd = mp->ldsc[j].sock; ++socket_count; } #if !defined(_WIN32) && !defined(VMS) if (mp->ldsc[j].serport) { units[socket_count] = mp->ldsc[j].uptr; if (units[socket_count] == NULL) units[socket_count] = mp->uptr; sockets[socket_count] = mp->ldsc[j].serport; FD_SET (mp->ldsc[j].serport, &readfds); FD_SET (mp->ldsc[j].serport, &errorfds); if (mp->ldsc[j].serport > max_socket_fd) max_socket_fd = mp->ldsc[j].serport; ++socket_count; } #endif if (mp->ldsc[j].connecting) { units[socket_count] = mp->uptr; sockets[socket_count] = mp->ldsc[j].connecting; FD_SET (mp->ldsc[j].connecting, &readfds); FD_SET (mp->ldsc[j].connecting, &errorfds); if (mp->ldsc[j].connecting > max_socket_fd) max_socket_fd = mp->ldsc[j].connecting; ++socket_count; } if (mp->ldsc[j].master) { units[socket_count] = mp->uptr; sockets[socket_count] = mp->ldsc[j].master; FD_SET (mp->ldsc[j].master, &readfds); FD_SET (mp->ldsc[j].master, &errorfds); if (mp->ldsc[j].master > max_socket_fd) max_socket_fd = mp->ldsc[j].master; ++socket_count; } } } pthread_mutex_unlock (&sim_tmxr_poll_lock); if (timeout_usec > 1000000) timeout_usec = 1000000; timeout.tv_sec = timeout_usec/1000000; timeout.tv_usec = timeout_usec%1000000; select_errno = 0; if (socket_count == 0) { sim_os_ms_sleep (timeout_usec/1000); status = 0; } else status = select (1+(int)max_socket_fd, &readfds, NULL, &errorfds, &timeout); select_errno = errno; wait_count=0; pthread_mutex_lock (&sim_tmxr_poll_lock); switch (status) { case 0: /* timeout */ for (i=max_socket_fd=socket_count=0; i<tmxr_open_device_count; ++i) { mp = tmxr_open_devices[i]; if (mp->master) { if (!mp->uptr->a_polling_now) { mp->uptr->a_polling_now = TRUE; mp->uptr->a_poll_waiter_count = 0; d = find_dev_from_unit(mp->uptr); sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating %s to poll connect\n", sim_uname(mp->uptr)); pthread_mutex_unlock (&sim_tmxr_poll_lock); _sim_activate (mp->uptr, 0); pthread_mutex_lock (&sim_tmxr_poll_lock); } if (mp->txcount) { timeout_usec = 10000; /* Wait 10ms next time (this gets doubled below) */ mp->txcount = 0; } } for (j=0; j<mp->lines; ++j) { if ((mp->ldsc[j].conn) && (mp->ldsc[j].uptr)) { if (tmxr_tqln(&mp->ldsc[j]) || tmxr_rqln (&mp->ldsc[j])) { timeout_usec = 10000; /* Wait 10ms next time (this gets doubled below) */ /* More than one socket can be associated with the same unit. Make sure to only activate it one time */ if (!mp->ldsc[j].uptr->a_polling_now) { mp->ldsc[j].uptr->a_polling_now = TRUE; mp->ldsc[j].uptr->a_poll_waiter_count = 0; d = find_dev_from_unit(mp->ldsc[j].uptr); sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Line %d Activating %s to poll data: %d/%d\n", j, sim_uname(mp->ldsc[j].uptr), tmxr_tqln(&mp->ldsc[j]), tmxr_rqln (&mp->ldsc[j])); pthread_mutex_unlock (&sim_tmxr_poll_lock); _sim_activate (mp->ldsc[j].uptr, 0); pthread_mutex_lock (&sim_tmxr_poll_lock); } } } } } sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - Poll Timeout - %dms\n", timeout_usec/1000); timeout_usec *= 2; /* Double timeout time */ break; case SOCKET_ERROR: wait_count = 0; if (select_errno == EINTR) break; sim_printf ("select() returned -1, errno=%d - %s\r\n", select_errno, strerror(select_errno)); abort(); break; default: wait_count = 0; for (i=0; i<socket_count; ++i) { if (FD_ISSET(sockets[i], &readfds) || FD_ISSET(sockets[i], &errorfds)) { /* More than one socket can be associated with the same unit. Only activate one time */ for (j=0; j<wait_count; ++j) if (activated[j] == units[i]) break; if (j == wait_count) { activated[j] = units[i]; ++wait_count; if (!activated[j]->a_polling_now) { activated[j]->a_polling_now = TRUE; activated[j]->a_poll_waiter_count = 1; d = find_dev_from_unit(activated[j]); sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Activating for data %s\n", sim_uname(activated[j])); pthread_mutex_unlock (&sim_tmxr_poll_lock); _sim_activate (activated[j], 0); pthread_mutex_lock (&sim_tmxr_poll_lock); } else { d = find_dev_from_unit(activated[j]); sim_debug (TMXR_DBG_ASY, d, "_tmxr_poll() - Already Activated %s%d %d times\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count); ++activated[j]->a_poll_waiter_count; } } } } if (wait_count) timeout_usec = 10000; /* Wait 10ms next time */ break; } sim_tmxr_poll_count += wait_count; } pthread_mutex_unlock (&sim_tmxr_poll_lock); free(units); free(activated); free(sockets); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - exiting\n"); return NULL; } #if defined(_WIN32) static void * _tmxr_serial_poll(void *arg) { int timeout_usec; DEVICE *dptr = tmxr_open_devices[0]->dptr; UNIT **units = NULL; UNIT **activated = NULL; SERHANDLE *serports = NULL; int wait_count = 0; /* Boost Priority for this I/O thread vs the CPU instruction execution thread which, in general, won't be readily yielding the processor when this thread needs to run */ sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - starting\n"); units = (UNIT **)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*units)); activated = (UNIT **)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*activated)); serports = (SERHANDLE *)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*serports)); timeout_usec = 1000000; pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_cond_signal (&sim_tmxr_serial_startup_cond); /* Signal we're ready to go */ while (sim_asynch_enabled) { int i, j; DWORD status; int serport_count; TMXR *mp; DEVICE *d; if ((tmxr_open_device_count == 0) || (!sim_is_running)) { for (j=0; j<wait_count; ++j) { d = find_dev_from_unit(activated[j]); sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_poll() - Removing interest in %s. Other interest: %d\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count); --activated[j]->a_poll_waiter_count; --sim_tmxr_poll_count; } break; } /* If we started something we should wait for, let it finish before polling again */ if (wait_count) { sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - waiting for %d units\n", wait_count); pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - continuing with timeout of %dms\n", timeout_usec/1000); } for (i=serport_count=0; i<tmxr_open_device_count; ++i) { mp = tmxr_open_devices[i]; for (j=0; j<mp->lines; ++j) { if (mp->ldsc[j].serport) { units[serport_count] = mp->ldsc[j].uptr; if (units[serport_count] == NULL) units[serport_count] = mp->uptr; serports[serport_count] = mp->ldsc[j].serport; ++serport_count; } } } if (serport_count == 0) /* No open serial ports? */ break; /* We're done */ pthread_mutex_unlock (&sim_tmxr_poll_lock); if (timeout_usec > 1000000) timeout_usec = 1000000; status = WaitForMultipleObjects (serport_count, serports, FALSE, timeout_usec/1000); wait_count=0; pthread_mutex_lock (&sim_tmxr_poll_lock); switch (status) { case WAIT_FAILED: sim_printf ("WaitForMultipleObjects() Failed, LastError=%d\r\n", GetLastError()); abort(); break; case WAIT_TIMEOUT: sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - Poll Timeout - %dms\n", timeout_usec/1000); timeout_usec *= 2; /* Double timeout time */ break; default: i = status - WAIT_OBJECT_0; wait_count = 0; j = wait_count; activated[j] = units[i]; ++wait_count; if (!activated[j]->a_polling_now) { activated[j]->a_polling_now = TRUE; activated[j]->a_poll_waiter_count = 1; d = find_dev_from_unit(activated[j]); sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_poll() - Activating for data %s\n", sim_uname(activated[j])); pthread_mutex_unlock (&sim_tmxr_poll_lock); _sim_activate (activated[j], 0); pthread_mutex_lock (&sim_tmxr_poll_lock); } else { d = find_dev_from_unit(activated[j]); sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_poll() - Already Activated %s%d %d times\n", sim_uname(activated[j]), activated[j]->a_poll_waiter_count); ++activated[j]->a_poll_waiter_count; } if (wait_count) timeout_usec = 10000; /* Wait 10ms next time */ break; } sim_tmxr_poll_count += wait_count; } pthread_mutex_unlock (&sim_tmxr_poll_lock); free(units); free(activated); free(serports); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - exiting\n"); return NULL; } #endif /* _WIN32 */ #if defined(VMS) #include <descrip.h> #include <ttdef.h> #include <tt2def.h> #include <iodef.h> #include <ssdef.h> #include <starlet.h> #include <unistd.h> typedef struct { unsigned short status; unsigned short count; unsigned int dev_status; } IOSB; #define MAXIMUM_WAIT_OBJECTS 64 /* Number of possible concurrently opened serial ports */ pthread_cond_t sim_serial_line_startup_cond; static void * _tmxr_serial_line_poll(void *arg) { TMLN *lp = (TMLN *)arg; DEVICE *dptr = tmxr_open_devices[0]->dptr; UNIT *uptr = (lp->uptr ? lp->uptr : lp->mp->uptr); DEVICE *d = find_dev_from_unit(uptr); int wait_count = 0; /* Boost Priority for this I/O thread vs the CPU instruction execution thread which, in general, won't be readily yielding the processor when this thread needs to run */ sim_os_set_thread_priority (PRIORITY_ABOVE_NORMAL); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_line_poll() - starting\n"); pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_cond_signal (&sim_serial_line_startup_cond); /* Signal we're ready to go */ while (sim_asynch_enabled) { int i, j; int serport_count; TMXR *mp = lp->mp; unsigned int status, term[2]; unsigned char buf[4]; IOSB iosb; if ((tmxr_open_device_count == 0) || (!sim_is_running)) { if (wait_count) { sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_line_poll() - Removing interest in %s. Other interest: %d\n", sim_uname(uptr), uptr->a_poll_waiter_count); --uptr->a_poll_waiter_count; --sim_tmxr_poll_count; } break; } /* If we started something we should wait for, let it finish before polling again */ if (wait_count) { sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_line_poll() - waiting for %d units\n", wait_count); pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_line_poll() - continuing with timeout of 1 sec\n"); } lp->a_active = TRUE; pthread_mutex_unlock (&sim_tmxr_poll_lock); term[0] = term[1] = 0; status = sys$qiow (0, lp->serport, IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, &iosb, 0, 0, buf, 1, 1, term, 0, 0); if (status != SS$_NORMAL) { sim_printf ("_tmxr_serial_line_poll() - QIO Failed, Status=%d\r\n", status); abort(); } wait_count = 0; sys$synch (0, &iosb); pthread_mutex_lock (&sim_tmxr_poll_lock); lp->a_active = FALSE; if (iosb.count == 1) { lp->a_buffered_character = buf[0] | SCPE_KFLAG; wait_count = 1; if (!uptr->a_polling_now) { uptr->a_polling_now = TRUE; uptr->a_poll_waiter_count = 1; sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_line_poll() - Activating for data %s\n", sim_uname(uptr)); pthread_mutex_unlock (&sim_tmxr_poll_lock); _sim_activate (uptr, 0); pthread_mutex_lock (&sim_tmxr_poll_lock); } else { sim_debug (TMXR_DBG_ASY, d, "_tmxr_serial_line_poll() - Already Activated %s%d %d times\n", sim_uname(uptr), uptr->a_poll_waiter_count); ++uptr->a_poll_waiter_count; } } sim_tmxr_poll_count += wait_count; } pthread_mutex_unlock (&sim_tmxr_poll_lock); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_line_poll() - exiting\n"); return NULL; } static void * _tmxr_serial_poll(void *arg) { int timeout_usec; DEVICE *dptr = tmxr_open_devices[0]->dptr; TMLN **lines = NULL; pthread_t *threads = NULL; /* Boost Priority for this I/O thread vs the CPU instruction execution thread which, in general, won't be readily yielding the processor when this thread needs to run */ sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - starting\n"); lines = (TMLN **)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*lines)); threads = (pthread_t *)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*threads)); pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_cond_signal (&sim_tmxr_serial_startup_cond); /* Signal we're ready to go */ pthread_cond_init (&sim_serial_line_startup_cond, NULL); while (sim_asynch_enabled) { pthread_attr_t attr; int i, j; int serport_count; TMXR *mp; DEVICE *d; if ((tmxr_open_device_count == 0) || (!sim_is_running)) break; pthread_attr_init (&attr); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); for (i=serport_count=0; i<tmxr_open_device_count; ++i) { mp = tmxr_open_devices[i]; for (j=0; j<mp->lines; ++j) { if (mp->ldsc[j].serport) { lines[serport_count] = &mp->ldsc[j]; pthread_create (&threads[serport_count], &attr, _tmxr_serial_line_poll, (void *)&mp->ldsc[j]); pthread_cond_wait (&sim_serial_line_startup_cond, &sim_tmxr_poll_lock); /* Wait for thread to stabilize */ ++serport_count; } } } pthread_attr_destroy( &attr); if (serport_count == 0) /* No open serial ports? */ break; /* We're done */ pthread_mutex_unlock (&sim_tmxr_poll_lock); for (i=0; i<serport_count; i++) pthread_join (threads[i], NULL); pthread_mutex_lock (&sim_tmxr_poll_lock); } pthread_mutex_unlock (&sim_tmxr_poll_lock); pthread_cond_destroy (&sim_serial_line_startup_cond); free(lines); free(threads); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - exiting\n"); return NULL; } #endif /* VMS */ #endif /* defined(SIM_ASYNCH_MUX) */ t_stat tmxr_start_poll (void) { #if defined(SIM_ASYNCH_MUX) pthread_mutex_lock (&sim_tmxr_poll_lock); if ((tmxr_open_device_count > 0) && sim_asynch_enabled && sim_is_running && !sim_tmxr_poll_running) { pthread_attr_t attr; pthread_cond_init (&sim_tmxr_startup_cond, NULL); pthread_attr_init (&attr); pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); pthread_create (&sim_tmxr_poll_thread, &attr, _tmxr_poll, NULL); pthread_attr_destroy( &attr); pthread_cond_wait (&sim_tmxr_startup_cond, &sim_tmxr_poll_lock); /* Wait for thread to stabilize */ pthread_cond_destroy (&sim_tmxr_startup_cond); sim_tmxr_poll_running = TRUE; } pthread_mutex_unlock (&sim_tmxr_poll_lock); #endif return SCPE_OK; } t_stat tmxr_stop_poll (void) { #if defined(SIM_ASYNCH_MUX) pthread_mutex_lock (&sim_tmxr_poll_lock); if (sim_tmxr_poll_running) { pthread_cond_signal (&sim_tmxr_poll_cond); pthread_mutex_unlock (&sim_tmxr_poll_lock); pthread_join (sim_tmxr_poll_thread, NULL); sim_tmxr_poll_running = FALSE; /* Transitioning from asynch mode so kick all polling units onto the event queue */ if (tmxr_open_device_count) { int i, j; for (i=0; i<tmxr_open_device_count; ++i) { TMXR *mp = tmxr_open_devices[i]; if (mp->uptr) _sim_activate (mp->uptr, 0); for (j = 0; j < mp->lines; ++j) if (mp->ldsc[j].uptr) _sim_activate (mp->ldsc[j].uptr, 0); } } } else pthread_mutex_unlock (&sim_tmxr_poll_lock); #endif return SCPE_OK; } static void tmxr_add_to_open_list (TMXR* mux) { int i; t_bool found = FALSE; #if defined(SIM_ASYNCH_MUX) pthread_mutex_lock (&sim_tmxr_poll_lock); #endif for (i=0; i<tmxr_open_device_count; ++i) if (tmxr_open_devices[i] == mux) { found = TRUE; break; } if (!found) { tmxr_open_devices = (TMXR **)realloc(tmxr_open_devices, (tmxr_open_device_count+1)*sizeof(*tmxr_open_devices)); tmxr_open_devices[tmxr_open_device_count++] = mux; for (i=0; i<mux->lines; i++) if (0 == mux->ldsc[i].send.delay) mux->ldsc[i].send.delay = SEND_DEFAULT_DELAY; } #if defined(SIM_ASYNCH_MUX) pthread_mutex_unlock (&sim_tmxr_poll_lock); if ((tmxr_open_device_count == 1) && (sim_asynch_enabled)) tmxr_start_poll (); #endif } static void _tmxr_remove_from_open_list (TMXR* mux) { int i, j; #if defined(SIM_ASYNCH_MUX) tmxr_stop_poll (); pthread_mutex_lock (&sim_tmxr_poll_lock); #endif for (i=0; i<tmxr_open_device_count; ++i) if (tmxr_open_devices[i] == mux) { for (j=i+1; j<tmxr_open_device_count; ++j) tmxr_open_devices[j-1] = tmxr_open_devices[j]; --tmxr_open_device_count; break; } #if defined(SIM_ASYNCH_MUX) pthread_mutex_unlock (&sim_tmxr_poll_lock); #endif } static t_stat _tmxr_locate_line_send_expect (const char *cptr, SEND **snd, EXPECT **exp) { char gbuf[CBUFSIZE]; DEVICE *dptr; int i; t_stat r; if (snd) *snd = NULL; if (exp) *exp = NULL; cptr = get_glyph(cptr, gbuf, ':'); dptr = find_dev (gbuf); /* device match? */ if (!dptr) return SCPE_ARG; for (i=0; i<tmxr_open_device_count; ++i) if (tmxr_open_devices[i]->dptr == dptr) { int line = (int)get_uint (cptr, 10, tmxr_open_devices[i]->lines, &r); if (r != SCPE_OK) return r; if (snd) *snd = &tmxr_open_devices[i]->ldsc[line].send; if (exp) *exp = &tmxr_open_devices[i]->ldsc[line].expect; return SCPE_OK; } return SCPE_ARG; } t_stat tmxr_locate_line_send (const char *cptr, SEND **snd) { return _tmxr_locate_line_send_expect (cptr, snd, NULL); } t_stat tmxr_locate_line_expect (const char *cptr, EXPECT **exp) { return _tmxr_locate_line_send_expect (cptr, NULL, exp); } t_stat tmxr_change_async (void) { #if defined(SIM_ASYNCH_IO) if (sim_asynch_enabled) tmxr_start_poll (); else tmxr_stop_poll (); #endif return SCPE_OK; } /* Attach unit to master socket */ t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, CONST char *cptr, t_bool async) { t_stat r; int32 i; r = tmxr_open_master (mp, cptr); /* open master socket */ if (r != SCPE_OK) /* error? */ return r; mp->uptr = uptr; /* save unit for polling */ uptr->filename = tmxr_mux_attach_string (uptr->filename, mp);/* save */ uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */ uptr->tmxr = (void *)mp; if ((mp->lines > 1) || ((mp->master == 0) && (mp->ldsc[0].connecting == 0) && (mp->ldsc[0].serport == 0))) uptr->dynflags = uptr->dynflags | UNIT_ATTMULT; /* allow multiple attach commands */ #if defined(SIM_ASYNCH_MUX) if (!async || (uptr->flags & TMUF_NOASYNCH)) /* if asynch disabled */ uptr->dynflags |= TMUF_NOASYNCH; /* tag as no asynch */ #else uptr->dynflags |= TMUF_NOASYNCH; /* tag as no asynch */ #endif if (mp->dptr == NULL) /* has device been set? */ mp->dptr = find_dev_from_unit (uptr); /* no, so set device now */ if (mp->dptr) { for (i=0; i<mp->lines; i++) { mp->ldsc[i].expect.dptr = mp->dptr; mp->ldsc[i].expect.dbit = TMXR_DBG_EXP; mp->ldsc[i].send.dptr = mp->dptr; mp->ldsc[i].send.dbit = TMXR_DBG_SEND; } } tmxr_add_to_open_list (mp); return SCPE_OK; } t_stat tmxr_startup (void) { return SCPE_OK; } t_stat tmxr_shutdown (void) { if (tmxr_open_device_count) return SCPE_IERR; return SCPE_OK; } t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc) { int i, j; if (0 == tmxr_open_device_count) fprintf(st, "No Attached Multiplexer Devices\n"); else { for (i=0; i<tmxr_open_device_count; ++i) { TMXR *mp = tmxr_open_devices[i]; TMLN *lp; char *attach; fprintf(st, "Multiplexer device: %s", (mp->dptr ? sim_dname (mp->dptr) : "")); if (mp->lines > 1) { fprintf(st, ", "); tmxr_show_lines(st, NULL, 0, mp); } if (mp->packet) fprintf(st, ", Packet"); if (mp->datagram) fprintf(st, ", UDP"); if (mp->notelnet) fprintf(st, ", Telnet=disabled"); if (mp->modem_control) fprintf(st, ", ModemControl=enabled"); if (mp->buffered) fprintf(st, ", Buffered=%d", mp->buffered); attach = tmxr_mux_attach_string (NULL, mp); if (attach) fprintf(st, ",\n attached to %s, ", attach); free (attach); tmxr_show_summ(st, NULL, 0, mp); fprintf(st, ", sessions=%d", mp->sessions); if (mp->lines == 1) { if (mp->ldsc->rxbps) { fprintf(st, ", Speed=%d", mp->ldsc->rxbps); if (mp->ldsc->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE) fprintf(st, "*%.0f", mp->ldsc->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE); fprintf(st, " bps"); } } fprintf(st, "\n"); if (mp->ring_start_time) { fprintf (st, " incoming Connection from: %s ringing for %d milliseconds\n", mp->ring_ipad, sim_os_msec () - mp->ring_start_time); } for (j = 0; j < mp->lines; j++) { lp = mp->ldsc + j; if (mp->lines > 1) { if (lp->dptr && (mp->dptr != lp->dptr)) fprintf (st, "Device: %s ", sim_dname(lp->dptr)); fprintf (st, "Line: %d", j); if (mp->notelnet != lp->notelnet) fprintf (st, " - %stelnet", lp->notelnet ? "no" : ""); if (lp->uptr && (lp->uptr != lp->mp->uptr)) fprintf (st, " - Unit: %s", sim_uname (lp->uptr)); if (mp->modem_control != lp->modem_control) fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled"); if (lp->loopback) fprintf(st, ", Loopback"); if (lp->rxbps) { fprintf(st, ", Speed=%d", lp->rxbps); if (lp->rxbpsfactor != TMXR_RX_BPS_UNIT_SCALE) fprintf(st, "*%.0f", lp->rxbpsfactor/TMXR_RX_BPS_UNIT_SCALE); fprintf(st, " bps"); } fprintf (st, "\n"); } if ((!lp->sock) && (!lp->connecting) && (!lp->serport) && (!lp->master)) { if (lp->modem_control) tmxr_fconns (st, lp, -1); continue; } tmxr_fconns (st, lp, -1); tmxr_fstats (st, lp, -1); } } } return SCPE_OK; } /* Close a master listening socket. The listening socket associated with multiplexer descriptor "mp" is closed and deallocated. In addition, all current Telnet sessions are disconnected. Serial and outgoing sessions are also disconnected. */ t_stat tmxr_close_master (TMXR *mp) { int32 i; TMLN *lp; for (i = 0; i < mp->lines; i++) { /* loop thru conn */ lp = mp->ldsc + i; if (!lp->destination && lp->sock) { /* not serial and is connected? */ tmxr_report_disconnection (lp); /* report disconnection */ tmxr_reset_ln (lp); /* disconnect line */ } else { if (lp->sock) { tmxr_report_disconnection (lp); /* report disconnection */ tmxr_reset_ln (lp); } if (lp->serport) { sim_control_serial (lp->serport, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);/* drop DTR and RTS */ tmxr_close_ln (lp); } free (lp->destination); lp->destination = NULL; if (lp->connecting) { lp->sock = lp->connecting; lp->connecting = 0; tmxr_reset_ln (lp); } lp->conn = FALSE; } if (lp->master) { sim_close_sock (lp->master); /* close master socket */ lp->master = 0; free (lp->port); lp->port = NULL; } lp->txbfd = 0; free (lp->txb); lp->txb = NULL; free (lp->rxb); lp->rxb = NULL; free (lp->rbr); lp->rbr = NULL; lp->modembits = 0; } if (mp->master) sim_close_sock (mp->master); /* close master socket */ mp->master = 0; free (mp->port); mp->port = NULL; if (mp->ring_sock != INVALID_SOCKET) { sim_close_sock (mp->ring_sock); mp->ring_sock = INVALID_SOCKET; free (mp->ring_ipad); mp->ring_ipad = NULL; mp->ring_start_time = 0; } _tmxr_remove_from_open_list (mp); return SCPE_OK; } /* Detach unit from master socket and close all active network connections and/or serial ports. Note that we return SCPE_OK, regardless of whether a listening socket was attached. */ t_stat tmxr_detach (TMXR *mp, UNIT *uptr) { int32 i; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; tmxr_close_master (mp); /* close master socket */ free (uptr->filename); /* free setup string */ uptr->filename = NULL; uptr->tmxr = NULL; mp->last_poll_time = 0; for (i=0; i < mp->lines; i++) { UNIT *uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr; UNIT *o_uptr = mp->ldsc[i].o_uptr ? mp->ldsc[i].o_uptr : mp->uptr; uptr->dynflags &= ~UNIT_TM_POLL; /* no polling */ o_uptr->dynflags &= ~UNIT_TM_POLL; /* no polling */ } uptr->flags &= ~(UNIT_ATT); /* not attached */ uptr->dynflags &= ~(UNIT_TM_POLL|TMUF_NOASYNCH); /* no polling, not asynch disabled */ return SCPE_OK; } t_stat tmxr_activate (UNIT *uptr, int32 interval) { if (uptr->dynflags & UNIT_TMR_UNIT) return sim_timer_activate (uptr, interval); #if defined(SIM_ASYNCH_MUX) if ((!(uptr->dynflags & UNIT_TM_POLL)) || (!sim_asynch_enabled)) { return _sim_activate (uptr, interval); } return SCPE_OK; #else return _sim_activate (uptr, interval); #endif } t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime) { #if defined(SIM_ASYNCH_MUX) if ((!(uptr->dynflags & UNIT_TM_POLL)) || (!sim_asynch_enabled)) { return _sim_activate_after (uptr, (double)usecs_walltime); } return SCPE_OK; #else return _sim_activate_after (uptr, (double)usecs_walltime); #endif } t_stat tmxr_activate_after_abs (UNIT *uptr, uint32 usecs_walltime) { #if defined(SIM_ASYNCH_MUX) if ((!(uptr->dynflags & UNIT_TM_POLL)) || (!sim_asynch_enabled)) { return _sim_activate_after_abs (uptr, (double)usecs_walltime); } return SCPE_OK; #else return _sim_activate_after_abs (uptr, (double)usecs_walltime); #endif } t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval) { int32 tmr = sim_rtcn_calibrated_tmr (); int32 ticks = (interval + (sim_rtcn_tick_size (tmr)/2))/sim_rtcn_tick_size (tmr);/* Convert to ticks */ return tmxr_clock_coschedule_tmr (uptr, tmr, ticks); } t_stat tmxr_clock_coschedule_abs (UNIT *uptr, int32 interval) { sim_cancel (uptr); return tmxr_clock_coschedule (uptr, interval); } #define MIN(a,b) (((a) < (b)) ? (a) : (b)) t_stat tmxr_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 ticks) { TMXR *mp = (TMXR *)uptr->tmxr; int32 interval = ticks * sim_rtcn_tick_size (tmr); #if defined(SIM_ASYNCH_MUX) if ((!(uptr->dynflags & UNIT_TM_POLL)) || (!sim_asynch_enabled)) { return sim_clock_coschedule (uptr, tmr, ticks); } return SCPE_OK; #else if (mp) { int32 i, soon = interval; double sim_gtime_now = sim_gtime (); for (i = 0; i < mp->lines; i++) { TMLN *lp = &mp->ldsc[i]; if (tmxr_rqln_bare (lp, FALSE)) { int32 due; if (lp->rxbps) if (lp->rxnexttime > sim_gtime_now) due = (int32)(lp->rxnexttime - sim_gtime_now); else due = sim_processing_event ? 1 : 0; /* avoid potential infinite loop if called from service routine */ else due = (int32)((uptr->wait * sim_timer_inst_per_sec ())/TMXR_RX_BPS_UNIT_SCALE); soon = MIN(soon, due); } } if (soon != interval) { sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "scheduling %s after %d instructions\n", sim_uname (uptr), soon); return _sim_activate (uptr, soon); } } sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "coscheduling %s after interval %d ticks\n", sim_uname (uptr), ticks); return sim_clock_coschedule_tmr (uptr, tmr, ticks); #endif } t_stat tmxr_clock_coschedule_tmr_abs (UNIT *uptr, int32 tmr, int32 ticks) { sim_cancel (uptr); return tmxr_clock_coschedule_tmr (uptr, tmr, ticks); } /* Generic Multiplexer attach help */ t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { TMXR *mux = (TMXR *)dptr->help_ctx; t_bool single_line = FALSE; /* default to Multi-Line help */ if (mux) single_line = (mux->lines == 1); if (!flag) fprintf (st, "%s Multiplexer Attach Help\n\n", dptr->name); if (single_line) { /* Single Line Multiplexer */ fprintf (st, "The %s multiplexer may be connected to terminal emulators supporting the\n", dptr->name); fprintf (st, "Telnet protocol via sockets, or to hardware terminals via host serial\n"); fprintf (st, "ports.\n\n"); if (mux->modem_control) { fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name); fprintf (st, "passing port configuration information and modem signals.\n"); } fprintf (st, "A Telnet listening port can be configured with:\n\n"); fprintf (st, " sim> ATTACH %s {interface:}port\n\n", dptr->name); fprintf (st, "Line buffering can be enabled for the %s device with:\n\n", dptr->name); fprintf (st, " sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name); fprintf (st, "Line buffering can be disabled for the %s device with:\n\n", dptr->name); fprintf (st, " sim> ATTACH %s NoBuffer\n\n", dptr->name); fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n"); fprintf (st, "The outbound traffic the %s device can be logged to a file with:\n", dptr->name); fprintf (st, " sim> ATTACH %s Log=LogFileName\n\n", dptr->name); fprintf (st, "File logging can be disabled for the %s device with:\n\n", dptr->name); fprintf (st, " sim> ATTACH %s NoLog\n\n", dptr->name); fprintf (st, "The %s device may be connected to a serial port on the host system.\n", dptr->name); } else { fprintf (st, "%s multiplexer lines may be connected to terminal emulators supporting the\n", dptr->name); fprintf (st, "Telnet protocol via sockets, or to hardware terminals via host serial\n"); fprintf (st, "ports. Concurrent Telnet and serial connections may be mixed on a given\n"); fprintf (st, "multiplexer.\n\n"); if (mux && mux->modem_control) { fprintf (st, "The %s device is a full modem control device and therefore is capable of\n", dptr->name); fprintf (st, "passing port configuration information and modem signals on all lines.\n"); } fprintf (st, "Modem Control signalling behaviors can be enabled/disabled on a specific\n"); fprintf (st, "multiplexer line with:\n\n"); fprintf (st, " sim> ATTACH %s Line=n,Modem\n", dptr->name); fprintf (st, " sim> ATTACH %s Line=n,NoModem\n\n", dptr->name); fprintf (st, "A Telnet listening port can be configured with:\n\n"); fprintf (st, " sim> ATTACH %s {interface:}port\n\n", dptr->name); if (mux) fprintf (st, "Line buffering for all %d lines on the %s device can be configured with:\n\n", mux->lines, dptr->name); else fprintf (st, "Line buffering for all lines on the %s device can be configured with:\n\n", dptr->name); fprintf (st, " sim> ATTACH %s Buffer{=bufsize}\n\n", dptr->name); if (mux) fprintf (st, "Line buffering for all %d lines on the %s device can be disabled with:\n\n", mux->lines, dptr->name); else fprintf (st, "Line buffering for all lines on the %s device can be disabled with:\n\n", dptr->name); fprintf (st, " sim> ATTACH %s NoBuffer\n\n", dptr->name); fprintf (st, "The default buffer size is 32k bytes, the max buffer size is 1024k bytes\n\n"); fprintf (st, "The outbound traffic for the lines of the %s device can be logged to files\n", dptr->name); fprintf (st, "with:\n\n"); fprintf (st, " sim> ATTACH %s Log=LogFileName\n\n", dptr->name); fprintf (st, "The log file name for each line uses the above LogFileName as a template\n"); fprintf (st, "for the actual file name which will be LogFileName_n where n is the line\n"); fprintf (st, "number.\n\n"); fprintf (st, "Multiplexer lines may be connected to serial ports on the host system.\n"); } fprintf (st, "Serial ports may be specified as an operating system specific device names\n"); fprintf (st, "or using simh generic serial names. simh generic names are of the form\n"); fprintf (st, "serN, where N is from 0 thru one less than the maximum number of serial\n"); fprintf (st, "ports on the local system. The mapping of simh generic port names to OS \n"); fprintf (st, "specific names can be displayed using the following command:\n\n"); fprintf (st, " sim> SHOW SERIAL\n"); fprintf (st, " Serial devices:\n"); fprintf (st, " ser0 COM1 (\\Device\\Serial0)\n"); fprintf (st, " ser1 COM3 (Winachcf0)\n\n"); if (single_line) { /* Single Line Multiplexer */ fprintf (st, " sim> ATTACH %s Connect=ser0\n\n", dptr->name); fprintf (st, "or equivalently:\n\n"); fprintf (st, " sim> ATTACH %s Connect=COM1\n\n", dptr->name); } else { fprintf (st, " sim> ATTACH %s Line=n,Connect=ser0\n\n", dptr->name); fprintf (st, "or equivalently:\n\n"); fprintf (st, " sim> ATTACH %s Line=n,Connect=COM1\n\n", dptr->name); if (mux) fprintf (st, "Valid line numbers are from 0 thru %d\n\n", mux->lines-1); } if (single_line) { /* Single Line Multiplexer */ fprintf (st, "The input data rate for the %s device can be controlled by\n", dptr->name); fprintf (st, "specifying SPEED=nnn{*fac} on the the ATTACH command.\n"); } else { fprintf (st, "The input data rate for all lines or a particular line of a the %s\n", dptr->name); fprintf (st, "device can be controlled by specifying SPEED=nnn{*fac} on the ATTACH command.\n"); } fprintf (st, "SPEED values can be any one of:\n\n"); fprintf (st, " 0 50 75 110 134 150 300 600 1200 1800 2000 2400\n"); fprintf (st, " 3600 4800 7200 9600 19200 38400 57600 76800 115200\n\n"); fprintf (st, "A SPEED value of 0 causes input data to be delivered to the simulated\n"); fprintf (st, "port as fast as it arrives.\n\n"); fprintf (st, "If a simulated multiplexor devices can programmatically set a serial\n"); fprintf (st, "port line speed, the programmatically specified speed will take precidence\n"); fprintf (st, "over any input speed specified on an attach command.\n"); fprintf (st, "Some simulated systems run very much faster than the original system\n"); fprintf (st, "which is being simulated. To accommodate this, the speed specified may\n"); fprintf (st, "include a factor which will increase the input data delivery rate by\n"); fprintf (st, "the specified factor. A factor is specified with a speed value of the\n"); fprintf (st, "form \"speed*factor\". Factor values can range from 1 thru 32.\n"); fprintf (st, "Example:\n\n"); fprintf (st, " sim> ATTACH %s 1234,SPEED=2400\n", dptr->name); fprintf (st, " sim> ATTACH %s 1234,SPEED=9600*8\n", dptr->name); if (!single_line) fprintf (st, " sim> ATTACH %s Line=2,SPEED=2400\n", dptr->name); fprintf (st, "\n"); fprintf (st, "The SPEED parameter only influences the rate at which data is deliverd\n"); fprintf (st, "into the simulated multiplexor port. Output data rates are unaffected\n"); fprintf (st, "If an attach command specifies a speed multiply factor, that value will\n"); fprintf (st, "persist independent of any programatic action by the simulated system to\n"); fprintf (st, "change the port speed.\n\n"); fprintf (st, "An optional serial port configuration string may be present after the port\n"); fprintf (st, "name. If present, it must be separated from the port name with a semicolon\n"); fprintf (st, "and has this form:\n\n"); fprintf (st, " <rate>-<charsize><parity><stopbits>\n\n"); fprintf (st, "where:\n"); fprintf (st, " rate = communication rate in bits per second\n"); fprintf (st, " charsize = character size in bits (5-8, including optional parity)\n"); fprintf (st, " parity = parity designator (N/E/O/M/S for no/even/odd/mark/space parity)\n"); fprintf (st, " stopbits = number of stop bits (1, 1.5, or 2)\n\n"); fprintf (st, "As an example:\n\n"); fprintf (st, " 9600-8n1\n\n"); fprintf (st, "The supported rates, sizes, and parity options are host-specific. If\n"); fprintf (st, "a configuration string is not supplied, then the default of 9600-8N1\n"); fprintf (st, "is used.\n"); fprintf (st, "Note: The serial port configuration option is only available on multiplexer\n"); fprintf (st, " lines which are not operating with full modem control behaviors enabled.\n"); fprintf (st, " Lines with full modem control behaviors enabled have all of their\n"); fprintf (st, " configuration managed by the Operating System running within the\n"); fprintf (st, " simulator.\n\n"); fprintf (st, "An attachment to a serial port with the '-V' switch will cause a\n"); fprintf (st, "connection message to be output to the connected serial port.\n"); fprintf (st, "This will help to confirm the correct port has been connected and\n"); fprintf (st, "that the port settings are reasonable for the connected device.\n"); fprintf (st, "This would be done as:\n\n"); if (single_line) /* Single Line Multiplexer */ fprintf (st, " sim> ATTACH -V %s Connect=SerN\n", dptr->name); else { fprintf (st, " sim> ATTACH -V %s Line=n,Connect=SerN\n\n", dptr->name); fprintf (st, "Line specific tcp listening ports are supported. These are configured\n"); fprintf (st, "using commands of the form:\n\n"); fprintf (st, " sim> ATTACH %s Line=n,{interface:}port{;notelnet}\n\n", dptr->name); } fprintf (st, "Direct computer to computer connections (Virutal Null Modem cables) may\n"); fprintf (st, "be established using the telnet protocol or via raw tcp sockets.\n\n"); fprintf (st, " sim> ATTACH %s Line=n,Connect=host:port{;notelnet}\n\n", dptr->name); fprintf (st, "Computer to computer virtual connections can be one way (as illustrated\n"); fprintf (st, "above) or symmetric. A symmetric connection is configured by combining\n"); if (single_line) { /* Single Line Multiplexer */ fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n"); fprintf (st, " sim> ATTACH %s listenport,Connect=host:port\n\n", dptr->name); } else { fprintf (st, "a one way connection with a tcp listening port on the same line:\n\n"); fprintf (st, " sim> ATTACH %s Line=n,listenport,Connect=host:port\n\n", dptr->name); } fprintf (st, "When symmetric virtual connections are configured, incoming connections\n"); fprintf (st, "on the specified listening port are checked to assure that they actually\n"); fprintf (st, "come from the specified connection destination host system.\n\n"); if (single_line) { /* Single Line Multiplexer */ fprintf (st, "The %s device can be attached in LOOPBACK mode:\n\n", dptr->name); fprintf (st, " sim> ATTACH %s Loopback\n\n", dptr->name); } else { fprintf (st, "A line on the %s device can be attached in LOOPBACK mode:\n\n", dptr->name); fprintf (st, " sim> ATTACH %s Line=n,Loopback\n\n", dptr->name); } fprintf (st, "When operating in LOOPBACK mode, all outgoing data arrives as input and\n"); fprintf (st, "outgoing modem signals (if enabled) (DTR and RTS) are reflected in the\n"); fprintf (st, "incoming modem signals (DTR->(DCD and DSR), RTS->CTS)\n\n"); if (single_line) /* Single Line Multiplexer */ fprintf (st, "The connection configured for the %s device is unconfigured by:\n\n", dptr->name); else fprintf (st, "All connections configured for the %s device are unconfigured by:\n\n", dptr->name); fprintf (st, " sim> DETACH %s\n\n", dptr->name); if (dptr->modifiers) { MTAB *mptr; for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) if (mptr->valid == &tmxr_dscln) { fprintf (st, "A specific line on the %s device can be disconnected with:\n\n", dptr->name); fprintf (st, " sim> SET %s %s=n\n\n", dptr->name, mptr->mstring); fprintf (st, "This will cause a telnet connection to be closed, but a serial port will\n"); fprintf (st, "normally have DTR dropped for 500ms and raised again (thus hanging up a\n"); fprintf (st, "modem on that serial port).\n\n"); fprintf (st, "A line which is connected to a serial port can be manually closed by\n"); fprintf (st, "adding the -C switch to a %s command.\n\n", mptr->mstring); fprintf (st, " sim> SET -C %s %s=n\n\n", dptr->name, mptr->mstring); } } return SCPE_OK; } /* Stub examine and deposit */ t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { return SCPE_NOFNC; } t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) { return SCPE_NOFNC; } /* Write a message directly to a socket */ void tmxr_msg (SOCKET sock, const char *msg) { if ((sock) && (sock != INVALID_SOCKET)) sim_write_sock (sock, msg, (int32)strlen (msg)); return; } /* Write a message to a line */ void tmxr_linemsg (TMLN *lp, const char *msg) { while (*msg) { while (SCPE_STALL == tmxr_putc_ln (lp, (int32)(*msg))) if (lp->txbsz == tmxr_send_buffered_data (lp)) sim_os_ms_sleep (10); ++msg; } return; } /* Write a formatted message to a line */ void tmxr_linemsgf (TMLN *lp, const char *fmt, ...) { va_list arglist; va_start (arglist, fmt); tmxr_linemsgvf (lp, fmt, arglist); va_end (arglist); } void tmxr_linemsgvf (TMLN *lp, const char *fmt, va_list arglist) { char stackbuf[STACKBUFSIZE]; int32 bufsize = sizeof(stackbuf); char *buf = stackbuf; int32 i, len; buf[bufsize-1] = '\0'; while (1) { /* format passed string, args */ #if defined(NO_vsnprintf) len = vsprintf (buf, fmt, arglist); #else /* !defined(NO_vsnprintf) */ len = vsnprintf (buf, bufsize-1, fmt, arglist); #endif /* NO_vsnprintf */ /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */ if ((len < 0) || (len >= bufsize-1)) { if (buf != stackbuf) free (buf); bufsize = bufsize * 2; if (bufsize < len + 2) bufsize = len + 2; buf = (char *) malloc (bufsize); if (buf == NULL) /* out of memory */ return; buf[bufsize-1] = '\0'; continue; } break; } /* Output the formatted data expanding newlines where they exist */ for (i = 0; i < len; ++i) { if (('\n' == buf[i]) && ((i == 0) || ('\r' != buf[i-1]))) { while (SCPE_STALL == tmxr_putc_ln (lp, '\r')) if (lp->txbsz == tmxr_send_buffered_data (lp)) sim_os_ms_sleep (10); } while (SCPE_STALL == tmxr_putc_ln (lp, buf[i])) if (lp->txbsz == tmxr_send_buffered_data (lp)) sim_os_ms_sleep (10); } if (buf != stackbuf) free (buf); return; } /* Print connections - used only in named SHOW command */ void tmxr_fconns (FILE *st, const TMLN *lp, int32 ln) { int32 hr, mn, sc; uint32 ctime; if (ln >= 0) fprintf (st, "line %d: ", ln); if ((lp->sock) || (lp->connecting)) { /* tcp connection? */ if (lp->destination) /* remote connection? */ if (lp->datagram) fprintf (st, "Datagram Connection from %s to remote port %s\n", lp->port, lp->destination);/* print port name */ else fprintf (st, "Connection to remote port %s\n", lp->destination);/* print port name */ else /* incoming connection */ fprintf (st, "Connection from IP address %s\n", lp->ipad); } else if (lp->destination) /* remote connection? */ fprintf (st, "Connecting to remote port %s\n", lp->destination);/* print port name */ if (lp->sock) { char *sockname, *peername; sim_getnames_sock (lp->sock, &sockname, &peername); fprintf (st, "Connection %s->%s\n", sockname, peername); free (sockname); free (peername); } if ((lp->port) && (!lp->datagram)) fprintf (st, "Listening on port %s\n", lp->port); /* print port name */ if (lp->serport) /* serial connection? */ fprintf (st, "Connected to serial port %s\n", lp->destination); /* print port name */ if (lp->cnms) { ctime = (sim_os_msec () - lp->cnms) / 1000; hr = ctime / 3600; mn = (ctime / 60) % 60; sc = ctime % 60; if (ctime) fprintf (st, " %s %02d:%02d:%02d\n", lp->connecting ? "Connecting for" : "Connected", hr, mn, sc); } else fprintf (st, " Line disconnected\n"); if (lp->modem_control) { fprintf (st, " Modem Bits: %s%s%s%s%s%s\n", (lp->modembits & TMXR_MDM_DTR) ? "DTR " : "", (lp->modembits & TMXR_MDM_RTS) ? "RTS " : "", (lp->modembits & TMXR_MDM_DCD) ? "DCD " : "", (lp->modembits & TMXR_MDM_RNG) ? "RNG " : "", (lp->modembits & TMXR_MDM_CTS) ? "CTS " : "", (lp->modembits & TMXR_MDM_DSR) ? "DSR " : ""); } if ((lp->serport == 0) && (lp->sock) && (!lp->datagram)) fprintf (st, " %s\n", (lp->notelnet) ? "Telnet disabled (RAW data)" : "Telnet protocol"); if (lp->send.buffer) sim_show_send_input (st, &lp->send); if (lp->expect.buf) sim_exp_showall (st, &lp->expect); if (lp->txlog) fprintf (st, " Logging to %s\n", lp->txlogname); return; } /* Print statistics - used only in named SHOW command */ void tmxr_fstats (FILE *st, const TMLN *lp, int32 ln) { static const char *enab = "on"; static const char *dsab = "off"; if (ln >= 0) fprintf (st, "Line %d:", ln); if ((!lp->sock) && (!lp->connecting) && (!lp->serport)) fprintf (st, " not connected\n"); else { if (ln >= 0) fprintf (st, "\n"); fprintf (st, " input (%s)", (lp->rcve? enab: dsab)); if (lp->rxcnt) fprintf (st, " queued/total = %d/%d", tmxr_rqln (lp), lp->rxcnt); if (lp->rxpcnt) fprintf (st, " packets = %d", lp->rxpcnt); fprintf (st, "\n output (%s)", (lp->xmte? enab: dsab)); if (lp->txcnt || lp->txbpi) fprintf (st, " queued/total = %d/%d", tmxr_tqln (lp), lp->txcnt); if (lp->txpcnt || tmxr_tpqln (lp)) fprintf (st, " packet data queued/packets sent = %d/%d", tmxr_tpqln (lp), lp->txpcnt); fprintf (st, "\n"); } if (lp->txbfd) fprintf (st, " output buffer size = %d\n", lp->txbsz); if (lp->txcnt || lp->txbpi) fprintf (st, " bytes in buffer = %d\n", ((lp->txcnt > 0) && (lp->txcnt > lp->txbsz)) ? lp->txbsz : lp->txbpi); if (lp->txdrp) fprintf (st, " dropped = %d\n", lp->txdrp); return; } /* Disconnect a line. Disconnect a line of the multiplexer associated with descriptor "desc" from a tcp session or a serial port. Two calling sequences are supported: 1. If "val" is zero, then "uptr" is implicitly associated with the line number corresponding to the position of the unit in the zero-based array of units belonging to the associated device, and "cptr" is ignored. For example, if "uptr" points to unit 3 in a given device, then line 3 will be disconnected. 2. If "val" is non-zero, then "cptr" points to a string that is parsed for an explicit line number, and "uptr" is ignored. For example, if "cptr" points to the string "3", then line 3 will be disconnected. If the line was connected to a tcp session, the socket associated with the line will be closed. If the line was connected to a serial port, the port will NOT be closed, but DTR will be dropped. After a 500ms delay DTR will be raised again. If the sim_switches -C flag is set, then a serial port connection will be closed. Implementation notes: 1. This function is usually called as an MTAB processing routine. */ t_stat tmxr_dscln (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { TMXR *mp = (TMXR *) desc; TMLN *lp; t_stat status; if (val) /* explicit line? */ uptr = NULL; /* indicate to get routine */ tmxr_debug_trace (mp, "tmxr_dscln()"); lp = tmxr_get_ldsc (uptr, cptr, mp, &status); /* get referenced line */ if (lp == NULL) /* bad line number? */ return status; /* report it */ if ((lp->sock) || (lp->serport)) { /* connection active? */ if (!lp->notelnet) tmxr_linemsg (lp, "\r\nOperator disconnected line\r\n\n");/* report closure */ tmxr_reset_ln_ex (lp, (sim_switches & SWMASK ('C'))); /* drop the line */ } return SCPE_OK; } /* Enable logging for line */ t_stat tmxr_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { TMXR *mp = (TMXR *) desc; TMLN *lp; if (cptr == NULL) /* no file name? */ return SCPE_2FARG; lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ if (lp == NULL) return SCPE_IERR; if (lp->txlog) /* close existing log */ tmxr_set_nolog (NULL, val, NULL, desc); lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc namebuf */ if (lp->txlogname == NULL) /* can't? */ return SCPE_MEM; strncpy (lp->txlogname, cptr, CBUFSIZE); /* save file name */ sim_open_logfile (cptr, TRUE, &lp->txlog, &lp->txlogref);/* open log */ if (lp->txlog == NULL) { /* error? */ free (lp->txlogname); /* free buffer */ return SCPE_OPENERR; } if (mp->uptr) /* attached?, then update attach string */ lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp); return SCPE_OK; } /* Disable logging for line */ t_stat tmxr_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { TMXR *mp = (TMXR *) desc; TMLN *lp; if (cptr) /* no arguments */ return SCPE_2MARG; lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ if (lp == NULL) return SCPE_IERR; if (lp->txlog) { /* logging? */ sim_close_logfile (&lp->txlogref); /* close log */ free (lp->txlogname); /* free namebuf */ lp->txlog = NULL; lp->txlogname = NULL; } if (mp->uptr) lp->mp->uptr->filename = tmxr_mux_attach_string (lp->mp->uptr->filename, lp->mp); return SCPE_OK; } /* Show logging status for line */ t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { const TMXR *mp = (const TMXR *) desc; TMLN *lp; lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ if (lp == NULL) return SCPE_IERR; if (lp->txlog) fprintf (st, "logging to %s", lp->txlogname); else fprintf (st, "no logging"); return SCPE_OK; } /* Set the line connection order. Example command for eight-line multiplexer: SET <dev> LINEORDER=1;5;2-4;7 Resulting connection order: 1,5,2,3,4,7,0,6. Parameters: - uptr = (not used) - val = (not used) - cptr = pointer to first character of range specification - desc = pointer to multiplexer's TMXR structure On entry, cptr points to the value portion of the command string, which may be either a semicolon-separated list of line ranges or the keyword ALL. If a line connection order array is not defined in the multiplexer descriptor, the command is rejected. If the specified range encompasses all of the lines, the first value of the connection order array is set to -1 to indicate sequential connection order. Otherwise, the line values in the array are set to the order specified by the command string. All values are populated, first with those explicitly specified in the command string, and then in ascending sequence with those not specified. If an error occurs, the original line order is not disturbed. */ t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *carg, void *desc) { TMXR *mp = (TMXR *) desc; char *tbuf; char *tptr; CONST char *cptr; t_addr low, high, max = (t_addr) mp->lines - 1; int32 *list; t_bool *set; uint32 line, idx = 0; t_stat result = SCPE_OK; if (mp->lnorder == NULL) /* line connection order undefined? */ return SCPE_NXPAR; /* "Non-existent parameter" error */ else if ((carg == NULL) || (*carg == '\0')) /* line range not supplied? */ return SCPE_MISVAL; /* "Missing value" error */ list = (int32 *) calloc (mp->lines, sizeof (int32)); /* allocate new line order array */ if (list == NULL) /* allocation failed? */ return SCPE_MEM; /* report it */ set = (t_bool *) calloc (mp->lines, sizeof (t_bool)); /* allocate line set tracking array */ if (set == NULL) { /* allocation failed? */ free (list); /* free successful list allocation */ return SCPE_MEM; /* report it */ } tbuf = (char *) calloc (strlen(carg)+2, sizeof(*carg)); strcpy (tbuf, carg); tptr = tbuf + strlen (tbuf); /* append a semicolon */ *tptr++ = ';'; /* to the command string */ *tptr = '\0'; /* to make parsing easier for get_range */ cptr = tbuf; while (*cptr) { /* parse command string */ cptr = get_range (NULL, cptr, &low, &high, 10, max, ';');/* get a line range */ if (cptr == NULL) { /* parsing error? */ result = SCPE_ARG; /* "Invalid argument" error */ break; } else if ((low > max) || (high > max)) { /* line out of range? */ result = SCPE_SUB; /* "Subscript out of range" error */ break; } else if ((low == 0) && (high == max)) { /* entire line range specified? */ list [0] = -1; /* set sequential order flag */ idx = (uint32) max + 1; /* indicate no fill-in needed */ break; } else for (line = (uint32) low; line <= (uint32) high; line++) /* see if previously specified */ if (set [line] == FALSE) { /* not already specified? */ set [line] = TRUE; /* now it is */ list [idx] = line; /* add line to connection order */ idx = idx + 1; /* bump "specified" count */ } } if (result == SCPE_OK) { /* assignment successful? */ if (idx <= max) /* any lines not specified? */ for (line = 0; line <= max; line++) /* fill them in sequentially */ if (set [line] == FALSE) { /* specified? */ list [idx] = line; /* no, so add it */ idx = idx + 1; } memcpy (mp->lnorder, list, mp->lines * sizeof (int32)); /* copy working array to connection array */ } free (list); /* free list allocation */ free (set); /* free set allocation */ free (tbuf); /* free arg copy with ; */ return result; } /* Show line connection order. Parameters: - st = stream on which output is to be written - uptr = (not used) - val = (not used) - desc = pointer to multiplexer's TMXR structure If a connection order array is not defined in the multiplexer descriptor, the command is rejected. If the first value of the connection order array is set to -1, then the connection order is sequential. Otherwise, the line values in the array are printed as a semicolon-separated list. Ranges are printed where possible to shorten the output. */ t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { int32 i, j, low, last; const TMXR *mp = (const TMXR *) desc; int32 *iptr = mp->lnorder; t_bool first = TRUE; if (iptr == NULL) /* connection order undefined? */ return SCPE_NXPAR; /* "Non-existent parameter" error */ if (*iptr < 0) /* sequential order indicated? */ fprintf (st, "Order=0-%d\n", mp->lines - 1); /* print full line range */ else { low = last = *iptr++; /* set first line value */ for (j = 1; j <= mp->lines; j++) { /* print remaining lines in order list */ if (j < mp->lines) /* more lines to process? */ i = *iptr++; /* get next line in list */ else /* final iteration */ i = -1; /* get "tie-off" value */ if (i != last + 1) { /* end of a range? */ if (first) { /* first line to print? */ fputs ("Order=", st); /* print header */ first = FALSE; } else /* not first line printed */ fputc (';', st); /* print separator */ if (low == last) /* range null? */ fprintf (st, "%d", last); /* print single line value */ else /* range established */ fprintf (st, "%d-%d", low, last); /* print start and end line */ low = i; /* start new range */ } last = i; /* note value for range check */ } } if (first == FALSE) /* sanity check for lines == 0 */ fputc ('\n', st); return SCPE_OK; } /* Show summary processor */ t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { const TMXR *mp = (const TMXR *) desc; int32 i, t; if (mp == NULL) return SCPE_IERR; for (i = t = 0; i < mp->lines; i++) if ((mp->ldsc[i].sock != 0) || (mp->ldsc[i].serport != 0)) t = t + 1; if (mp->lines > 1) fprintf (st, "%d current connection%s", t, (t != 1) ? "s" : ""); else fprintf (st, "%s", (t == 1) ? "connected" : "disconnected"); return SCPE_OK; } /* Show conn/stat processor */ t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { const TMXR *mp = (const TMXR *) desc; int32 i, any; if (mp == NULL) return SCPE_IERR; for (i = any = 0; i < mp->lines; i++) { if ((mp->ldsc[i].sock != 0) || (mp->ldsc[i].serport != 0) || mp->ldsc[i].modem_control) { if ((mp->ldsc[i].sock != 0) || (mp->ldsc[i].serport != 0)) any++; if (val) tmxr_fconns (st, &mp->ldsc[i], i); else if ((mp->ldsc[i].sock != 0) || (mp->ldsc[i].serport != 0)) tmxr_fstats (st, &mp->ldsc[i], i); } } if (any == 0) fprintf (st, (mp->lines == 1? "disconnected\n": "all disconnected\n")); return SCPE_OK; } /* Show number of lines */ t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { const TMXR *mp = (const TMXR *) desc; if (mp == NULL) return SCPE_IERR; fprintf (st, "lines=%d", mp->lines); return SCPE_OK; } static struct { u_char value; const char *name; } tn_chars[] = { {TN_IAC, "TN_IAC"}, /* protocol delim */ {TN_DONT, "TN_DONT"}, /* dont */ {TN_DO, "TN_DO"}, /* do */ {TN_WONT, "TN_WONT"}, /* wont */ {TN_WILL, "TN_WILL"}, /* will */ {TN_SB, "TN_SB"}, /* sub-option negotiation */ {TN_GA, "TN_SG"}, /* go ahead */ {TN_EL, "TN_EL"}, /* erase line */ {TN_EC, "TN_EC"}, /* erase character */ {TN_AYT, "TN_AYT"}, /* are you there */ {TN_AO, "TN_AO"}, /* abort output */ {TN_IP, "TN_IP"}, /* interrupt process */ {TN_BRK, "TN_BRK"}, /* break */ {TN_DATAMK, "TN_DATAMK"}, /* data mark */ {TN_NOP, "TN_NOP"}, /* no operation */ {TN_SE, "TN_SE"}, /* end sub-option negot */ /* Options */ {TN_BIN, "TN_BIN"}, /* bin */ {TN_ECHO, "TN_ECHO"}, /* echo */ {TN_SGA, "TN_SGA"}, /* sga */ {TN_STATUS, "TN_STATUS"}, /* option status query */ {TN_TIMING, "TN_TIMING"}, /* Timing Mark */ {TN_NAOCRD, "TN_NAOCRD"}, /* Output Carriage-Return Disposition */ {TN_NAOHTS, "TN_NAOHTS"}, /* Output Horizontal Tab Stops */ {TN_NAOHTD, "TN_NAOHTD"}, /* Output Horizontal Tab Stop Disposition */ {TN_NAOFFD, "TN_NAOFFD"}, /* Output Forfeed Disposition */ {TN_NAOVTS, "TN_NAOVTS"}, /* Output Vertical Tab Stop */ {TN_NAOVTD, "TN_NAOVTD"}, /* Output Vertical Tab Stop Disposition */ {TN_NAOLFD, "TN_NAOLFD"}, /* Output Linefeed Disposition */ {TN_EXTEND, "TN_EXTEND"}, /* Extended Ascii */ {TN_LOGOUT, "TN_LOGOUT"}, /* Logout */ {TN_BM, "TN_BM"}, /* Byte Macro */ {TN_DET, "TN_DET"}, /* Data Entry Terminal */ {TN_SENDLO, "TN_SENDLO"}, /* Send Location */ {TN_TERMTY, "TN_TERMTY"}, /* Terminal Type */ {TN_ENDREC, "TN_ENDREC"}, /* Terminal Type */ {TN_TUID, "TN_TUID"}, /* TACACS User Identification */ {TN_OUTMRK, "TN_OUTMRK"}, /* Output Marking */ {TN_TTYLOC, "TN_TTYLOC"}, /* Terminal Location Number */ {TN_3270, "TN_3270"}, /* 3270 Regime */ {TN_X3PAD, "TN_X3PAD"}, /* X.3 PAD */ {TN_NAWS, "TN_NAWS"}, /* Negotiate About Window Size */ {TN_TERMSP, "TN_TERMSP"}, /* Terminal Speed */ {TN_TOGFLO, "TN_TOGFLO"}, /* Remote Flow Control */ {TN_LINE, "TN_LINE"}, /* line mode */ {TN_XDISPL, "TN_XDISPL"}, /* X Display Location */ {TN_ENVIRO, "TN_ENVIRO"}, /* Environment */ {TN_AUTH, "TN_AUTH"}, /* Authentication */ {TN_ENCRYP, "TN_ENCRYP"}, /* Data Encryption */ {TN_NEWENV, "TN_NEWENV"}, /* New Environment */ {TN_TN3270, "TN_TN3270"}, /* TN3270 Enhancements */ {TN_CHARST, "TN_CHARST"}, /* CHARSET */ {TN_COMPRT, "TN_COMPRT"}, /* Com Port Control */ {TN_KERMIT, "TN_KERMIT"}, /* KERMIT */ {0, NULL}}; static char *tmxr_debug_buf = NULL; static size_t tmxr_debug_buf_used = 0; static size_t tmxr_debug_buf_size = 0; static void tmxr_buf_debug_char (char value) { if (tmxr_debug_buf_used+2 > tmxr_debug_buf_size) { tmxr_debug_buf_size += 1024; tmxr_debug_buf = (char *)realloc (tmxr_debug_buf, tmxr_debug_buf_size); } tmxr_debug_buf[tmxr_debug_buf_used++] = value; tmxr_debug_buf[tmxr_debug_buf_used] = '\0'; } static void tmxr_buf_debug_string (const char *string) { while (*string) tmxr_buf_debug_char (*string++); } static void tmxr_buf_debug_telnet_option (u_char chr) { int j; for (j=0; 1; ++j) { if (NULL == tn_chars[j].name) { if (isprint(chr)) tmxr_buf_debug_char (chr); else { tmxr_buf_debug_char ('_'); if ((chr >= 1) && (chr <= 26)) { tmxr_buf_debug_char ('^'); tmxr_buf_debug_char ('A' + chr - 1); } else { char octal[8]; sprintf(octal, "\\%03o", (u_char)chr); tmxr_buf_debug_string (octal); } tmxr_buf_debug_char ('_'); } break; } if ((u_char)chr == tn_chars[j].value) { tmxr_buf_debug_char ('_'); tmxr_buf_debug_string (tn_chars[j].name); tmxr_buf_debug_char ('_'); break; } } } static int tmxr_buf_debug_telnet_options (u_char *buf, int bufsize) { int optsize = 2; tmxr_buf_debug_telnet_option ((u_char)buf[0]); tmxr_buf_debug_telnet_option ((u_char)buf[1]); switch ((u_char)buf[1]) { case TN_IAC: default: return optsize; break; case TN_WILL: case TN_WONT: case TN_DO: case TN_DONT: ++optsize; tmxr_buf_debug_telnet_option ((u_char)buf[2]); break; } return optsize; } void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize) { DEVICE *dptr = (lp->dptr ? lp->dptr : (lp->mp ? lp->mp->dptr : NULL)); if ((dptr) && (dbits & dptr->dctrl)) { int i; tmxr_debug_buf_used = 0; if (tmxr_debug_buf) tmxr_debug_buf[tmxr_debug_buf_used] = '\0'; if (lp->notelnet) { int same, group, sidx, oidx; char outbuf[80], strbuf[18]; static char hex[] = "0123456789ABCDEF"; for (i=same=0; i<bufsize; i += 16) { if ((i > 0) && (0 == memcmp(&buf[i], &buf[i-16], 16))) { ++same; continue; } if (same > 0) { if (lp->mp->lines > 1) sim_debug (dbits, dptr, "Line:%d %04X thru %04X same as above\n", (int)(lp-lp->mp->ldsc), i-(16*same), i-1); else sim_debug (dbits, dptr, "%04X thru %04X same as above\n", i-(16*same), i-1); same = 0; } group = (((bufsize - i) > 16) ? 16 : (bufsize - i)); for (sidx=oidx=0; sidx<group; ++sidx) { outbuf[oidx++] = ' '; outbuf[oidx++] = hex[(buf[i+sidx]>>4)&0xf]; outbuf[oidx++] = hex[buf[i+sidx]&0xf]; if (isprint((u_char)buf[i+sidx])) strbuf[sidx] = buf[i+sidx]; else strbuf[sidx] = '.'; } outbuf[oidx] = '\0'; strbuf[sidx] = '\0'; if (lp->mp->lines > 1) sim_debug (dbits, dptr, "Line:%d %04X%-48s %s\n", (int)(lp-lp->mp->ldsc), i, outbuf, strbuf); else sim_debug (dbits, dptr, "%04X%-48s %s\n", i, outbuf, strbuf); } if (same > 0) { if (lp->mp->lines > 1) sim_debug (dbits, dptr, "Line:%d %04X thru %04X same as above\n", (int)(lp-lp->mp->ldsc), i-(16*same), bufsize-1); else sim_debug (dbits, dptr, "%04X thru %04X same as above\n", i-(16*same), bufsize-1); } } else { tmxr_debug_buf_used = 0; if (tmxr_debug_buf) tmxr_debug_buf[tmxr_debug_buf_used] = '\0'; for (i=0; i<bufsize; ++i) { switch ((u_char)buf[i]) { case TN_CR: tmxr_buf_debug_string ("_TN_CR_"); break; case TN_LF: tmxr_buf_debug_string ("_TN_LF_"); break; case TN_IAC: if (!lp->notelnet) { i += (tmxr_buf_debug_telnet_options ((u_char *)(&buf[i]), bufsize-i) - 1); break; } default: if (isprint((u_char)buf[i])) tmxr_buf_debug_char (buf[i]); else { tmxr_buf_debug_char ('_'); if ((buf[i] >= 1) && (buf[i] <= 26)) { tmxr_buf_debug_char ('^'); tmxr_buf_debug_char ('A' + buf[i] - 1); } else { char octal[8]; sprintf(octal, "\\%03o", (u_char)buf[i]); tmxr_buf_debug_string (octal); } tmxr_buf_debug_char ('_'); } break; } } if (lp->mp->lines > 1) sim_debug (dbits, dptr, "Line:%d %s %d bytes '%s'\n", (int)(lp-lp->mp->ldsc), msg, bufsize, tmxr_debug_buf); else sim_debug (dbits, dptr, "%s %d bytes '%s'\n", msg, bufsize, tmxr_debug_buf); } } } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | /* sim_tmxr.h: terminal multiplexer definitions Copyright (c) 2001-2008, Robert M Supnik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of Robert M Supnik shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. Based on the original DZ11 simulator by Thord Nilson, as updated by Arthur Krewat. 10-Oct-12 MP Added extended attach support for serial, per line listener and outgoing connections 17-Jan-11 MP Added buffered line capabilities 20-Nov-08 RMS Added three new standardized SHOW routines 07-Oct-08 JDB Added serial port support to TMXR, TMLN 27-May-08 JDB Added lnorder to TMXR structure, added tmxr_set_lnorder and tmxr_set_lnorder 14-May-08 JDB Added dptr to TMXR structure 04-Jan-04 RMS Changed TMXR ldsc to be pointer to linedesc array Added tmxr_linemsg, logging (from Mark Pizzolato) 29-Dec-03 RMS Added output stall support, increased buffer size 22-Dec-02 RMS Added break support (from Mark Pizzolato) 20-Aug-02 RMS Added tmxr_open_master, tmxr_close_master, tmxr.port 30-Dec-01 RMS Renamed tmxr_fstatus, added tmxr_fstats 20-Oct-01 RMS Removed tmxr_getchar, formalized buffer guard, added tmxr_rqln, tmxr_tqln */ #ifndef SIM_TMXR_H_ #define SIM_TMXR_H_ 0 #ifdef __cplusplus extern "C" { #endif #ifndef SIMH_SERHANDLE_DEFINED #define SIMH_SERHANDLE_DEFINED 0 typedef struct SERPORT *SERHANDLE; #endif #include "sim_sock.h" #define TMXR_V_VALID 15 #define TMXR_VALID (1 << TMXR_V_VALID) #define TMXR_MAXBUF 256 /* buffer size */ #define TMXR_DTR_DROP_TIME 500 /* milliseconds to drop DTR for 'pseudo' modem control */ #define TMXR_MODEM_RING_TIME 3 /* seconds to wait for DTR for incoming connections */ #define TMXR_DEFAULT_CONNECT_POLL_INTERVAL 1 /* seconds between connection polls */ #define TMXR_DBG_XMT 0x0010000 /* Debug Transmit Data */ #define TMXR_DBG_RCV 0x0020000 /* Debug Received Data */ #define TMXR_DBG_RET 0x0040000 /* Debug Returned Received Data */ #define TMXR_DBG_MDM 0x0080000 /* Debug Modem Signals */ #define TMXR_DBG_CON 0x0100000 /* Debug Connection Activities */ #define TMXR_DBG_ASY 0x0200000 /* Debug Asynchronous Activities */ #define TMXR_DBG_TRC 0x0400000 /* Debug trace routine calls */ #define TMXR_DBG_PXMT 0x0800000 /* Debug Transmit Packet Data */ #define TMXR_DBG_PRCV 0x1000000 /* Debug Received Packet Data */ #define TMXR_DBG_EXP 0x2000000 /* Debug Expect Activities */ #define TMXR_DBG_SEND 0x4000000 /* Debug Send Activities */ /* Modem Control Bits */ #define TMXR_MDM_DTR 0x01 /* Data Terminal Ready */ #define TMXR_MDM_RTS 0x02 /* Request To Send */ #define TMXR_MDM_DCD 0x04 /* Data Carrier Detect */ #define TMXR_MDM_RNG 0x08 /* Ring Indicator */ #define TMXR_MDM_CTS 0x10 /* Clear To Send */ #define TMXR_MDM_DSR 0x20 /* Data Set Ready */ #define TMXR_MDM_INCOMING (TMXR_MDM_DCD|TMXR_MDM_RNG|TMXR_MDM_CTS|TMXR_MDM_DSR) /* Settable Modem Bits */ #define TMXR_MDM_OUTGOING (TMXR_MDM_DTR|TMXR_MDM_RTS) /* Settable Modem Bits */ /* Unit flags */ #define TMUF_V_NOASYNCH (UNIT_V_UF + 12) /* Asynch Disabled unit */ #define TMUF_NOASYNCH (1u << TMUF_V_NOASYNCH) /* This flag can be defined */ /* statically in a unit's flag field */ /* This will disable the unit from */ /* supporting asynchronmous mux behaviors */ /* Receive line speed limits */ #define TMLN_SPD_50_BPS 200000 /* usec per character */ #define TMLN_SPD_75_BPS 133333 /* usec per character */ #define TMLN_SPD_110_BPS 90909 /* usec per character */ #define TMLN_SPD_134_BPS 74626 /* usec per character */ #define TMLN_SPD_150_BPS 66666 /* usec per character */ #define TMLN_SPD_300_BPS 33333 /* usec per character */ #define TMLN_SPD_600_BPS 16666 /* usec per character */ #define TMLN_SPD_1200_BPS 8333 /* usec per character */ #define TMLN_SPD_1800_BPS 5555 /* usec per character */ #define TMLN_SPD_2000_BPS 5000 /* usec per character */ #define TMLN_SPD_2400_BPS 4166 /* usec per character */ #define TMLN_SPD_3600_BPS 2777 /* usec per character */ #define TMLN_SPD_4800_BPS 2083 /* usec per character */ #define TMLN_SPD_7200_BPS 1388 /* usec per character */ #define TMLN_SPD_9600_BPS 1041 /* usec per character */ #define TMLN_SPD_19200_BPS 520 /* usec per character */ #define TMLN_SPD_38400_BPS 260 /* usec per character */ #define TMLN_SPD_57600_BPS 173 /* usec per character */ #define TMLN_SPD_76800_BPS 130 /* usec per character */ #define TMLN_SPD_115200_BPS 86 /* usec per character */ typedef struct tmln TMLN; typedef struct tmxr TMXR; struct loopbuf { int32 bpr; /* xmt buf remove */ int32 bpi; /* xmt buf insert */ int32 size; }; struct tmln { int conn; /* line connected flag */ SOCKET sock; /* connection socket */ char *ipad; /* IP address */ SOCKET master; /* line specific master socket */ char *port; /* line specific listening port */ int32 sessions; /* count of tcp connections received */ uint32 cnms; /* conn time */ int32 tsta; /* Telnet state */ int32 rcve; /* rcv enable */ int32 xmte; /* xmt enable */ int32 dstb; /* disable Telnet binary mode */ t_bool notelnet; /* raw binary data (no telnet interpretation) */ uint8 *telnet_sent_opts; /* Telnet Options which we have sent a DON'T/WON'T */ int32 rxbpr; /* rcv buf remove */ int32 rxbpi; /* rcv buf insert */ int32 rxbsz; /* rcv buffer size */ int32 rxcnt; /* rcv count */ int32 rxpcnt; /* rcv packet count */ int32 txbpr; /* xmt buf remove */ int32 txbpi; /* xmt buf insert */ int32 txcnt; /* xmt count */ int32 txpcnt; /* xmt packet count */ int32 txdrp; /* xmt drop count */ int32 txbsz; /* xmt buffer size */ int32 txbfd; /* xmt buffered flag */ t_bool modem_control; /* line supports modem control behaviors */ int32 modembits; /* modem bits which are currently set */ FILE *txlog; /* xmt log file */ FILEREF *txlogref; /* xmt log file reference */ char *txlogname; /* xmt log file name */ char *rxb; /* rcv buffer */ char *rbr; /* rcv break */ char *txb; /* xmt buffer */ uint8 *rxpb; /* rcv packet buffer */ uint32 rxpbsize; /* rcv packet buffer size */ uint32 rxpboffset; /* rcv packet buffer offset */ uint32 rxbps; /* rcv bps speed (0 - unlimited) */ double rxbpsfactor; /* receive speed factor (scaled to usecs) */ #define TMXR_RX_BPS_UNIT_SCALE 1000000.0 uint32 rxdelta; /* rcv inter character min time (usecs) */ double rxnexttime; /* min time for next receive character */ uint32 txbps; /* xmt bps speed (0 - unlimited) */ uint32 txdelta; /* xmt inter character min time (usecs) */ double txnexttime; /* min time for next transmit character */ uint8 *txpb; /* xmt packet buffer */ uint32 txpbsize; /* xmt packet buffer size */ uint32 txppsize; /* xmt packet packet size */ uint32 txppoffset; /* xmt packet buffer offset */ TMXR *mp; /* back pointer to mux */ char *serconfig; /* line config */ SERHANDLE serport; /* serial port handle */ t_bool ser_connect_pending; /* serial connection notice pending */ SOCKET connecting; /* Outgoing socket while connecting */ char *destination; /* Outgoing destination address:port */ t_bool loopback; /* Line in loopback mode */ t_bool halfduplex; /* Line in half-duplex mode */ t_bool datagram; /* Line is datagram packet oriented */ t_bool packet; /* Line is packet oriented */ int32 lpbpr; /* loopback buf remove */ int32 lpbpi; /* loopback buf insert */ int32 lpbcnt; /* loopback buf used count */ int32 lpbsz; /* loopback buffer size */ char *lpb; /* loopback buffer */ UNIT *uptr; /* input polling unit (default to mp->uptr) */ UNIT *o_uptr; /* output polling unit (default to lp->uptr)*/ DEVICE *dptr; /* line specific device */ EXPECT expect; /* Expect rules */ SEND send; /* Send input state */ }; struct tmxr { int32 lines; /* # lines */ char *port; /* listening port */ SOCKET master; /* master socket */ TMLN *ldsc; /* line descriptors */ int32 *lnorder; /* line connection order */ DEVICE *dptr; /* multiplexer device */ UNIT *uptr; /* polling unit (connection) */ char logfiletmpl[FILENAME_MAX]; /* template logfile name */ int32 txcount; /* count of transmit bytes */ int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */ int32 sessions; /* count of tcp connections received */ uint32 poll_interval; /* frequency of connection polls (seconds) */ uint32 last_poll_time; /* time of last connection poll */ uint32 ring_start_time; /* time ring signal was raised */ char *ring_ipad; /* incoming connection address awaiting DTR */ SOCKET ring_sock; /* incoming connection socket awaiting DTR */ t_bool notelnet; /* default telnet capability for incoming connections */ t_bool modem_control; /* multiplexer supports modem control behaviors */ t_bool packet; /* Lines are packet oriented */ t_bool datagram; /* Lines use datagram packet transport */ }; int32 tmxr_poll_conn (TMXR *mp); t_stat tmxr_reset_ln (TMLN *lp); t_stat tmxr_detach_ln (TMLN *lp); int32 tmxr_input_pending_ln (TMLN *lp); int32 tmxr_getc_ln (TMLN *lp); t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize); t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte); void tmxr_poll_rx (TMXR *mp); t_stat tmxr_putc_ln (TMLN *lp, int32 chr); t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size); t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte); void tmxr_poll_tx (TMXR *mp); int32 tmxr_send_buffered_data (TMLN *lp); t_stat tmxr_open_master (TMXR *mp, CONST char *cptr); t_stat tmxr_close_master (TMXR *mp); t_stat tmxr_connection_poll_interval (TMXR *mp, uint32 seconds); t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, CONST char *cptr, t_bool async); t_stat tmxr_detach (TMXR *mp, UNIT *uptr); t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); char *tmxr_line_attach_string(TMLN *lp); t_stat tmxr_set_modem_control_passthru (TMXR *mp); t_stat tmxr_clear_modem_control_passthru (TMXR *mp); t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits); t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback); t_bool tmxr_get_line_loopback (TMLN *lp); t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_loopback); t_bool tmxr_get_line_halfduplex (TMLN *lp); t_stat tmxr_set_line_speed (TMLN *lp, CONST char *speed); t_stat tmxr_set_config_line (TMLN *lp, CONST char *config); t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll); t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll); t_stat tmxr_set_console_units (UNIT *rxuptr, UNIT *txuptr); t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); void tmxr_msg (SOCKET sock, const char *msg); void tmxr_linemsg (TMLN *lp, const char *msg); void tmxr_linemsgf (TMLN *lp, const char *fmt, ...); void tmxr_linemsgvf (TMLN *lp, const char *fmt, va_list args); void tmxr_fconns (FILE *st, const TMLN *lp, int32 ln); void tmxr_fstats (FILE *st, const TMLN *lp, int32 ln); t_stat tmxr_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat tmxr_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat tmxr_dscln (UNIT *uptr, int32 val, CONST char *cptr, void *desc); int32 tmxr_rqln (const TMLN *lp); int32 tmxr_tqln (const TMLN *lp); int32 tmxr_tpqln (const TMLN *lp); t_bool tmxr_tpbusyln (const TMLN *lp); t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc); t_stat tmxr_activate (UNIT *uptr, int32 interval); t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime); t_stat tmxr_activate_after_abs (UNIT *uptr, uint32 usecs_walltime); t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval); t_stat tmxr_clock_coschedule_abs (UNIT *uptr, int32 interval); t_stat tmxr_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 ticks); t_stat tmxr_clock_coschedule_tmr_abs (UNIT *uptr, int32 tmr, int32 ticks); t_stat tmxr_change_async (void); t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd); t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp); t_stat tmxr_startup (void); t_stat tmxr_shutdown (void); t_stat tmxr_start_poll (void); t_stat tmxr_stop_poll (void); void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize); #define tmxr_debug(dbits, lp, msg, buf, bufsize) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) _tmxr_debug (dbits, lp, msg, buf, bufsize); } while (0) #define tmxr_debug_msg(dbits, lp, msg) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) sim_debug (dbits, (lp)->mp->dptr, "%s", msg); } while (0) #define tmxr_debug_return(lp, val) do {if (sim_deb && (val) && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_RET & (lp)->mp->dptr->dctrl)) { if ((lp)->rxbps) sim_debug (TMXR_DBG_RET, (lp)->mp->dptr, "Ln%d: 0x%x - Next after: %.0f\n", (int)((lp)-(lp)->mp->ldsc), val, (lp)->rxnexttime); else sim_debug (TMXR_DBG_RET, (lp)->mp->dptr, "Ln%d: 0x%x\n", (int)((lp)-(lp)->mp->ldsc), val); } } while (0) #define tmxr_debug_trace(mp, msg) do {if (sim_deb && (mp)->dptr && (TMXR_DBG_TRC & (mp)->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, mp->dptr, "%s\n", (msg)); } while (0) #define tmxr_debug_trace_line(lp, msg) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_TRC & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, (lp)->mp->dptr, "Ln%d:%s\n", (int)((lp)-(lp)->mp->ldsc), (msg)); } while (0) #define tmxr_debug_connect(mp, msg) do {if (sim_deb && (mp)->dptr && (TMXR_DBG_CON & (mp)->dptr->dctrl)) sim_debug (TMXR_DBG_CON, mp->dptr, "%s\n", (msg)); } while (0) #define tmxr_debug_connect_line(lp, msg) do {if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_CON & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_CON, (lp)->mp->dptr, "Ln%d:%s\n", (int)((lp)-(lp)->mp->ldsc), (msg)); } while (0) #if defined(SIM_ASYNCH_MUX) && !defined(SIM_ASYNCH_IO) #undef SIM_ASYNCH_MUX #endif /* defined(SIM_ASYNCH_MUX) && !defined(SIM_ASYNCH_IO) */ #if defined(SIM_ASYNCH_MUX) #define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, TRUE) #else #define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, FALSE) #endif #if (!defined(NOT_MUX_USING_CODE)) #define sim_activate tmxr_activate #define sim_activate_after tmxr_activate_after #define sim_activate_after_abs tmxr_activate_after_abs #define sim_clock_coschedule tmxr_clock_coschedule #define sim_clock_coschedule_abs tmxr_clock_coschedule_abs #define sim_clock_coschedule_tmr tmxr_clock_coschedule_tmr #define sim_clock_coschedule_tmr_abs tmxr_clock_coschedule_tmr_abs #endif #ifdef __cplusplus } #endif #endif /* _SIM_TMXR_H_ */ |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | /* test.c - Front panel LED and switch testing program, built as pidp8i-test Copyright © 2016 Paul R. Bernard Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the names of the authors above shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from those authors. */ #include "gpio-common.h" #include <assert.h> #include <ctype.h> #include <pthread.h> #include <signal.h> #include <stddef.h> #include <stdlib.h> #include <time.h> typedef unsigned int uint32; typedef unsigned char uint8; static uint16_t lastswitchstatus[3]; // to watch for switch changes uint8 path[] = { 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x4c, 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x87, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x69, 0x6a, 0x6b, 0x6c, 0x71, 0x72, 0x75, 0x74, 0x73 }; #define CHECK(cond,name) while(!(cond)) goto CLN_ ## name; #define CLEANUP(name) if( 0 ) { CLN_ ## name: { #define CLEANUP_END } } else; int terminate=0; void sig_handler( int signo ) { if( signo == SIGINT ) terminate = 1; } int main( int argc, char *argv[] ) { pthread_t thread1; int iret1, row, col, i, chr; int path_idx = 0, led_row = 0, delay = 0; assert(sizeof(lastswitchstatus == switchstatus)); // install handler to terminate future thread if( signal(SIGINT, sig_handler) == SIG_ERR ) { fprintf( stderr, "Failed to install SIGINT handler.\n" ); exit( EXIT_FAILURE ); } // create thread iret1 = pthread_create( &thread1, NULL, blink, &terminate ); if( iret1 ) { fprintf( stderr, "Error creating thread, return code %d\n", iret1 ); exit( EXIT_FAILURE ); } sleep( 2 ); // allow 2 sec for multiplex to start if( !pidp8i_gpio_present ) { fprintf( stderr, "Cannot run the test while another PiDP-8/I program runs.\n"); exit( EXIT_FAILURE ); } fprintf( stdout, "turn on ALL LEDs\n" ); for( row=0; row<nledrows; ++row ) ledstatus[row] = 07777; CHECK( !terminate, TERMINATE ) sleep( 5 ); for( row=0; row<nledrows; ++row ) ledstatus[row] = 0; fprintf( stdout, "turn off ALL LEDs\n" ); CHECK( !terminate, TERMINATE ) sleep( 5 ); for( row=0; row<nledrows; ++row ) { CHECK( !terminate, TERMINATE ) fprintf( stdout, "turning on LED row %d\n", row ); ledstatus[row] = 07777; sleep( 5 ); ledstatus[row] = 0; } for( col=0; col<ncols; ++col ) { CHECK( !terminate, TERMINATE ) fprintf( stdout, "turning on LED col %d\n", col ); for( row=0; row<nledrows; ++row ) ledstatus[row] |= 1<<col; sleep( 5 ); for( row=0; row<nledrows; ++row ) ledstatus[row] = 0; } fprintf( stdout, "Reading the switches. Toggle any pattern desired. CTRL-C to quit.\n" ); for( i=0; i<nrows; ++i ) lastswitchstatus[i] = switchstatus[i]; for( ;; ) { CHECK( !terminate, TERMINATE ) if( delay++ >= 30 ) { delay = 0; ledstatus[led_row] = 0; ledstatus[led_row=(path[path_idx]>>4) - 1] = 04000 >> ((path[path_idx]&0x0F) - 1); if( ++path_idx >= sizeof(path)/sizeof(path[0]) ) path_idx = 0; } if( lastswitchstatus[0]!=switchstatus[0] || lastswitchstatus[1]!=switchstatus[1] || lastswitchstatus[2]!=switchstatus[2] ) { for( i=0; i<nrows; ++i ) { fprintf( stdout, "%04o ", ~switchstatus[i] & 07777 ); lastswitchstatus[i] = switchstatus[i]; } fprintf( stdout, "\n" ); } usleep( 1000 ); } CLEANUP( TERMINATE ) if( pthread_join(thread1, NULL) ) printf( "\r\nError joining multiplex thread\r\n" ); return 0; CLEANUP_END } |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | #!/bin/bash -x # bosi - The Binary OS Image creation/update script # # Copyright © 2016-2017 by Warren Young # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR # THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above shall # not be used in advertising or otherwise to promote the sale, use or # other dealings in this Software without prior written authorization from # those authors. verb="$1" object="$2" tag=$(echo $verb | cut -f2 -d- -s) test -z "$tag" && tag=ils nu=pidp8i nh=/home/$nu repo=pidp8i dldir="$HOME/tangentsoft.com/dl" os=jessie-lite img=$dldir/pidp8i-$(date +%Y.%m.%d)-$tag-$os.img greadlink=$(type -p greadlink || type -p readlink) this=$($greadlink -f $0) # Initial steps function do_init() { sudo apt-get update && sudo apt-get -y upgrade || true test "$USER" = "root" && exec sudo -i -u $nu $nh/bosi init $object cd $HOME test -n "$(type -p fossil)" || sudo apt-get -y install fossil if [ ! -d museum ] then mkdir -p museum $repo fossil clone https://tangentsoft.com/$repo museum/$repo.fossil fi cd $repo if [ -r ChangeLog.md ] then fossil revert # just in case fossil update release else fossil open ~/museum/$repo.fossil release ./configure $object fi tools/mmake sudo make install || true sudo reboot } # This script resets the OS's configuration to a clean first boot state. function do_reset() { if [ "$USER" != "root" ] then echo "The reset step has to be run as root. The explanation is" echo "given in the section for 'reset' in RELEASE-PROCESS.md." echo exit 1 fi history -c ; rm -f ~/.bash_history test "$USER" = "root" || exec sudo $this reset shred -u /etc/ssh/*key* dphys-swapfile uninstall dd if=/dev/zero of=/junk bs=1M || true # it *will* error-out! rm /junk encpass=$(openssl passwd -1 edsonDeCastro1968) if [ -e "$nh" ] then # A prior pass already renamed the default user and moved its # home directory, so just change the password. usermod -p $encpass pidp8i else # First pass on a clean SD card: rename 'pi' user to 'pidp8i', # move its home directory usermod -d $nh -l $nu -p $encpass -m pi fi passwd -e pidp8i ( sleep 1 ; poweroff ) & exit } # Shrink the filesystem on the OS SD card we're about to image to just a # bit bigger than required to hold its present contents. # # The extra 100 megs in the arithmetic below accounts for the /boot # partition, since the `resizepart` command takes a partition end value, # not a size value. # # We don't calculate the actual end of the /boot partition and use that # value because we want a bit of slack space to buy time for an end user # who neglects to expand the card image into the free space available on # their SD card after first boot. function do_shrink() { test "$USER" = "root" || exec sudo $this shrink $object umount /dev/sda2 || true # might auto-mount, might not e2fsck -f /dev/sda2 # resize2fs demands it blocks=$( resize2fs -M /dev/sda2 2>&1 | grep 'blocks long' | grep -wo '[0-9]\+' ) if [ "$blocks" -gt 0 ] then parted /dev/sda resizepart 2 $(($blocks * 4096 + 10**8))b blocks=$( resize2fs /dev/sda2 2>&1 | grep 'blocks long' | grep -wo '[0-9]\+' ) cat <<NEXT Move the USB SD card reader to the desktop machine and resume the process with bosi image $blocks NEXT else echo "Failed to extract new filesystem size from resize2fs!" echo exit 1 fi } # This script images the OS SD card in a USB reader on a Mac OS X box. function do_image() { set +x if [ -n "$object" ] then while read line do case $line in /dev/*) dev=$(echo $line | cut -f1 -d' ') ;; 0:*) case $line in *FDisk_partition_scheme*) ;; *) dev= ;; # can't be the OS SD card esac ;; 1:*) case $line in *Windows_FAT_32\ boot*) ;; *) dev= ;; # can't be the OS SD card esac ;; 2:*) case $line in *Linux\ Untitled*) break ;; # found it! *) dev= ;; # can't be the OS SD card esac ;; esac done < <(diskutil list) if [ -z "$dev" ] then echo "Failed to find OS SD card!" echo exit 1 fi echo echo "-------------------------------------------------------" diskutil info "$dev" echo "-------------------------------------------------------" echo read -p "Is that the OS SD card? [y/N]: " answer case $answer in [Yy]*) ;; *) exit 1 esac set -x sudo diskutil unmountDisk $dev # it auto-mounted sudo dd if=$dev bs=4k count=$object of=$img zip -9j $img.zip $img sudo dd if=$img of=$dev bs=1m sudo diskutil unmountDisk $dev || true # Paragon ExtFS might be installed else usage fi } # Clean up after the above function do_finish() { rmtrash $img cd $dldir/.. make synch } # Display the usage message function usage() { cat <<USAGE usage: $0 <verb[-tag]> [object] The available verbs are init, fossil, shrink, image, and finish. You may append a tag to the image and finish verbs (e.g. image-nls) to override the default tag ('all') used in image and zip file outputs. The object depends on the verb. See RELEASE-PROCESS.md. USAGE exit 1 } # Main routine set -e case "$verb" in in*) do_init ;; re*) do_reset ;; sh*) do_shrink ;; im*) do_image ;; fi*) do_finish ;; *) usage ;; esac |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #!/bin/bash ######################################################################## # corecount - Prints the number of CPU cores found on this system # # Copyright © 2017 by Warren Young # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR # THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above shall # not be used in advertising or otherwise to promote the sale, use or # other dealings in this Software without prior written authorization from # those authors. ######################################################################## sys="$(uname -s)" if [ -r /proc/cpuinfo ] then # It's a Linux box grep -Ec '^processor\s+:' /proc/cpuinfo elif [ "$sys" = "Darwin" -o "$sys" = "FreeBSD" -o "$sys" = "OpenBSD" -o "$sys" = "NetBSD" ] then # It's a macOS or BSD box /usr/sbin/sysctl -n hw.ncpu else # No known way to find out, so report only 1 core echo 1 fi |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | #!/usr/bin/env perl ######################################################################## # mkbootscript - Generate boot/*.script from obj/*.lst. See the usage # message below for more details. # # Copyright © 2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## use strict; use warnings; use File::Basename; # Parse command line my $outPath = '../boot'; if (@ARGV == 0 || ! -r $ARGV[0]) { print <<USAGE; usage: $0 <somefile.lst> Given a palbart listing file, transform its contents into a SIMH boot script named after the listing file. Comments in the listing file are variously translated to either comments or echo statements in the boot script. Output is stored in $outPath/somefile.script relative to the listing file, as makes sense when translating obj/*.lst files produced from examples/*.pal files. USAGE exit 1; } # Globals my $keepComments = 1; # keep header comments; ignore the rest my (@comments, @directives); my $firstAddr; my %core; my $inFile = $ARGV[0]; my $bni = basename($inFile); my $outFile = join('/', ( dirname($inFile), $outPath, $bni )); $outFile =~ s{\.lst$}{.script}; my $oneLiner = $bni; # use input file name as one-liner fall-back # Parse the input file my $comment; open my $lst, '<', $inFile or die "Cannot read $inFile: $!\n"; while (<$lst>) { chomp; my ($line, $addr, $val, $tail) = m{ ^\s+ # ignore leading whitespace (\d+\s+) # first number on the line must be a line number (\d+)? # if followed by another number, it's an address \s* # ignore space between addr and val (\d+)? # ...and the value to store at that address (.*) # everything else on the line $ }x; my ($lineIsEmpty) = m{^\s+\d+\s*$}; next unless $lineIsEmpty || $tail; ($comment) = $tail =~ m{/\s*(.*)$} if $tail; if (defined $line and int($line) == 1 and defined $comment) { # Save the comment on the first line as a one-line description # of what the program does, emitted to the console by SIMH when # running our output script unless we find a "SIMH: echo..." # directive later on in that file, which overrides it. $oneLiner = $comment; $oneLiner =~ s{ - }{: }; $oneLiner =~ s{([\w-]+).pal}{"$1" example}; } elsif ($keepComments and defined $lineIsEmpty) { # The first blank line in the input file will appear in the # listing as a file containing only a line number. Stop saving # comments, since everything after this would be comments on # individual source lines, which don't get copied into output. $keepComments = 0; } elsif (defined $addr and defined $val) { # Save address and value to our core image $firstAddr = oct($addr) if not defined $firstAddr; $core{oct($addr)} = oct($val); } elsif ($keepComments and defined $comment and defined $line) { # It's a header comment line if ($comment =~ m{^SIMH: }) { # It's a directive to SIMH, so save it separately push @directives, substr($comment, 6); } else { # Nothing special, so just save the text portion push @comments, $comment; } } } close $lst; # Remove leading and trailing blank comment lines while (@comments and length($comments[0]) == 0) { shift @comments; } while (@comments and length($comments[$#comments]) == 0) { pop @comments; } # Write parsed data into output file open my $scr, '>', $outFile or die "Cannot write $outFile: $!\n"; for my $c (@comments) { print $scr "; $c\n"; } print $scr ";\n"; my $foundEcho; for my $d (@directives) { print $scr $d, "\n"; $foundEcho = 1 if $d =~ m{^echo }; } print $scr "echo Running $oneLiner...\n" unless $foundEcho; for my $a (sort { $a <=> $b } keys %core) { printf $scr "dep %05o %04o\n", $a, $core{$a}; } printf $scr "go %05o\n", $firstAddr; close $scr; print "Converted $inFile to $outFile\n"; |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #!/bin/bash ######################################################################## # mkrel - Automatically merge trunk changes into the release branch # for a new public release of the software. Also tags the trunk with # the date of release, so old releases can be easily checked out. # # Copyright © 2016-2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## set -e tag=v$(date +%Y%m%d) fossil update && fossil ci --tag $tag -m "Tagged release $tag" && cd ../release && fossil update && fossil merge $tag && ! fossil status | grep -q '^CONFLICT' && tools/mmake && fossil diff -w | less && fossil ci -m "Merged trunk changes for $tag into release branch" |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #!/bin/bash ######################################################################## # mmake - Runs make -jN where N is 1.5x the number of CPU cores # # Copyright © 2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## make -j$(($("$(dirname "$0")"/corecount) * 15 / 10)) "$@" |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | #!/bin/bash ######################################################################## # simh-update - Attempt to automatically merge in the latest upstream # SIMH 4 changes from the GitHub repository. # # Copyright © 2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## SRCDIR="@srcdir@" SRCSUBDIR="$SRCDIR/src" PRGNAME="$(basename "$0")" WORKDIR="@builddir@/$PRGNAME-temp" OUR_SIMH_DIR="$WORKDIR/simh/ours" CURR_SIMH_DIR="$WORKDIR/simh/curr" LOGFILE="$WORKDIR/output.log" PATCHFILE="$WORKDIR/pidp8i.patch" OLD_SGCID=$(grep ^SGCID "@srcdir@"/Makefile.in | cut -f2 -d=) rm -rf "$WORKDIR" mkdir -p "$WORKDIR" # From here on, send all output to the log file. # Code based on http://stackoverflow.com/a/20564208/142454 exec 1<&- exec 2<&- exec 1<>"$LOGFILE" exec 2>&1 function say() { echo "$@" >> /dev/tty } # Bail on errors so we don't have to test everything. Ideally, nothing # from here on will fail. If it does, the log file will explain it. # Code based on http://stackoverflow.com/a/185900/142454 function error() { lineno="$1" message="$2" code="${3:-1}" if [ -n "$message" ] ; then message=": $message" ; fi read -r -d '%' errmsg <<ERROR Error on or near line $lineno, code ${code}$message! (See log file for more info: $LOGFILE) % ERROR say say "$errmsg" say exit $code } trap 'error ${LINENO}' ERR # Deal with uncommitted changes in $SRCSUBDIR cd "$SRCSUBDIR/.." # we need the src/ prefix to do this test properly! if [ $(fossil status | grep '^EDITED.*src/' | wc -l) -gt 0 ] then if [ "$1" = "-f" ] then say "Tossing uncommitted changes in $SRCSUBDIR..." fossil revert $(fossil status | grep '^EDITED.*src/' | cut -f 2- -d' ') shift else read -r -d '%' errmsg <<ERROR Cowardly refusing to update SIMH to the current upstream version while there are uncommitted changes in $SRCSUBDIR. Say make simh-update-f or pass -f to force those changes to be tossed. % ERROR say say "$errmsg" say exit 3 fi fi cd "@builddir@" # Retreive the tip-of-master and $OLD_SGCID versions of SIMH say Retreiving upstream SIMH versions... mkdir -p "$OUR_SIMH_DIR" mkdir -p "$CURR_SIMH_DIR" git clone https://github.com/simh/simh "$CURR_SIMH_DIR" pushd "$CURR_SIMH_DIR" NEW_SGCID=$(git rev-parse HEAD) git worktree add "$OUR_SIMH_DIR" $OLD_SGCID popd # Copy over updated versions of the docs and replace them in the Fossil # unversioned area. We simplify the upstream naming scheme in the # transfer, dropping unnecessary prefixes and suffixes. pushd "$SRCDIR" for fs in pdp8_doc simh_faq simh_doc do ft=$(echo $fs | sed -e 's/simh_//' -e 's/_doc//') test "$ft" = "doc" && ft=main doc="doc/simh/$ft.doc" cp "$CURR_SIMH_DIR/doc/$fs.doc" "$doc" fossil uv add "$doc" done fossil uv sync popd # Rename upstream Git paths to match our *.in files so that our produced # patches are made against those higher-level versions. If we didn't do # this, we'd have to manually resubstitute variables for absolute paths. say Renaming upstream files to match our \*.in variants... find "$SRCSUBDIR" -name \*.in -print | while read f do inf="${f#$SRCSUBDIR/}" # make path fully relative genf="${inf%.in}" # remove .in from the end mv "$OUR_SIMH_DIR/$genf" "$OUR_SIMH_DIR/$inf" mv "$CURR_SIMH_DIR/$genf" "$CURR_SIMH_DIR/$inf" done # Produce a patch file for modifying the upstream $OLD_SGCID version to # merge in our local changes. # # For some reason, diff(1) returns an error when we do this, at least on # OS X. Perhaps it is not happy about the fact that the file set in # each tree is different? Regardless of reason, we must check for a # non-empty patch file to determine whether an actual error occurred. say Producing clean patch file from upstream ${OLD_SGCID:0:8} version say to our local PiDP-8/I version... if ! diff -ru "$OUR_SIMH_DIR" "$SRCSUBDIR" | grep -v '^Only in' > "$PATCHFILE" && [ ! -s "$PATCHFILE" ] then error $LINENO "patch generation failed" 2 fi # For each file in src that is also present in the current upstream # version of SIMH, overwrite our version. find "$SRCSUBDIR" -type f -print | while read f do base="${f#$SRCSUBDIR}" upstream="$CURR_SIMH_DIR/$base" test -e "$upstream" && cp "$upstream" "$f" done # Now try to apply the patch we made above to the upstream files. patch -p0 < "$PATCHFILE" # No error, so save the new tip-of-master Git commit ID and rebuild say "Patch appears to have applied cleanly. Attempting a rebuild..." sed -e "s/^SGCID=.*/SGCID=$NEW_SGCID/" -i tmp @srcdir@/Makefile.in tools/mmake # Restore stdout and let the human test it say "Build completed without error. Starting OS/8 in the simulator..." say say "(Nuke $WORKDIR when finished.)" say exec 1<&- exec 1<>/dev/tty exec make os8test |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | #!/usr/bin/env perl ######################################################################## # tools/version - Print a string summarizing the software version # # Copyright © 2017 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the names of the authors above # shall not be used in advertising or otherwise to promote the sale, # use or other dealings in this Software without prior written # authorization from those authors. ######################################################################## use strict; use warnings; use Cwd 'abs_path'; use File::Basename; my $topdir = dirname($0) . '/..'; if (not -e "$topdir/.fslckout") { # We're not within a Fossil checkout, so try to get the version # number from the top directory name. $topdir = basename(abs_path($topdir)); my ($v) = $topdir =~ m{v\d+}; print 'pkg:', ($v ? $v : 'vUNKNOWN'), "\n"; exit 0; } # Get version info from Fossil my ($branch, $checkout, $version, $comment); open my $s, '-|', 'fossil status'; while (<$s>) { chomp; my ($attr, $value) = split /:\s+/; if ($attr eq 'checkout') { my @elems = split ' ', $value; $checkout = substr($elems[0], 0, 8); } elsif ($attr eq 'tags') { my @tags = split /, /, $value; for my $t (@tags) { if ($t =~ m{^v\d+}) { $version = $t; } else { $branch = $t; } } } elsif ($attr eq 'comment') { $comment = $value; } } close $s; ($version) = $comment =~ m{(v\d+)} if $branch eq 'release'; print $branch, ':', ($version || "i$checkout"); |