Index: HACKERS.md ================================================================== --- HACKERS.md +++ HACKERS.md @@ -435,23 +435,30 @@ (Contrast `bin` which does have some files checked into Fossil; all of the *other* files that end up in `bin` can be recreated by `make`, but not these few hand-written programs.) * `src` - Source code for the project's programs, especially - those that cannot be used until they are built. The build system's - output directories are `bin`, `boot`, `libexec`, and `obj`. + those that cannot be used until they are built. The great majority + of these programs are written in C. The build system's output + directories are `bin`, `boot`, `libexec`, and `obj`. - Programs that can be used without being "built", example programs, + Programs that can be used without being "built," example programs, and single-file scripts are placed elsewhere: `bin`, `examples`, - `libexec`, `tools`, etc. Basically, we place such files where the + `libexec`, `tools`, etc. Basically, we place such files where the build system *would* place them if they were built from something under `src`. - The top level of `src` is for the SIMH core, with the PDP-8 - simulator specific bits in the `PDP8` subdirectory. + There are no program sources in the top level of `src`. The file + `src/config.h` may appear to be an exception to that restriction, + but it is *generated output* of the `configure` script, not "source + code" *per se*. + + Multi-module programs each have their own subdirectory of `src`, + each named after the program contained within. - The subdirectories of `src` are for other programs' source code. + Single module programs live in `src/misc` or `src/asm`, depending on + whether they are host-side C programs or PAL8 assembly programs. * `test` - Output directory used by `tools/test-*`. * `tools` - Programs run only during development and not installed. Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -5,11 +5,11 @@ # 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 +# Copyright © 2015 by Oscar Vermeulen, © 2016-2018 by Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, @@ -34,25 +34,26 @@ # 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=27f9fc3c3e7ff105c6102dfffc6da7fb838d1e8f +SGCID=097ebbb8ebf262c80c39f0d52a8880551abf55bf -# C build flags for src/*.c and PDP8/*.c, containing the SIMH core -# modules and the SIMH PDP-8 simulator, respectively. -TOP_CFLAGS := @CFLAGS@ @BUILDMODE@ \ +# C build flags for the PDP-8 simulator and its PiDP-8/I extensions. +SIM_CFLAGS := @CFLAGS@ @BUILDMODE@ \ -Wno-unused-result -Wno-parentheses \ -DUSE_READER_THREAD -DHAVE_DLOPEN=$(subst .,,@SH_SOEXT@) -DPIDP8I \ -DSIM_ASYNCH_IO -DHAVE_REGEX_H -DHAVE_GLOB \ -DSIM_GIT_COMMIT_ID=$(SGCID) -D_GNU_SOURCE \ -U__STRICT_ANSI__ \ - -I @srcdir@/src -I @srcdir@/src/PDP8 -I src -PDP8_CFLAGS = $(TOP_CFLAGS) + -I @srcdir@/src/SIMH -I @srcdir@/src/pidp8i -I src -I src/SIMH -I src/pidp8i +PIDP8I_CFLAGS = $(SIM_CFLAGS) +SIMH_CFLAGS = $(SIM_CFLAGS) +SIMH_PDP8_CFLAGS = $(SIM_CFLAGS) # Greatly stripped-down build options for the cc8 cross-compiler -# primarily because it's K&R C. Building under TOP_CFLAGS spews +# primarily because it's K&R C. Building under SIM_CFLAGS spews # pages of warnings. The only thing we share is whether to build # a debug or optimized version, from BUILDMODE, set by auto.def. CC8_CROSS_CFLAGS = -w @BUILDMODE@ # Krten's d8tape doesn't need much in the way of options. It builds @@ -59,11 +60,14 @@ # without warnings under the supported version of Raspbian. On some # other OS types, it complains about one of the printf() format # specifiers, which we cannot fix portably, so we suppress that one. D8TAPE_CFLAGS = -Wno-format @BUILDMODE@ -# Ditto palbart. +# Simpler options for src/misc/*.c +MISC_CFLAGS = -I @srcdir@/src/pidp8i -I src/pidp8i @BUILDMODE@ -std=c99 + +# Even simpler options for palbart. PALBART_CFLAGS = @BUILDMODE@ SIM = bin/pidp8i-sim BINS = $(SIM) @CC8_CROSS@ \ bin/d8tape \ @@ -95,46 +99,57 @@ $(MKOS8_LIB)/argparser.py \ lib/mkos8/opts.py MKOS8_PY_ALL := $(GENNED_PY) $(MKOS8_PY) $(SIMH_PY_SRC) MKOS8_SRCS := $(MKOS8) $(MKOS8_PY_ALL) $(PIDP8I_DIN) -BUILDDIRS := bin libexec obj/cc8/cross obj/d8tape obj/palbart obj/PDP8 +# If you edit this directory list, you should probably also edit +# the argument list to the mkadrules call in auto.def! +BUILDDIRS := \ + bin \ + libexec \ + obj/cc8/cross \ + obj/d8tape \ + obj/misc \ + obj/palbart \ + obj/pidp8i \ + obj/SIMH \ + obj/SIMH/PDP8 INSTDIRS := bin etc lib/mkos8 lib/pidp8i libexec share/boot share/media share/include share/man/man1 SIM_OBJS := \ - obj/gpio-common.o \ - obj/PDP8/pdp8_df.o \ - obj/PDP8/pdp8_cpu.o \ - obj/PDP8/pdp8_clk.o \ - obj/PDP8/pdp8_ct.o \ - obj/PDP8/pdp8_dt.o \ - obj/PDP8/pdp8_fpp.o \ - 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_console.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 \ - obj/sim_video.o + obj/pidp8i/gpio-common.o \ + obj/pidp8i/main.o \ + obj/SIMH/PDP8/pdp8_df.o \ + obj/SIMH/PDP8/pdp8_cpu.o \ + obj/SIMH/PDP8/pdp8_clk.o \ + obj/SIMH/PDP8/pdp8_ct.o \ + obj/SIMH/PDP8/pdp8_dt.o \ + obj/SIMH/PDP8/pdp8_fpp.o \ + obj/SIMH/PDP8/pdp8_lp.o \ + obj/SIMH/PDP8/pdp8_mt.o \ + obj/SIMH/PDP8/pdp8_pt.o \ + obj/SIMH/PDP8/pdp8_rf.o \ + obj/SIMH/PDP8/pdp8_rk.o \ + obj/SIMH/PDP8/pdp8_rl.o \ + obj/SIMH/PDP8/pdp8_rx.o \ + obj/SIMH/PDP8/pdp8_sys.o \ + obj/SIMH/PDP8/pdp8_td.o \ + obj/SIMH/PDP8/pdp8_tsc.o \ + obj/SIMH/PDP8/pdp8_tt.o \ + obj/SIMH/PDP8/pdp8_ttx.o \ + obj/SIMH/scp.o \ + obj/SIMH/sim_console.o \ + obj/SIMH/sim_disk.o \ + obj/SIMH/sim_ether.o \ + obj/SIMH/sim_fio.o \ + obj/SIMH/sim_serial.o \ + obj/SIMH/sim_sock.o \ + obj/SIMH/sim_tape.o \ + obj/SIMH/sim_timer.o \ + obj/SIMH/sim_tmxr.o \ + obj/SIMH/sim_video.o CC8_OBJS := \ obj/cc8/cross/code8.o \ obj/cc8/cross/data.o \ obj/cc8/cross/error.o \ @@ -155,13 +170,13 @@ obj/d8tape/flow.o \ obj/d8tape/main.o \ obj/d8tape/version.o MISC_OBJS := \ - obj/ptp2txt.o \ - obj/scanswitch.o \ - obj/test.o + obj/misc/ptp2txt.o \ + obj/misc/scanswitch.o \ + obj/misc/test.o PALBART_OBJS := obj/palbart/palbart.o ifeq (@BUILD_DEEPER_THOUGHT@, 1) BINS += bin/deeper @@ -200,18 +215,19 @@ # The rest have no special treatment. MKOS8_INFILES = \ @srcdir@/lib/pidp8i/__init__.py.in \ @srcdir@/lib/pidp8i/ips.py.in \ @srcdir@/media/os8/init.tx.in \ - @srcdir@/src/PDP8/pidp8i.c.in \ + @srcdir@/src/pidp8i/main.c.in \ $(PIDP8I_DIN) PRECIOUS_INFILES = \ @srcdir@/Makefile.in \ @srcdir@/examples/Makefile.in \ @srcdir@/src/Makefile.in \ @srcdir@/src/cc8/Makefile.in \ - @srcdir@/src/PDP8/Makefile.in + @srcdir@/src/SIMH/Makefile.in \ + @srcdir@/src/SIMH/PDP8/Makefile.in INFILES = \ @srcdir@/bin/pidp8i.in \ @srcdir@/boot/0.script.in \ @srcdir@/boot/2.script.in \ @srcdir@/boot/3.script.in \ @@ -220,11 +236,11 @@ @srcdir@/boot/7.script.in \ @srcdir@/boot/run.script.in \ @srcdir@/etc/pidp8i-init.in \ @srcdir@/etc/sudoers.in \ @srcdir@/etc/usb-mount@.service.in \ - @srcdir@/src/gpio-common.c.in \ + @srcdir@/src/pidp8i/gpio-common.c.in \ @srcdir@/tools/simh-update.in \ $(MKOS8_INFILES) MKOS8_OUTFILES := $(subst @srcdir@/,,$(MKOS8_INFILES)) MKOS8_OUTFILES := $(subst .in,,$(MKOS8_OUTFILES)) PRECIOUS_OUTFILES := $(subst @srcdir@/,,$(PRECIOUS_INFILES)) @@ -273,11 +289,11 @@ install: all instdirs @echo Installing to @prefix@... @# Install files into those dirs and set their perms @for f in $(BINS) ; do \ - dest=@prefix@/bin/$$(basename $$f) ; \ + dest=@prefix@/$$f ; \ echo "Installing binary $$dest..." ; \ @INSTALL@ -m 755 -D -s $$f $$dest ; \ done @for f in $(BIN_SCRIPTS) ; do \ dest=@prefix@/bin/$$(basename $$f) ; \ @@ -417,10 +433,13 @@ @srcdir@/tools/mkrel run: $(SIM) $(SIM) boot/run.script +run-tss8: $(SIM) + $(SIM) boot/tss8.script + simh-update simh-update-f: @@srcdir@/tools/simh-update $(subst simh-update,,$@) test-mkos8: tools/test-mkos8 @@ -495,11 +514,11 @@ ln -f $< $@ $(BUILDDIRS): mkdir -p $@ -$(SIM): $(SIM_OBJS) obj/gpio-@LED_DRIVER_MODULE@ls.o +$(SIM): $(SIM_OBJS) obj/pidp8i/gpio-@LED_DRIVER_MODULE@ls.o $(CC) -o $@ $^ $(LIBS) bin/cc8: $(CC8_OBJS) $(CC) -o $@ $^ $(LIBS) @@ -507,24 +526,24 @@ $(CC) -o $@ $^ bin/palbart: $(PALBART_OBJS) $(CC) -o $@ $^ -bin/pidp8i-test: obj/test.o obj/gpio-nls.o obj/gpio-common.o +bin/pidp8i-test: obj/misc/test.o obj/pidp8i/gpio-nls.o obj/pidp8i/gpio-common.o $(CC) -o $@ $^ $(LIBS) -lncurses -bin/ptp2txt: obj/ptp2txt.o +bin/ptp2txt: obj/misc/ptp2txt.o $(CC) -o $@ $^ ln -f bin/ptp2txt bin/txt2ptp bin/txt2ptp: bin/ptp2txt ln -f bin/ptp2txt bin/txt2ptp -bin/deeper: obj/deeper.o obj/gpio-@LED_DRIVER_MODULE@ls.o obj/gpio-common.o +bin/deeper: obj/deeper.o obj/pidp8i/gpio-@LED_DRIVER_MODULE@ls.o obj/pidp8i/gpio-common.o $(CC) -o $@ $^ $(LIBS) -libexec/scanswitch: obj/scanswitch.o obj/gpio-nls.o obj/gpio-common.o +libexec/scanswitch: obj/misc/scanswitch.o obj/pidp8i/gpio-nls.o obj/pidp8i/gpio-common.o $(CC) -o $@ $^ $(LIBS) # Reconfigure whenever one of the *.in or autosetup files changes unless # this is "make clean". # @@ -543,11 +562,11 @@ @AUTOREMAKE@ && $(MAKE) endif # Rebuild simulator if the version string tool changes, since its output # may have changed. -src/gpio-common.c: @srcdir@/tools/version +src/pidp8i/gpio-common.c: @srcdir@/tools/version # Pull in *.d files generated by the autodependency mechanism. See the # header comment of tools/mkadrules. -include \ $(SIM_OBJS:.o=.d) \ Index: README-throttle.md ================================================================== --- README-throttle.md +++ README-throttle.md @@ -172,10 +172,46 @@ Therefore, you should only use the ratio form of throttle value when you need to get under 1000 instructions per second. For any value over that, give `--throttle=1k` or higher, which allows the ILS feature to properly maintain its LED brightness values. + +## Throttle Stabilization + +In early January 2018, the [upstream SIMH v4 project][simh] changed the +way throttling is handled in that the simulator doesn't make any +decisions about whether your requested throttle value is plausible until +some seconds after the simulator starts. + +The SIMH default for this is 20 seconds, since the default must work for +all simulators in the SIMH family, some of which have long bootup +cycles. 20 seconds is far too long for a PDP-8, which boots instantly, +so we've overridden that in the stock `boot/*.script` files, setting the +throttle calibration delay to 3 seconds in order to give the SIMH timing +code a sufficiently long baseline to work from. + +For those first 3 seconds, the simulator runs *unthrottled*, after which +the SIMH core timing code looks at the number of instructions executed +during that time and then determines from that what timing values it +needs to use to achieve your requested throttle value. It also checks +whether the throttle value is even possible at this time. + +There is one case where we anticipate that you might want to increase +this value: you've set a fixed throttle value that is right near the +host CPU's ability to achieve and you have the simulator set to start at +host boot time, so that there are lots of processes starting up and +initializing in parallel with the PiDP-8/I simulator. In that case, you +might want to override the following line in `boot/*.script`, setting a +long enough value for the system load to stabilize: + + deposit int-throttle THROT_DELAY 15 + +That would override the 20-second stabilization time default to 15 +seconds. + +[simh]: https://github.com/simh/simh/issues/508#issuecomment-359855788 + ## I/O Matters The throttle mechanism discussed above only affects the speed of the PDP-8 CPU simulator. It does not affect the speed of I/O operations. Index: README.md ================================================================== --- README.md +++ README.md @@ -25,38 +25,17 @@ * A working C compiler and other standard Linux build tools, such as `make(1)`. * Python's `pexpect` library - * The `usbmount` tool - - This is provides two things: - - * USB stick auto-mounting on stripped-down OSes like Raspbian - Lite so you can use the PiDP-8/I `SING_STEP` + `DF` feature - without having to manually mount the USB stick first. - - * A known directory structure that allows the PiDP-8/I - software to find the media image files on those sticks. - (`*.pt`, `*.dt`, `*.rk`, etc.) - - Full-blown GUI OSes tend to have USB auto-mounting set up - already, though they won't meet the second criteria unless they - use the same directory layout as `usbmount`: `/media/usbN`, - where `N` is a number from 0 to 7, depending on the order - you attached the USB stick. Many Linuxes use `/media/LABEL` - instead, for example, where `LABEL` is the partition's label; - the PiDP-8/I software won't find the files on those USB sticks - in that case. - * The `ncurses` development libraries To install all of this on a Raspbian type OS, say: $ sudo apt update $ sudo apt install build-essential - $ sudo apt install libncurses-dev python-pip usbmount + $ sudo apt install libncurses-dev python-pip $ sudo pip install pexpect [os]: https://tangentsoft.com/pidp8i/wiki?name=OS+Compatibility @@ -649,11 +628,11 @@ the OS configuration or saved personal files to the disk the OS boots from, which in turn modifies this media image file out in the host operating environment. There is an important exception here: when upgrading from v20170404 - to v201712xx or newer, the old `os8.rk05` disk image will be left + to v20171222 or newer, the old `os8.rk05` disk image will be left untouched per the above, but because `os8v3d-*.rk05` does not exist yet, those will be copied alongside `os8.rk05`. However, the next item still holds, so that the simulator will continue to use `os8.rk05` because it will be booted by the preexisting scripts. Index: auto.def ================================================================== --- auto.def +++ auto.def @@ -1,10 +1,10 @@ ######################################################################## # auto.def - Configure file for the PiDP-8/I software build system, # based on autosetup. # -# Copyright © 2016-2017 Warren Young +# Copyright © 2016-2018 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, @@ -433,12 +433,23 @@ # Generate autodependency Makefile rule sets. # # It is important to list "src" last, because GNU make takes the first # one that matches, and the wildcards in the generated rules for "src" # match all "src/*" subdirs. +# +# If you edit this directory list, you should probably also edit +# BUILDDIRS in Makefile.in! set status [catch { - exec $srcdir/tools/mkadrules $srcdir src/cc8/cross src/d8tape src/palbart src/PDP8 src + exec $srcdir/tools/mkadrules \ + $srcdir \ + src/cc8/cross \ + src/d8tape \ + src/misc \ + src/palbart \ + src/pidp8i \ + src/SIMH \ + src/SIMH/PDP8 } result] if {$status == 0} { msg-result $result } else { user-error "Failed to generate autodependency rules: $result!" @@ -458,10 +469,11 @@ make-template boot/3.script.in make-template boot/4.script.in make-template boot/6.script.in make-template boot/7.script.in make-template boot/run.script.in +make-template boot/tss8.script.in make-template etc/pidp8i-init.in make-template etc/sudoers.in make-template etc/usb-mount@.service.in make-template examples/Makefile.in make-template lib/pidp8i/__init__.py.in @@ -469,10 +481,11 @@ make-template lib/pidp8i/ips.py.in make-template media/os8/init.tx.in make-template src/Makefile.in make-template src/cc8/Makefile.in make-template src/cc8/os8/Makefile.in -make-template src/gpio-common.c.in -make-template src/PDP8/Makefile.in -make-template src/PDP8/pidp8i.c.in +make-template src/pidp8i/gpio-common.c.in +make-template src/pidp8i/main.c.in +make-template src/SIMH/Makefile.in +make-template src/SIMH/PDP8/Makefile.in make-template tools/simh-update.in exec chmod +x "$builddir/tools/simh-update" Index: bin/cc8-to-os8 ================================================================== --- bin/cc8-to-os8 +++ bin/cc8-to-os8 @@ -14,11 +14,11 @@ # Or you can just give it one or more file name(s), with output going # to stdout, which gives this way to hack around the lack of #include: # # $ cc8-to-os8 myheader.h foo.c > OS8FOO.C # -# Copyright © 2017 Warren Young +# Copyright © 2017-2018 Warren Young # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, @@ -47,16 +47,11 @@ use warnings; my $nblines = 0; # number of non-blank lines written out while (<>) { - if (m{^[a-zA-Z_][a-zA-Z0-9_]*\s*\(}) { - # Looks like a function definition without a type; assume int. - print "int $_"; - ++$nblines; - } - elsif (!m{^\#include } and length and $nblines) { + if (!m{^\#include } and length and $nblines) { # Pass it through unchanged. print; } else { # Unlike the CC8 cross-compiler, the OS/8 version of CC8 doesn't Index: boot/0.script.in ================================================================== --- boot/0.script.in +++ boot/0.script.in @@ -25,10 +25,14 @@ echo Loading OS/8 from the RK05 cartridge disk... set cpu 32k set cpu noidle set df disabled @SET_THROTTLE@ + +; Override the default simulator throttle stabilization time. See +; README-throttle.md for details. +deposit int-throttle THROT_DELAY 3 @if SIMH_PASS_LOWERCASE ; The software was configured with either --lowercase=auto or =pass, ; so send all text input to the simulator as 7-bit ASCII, including ; lowercase. Lowercase output from the simulator will be sent to the Index: boot/2.script.in ================================================================== --- boot/2.script.in +++ boot/2.script.in @@ -6,8 +6,9 @@ load @MEDIADIR@/tss8/tss8_init.bin set rf enabled set df disabled set cpu noidle @SET_THROTTLE@ +deposit int-throttle THROT_DELAY 3 attach rf @MEDIADIR@/tss8/tss8_rf.dsk attach ttix 4000 run 24200 Index: boot/3.script.in ================================================================== --- boot/3.script.in +++ boot/3.script.in @@ -5,7 +5,8 @@ set df disabled set dt disabled set td enabled set cpu noidle @SET_THROTTLE@ +deposit int-throttle THROT_DELAY 3 attach td0 @MEDIADIR@/os8/os8.tu56 boot td0 Index: boot/4.script.in ================================================================== --- boot/4.script.in +++ boot/4.script.in @@ -18,10 +18,11 @@ echo things. See the SimH manual for details. echo l @MEDIADIR@/spacewar/spacewar.bin set cpu noidle @SET_THROTTLE@ +deposit int-throttle THROT_DELAY 3 set df disabled at ttix 2222 set ttox0 8b g 200 Index: boot/6.script.in ================================================================== --- boot/6.script.in +++ boot/6.script.in @@ -3,10 +3,11 @@ echo Loading ETOS from the RK05 cartridge disk drive... reset set cpu 32k set cpu noidle @SET_THROTTLE@ +deposit int-throttle THROT_DELAY 3 set df disabled set tsc enabled attach ttix 4000 att rk0 @MEDIADIR@/etos/etosv5b-demo.rk05 boot -d rk0 Index: boot/run.script.in ================================================================== --- boot/run.script.in +++ boot/run.script.in @@ -6,10 +6,11 @@ echo Loading OS/8 from the RK05 cartridge disk in the build directory... set cpu 32k set cpu noidle set df disabled @SET_THROTTLE@ +deposit int-throttle THROT_DELAY 3 @if SIMH_PASS_LOWERCASE set tti 7b @else set tti ksr ADDED boot/tss8.script.in Index: boot/tss8.script.in ================================================================== --- /dev/null +++ boot/tss8.script.in @@ -0,0 +1,19 @@ +; Same as 2.script, except that the binary disk image and core init +; image files are copied into the build tree's bin/ directory and run +; from there rather than being run from the installation tree. +; +; This is used by "make run-tss8" so you don't have to say +; "make mediainstall" then manually start the simulator with 2.script. +; +echo Loading TSS/8 from the RS08 fixed-head disk... +!cp media/tss8/tss8_init.bin bin +load bin/tss8_init.bin +set rf enabled +set df disabled +set cpu noidle +@SET_THROTTLE@ +deposit int-throttle THROT_DELAY 3 +!cp -n media/tss8/tss8_rf.dsk bin +attach rf bin/tss8_rf.dsk +attach ttix 4000 +run 24200 Index: doc/RELEASE-PROCESS.md ================================================================== --- doc/RELEASE-PROCESS.md +++ doc/RELEASE-PROCESS.md @@ -13,19 +13,22 @@ Or reclassify them, of course. Most of the bug levels simply aid scheduling: Immediate priority bugs should be fixed before High, etc. Low priority bugs are "someone should -fix this someday" type of problems, so they can move from release to -release. +fix this someday" type of problems; these are the only ones allowed to +move from release to release. Think of bugs at this level as analogous +to the `BUGS` section of a Unix man page: our "low" intent to fix these +problems means they may stay at this level indefinitely, acting only as +advisories to the software's users. The Features levels may be read as: * **Immediate**: ASAP, or sooner. :) -* **High**: Features for this release. -* **Medium**: Features we'll look at lifting individually to High for - the next release. +* **High**: Features for the upcoming release. +* **Medium**: Features we'll look at lifting to High for the release + after that. * **Low**: "Wouldn't it be nice if..." ## Update SIMH Index: doc/class-simh.md ================================================================== --- doc/class-simh.md +++ doc/class-simh.md @@ -9,12 +9,12 @@ achieve the same ends! When someone on the mailing list asked for a way to automatically drive a demo script he'd found online, it was natural to generalize the core functionality of `mkos8` as a reusable Python class, then write a script -to make use of it. I called resulting demo script `teco-pi-demo` and the -reusable bits `class simh`, which is now shared with `mkos8`. +to make use of it. The result is `class simh`, currently used by three +different scripts, including `mkos8` and the `teco-pi-demo` demo script. This document describes how `teco-pi-demo` works, and through it, how `class simh` works, with an eye toward teaching you how to reuse this functionality for your own ends. Index: doc/os8-macrel.md ================================================================== --- doc/os8-macrel.md +++ doc/os8-macrel.md @@ -36,11 +36,11 @@ Very recently we found a complete set of `MACREL` version 2 binaries as part of a buildable RTS-8 Archive at [ibiblio.org ... pdp-8/rts8/v3/release][rts8rel] We found a source distribution of `MACREL` v2 in Dave Gesswein's -[misc_floppy][dgfloppy] archive. Part one is flagged as having +[`misc_floppy`][dgfloppy] archive. Part one is flagged as having errors, but another obscure site had a mis-labeled archive of this same stuff so we may be ok. The `MACREL` v2 source would not build under `MACREL` v1, but now we have `MACREL` v2 and initial tests look promising. @@ -65,22 +65,22 @@ To reduce uncertainty around the operation of `OVRDRV.MA`, Source patch `41.5.1M` has been applied by hand to `OVRDRV.MA`. See also: [our documentaiton on the OS/8 Patching][os8patches] -[vandermarkman]: http://vandermark.ch/pdp8/index.php?n=PDP8.Manuals -[maclinkss]: http://vandermark.ch/pdp8/uploads/PDP8/PDP8.Manuals/AA-J073A-TA.txt -[maclinkuser]: http://vandermark.ch/pdp8/uploads/PDP8/PDP8.Manuals/AA-5664B-TA.txt -[dbitdocs]: ftp://ftp.dbit.com/pub/pdp8/doc/ -[dbitmacssm]: ftp://ftp.dbit.com/pub/pdp8/doc/maclkssm.doc -[dbitmacuser]: ftp://ftp.dbit.com/pub/pdp8/doc/maclnkum.doc -[dbitmacrel]: ftp://ftp.dbit.com/pub/pdp8/doc/macrelrn.doc -[rts8rel]: http://www.ibiblio.org/pub/academic/computer-science/history/pdp-8/rts8/v3/release -[dgfloppy]: http://www.pdp8online.com/images/images/misc_floppy.shtml -[os8patches]: https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-patching.md +[vandermarkman]: http://vandermark.ch/pdp8/index.php?n=PDP8.Manuals +[maclinkss]: http://vandermark.ch/pdp8/uploads/PDP8/PDP8.Manuals/AA-J073A-TA.txt +[maclinkuser]: http://vandermark.ch/pdp8/uploads/PDP8/PDP8.Manuals/AA-5664B-TA.txt +[dbitdocs]: ftp://ftp.dbit.com/pub/pdp8/doc/ +[dbitmacssm]: ftp://ftp.dbit.com/pub/pdp8/doc/maclkssm.doc +[dbitmacuser]: ftp://ftp.dbit.com/pub/pdp8/doc/maclnkum.doc +[dbitmacrel]: ftp://ftp.dbit.com/pub/pdp8/doc/macrelrn.doc +[rts8rel]: http://www.ibiblio.org/pub/academic/computer-science/history/pdp-8/rts8/v3/release +[dgfloppy]: http://www.pdp8online.com/images/images/misc_floppy.shtml +[os8patches]: https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-patching.md ### License Copyright © 2017 by Bill Cattey. Licensed under the terms of [the SIMH license][sl]. [sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md Index: doc/os8-patching.md ================================================================== --- doc/os8-patching.md +++ doc/os8-patching.md @@ -27,11 +27,11 @@ in source form, so I built the programs from source, and then bench checked the patch against the source. In a few cases the code was too obscure, and I marked the patch as "plausable" rather than "verified" in my spreadsheet. -The file [patch_list.txt][pl] lists all of the patch files in +The file [`patch_list.txt`][pl] lists all of the patch files in `media/os8/patches`. Comments in that file begin with `#` and are used to disable patches we have rejected for one reason or another. Each rejected patch also has a comment that explains why that particular patch was rejected from the default set. Typical reasons are: @@ -145,13 +145,13 @@ ## Patch Application Order The `patch` routine in `mkos8` applies the patches in the order they -appear in `patch_list.txt`. That list is currently in alphabetical -order. However, there may in future emerge patches that impose an -order. +appear in [`patch_list.txt`][pl]. That list is currently in +alphabetical order. However, there may in future emerge patches that +impose an order. For example, if the `ABSLDR` patch actually did work, it needs the `FUTIL 31.21.2 M` in order to patch into the `ABSLDR` overlay bits. I was skeptical of `FUTIL 31.21.2M` because, when I @@ -158,10 +158,11 @@ load `ABSLDR.SV` into core with GET, the contents of memory showed by `ODT` are *DIFFERENT* from those shown by `FUTIL`. With deeper understanding of the OS/8 Device Extensions kit, I see that the patch was incorporated into the version 8 `FUTIL` source, and also that `ODT` is expected to be updated in version 3S of the Keyboard Monitor. + ## Then There's `MACREL` I've gone into detail on the explorations and understandings with regard to `MACREL` in a [sister document to this one][macreldoc]. @@ -181,25 +182,25 @@ With that TECO patch, I simply changed the version amendment line in that `TECO` patch, because the rest was correct. Whoever published the patch got the version number wrong, and nobody complained. -With no `MACREL` v1 source verification was not really possible, so -applying those patches was postponed. But then we found both binary +With no `MACREL` v1 source code, verification was not really possible, +so applying those patches was postponed. But then we found both binary and source of `MACREL` v2! -In the interests of shipping out system packs in finite time, we will -integrate `MACREL` v2 into the system packs, and verify/apply `MACREL` -v2 patches as follow-on work. +None of the available `MACREL` v2 patches are currently applied. We may +get to that later. -After further testing of 'MACREL' I have concluded that integrating -the source-level patch `41.5.1M` will reduce uncertainty. So I have +After further testing of `MACREL`, I have concluded that integrating the +source-level patch `41.5.1M` will reduce uncertainty, so I have hand-integrated that patch into the `MACREL` tu56 image as well. [macreldoc]:https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-macrel.md -## `FUTIL`: + +## `FUTIL` I was dubious of some of the `FUTIL` patches, but with finding source to version 8A, I gained confidence in the version 7 patches, and understood how seriously important the first patch was to version 8. Index: etc/pidp8i-init.in ================================================================== --- etc/pidp8i-init.in +++ etc/pidp8i-init.in @@ -1,12 +1,12 @@ #!/bin/sh ### BEGIN INIT INFO -# Provides: pidp8i -# Required-Start: $syslog -# Required-Stop: $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 6 +# 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 @@ -60,99 +60,104 @@ 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 + 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 + 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 + if is_running ; then + echo "PiDP-8/I is already running, not starting again." >&2 + exit 0 + fi # Regenerate SSH host keys if this is the first run on a fresh image if [ ! -f /etc/ssh/ssh_host_ecdsa_key -a -x /usr/sbin/dpkg-reconfigure ] then log_daemon_msg "Regenerating SSH host keys..." "pidp8i" /usr/sbin/dpkg-reconfigure openssh-server fi - $scanswitch >/dev/null 2>&1 - script=$? - if [ $script -eq 8 ]; then - echo "PiDP-8/I STOP switch detected, aborting." >&2 - exit 0 - elif [ $script -lt 8 ]; then - bscript="@BOOTDIR@/""$script"".script" - echo "Booting from $bscript..." - elif [ $script -eq 127 ]; then + $scanswitch >/dev/null 2>&1 + script=$? + if [ $script -eq 8 ]; then + echo "PiDP-8/I STOP switch detected, aborting." >&2 + exit 0 + elif [ $script -lt 8 ]; then + bscript="@BOOTDIR@/""$script"".script" + echo "Booting from $bscript..." + elif [ $script -eq 127 ]; then echo "PiDP-8/I panel not present. Booting from 0.script..." - bscript="@BOOTDIR@/0.script" - else - echo "Bad return value $script from $scanswitch!" - exit 1 - fi + bscript="@BOOTDIR@/0.script" + else + echo "Bad return value $script from $scanswitch!" + exit 1 + fi # We want SIMH to have a sensible working directory: somewhere the # user can write files and which makes sense when giving SIMH # commands involving file paths. This default is chosen because it # satisfies both criteria, plus it makes tools/mkos8 happy. # If you change the default here, change that script as well. cd "$prefix/share/media" - log_daemon_msg "Starting PiDP-8/I simulator" "pidp8i" - screenu -dmS pidp8i "$sim" $bscript - status=$? - log_end_msg $status - return $status + 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 + if ! is_running ; then + echo "PiDP-8/I is already stopped." >&2 + status=1 + else + log_daemon_msg "Stopping PiDP-8/I simulator" "pidp8i" + screenu -S pidp8i -X quit ; sleep 1 ; pkill -f pidp8i-sim + status=$? + log_end_msg $status + fi + return $status } case "$1" in start) - do_start - ;; + do_start + ;; stop) - do_stop - ;; + do_stop + ;; restart) - do_stop - do_start - ;; + do_stop + do_start + ;; status) - screenu -ls pidp8i | egrep '[0-9]+\.pidp8i' - ;; + if screenu -ls pidp8i | egrep -q '[0-9]+\.pidp8i' + then + echo "The PiDP-8/I simulator is running." + else + echo "The PiDP-8/I simulator is STOPPED." + fi + ;; *) - log_action_msg "Usage: /etc/init.d/pidp8i {start|stop|restart|status}" || true - exit 1 + log_action_msg "Usage: /etc/init.d/pidp8i {start|stop|restart|status}" || true + exit 1 esac exit 0 Index: examples/README.md ================================================================== --- examples/README.md +++ examples/README.md @@ -8,11 +8,11 @@ | 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 +| `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 @@ -20,18 +20,24 @@ memory. It is interesting to compare them. There are writeups on each of these: * [**`pep001.pal`**][pal] — PAL8 Assembly Language * [**`pep001.bas`**][bas] — OS/8 BASIC +* [**`pep001-*.c`**][c] — two solutions for the CC8 dialects of C +* [**`pep001.fc`**][fc] — U/W FOCAL +* [**`pep001-f?.ft`**][ft] — FORTRAN II and IV [pal]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.PA [bas]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.BA +[c]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.C +[fc]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.FC +[ft]: https://tangentsoft.com/pidp8i/wiki?name=PEP001.FT ## How to Use the BASIC Examples -To use the example BASIC program, simply transcribe it into OS/8 BASIC: +Here's one way to run the `pep001.ba` program mentioned above: .R BASIC NEW OR OLD--NEW FILE NAME--PEP001.BA @@ -56,20 +62,41 @@ 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. +While you could simply type all of that, if you're SSH'd into the +PiDP-8/I, you could instead just copy-and-paste the bulk of the text +above into OS/8 BASIC from the `examples/pep001.ba` file on the host +side. This and several more useful methods are given in the companion +article [Getting Text In][gti]. -I've obscured the output on purpose, since I don't want this page to be -a spoiler for the Project Euler site. +Other methods given in that article let you create the `PEP001.BA` file +on the OS/8 disk first, allowing you to load it up within OS/8 BASIC +like so: + + .R BASIC + NEW OR OLD--old pep001.ba + +Notice that you can give the file name with the `NEW` or `OLD` command +above, rather than wait for OS/8 BASIC to prompt you for it separately. +Also notice that our version of OS/8 BASIC has a patch applied to it by +default which allows it to tolerate lowercase input. (This patch may be +disabled by giving the [`--lowercase` option to the `configure` +script][lcopt].) + + +I obscured the output in the first terminal transcript above on purpose, +since I don't want this page to be a spoiler for the Project Euler site. If you get a 2-letter code from BASIC in response to your `RUN` command, it means you have an error in the program. See the BASIC section of the OS/8 Handbook for a decoding guide. +[gti]: https://tangentsoft.com/pidp8i/wiki?name=Getting+Text+In +[lcopt]: https://tangentsoft.com/pidp8i/doc/trunk/README.md#lowercase + ## How to Use the Assembly Language Examples For each PAL8 assembly program in `src/asm/*.pal` or `examples/*.pal`, the build process produces several output files: @@ -85,26 +112,28 @@ running in the simulator: 1. Transcribe the assembly program text to a file within a PDP-8 operating system and assemble it inside the simulator. -2. Toggle the program in from the front panel. I can recommend this - method only for very short programs. +2. Toggle the machine code for the program in from the front panel. I + can recommend this method only for very short programs. -3. Copy the `*-pal.pt` file to a USB stick and use the PiDP-8/I's - [automatic media mounting feature][howto]. This is the fastest method. +3. Attach the `*-pal.pt` file to the simulator and read the assembly + language text in, such as via the PiDP-8/I [automatic media mounting + feature][howto]. 4. Boot SIMH with the example in core, running the program immediately. I cover each of these options below, in the same order as the list above. ### Option 1: Transcribing the Assembly Code into an OS/8 Session -To transcribe [`examples/add.pal`][pal] into the OS/8 simulation on a -PiDP-8/I: +Perhaps the most period-correct of the options given here is to +transcribe [`examples/add.pal`][pal] into the OS/8 simulation on a +PiDP-8/I using the OS/8 `EDIT` program: .R EDIT *ADD.PA< #A ← append to ADD.PA @@ -159,36 +188,30 @@ 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 #include -ire0 (n, d) +int ire0 (n, d) +int n, d; { while (n > 0) n = n - d; return n == 0; } -main () +int main () { int i, st; st = 0; for (i = 3; i < 1000; i++) { Index: examples/pep001-mod.c ================================================================== --- examples/pep001-mod.c +++ examples/pep001-mod.c @@ -1,9 +1,9 @@ #include #include -main () +int main () { int i, st; st = 0; for (i = 3; i < 1000; i++) { Index: examples/pep001.bas ================================================================== --- examples/pep001.bas +++ examples/pep001.bas @@ -1,5 +1,8 @@ +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 DELETED src/PDP8/Makefile.in Index: src/PDP8/Makefile.in ================================================================== --- src/PDP8/Makefile.in +++ /dev/null @@ -1,47 +0,0 @@ -######################################################################## -# 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 $@ DELETED src/PDP8/pdp8_clk.c Index: src/PDP8/pdp8_clk.c ================================================================== --- src/PDP8/pdp8_clk.c +++ /dev/null @@ -1,183 +0,0 @@ -/* 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; -} DELETED src/PDP8/pdp8_cpu.c Index: src/PDP8/pdp8_cpu.c ================================================================== --- src/PDP8/pdp8_cpu.c +++ /dev/null @@ -1,1801 +0,0 @@ -/* pdp8_cpu.c: PDP-8 CPU simulator - - Copyright (c) 1993-2017, Robert M Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not be - used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. - - ---------------------------------------------------------------------------- - - Portions copyright (c) 2015-2017, Oscar Vermeulen and Warren Young - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the names of the authors above shall - not be used in advertising or otherwise to promote the sale, use or other - dealings in this Software without prior written authorization from those - authors. - - ---------------------------------------------------------------------------- - - cpu central processor - - 07-Sep-17 RMS Fixed sim_eval declaration in history routine (COVERITY) - 09-Mar-17 RMS Fixed PCQ_ENTRY for interrupts (COVERITY) - 13-Feb-17 RMS RESET clear L'AC, per schematics - 28-Jan-17 RMS Renamed switch register variable to SR, per request - 18-Sep-16 RMS Added alternate dispatch table for non-contiguous devices - 17-Sep-13 RMS Fixed boot in wrong field problem (Dave Gesswein) - 28-Apr-07 RMS Removed clock initialization - 30-Oct-06 RMS Added idle and infinite loop detection - 30-Sep-06 RMS Fixed SC value after DVI overflow (Don North) - 22-Sep-05 RMS Fixed declarations (Sterling Garwood) - 16-Aug-05 RMS Fixed C++ declaration and cast problems - 06-Nov-04 RMS Added =n to SHOW HISTORY - 31-Dec-03 RMS Fixed bug in set_cpu_hist - 13-Oct-03 RMS Added instruction history - Added TSC8-75 support (Bernhard Baehr) - 12-Mar-03 RMS Added logical name support - 04-Oct-02 RMS Revamped device dispatching, added device number support - 06-Jan-02 RMS Added device enable/disable routines - 30-Dec-01 RMS Added old PC queue - 16-Dec-01 RMS Fixed bugs in EAE - 07-Dec-01 RMS Revised to use new breakpoint package - 30-Nov-01 RMS Added RL8A, extended SET/SHOW support - 16-Sep-01 RMS Fixed bug in reset routine, added KL8A support - 10-Aug-01 RMS Removed register from declarations - 17-Jul-01 RMS Moved function prototype - 07-Jun-01 RMS Fixed bug in JMS to non-existent memory - 25-Apr-01 RMS Added device enable/disable support - 18-Mar-01 RMS Added DF32 support - 05-Mar-01 RMS Added clock calibration support - 15-Feb-01 RMS Added DECtape support - 14-Apr-99 RMS Changed t_addr to unsigned - - The register state for the PDP-8 is: - - AC<0:11> accumulator - MQ<0:11> multiplier-quotient - L link flag - PC<0:11> program counter - IF<0:2> instruction field - IB<0:2> instruction buffer - DF<0:2> data field - UF user flag - UB user buffer - SF<0:6> interrupt save field - - The PDP-8 has three instruction formats: memory reference, I/O transfer, - and operate. The memory reference format is: - - 0 1 2 3 4 5 6 7 8 9 10 11 - +--+--+--+--+--+--+--+--+--+--+--+--+ - | op |in|zr| page offset | memory reference - +--+--+--+--+--+--+--+--+--+--+--+--+ - - <0:2> mnemonic action - - 000 AND AC = AC & M[MA] - 001 TAD L'AC = AC + M[MA] - 010 DCA M[MA] = AC, AC = 0 - 011 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 - 100 JMS M[MA] = PC, PC = MA + 1 - 101 JMP PC = MA - - <3:4> mode action - 00 page zero MA = IF'0'IR<5:11> - 01 current page MA = IF'PC<0:4>'IR<5:11> - 10 indirect page zero MA = xF'M[IF'0'IR<5:11>] - 11 indirect current page MA = xF'M[IF'PC<0:4>'IR<5:11>] - - where x is D for AND, TAD, ISZ, DCA, and I for JMS, JMP. - - Memory reference instructions can access an address space of 32K words. - The address space is divided into eight 4K word fields; each field is - divided into thirty-two 128 word pages. An instruction can directly - address, via its 7b offset, locations 0-127 on page zero or on the current - page. All 32k words can be accessed via indirect addressing and the - instruction and data field registers. If an indirect address is in - locations 0010-0017 of any field, the indirect address is incremented - and rewritten to memory before use. - - The I/O transfer format is as follows: - - 0 1 2 3 4 5 6 7 8 9 10 11 - +--+--+--+--+--+--+--+--+--+--+--+--+ - | op | device | pulse | I/O transfer - +--+--+--+--+--+--+--+--+--+--+--+--+ - - The IO transfer instruction sends the the specified pulse to the - specified I/O device. The I/O device may take data from the AC, - return data to the AC, initiate or cancel operations, or skip on - status. - - The operate format is as follows: - - +--+--+--+--+--+--+--+--+--+--+--+--+ - | 1| 1| 1| 0| | | | | | | | | operate group 1 - +--+--+--+--+--+--+--+--+--+--+--+--+ - | | | | | | | | - | | | | | | | +--- increment AC 3 - | | | | | | +--- rotate 1 or 2 4 - | | | | | +--- rotate left 4 - | | | | +--- rotate right 4 - | | | +--- complement L 2 - | | +--- complement AC 2 - | +--- clear L 1 - +-- clear AC 1 - - +--+--+--+--+--+--+--+--+--+--+--+--+ - | 1| 1| 1| 1| | | | | | | | 0| operate group 2 - +--+--+--+--+--+--+--+--+--+--+--+--+ - | | | | | | | - | | | | | | +--- halt 3 - | | | | | +--- or switch register 3 - | | | | +--- reverse skip sense 1 - | | | +--- skip on L != 0 1 - | | +--- skip on AC == 0 1 - | +--- skip on AC < 0 1 - +-- clear AC 2 - - +--+--+--+--+--+--+--+--+--+--+--+--+ - | 1| 1| 1| 1| | | | | | | | 1| operate group 3 - +--+--+--+--+--+--+--+--+--+--+--+--+ - | | | | \______/ - | | | | | - | | +--|-----+--- EAE command 3 - | | +--- AC -> MQ, 0 -> AC 2 - | +--- MQ v AC --> AC 2 - +-- clear AC 1 - - The operate instruction can be microprogrammed to perform operations - on the AC, MQ, and link. - - This routine is the instruction decode routine for the PDP-8. - It is called from the simulator control program to execute - instructions in simulated memory, starting at the simulated PC. - It runs until 'reason' is set non-zero. - - General notes: - - 1. Reasons to stop. The simulator can be stopped by: - - HALT instruction - breakpoint encountered - unimplemented instruction and stop_inst flag set - I/O error in I/O simulator - - 2. Interrupts. Interrupts are maintained by three parallel variables: - - dev_done device done flags - int_enable interrupt enable flags - int_req interrupt requests - - In addition, int_req contains the interrupt enable flag, the - CIF not pending flag, and the ION not pending flag. If all - three of these flags are set, and at least one interrupt request - is set, then an interrupt occurs. - - 3. Non-existent memory. On the PDP-8, reads to non-existent memory - return zero, and writes are ignored. In the simulator, the - largest possible memory is instantiated and initialized to zero. - Thus, only writes outside the current field (indirect writes) need - be checked against actual memory size. - - 3. Adding I/O devices. These modules must be modified: - - pdp8_defs.h add device number and interrupt definitions - pdp8_sys.c add sim_devices table entry -*/ - -#include "pdp8_defs.h" - -/* ---PiDP add---------------------------------------------------------------------------------------------- */ -#include "gpio-common.h" -#include "pidp8i.h" -/* ---PiDP end---------------------------------------------------------------------------------------------- */ - -#define PCQ_SIZE 64 /* must be 2**n */ -#define PCQ_MASK (PCQ_SIZE - 1) -#define PCQ_ENTRY(x) pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = x -#define UNIT_V_NOEAE (UNIT_V_UF) /* EAE absent */ -#define UNIT_NOEAE (1 << UNIT_V_NOEAE) -#define UNIT_V_MSIZE (UNIT_V_UF + 1) /* dummy mask */ -#define UNIT_MSIZE (1 << UNIT_V_MSIZE) -#define OP_KSF 06031 /* for idle */ - -#define HIST_PC 0x40000000 -#define HIST_MIN 64 -#define HIST_MAX 65536 - -typedef struct { - int32 pc; - int32 ea; - int16 ir; - int16 opnd; - int16 lac; - int16 mq; - } InstHistory; - -uint16 M[MAXMEMSIZE] = { 0 }; /* main memory */ -int32 saved_LAC = 0; /* saved L'AC */ -int32 saved_MQ = 0; /* saved MQ */ -int32 saved_PC = 0; /* saved IF'PC */ -int32 saved_DF = 0; /* saved Data Field */ -int32 IB = 0; /* Instruction Buffer */ -int32 SF = 0; /* Save Field */ -int32 emode = 0; /* EAE mode */ -int32 gtf = 0; /* EAE gtf flag */ -int32 SC = 0; /* EAE shift count */ -int32 UB = 0; /* User mode Buffer */ -int32 UF = 0; /* User mode Flag */ -int32 SR = 0; /* Switch Register */ -int32 tsc_ir = 0; /* TSC8-75 IR */ -int32 tsc_pc = 0; /* TSC8-75 PC */ -int32 tsc_cdf = 0; /* TSC8-75 CDF flag */ -int32 tsc_enb = 0; /* TSC8-75 enabled */ -int32 cpu_astop = 0; /* address stop */ -int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ -int32 pcq_p = 0; /* PC queue ptr */ -REG *pcq_r = NULL; /* PC queue reg ptr */ -int32 dev_done = 0; /* dev done flags */ -int32 int_enable = INT_INIT_ENABLE; /* intr enables */ -int32 int_req = 0; /* intr requests */ -int32 stop_inst = 0; /* trap on ill inst */ -int32 (*dev_tab[DEV_MAX])(int32 IR, int32 dat); /* device dispatch */ -int32 hst_p = 0; /* history pointer */ -int32 hst_lnt = 0; /* history length */ -InstHistory *hst = NULL; /* instruction history */ - -t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); -t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); -t_stat cpu_reset (DEVICE *dptr); -t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); -t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc); -t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc); -t_bool build_dev_tab (void); - -/* CPU data structures - - cpu_dev CPU device descriptor - cpu_unit CPU unit descriptor - cpu_reg CPU register list - cpu_mod CPU modifier list -*/ - -UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; - -REG cpu_reg[] = { - { ORDATAD (PC, saved_PC, 15, "program counter") }, - { ORDATAD (AC, saved_LAC, 12, "accumulator") }, - { FLDATAD (L, saved_LAC, 12, "link") }, - { ORDATAD (MQ, saved_MQ, 12, "multiplier-quotient") }, - { ORDATAD (SR, SR, 12, "front panel switches") }, - { GRDATAD (IF, saved_PC, 8, 3, 12, "instruction field") }, - { GRDATAD (DF, saved_DF, 8, 3, 12, "data field") }, - { GRDATAD (IB, IB, 8, 3, 12, "instruction field buffter") }, - { ORDATAD (SF, SF, 7, "save field") }, - { FLDATAD (UB, UB, 0, "user mode buffer") }, - { FLDATAD (UF, UF, 0, "user mode flag") }, - { ORDATAD (SC, SC, 5, "EAE shift counter") }, - { FLDATAD (GTF, gtf, 0, "EAE greater than flag") }, - { FLDATAD (EMODE, emode, 0, "EAE mode (0 = A, 1 = B)") }, - { FLDATAD (ION, int_req, INT_V_ION, "interrupt enable") }, - { FLDATAD (ION_DELAY, int_req, INT_V_NO_ION_PENDING, "interrupt enable delay for ION") }, - { FLDATAD (CIF_DELAY, int_req, INT_V_NO_CIF_PENDING, "interrupt enable delay for CIF") }, - { FLDATAD (PWR_INT, int_req, INT_V_PWR, "power fail interrupt") }, - { FLDATAD (UF_INT, int_req, INT_V_UF, "user mode violation interrupt") }, - { ORDATAD (INT, int_req, INT_V_ION+1, "interrupt pending flags"), REG_RO }, - { ORDATAD (DONE, dev_done, INT_V_DIRECT, "device done flags"), REG_RO }, - { ORDATAD (ENABLE, int_enable, INT_V_DIRECT, "device interrupt enable flags"), REG_RO }, - { BRDATAD (PCQ, pcq, 8, 15, PCQ_SIZE, "PC prior to last JMP, JMS, or interrupt; most recent PC change first"), REG_RO+REG_CIRC }, - { ORDATA (PCQP, pcq_p, 6), REG_HRO }, - { FLDATAD (STOP_INST, stop_inst, 0, "stop on undefined instruction") }, - { ORDATAD (WRU, sim_int_char, 8, "interrupt character") }, - { NULL } - }; - -MTAB cpu_mod[] = { - { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL }, - { UNIT_NOEAE, 0, "EAE", "EAE", NULL }, - { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, - { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, - { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, - { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, - { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, - { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, - { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, - { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, - { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, - { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, - { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", - &cpu_set_hist, &cpu_show_hist }, - { 0 } - }; - -DEVICE cpu_dev = { - "CPU", &cpu_unit, cpu_reg, cpu_mod, - 1, 8, 15, 1, 8, 12, - &cpu_ex, &cpu_dep, &cpu_reset, - NULL, NULL, NULL, - NULL, 0 - }; - -t_stat sim_instr (void) -{ -int32 IR, MB, IF, DF, LAC, MQ; -uint32 PC, MA; -int32 device, pulse, temp, iot_data; -t_stat reason; - -/* Restore register state */ - -if (build_dev_tab ()) /* build dev_tab */ - return SCPE_STOP; -PC = saved_PC & 007777; /* load local copies */ -IF = saved_PC & 070000; -DF = saved_DF & 070000; -LAC = saved_LAC & 017777; -MQ = saved_MQ & 07777; -int_req = INT_UPDATE; -reason = 0; - - -/* ---PiDP add--------------------------------------------------------------------------------------------- */ -// PiDP-8/I specific flag, set when the last instruction was an IOT -// instruction to a real device. SIMH doesn't track this, but the front -// panel needs it. -int Pause = 0; - -// Set our initial IPS value from the throttle, if given. -static time_t last_update = 0; -static size_t max_skips = 0; -static const size_t pidp8i_updates_per_sec = 3200; -max_skips = get_pidp8i_initial_max_skips (pidp8i_updates_per_sec); -srand48 (time (&last_update)); - -// Reset display info in case we're re-entering the simulator from Ctrl-E -extern display display_bufs[2]; -memset (display_bufs, 0, sizeof(display_bufs)); -static size_t skip_count, dither, inst_count; -skip_count = dither = inst_count = 0; -/* ---PiDP end---------------------------------------------------------------------------------------------- */ - - -/* Main instruction fetch/decode loop */ - -while (reason == 0) { /* loop until halted */ - - // Allow clean exit to SCP: https://github.com/simh/simh/issues/387 - if (cpu_astop != 0) { - cpu_astop = 0; - reason = SCPE_STOP; - break; - } - - if (sim_interval <= 0) { /* check clock queue */ - if ((reason = sim_process_event ())) { -/* ---PiDP add--------------------------------------------------------------------------------------------- */ - // We're about to leave the loop, so repaint one last time - // in case this is a Ctrl-E and we later get a "cont" - // command. Set a flag that will let us auto-resume. - extern int resumeFromInstructionLoopExit, swStop, swSingInst; - resumeFromInstructionLoopExit = swStop = swSingInst = 1; - set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, - int_req, Pause); - - // Also copy SR hardware value to software register in case - // the user tries poking at it from the sim> prompt. - SR = get_switch_register(); -/* ---PiDP end---------------------------------------------------------------------------------------------- */ - break; - } - } - -/* ---PiDP add--------------------------------------------------------------------------------------------- */ - - switch (handle_flow_control_switches(M, &PC, &MA, &MB, &LAC, &IF, - &DF, &int_req)) { - case pft_stop: - // Tell the SIMH event queue to keep running even though - // we're stopped. Without this, it will ignore Ctrl-E - // until the simulator is back in free-running mode. - sim_interval = sim_interval - 1; - - // Have to keep display updated while stopped. This does - // mean if the software starts with the STOP switch held - // down, we'll put garbage onto the display for MA, MB, and - // IR, but that's what the real hardware does, too. See - // https://github.com/simh/simh/issues/386 - set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, - int_req, Pause); - - // Go no further in STOP mode. In particular, fetch no more - // instructions, and do not touch PC! - continue; - - case pft_halt: - // Clear all registers and halt simulator - PC = saved_PC = 0; - IF = saved_PC = 0; - DF = saved_DF = 0; - LAC = saved_LAC = 0; - MQ = saved_MQ = 0; - int_req = 0; - reason = STOP_HALT; - continue; - - case pft_normal: - // execute normally - break; - } - -/* ---PiDP end---------------------------------------------------------------------------------------------- */ - - if (int_req > INT_PENDING) { /* interrupt? */ - int_req = int_req & ~INT_ION; /* interrupts off */ - SF = (UF << 6) | (IF >> 9) | (DF >> 12); /* form save field */ - PCQ_ENTRY (IF | PC); /* save old PC with IF */ - IF = IB = DF = UF = UB = 0; /* clear mem ext */ - M[0] = PC; /* save PC in 0 */ - PC = 1; /* fetch next from 1 */ - } - - MA = IF | PC; /* form PC */ - if (sim_brk_summ && - sim_brk_test (MA, (1u << SIM_BKPT_V_SPC) | SWMASK ('E'))) { /* breakpoint? */ - reason = STOP_IBKPT; /* stop simulation */ - break; - } - - IR = M[MA]; /* fetch instruction */ - if (sim_brk_summ && - sim_brk_test (IR, (2u << SIM_BKPT_V_SPC) | SWMASK ('I'))) { /* breakpoint? */ - reason = STOP_OPBKPT; /* stop simulation */ - break; - } - PC = (PC + 1) & 07777; /* increment PC */ - int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ - sim_interval = sim_interval - 1; - -/* Instruction decoding. - - The opcode (IR<0:2>), indirect flag (IR<3>), and page flag (IR<4>) - are decoded together. This produces 32 decode points, four per - major opcode. For IOT, the extra decode points are not useful; - for OPR, only the group flag (IR<3>) is used. - - AND, TAD, ISZ, DCA calculate a full 15b effective address. - JMS, JMP calculate a 12b field-relative effective address. - - Autoindex calculations always occur within the same field as the - instruction fetch. The field must exist; otherwise, the instruction - fetched would be 0000, and indirect addressing could not occur. - - Note that MA contains IF'PC. -*/ - - if (hst_lnt) { /* history enabled? */ - int32 ea; - - hst_p = (hst_p + 1); /* next entry */ - if (hst_p >= hst_lnt) - hst_p = 0; - hst[hst_p].pc = MA | HIST_PC; /* save PC, IR, LAC, MQ */ - hst[hst_p].ir = IR; - hst[hst_p].lac = LAC; - hst[hst_p].mq = MQ; - if (IR < 06000) { /* mem ref? */ - if (IR & 0200) - ea = (MA & 077600) | (IR & 0177); - else ea = IF | (IR & 0177); /* direct addr */ - if (IR & 0400) { /* indirect? */ - if (IR < 04000) { /* mem operand? */ - if ((ea & 07770) != 00010) - ea = DF | M[ea]; - else ea = DF | ((M[ea] + 1) & 07777); - } - else { /* no, jms/jmp */ - if ((ea & 07770) != 00010) - ea = IB | M[ea]; - else ea = IB | ((M[ea] + 1) & 07777); - } - } - hst[hst_p].ea = ea; /* save eff addr */ - hst[hst_p].opnd = M[ea]; /* save operand */ - } - } - -switch ((IR >> 7) & 037) { /* decode IR<0:4> */ - -/* Opcode 0, AND */ - - case 000: /* AND, dir, zero */ - MA = IF | (IR & 0177); /* dir addr, page zero */ - LAC = LAC & (M[MA] | 010000); - break; - - case 001: /* AND, dir, curr */ - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - LAC = LAC & (M[MA] | 010000); - break; - - case 002: /* AND, indir, zero */ - MA = IF | (IR & 0177); /* dir addr, page zero */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - LAC = LAC & (M[MA] | 010000); - break; - - case 003: /* AND, indir, curr */ - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - LAC = LAC & (M[MA] | 010000); - break; - -/* Opcode 1, TAD */ - - case 004: /* TAD, dir, zero */ - MA = IF | (IR & 0177); /* dir addr, page zero */ - LAC = (LAC + M[MA]) & 017777; - break; - - case 005: /* TAD, dir, curr */ - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - LAC = (LAC + M[MA]) & 017777; - break; - - case 006: /* TAD, indir, zero */ - MA = IF | (IR & 0177); /* dir addr, page zero */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - LAC = (LAC + M[MA]) & 017777; - break; - - case 007: /* TAD, indir, curr */ - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - LAC = (LAC + M[MA]) & 017777; - break; - -/* Opcode 2, ISZ */ - - case 010: /* ISZ, dir, zero */ - MA = IF | (IR & 0177); /* dir addr, page zero */ - M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ - if (MB == 0) - PC = (PC + 1) & 07777; - break; - - case 011: /* ISZ, dir, curr */ - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ - if (MB == 0) - PC = (PC + 1) & 07777; - break; - - case 012: /* ISZ, indir, zero */ - MA = IF | (IR & 0177); /* dir addr, page zero */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - MB = (M[MA] + 1) & 07777; - if (MEM_ADDR_OK (MA)) - M[MA] = MB; - if (MB == 0) - PC = (PC + 1) & 07777; - break; - - case 013: /* ISZ, indir, curr */ - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - MB = (M[MA] + 1) & 07777; - if (MEM_ADDR_OK (MA)) - M[MA] = MB; - if (MB == 0) - PC = (PC + 1) & 07777; - break; - -/* Opcode 3, DCA */ - - case 014: /* DCA, dir, zero */ - MA = IF | (IR & 0177); /* dir addr, page zero */ - M[MA] = LAC & 07777; - LAC = LAC & 010000; - break; - - case 015: /* DCA, dir, curr */ - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - M[MA] = LAC & 07777; - LAC = LAC & 010000; - break; - - case 016: /* DCA, indir, zero */ - MA = IF | (IR & 0177); /* dir addr, page zero */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - if (MEM_ADDR_OK (MA)) - M[MA] = LAC & 07777; - LAC = LAC & 010000; - break; - - case 017: /* DCA, indir, curr */ - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - if (MEM_ADDR_OK (MA)) - M[MA] = LAC & 07777; - LAC = LAC & 010000; - break; - -/* Opcode 4, JMS. From Bernhard Baehr's description of the TSC8-75: - - (In user mode) the current JMS opcode is moved to the ERIOT register, the ECDF - flag is cleared. The address of the JMS instruction is loaded into the ERTB - register and the TSC8-75 I/O flag is raised. When the TSC8-75 is enabled, the - target addess of the JMS is loaded into PC, but nothing else (loading of IF, UF, - clearing the interrupt inhibit flag, storing of the return address in the first - word of the subroutine) happens. When the TSC8-75 is disabled, the JMS is performed - as usual. */ - - case 020: /* JMS, dir, zero */ - PCQ_ENTRY (MA); - MA = IR & 0177; /* dir addr, page zero */ - if (UF) { /* user mode? */ - tsc_ir = IR; /* save instruction */ - tsc_cdf = 0; /* clear flag */ - } - if (UF && tsc_enb) { /* user mode, TSC enab? */ - tsc_pc = (PC - 1) & 07777; /* save PC */ - int_req = int_req | INT_TSC; /* request intr */ - } - else { /* normal */ - IF = IB; /* change IF */ - UF = UB; /* change UF */ - int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ - MA = IF | MA; - if (MEM_ADDR_OK (MA)) - M[MA] = PC; - } - PC = (MA + 1) & 07777; - break; - - case 021: /* JMS, dir, curr */ - PCQ_ENTRY (MA); - MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ - if (UF) { /* user mode? */ - tsc_ir = IR; /* save instruction */ - tsc_cdf = 0; /* clear flag */ - } - if (UF && tsc_enb) { /* user mode, TSC enab? */ - tsc_pc = (PC - 1) & 07777; /* save PC */ - int_req = int_req | INT_TSC; /* request intr */ - } - else { /* normal */ - IF = IB; /* change IF */ - UF = UB; /* change UF */ - int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ - MA = IF | MA; - if (MEM_ADDR_OK (MA)) - M[MA] = PC; - } - PC = (MA + 1) & 07777; - break; - - case 022: /* JMS, indir, zero */ - PCQ_ENTRY (MA); - MA = IF | (IR & 0177); /* dir addr, page zero */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = M[MA]; - else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - if (UF) { /* user mode? */ - tsc_ir = IR; /* save instruction */ - tsc_cdf = 0; /* clear flag */ - } - if (UF && tsc_enb) { /* user mode, TSC enab? */ - tsc_pc = (PC - 1) & 07777; /* save PC */ - int_req = int_req | INT_TSC; /* request intr */ - } - else { /* normal */ - IF = IB; /* change IF */ - UF = UB; /* change UF */ - int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ - MA = IF | MA; - if (MEM_ADDR_OK (MA)) - M[MA] = PC; - } - PC = (MA + 1) & 07777; - break; - - case 023: /* JMS, indir, curr */ - PCQ_ENTRY (MA); - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = M[MA]; - else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - if (UF) { /* user mode? */ - tsc_ir = IR; /* save instruction */ - tsc_cdf = 0; /* clear flag */ - } - if (UF && tsc_enb) { /* user mode, TSC enab? */ - tsc_pc = (PC - 1) & 07777; /* save PC */ - int_req = int_req | INT_TSC; /* request intr */ - } - else { /* normal */ - IF = IB; /* change IF */ - UF = UB; /* change UF */ - int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ - MA = IF | MA; - if (MEM_ADDR_OK (MA)) - M[MA] = PC; - } - PC = (MA + 1) & 07777; - break; - -/* Opcode 5, JMP. From Bernhard Baehr's description of the TSC8-75: - - (In user mode) the current JMP opcode is moved to the ERIOT register, the ECDF - flag is cleared. The address of the JMP instruction is loaded into the ERTB - register and the TSC8-75 I/O flag is raised. Then the JMP is performed as usual - (including the setting of IF, UF and clearing the interrupt inhibit flag). */ - - - case 024: /* JMP, dir, zero */ - PCQ_ENTRY (MA); - MA = IR & 0177; /* dir addr, page zero */ - if (UF) { /* user mode? */ - tsc_ir = IR; /* save instruction */ - tsc_cdf = 0; /* clear flag */ - if (tsc_enb) { /* TSC8 enabled? */ - tsc_pc = (PC - 1) & 07777; /* save PC */ - int_req = int_req | INT_TSC; /* request intr */ - } - } - IF = IB; /* change IF */ - UF = UB; /* change UF */ - int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ - PC = MA; - break; - -/* If JMP direct, also check for idle (KSF/JMP *-1) and infinite loop */ - - case 025: /* JMP, dir, curr */ - PCQ_ENTRY (MA); - MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ - if (UF) { /* user mode? */ - tsc_ir = IR; /* save instruction */ - tsc_cdf = 0; /* clear flag */ - if (tsc_enb) { /* TSC8 enabled? */ - tsc_pc = (PC - 1) & 07777; /* save PC */ - int_req = int_req | INT_TSC; /* request intr */ - } - } - if (sim_idle_enab && /* idling enabled? */ - (IF == IB)) { /* to same bank? */ - if (MA == ((PC - 2) & 07777)) { /* 1) JMP *-1? */ - if (!(int_req & (INT_ION|INT_TTI)) && /* iof, TTI flag off? */ - (M[IB|((PC - 2) & 07777)] == OP_KSF)) /* next is KSF? */ - sim_idle (TMR_CLK, FALSE); /* we're idle */ - } /* end JMP *-1 */ - else if (MA == ((PC - 1) & 07777)) { /* 2) JMP *? */ - if (!(int_req & INT_ION)) /* iof? */ - reason = STOP_LOOP; /* then infinite loop */ - else if (!(int_req & INT_ALL)) /* ion, not intr? */ - sim_idle (TMR_CLK, FALSE); /* we're idle */ - } /* end JMP */ - } /* end idle enabled */ - IF = IB; /* change IF */ - UF = UB; /* change UF */ - int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ - PC = MA; - break; - - case 026: /* JMP, indir, zero */ - PCQ_ENTRY (MA); - MA = IF | (IR & 0177); /* dir addr, page zero */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = M[MA]; - else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - if (UF) { /* user mode? */ - tsc_ir = IR; /* save instruction */ - tsc_cdf = 0; /* clear flag */ - if (tsc_enb) { /* TSC8 enabled? */ - tsc_pc = (PC - 1) & 07777; /* save PC */ - int_req = int_req | INT_TSC; /* request intr */ - } - } - IF = IB; /* change IF */ - UF = UB; /* change UF */ - int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ - PC = MA; - break; - - case 027: /* JMP, indir, curr */ - PCQ_ENTRY (MA); - MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = M[MA]; - else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - if (UF) { /* user mode? */ - tsc_ir = IR; /* save instruction */ - tsc_cdf = 0; /* clear flag */ - if (tsc_enb) { /* TSC8 enabled? */ - tsc_pc = (PC - 1) & 07777; /* save PC */ - int_req = int_req | INT_TSC; /* request intr */ - } - } - IF = IB; /* change IF */ - UF = UB; /* change UF */ - int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ - PC = MA; - break; - -/* Opcode 7, OPR group 1 */ - - case 034:case 035: /* OPR, group 1 */ - switch ((IR >> 4) & 017) { /* decode IR<4:7> */ - case 0: /* nop */ - break; - case 1: /* CML */ - LAC = LAC ^ 010000; - break; - case 2: /* CMA */ - LAC = LAC ^ 07777; - break; - case 3: /* CMA CML */ - LAC = LAC ^ 017777; - break; - case 4: /* CLL */ - LAC = LAC & 07777; - break; - case 5: /* CLL CML = STL */ - LAC = LAC | 010000; - break; - case 6: /* CLL CMA */ - LAC = (LAC ^ 07777) & 07777; - break; - case 7: /* CLL CMA CML */ - LAC = (LAC ^ 07777) | 010000; - break; - case 010: /* CLA */ - LAC = LAC & 010000; - break; - case 011: /* CLA CML */ - LAC = (LAC & 010000) ^ 010000; - break; - case 012: /* CLA CMA = STA */ - LAC = LAC | 07777; - break; - case 013: /* CLA CMA CML */ - LAC = (LAC | 07777) ^ 010000; - break; - case 014: /* CLA CLL */ - LAC = 0; - break; - case 015: /* CLA CLL CML */ - LAC = 010000; - break; - case 016: /* CLA CLL CMA */ - LAC = 07777; - break; - case 017: /* CLA CLL CMA CML */ - LAC = 017777; - break; - } /* end switch opers */ - - if (IR & 01) /* IAC */ - LAC = (LAC + 1) & 017777; - switch ((IR >> 1) & 07) { /* decode IR<8:10> */ - case 0: /* nop */ - break; - case 1: /* BSW */ - LAC = (LAC & 010000) | ((LAC >> 6) & 077) | ((LAC & 077) << 6); - break; - case 2: /* RAL */ - LAC = ((LAC << 1) | (LAC >> 12)) & 017777; - break; - case 3: /* RTL */ - LAC = ((LAC << 2) | (LAC >> 11)) & 017777; - break; - case 4: /* RAR */ - LAC = ((LAC >> 1) | (LAC << 12)) & 017777; - break; - case 5: /* RTR */ - LAC = ((LAC >> 2) | (LAC << 11)) & 017777; - break; - case 6: /* RAL RAR - undef */ - LAC = LAC & (IR | 010000); /* uses AND path */ - break; - case 7: /* RTL RTR - undef */ - LAC = (LAC & 010000) | (MA & 07600) | (IR & 0177); - break; /* uses address path */ - } /* end switch shifts */ - break; /* end group 1 */ - -/* OPR group 2. From Bernhard Baehr's description of the TSC8-75: - - (In user mode) HLT (7402), OSR (7404) and microprogrammed combinations with - HLT and OSR: Additional to raising a user mode interrupt, the current OPR - opcode is moved to the ERIOT register and the ECDF flag is cleared. */ - - case 036:case 037: /* OPR, groups 2, 3 */ - if ((IR & 01) == 0) { /* group 2 */ - switch ((IR >> 3) & 017) { /* decode IR<6:8> */ - case 0: /* nop */ - break; - case 1: /* SKP */ - PC = (PC + 1) & 07777; - break; - case 2: /* SNL */ - if (LAC >= 010000) - PC = (PC + 1) & 07777; - break; - case 3: /* SZL */ - if (LAC < 010000) - PC = (PC + 1) & 07777; - break; - case 4: /* SZA */ - if ((LAC & 07777) == 0) - PC = (PC + 1) & 07777; - break; - case 5: /* SNA */ - if ((LAC & 07777) - != 0) PC = (PC + 1) & 07777; - break; - case 6: /* SZA | SNL */ - if ((LAC == 0) || (LAC >= 010000)) - PC = (PC + 1) & 07777; - break; - case 7: /* SNA & SZL */ - if ((LAC != 0) && (LAC < 010000)) - PC = (PC + 1) & 07777; - break; - case 010: /* SMA */ - if ((LAC & 04000) != 0) - PC = (PC + 1) & 07777; - break; - case 011: /* SPA */ - if ((LAC & 04000) == 0) - PC = (PC + 1) & 07777; - break; - case 012: /* SMA | SNL */ - if (LAC >= 04000) - PC = (PC + 1) & 07777; - break; - case 013: /* SPA & SZL */ - if (LAC < 04000) - PC = (PC + 1) & 07777; - break; - case 014: /* SMA | SZA */ - if (((LAC & 04000) != 0) || ((LAC & 07777) == 0)) - PC = (PC + 1) & 07777; - break; - case 015: /* SPA & SNA */ - if (((LAC & 04000) == 0) && ((LAC & 07777) != 0)) - PC = (PC + 1) & 07777; - break; - case 016: /* SMA | SZA | SNL */ - if ((LAC >= 04000) || (LAC == 0)) - PC = (PC + 1) & 07777; - break; - case 017: /* SPA & SNA & SZL */ - if ((LAC < 04000) && (LAC != 0)) - PC = (PC + 1) & 07777; - break; - } /* end switch skips */ - if (IR & 0200) /* CLA */ - LAC = LAC & 010000; - if ((IR & 06) && UF) { /* user mode? */ - int_req = int_req | INT_UF; /* request intr */ - tsc_ir = IR; /* save instruction */ - tsc_cdf = 0; /* clear flag */ - } - else { - if (IR & 04) { /* OSR */ -/* ---PiDP add--------------------------------------------------------------------------------------------- */ - SR = get_switch_register(); /* get current SR */ -/* ---PiDP end---------------------------------------------------------------------------------------------- */ - LAC = LAC | SR; - } - if (IR & 02) { /* HLT */ -//--- PiDP change----------------------------------------------------------------------- - // reason = STOP_HALT; - extern int swStop; - swStop = 1; - } -//--- end of PiDP change---------------------------------------------------------------- - } - break; - } /* end if group 2 */ - -/* OPR group 3 standard - - MQA!MQL exchanges AC and MQ, as follows: - - temp = MQ; - MQ = LAC & 07777; - LAC = LAC & 010000 | temp; -*/ - - temp = MQ; /* group 3 */ - if (IR & 0200) /* CLA */ - LAC = LAC & 010000; - if (IR & 0020) { /* MQL */ - MQ = LAC & 07777; - LAC = LAC & 010000; - } - if (IR & 0100) /* MQA */ - LAC = LAC | temp; - if ((IR & 0056) && (cpu_unit.flags & UNIT_NOEAE)) { - reason = stop_inst; /* EAE not present */ - break; - } - -/* OPR group 3 EAE - - The EAE operates in two modes: - - Mode A, PDP-8/I compatible - Mode B, extended capability - - Mode B provides eight additional subfunctions; in addition, some - of the Mode A functions operate differently in Mode B. - - The mode switch instructions are decoded explicitly and cannot be - microprogrammed with other EAE functions (SWAB performs an MQL as - part of standard group 3 decoding). If mode switching is decoded, - all other EAE timing is suppressed. -*/ - - if (IR == 07431) { /* SWAB */ - emode = 1; /* set mode flag */ - break; - } - if (IR == 07447) { /* SWBA */ - emode = gtf = 0; /* clear mode, gtf */ - break; - } - -/* If not switching modes, the EAE operation is determined by the mode - and IR<6,8:10>: - - <6:10> mode A mode B comments - - 0x000 NOP NOP - 0x001 SCL ACS - 0x010 MUY MUY if mode B, next = address - 0x011 DVI DVI if mode B, next = address - 0x100 NMI NMI if mode B, clear AC if - result = 4000'0000 - 0x101 SHL SHL if mode A, extra shift - 0x110 ASR ASR if mode A, extra shift - 0x111 LSR LSR if mode A, extra shift - 1x000 SCA SCA - 1x001 SCA + SCL DAD - 1x010 SCA + MUY DST - 1x011 SCA + DVI SWBA NOP if not detected earlier - 1x100 SCA + NMI DPSZ - 1x101 SCA + SHL DPIC must be combined with MQA!MQL - 1x110 SCA + ASR DCM must be combined with MQA!MQL - 1x111 SCA + LSR SAM - - EAE instructions which fetch memory operands use the CPU's DEFER - state to read the first word; if the address operand is in locations - x0010 - x0017, it is autoincremented. -*/ - - if (emode == 0) /* mode A? clr gtf */ - gtf = 0; - switch ((IR >> 1) & 027) { /* decode IR<6,8:10> */ - - case 020: /* mode A, B: SCA */ - LAC = LAC | SC; - break; - case 000: /* mode A, B: NOP */ - break; - - case 021: /* mode B: DAD */ - if (emode) { - MA = IF | PC; - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - MQ = MQ + M[MA]; - MA = DF | ((MA + 1) & 07777); - LAC = (LAC & 07777) + M[MA] + (MQ >> 12); - MQ = MQ & 07777; - PC = (PC + 1) & 07777; - break; - } - LAC = LAC | SC; /* mode A: SCA then */ - case 001: /* mode B: ACS */ - if (emode) { - SC = LAC & 037; - LAC = LAC & 010000; - } - else { /* mode A: SCL */ - SC = (~M[IF | PC]) & 037; - PC = (PC + 1) & 07777; - } - break; - - case 022: /* mode B: DST */ - if (emode) { - MA = IF | PC; - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - if (MEM_ADDR_OK (MA)) - M[MA] = MQ & 07777; - MA = DF | ((MA + 1) & 07777); - if (MEM_ADDR_OK (MA)) - M[MA] = LAC & 07777; - PC = (PC + 1) & 07777; - break; - } - LAC = LAC | SC; /* mode A: SCA then */ - case 002: /* MUY */ - MA = IF | PC; - if (emode) { /* mode B: defer */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - } - temp = (MQ * M[MA]) + (LAC & 07777); - LAC = (temp >> 12) & 07777; - MQ = temp & 07777; - PC = (PC + 1) & 07777; - SC = 014; /* 12 shifts */ - break; - - case 023: /* mode B: SWBA */ - if (emode) - break; - LAC = LAC | SC; /* mode A: SCA then */ - case 003: /* DVI */ - MA = IF | PC; - if (emode) { /* mode B: defer */ - if ((MA & 07770) != 00010) /* indirect; autoinc? */ - MA = DF | M[MA]; - else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ - } - if ((LAC & 07777) >= M[MA]) { /* overflow? */ - LAC = LAC | 010000; /* set link */ - MQ = ((MQ << 1) + 1) & 07777; /* rotate MQ */ - SC = 0; /* no shifts */ - } - else { - temp = ((LAC & 07777) << 12) | MQ; - MQ = temp / M[MA]; - LAC = temp % M[MA]; - SC = 015; /* 13 shifts */ - } - PC = (PC + 1) & 07777; - break; - - case 024: /* mode B: DPSZ */ - if (emode) { - if (((LAC | MQ) & 07777) == 0) - PC = (PC + 1) & 07777; - break; - } - LAC = LAC | SC; /* mode A: SCA then */ - case 004: /* NMI */ - temp = (LAC << 12) | MQ; /* preserve link */ - for (SC = 0; ((temp & 017777777) != 0) && - (temp & 040000000) == ((temp << 1) & 040000000); SC++) - temp = temp << 1; - LAC = (temp >> 12) & 017777; - MQ = temp & 07777; - if (emode && ((LAC & 07777) == 04000) && (MQ == 0)) - LAC = LAC & 010000; /* clr if 4000'0000 */ - break; - - case 025: /* mode B: DPIC */ - if (emode) { - temp = (LAC + 1) & 07777; /* SWP already done! */ - LAC = MQ + (temp == 0); - MQ = temp; - break; - } - LAC = LAC | SC; /* mode A: SCA then */ - case 5: /* SHL */ - SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ - if (SC > 25) /* >25? result = 0 */ - temp = 0; - else temp = ((LAC << 12) | MQ) << SC; /* <=25? shift LAC:MQ */ - LAC = (temp >> 12) & 017777; - MQ = temp & 07777; - PC = (PC + 1) & 07777; - SC = emode? 037: 0; /* SC = 0 if mode A */ - break; - - case 026: /* mode B: DCM */ - if (emode) { - temp = (-LAC) & 07777; /* SWP already done! */ - LAC = (MQ ^ 07777) + (temp == 0); - MQ = temp; - break; - } - LAC = LAC | SC; /* mode A: SCA then */ - case 6: /* ASR */ - SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ - temp = ((LAC & 07777) << 12) | MQ; /* sext from AC0 */ - if (LAC & 04000) - temp = temp | ~037777777; - if (emode && (SC != 0)) - gtf = (temp >> (SC - 1)) & 1; - if (SC > 25) - temp = (LAC & 04000)? -1: 0; - else temp = temp >> SC; - LAC = (temp >> 12) & 017777; - MQ = temp & 07777; - PC = (PC + 1) & 07777; - SC = emode? 037: 0; /* SC = 0 if mode A */ - break; - - case 027: /* mode B: SAM */ - if (emode) { - temp = LAC & 07777; - LAC = MQ + (temp ^ 07777) + 1; /* L'AC = MQ - AC */ - gtf = (temp <= MQ) ^ ((temp ^ MQ) >> 11); - break; - } - LAC = LAC | SC; /* mode A: SCA then */ - case 7: /* LSR */ - SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ - temp = ((LAC & 07777) << 12) | MQ; /* clear link */ - if (emode && (SC != 0)) - gtf = (temp >> (SC - 1)) & 1; - if (SC > 24) /* >24? result = 0 */ - temp = 0; - else temp = temp >> SC; /* <=24? shift AC:MQ */ - LAC = (temp >> 12) & 07777; - MQ = temp & 07777; - PC = (PC + 1) & 07777; - SC = emode? 037: 0; /* SC = 0 if mode A */ - break; - } /* end switch */ - break; /* end case 7 */ - -/* Opcode 6, IOT. From Bernhard Baehr's description of the TSC8-75: - - (In user mode) Additional to raising a user mode interrupt, the current IOT - opcode is moved to the ERIOT register. When the IOT is a CDF instruction (62x1), - the ECDF flag is set, otherwise it is cleared. */ - - case 030:case 031:case 032:case 033: /* IOT */ - if (UF) { /* privileged? */ - int_req = int_req | INT_UF; /* request intr */ - tsc_ir = IR; /* save instruction */ - if ((IR & 07707) == 06201) /* set/clear flag */ - tsc_cdf = 1; - else tsc_cdf = 0; - break; - } - device = (IR >> 3) & 077; /* device = IR<3:8> */ - pulse = IR & 07; /* pulse = IR<9:11> */ - iot_data = LAC & 07777; /* AC unchanged */ - switch (device) { /* decode IR<3:8> */ - - case 000: /* CPU control */ - switch (pulse) { /* decode IR<9:11> */ - - case 0: /* SKON */ - if (int_req & INT_ION) - PC = (PC + 1) & 07777; - int_req = int_req & ~INT_ION; - break; - - case 1: /* ION */ - int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; - break; - - case 2: /* IOF */ - int_req = int_req & ~INT_ION; - break; - - case 3: /* SRQ */ - if (int_req & INT_ALL) - PC = (PC + 1) & 07777; - break; - - case 4: /* GTF */ - LAC = (LAC & 010000) | - ((LAC & 010000) >> 1) | (gtf << 10) | - (((int_req & INT_ALL) != 0) << 9) | - (((int_req & INT_ION) != 0) << 7) | SF; - break; - - case 5: /* RTF */ - gtf = ((LAC & 02000) >> 10); - UB = (LAC & 0100) >> 6; - IB = (LAC & 0070) << 9; - DF = (LAC & 0007) << 12; - LAC = ((LAC & 04000) << 1) | iot_data; - int_req = (int_req | INT_ION) & ~INT_NO_CIF_PENDING; - break; - - case 6: /* SGT */ - if (gtf) - PC = (PC + 1) & 07777; - break; - - case 7: /* CAF */ - gtf = 0; - emode = 0; - int_req = int_req & INT_NO_CIF_PENDING; - dev_done = 0; - int_enable = INT_INIT_ENABLE; - LAC = 0; - reset_all (1); /* reset all dev */ - break; - } /* end switch pulse */ - break; /* end case 0 */ - - case 020:case 021:case 022:case 023: - case 024:case 025:case 026:case 027: /* memory extension */ - switch (pulse) { /* decode IR<9:11> */ - - case 1: /* CDF */ - DF = (IR & 0070) << 9; - break; - - case 2: /* CIF */ - IB = (IR & 0070) << 9; - int_req = int_req & ~INT_NO_CIF_PENDING; - break; - - case 3: /* CDF CIF */ - DF = IB = (IR & 0070) << 9; - int_req = int_req & ~INT_NO_CIF_PENDING; - break; - - case 4: - switch (device & 07) { /* decode IR<6:8> */ - - case 0: /* CINT */ - int_req = int_req & ~INT_UF; - break; - - case 1: /* RDF */ - LAC = LAC | (DF >> 9); - break; - - case 2: /* RIF */ - LAC = LAC | (IF >> 9); - break; - - case 3: /* RIB */ - LAC = LAC | SF; - break; - - case 4: /* RMF */ - UB = (SF & 0100) >> 6; - IB = (SF & 0070) << 9; - DF = (SF & 0007) << 12; - int_req = int_req & ~INT_NO_CIF_PENDING; - break; - - case 5: /* SINT */ - if (int_req & INT_UF) - PC = (PC + 1) & 07777; - break; - - case 6: /* CUF */ - UB = 0; - int_req = int_req & ~INT_NO_CIF_PENDING; - break; - - case 7: /* SUF */ - UB = 1; - int_req = int_req & ~INT_NO_CIF_PENDING; - break; - } /* end switch device */ - break; - - default: - reason = stop_inst; - break; - } /* end switch pulse */ - break; /* end case 20-27 */ - - case 010: /* power fail */ - switch (pulse) { /* decode IR<9:11> */ - - case 1: /* SBE */ - break; - - case 2: /* SPL */ - if (int_req & INT_PWR) - PC = (PC + 1) & 07777; - break; - - case 3: /* CAL */ - int_req = int_req & ~INT_PWR; - break; - - default: - reason = stop_inst; - break; - } /* end switch pulse */ - break; /* end case 10 */ - - default: /* I/O device */ - if (dev_tab[device]) { /* dev present? */ -/* ---PiDP add--------------------------------------------------------------------------------------------- */ - // Any other device will trigger IOP, so light pause - Pause = 1; -/* ---PiDP end---------------------------------------------------------------------------------------------- */ - iot_data = dev_tab[device] (IR, iot_data); - LAC = (LAC & 010000) | (iot_data & 07777); - if (iot_data & IOT_SKP) - PC = (PC + 1) & 07777; - if (iot_data >= IOT_REASON) - reason = iot_data >> IOT_V_REASON; - } - else reason = stop_inst; /* stop on flag */ - break; - } /* end switch device */ - break; /* end case IOT */ - } /* end switch opcode */ - -/* ---PiDP add--------------------------------------------------------------------------------------------- */ - // Update the front panel with this instruction's final state. - // - // There's no point saving *every* LED "on" count. We just need a - // suitable amount of oversampling. We can skip this if we called - // set_pidp8i_leds recently enough, avoiding all the expensive bit - // shift and memory update work it does. - // - // The trick here is figuring out what "recently enough" means - // without making expensive OS timer calls. These timers aren't - // hopelessly slow (http://stackoverflow.com/a/13096917/142454) but - // we still don't want to be taking dozens of cycles per instruction - // just to keep our update estimate current in the face of system - // load changes and SET THROTTLE updates. - // - // Instead, we maintain a model of the current IPS value — seeded - // with the initial "SET THROTTLE" value, if any — to figure out - // how many calls we can skip while still meeting our other goals. - // This involves a bit of math, but when paid only once a second, - // it amortizes much nicer than estimating the skip count directly - // based on a more accurate time source which is more expensive - // to call. It's also cheaper than continually asking SIMH to - // estimate the SIMH IPS value, since it uses FP math. - // - // Each LED panel repaint takes about 10 ms, so we do about 100 - // full-panel updates per second. We need a bare minimum of 32 - // discernible brightness values per update for ILS, so if we don't - // update the LED status data at least 3,200 times per second, we - // don't have enough data for smooth panel updates. Fortunately, - // computers are pretty quick, and our slowest script runs at 30 - // kIPS. (5.script.) - // - // We deliberately add some timing jitter here to get stochastic - // sampling of the incoming instructions to avoid beat frequencies - // between our update rate and the instruction pattern being - // executed by the front panel. It's a form of dithering. - // - // You might think to move this code to the top of set_pidp8i_leds, - // but the function call itself is a nontrivial hit. In fact, you - // don't even want to move all of this to a function here in this - // module and try to get GCC to inline it: that's good for a 1 MIPS - // speed hit in my testing! (GCC 4.9.2, Raspbian Jessie on Pi 3B.) - - if (++skip_count >= (max_skips - dither)) { - // Save skips to inst counter and reset - inst_count += skip_count; - skip_count = 0; - - // We need to update the LED data again - set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, int_req, Pause); - - // Has it been ~1s since we updated our max_skips value? - time_t now; - if (time(&now) > last_update) { - // Yep; simulator IPS may have changed, so freshen it. - last_update = now; - max_skips = inst_count / pidp8i_updates_per_sec; - //printf("Inst./repaint: %zu - %zu; %.2f MIPS\r\n", - // max_skips, dither, inst_count / 1e6); - inst_count = 0; - } - dither = max_skips > 32 ? lrand48() % (max_skips >> 3) : 0; // 12.5% - } - Pause = 0; // it's set outside the "if", so it must be *reset* outside -/* ---PiDP end---------------------------------------------------------------------------------------------- */ - } /* end while */ - -/* Simulation halted */ - -saved_PC = IF | (PC & 07777); /* save copies */ -saved_DF = DF & 070000; -saved_LAC = LAC & 017777; -saved_MQ = MQ & 07777; -pcq_r->qptr = pcq_p; /* update pc q ptr */ -return reason; -} /* end sim_instr */ - -/* Reset routine */ - -t_stat cpu_reset (DEVICE *dptr) -{ -saved_LAC = 0; -int_req = (int_req & ~INT_ION) | INT_NO_CIF_PENDING; -saved_DF = IB = saved_PC & 070000; -UF = UB = gtf = emode = 0; -pcq_r = find_reg ("PCQ", NULL, dptr); -if (pcq_r) - pcq_r->qptr = 0; -else return SCPE_IERR; -sim_brk_types = SWMASK ('E') | SWMASK('I'); -sim_brk_dflt = SWMASK ('E'); -return SCPE_OK; -} - -/* Set PC for boot (PC<14:12> will typically be 0) */ - -void cpu_set_bootpc (int32 pc) -{ -saved_PC = pc; /* set PC, IF */ -saved_DF = IB = pc & 070000; /* set IB, DF */ -return; -} - -/* Memory examine */ - -t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) -{ -if (addr >= MEMSIZE) - return SCPE_NXM; -if (vptr != NULL) - *vptr = M[addr] & 07777; -return SCPE_OK; -} - -/* Memory deposit */ - -t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) -{ -if (addr >= MEMSIZE) - return SCPE_NXM; -M[addr] = val & 07777; -return SCPE_OK; -} - -/* Memory size change */ - -t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) -{ -int32 mc = 0; -uint32 i; - -if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) - return SCPE_ARG; -for (i = val; i < MEMSIZE; i++) - mc = mc | M[i]; -if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) - return SCPE_OK; -MEMSIZE = val; -for (i = MEMSIZE; i < MAXMEMSIZE; i++) - M[i] = 0; -return SCPE_OK; -} - -/* Change device number for a device */ - -t_stat set_dev (UNIT *uptr, int32 val, CONST char *cptr, void *desc) -{ -DEVICE *dptr; -DIB *dibp; -uint32 newdev; -t_stat r; - -if (cptr == NULL) - return SCPE_ARG; -if (uptr == NULL) - return SCPE_IERR; -dptr = find_dev_from_unit (uptr); -if (dptr == NULL) - return SCPE_IERR; -dibp = (DIB *) dptr->ctxt; -if (dibp == NULL) - return SCPE_IERR; -newdev = get_uint (cptr, 8, DEV_MAX - 1, &r); /* get new */ -if ((r != SCPE_OK) || (newdev == dibp->dev)) - return r; -dibp->dev = newdev; /* store */ -return SCPE_OK; -} - -/* Show device number for a device */ - -t_stat show_dev (FILE *st, UNIT *uptr, int32 val, CONST void *desc) -{ -DEVICE *dptr; -DIB *dibp; - -if (uptr == NULL) - return SCPE_IERR; -dptr = find_dev_from_unit (uptr); -if (dptr == NULL) - return SCPE_IERR; -dibp = (DIB *) dptr->ctxt; -if (dibp == NULL) - return SCPE_IERR; -fprintf (st, "devno=%02o", dibp->dev); -if (dibp->num > 1) - fprintf (st, "-%2o", dibp->dev + dibp->num - 1); -return SCPE_OK; -} - -/* CPU device handler - should never get here! */ - -int32 bad_dev (int32 IR, int32 AC) -{ -return (SCPE_IERR << IOT_V_REASON) | AC; /* broken! */ -} - -/* Build device dispatch table */ - -t_bool build_dev_tab (void) -{ -DEVICE *dptr; -DIB *dibp; -uint32 i, j; -static const uint8 std_dev[] = { - 000, 010, 020, 021, 022, 023, 024, 025, 026, 027 - }; - -for (i = 0; i < DEV_MAX; i++) /* clr table */ - dev_tab[i] = NULL; -for (i = 0; i < ((uint32) sizeof (std_dev)); i++) /* std entries */ - dev_tab[std_dev[i]] = &bad_dev; -for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* add devices */ - dibp = (DIB *) dptr->ctxt; /* get DIB */ - if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ - if (dibp->dsp_tbl) { /* dispatch table? */ - DIB_DSP *dspp = dibp->dsp_tbl; /* set ptr */ - for (j = 0; j < dibp->num; j++, dspp++) { /* loop thru tbl */ - if (dspp->dsp) { /* any dispatch? */ - if (dev_tab[dspp->dev]) { /* already filled? */ - sim_printf ("%s device number conflict at %02o\n", - sim_dname (dptr), dibp->dev + j); - return TRUE; - } - dev_tab[dspp->dev] = dspp->dsp; /* fill */ - } /* end if dsp */ - } /* end for j */ - } /* end if dsp_tbl */ - else { /* inline dispatches */ - for (j = 0; j < dibp->num; j++) { /* loop thru disp */ - if (dibp->dsp[j]) { /* any dispatch? */ - if (dev_tab[dibp->dev + j]) { /* already filled? */ - sim_printf ("%s device number conflict at %02o\n", - sim_dname (dptr), dibp->dev + j); - return TRUE; - } - dev_tab[dibp->dev + j] = dibp->dsp[j]; /* fill */ - } /* end if dsp */ - } /* end for j */ - } /* end else */ - } /* end if enb */ - } /* end for i */ -return FALSE; -} - -/* Set history */ - -t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc) -{ -int32 i, lnt; -t_stat r; - -if (cptr == NULL) { - for (i = 0; i < hst_lnt; i++) - hst[i].pc = 0; - hst_p = 0; - return SCPE_OK; - } -lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); -if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) - return SCPE_ARG; -hst_p = 0; -if (hst_lnt) { - free (hst); - hst_lnt = 0; - hst = NULL; - } -if (lnt) { - hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); - if (hst == NULL) - return SCPE_MEM; - hst_lnt = lnt; - } -return SCPE_OK; -} - -/* Show history */ - -t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc) -{ -int32 l, k, di, lnt; -const char *cptr = (const char *) desc; -t_stat r; -InstHistory *h; - -if (hst_lnt == 0) /* enabled? */ - return SCPE_NOFNC; -if (cptr) { - lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); - if ((r != SCPE_OK) || (lnt == 0)) - return SCPE_ARG; - } -else lnt = hst_lnt; -di = hst_p - lnt; /* work forward */ -if (di < 0) - di = di + hst_lnt; -fprintf (st, "PC L AC MQ ea IR\n\n"); -for (k = 0; k < lnt; k++) { /* print specified */ - h = &hst[(++di) % hst_lnt]; /* entry pointer */ - if (h->pc & HIST_PC) { /* instruction? */ - l = (h->lac >> 12) & 1; /* link */ - fprintf (st, "%05o %o %04o %04o ", h->pc & ADDRMASK, l, h->lac & 07777, h->mq); - if (h->ir < 06000) - fprintf (st, "%05o ", h->ea); - else fprintf (st, " "); - sim_eval[0] = h->ir; - if ((fprint_sym (st, h->pc & ADDRMASK, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) - fprintf (st, "(undefined) %04o", h->ir); - if (h->ir < 04000) - fprintf (st, " [%04o]", h->opnd); - fputc ('\n', st); /* end line */ - } /* end else instruction */ - } /* end for */ -return SCPE_OK; -} DELETED src/PDP8/pdp8_ct.c Index: src/PDP8/pdp8_ct.c ================================================================== --- src/PDP8/pdp8_ct.c +++ /dev/null @@ -1,729 +0,0 @@ -/* 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 - - KGOA /READ 1ST CHAR, CLEAR DF - DCA CHAR - : - - KGOA /READ LAST CHAR, CLEAR DF - DCA CHAR - - KLSA /SELECT CRC MODE - KGOA /READ 1ST CRC - - KGOA /READ 2ND CRC - - - Write: KLSA /SELECT WRITE - TAD CHAR /1ST CHAR - KGOA /INIT WRITE, CHAR TO BUF, CLEAR DF - - : - TAD CHAR /LAST CHAR - KGOA /CHAR TO BUF, CLEAR DF - - KLSA /SELECT CRC MODE - KGOA /WRITE CRC, CLEAR DF - -*/ - -#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; -} DELETED src/PDP8/pdp8_defs.h Index: src/PDP8/pdp8_defs.h ================================================================== --- src/PDP8/pdp8_defs.h +++ /dev/null @@ -1,258 +0,0 @@ -/* 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 DELETED src/PDP8/pdp8_df.c Index: src/PDP8/pdp8_df.c ================================================================== --- src/PDP8/pdp8_df.c +++ /dev/null @@ -1,382 +0,0 @@ -/* 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 - -#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 - pulse 4 = df_sta = df_sta | AC - AC = AC | old_df_sta - 6616 pulse 2 = clear AC, skip if address confirmed - pulse 4 = df_sta = df_sta | AC = 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; -} DELETED src/PDP8/pdp8_dt.c Index: src/PDP8/pdp8_dt.c ================================================================== --- src/PDP8/pdp8_dt.c +++ /dev/null @@ -1,1349 +0,0 @@ -/* pdp8_dt.c: PDP-8 DECtape simulator - - Copyright (c) 1993-2017, Robert M Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not be - used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. - - dt TC08/TU56 DECtape - - 15-Mar-17 RMS Fixed dt_seterr to clear successor states - 17-Sep-13 RMS Changed to use central set_bootpc routine - 23-Jun-06 RMS Fixed switch conflict in ATTACH - 07-Jan-06 RMS Fixed unaligned register access bug (Doug Carman) - 16-Aug-05 RMS Fixed C++ declaration and cast problems - 25-Jan-04 RMS Revised for device debug support - 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR - 18-Oct-03 RMS Fixed bugs in read all, tightened timing - 25-Apr-03 RMS Revised for extended file support - 14-Mar-03 RMS Fixed sizing interaction with save/restore - 17-Oct-02 RMS Fixed bug in end of reel logic - 04-Oct-02 RMS Added DIB, device number support - 12-Sep-02 RMS Added support for 16b format - 30-May-02 RMS Widened POS to 32b - 06-Jan-02 RMS Changed enable/disable support - 30-Nov-01 RMS Added read only unit, extended SET/SHOW support - 24-Nov-01 RMS Changed POS, STATT, LASTT, FLG to arrays - 29-Aug-01 RMS Added casts to PDP-18b packup routine - 17-Jul-01 RMS Moved function prototype - 11-May-01 RMS Fixed bug in reset - 25-Apr-01 RMS Added device enable/disable support - 18-Apr-01 RMS Changed to rewind tape before boot - 19-Mar-01 RMS Changed bootstrap to support 4k disk monitor - 15-Mar-01 RMS Added 129th word to PDP-8 format - - PDP-8 DECtapes are represented in memory by fixed length buffer of 16b words. - Three file formats are supported: - - 18b/36b 256 words per block [256 x 18b] - 16b 256 words per block [256 x 16b] - 12b 129 words per block [129 x 12b] - - When a 16b or 18/36bb DECtape file is read in, it is converted to 12b format. - - DECtape motion is measured in 3b lines. Time between lines is 33.33us. - Tape density is nominally 300 lines per inch. The format of a DECtape (as - taken from the TD8E formatter) is: - - reverse end zone 8192 reverse end zone codes ~ 10 feet - reverse buffer 200 interblock codes - block 0 - : - block n - forward buffer 200 interblock codes - forward end zone 8192 forward end zone codes ~ 10 feet - - A block consists of five 18b header words, a tape-specific number of data - words, and five 18b trailer words. All systems except the PDP-8 use a - standard block length of 256 words; the PDP-8 uses a standard block length - of 86 words (x 18b = 129 words x 12b). - - Because a DECtape file only contains data, the simulator cannot support - write timing and mark track and can only do a limited implementation - of read all and write all. Read all assumes that the tape has been - conventionally written forward: - - header word 0 0 - header word 1 block number (for forward reads) - header words 2,3 0 - header word 4 checksum (for reverse reads) - : - trailer word 4 checksum (for forward reads) - trailer words 3,2 0 - trailer word 1 block number (for reverse reads) - trailer word 0 0 - - Write all writes only the data words and dumps the non-data words in the - bit bucket. -*/ - -#include "pdp8_defs.h" - -#define DT_NUMDR 8 /* #drives */ -#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ -#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ -#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ -#define UNIT_WLK (1 << UNIT_V_WLK) -#define UNIT_8FMT (1 << UNIT_V_8FMT) -#define UNIT_11FMT (1 << UNIT_V_11FMT) -#define STATE u3 /* unit state */ -#define LASTT u4 /* last time update */ -#define WRITTEN u5 /* device buffer is dirty and needs flushing */ -#define DT_WC 07754 /* word count */ -#define DT_CA 07755 /* current addr */ -#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ - -/* System independent DECtape constants */ - -#define DT_LPERMC 6 /* lines per mark track */ -#define DT_BLKWD 1 /* blk no word in h/t */ -#define DT_CSMWD 4 /* checksum word in h/t */ -#define DT_HTWRD 5 /* header/trailer words */ -#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ -#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ -#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ -#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ -#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ - -/* 16b, 18b, 36b DECtape constants */ - -#define D18_WSIZE 6 /* word size in lines */ -#define D18_BSIZE 384 /* block size in 12b */ -#define D18_TSIZE 578 /* tape size */ -#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) -#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) -#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ - -#define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE) -#define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32)) -#define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16)) - -/* 12b DECtape constants */ - -#define D8_WSIZE 4 /* word size in lines */ -#define D8_BSIZE 129 /* block size in 12b */ -#define D8_TSIZE 1474 /* tape size */ -#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) -#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) -#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ -#define D8_FILSIZ (D8_CAPAC * sizeof (int16)) - -/* This controller */ - -#define DT_CAPAC D8_CAPAC /* default */ -#define DT_WSIZE D8_WSIZE - -/* Calculated constants, per unit */ - -#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) -#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) -#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) -#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) -#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) - -#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) -#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) -#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) -#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) -#define DT_QREZ(u) (((u)->pos) < DT_EZLIN) -#define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) -#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) - -/* Status register A */ - -#define DTA_V_UNIT 9 /* unit select */ -#define DTA_M_UNIT 07 -#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) -#define DTA_V_MOT 7 /* motion */ -#define DTA_M_MOT 03 -#define DTA_V_MODE 6 /* mode */ -#define DTA_V_FNC 3 /* function */ -#define DTA_M_FNC 07 -#define FNC_MOVE 00 /* move */ -#define FNC_SRCH 01 /* search */ -#define FNC_READ 02 /* read */ -#define FNC_RALL 03 /* read all */ -#define FNC_WRIT 04 /* write */ -#define FNC_WALL 05 /* write all */ -#define FNC_WMRK 06 /* write timing */ -#define DTA_V_ENB 2 /* int enable */ -#define DTA_V_CERF 1 /* clr error flag */ -#define DTA_V_CDTF 0 /* clr DECtape flag */ -#define DTA_FWDRV (1u << (DTA_V_MOT + 1)) -#define DTA_STSTP (1u << DTA_V_MOT) -#define DTA_MODE (1u << DTA_V_MODE) -#define DTA_ENB (1u << DTA_V_ENB) -#define DTA_CERF (1u << DTA_V_CERF) -#define DTA_CDTF (1u << DTA_V_CDTF) -#define DTA_RW (07777 & ~(DTA_CERF | DTA_CDTF)) - -#define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT) -#define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) -#define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) - -/* Status register B */ - -#define DTB_V_ERF 11 /* error flag */ -#define DTB_V_MRK 10 /* mark trk err */ -#define DTB_V_END 9 /* end zone err */ -#define DTB_V_SEL 8 /* select err */ -#define DTB_V_PAR 7 /* parity err */ -#define DTB_V_TIM 6 /* timing err */ -#define DTB_V_MEX 3 /* memory extension */ -#define DTB_M_MEX 07 -#define DTB_MEX (DTB_M_MEX << DTB_V_MEX) -#define DTB_V_DTF 0 /* DECtape flag */ -#define DTB_ERF (1u << DTB_V_ERF) -#define DTB_MRK (1u << DTB_V_MRK) -#define DTB_END (1u << DTB_V_END) -#define DTB_SEL (1u << DTB_V_SEL) -#define DTB_PAR (1u << DTB_V_PAR) -#define DTB_TIM (1u << DTB_V_TIM) -#define DTB_DTF (1u << DTB_V_DTF) -#define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \ - DTB_PAR | DTB_TIM) -#define DTB_GETMEX(x) (((x) & DTB_MEX) << (12 - DTB_V_MEX)) - -/* DECtape state */ - -#define DTS_V_MOT 3 /* motion */ -#define DTS_M_MOT 07 -#define DTS_STOP 0 /* stopped */ -#define DTS_DECF 2 /* decel, fwd */ -#define DTS_DECR 3 /* decel, rev */ -#define DTS_ACCF 4 /* accel, fwd */ -#define DTS_ACCR 5 /* accel, rev */ -#define DTS_ATSF 6 /* @speed, fwd */ -#define DTS_ATSR 7 /* @speed, rev */ -#define DTS_DIR 01 /* dir mask */ -#define DTS_V_FNC 0 /* function */ -#define DTS_M_FNC 07 -#define DTS_OFR 7 /* "off reel" */ -#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) -#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) -#define DTS_V_2ND 6 /* next state */ -#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ -#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) -#define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) -#define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ - ((DTS_STA (y, z)) << DTS_V_2ND) -#define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ - ((DTS_STA (y, z)) << DTS_V_3RD) -#define DTS_NXTSTA(x) (x >> DTS_V_2ND) - -/* Operation substates */ - -#define DTO_WCO 1 /* wc overflow */ -#define DTO_SOB 2 /* start of block */ - -/* Logging */ - -#define LOG_MS 001 /* move, search */ -#define LOG_RW 002 /* read, write */ -#define LOG_BL 004 /* block # lblk */ - -#define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \ - int_req = int_req | INT_DTA; \ - else int_req = int_req & ~INT_DTA; -#define ABS(x) (((x) < 0)? (-(x)): (x)) - -extern uint16 M[]; -extern int32 int_req; -extern UNIT cpu_unit; - -int32 dtsa = 0; /* status A */ -int32 dtsb = 0; /* status B */ -int32 dt_ltime = 12; /* interline time */ -int32 dt_dctime = 40000; /* decel time */ -int32 dt_substate = 0; -int32 dt_logblk = 0; -int32 dt_stopoffr = 0; - -int32 dt76 (int32 IR, int32 AC); -int32 dt77 (int32 IR, int32 AC); -t_stat dt_svc (UNIT *uptr); -t_stat dt_reset (DEVICE *dptr); -t_stat dt_attach (UNIT *uptr, CONST char *cptr); -void dt_flush (UNIT *uptr); -t_stat dt_detach (UNIT *uptr); -t_stat dt_boot (int32 unitno, DEVICE *dptr); -void dt_deselect (int32 oldf); -void dt_newsa (int32 newf); -void dt_newfnc (UNIT *uptr, int32 newsta); -t_bool dt_setpos (UNIT *uptr); -void dt_schedez (UNIT *uptr, int32 dir); -void dt_seterr (UNIT *uptr, int32 e); -int32 dt_comobv (int32 val); -int32 dt_csum (UNIT *uptr, int32 blk); -int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir); - -/* DT data structures - - dt_dev DT device descriptor - dt_unit DT unit list - dt_reg DT register list - dt_mod DT modifier list -*/ - -DIB dt_dib = { DEV_DTA, 2, { &dt76, &dt77 } }; - -UNIT dt_unit[] = { - { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ - UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, - { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ - UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, - { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ - UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, - { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ - UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, - { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ - UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, - { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ - UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, - { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ - UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, - { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ - UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) } - }; - -REG dt_reg[] = { - { ORDATAD (DTSA, dtsa, 12, "status register A") }, - { ORDATAD (DTSB, dtsb, 12, "status register B") }, - { FLDATAD (INT, int_req, INT_V_DTA, "interrupt pending flag") }, - { FLDATAD (ENB, dtsa, DTA_V_ENB, "interrupt enable flag") }, - { FLDATAD (DTF, dtsb, DTB_V_DTF, "DECtape flag") }, - { FLDATAD (ERF, dtsb, DTB_V_ERF, "error flag") }, - { ORDATAD (WC, M[DT_WC], 12, "word count (memory location 7755)"), REG_FIT }, - { ORDATAD (CA, M[DT_CA], 12, "current address (memory location 7754)"), REG_FIT }, - { DRDATAD (LTIME, dt_ltime, 24, "time between lines"), REG_NZ | PV_LEFT }, - { DRDATAD (DCTIME, dt_dctime, 24, "time to decelerate to a full stop"), REG_NZ | PV_LEFT }, - { ORDATAD (SUBSTATE, dt_substate, 2, "read/write command substate") }, - { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, - { URDATAD (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, - DT_NUMDR, PV_LEFT | REG_RO, "position, in lines, units 0 to 7") }, - { URDATAD (STATT, dt_unit[0].STATE, 8, 18, 0, - DT_NUMDR, REG_RO, "unit state, units 0 to 7") }, - { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0, - DT_NUMDR, REG_HRO) }, - { FLDATAD (STOP_OFFR, dt_stopoffr, 0, "stop on off-reel error") }, - { ORDATA (DEVNUM, dt_dib.dev, 6), REG_HRO }, - { NULL } - }; - -MTAB dt_mod[] = { - { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, - { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, - { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, - { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, - { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, - { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", - &set_dev, &show_dev, NULL }, - { 0 } - }; - -DEBTAB dt_deb[] = { - { "MOTION", LOG_MS }, - { "DATA", LOG_RW }, - { "BLOCK", LOG_BL }, - { NULL, 0 } - }; - -DEVICE dt_dev = { - "DT", dt_unit, dt_reg, dt_mod, - DT_NUMDR, 8, 24, 1, 8, 12, - NULL, NULL, &dt_reset, - &dt_boot, &dt_attach, &dt_detach, - &dt_dib, DEV_DISABLE | DEV_DEBUG, 0, - dt_deb, NULL, NULL - }; - -/* IOT routines */ - -int32 dt76 (int32 IR, int32 AC) -{ -int32 pulse = IR & 07; -int32 old_dtsa = dtsa, fnc; -UNIT *uptr; - -if (pulse & 01) /* DTRA */ - AC = AC | dtsa; -if (pulse & 06) { /* select */ - if (pulse & 02) /* DTCA */ - dtsa = 0; - if (pulse & 04) { /* DTXA */ - if ((AC & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR; - if ((AC & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF; - dtsa = dtsa ^ (AC & DTA_RW); - AC = 0; /* clr AC */ - } - if ((old_dtsa ^ dtsa) & DTA_UNIT) - dt_deselect (old_dtsa); - uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */ - fnc = DTA_GETFNC (dtsa); /* get fnc */ - if (((uptr->flags) & UNIT_DIS) || /* disabled? */ - (fnc >= FNC_WMRK) || /* write mark? */ - ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) || - ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT))) - dt_seterr (uptr, DTB_SEL); /* select err */ - else dt_newsa (dtsa); - DT_UPDINT; - } -return AC; -} - -int32 dt77 (int32 IR, int32 AC) -{ -int32 pulse = IR & 07; - -if ((pulse & 01) && (dtsb & (DTB_ERF |DTB_DTF))) /* DTSF */ - AC = IOT_SKP | AC; -if (pulse & 02) /* DTRB */ - AC = AC | dtsb; -if (pulse & 04) { /* DTLB */ - dtsb = (dtsb & ~DTB_MEX) | (AC & DTB_MEX); - AC = AC & ~07777; /* clear AC */ - } -return AC; -} - -/* Unit deselect */ - -void dt_deselect (int32 oldf) -{ -int32 old_unit = DTA_GETUNIT (oldf); -UNIT *uptr = dt_dev.units + old_unit; -int32 old_mot = DTS_GETMOT (uptr->STATE); - -if (old_mot >= DTS_ATSF) /* at speed? */ - dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); -else if (old_mot >= DTS_ACCF) /* accelerating? */ - DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); -return; -} - -/* Command register change - - 1. If change in motion, stop to start - - schedule acceleration - - set function as next state - 2. If change in motion, start to stop - - if not already decelerating (could be reversing), - schedule deceleration - 3. If change in direction, - - if not decelerating, schedule deceleration - - set accelerating (other dir) as next state - - set function as next next state - 4. If not accelerating or at speed, - - schedule acceleration - - set function as next state - 5. If not yet at speed, - - set function as next state - 6. If at speed, - - set function as current state, schedule function -*/ - -void dt_newsa (int32 newf) -{ -int32 new_unit, prev_mot, new_fnc; -int32 prev_mving, new_mving, prev_dir, new_dir; -UNIT *uptr; - -new_unit = DTA_GETUNIT (newf); /* new, old units */ -uptr = dt_dev.units + new_unit; -if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ - dt_seterr (uptr, DTB_SEL); /* no, error */ - return; - } -prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ -prev_mving = prev_mot != DTS_STOP; /* previous moving? */ -prev_dir = prev_mot & DTS_DIR; /* previous dir? */ -new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ -new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ -new_fnc = DTA_GETFNC (newf); /* new function? */ - -if ((prev_mving | new_mving) == 0) /* stop to stop */ - return; - -if (new_mving & ~prev_mving) { /* start? */ - if (dt_setpos (uptr)) /* update pos */ - return; - sim_cancel (uptr); /* stop current */ - sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* schedule acc */ - DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ - DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ - return; - } - -if (prev_mving & ~new_mving) { /* stop? */ - if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ - if (dt_setpos (uptr)) /* update pos */ - return; - sim_cancel (uptr); /* stop current */ - sim_activate (uptr, dt_dctime); /* schedule decel */ - } - DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ - return; - } - -if (prev_dir ^ new_dir) { /* dir chg? */ - if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ - if (dt_setpos (uptr)) /* update pos */ - return; - sim_cancel (uptr); /* stop current */ - sim_activate (uptr, dt_dctime); /* schedule decel */ - } - DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ - DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ - DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ - return; - } - -if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ - if (dt_setpos (uptr)) /* update pos */ - return; - sim_cancel (uptr); /* cancel cur */ - sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ - DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ - DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ - return; - } - -if (prev_mot < DTS_ATSF) { /* not at speed? */ - DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ - return; - } - -dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ -return; -} - -/* Schedule new DECtape function - - This routine is only called if - - the selected unit is attached - - the selected unit is at speed (forward or backward) - - This routine - - updates the selected unit's position - - updates the selected unit's state - - schedules the new operation -*/ - -void dt_newfnc (UNIT *uptr, int32 newsta) -{ -int32 fnc, dir, blk, unum, relpos, newpos; -uint32 oldpos; - -oldpos = uptr->pos; /* save old pos */ -if (dt_setpos (uptr)) /* update pos */ - return; -uptr->STATE = newsta; /* update state */ -fnc = DTS_GETFNC (uptr->STATE); /* set variables */ -dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; -unum = (int32) (uptr - dt_dev.units); -if (oldpos == uptr->pos) /* bump pos */ - uptr->pos = uptr->pos + (dir? -1: 1); -blk = DT_LIN2BL (uptr->pos, uptr); - -if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ - dt_seterr (uptr, DTB_END); /* set ez flag, stop */ - return; - } -sim_cancel (uptr); /* cancel cur op */ -dt_substate = DTO_SOB; /* substate = block start */ -switch (fnc) { /* case function */ - - case DTS_OFR: /* off reel */ - if (dir) /* rev? < start */ - newpos = -1000; - else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ - break; - - case FNC_MOVE: /* move */ - dt_schedez (uptr, dir); /* sched end zone */ - if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n", - unum, (dir? "backward": "forward")); - return; /* done */ - - case FNC_SRCH: /* search */ - if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? - DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; - else newpos = DT_BLK2LN ((DT_QREZ (uptr)? - 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); - if (DEBUG_PRI (dt_dev, LOG_MS)) - fprintf (sim_deb, ">>DT%d: searching %s]\n", - unum, (dir? "backward": "forward")); - break; - - case FNC_WRIT: /* write */ - case FNC_READ: /* read */ - case FNC_RALL: /* read all */ - case FNC_WALL: /* write all */ - if (DT_QEZ (uptr)) { /* in "ok" end zone? */ - if (dir) - newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; - else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); - break; - } - relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ - if ((relpos >= DT_HTLIN) && /* in data zone? */ - (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { - dt_seterr (uptr, DTB_SEL); - return; - } - if (dir) - newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? - blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; - else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? - blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); - break; - - default: - dt_seterr (uptr, DTB_SEL); /* bad state */ - return; - } - -sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); -return; -} - -/* Update DECtape position - - DECtape motion is modeled as a constant velocity, with linear - acceleration and deceleration. The motion equations are as follows: - - t = time since operation started - tmax = time for operation (accel, decel only) - v = at speed velocity in lines (= 1/dt_ltime) - - Then: - at speed dist = t * v - accel dist = (t^2 * v) / (2 * tmax) - decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) - - This routine uses the relative (integer) time, rather than the absolute - (floating point) time, to allow save and restore of the start times. -*/ - -t_bool dt_setpos (UNIT *uptr) -{ -uint32 new_time, ut, ulin, udelt; -int32 mot = DTS_GETMOT (uptr->STATE); -int32 unum, delta; - -new_time = sim_grtime (); /* current time */ -ut = new_time - uptr->LASTT; /* elapsed time */ -if (ut == 0) /* no time gone? exit */ - return FALSE; -uptr->LASTT = new_time; /* update last time */ -switch (mot & ~DTS_DIR) { /* case on motion */ - - case DTS_STOP: /* stop */ - delta = 0; - break; - - case DTS_DECF: /* slowing */ - ulin = ut / (uint32) dt_ltime; - udelt = dt_dctime / dt_ltime; - delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); - break; - - case DTS_ACCF: /* accelerating */ - ulin = ut / (uint32) dt_ltime; - udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; - delta = (ulin * ulin) / (2 * udelt); - break; - - case DTS_ATSF: /* at speed */ - delta = ut / (uint32) dt_ltime; - break; - } - -if (mot & DTS_DIR) /* update pos */ - uptr->pos = uptr->pos - delta; -else uptr->pos = uptr->pos + delta; -if (((int32) uptr->pos < 0) || - ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { - detach_unit (uptr); /* off reel? */ - uptr->STATE = uptr->pos = 0; - unum = (int32) (uptr - dt_dev.units); - if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ - dt_seterr (uptr, DTB_SEL); /* error */ - return TRUE; - } -return FALSE; -} - -/* Unit service - - Unit must be attached, detach cancels operation -*/ - -t_stat dt_svc (UNIT *uptr) -{ -int32 mot = DTS_GETMOT (uptr->STATE); -int32 dir = mot & DTS_DIR; -int32 fnc = DTS_GETFNC (uptr->STATE); -int16 *fbuf = (int16 *) uptr->filebuf; -int32 unum = uptr - dt_dev.units; -int32 blk, wrd, ma, relpos, dat; -uint32 ba; - -/* Motion cases - - Decelerating - if next state != stopped, must be accel reverse - Accelerating - next state must be @speed, schedule function - At speed - do functional processing -*/ - -switch (mot) { - - case DTS_DECF: case DTS_DECR: /* decelerating */ - if (dt_setpos (uptr)) /* upd pos; off reel? */ - return IORETURN (dt_stopoffr, STOP_DTOFF); - uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ - if (uptr->STATE) /* not stopped? */ - sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* must be reversing */ - return SCPE_OK; - - case DTS_ACCF: case DTS_ACCR: /* accelerating */ - dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ - return SCPE_OK; - - case DTS_ATSF: case DTS_ATSR: /* at speed */ - break; /* check function */ - - default: /* other */ - dt_seterr (uptr, DTB_SEL); /* state error */ - return SCPE_OK; - } - -/* Functional cases - - Move - must be at end zone - Search - transfer block number, schedule next block - Off reel - detach unit (it must be deselected) -*/ - -if (dt_setpos (uptr)) /* upd pos; off reel? */ - return IORETURN (dt_stopoffr, STOP_DTOFF); -if (DT_QEZ (uptr)) { /* in end zone? */ - dt_seterr (uptr, DTB_END); /* end zone error */ - return SCPE_OK; - } -blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ -switch (fnc) { /* at speed, check fnc */ - - case FNC_MOVE: /* move */ - dt_seterr (uptr, DTB_END); /* end zone error */ - return SCPE_OK; - - case FNC_SRCH: /* search */ - if (dtsb & DTB_DTF) { /* DTF set? */ - dt_seterr (uptr, DTB_TIM); /* timing error */ - return SCPE_OK; - } - sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ - M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr word cnt */ - ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ - if (MEM_ADDR_OK (ma)) /* store block # */ - M[ma] = blk & 07777; - if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) - dtsb = dtsb | DTB_DTF; /* set DTF */ - break; - - case DTS_OFR: /* off reel */ - detach_unit (uptr); /* must be deselected */ - uptr->STATE = uptr->pos = 0; /* no visible action */ - break; - -/* Read has four subcases - - Start of block, not wc ovf - check that DTF is clear, otherwise normal - Normal - increment MA, WC, copy word from tape to memory - if read dir != write dir, bits must be scrambled - if wc overflow, next state is wc overflow - if end of block, possibly set DTF, next state is start of block - Wc ovf, not start of block - - if end of block, possibly set DTF, next state is start of block - Wc ovf, start of block - if end of block reached, timing error, - otherwise, continue to next word -*/ - - case FNC_READ: /* read */ - wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ - switch (dt_substate) { /* case on substate */ - - case DTO_SOB: /* start of block */ - if (dtsb & DTB_DTF) { /* DTF set? */ - dt_seterr (uptr, DTB_TIM); /* timing error */ - return SCPE_OK; - } - if (DEBUG_PRI (dt_dev, LOG_RW) || - (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) - fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n", - unum, blk, (dir? "backward": "forward"), - ((dtsa & DTA_MODE)? " continuous": " ")); - dt_substate = 0; - /* fall through */ - case 0: /* normal read */ - M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ - M[DT_CA] = (M[DT_CA] + 1) & 07777; - ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ - ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ - dat = fbuf[ba]; /* get tape word */ - if (dir) /* rev? comp obv */ - dat = dt_comobv (dat); - if (MEM_ADDR_OK (ma)) /* mem addr legal? */ - M[ma] = dat; - if (M[DT_WC] == 0) /* wc ovf? */ - dt_substate = DTO_WCO; - /* fall through */ - case DTO_WCO: /* wc ovf, not sob */ - if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ - sim_activate (uptr, DT_WSIZE * dt_ltime); - else { - dt_substate = dt_substate | DTO_SOB; - sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); - if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) - dtsb = dtsb | DTB_DTF; /* set DTF */ - } - break; - - case DTO_WCO | DTO_SOB: /* next block */ - if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */ - dt_seterr (uptr, DTB_TIM); /* timing error */ - else sim_activate (uptr, DT_WSIZE * dt_ltime); - break; - } - - break; - -/* Write has four subcases - - Start of block, not wc ovf - check that DTF is clear, set block direction - Normal - increment MA, WC, copy word from memory to tape - if wc overflow, next state is wc overflow - if end of block, possibly set DTF, next state is start of block - Wc ovf, not start of block - - copy 0 to tape - if end of block, possibly set DTF, next state is start of block - Wc ovf, start of block - schedule end zone -*/ - - case FNC_WRIT: /* write */ - wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ - switch (dt_substate) { /* case on substate */ - - case DTO_SOB: /* start block */ - if (dtsb & DTB_DTF) { /* DTF set? */ - dt_seterr (uptr, DTB_TIM); /* timing error */ - return SCPE_OK; - } - if (DEBUG_PRI (dt_dev, LOG_RW) || - (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) - fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk, - (dir? "backward": "forward"), - ((dtsa & DTA_MODE)? " continuous": " ")); - dt_substate = 0; - /* fall through */ - case 0: /* normal write */ - M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ - M[DT_CA] = (M[DT_CA] + 1) & 07777; - /* fall through */ - case DTO_WCO: /* wc ovflo */ - ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ - ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ - dat = dt_substate? 0: M[ma]; /* get word */ - if (dir) /* rev? comp obv */ - dat = dt_comobv (dat); - fbuf[ba] = dat; /* write word */ - uptr->WRITTEN = TRUE; - if (ba >= uptr->hwmark) - uptr->hwmark = ba + 1; - if (M[DT_WC] == 0) - dt_substate = DTO_WCO; - if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ - sim_activate (uptr, DT_WSIZE * dt_ltime); - else { - dt_substate = dt_substate | DTO_SOB; - sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); - if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) - dtsb = dtsb | DTB_DTF; /* set DTF */ - } - break; - - case DTO_WCO | DTO_SOB: /* all done */ - dt_schedez (uptr, dir); /* sched end zone */ - break; - } - - break; - -/* Read all has two subcases - - Not word count overflow - increment MA, WC, copy word from tape to memory - Word count overflow - schedule end zone -*/ - - case FNC_RALL: - switch (dt_substate) { /* case on substate */ - - case 0: case DTO_SOB: /* read in progress */ - if (dtsb & DTB_DTF) { /* DTF set? */ - dt_seterr (uptr, DTB_TIM); /* timing error */ - return SCPE_OK; - } - relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ - M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ - M[DT_CA] = (M[DT_CA] + 1) & 07777; - ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ - if ((relpos >= DT_HTLIN) && /* in data zone? */ - (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { - wrd = DT_LIN2WD (uptr->pos, uptr); - ba = (blk * DTU_BSIZE (uptr)) + wrd; - dat = fbuf[ba]; /* get tape word */ - if (dir) /* rev? comp obv */ - dat = dt_comobv (dat); - } - else dat = dt_gethdr (uptr, blk, relpos, dir); /* get hdr */ - sim_activate (uptr, DT_WSIZE * dt_ltime); - if (MEM_ADDR_OK (ma)) /* mem addr legal? */ - M[ma] = dat; - if (M[DT_WC] == 0) - dt_substate = DTO_WCO; - if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) - dtsb = dtsb | DTB_DTF; /* set DTF */ - break; - - case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ - dt_schedez (uptr, dir); /* sched end zone */ - break; - } /* end case substate */ - - break; - -/* Write all has two subcases - - Not word count overflow - increment MA, WC, copy word from memory to tape - Word count overflow - schedule end zone -*/ - - case FNC_WALL: - switch (dt_substate) { /* case on substate */ - - case 0: case DTO_SOB: /* read in progress */ - if (dtsb & DTB_DTF) { /* DTF set? */ - dt_seterr (uptr, DTB_TIM); /* timing error */ - return SCPE_OK; - } - relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ - M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ - M[DT_CA] = (M[DT_CA] + 1) & 07777; - ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ - if ((relpos >= DT_HTLIN) && /* in data zone? */ - (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { - dat = M[ma]; /* get mem word */ - if (dir) - dat = dt_comobv (dat); - wrd = DT_LIN2WD (uptr->pos, uptr); - ba = (blk * DTU_BSIZE (uptr)) + wrd; - fbuf[ba] = dat; /* write word */ - if (ba >= uptr->hwmark) - uptr->hwmark = ba + 1; - } - /* ignore hdr */ - sim_activate (uptr, DT_WSIZE * dt_ltime); - if (M[DT_WC] == 0) - dt_substate = DTO_WCO; - if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) - dtsb = dtsb | DTB_DTF; /* set DTF */ - break; - - case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ - dt_schedez (uptr, dir); /* sched end zone */ - break; - } /* end case substate */ - break; - - default: - dt_seterr (uptr, DTB_SEL); /* impossible state */ - break; - } - -DT_UPDINT; /* update interrupts */ -return SCPE_OK; -} - -/* Reading the header is complicated, because 18b words are being parsed - out 12b at a time. The sequence of word numbers is directionally - sensitive - - Forward Reverse - Word Word Content Word Word Content - (abs) (rel) (abs) (rel) - - 137 8 fwd csm'00 6 6 rev csm'00 - 138 9 0000 5 5 0000 - 139 10 0000 4 4 0000 - 140 11 0000 3 3 0000 - 141 12 00'lo rev blk 2 2 00'lo fwd blk - 142 13 hi rev blk 1 1 hi fwd blk - 143 14 0000 0 0 0000 - 0 0 0000 143 14 0000 - 1 1 0000 142 13 0000 - 2 2 hi fwd blk 141 12 hi rev blk - 3 3 lo fwd blk'00 140 11 lo rev blk'00 - 4 4 0000 139 10 0000 - 5 5 0000 138 9 0000 - 6 6 0000 137 8 0000 - 7 7 rev csm 136 7 00'fwd csm -*/ - -int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir) -{ -if (relpos >= DT_HTLIN) - relpos = relpos - (DT_WSIZE * DTU_BSIZE (uptr)); -if (dir) { /* reverse */ - switch (relpos / DT_WSIZE) { - case 6: /* rev csm */ - return 077; - case 2: /* lo fwd blk */ - return dt_comobv ((blk & 077) << 6); - case 1: /* hi fwd blk */ - return dt_comobv (blk >> 6); - case 12: /* hi rev blk */ - return (blk >> 6) & 07777; - case 11: /* lo rev blk */ - return ((blk & 077) << 6); - case 7: /* fwd csum */ - return (dt_comobv (dt_csum (uptr, blk)) << 6); - default: /* others */ - return 07777; - } - } -else { /* forward */ - switch (relpos / DT_WSIZE) { - case 8: /* fwd csum */ - return (dt_csum (uptr, blk) << 6); - case 12: /* lo rev blk */ - return dt_comobv ((blk & 077) << 6); - case 13: /* hi rev blk */ - return dt_comobv (blk >> 6); - case 2: /* hi fwd blk */ - return ((blk >> 6) & 07777); - case 3: /* lo fwd blk */ - return ((blk & 077) << 6); - case 7: /* rev csum */ - return 077; - default: /* others */ - break; - } - } -return 0; -} - -/* Utility routines */ - -/* Set error flag */ - -void dt_seterr (UNIT *uptr, int32 e) -{ -int32 mot = DTS_GETMOT (uptr->STATE); - -dtsa = dtsa & ~DTA_STSTP; /* clear go */ -dtsb = dtsb | DTB_ERF | e; /* set error flag */ -if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ - sim_cancel (uptr); /* cancel activity */ - if (dt_setpos (uptr)) /* update position */ - return; - sim_activate (uptr, dt_dctime); /* sched decel */ - DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ - } -else DTS_SETSTA (mot, 0); /* clear 2nd, 3rd */ -DT_UPDINT; -return; -} - -/* Schedule end zone */ - -void dt_schedez (UNIT *uptr, int32 dir) -{ -int32 newpos; - -if (dir) /* rev? rev ez */ - newpos = DT_EZLIN - DT_WSIZE; -else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ -sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); -return; -} - -/* Complement obverse routine */ - -int32 dt_comobv (int32 dat) -{ -dat = dat ^ 07777; /* compl obverse */ -dat = ((dat >> 9) & 07) | ((dat >> 3) & 070) | - ((dat & 070) << 3) | ((dat & 07) << 9); -return dat; -} - -/* Checksum routine */ - -int32 dt_csum (UNIT *uptr, int32 blk) -{ -int16 *fbuf = (int16 *) uptr->filebuf; -int32 ba = blk * DTU_BSIZE (uptr); -int32 i, csum, wrd; - -csum = 077; /* init csum */ -for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ - wrd = fbuf[ba + i] ^ 07777; /* get ~word */ - csum = csum ^ (wrd >> 6) ^ wrd; - } -return (csum & 077); -} - -/* Reset routine */ - -t_stat dt_reset (DEVICE *dptr) -{ -int32 i, prev_mot; -UNIT *uptr; - -for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ - uptr = dt_dev.units + i; - if (sim_is_running) { /* CAF? */ - prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ - if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ - if (dt_setpos (uptr)) /* update pos */ - continue; - sim_cancel (uptr); - sim_activate (uptr, dt_dctime); /* sched decel */ - DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); - } - } - else { - sim_cancel (uptr); /* sim reset */ - uptr->STATE = 0; - uptr->LASTT = sim_grtime (); - } - } -dtsa = dtsb = 0; /* clear status */ -DT_UPDINT; /* reset interrupt */ -return SCPE_OK; -} - -/* Bootstrap routine - - This is actually the 4K disk monitor bootstrap, which also - works with OS/8. The reverse is not true - the OS/8 bootstrap - doesn't work with the disk monitor. -*/ - -#define BOOT_START 0200 -#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) - -static const uint16 boot_rom[] = { - 07600, /* 200, CLA CLL */ - 01216, /* TAD MVB ; move back */ - 04210, /* JMS DO ; action */ - 01217, /* TAD K7577 ; addr */ - 03620, /* DCA I CA */ - 01222, /* TAD RDF ; read fwd */ - 04210, /* JMS DO ; action */ - 05600, /* JMP I 200 ; enter boot */ - 00000, /* DO, 0 */ - 06766, /* DTCA!DTXA ; start tape */ - 03621, /* DCA I WC ; clear wc */ - 06771, /* DTSF ; wait */ - 05213, /* JMP .-1 */ - 05610, /* JMP I DO */ - 00600, /* MVB, 0600 */ - 07577, /* K7577, 7757 */ - 07755, /* CA, 7755 */ - 07754, /* WC, 7754 */ - 00220 /* RF, 0220 */ - }; - -t_stat dt_boot (int32 unitno, DEVICE *dptr) -{ -size_t i; - -if (unitno) /* only unit 0 */ - return SCPE_ARG; -if (dt_dib.dev != DEV_DTA) /* only std devno */ - return STOP_NOTSTD; -dt_unit[unitno].pos = DT_EZLIN; -for (i = 0; i < BOOT_LEN; i++) - M[BOOT_START + i] = boot_rom[i]; -cpu_set_bootpc (BOOT_START); -return SCPE_OK; -} - -/* Attach routine - - Determine 12b, 16b, or 18b/36b format - Allocate buffer - If 16b or 18b, read 16b or 18b format and convert to 12b in buffer - If 12b, read data into buffer -*/ - -t_stat dt_attach (UNIT *uptr, CONST char *cptr) -{ -uint32 pdp18b[D18_NBSIZE]; -uint16 pdp11b[D18_NBSIZE], *fbuf; -int32 i, k; -int32 u = uptr - dt_dev.units; -t_stat r; -uint32 ba, sz; - -r = attach_unit (uptr, cptr); /* attach */ -if (r != SCPE_OK) return r; /* fail? */ -if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ - uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; - if (sim_switches & SWMASK ('F')) /* att 18b? */ - uptr->flags = uptr->flags & ~UNIT_8FMT; - else if (sim_switches & SWMASK ('S')) /* att 16b? */ - uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; - else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ - (sz = sim_fsize (uptr->fileref))) { - if (sz == D11_FILSIZ) - uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; - else if (sz > D8_FILSIZ) - uptr->flags = uptr->flags & ~UNIT_8FMT; - } - } -uptr->capac = DTU_CAPAC (uptr); /* set capacity */ -uptr->filebuf = calloc (uptr->capac, sizeof (uint16)); -if (uptr->filebuf == NULL) { /* can't alloc? */ - detach_unit (uptr); - return SCPE_MEM; - } -fbuf = (uint16 *) uptr->filebuf; /* file buffer */ -sim_printf ("%s%d: ", sim_dname (&dt_dev), u); -if (uptr->flags & UNIT_8FMT) - sim_printf ("12b format"); -else if (uptr->flags & UNIT_11FMT) - sim_printf ("16b format"); -else sim_printf ("18b/36b format"); -sim_printf (", buffering file in memory\n"); -uptr->io_flush = dt_flush; -if (uptr->flags & UNIT_8FMT) /* 12b? */ - uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16), - uptr->capac, uptr->fileref); -else { /* 16b/18b */ - for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ - if (uptr->flags & UNIT_11FMT) { - k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); - for (i = 0; i < k; i++) - pdp18b[i] = pdp11b[i]; - } - else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); - if (k == 0) - break; - for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0; - for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */ - fbuf[ba] = (pdp18b[k] >> 6) & 07777; - fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) | - ((pdp18b[k + 1] >> 12) & 077); - fbuf[ba + 2] = pdp18b[k + 1] & 07777; - ba = ba + 3; - } /* end blk loop */ - } /* end file loop */ - uptr->hwmark = ba; - } /* end else */ -uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ -uptr->pos = DT_EZLIN; /* beyond leader */ -uptr->LASTT = sim_grtime (); /* last pos update */ -return SCPE_OK; -} - -/* Detach routine - - Cancel in progress operation - If 12b, write buffer to file - If 16b or 18b, convert 12b buffer to 16b or 18b and write to file - Deallocate buffer -*/ -void dt_flush (UNIT* uptr) -{ -uint32 pdp18b[D18_NBSIZE]; -uint16 pdp11b[D18_NBSIZE], *fbuf; -int32 i, k; -uint32 ba; - -if (uptr->WRITTEN && uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ - rewind (uptr->fileref); /* start of file */ - fbuf = (uint16 *) uptr->filebuf; /* file buffer */ - if (uptr->flags & UNIT_8FMT) /* PDP8? */ - fxwrite (uptr->filebuf, sizeof (uint16), /* write file */ - uptr->hwmark, uptr->fileref); - else { /* 16b/18b */ - for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */ - for (k = 0; k < D18_NBSIZE; k = k + 2) { - pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) | - ((uint32) (fbuf[ba + 1] >> 6) & 077); - pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) | - ((uint32) (fbuf[ba + 2] & 07777)); - ba = ba + 3; - } /* end loop blk */ - if (uptr->flags & UNIT_11FMT) { /* 16b? */ - for (i = 0; i < D18_NBSIZE; i++) - pdp11b[i] = pdp18b[i]; - fxwrite (pdp11b, sizeof (uint16), - D18_NBSIZE, uptr->fileref); - } - else fxwrite (pdp18b, sizeof (uint32), - D18_NBSIZE, uptr->fileref); - } /* end loop buf */ - } /* end else */ - if (ferror (uptr->fileref)) - sim_perror ("I/O error"); - } -uptr->WRITTEN = FALSE; /* no longer dirty */ -} - -t_stat dt_detach (UNIT* uptr) -{ -int u = (int)(uptr - dt_dev.units); - -if (!(uptr->flags & UNIT_ATT)) /* attached? */ - return SCPE_OK; -if (sim_is_active (uptr)) { - sim_cancel (uptr); - if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { - dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; - DT_UPDINT; - } - uptr->STATE = uptr->pos = 0; - } -if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ - sim_printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); - dt_flush (uptr); - } /* end if hwmark */ -free (uptr->filebuf); /* release buf */ -uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ -uptr->filebuf = NULL; /* clear buf ptr */ -uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */ -uptr->capac = DT_CAPAC; /* default size */ -return detach_unit (uptr); -} DELETED src/PDP8/pdp8_fpp.c Index: src/PDP8/pdp8_fpp.c ================================================================== --- src/PDP8/pdp8_fpp.c +++ /dev/null @@ -1,1513 +0,0 @@ -/* 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. - 1. - 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; -} DELETED src/PDP8/pdp8_lp.c Index: src/PDP8/pdp8_lp.c ================================================================== --- src/PDP8/pdp8_lp.c +++ /dev/null @@ -1,188 +0,0 @@ -/* 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); -} DELETED src/PDP8/pdp8_mt.c Index: src/PDP8/pdp8_mt.c ================================================================== --- src/PDP8/pdp8_mt.c +++ /dev/null @@ -1,661 +0,0 @@ -/* 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; -} DELETED src/PDP8/pdp8_pt.c Index: src/PDP8/pdp8_pt.c ================================================================== --- src/PDP8/pdp8_pt.c +++ /dev/null @@ -1,293 +0,0 @@ -/* pdp8_pt.c: PDP-8 paper tape reader/punch simulator - - Copyright (c) 1993-2017, Robert M Supnik - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of Robert M Supnik shall not be - used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from Robert M Supnik. - - ptr,ptp PC8E paper tape reader/punch - - 13-Mar-17 RMS Annotated fall through in switch - 17-Mar-13 RMS Modified to use central set_bootpc routine - 25-Apr-03 RMS Revised for extended file support - 04-Oct-02 RMS Added DIBs - 30-May-02 RMS Widened POS to 32b - 30-Nov-01 RMS Added read only unit support - 30-Mar-98 RMS Added RIM loader as PTR bootstrap -*/ - -#include "pdp8_defs.h" - -extern int32 int_req, int_enable, dev_done, stop_inst; - -int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ - -int32 ptr (int32 IR, int32 AC); -int32 ptp (int32 IR, int32 AC); -t_stat ptr_svc (UNIT *uptr); -t_stat ptp_svc (UNIT *uptr); -t_stat ptr_reset (DEVICE *dptr); -t_stat ptp_reset (DEVICE *dptr); -t_stat ptr_boot (int32 unitno, DEVICE *dptr); - -/* PTR data structures - - ptr_dev PTR device descriptor - ptr_unit PTR unit descriptor - ptr_reg PTR register list -*/ - -DIB ptr_dib = { DEV_PTR, 1, { &ptr } }; - -UNIT ptr_unit = { - UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), - SERIAL_IN_WAIT - }; - -REG ptr_reg[] = { - { ORDATAD (BUF, ptr_unit.buf, 8, "last data item processed") }, - { FLDATAD (DONE, dev_done, INT_V_PTR, "device done flag") }, - { FLDATAD (ENABLE, int_enable, INT_V_PTR, "interrupt enable flag") }, - { FLDATAD (INT, int_req, INT_V_PTR, "interrupt pending flag") }, - { DRDATAD (POS, ptr_unit.pos, T_ADDR_W, "position in the input file"), PV_LEFT }, - { DRDATAD (TIME, ptr_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT }, - { FLDATAD (STOP_IOE, ptr_stopioe, 0, "stop on I/O error") }, - { NULL } - }; - -MTAB ptr_mod[] = { - { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, - { 0 } - }; - -DEVICE ptr_dev = { - "PTR", &ptr_unit, ptr_reg, ptr_mod, - 1, 10, 31, 1, 8, 8, - NULL, NULL, &ptr_reset, - &ptr_boot, NULL, NULL, - &ptr_dib, 0 }; - -/* PTP data structures - - ptp_dev PTP device descriptor - ptp_unit PTP unit descriptor - ptp_reg PTP register list -*/ - -DIB ptp_dib = { DEV_PTP, 1, { &ptp } }; - -UNIT ptp_unit = { - UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT - }; - -REG ptp_reg[] = { - { ORDATAD (BUF, ptp_unit.buf, 8, "last data item processed") }, - { FLDATAD (DONE, dev_done, INT_V_PTP, "device done flag") }, - { FLDATAD (ENABLE, int_enable, INT_V_PTP, "interrupt enable flag") }, - { FLDATAD (INT, int_req, INT_V_PTP, "interrupt pending flag") }, - { DRDATAD (POS, ptp_unit.pos, T_ADDR_W, "position in the output file"), PV_LEFT }, - { DRDATAD (TIME, ptp_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT }, - { FLDATAD (STOP_IOE, ptp_stopioe, 0, "stop on I/O error") }, - { NULL } - }; - -MTAB ptp_mod[] = { - { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, - { 0 } - }; - -DEVICE ptp_dev = { - "PTP", &ptp_unit, ptp_reg, ptp_mod, - 1, 10, 31, 1, 8, 8, - NULL, NULL, &ptp_reset, - NULL, NULL, NULL, - &ptp_dib, 0 - }; - -/* Paper tape reader: IOT routine */ - -int32 ptr (int32 IR, int32 AC) -{ -switch (IR & 07) { /* decode IR<9:11> */ - - case 0: /* RPE */ - int_enable = int_enable | (INT_PTR+INT_PTP); /* set enable */ - int_req = INT_UPDATE; /* update interrupts */ - return AC; - - case 1: /* RSF */ - return (dev_done & INT_PTR)? IOT_SKP + AC: AC; - - case 6: /* RFC!RRB */ - sim_activate (&ptr_unit, ptr_unit.wait); /* activate */ - /* fall through */ - case 2: /* RRB */ - dev_done = dev_done & ~INT_PTR; /* clear flag */ - int_req = int_req & ~INT_PTR; /* clear int req */ - return (AC | ptr_unit.buf); /* or data to AC */ - - case 4: /* RFC */ - sim_activate (&ptr_unit, ptr_unit.wait); - dev_done = dev_done & ~INT_PTR; /* clear flag */ - int_req = int_req & ~INT_PTR; /* clear int req */ - return AC; - - default: - return (stop_inst << IOT_V_REASON) + AC; - } /* end switch */ -} - -/* Unit service */ - -t_stat ptr_svc (UNIT *uptr) -{ -int32 temp; - -if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ - return IORETURN (ptr_stopioe, SCPE_UNATT); -if ((temp = getc (ptr_unit.fileref)) == EOF) { - if (feof (ptr_unit.fileref)) { - if (ptr_stopioe) - sim_printf ("PTR end of file\n"); - else return SCPE_OK; - } - else sim_perror ("PTR I/O error"); - clearerr (ptr_unit.fileref); - return SCPE_IOERR; - } -dev_done = dev_done | INT_PTR; /* set done */ -int_req = INT_UPDATE; /* update interrupts */ -ptr_unit.buf = temp & 0377; -ptr_unit.pos = ptr_unit.pos + 1; -return SCPE_OK; -} - -/* Reset routine */ - -t_stat ptr_reset (DEVICE *dptr) -{ -ptr_unit.buf = 0; -dev_done = dev_done & ~INT_PTR; /* clear done, int */ -int_req = int_req & ~INT_PTR; -int_enable = int_enable | INT_PTR; /* set enable */ -sim_cancel (&ptr_unit); /* deactivate unit */ -return SCPE_OK; -} - -/* Paper tape punch: IOT routine */ - -int32 ptp (int32 IR, int32 AC) -{ -switch (IR & 07) { /* decode IR<9:11> */ - - case 0: /* PCE */ - int_enable = int_enable & ~(INT_PTR+INT_PTP); /* clear enables */ - int_req = INT_UPDATE; /* update interrupts */ - return AC; - - case 1: /* PSF */ - return (dev_done & INT_PTP)? IOT_SKP + AC: AC; - - case 2: /* PCF */ - dev_done = dev_done & ~INT_PTP; /* clear flag */ - int_req = int_req & ~INT_PTP; /* clear int req */ - return AC; - - case 6: /* PLS */ - dev_done = dev_done & ~INT_PTP; /* clear flag */ - int_req = int_req & ~INT_PTP; /* clear int req */ - case 4: /* PPC */ - ptp_unit.buf = AC & 0377; /* load punch buf */ - sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ - return AC; - - default: - return (stop_inst << IOT_V_REASON) + AC; - } /* end switch */ -} - -/* Unit service */ - -t_stat ptp_svc (UNIT *uptr) -{ -dev_done = dev_done | INT_PTP; /* set done */ -int_req = INT_UPDATE; /* update interrupts */ -if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ - return IORETURN (ptp_stopioe, SCPE_UNATT); -if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { - sim_perror ("PTP I/O error"); - clearerr (ptp_unit.fileref); - return SCPE_IOERR; - } -ptp_unit.pos = ptp_unit.pos + 1; -return SCPE_OK; -} - -/* Reset routine */ - -t_stat ptp_reset (DEVICE *dptr) -{ -ptp_unit.buf = 0; -dev_done = dev_done & ~INT_PTP; /* clear done, int */ -int_req = int_req & ~INT_PTP; -int_enable = int_enable | INT_PTP; /* set enable */ -sim_cancel (&ptp_unit); /* deactivate unit */ -return SCPE_OK; -} - -/* Bootstrap routine */ - -#define BOOT_START 07756 -#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) - -static const uint16 boot_rom[] = { - 06014, /* 7756, RFC */ - 06011, /* 7757, LOOP, RSF */ - 05357, /* JMP .-1 */ - 06016, /* RFC RRB */ - 07106, /* CLL RTL*/ - 07006, /* RTL */ - 07510, /* SPA*/ - 05374, /* JMP 7774 */ - 07006, /* RTL */ - 06011, /* RSF */ - 05367, /* JMP .-1 */ - 06016, /* RFC RRB */ - 07420, /* SNL */ - 03776, /* DCA I 7776 */ - 03376, /* 7774, DCA 7776 */ - 05357, /* JMP 7757 */ - 00000, /* 7776, 0 */ - 05301 /* 7777, JMP 7701 */ - }; - -t_stat ptr_boot (int32 unitno, DEVICE *dptr) -{ -size_t i; -extern uint16 M[]; - -if (ptr_dib.dev != DEV_PTR) /* only std devno */ - return STOP_NOTSTD; -for (i = 0; i < BOOT_LEN; i++) - M[BOOT_START + i] = boot_rom[i]; -cpu_set_bootpc (BOOT_START); -return SCPE_OK; -} DELETED src/PDP8/pdp8_rf.c Index: src/PDP8/pdp8_rf.c ================================================================== --- src/PDP8/pdp8_rf.c +++ /dev/null @@ -1,448 +0,0 @@ -/* 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 - -#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; -} DELETED src/PDP8/pdp8_rk.c Index: src/PDP8/pdp8_rk.c ================================================================== --- src/PDP8/pdp8_rk.c +++ /dev/null @@ -1,463 +0,0 @@ -/* 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; -} DELETED src/PDP8/pdp8_rl.c Index: src/PDP8/pdp8_rl.c ================================================================== --- src/PDP8/pdp8_rl.c +++ /dev/null @@ -1,703 +0,0 @@ -/* 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; -} DELETED src/PDP8/pdp8_rx.c Index: src/PDP8/pdp8_rx.c ================================================================== --- src/PDP8/pdp8_rx.c +++ /dev/null @@ -1,754 +0,0 @@ -/* 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; -} DELETED src/PDP8/pdp8_sys.c Index: src/PDP8/pdp8_sys.c ================================================================== --- src/PDP8/pdp8_sys.c +++ /dev/null @@ -1,1015 +0,0 @@ -/* 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 - -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); -} DELETED src/PDP8/pdp8_td.c Index: src/PDP8/pdp8_td.c ================================================================== --- src/PDP8/pdp8_td.c +++ /dev/null @@ -1,955 +0,0 @@ -/* 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; -} - DELETED src/PDP8/pdp8_tsc.c Index: src/PDP8/pdp8_tsc.c ================================================================== --- src/PDP8/pdp8_tsc.c +++ /dev/null @@ -1,158 +0,0 @@ -/* 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; -} DELETED src/PDP8/pdp8_tt.c Index: src/PDP8/pdp8_tt.c ================================================================== --- src/PDP8/pdp8_tt.c +++ /dev/null @@ -1,285 +0,0 @@ -/* 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 - -extern int32 int_req, int_enable, dev_done, stop_inst; -extern int32 tmxr_poll; - -int32 tti (int32 IR, int32 AC); -int32 tto (int32 IR, int32 AC); -t_stat tti_svc (UNIT *uptr); -t_stat tto_svc (UNIT *uptr); -t_stat tti_reset (DEVICE *dptr); -t_stat tto_reset (DEVICE *dptr); -t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc); - -/* TTI data structures - - tti_dev TTI device descriptor - tti_unit TTI unit descriptor - tti_reg TTI register list - tti_mod TTI modifiers list -*/ - -DIB tti_dib = { DEV_TTI, 1, { &tti } }; - -UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE|TT_MODE_KSR, 0), SERIAL_IN_WAIT }; - -REG tti_reg[] = { - { ORDATAD (BUF, tti_unit.buf, 8, "last data item processed") }, - { FLDATAD (DONE, dev_done, INT_V_TTI, "device done flag") }, - { FLDATAD (ENABLE, int_enable, INT_V_TTI, "interrupt enable flag") }, - { FLDATAD (INT, int_req, INT_V_TTI, "interrupt pending flag") }, - { DRDATAD (POS, tti_unit.pos, T_ADDR_W, "number of characters input"), PV_LEFT }, - { DRDATAD (TIME, tti_unit.wait, 24, "input polling interval (if 0, the keyboard is polled synchronously with the clock)"), PV_LEFT+REG_NZ }, - { NULL } - }; - -MTAB tti_mod[] = { - { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, - { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, - { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, - { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, - { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev, NULL }, - { 0 } - }; - -DEVICE tti_dev = { - "TTI", &tti_unit, tti_reg, tti_mod, - 1, 10, 31, 1, 8, 8, - NULL, NULL, &tti_reset, - NULL, NULL, NULL, - &tti_dib, 0 - }; - -uint32 tti_buftime; /* time input character arrived */ - -/* TTO data structures - - tto_dev TTO device descriptor - tto_unit TTO unit descriptor - tto_reg TTO register list -*/ - -DIB tto_dib = { DEV_TTO, 1, { &tto } }; - -UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }; - -REG tto_reg[] = { - { ORDATAD (BUF, tto_unit.buf, 8, "last date item processed") }, - { FLDATAD (DONE, dev_done, INT_V_TTO, "device done flag") }, - { FLDATAD (ENABLE, int_enable, INT_V_TTO, "interrupt enable flag") }, - { FLDATAD (INT, int_req, INT_V_TTO, "interrupt pending flag") }, - { DRDATAD (POS, tto_unit.pos, T_ADDR_W, "number of characters output"), PV_LEFT }, - { DRDATAD (TIME, tto_unit.wait, 24, "time form I/O initiation to interrupt"), PV_LEFT }, - { NULL } - }; - -MTAB tto_mod[] = { - { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, - { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, - { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, - { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, - { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, - { 0 } - }; - -DEVICE tto_dev = { - "TTO", &tto_unit, tto_reg, tto_mod, - 1, 10, 31, 1, 8, 8, - NULL, NULL, &tto_reset, - NULL, NULL, NULL, - &tto_dib, 0 - }; - -/* Terminal input: IOT routine */ - -int32 tti (int32 IR, int32 AC) -{ -switch (IR & 07) { /* decode IR<9:11> */ - case 0: /* KCF */ - dev_done = dev_done & ~INT_TTI; /* clear flag */ - int_req = int_req & ~INT_TTI; - return AC; - - case 1: /* KSF */ - return (dev_done & INT_TTI)? IOT_SKP + AC: AC; - - case 2: /* KCC */ - dev_done = dev_done & ~INT_TTI; /* clear flag */ - int_req = int_req & ~INT_TTI; - return 0; /* clear AC */ - - case 4: /* KRS */ - return (AC | tti_unit.buf); /* return buffer */ - - case 5: /* KIE */ - if (AC & 1) - int_enable = int_enable | (INT_TTI+INT_TTO); - else int_enable = int_enable & ~(INT_TTI+INT_TTO); - int_req = INT_UPDATE; /* update interrupts */ - return AC; - - case 6: /* KRB */ - dev_done = dev_done & ~INT_TTI; /* clear flag */ - int_req = int_req & ~INT_TTI; - sim_activate_abs (&tti_unit, tti_unit.wait); /* check soon for more input */ - return (tti_unit.buf); /* return buffer */ - - default: - return (stop_inst << IOT_V_REASON) + AC; - } /* end switch */ -} - -/* Unit service */ - -t_stat tti_svc (UNIT *uptr) -{ -int32 c; - -sim_clock_coschedule (uptr, tmxr_poll); /* continue poll */ -if ((dev_done & INT_TTI) && /* prior character still pending and < 500ms? */ - ((sim_os_msec () - tti_buftime) < 500)) - return SCPE_OK; -if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ - return c; -if (c & SCPE_BREAK) /* break? */ - uptr->buf = 0; -else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); -tti_buftime = sim_os_msec (); -uptr->pos = uptr->pos + 1; -dev_done = dev_done | INT_TTI; /* set done */ -int_req = INT_UPDATE; /* update interrupts */ -return SCPE_OK; -} - -/* Reset routine */ - -t_stat tti_reset (DEVICE *dptr) -{ -tmxr_set_console_units (&tti_unit, &tto_unit); -tti_unit.buf = 0; -dev_done = dev_done & ~INT_TTI; /* clear done, int */ -int_req = int_req & ~INT_TTI; -int_enable = int_enable | INT_TTI; /* set enable */ -if (!sim_is_running) /* RESET (not CAF)? */ - sim_activate (&tti_unit, tmxr_poll); -return SCPE_OK; -} - -/* Terminal output: IOT routine */ - -int32 tto (int32 IR, int32 AC) -{ -switch (IR & 07) { /* decode IR<9:11> */ - - case 0: /* TLF */ - dev_done = dev_done | INT_TTO; /* set flag */ - int_req = INT_UPDATE; /* update interrupts */ - return AC; - - case 1: /* TSF */ - return (dev_done & INT_TTO)? IOT_SKP + AC: AC; - - case 2: /* TCF */ - dev_done = dev_done & ~INT_TTO; /* clear flag */ - int_req = int_req & ~INT_TTO; /* clear int req */ - return AC; - - case 5: /* SPI */ - return (int_req & (INT_TTI+INT_TTO))? IOT_SKP + AC: AC; - - case 6: /* TLS */ - dev_done = dev_done & ~INT_TTO; /* clear flag */ - int_req = int_req & ~INT_TTO; /* clear int req */ - case 4: /* TPC */ - sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ - tto_unit.buf = AC; /* load buffer */ - return AC; - - default: - return (stop_inst << IOT_V_REASON) + AC; - } /* end switch */ -} - -/* Unit service */ - -t_stat tto_svc (UNIT *uptr) -{ -int32 c; -t_stat r; - -c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); -if (c >= 0) { - if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output char; error? */ - sim_activate (uptr, uptr->wait); /* try again */ - return ((r == SCPE_STALL)? SCPE_OK: r); /* if !stall, report */ - } - } -dev_done = dev_done | INT_TTO; /* set done */ -int_req = INT_UPDATE; /* update interrupts */ -uptr->pos = uptr->pos + 1; -return SCPE_OK; -} - -/* Reset routine */ - -t_stat tto_reset (DEVICE *dptr) -{ -tto_unit.buf = 0; -dev_done = dev_done & ~INT_TTO; /* clear done, int */ -int_req = int_req & ~INT_TTO; -int_enable = int_enable | INT_TTO; /* set enable */ -sim_cancel (&tto_unit); /* deactivate unit */ -return SCPE_OK; -} - -t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc) -{ -tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; -tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; -return SCPE_OK; -} DELETED src/PDP8/pdp8_ttx.c Index: src/PDP8/pdp8_ttx.c ================================================================== --- src/PDP8/pdp8_ttx.c +++ /dev/null @@ -1,546 +0,0 @@ -/* 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 - -#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; -} - DELETED src/PDP8/pidp8i.c.in Index: src/PDP8/pidp8i.c.in ================================================================== --- src/PDP8/pidp8i.c.in +++ /dev/null @@ -1,830 +0,0 @@ -/* 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 "pdp8_defs.h" - -#include -#include // for USB stick searching -#include -#include - - -//// MODULE GLOBALS //////////////////////////////////////////////////// - -// handle_sing_step() sets this to nonzero and returns a value breaking -// us out of the PDP-8 simulator's sim_instr() loop, which causes SCP to -// call our build_pidp8i_scp_cmd(), which gives SCP a command to run: -// either "exit" when it wants the simulator to stop (e.g the shutdown -// and reboot combos) or "do $script" on IF + SING_STEP combo. -// -// We loop the flow control from this module out into the generic SIMH -// code and then back in here so we don't have to export this global. -// Basically, this module global lets us remember what handle_sing_step -// wants SCP to do in the window between switch handling time and SCP -// command handling time. -static enum { - CMD_NONE = 0, // "do nothing" idle case - CMD_DO_BOOTSCRIPT_1, // SING_STEP + IF combos - CMD_DO_BOOTSCRIPT_2, - CMD_DO_BOOTSCRIPT_3, - CMD_DO_BOOTSCRIPT_4, - CMD_DO_BOOTSCRIPT_5, - CMD_DO_BOOTSCRIPT_6, - CMD_DO_BOOTSCRIPT_7, - CMD_EXIT, -} insert_scp_cmd = CMD_NONE; - - -//// build_pidp8i_scp_cmd ////////////////////////////////////////////// -// If insert_scp_cmd is nonzero, we return the corresponding SCP command -// we want run to make the simulator do something else. - -char *build_pidp8i_scp_cmd (char *cbuf, size_t cbufsize) -{ - if (insert_scp_cmd == CMD_NONE) { - return 0; // nothing to do yet - } - else if ((insert_scp_cmd > 0) && (insert_scp_cmd <= 7)) { - // We got one of the IF + SING_STEP combos, so restart the - // simulator with the corresponding init script. - char path[256]; - snprintf (path, sizeof (path), "@BOOTDIR@/%d.script", insert_scp_cmd); - insert_scp_cmd = CMD_NONE; // it's a one-shot - if (access(path, R_OK) == 0) { - snprintf (cbuf, cbufsize, "do %s", path); - return cbuf; - } - else { - // That boot script doesn't exist or isn't readable. - // - // Fall through to the "exit" command builder below because - // we don't want to keep coming back in here at host CPU - // speed, failing the same way and issuing the same error - // until the slow human manages to flip the offending switch - // back. This is especially annoying when the PiDP-8/I is - // attached to a slow serial console. Ask me how I know. - int access_errno = errno; // preserve it from getcwd() - char cwd[256]; - getcwd (cwd, sizeof (cwd)); - printf ("Cannot read %s from %s: %s!\n", path, cwd, - strerror (access_errno)); - } - } - else if (insert_scp_cmd > CMD_EXIT) { - printf ("Invalid PiDP-8/I SCP command value %d given!\n", - insert_scp_cmd); - } - else { - // C doesn't require that "if" statements handle all cases - // statically, so do a runtime check to make sure we have - // exhausted all the other cases above. - // - // We can get in here by means other than programmer error in - // modifying the enum: some C compilers allow signed values - // to be assigned to enums, so we could get in here on negative - // values. We can't test for that above because one of the C - // compilers we build under (clang on macOS 10.12+) won't allow - // that short of nasty low-level hackery, so it complains if we - // test for negative values, claiming it can never happen. - assert (insert_scp_cmd == CMD_EXIT); - } - - // If we get here, we got a nonzero command value but didn't get - // into the happy path above, so die. - return strncpy (cbuf, "exit", cbufsize); -} - - -//// set_pidp8i_led //////////////////////////////////////////////////// -// Sets the current state for a single LED at the given row and column -// on the PiDP-8/I PCB. Also increments the LED on-count value for -// that LED. -// -// You may say, "You can't just use the C postincrement operator here! -// Look at the assembly output! You must use an atomic increment for -// this!" And indeed, there is a big difference between the two -// methods: https://godbolt.org/g/0Qt0Ap -// -// The thing is, both structures referred to by pdis_* are fixed in RAM, -// and the two threads involved are arranged in a strict producer-and- -// consumer fashion, so it doesn't actually matter if pdis_update gets -// swapped for pdis_paint while we're halfway through an increment: we -// get a copy of the pointer to dereference here, so we'll finish our -// increment within the same structure we started with, even if -// pdis_update points at the other display structure before we leave. - -static inline void set_pidp8i_led (display *pd, size_t row, size_t col) -{ - ++pd->on[row][col]; - pd->curr[row] |= 1 << col; -} - - -//// set_pidp8i_row_leds /////////////////////////////////////////////// -// Like set_pidp8i_led, except that it takes a 12-bit state value for -// setting all LEDs on the given row. Because we copy the pdis_update -// pointer before making changes, if the display swap happens while -// we're working, we'll simply finish updating what has become the -// paint-from display, which is what you want; you don't want the -// updates spread over both displays. - -static inline void set_pidp8i_row_leds (display *pd, size_t row, - uint16 state) -{ - size_t *prow = pd->on[row]; - pd->curr[row] = state; - for (size_t col = 0, mask = 1; col < NCOLS; ++col, mask <<= 1) { - if (state & mask) ++prow[col]; - } -} - - -//// set_3_pidp8i_leds ///////////////////////////////////////////////// -// Special case of set_pidp8i_row_leds for the DF and IF LEDs: we only -// pay attention to bits 12, 13, and 14 of the given state value, -// because SIMH's PDP-8 simulator shifts those 3 bits up there so it can -// simply OR these 3-bit registers with PC to produce a 15-bit extended -// address. -// -// We don't take a row parameter because we know which row they're on, -// but we do take a column parameter so we can generalize for IF & DF. - -static inline void set_3_pidp8i_leds (display *pd, size_t col, - uint16 state) -{ - static const int row = 7; // DF and IF are on row 6 - size_t *prow = pd->on[row]; - size_t last_col = col + 3; - pd->curr[row] |= state >> (12 - col); - for (size_t mask = 1 << 12; col < last_col; ++col, mask <<= 1) { - if (state & mask) ++prow[col]; - } -} - - -//// set_5_pidp8i_leds ///////////////////////////////////////////////// -// Like set_3... but for the 5-bit SC register. Because it's only used -// for that purpose, we don't need the col parameter. - -static inline void set_5_pidp8i_leds (display *pd, uint16 state) -{ - static const int row = 6; // SC is on row 6 - size_t *prow = pd->on[row]; - size_t last_col = 7; - pd->curr[row] |= (state & 0x1f) << 2; - for (size_t col = 2, mask = 1; col < last_col; ++col, mask <<= 1) { - if (state & mask) ++prow[col]; - } -} - - -//// get_pidp8i_initial_max_skips ////////////////////////////////////// -// Return the number of times we should skip updating the front panel -// LEDs the first time thru, to give the simulator time to settle. -// If we don't do this, the front panel LEDs can start out dim and -// slowly rise or they can overshoot and then take a while to recover -// with the IPS. - -size_t get_pidp8i_initial_max_skips (size_t updates_per_sec) -{ - DEVICE *pthrot = find_dev ("INT-THROTTLE"); - if (pthrot) { - extern int suppressILS; - REG *ptyper = find_reg ("THROT_TYPE", NULL, pthrot); - REG *pvalr = find_reg ("THROT_VAL", NULL, pthrot); - if (ptyper && pvalr) { - uint32 *ptype = ptyper->loc; - uint32 *pval = pvalr->loc; - size_t ips = 0; - switch (*ptype) { - case SIM_THROT_MCYC: ips = *pval * 1e6; break; - case SIM_THROT_KCYC: ips = *pval * 1e3; break; - case SIM_THROT_SPC: { - suppressILS = 1; - break; - } - } - if (ips) { - suppressILS = 0; - printf("PiDP-8/I initial throttle = %zu IPS\r\n", ips); - return ips / updates_per_sec; - } - } - } - - // No better idea, so give a plausible value for an unthrottled Pi 1 - return 200; -} - - -//// set_pidp8i_leds /////////////////////////////////////////////////// -// Given all of the PDP-8's internal registers that affect the front -// panel display, modify the GPIO thread's LED state values accordingly. -// -// Also update the LED brightness values based on those new states. - -void set_pidp8i_leds (uint32 sPC, uint32 sMA, uint16 sIR, int32 sLAC, - int32 sMQ, int32 sIF, int32 sDF, int32 sSC, int32 int_req, - int Pause) -{ - // Bump the instruction count. This should always be equal to the - // Fetch LED's value, but integers are too cheap to get cute here. - // - // Note that we only update pdis_update directly once in this whole - // process. This is in case the display swap happens while we're - // working: we want to finish work on the same display even though - // it's now called the paint-from display, so it's consistent. - display* pd = pdis_update; - ++pd->inst_count; - - // Rows 0-4, easy cases: single-register LED strings. - // - // The only non-obvious one is that we use the PDP-8 simulator's IR - // register value for the MB lights due to a quirk in the way the - // register states are set at the time this function is called; MB - // is not passed to us because its value at the call time is bogus. - set_pidp8i_row_leds (pd, 0, sPC); - set_pidp8i_row_leds (pd, 1, sMA); - set_pidp8i_row_leds (pd, 2, sIR); - set_pidp8i_row_leds (pd, 3, sLAC & 07777); - set_pidp8i_row_leds (pd, 4, sMQ); - - -#if 0 // debugging - static time_t last = 0, now; - if (time(&now) != last) { - uint16* pcurr = pd->curr; - printf("\r\nSET: [PC:%04o] [MA:%04o] [MB:%04o] [AC:%04o] [MQ:%04o]", - pcurr[0], pcurr[1], pcurr[2], pcurr[3], pcurr[4]); - last = now; - } -#endif - - // Row 5a: instruction type column, decoded from high octal - // digit of IR value - pd->curr[5] = 0; - uint16 inst_type = sIR & 07000; - switch (inst_type) { - case 00000: set_pidp8i_led (pd, 5, 11); break; // 000 AND - case 01000: set_pidp8i_led (pd, 5, 10); break; // 001 TAD - case 02000: set_pidp8i_led (pd, 5, 9); break; // 010 DCA - case 03000: set_pidp8i_led (pd, 5, 8); break; // 011 ISZ - case 04000: set_pidp8i_led (pd, 5, 7); break; // 100 JMS - case 05000: set_pidp8i_led (pd, 5, 6); break; // 101 JMP - case 06000: set_pidp8i_led (pd, 5, 5); break; // 110 IOT - case 07000: set_pidp8i_led (pd, 5, 4); break; // 111 OPR 1 & 2 - } - - // Row 5b: set the Defer LED if... - if ((inst_type <= 05000) && // it's a memory reference instruction - (sIR & 00400)) { // and indirect addressing flag is set - set_pidp8i_led (pd, 5, 1); - } - - // Row 5c: The Fetch & Execute LEDs are pulsed once per instruction. - // On real hardware, the pulses don't happen at exactly the same - // time, but we can't simulate that because SIMH handles each CPU - // instruction "whole." When running real code, all we care about - // is that both LEDs are twiddled so rapidly that they both just - // become a 50% blur, mimicking the hardware closely enough. - // - // The exception is that when the CPU is stopped, both LEDs are off, - // because the pulses happen "outside" the STOP state: Fetch before - // and Execute after resuming from STOP. - extern int swStop, swSingInst; - int running = !swStop && !swSingInst; - if (running) { - set_pidp8i_led (pd, 5, 2); // Execute - set_pidp8i_led (pd, 5, 3); // Fetch - } - - // Row 6a: Remaining LEDs in upper right block - pd->curr[6] = 0; - if (running) set_pidp8i_led (pd, 6, 7); // bump Run LED - if (Pause) set_pidp8i_led (pd, 6, 8); // bump Pause LED - if (int_req & INT_ION) set_pidp8i_led (pd, 6, 9); // bump ION LED - - // Row 6b: The Step Count LEDs are also on row 6 - set_5_pidp8i_leds (pd, sSC); - - // Row 7: DF, IF, and Link. - pd->curr[7] = 0; - set_3_pidp8i_leds (pd, 9, sDF); - set_3_pidp8i_leds (pd, 6, sIF); - if (sLAC & 010000) set_pidp8i_led (pd, 7, 5); - - // If we're stopped or single-stepped, the display-swapping code - // won't happen, so copy the above over to the paint-from version. - extern int resumeFromInstructionLoopExit; - if (!running || resumeFromInstructionLoopExit) { - memcpy(pdis_paint, pdis_update, sizeof(struct display)); - } -} - - -//// mount_usb_stick_file ////////////////////////////////////////////// -// Search for a PDP-8 media image on a USB device mounted under /media -// and attempt to ATTACH it to the simulator. - -static void mount_usb_stick_file (int devNo, char *devCode) -{ - char sFoundFile[CBUFSIZE] = { '\0' }; - char sDirName[CBUFSIZE]; // will be "/media/DIRNAME" etc - char fileExtension[4]; // will be ".RX" etc - int i, j; - - // Build expected file name extension from the first two characters of - // the passed-in device code. - fileExtension[0] = '.'; // extension starts with a . - strncpy (fileExtension + 1, devCode, 2); // extension is PT, RX, RL etc - fileExtension[3] = '\0'; // chop off device number - -#if 0 // debugging - printf("\r\nMOUNT USB: [DEV:%d] [CODE:%s], [EXT:%s]", - devNo, devCode, fileExtension); -#endif - - // Forget the prior file attached to this PDP-8 device. The only reason - // we keep track is so we don't have the same media image file attached - // to both devices of a given type we support. That is, you can't have - // a given floppy image file attached to both RX01 drives, but you *can* - // repeatedly re-ATTACH the same floppy image to the first RX01 drive. - static char mountedFiles[8][CBUFSIZE]; - mountedFiles[devNo][0] = '\0'; - - // Search all directories under /media - DIR *pDir1 = opendir ("/media"); - if (pDir1) { - struct dirent* pDE1; - while ((pDE1 = readdir (pDir1)) != 0) { - if (pDE1->d_type != DT_DIR) continue; - - // Found a directory under /media. Search it for plausibly - // named files given devCode. - snprintf (sDirName, sizeof(sDirName), "/media/%s", pDE1->d_name); - DIR *pDir2 = opendir (sDirName); - if (pDir2) { - struct dirent* pDE2; - while ((pDE2 = readdir (pDir2)) != 0) { // search all files in directory - if (pDE2->d_name[0] == '.') continue; // dotfiles clutter debug output - - char* pext = strstr (pDE2->d_name, fileExtension); - if (pext && (pext == (pDE2->d_name + strlen (pDE2->d_name) - 3))) { - snprintf (sFoundFile, sizeof (sFoundFile), "%s/%s", - sDirName, pDE2->d_name); - #if 0 // debugging - printf("\r\nFound candidate file %s for dev %s, ext *%s...", - sFoundFile, devCode, fileExtension); - #endif - for (j = 0; j < 7; ++j) { - if (strncmp (mountedFiles[j], sFoundFile, CBUFSIZE) == 0) { - #if 0 // debugging - printf("\r\nAlready have %s mounted, slot %d; will not remount.", - sFoundFile, j); - #endif - sFoundFile[0] = '\0'; // don't leave outer loop; keep looking - break; - } - } - if (j == 7) { - // Media image file is not already mounted, so leave while - // loop with path set to mount it - break; - } - } - #if 0 // debugging - else { - printf("\r\nFile %s on %s doesn't match *%s...", - pDE2->d_name, sDirName, fileExtension); - } - #endif - } // end while (pDE2...) - - closedir (pDir2); - } // end if (pDir2) - else { - // USB auto-mounting either doesn't work here or uses - // something other than the /media/DIR/FILE.EXT scheme - // we expect. - printf ("\r\nCannot open %s: %s\r\n", sDirName, strerror (errno)); - return; - } - } // end while (pDE1...) - - closedir(pDir1); - } // end if (pDir1) - - if (sFoundFile[0]) { // no file found, exit - if (access (sFoundFile, R_OK) == 0) { - char sAttachCmd[CBUFSIZE] = { '\0' }; - snprintf (sAttachCmd, sizeof(sAttachCmd), "%s %s", - devCode, sFoundFile); - t_stat scpCode = attach_cmd ((int32) 0, sAttachCmd); - if (scpCode == SCPE_OK) { - // add file to mount list - strncpy (mountedFiles[devNo], sFoundFile, CBUFSIZE); - printf ("\r\nMounted %s %s\r\n", devCode, mountedFiles[devNo]); - } - else { - // SIMH ATTACH command failed - printf ("\r\nSIMH error mounting %s on %s: %s\r\n", - sFoundFile, devCode, sim_error_text (scpCode)); - } - } - else { - printf ("\r\nCannot read medium image %s from USB: %s\r\n", - sFoundFile, strerror (errno)); - } - } - else { - printf ("\r\nNo unmounted %s file found\r\n", devCode); - } -} - - -//// handle_sing_step ////////////////////////////////////////////////// -// Handle SING_STEP combinations as nonstandard functions with respect -// to a real PDP-8, since SIMH doesn't try to emulate the PDP-8's -// single-stepping mode — not to be confused with single-instruction -// mode, which SIMH *does* emulate — so the SING_STEP switch is free -// for our nonstandard uses. -// -// This is separate from handle_flow_control_switches only because -// there are so many cases here that it would obscure the overall flow -// of our calling function to do all this there. - -static pidp8i_flow_t handle_sing_step (int closed) -{ - // If SING_STEP is open, we do nothing here except reset the single-shot - // flag if it was set. - static int single_shot = 0; - if (!closed) { - single_shot = 0; - return pft_normal; - } - - // There are two sets of SING_STEP combos: first up are those where the - // other switches involved have to be set already, and the function is - // triggered as soon as SING_STEP closes. These are functions we don't - // want re-executing repeatedly while SING_STEP remains closed. - if (single_shot == 0) { - // SING_STEP switch was open last we knew, and now it's closed, so - // set the single-shot flag. - single_shot = 1; - - // 1. Convert DF switch values to a device number, which - // we will map to a PDP-8 device type, then attempt to - // ATTACH some unmounted medium from USB to that device - // - // We treat DF == 0 as nothing to mount, since we use - // SING_STEP for other things, so we need a way to - // decide which meaning of SING_STEP to take here. - // - // The shift by 9 is how many non-DF bits are below - // DF in switchstatus[1] - // - // The bit complement is because closed DF switches show - // as 0, because they're dragging the pull-up down, but - // we want those treated as 1s, and vice versa. - uint16_t css1 = ~switchstatus[1]; - int swDevice = (css1 & SS1_DF_ALL) >> 9; - if (swDevice) { - char swDevCode[4] = { '\0' }; - switch (swDevice) { - case 1: strcpy (swDevCode, "ptr"); break; // PTR paper tape reader - case 2: strcpy (swDevCode, "ptp"); break; // High speed paper tape punch - case 3: strcpy (swDevCode, "dt0"); break; // TC08 DECtape (#8 is first!) - case 4: strcpy (swDevCode, "dt1"); break; - case 5: strcpy (swDevCode, "rx0"); break; // RX8E (8/e peripheral!) - case 6: strcpy (swDevCode, "rx1"); break; - case 7: strcpy (swDevCode, "rk1"); break; // second RK05 disk pack - } - if (swDevCode[0]) mount_usb_stick_file (swDevice, swDevCode); - } - - // 2. Do the same with IF, except that the switch value - // is used to decide which boot script to restart with via - // SIMH's DO command. - // - // The shift value of 6 is because the IF switches are 3 - // down from the DF switches above. - int swScript = (css1 & SS1_IF_ALL) >> 6; - if (swScript) { - printf ("\r\n\nRestarting with IF == %d...\r\n\r\n", swScript); - insert_scp_cmd = swScript; - return pft_halt; - } - } // end if single-shot flag clear - else { - // Now handle the second set of SING_STEP special-function - // combos, being those where the switches can be pressed in any - // order, so that we take action when the last one of the set - // closes, no matter which one that is. These immediately exit - // the SIMH instruction interpreter, so they won't re-execute - // merely because the human isn't fast enough to lift his finger - // by the time the next iteration of that loop starts. - - // 3. Scan for host poweroff command (Sing_Step + Sing_Inst + Stop) - if ((switchstatus[2] & (SS2_S_INST | SS2_STOP)) == 0) { - printf ("\r\nShutdown\r\n\r\n"); - insert_scp_cmd = CMD_EXIT; - if (spawn_cmd (0, "sudo /bin/systemctl poweroff") != SCPE_OK) { - printf ("\r\n\r\npoweroff failed\r\n\r\n"); - } - return pft_halt; - } - - // 4. Scan for host reboot command (Sing_Step + Sing_Inst + Start) - if ((switchstatus[2] & (SS2_S_INST | SS2_START)) == 0) { - printf ("\r\nReboot\r\n\r\n"); - insert_scp_cmd = CMD_EXIT; - if (spawn_cmd (0, "sudo /bin/systemctl reboot") != SCPE_OK) { - printf ("\r\n\r\nreboot failed\r\n\r\n"); - } - return pft_halt; - } - - #if 0 - // These combos once meant something, but no longer do. If you - // reassign them, think carefully whether they should continue to - // be handled here and not above in the "if" branch. If nothing - // prevents your function from being re-executed while SING_STEP - // remains closed and re-execution would be bad, move the test - // under the aegis of the single_shot flag. - - // 5. Sing_Step + Sing_Inst + Load Add - if ((switchstatus[2] & (SS2_S_INST | SS2_L_ADD)) == 0) { } - - // 6. Sing_Step + Sing_Inst + Deposit - if ((switchstatus[2] & (SS2_S_INST | SS2_DEP)) == 0) { } - #endif - } - - return pft_normal; -} - - -//// handle_flow_control_switches ////////////////////////////////////// -// Process all of the PiDP-8/I front panel switches that can affect the -// flow path of the PDP-8 simulator's instruction interpretation loop, -// returning a code telling the simulator our decision. -// -// The simulator passes in pointers to PDP-8 registers we may modify as -// a side effect of handling these switches. - -pidp8i_flow_t handle_flow_control_switches (uint16* pM, - uint32 *pPC, uint32 *pMA, int32 *pMB, int32 *pLAC, int32 *pIF, - int32 *pDF, int32* pint_req) -{ - // Exit early if the blink thread has not attached itself to the GPIO - // peripheral in the Pi, since that means we cannot safely interpret the - // data in the switchstatus array. This is especially important on - // non-Pi hosts, since switchstatus will remain zeroed, which we would - // interpret as "all switches are pressed!", causing havoc. - // - // It would be cheaper for our caller to check this for us and skip the - // call, but there's no good reason to expose such implementations - // details to it. We're trying to keep the PDP-8 simulator's CPU core - // as free of PiDP-8/I details as is practical. - if (!pidp8i_gpio_present) return pft_normal; - - // Handle the nonstandard SING_STEP + X combos, some of which halt - // the processor. - if (handle_sing_step ((switchstatus[2] & SS2_S_STEP) == 0) == pft_halt) { - return pft_halt; - } - - // Check for SING_INST switch close... - extern int swSingInst; - if (((switchstatus[2] & SS2_S_INST) == 0) && (swSingInst == 0)) { - // Put the processor in single-instruction mode until we get a - // CONT or START switch closure. Technically this is wrong - // according to DEC's docs: we're supposed to finish executing - // the next instruction before we "clear the RUN flip-flop" in - // DEC terms, whereas we're testing these switches before we - // fetch the next instruction. Show me how it matters, and - // I'll fix it. :) - swSingInst = 1; - } - - // ...and SING_INST switch open - extern int swStop; - if (swSingInst && (switchstatus[2] & SS2_S_INST)) { - swSingInst = 0; - swStop = 1; // still stopped on leaving SING_INST mode - } - - // Check for START switch press... - static int swStart = 0; - if (((switchstatus[2] & SS2_START) == 0) && (swStart == 0)) { - // Reset the CPU. - extern DEVICE cpu_dev; - extern t_stat cpu_reset (DEVICE *); - cpu_reset (&cpu_dev); - - // DEC's docs say there are a few additional things START does - // that cpu_reset() doesn't do for us. - // - // Don't need to do anything with MA and IR, as SIMH does that - // shortly after this function returns. - *pLAC = *pMB = 0; - - // cpu_reset() does its thing to the saved_* register copies - // in a few cases, but we need it to happen to the "real" - // registers instead, since our STOP/START behavior doesn't - // make use of saved_*. - REG* pibr = find_reg ("IB", NULL, &cpu_dev); - int32* pIB = pibr ? pibr->loc : 0 /* force segfault on err */ ; - *pIB = *pIF; - - // Reset our switch flags, too - swStop = 0; // START cancels STOP mode - swSingInst = 0; // allow SING INST mode re-entry - swStart = 1; // make it single-shot - -#if 0 // debugging - printf("\r\nSTART: [DF:%o] [IF:%o] [IB:%o] [PC:%04o] " - "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]", - (*pDF >> 12), (*pIF >> 12), (*pIB >> 12), (*pPC & 07777), - *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777); -#endif - } - - // ...and START switch release - if (swStart && (switchstatus[2] & SS2_START)) { - swStart = 0; - } - - // Check for CONT switch press... - static int swCont = 0; - extern int resumeFromInstructionLoopExit; - if ((((switchstatus[2] & SS2_CONT) == 0) && (swCont == 0)) || - resumeFromInstructionLoopExit) { - // The initial CONT press is special: how we handle it - // depends on the processor's state. - // - // FIXME: Are we handling MB correctly? [973271ae36] - swCont = 1; // make it single-shot - resumeFromInstructionLoopExit = 0; - if (swSingInst) { - // On the initial CONT press while in SING_INST mode, run - // one instruction only. - return pft_normal; - } - else if (swStop) { - // We were HLTed or STOPped, so CONT returns us to - // free-running mode. - swStop = 0; - -#if 0 // debugging - printf("\r\nCONT: [DF:%o] [IF:%o] [PC:%04o] " - "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]", - (*pDF >> 12), (*pIF >> 12), (*pPC & 07777), - *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777); -#endif - } - // else, CONT has no effect in this state - } - - // ...and CONT switch release - if (swCont && (switchstatus[2] & SS2_CONT)) { - swCont = 0; - } - - // Check for LOAD_ADD switch press. The only reason we bother - // making it single-shot is in case debugging is enabled. - // Otherwise, it matters not how long the slow human holds this - // swithc down, and thus how often we apply the values: all else - // but our printf() here is idempotent. - static int swLAdd = 0; - if ((swLAdd == 0) && (switchstatus[2] & SS2_L_ADD) == 0) { - // Copy SR into PC. Have to flip the bits because GPIO gives - // 0 for a closed switch and 1 for open, opposite what we want. - *pPC = (~switchstatus[0]) & 07777; - - // Copy DF switch settings to DF register - // - // The shift is because the DF positions inside the switchstatus[1] - // register happen to be 3 bit positions off of where we want them - // in DF here: we want to be able to logically OR PC and DF to make - // 15-bit data access addresses. - // - // We complement the bits here for the same reason we did above - uint16_t css1 = ~switchstatus[1]; - *pDF = (css1 & SS1_DF_ALL) << 3; - - // Do the same for IF. The only difference comes from the fact - // that IF is the next 3 bits down in switchstatus[1]. - *pIF = (css1 & SS1_IF_ALL) << 6; - -#if 0 // debugging - printf("\r\nL_ADD: [DF:%o] [IF:%o] [PC:%04o] " - "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]", - (*pDF >> 12), (*pIF >> 12), (*pPC & 07777), - *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777); -#endif - swLAdd = 1; // make it single-shot - } - - // ...and L_ADD switch release - if (swLAdd && (switchstatus[2] & SS2_L_ADD)) { - swLAdd = 0; - } - - // Check for DEP switch press... - static int swDep = 0; - if (((switchstatus[2] & SS2_DEP) == 0) && (swDep == 0)) { - uint16 sSR = (~switchstatus[0]) & 07777; // bit flip justified above - *pPC = *pPC & 07777; // sometimes high bits get set; squish 'em - -#if 0 // debugging - printf("\r\nDEP: [IF:%o] [PC:%04o] [SR:%04o]", - (*pIF >> 12), *pPC, sSR); -#endif - - /* ??? in 66 handbook: strictly speaking, SR goes into AC, - then AC into MB. Does it clear AC afterwards? If not, needs fix */ - pM[*pPC] = sSR; // FIXME: shouldn't we use IF/DF here? - *pMB = sSR; - *pMA = *pPC & 07777; // MA trails PC on FP; FIXME: OR in IF? - *pPC = (*pPC + 1) & 07777; // increment PC - swDep = 1; // make it single-shot - } - - // ...and DEP switch release - if (swDep && (switchstatus[2] & SS2_DEP)) { - swDep = 0; - } - - // Check for EXAM switch press... - static int swExam = 0; - if (((switchstatus[2] & SS2_EXAM) == 0) && (swExam == 0)) { - *pMB = pM[*pPC]; - *pMA = *pPC & 07777; // MA trails PC on FP - *pPC = (*pPC + 1) & 07777; // increment PC - swExam = 1; // make it single-shot - } - - // ...and EXAM switch release - if (swExam && (switchstatus[2] & SS2_EXAM)) { - swExam = 0; - } - - // Check for STOP switch press. No "and release" because we get out of - // STOP mode with START or CONT, not by releasing STOP, and while in - // STOP mode, this switch's function is idempotent. - if (!swStop && ((switchstatus[2] & SS2_STOP) == 0)) { - swStop = 1; - -#if 0 // debugging - printf("\r\nSTOP: [DF:%o] [IF:%o] [PC:%04o] " - "[MA:%04o] [MB:%04o] [L:%d] [AC:%04o]", - (*pDF >> 12), (*pIF >> 12), (*pPC & 07777), - *pMA, *pMB, !!(*pLAC & 010000), *pLAC & 07777); -#endif - } - - // If any of the above put us into STOP or SING_INST mode, go no - // further. In particular, fetch no more instructions, and do not - // touch PC! The only way to get un-stuck is CONT or STOP. - return (swStop || swSingInst) ? pft_stop : pft_normal; -} - - -//// get_switch_register /////////////////////////////////////////////// -// Return the current contents of the switch register - -int32 get_switch_register (void) -{ - return switchstatus[0] ^ 07777; -} DELETED src/PDP8/pidp8i.h Index: src/PDP8/pidp8i.h ================================================================== --- src/PDP8/pidp8i.h +++ /dev/null @@ -1,55 +0,0 @@ -/* 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 -#include - -typedef enum { - pft_normal, - pft_halt, - pft_stop, -} pidp8i_flow_t; - -extern char *build_pidp8i_scp_cmd (char* cbuf, size_t cbufsize); - -extern int32_t get_switch_register (void); -extern size_t get_pidp8i_initial_max_skips (size_t updates_per_sec); - -extern pidp8i_flow_t handle_flow_control_switches (uint16_t* pM, - uint32_t *pPC, uint32_t *pMA, int32_t *pMB, int32_t *pLAC, int32_t *pIF, - int32_t *pDF, int32_t* pint_req); - -extern void set_pidp8i_leds (uint32_t sPC, uint32_t sMA, uint16_t sIR, - int32_t sLAC, int32_t sMQ, int32_t sIF, int32_t sDF, int32_t sSC, - int32_t int_req, int Pause); - -#endif // !defined(PIDP8I_H) ADDED src/SIMH/Makefile.in Index: src/SIMH/Makefile.in ================================================================== --- /dev/null +++ src/SIMH/Makefile.in @@ -0,0 +1,47 @@ +######################################################################## +# Makefile.in - Processed by autosetup's configure script to generate +# an intermediate GNU make(1) file for building the PiDP-8/I software +# from within its src/SIMH subdirectory. +# +# The resulting Makefile will redirect simple "make" calls to the top +# level as well as the major top-level targets (e.g. "make clean") but +# purposefully will not redirect anything like an installation or "run +# the system" type target. Its only purpose is to help out those who +# are working on the PiDP-8/I project's C source code from within this +# directory. If you need to work on the wider system, do it from the +# project's top level. +# +# If you are seeing this at the top of a file called Makefile and you +# intend to make edits, do that in Makefile.in. Saying "make" will then +# re-build Makefile from that modified Makefile.in before proceeding to +# do the "make" operation. +# +# Copyright © 2017 Warren Young +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the names of the authors above +# shall not be used in advertising or otherwise to promote the sale, +# use or other dealings in this Software without prior written +# authorization from those authors. +######################################################################## + +all clean ctags distclean tags reconfig: + cd @builddir@; make $@ ADDED src/SIMH/PDP8/Makefile.in Index: src/SIMH/PDP8/Makefile.in ================================================================== --- /dev/null +++ src/SIMH/PDP8/Makefile.in @@ -0,0 +1,47 @@ +######################################################################## +# Makefile.in - Processed by autosetup's configure script to generate +# an intermediate GNU make(1) file for building the PiDP-8/I software +# from within its src/SIMH/PDP8 subdirectory. +# +# The resulting Makefile will redirect simple "make" calls to the top +# level as well as the major top-level targets (e.g. "make clean") but +# purposefully will not redirect anything like an installation or "run +# the system" type target. Its only purpose is to help out those who +# are working on the PiDP-8/I project's C source code from within this +# directory. If you need to work on the wider system, do it from the +# project's top level. +# +# If you are seeing this at the top of a file called Makefile and you +# intend to make edits, do that in Makefile.in. Saying "make" will then +# re-build Makefile from that modified Makefile.in before proceeding to +# do the "make" operation. +# +# Copyright © 2017 Warren Young +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the names of the authors above +# shall not be used in advertising or otherwise to promote the sale, +# use or other dealings in this Software without prior written +# authorization from those authors. +######################################################################## + +all clean ctags distclean tags reconfig: + cd @builddir@; make $@ ADDED src/SIMH/PDP8/pdp8_clk.c Index: src/SIMH/PDP8/pdp8_clk.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_clk.c @@ -0,0 +1,183 @@ +/* pdp8_clk.c: PDP-8 real-time clock simulator + + Copyright (c) 1993-2012, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + clk real time clock + + 18-Apr-12 RMS Added clock coscheduling + 18-Jun-07 RMS Added UNIT_IDLE flag + 01-Mar-03 RMS Aded SET/SHOW CLK FREQ support + 04-Oct-02 RMS Added DIB, device number support + 30-Dec-01 RMS Removed for generalized timers + 05-Sep-01 RMS Added terminal multiplexor support + 17-Jul-01 RMS Moved function prototype + 05-Mar-01 RMS Added clock calibration support + + Note: includes the IOT's for both the PDP-8/E and PDP-8/A clocks +*/ + +#include "pdp8_defs.h" + +extern int32 int_req, int_enable, dev_done, stop_inst; + +int32 clk_tps = 60; /* ticks/second */ +int32 tmxr_poll = 16000; /* term mux poll */ + +int32 clk (int32 IR, int32 AC); +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +t_stat clk_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc); + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_reg CLK register list +*/ + +DIB clk_dib = { DEV_CLK, 1, { &clk } }; + +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), 16000 }; + +REG clk_reg[] = { + { FLDATAD (DONE, dev_done, INT_V_CLK, "device done flag") }, + { FLDATAD (ENABLE, int_enable, INT_V_CLK, "interrupt enable flag") }, + { FLDATAD (INT, int_req, INT_V_CLK, "interrupt pending flag") }, + { DRDATAD (TIME, clk_unit.wait, 24, "clock interval"), REG_NZ + PV_LEFT }, + { DRDATA (TPS, clk_tps, 8), PV_LEFT + REG_HRO }, + { NULL } + }; + +MTAB clk_mod[] = { + { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, + NULL, &clk_show_freq, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, + { 0 } + }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + &clk_dib, 0 + }; + +/* IOT routine + + IOT's 6131-6133 are the PDP-8/E clock + IOT's 6135-6137 are the PDP-8/A clock +*/ + +int32 clk (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* CLEI */ + int_enable = int_enable | INT_CLK; /* enable clk ints */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 2: /* CLDI */ + int_enable = int_enable & ~INT_CLK; /* disable clk ints */ + int_req = int_req & ~INT_CLK; /* update interrupts */ + return AC; + + case 3: /* CLSC */ + if (dev_done & INT_CLK) { /* flag set? */ + dev_done = dev_done & ~INT_CLK; /* clear flag */ + int_req = int_req & ~INT_CLK; /* clear int req */ + return IOT_SKP + AC; + } + return AC; + + case 5: /* CLLE */ + if (AC & 1) /* test AC<11> */ + int_enable = int_enable | INT_CLK; + else int_enable = int_enable & ~INT_CLK; + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 6: /* CLCL */ + dev_done = dev_done & ~INT_CLK; /* clear flag */ + int_req = int_req & ~INT_CLK; /* clear int req */ + return AC; + + case 7: /* CLSK */ + return (dev_done & INT_CLK)? IOT_SKP + AC: AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_CLK; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +tmxr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ +sim_activate_after (uptr, 1000000/clk_tps); /* reactivate unit */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +dev_done = dev_done & ~INT_CLK; /* clear done, int */ +int_req = int_req & ~INT_CLK; +int_enable = int_enable & ~INT_CLK; /* clear enable */ +if (!sim_is_running) { /* RESET (not CAF)? */ + tmxr_poll = sim_rtcn_init_unit (&clk_unit, clk_unit.wait, TMR_CLK);/* init 100Hz timer */ + sim_activate_after (&clk_unit, 1000000/clk_tps); /* activate 100Hz unit */ + } +return SCPE_OK; +} + +/* Set frequency */ + +t_stat clk_set_freq (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +if (cptr) + return SCPE_ARG; +if ((val != 50) && (val != 60)) + return SCPE_IERR; +clk_tps = val; +return SCPE_OK; +} + +/* Show frequency */ + +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +fprintf (st, (clk_tps == 50)? "50Hz": "60Hz"); +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_cpu.c Index: src/SIMH/PDP8/pdp8_cpu.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_cpu.c @@ -0,0 +1,1800 @@ +/* pdp8_cpu.c: PDP-8 CPU simulator + + Copyright (c) 1993-2017, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ---------------------------------------------------------------------------- + + Portions copyright (c) 2015-2017, Oscar Vermeulen and Warren Young + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the names of the authors above shall + not be used in advertising or otherwise to promote the sale, use or other + dealings in this Software without prior written authorization from those + authors. + + ---------------------------------------------------------------------------- + + cpu central processor + + 07-Sep-17 RMS Fixed sim_eval declaration in history routine (COVERITY) + 09-Mar-17 RMS Fixed PCQ_ENTRY for interrupts (COVERITY) + 13-Feb-17 RMS RESET clear L'AC, per schematics + 28-Jan-17 RMS Renamed switch register variable to SR, per request + 18-Sep-16 RMS Added alternate dispatch table for non-contiguous devices + 17-Sep-13 RMS Fixed boot in wrong field problem (Dave Gesswein) + 28-Apr-07 RMS Removed clock initialization + 30-Oct-06 RMS Added idle and infinite loop detection + 30-Sep-06 RMS Fixed SC value after DVI overflow (Don North) + 22-Sep-05 RMS Fixed declarations (Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 06-Nov-04 RMS Added =n to SHOW HISTORY + 31-Dec-03 RMS Fixed bug in set_cpu_hist + 13-Oct-03 RMS Added instruction history + Added TSC8-75 support (Bernhard Baehr) + 12-Mar-03 RMS Added logical name support + 04-Oct-02 RMS Revamped device dispatching, added device number support + 06-Jan-02 RMS Added device enable/disable routines + 30-Dec-01 RMS Added old PC queue + 16-Dec-01 RMS Fixed bugs in EAE + 07-Dec-01 RMS Revised to use new breakpoint package + 30-Nov-01 RMS Added RL8A, extended SET/SHOW support + 16-Sep-01 RMS Fixed bug in reset routine, added KL8A support + 10-Aug-01 RMS Removed register from declarations + 17-Jul-01 RMS Moved function prototype + 07-Jun-01 RMS Fixed bug in JMS to non-existent memory + 25-Apr-01 RMS Added device enable/disable support + 18-Mar-01 RMS Added DF32 support + 05-Mar-01 RMS Added clock calibration support + 15-Feb-01 RMS Added DECtape support + 14-Apr-99 RMS Changed t_addr to unsigned + + The register state for the PDP-8 is: + + AC<0:11> accumulator + MQ<0:11> multiplier-quotient + L link flag + PC<0:11> program counter + IF<0:2> instruction field + IB<0:2> instruction buffer + DF<0:2> data field + UF user flag + UB user buffer + SF<0:6> interrupt save field + + The PDP-8 has three instruction formats: memory reference, I/O transfer, + and operate. The memory reference format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | op |in|zr| page offset | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:2> mnemonic action + + 000 AND AC = AC & M[MA] + 001 TAD L'AC = AC + M[MA] + 010 DCA M[MA] = AC, AC = 0 + 011 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 100 JMS M[MA] = PC, PC = MA + 1 + 101 JMP PC = MA + + <3:4> mode action + 00 page zero MA = IF'0'IR<5:11> + 01 current page MA = IF'PC<0:4>'IR<5:11> + 10 indirect page zero MA = xF'M[IF'0'IR<5:11>] + 11 indirect current page MA = xF'M[IF'PC<0:4>'IR<5:11>] + + where x is D for AND, TAD, ISZ, DCA, and I for JMS, JMP. + + Memory reference instructions can access an address space of 32K words. + The address space is divided into eight 4K word fields; each field is + divided into thirty-two 128 word pages. An instruction can directly + address, via its 7b offset, locations 0-127 on page zero or on the current + page. All 32k words can be accessed via indirect addressing and the + instruction and data field registers. If an indirect address is in + locations 0010-0017 of any field, the indirect address is incremented + and rewritten to memory before use. + + The I/O transfer format is as follows: + + 0 1 2 3 4 5 6 7 8 9 10 11 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | op | device | pulse | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+ + + The IO transfer instruction sends the the specified pulse to the + specified I/O device. The I/O device may take data from the AC, + return data to the AC, initiate or cancel operations, or skip on + status. + + The operate format is as follows: + + +--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 1| 1| 0| | | | | | | | | operate group 1 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | + | | | | | | | +--- increment AC 3 + | | | | | | +--- rotate 1 or 2 4 + | | | | | +--- rotate left 4 + | | | | +--- rotate right 4 + | | | +--- complement L 2 + | | +--- complement AC 2 + | +--- clear L 1 + +-- clear AC 1 + + +--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 1| 1| 1| | | | | | | | 0| operate group 2 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | + | | | | | | +--- halt 3 + | | | | | +--- or switch register 3 + | | | | +--- reverse skip sense 1 + | | | +--- skip on L != 0 1 + | | +--- skip on AC == 0 1 + | +--- skip on AC < 0 1 + +-- clear AC 2 + + +--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 1| 1| 1| | | | | | | | 1| operate group 3 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | \______/ + | | | | | + | | +--|-----+--- EAE command 3 + | | +--- AC -> MQ, 0 -> AC 2 + | +--- MQ v AC --> AC 2 + +-- clear AC 1 + + The operate instruction can be microprogrammed to perform operations + on the AC, MQ, and link. + + This routine is the instruction decode routine for the PDP-8. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + unimplemented instruction and stop_inst flag set + I/O error in I/O simulator + + 2. Interrupts. Interrupts are maintained by three parallel variables: + + dev_done device done flags + int_enable interrupt enable flags + int_req interrupt requests + + In addition, int_req contains the interrupt enable flag, the + CIF not pending flag, and the ION not pending flag. If all + three of these flags are set, and at least one interrupt request + is set, then an interrupt occurs. + + 3. Non-existent memory. On the PDP-8, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes outside the current field (indirect writes) need + be checked against actual memory size. + + 3. Adding I/O devices. These modules must be modified: + + pdp8_defs.h add device number and interrupt definitions + pdp8_sys.c add sim_devices table entry +*/ + +#include "pdp8_defs.h" + +/* ---PiDP add---------------------------------------------------------------------------------------------- */ +#include +/* ---PiDP end---------------------------------------------------------------------------------------------- */ + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY(x) pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = x +#define UNIT_V_NOEAE (UNIT_V_UF) /* EAE absent */ +#define UNIT_NOEAE (1 << UNIT_V_NOEAE) +#define UNIT_V_MSIZE (UNIT_V_UF + 1) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define OP_KSF 06031 /* for idle */ + +#define HIST_PC 0x40000000 +#define HIST_MIN 64 +#define HIST_MAX 65536 + +typedef struct { + int32 pc; + int32 ea; + int16 ir; + int16 opnd; + int16 lac; + int16 mq; + } InstHistory; + +uint16 M[MAXMEMSIZE] = { 0 }; /* main memory */ +int32 saved_LAC = 0; /* saved L'AC */ +int32 saved_MQ = 0; /* saved MQ */ +int32 saved_PC = 0; /* saved IF'PC */ +int32 saved_DF = 0; /* saved Data Field */ +int32 IB = 0; /* Instruction Buffer */ +int32 SF = 0; /* Save Field */ +int32 emode = 0; /* EAE mode */ +int32 gtf = 0; /* EAE gtf flag */ +int32 SC = 0; /* EAE shift count */ +int32 UB = 0; /* User mode Buffer */ +int32 UF = 0; /* User mode Flag */ +int32 SR = 0; /* Switch Register */ +int32 tsc_ir = 0; /* TSC8-75 IR */ +int32 tsc_pc = 0; /* TSC8-75 PC */ +int32 tsc_cdf = 0; /* TSC8-75 CDF flag */ +int32 tsc_enb = 0; /* TSC8-75 enabled */ +int32 cpu_astop = 0; /* address stop */ +int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 dev_done = 0; /* dev done flags */ +int32 int_enable = INT_INIT_ENABLE; /* intr enables */ +int32 int_req = 0; /* intr requests */ +int32 stop_inst = 0; /* trap on ill inst */ +int32 (*dev_tab[DEV_MAX])(int32 IR, int32 dat); /* device dispatch */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc); +t_bool build_dev_tab (void); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATAD (PC, saved_PC, 15, "program counter") }, + { ORDATAD (AC, saved_LAC, 12, "accumulator") }, + { FLDATAD (L, saved_LAC, 12, "link") }, + { ORDATAD (MQ, saved_MQ, 12, "multiplier-quotient") }, + { ORDATAD (SR, SR, 12, "front panel switches") }, + { GRDATAD (IF, saved_PC, 8, 3, 12, "instruction field") }, + { GRDATAD (DF, saved_DF, 8, 3, 12, "data field") }, + { GRDATAD (IB, IB, 8, 3, 12, "instruction field buffter") }, + { ORDATAD (SF, SF, 7, "save field") }, + { FLDATAD (UB, UB, 0, "user mode buffer") }, + { FLDATAD (UF, UF, 0, "user mode flag") }, + { ORDATAD (SC, SC, 5, "EAE shift counter") }, + { FLDATAD (GTF, gtf, 0, "EAE greater than flag") }, + { FLDATAD (EMODE, emode, 0, "EAE mode (0 = A, 1 = B)") }, + { FLDATAD (ION, int_req, INT_V_ION, "interrupt enable") }, + { FLDATAD (ION_DELAY, int_req, INT_V_NO_ION_PENDING, "interrupt enable delay for ION") }, + { FLDATAD (CIF_DELAY, int_req, INT_V_NO_CIF_PENDING, "interrupt enable delay for CIF") }, + { FLDATAD (PWR_INT, int_req, INT_V_PWR, "power fail interrupt") }, + { FLDATAD (UF_INT, int_req, INT_V_UF, "user mode violation interrupt") }, + { ORDATAD (INT, int_req, INT_V_ION+1, "interrupt pending flags"), REG_RO }, + { ORDATAD (DONE, dev_done, INT_V_DIRECT, "device done flags"), REG_RO }, + { ORDATAD (ENABLE, int_enable, INT_V_DIRECT, "device interrupt enable flags"), REG_RO }, + { BRDATAD (PCQ, pcq, 8, 15, PCQ_SIZE, "PC prior to last JMP, JMS, or interrupt; most recent PC change first"), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { FLDATAD (STOP_INST, stop_inst, 0, "stop on undefined instruction") }, + { ORDATAD (WRU, sim_int_char, 8, "interrupt character") }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL }, + { UNIT_NOEAE, 0, "EAE", "EAE", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 15, 1, 8, 12, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +t_stat sim_instr (void) +{ +int32 IR, MB, IF, DF, LAC, MQ; +uint32 PC, MA; +int32 device, pulse, temp, iot_data; +t_stat reason; + +/* Restore register state */ + +if (build_dev_tab ()) /* build dev_tab */ + return SCPE_STOP; +PC = saved_PC & 007777; /* load local copies */ +IF = saved_PC & 070000; +DF = saved_DF & 070000; +LAC = saved_LAC & 017777; +MQ = saved_MQ & 07777; +int_req = INT_UPDATE; +reason = 0; + + +/* ---PiDP add--------------------------------------------------------------------------------------------- */ +// PiDP-8/I specific flag, set when the last instruction was an IOT +// instruction to a real device. SIMH doesn't track this, but the front +// panel needs it. +int Pause = 0; + +// Set our initial IPS value from the throttle, if given. +static time_t last_update = 0; +static size_t max_skips = 0; +static const size_t pidp8i_updates_per_sec = 3200; +max_skips = get_pidp8i_initial_max_skips (pidp8i_updates_per_sec); +srand48 (time (&last_update)); + +// Reset display info in case we're re-entering the simulator from Ctrl-E +extern display display_bufs[2]; +memset (display_bufs, 0, sizeof(display_bufs)); +static size_t skip_count, dither, inst_count; +skip_count = dither = inst_count = 0; +/* ---PiDP end---------------------------------------------------------------------------------------------- */ + + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + + // Allow clean exit to SCP: https://github.com/simh/simh/issues/387 + if (cpu_astop != 0) { + cpu_astop = 0; + reason = SCPE_STOP; + break; + } + + if (sim_interval <= 0) { /* check clock queue */ + if ((reason = sim_process_event ())) { +/* ---PiDP add--------------------------------------------------------------------------------------------- */ + // We're about to leave the loop, so repaint one last time + // in case this is a Ctrl-E and we later get a "cont" + // command. Set a flag that will let us auto-resume. + extern int resumeFromInstructionLoopExit, swStop, swSingInst; + resumeFromInstructionLoopExit = swStop = swSingInst = 1; + set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, + int_req, Pause); + + // Also copy SR hardware value to software register in case + // the user tries poking at it from the sim> prompt. + SR = get_switch_register(); +/* ---PiDP end---------------------------------------------------------------------------------------------- */ + break; + } + } + +/* ---PiDP add--------------------------------------------------------------------------------------------- */ + + switch (handle_flow_control_switches(M, &PC, &MA, &MB, &LAC, &IF, + &DF, &int_req)) { + case pft_stop: + // Tell the SIMH event queue to keep running even though + // we're stopped. Without this, it will ignore Ctrl-E + // until the simulator is back in free-running mode. + sim_interval = sim_interval - 1; + + // Have to keep display updated while stopped. This does + // mean if the software starts with the STOP switch held + // down, we'll put garbage onto the display for MA, MB, and + // IR, but that's what the real hardware does, too. See + // https://github.com/simh/simh/issues/386 + set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, + int_req, Pause); + + // Go no further in STOP mode. In particular, fetch no more + // instructions, and do not touch PC! + continue; + + case pft_halt: + // Clear all registers and halt simulator + PC = saved_PC = 0; + IF = saved_PC = 0; + DF = saved_DF = 0; + LAC = saved_LAC = 0; + MQ = saved_MQ = 0; + int_req = 0; + reason = STOP_HALT; + continue; + + case pft_normal: + // execute normally + break; + } + +/* ---PiDP end---------------------------------------------------------------------------------------------- */ + + if (int_req > INT_PENDING) { /* interrupt? */ + int_req = int_req & ~INT_ION; /* interrupts off */ + SF = (UF << 6) | (IF >> 9) | (DF >> 12); /* form save field */ + PCQ_ENTRY (IF | PC); /* save old PC with IF */ + IF = IB = DF = UF = UB = 0; /* clear mem ext */ + M[0] = PC; /* save PC in 0 */ + PC = 1; /* fetch next from 1 */ + } + + MA = IF | PC; /* form PC */ + if (sim_brk_summ && + sim_brk_test (MA, (1u << SIM_BKPT_V_SPC) | SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + IR = M[MA]; /* fetch instruction */ + if (sim_brk_summ && + sim_brk_test (IR, (2u << SIM_BKPT_V_SPC) | SWMASK ('I'))) { /* breakpoint? */ + reason = STOP_OPBKPT; /* stop simulation */ + break; + } + PC = (PC + 1) & 07777; /* increment PC */ + int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ + sim_interval = sim_interval - 1; + +/* Instruction decoding. + + The opcode (IR<0:2>), indirect flag (IR<3>), and page flag (IR<4>) + are decoded together. This produces 32 decode points, four per + major opcode. For IOT, the extra decode points are not useful; + for OPR, only the group flag (IR<3>) is used. + + AND, TAD, ISZ, DCA calculate a full 15b effective address. + JMS, JMP calculate a 12b field-relative effective address. + + Autoindex calculations always occur within the same field as the + instruction fetch. The field must exist; otherwise, the instruction + fetched would be 0000, and indirect addressing could not occur. + + Note that MA contains IF'PC. +*/ + + if (hst_lnt) { /* history enabled? */ + int32 ea; + + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) + hst_p = 0; + hst[hst_p].pc = MA | HIST_PC; /* save PC, IR, LAC, MQ */ + hst[hst_p].ir = IR; + hst[hst_p].lac = LAC; + hst[hst_p].mq = MQ; + if (IR < 06000) { /* mem ref? */ + if (IR & 0200) + ea = (MA & 077600) | (IR & 0177); + else ea = IF | (IR & 0177); /* direct addr */ + if (IR & 0400) { /* indirect? */ + if (IR < 04000) { /* mem operand? */ + if ((ea & 07770) != 00010) + ea = DF | M[ea]; + else ea = DF | ((M[ea] + 1) & 07777); + } + else { /* no, jms/jmp */ + if ((ea & 07770) != 00010) + ea = IB | M[ea]; + else ea = IB | ((M[ea] + 1) & 07777); + } + } + hst[hst_p].ea = ea; /* save eff addr */ + hst[hst_p].opnd = M[ea]; /* save operand */ + } + } + +switch ((IR >> 7) & 037) { /* decode IR<0:4> */ + +/* Opcode 0, AND */ + + case 000: /* AND, dir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + LAC = LAC & (M[MA] | 010000); + break; + + case 001: /* AND, dir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + LAC = LAC & (M[MA] | 010000); + break; + + case 002: /* AND, indir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + LAC = LAC & (M[MA] | 010000); + break; + + case 003: /* AND, indir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + LAC = LAC & (M[MA] | 010000); + break; + +/* Opcode 1, TAD */ + + case 004: /* TAD, dir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + LAC = (LAC + M[MA]) & 017777; + break; + + case 005: /* TAD, dir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + LAC = (LAC + M[MA]) & 017777; + break; + + case 006: /* TAD, indir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + LAC = (LAC + M[MA]) & 017777; + break; + + case 007: /* TAD, indir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + LAC = (LAC + M[MA]) & 017777; + break; + +/* Opcode 2, ISZ */ + + case 010: /* ISZ, dir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ + if (MB == 0) + PC = (PC + 1) & 07777; + break; + + case 011: /* ISZ, dir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ + if (MB == 0) + PC = (PC + 1) & 07777; + break; + + case 012: /* ISZ, indir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + MB = (M[MA] + 1) & 07777; + if (MEM_ADDR_OK (MA)) + M[MA] = MB; + if (MB == 0) + PC = (PC + 1) & 07777; + break; + + case 013: /* ISZ, indir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + MB = (M[MA] + 1) & 07777; + if (MEM_ADDR_OK (MA)) + M[MA] = MB; + if (MB == 0) + PC = (PC + 1) & 07777; + break; + +/* Opcode 3, DCA */ + + case 014: /* DCA, dir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; + + case 015: /* DCA, dir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; + + case 016: /* DCA, indir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (MEM_ADDR_OK (MA)) + M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; + + case 017: /* DCA, indir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (MEM_ADDR_OK (MA)) + M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; + +/* Opcode 4, JMS. From Bernhard Baehr's description of the TSC8-75: + + (In user mode) the current JMS opcode is moved to the ERIOT register, the ECDF + flag is cleared. The address of the JMS instruction is loaded into the ERTB + register and the TSC8-75 I/O flag is raised. When the TSC8-75 is enabled, the + target addess of the JMS is loaded into PC, but nothing else (loading of IF, UF, + clearing the interrupt inhibit flag, storing of the return address in the first + word of the subroutine) happens. When the TSC8-75 is disabled, the JMS is performed + as usual. */ + + case 020: /* JMS, dir, zero */ + PCQ_ENTRY (MA); + MA = IR & 0177; /* dir addr, page zero */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + if (UF && tsc_enb) { /* user mode, TSC enab? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + else { /* normal */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + MA = IF | MA; + if (MEM_ADDR_OK (MA)) + M[MA] = PC; + } + PC = (MA + 1) & 07777; + break; + + case 021: /* JMS, dir, curr */ + PCQ_ENTRY (MA); + MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + if (UF && tsc_enb) { /* user mode, TSC enab? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + else { /* normal */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + MA = IF | MA; + if (MEM_ADDR_OK (MA)) + M[MA] = PC; + } + PC = (MA + 1) & 07777; + break; + + case 022: /* JMS, indir, zero */ + PCQ_ENTRY (MA); + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = M[MA]; + else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + if (UF && tsc_enb) { /* user mode, TSC enab? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + else { /* normal */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + MA = IF | MA; + if (MEM_ADDR_OK (MA)) + M[MA] = PC; + } + PC = (MA + 1) & 07777; + break; + + case 023: /* JMS, indir, curr */ + PCQ_ENTRY (MA); + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = M[MA]; + else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + if (UF && tsc_enb) { /* user mode, TSC enab? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + else { /* normal */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + MA = IF | MA; + if (MEM_ADDR_OK (MA)) + M[MA] = PC; + } + PC = (MA + 1) & 07777; + break; + +/* Opcode 5, JMP. From Bernhard Baehr's description of the TSC8-75: + + (In user mode) the current JMP opcode is moved to the ERIOT register, the ECDF + flag is cleared. The address of the JMP instruction is loaded into the ERTB + register and the TSC8-75 I/O flag is raised. Then the JMP is performed as usual + (including the setting of IF, UF and clearing the interrupt inhibit flag). */ + + + case 024: /* JMP, dir, zero */ + PCQ_ENTRY (MA); + MA = IR & 0177; /* dir addr, page zero */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + if (tsc_enb) { /* TSC8 enabled? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + } + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + PC = MA; + break; + +/* If JMP direct, also check for idle (KSF/JMP *-1) and infinite loop */ + + case 025: /* JMP, dir, curr */ + PCQ_ENTRY (MA); + MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + if (tsc_enb) { /* TSC8 enabled? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + } + if (sim_idle_enab && /* idling enabled? */ + (IF == IB)) { /* to same bank? */ + if (MA == ((PC - 2) & 07777)) { /* 1) JMP *-1? */ + if (!(int_req & (INT_ION|INT_TTI)) && /* iof, TTI flag off? */ + (M[IB|((PC - 2) & 07777)] == OP_KSF)) /* next is KSF? */ + sim_idle (TMR_CLK, FALSE); /* we're idle */ + } /* end JMP *-1 */ + else if (MA == ((PC - 1) & 07777)) { /* 2) JMP *? */ + if (!(int_req & INT_ION)) /* iof? */ + reason = STOP_LOOP; /* then infinite loop */ + else if (!(int_req & INT_ALL)) /* ion, not intr? */ + sim_idle (TMR_CLK, FALSE); /* we're idle */ + } /* end JMP */ + } /* end idle enabled */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + PC = MA; + break; + + case 026: /* JMP, indir, zero */ + PCQ_ENTRY (MA); + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = M[MA]; + else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + if (tsc_enb) { /* TSC8 enabled? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + } + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + PC = MA; + break; + + case 027: /* JMP, indir, curr */ + PCQ_ENTRY (MA); + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = M[MA]; + else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + if (tsc_enb) { /* TSC8 enabled? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + } + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + PC = MA; + break; + +/* Opcode 7, OPR group 1 */ + + case 034:case 035: /* OPR, group 1 */ + switch ((IR >> 4) & 017) { /* decode IR<4:7> */ + case 0: /* nop */ + break; + case 1: /* CML */ + LAC = LAC ^ 010000; + break; + case 2: /* CMA */ + LAC = LAC ^ 07777; + break; + case 3: /* CMA CML */ + LAC = LAC ^ 017777; + break; + case 4: /* CLL */ + LAC = LAC & 07777; + break; + case 5: /* CLL CML = STL */ + LAC = LAC | 010000; + break; + case 6: /* CLL CMA */ + LAC = (LAC ^ 07777) & 07777; + break; + case 7: /* CLL CMA CML */ + LAC = (LAC ^ 07777) | 010000; + break; + case 010: /* CLA */ + LAC = LAC & 010000; + break; + case 011: /* CLA CML */ + LAC = (LAC & 010000) ^ 010000; + break; + case 012: /* CLA CMA = STA */ + LAC = LAC | 07777; + break; + case 013: /* CLA CMA CML */ + LAC = (LAC | 07777) ^ 010000; + break; + case 014: /* CLA CLL */ + LAC = 0; + break; + case 015: /* CLA CLL CML */ + LAC = 010000; + break; + case 016: /* CLA CLL CMA */ + LAC = 07777; + break; + case 017: /* CLA CLL CMA CML */ + LAC = 017777; + break; + } /* end switch opers */ + + if (IR & 01) /* IAC */ + LAC = (LAC + 1) & 017777; + switch ((IR >> 1) & 07) { /* decode IR<8:10> */ + case 0: /* nop */ + break; + case 1: /* BSW */ + LAC = (LAC & 010000) | ((LAC >> 6) & 077) | ((LAC & 077) << 6); + break; + case 2: /* RAL */ + LAC = ((LAC << 1) | (LAC >> 12)) & 017777; + break; + case 3: /* RTL */ + LAC = ((LAC << 2) | (LAC >> 11)) & 017777; + break; + case 4: /* RAR */ + LAC = ((LAC >> 1) | (LAC << 12)) & 017777; + break; + case 5: /* RTR */ + LAC = ((LAC >> 2) | (LAC << 11)) & 017777; + break; + case 6: /* RAL RAR - undef */ + LAC = LAC & (IR | 010000); /* uses AND path */ + break; + case 7: /* RTL RTR - undef */ + LAC = (LAC & 010000) | (MA & 07600) | (IR & 0177); + break; /* uses address path */ + } /* end switch shifts */ + break; /* end group 1 */ + +/* OPR group 2. From Bernhard Baehr's description of the TSC8-75: + + (In user mode) HLT (7402), OSR (7404) and microprogrammed combinations with + HLT and OSR: Additional to raising a user mode interrupt, the current OPR + opcode is moved to the ERIOT register and the ECDF flag is cleared. */ + + case 036:case 037: /* OPR, groups 2, 3 */ + if ((IR & 01) == 0) { /* group 2 */ + switch ((IR >> 3) & 017) { /* decode IR<6:8> */ + case 0: /* nop */ + break; + case 1: /* SKP */ + PC = (PC + 1) & 07777; + break; + case 2: /* SNL */ + if (LAC >= 010000) + PC = (PC + 1) & 07777; + break; + case 3: /* SZL */ + if (LAC < 010000) + PC = (PC + 1) & 07777; + break; + case 4: /* SZA */ + if ((LAC & 07777) == 0) + PC = (PC + 1) & 07777; + break; + case 5: /* SNA */ + if ((LAC & 07777) + != 0) PC = (PC + 1) & 07777; + break; + case 6: /* SZA | SNL */ + if ((LAC == 0) || (LAC >= 010000)) + PC = (PC + 1) & 07777; + break; + case 7: /* SNA & SZL */ + if ((LAC != 0) && (LAC < 010000)) + PC = (PC + 1) & 07777; + break; + case 010: /* SMA */ + if ((LAC & 04000) != 0) + PC = (PC + 1) & 07777; + break; + case 011: /* SPA */ + if ((LAC & 04000) == 0) + PC = (PC + 1) & 07777; + break; + case 012: /* SMA | SNL */ + if (LAC >= 04000) + PC = (PC + 1) & 07777; + break; + case 013: /* SPA & SZL */ + if (LAC < 04000) + PC = (PC + 1) & 07777; + break; + case 014: /* SMA | SZA */ + if (((LAC & 04000) != 0) || ((LAC & 07777) == 0)) + PC = (PC + 1) & 07777; + break; + case 015: /* SPA & SNA */ + if (((LAC & 04000) == 0) && ((LAC & 07777) != 0)) + PC = (PC + 1) & 07777; + break; + case 016: /* SMA | SZA | SNL */ + if ((LAC >= 04000) || (LAC == 0)) + PC = (PC + 1) & 07777; + break; + case 017: /* SPA & SNA & SZL */ + if ((LAC < 04000) && (LAC != 0)) + PC = (PC + 1) & 07777; + break; + } /* end switch skips */ + if (IR & 0200) /* CLA */ + LAC = LAC & 010000; + if ((IR & 06) && UF) { /* user mode? */ + int_req = int_req | INT_UF; /* request intr */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + else { + if (IR & 04) { /* OSR */ +/* ---PiDP add--------------------------------------------------------------------------------------------- */ + SR = get_switch_register(); /* get current SR */ +/* ---PiDP end---------------------------------------------------------------------------------------------- */ + LAC = LAC | SR; + } + if (IR & 02) { /* HLT */ +//--- PiDP change----------------------------------------------------------------------- + // reason = STOP_HALT; + extern int swStop; + swStop = 1; + } +//--- end of PiDP change---------------------------------------------------------------- + } + break; + } /* end if group 2 */ + +/* OPR group 3 standard + + MQA!MQL exchanges AC and MQ, as follows: + + temp = MQ; + MQ = LAC & 07777; + LAC = LAC & 010000 | temp; +*/ + + temp = MQ; /* group 3 */ + if (IR & 0200) /* CLA */ + LAC = LAC & 010000; + if (IR & 0020) { /* MQL */ + MQ = LAC & 07777; + LAC = LAC & 010000; + } + if (IR & 0100) /* MQA */ + LAC = LAC | temp; + if ((IR & 0056) && (cpu_unit.flags & UNIT_NOEAE)) { + reason = stop_inst; /* EAE not present */ + break; + } + +/* OPR group 3 EAE + + The EAE operates in two modes: + + Mode A, PDP-8/I compatible + Mode B, extended capability + + Mode B provides eight additional subfunctions; in addition, some + of the Mode A functions operate differently in Mode B. + + The mode switch instructions are decoded explicitly and cannot be + microprogrammed with other EAE functions (SWAB performs an MQL as + part of standard group 3 decoding). If mode switching is decoded, + all other EAE timing is suppressed. +*/ + + if (IR == 07431) { /* SWAB */ + emode = 1; /* set mode flag */ + break; + } + if (IR == 07447) { /* SWBA */ + emode = gtf = 0; /* clear mode, gtf */ + break; + } + +/* If not switching modes, the EAE operation is determined by the mode + and IR<6,8:10>: + + <6:10> mode A mode B comments + + 0x000 NOP NOP + 0x001 SCL ACS + 0x010 MUY MUY if mode B, next = address + 0x011 DVI DVI if mode B, next = address + 0x100 NMI NMI if mode B, clear AC if + result = 4000'0000 + 0x101 SHL SHL if mode A, extra shift + 0x110 ASR ASR if mode A, extra shift + 0x111 LSR LSR if mode A, extra shift + 1x000 SCA SCA + 1x001 SCA + SCL DAD + 1x010 SCA + MUY DST + 1x011 SCA + DVI SWBA NOP if not detected earlier + 1x100 SCA + NMI DPSZ + 1x101 SCA + SHL DPIC must be combined with MQA!MQL + 1x110 SCA + ASR DCM must be combined with MQA!MQL + 1x111 SCA + LSR SAM + + EAE instructions which fetch memory operands use the CPU's DEFER + state to read the first word; if the address operand is in locations + x0010 - x0017, it is autoincremented. +*/ + + if (emode == 0) /* mode A? clr gtf */ + gtf = 0; + switch ((IR >> 1) & 027) { /* decode IR<6,8:10> */ + + case 020: /* mode A, B: SCA */ + LAC = LAC | SC; + break; + case 000: /* mode A, B: NOP */ + break; + + case 021: /* mode B: DAD */ + if (emode) { + MA = IF | PC; + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + MQ = MQ + M[MA]; + MA = DF | ((MA + 1) & 07777); + LAC = (LAC & 07777) + M[MA] + (MQ >> 12); + MQ = MQ & 07777; + PC = (PC + 1) & 07777; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 001: /* mode B: ACS */ + if (emode) { + SC = LAC & 037; + LAC = LAC & 010000; + } + else { /* mode A: SCL */ + SC = (~M[IF | PC]) & 037; + PC = (PC + 1) & 07777; + } + break; + + case 022: /* mode B: DST */ + if (emode) { + MA = IF | PC; + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (MEM_ADDR_OK (MA)) + M[MA] = MQ & 07777; + MA = DF | ((MA + 1) & 07777); + if (MEM_ADDR_OK (MA)) + M[MA] = LAC & 07777; + PC = (PC + 1) & 07777; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 002: /* MUY */ + MA = IF | PC; + if (emode) { /* mode B: defer */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + } + temp = (MQ * M[MA]) + (LAC & 07777); + LAC = (temp >> 12) & 07777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = 014; /* 12 shifts */ + break; + + case 023: /* mode B: SWBA */ + if (emode) + break; + LAC = LAC | SC; /* mode A: SCA then */ + case 003: /* DVI */ + MA = IF | PC; + if (emode) { /* mode B: defer */ + if ((MA & 07770) != 00010) /* indirect; autoinc? */ + MA = DF | M[MA]; + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + } + if ((LAC & 07777) >= M[MA]) { /* overflow? */ + LAC = LAC | 010000; /* set link */ + MQ = ((MQ << 1) + 1) & 07777; /* rotate MQ */ + SC = 0; /* no shifts */ + } + else { + temp = ((LAC & 07777) << 12) | MQ; + MQ = temp / M[MA]; + LAC = temp % M[MA]; + SC = 015; /* 13 shifts */ + } + PC = (PC + 1) & 07777; + break; + + case 024: /* mode B: DPSZ */ + if (emode) { + if (((LAC | MQ) & 07777) == 0) + PC = (PC + 1) & 07777; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 004: /* NMI */ + temp = (LAC << 12) | MQ; /* preserve link */ + for (SC = 0; ((temp & 017777777) != 0) && + (temp & 040000000) == ((temp << 1) & 040000000); SC++) + temp = temp << 1; + LAC = (temp >> 12) & 017777; + MQ = temp & 07777; + if (emode && ((LAC & 07777) == 04000) && (MQ == 0)) + LAC = LAC & 010000; /* clr if 4000'0000 */ + break; + + case 025: /* mode B: DPIC */ + if (emode) { + temp = (LAC + 1) & 07777; /* SWP already done! */ + LAC = MQ + (temp == 0); + MQ = temp; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 5: /* SHL */ + SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ + if (SC > 25) /* >25? result = 0 */ + temp = 0; + else temp = ((LAC << 12) | MQ) << SC; /* <=25? shift LAC:MQ */ + LAC = (temp >> 12) & 017777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = emode? 037: 0; /* SC = 0 if mode A */ + break; + + case 026: /* mode B: DCM */ + if (emode) { + temp = (-LAC) & 07777; /* SWP already done! */ + LAC = (MQ ^ 07777) + (temp == 0); + MQ = temp; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 6: /* ASR */ + SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ + temp = ((LAC & 07777) << 12) | MQ; /* sext from AC0 */ + if (LAC & 04000) + temp = temp | ~037777777; + if (emode && (SC != 0)) + gtf = (temp >> (SC - 1)) & 1; + if (SC > 25) + temp = (LAC & 04000)? -1: 0; + else temp = temp >> SC; + LAC = (temp >> 12) & 017777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = emode? 037: 0; /* SC = 0 if mode A */ + break; + + case 027: /* mode B: SAM */ + if (emode) { + temp = LAC & 07777; + LAC = MQ + (temp ^ 07777) + 1; /* L'AC = MQ - AC */ + gtf = (temp <= MQ) ^ ((temp ^ MQ) >> 11); + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 7: /* LSR */ + SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ + temp = ((LAC & 07777) << 12) | MQ; /* clear link */ + if (emode && (SC != 0)) + gtf = (temp >> (SC - 1)) & 1; + if (SC > 24) /* >24? result = 0 */ + temp = 0; + else temp = temp >> SC; /* <=24? shift AC:MQ */ + LAC = (temp >> 12) & 07777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = emode? 037: 0; /* SC = 0 if mode A */ + break; + } /* end switch */ + break; /* end case 7 */ + +/* Opcode 6, IOT. From Bernhard Baehr's description of the TSC8-75: + + (In user mode) Additional to raising a user mode interrupt, the current IOT + opcode is moved to the ERIOT register. When the IOT is a CDF instruction (62x1), + the ECDF flag is set, otherwise it is cleared. */ + + case 030:case 031:case 032:case 033: /* IOT */ + if (UF) { /* privileged? */ + int_req = int_req | INT_UF; /* request intr */ + tsc_ir = IR; /* save instruction */ + if ((IR & 07707) == 06201) /* set/clear flag */ + tsc_cdf = 1; + else tsc_cdf = 0; + break; + } + device = (IR >> 3) & 077; /* device = IR<3:8> */ + pulse = IR & 07; /* pulse = IR<9:11> */ + iot_data = LAC & 07777; /* AC unchanged */ + switch (device) { /* decode IR<3:8> */ + + case 000: /* CPU control */ + switch (pulse) { /* decode IR<9:11> */ + + case 0: /* SKON */ + if (int_req & INT_ION) + PC = (PC + 1) & 07777; + int_req = int_req & ~INT_ION; + break; + + case 1: /* ION */ + int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; + break; + + case 2: /* IOF */ + int_req = int_req & ~INT_ION; + break; + + case 3: /* SRQ */ + if (int_req & INT_ALL) + PC = (PC + 1) & 07777; + break; + + case 4: /* GTF */ + LAC = (LAC & 010000) | + ((LAC & 010000) >> 1) | (gtf << 10) | + (((int_req & INT_ALL) != 0) << 9) | + (((int_req & INT_ION) != 0) << 7) | SF; + break; + + case 5: /* RTF */ + gtf = ((LAC & 02000) >> 10); + UB = (LAC & 0100) >> 6; + IB = (LAC & 0070) << 9; + DF = (LAC & 0007) << 12; + LAC = ((LAC & 04000) << 1) | iot_data; + int_req = (int_req | INT_ION) & ~INT_NO_CIF_PENDING; + break; + + case 6: /* SGT */ + if (gtf) + PC = (PC + 1) & 07777; + break; + + case 7: /* CAF */ + gtf = 0; + emode = 0; + int_req = int_req & INT_NO_CIF_PENDING; + dev_done = 0; + int_enable = INT_INIT_ENABLE; + LAC = 0; + reset_all (1); /* reset all dev */ + break; + } /* end switch pulse */ + break; /* end case 0 */ + + case 020:case 021:case 022:case 023: + case 024:case 025:case 026:case 027: /* memory extension */ + switch (pulse) { /* decode IR<9:11> */ + + case 1: /* CDF */ + DF = (IR & 0070) << 9; + break; + + case 2: /* CIF */ + IB = (IR & 0070) << 9; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + + case 3: /* CDF CIF */ + DF = IB = (IR & 0070) << 9; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + + case 4: + switch (device & 07) { /* decode IR<6:8> */ + + case 0: /* CINT */ + int_req = int_req & ~INT_UF; + break; + + case 1: /* RDF */ + LAC = LAC | (DF >> 9); + break; + + case 2: /* RIF */ + LAC = LAC | (IF >> 9); + break; + + case 3: /* RIB */ + LAC = LAC | SF; + break; + + case 4: /* RMF */ + UB = (SF & 0100) >> 6; + IB = (SF & 0070) << 9; + DF = (SF & 0007) << 12; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + + case 5: /* SINT */ + if (int_req & INT_UF) + PC = (PC + 1) & 07777; + break; + + case 6: /* CUF */ + UB = 0; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + + case 7: /* SUF */ + UB = 1; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + } /* end switch device */ + break; + + default: + reason = stop_inst; + break; + } /* end switch pulse */ + break; /* end case 20-27 */ + + case 010: /* power fail */ + switch (pulse) { /* decode IR<9:11> */ + + case 1: /* SBE */ + break; + + case 2: /* SPL */ + if (int_req & INT_PWR) + PC = (PC + 1) & 07777; + break; + + case 3: /* CAL */ + int_req = int_req & ~INT_PWR; + break; + + default: + reason = stop_inst; + break; + } /* end switch pulse */ + break; /* end case 10 */ + + default: /* I/O device */ + if (dev_tab[device]) { /* dev present? */ +/* ---PiDP add--------------------------------------------------------------------------------------------- */ + // Any other device will trigger IOP, so light pause + Pause = 1; +/* ---PiDP end---------------------------------------------------------------------------------------------- */ + iot_data = dev_tab[device] (IR, iot_data); + LAC = (LAC & 010000) | (iot_data & 07777); + if (iot_data & IOT_SKP) + PC = (PC + 1) & 07777; + if (iot_data >= IOT_REASON) + reason = iot_data >> IOT_V_REASON; + } + else reason = stop_inst; /* stop on flag */ + break; + } /* end switch device */ + break; /* end case IOT */ + } /* end switch opcode */ + +/* ---PiDP add--------------------------------------------------------------------------------------------- */ + // Update the front panel with this instruction's final state. + // + // There's no point saving *every* LED "on" count. We just need a + // suitable amount of oversampling. We can skip this if we called + // set_pidp8i_leds recently enough, avoiding all the expensive bit + // shift and memory update work it does. + // + // The trick here is figuring out what "recently enough" means + // without making expensive OS timer calls. These timers aren't + // hopelessly slow (http://stackoverflow.com/a/13096917/142454) but + // we still don't want to be taking dozens of cycles per instruction + // just to keep our update estimate current in the face of system + // load changes and SET THROTTLE updates. + // + // Instead, we maintain a model of the current IPS value — seeded + // with the initial "SET THROTTLE" value, if any — to figure out + // how many calls we can skip while still meeting our other goals. + // This involves a bit of math, but when paid only once a second, + // it amortizes much nicer than estimating the skip count directly + // based on a more accurate time source which is more expensive + // to call. It's also cheaper than continually asking SIMH to + // estimate the SIMH IPS value, since it uses FP math. + // + // Each LED panel repaint takes about 10 ms, so we do about 100 + // full-panel updates per second. We need a bare minimum of 32 + // discernible brightness values per update for ILS, so if we don't + // update the LED status data at least 3,200 times per second, we + // don't have enough data for smooth panel updates. Fortunately, + // computers are pretty quick, and our slowest script runs at 30 + // kIPS. (5.script.) + // + // We deliberately add some timing jitter here to get stochastic + // sampling of the incoming instructions to avoid beat frequencies + // between our update rate and the instruction pattern being + // executed by the front panel. It's a form of dithering. + // + // You might think to move this code to the top of set_pidp8i_leds, + // but the function call itself is a nontrivial hit. In fact, you + // don't even want to move all of this to a function here in this + // module and try to get GCC to inline it: that's good for a 1 MIPS + // speed hit in my testing! (GCC 4.9.2, Raspbian Jessie on Pi 3B.) + + if (++skip_count >= (max_skips - dither)) { + // Save skips to inst counter and reset + inst_count += skip_count; + skip_count = 0; + + // We need to update the LED data again + set_pidp8i_leds (PC, MA, IR, LAC, MQ, IF, DF, SC, int_req, Pause); + + // Has it been ~1s since we updated our max_skips value? + time_t now; + if (time(&now) > last_update) { + // Yep; simulator IPS may have changed, so freshen it. + last_update = now; + max_skips = inst_count / pidp8i_updates_per_sec; + //printf("Inst./repaint: %zu - %zu; %.2f MIPS\r\n", + // max_skips, dither, inst_count / 1e6); + inst_count = 0; + } + dither = max_skips > 32 ? lrand48() % (max_skips >> 3) : 0; // 12.5% + } + Pause = 0; // it's set outside the "if", so it must be *reset* outside +/* ---PiDP end---------------------------------------------------------------------------------------------- */ + } /* end while */ + +/* Simulation halted */ + +saved_PC = IF | (PC & 07777); /* save copies */ +saved_DF = DF & 070000; +saved_LAC = LAC & 017777; +saved_MQ = MQ & 07777; +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} /* end sim_instr */ + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +saved_LAC = 0; +int_req = (int_req & ~INT_ION) | INT_NO_CIF_PENDING; +saved_DF = IB = saved_PC & 070000; +UF = UB = gtf = emode = 0; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) + pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = SWMASK ('E') | SWMASK('I'); +sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Set PC for boot (PC<14:12> will typically be 0) */ + +void cpu_set_bootpc (int32 pc) +{ +saved_PC = pc; /* set PC, IF */ +saved_DF = IB = pc & 070000; /* set IB, DF */ +return; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) + return SCPE_NXM; +if (vptr != NULL) + *vptr = M[addr] & 07777; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) + return SCPE_NXM; +M[addr] = val & 07777; +return SCPE_OK; +} + +/* Memory size change */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) + mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) + M[i] = 0; +return SCPE_OK; +} + +/* Change device number for a device */ + +t_stat set_dev (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newdev; +t_stat r; + +if (cptr == NULL) + return SCPE_ARG; +if (uptr == NULL) + return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) + return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) + return SCPE_IERR; +newdev = get_uint (cptr, 8, DEV_MAX - 1, &r); /* get new */ +if ((r != SCPE_OK) || (newdev == dibp->dev)) + return r; +dibp->dev = newdev; /* store */ +return SCPE_OK; +} + +/* Show device number for a device */ + +t_stat show_dev (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) + return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) + return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) + return SCPE_IERR; +fprintf (st, "devno=%02o", dibp->dev); +if (dibp->num > 1) + fprintf (st, "-%2o", dibp->dev + dibp->num - 1); +return SCPE_OK; +} + +/* CPU device handler - should never get here! */ + +int32 bad_dev (int32 IR, int32 AC) +{ +return (SCPE_IERR << IOT_V_REASON) | AC; /* broken! */ +} + +/* Build device dispatch table */ + +t_bool build_dev_tab (void) +{ +DEVICE *dptr; +DIB *dibp; +uint32 i, j; +static const uint8 std_dev[] = { + 000, 010, 020, 021, 022, 023, 024, 025, 026, 027 + }; + +for (i = 0; i < DEV_MAX; i++) /* clr table */ + dev_tab[i] = NULL; +for (i = 0; i < ((uint32) sizeof (std_dev)); i++) /* std entries */ + dev_tab[std_dev[i]] = &bad_dev; +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* add devices */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ + if (dibp->dsp_tbl) { /* dispatch table? */ + DIB_DSP *dspp = dibp->dsp_tbl; /* set ptr */ + for (j = 0; j < dibp->num; j++, dspp++) { /* loop thru tbl */ + if (dspp->dsp) { /* any dispatch? */ + if (dev_tab[dspp->dev]) { /* already filled? */ + sim_printf ("%s device number conflict at %02o\n", + sim_dname (dptr), dibp->dev + j); + return TRUE; + } + dev_tab[dspp->dev] = dspp->dsp; /* fill */ + } /* end if dsp */ + } /* end for j */ + } /* end if dsp_tbl */ + else { /* inline dispatches */ + for (j = 0; j < dibp->num; j++) { /* loop thru disp */ + if (dibp->dsp[j]) { /* any dispatch? */ + if (dev_tab[dibp->dev + j]) { /* already filled? */ + sim_printf ("%s device number conflict at %02o\n", + sim_dname (dptr), dibp->dev + j); + return TRUE; + } + dev_tab[dibp->dev + j] = dibp->dsp[j]; /* fill */ + } /* end if dsp */ + } /* end for j */ + } /* end else */ + } /* end if enb */ + } /* end for i */ +return FALSE; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) + hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) + return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) + return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +int32 l, k, di, lnt; +const char *cptr = (const char *) desc; +t_stat r; +InstHistory *h; + +if (hst_lnt == 0) /* enabled? */ + return SCPE_NOFNC; +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) + return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) + di = di + hst_lnt; +fprintf (st, "PC L AC MQ ea IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + l = (h->lac >> 12) & 1; /* link */ + fprintf (st, "%05o %o %04o %04o ", h->pc & ADDRMASK, l, h->lac & 07777, h->mq); + if (h->ir < 06000) + fprintf (st, "%05o ", h->ea); + else fprintf (st, " "); + sim_eval[0] = h->ir; + if ((fprint_sym (st, h->pc & ADDRMASK, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %04o", h->ir); + if (h->ir < 04000) + fprintf (st, " [%04o]", h->opnd); + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_ct.c Index: src/SIMH/PDP8/pdp8_ct.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_ct.c @@ -0,0 +1,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 + + KGOA /READ 1ST CHAR, CLEAR DF + DCA CHAR + : + + KGOA /READ LAST CHAR, CLEAR DF + DCA CHAR + + KLSA /SELECT CRC MODE + KGOA /READ 1ST CRC + + KGOA /READ 2ND CRC + + + Write: KLSA /SELECT WRITE + TAD CHAR /1ST CHAR + KGOA /INIT WRITE, CHAR TO BUF, CLEAR DF + + : + TAD CHAR /LAST CHAR + KGOA /CHAR TO BUF, CLEAR DF + + KLSA /SELECT CRC MODE + KGOA /WRITE CRC, CLEAR DF + +*/ + +#include "pdp8_defs.h" +#include "sim_tape.h" + +#define CT_NUMDR 2 /* #drives */ +#define FNC u3 /* unit function */ +#define UST u4 /* unit status */ +#define CT_MAXFR (CT_SIZE) /* max record lnt */ +#define CT_SIZE 93000 /* chars/tape */ + +/* Status Register A */ + +#define SRA_ENAB 0200 /* enable */ +#define SRA_V_UNIT 6 /* unit */ +#define SRA_M_UNIT (CT_NUMDR - 1) +#define SRA_V_FNC 3 /* function */ +#define SRA_M_FNC 07 +#define SRA_READ 00 +#define SRA_REW 01 +#define SRA_WRITE 02 +#define SRA_SRF 03 +#define SRA_WFG 04 +#define SRA_SRB 05 +#define SRA_CRC 06 +#define SRA_SFF 07 +#define SRA_2ND 010 +#define SRA_IE 0001 /* int enable */ +#define GET_UNIT(x) (((x) >> SRA_V_UNIT) & SRA_M_UNIT) +#define GET_FNC(x) (((x) >> SRA_V_FNC) & SRA_M_FNC) + +/* Function code flags */ + +#define OP_WRI 01 /* op is a write */ +#define OP_REV 02 /* op is rev motion */ +#define OP_FWD 04 /* op is fwd motion */ + +/* Unit status flags */ + +#define UST_REV (OP_REV) /* last op was rev */ +#define UST_GAP 01 /* last op hit gap */ + +/* Status Register B, ^ = computed on the fly */ + +#define SRB_WLE 0400 /* "write lock err" */ +#define SRB_CRC 0200 /* CRC error */ +#define SRB_TIM 0100 /* timing error */ +#define SRB_BEOT 0040 /* ^BOT/EOT */ +#define SRB_EOF 0020 /* end of file */ +#define SRB_EMP 0010 /* ^drive empty */ +#define SRB_REW 0004 /* rewinding */ +#define SRB_WLK 0002 /* ^write locked */ +#define SRB_RDY 0001 /* ^ready */ +#define SRB_ALLERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_BEOT|SRB_EOF|SRB_EMP) +#define SRB_XFRERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_EOF) + +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; + +uint32 ct_sra = 0; /* status reg A */ +uint32 ct_srb = 0; /* status reg B */ +uint32 ct_db = 0; /* data buffer */ +uint32 ct_df = 0; /* data flag */ +uint32 ct_write = 0; /* TU60 write flag */ +uint32 ct_bptr = 0; /* buf ptr */ +uint32 ct_blnt = 0; /* buf length */ +int32 ct_stime = 1000; /* start time */ +int32 ct_ctime = 100; /* char latency */ +uint32 ct_stopioe = 1; /* stop on error */ +uint8 *ct_xb = NULL; /* transfer buffer */ +static uint8 ct_fnc_tab[SRA_M_FNC + 1] = { + OP_FWD, 0 , OP_WRI|OP_FWD, OP_REV, + OP_WRI|OP_FWD, OP_REV, 0, OP_FWD + }; + +int32 ct70 (int32 IR, int32 AC); +t_stat ct_svc (UNIT *uptr); +t_stat ct_reset (DEVICE *dptr); +t_stat ct_attach (UNIT *uptr, CONST char *cptr); +t_stat ct_detach (UNIT *uptr); +t_stat ct_boot (int32 unitno, DEVICE *dptr); +uint32 ct_updsta (UNIT *uptr); +int32 ct_go_start (int32 AC); +int32 ct_go_cont (UNIT *uptr, int32 AC); +t_stat ct_map_err (UNIT *uptr, t_stat st); +UNIT *ct_busy (void); +void ct_set_df (t_bool timchk); +t_bool ct_read_char (void); +uint32 ct_crc (uint8 *buf, uint32 cnt); + +/* CT data structures + + ct_dev CT device descriptor + ct_unit CT unit list + ct_reg CT register list + ct_mod CT modifier list +*/ + +DIB ct_dib = { DEV_CT, 1, { &ct70 } }; + +UNIT ct_unit[] = { + { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) }, + { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) }, + }; + +REG ct_reg[] = { + { ORDATAD (CTSRA, ct_sra, 8, "status register A") }, + { ORDATAD (CTSRB, ct_srb, 8, "status register B") }, + { ORDATAD (CTDB, ct_db, 8, "data buffer") }, + { FLDATAD (CTDF, ct_df, 0, "data flag") }, + { FLDATAD (RDY, ct_srb, 0, "ready flag") }, + { FLDATAD (WLE, ct_srb, 8, "write lock error") }, + { FLDATAD (WRITE, ct_write, 0, "TA60 write operation flag") }, + { FLDATAD (INT, int_req, INT_V_CT, "interrupt request") }, + { DRDATAD (BPTR, ct_bptr, 17, "buffer pointer") }, + { DRDATAD (BLNT, ct_blnt, 17, "buffer length") }, + { DRDATAD (STIME, ct_stime, 24, "operation start time"), PV_LEFT + REG_NZ }, + { DRDATAD (CTIME, ct_ctime, 24, "character latency"), PV_LEFT + REG_NZ }, + { FLDATAD (STOP_IOE, ct_stopioe, 0, "stop on I/O errors flag") }, + { URDATA (UFNC, ct_unit[0].FNC, 8, 4, 0, CT_NUMDR, REG_HRO) }, + { URDATA (UST, ct_unit[0].UST, 8, 2, 0, CT_NUMDR, REG_HRO) }, + { URDATAD (POS, ct_unit[0].pos, 10, T_ADDR_W, 0, + CT_NUMDR, PV_LEFT | REG_RO, "position, units 0-1") }, + { FLDATA (DEVNUM, ct_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB ct_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, +// { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", +// &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL, + NULL, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE ct_dev = { + "CT", ct_unit, ct_reg, ct_mod, + CT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &ct_reset, + &ct_boot, &ct_attach, &ct_detach, + &ct_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_TAPE + }; + +/* IOT routines */ + +int32 ct70 (int32 IR, int32 AC) +{ +int32 srb; +UNIT *uptr; + +srb = ct_updsta (NULL); /* update status */ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* KCLR */ + ct_reset (&ct_dev); /* reset the world */ + break; + + case 1: /* KSDR */ + if (ct_df) + AC |= IOT_SKP; + break; + + case 2: /* KSEN */ + if (srb & SRB_ALLERR) + AC |= IOT_SKP; + break; + + case 3: /* KSBF */ + if ((srb & SRB_RDY) && !(srb & SRB_EMP)) + AC |= IOT_SKP; + break; + + case 4: /* KLSA */ + ct_sra = AC & 0377; + ct_updsta (NULL); + return ct_sra ^ 0377; + + case 5: /* KSAF */ + if (ct_df || (srb & (SRB_ALLERR|SRB_RDY))) + AC |= IOT_SKP; + break; + + case 6: /* KGOA */ + ct_df = 0; /* clear data flag */ + if ((uptr = ct_busy ())) /* op in progress? */ + AC = ct_go_cont (uptr, AC); /* yes */ + else AC = ct_go_start (AC); /* no, start */ + ct_updsta (NULL); + break; + + case 7: /* KSRB */ + return srb & 0377; + } /* end switch */ + +return AC; +} + +/* Start a new operation - cassette is not busy */ + +int32 ct_go_start (int32 AC) +{ +UNIT *uptr = ct_dev.units + GET_UNIT (ct_sra); +uint32 fnc = GET_FNC (ct_sra); +uint32 flg = ct_fnc_tab[fnc]; +uint32 old_ust = uptr->UST; + +if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, + ">>CT start: op=%o, old_sta = %o, pos=%d\n", + fnc, uptr->UST, uptr->pos); +if ((ct_sra & SRA_ENAB) && (uptr->flags & UNIT_ATT)) { /* enabled, att? */ + ct_srb &= ~(SRB_XFRERR|SRB_REW); /* clear err, rew */ + if (flg & OP_WRI) { /* write-type op? */ + if (sim_tape_wrp (uptr)) { /* locked? */ + ct_srb |= SRB_WLE; /* set flag, abort */ + return AC; + } + ct_write = 1; /* set TU60 wr flag */ + ct_db = AC & 0377; + } + else { + ct_write = 0; + ct_db = 0; + } + ct_srb &= ~SRB_BEOT; /* tape in motion */ + if (fnc == SRA_REW) /* rew? set flag */ + ct_srb |= SRB_REW; + if ((fnc != SRA_REW) && !(flg & OP_WRI)) { /* read cmd? */ + t_mtrlnt t; + t_stat st; + uptr->UST = flg & UST_REV; /* save direction */ + if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */ + st = sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); /* skip file gap */ + if (st != MTSE_TMK) /* not there? */ + sim_tape_rewind (uptr); /* restore tap pos */ + else old_ust = 0; /* defang next */ + } + if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* rev in gap? */ + if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, + ">>CT skip gap: op=%o, old_sta = %o, pos=%d\n", + fnc, uptr->UST, uptr->pos); + if (uptr->UST) /* skip file gap */ + sim_tape_rdrecr (uptr, ct_xb, &t, CT_MAXFR); + else sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); + } + } + else uptr->UST = 0; + ct_bptr = 0; /* init buffer */ + ct_blnt = 0; + uptr->FNC = fnc; /* save function */ + sim_activate (uptr, ct_stime); /* schedule op */ + } +if ((fnc == SRA_READ) || (fnc == SRA_CRC)) /* read or CRC? */ + return 0; /* get "char" */ +return AC; +} + +/* Continue an in-progress operation - cassette is in motion */ + +int32 ct_go_cont (UNIT *uptr, int32 AC) +{ +int32 fnc = GET_FNC (ct_sra); + +switch (fnc) { /* case on function */ + + case SRA_READ: /* read */ + return ct_db; /* return data */ + + case SRA_WRITE: /* write */ + ct_db = AC & 0377; /* save data */ + break; + + case SRA_CRC: /* CRC */ + if ((uptr->FNC & SRA_M_FNC) != SRA_CRC) /* if not CRC */ + uptr->FNC = SRA_CRC; /* start CRC seq */ + if (!ct_write) /* read? AC <- buf */ + return ct_db; + break; + + default: + break; + } + +return AC; +} + +/* Unit service */ + +t_stat ct_svc (UNIT *uptr) +{ +uint32 i, crc; +uint32 flgs = ct_fnc_tab[uptr->FNC & SRA_M_FNC]; +t_mtrlnt tbc; +t_stat st, r; + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + ct_updsta (uptr); /* update status */ + return (ct_stopioe? SCPE_UNATT: SCPE_OK); + } +if (((flgs & OP_REV) && sim_tape_bot (uptr)) || /* rev at BOT or */ + ((flgs & OP_FWD) && sim_tape_eot (uptr))) { /* fwd at EOT? */ + ct_srb |= SRB_BEOT; /* error */ + ct_updsta (uptr); /* op done */ + return SCPE_OK; + } + +r = SCPE_OK; +switch (uptr->FNC) { /* case on function */ + + case SRA_READ: /* read start */ + st = sim_tape_rdrecf (uptr, ct_xb, &ct_blnt, CT_MAXFR); /* get rec */ + if (st == MTSE_RECE) /* rec in err? */ + ct_srb |= SRB_CRC; + else if (st != MTSE_OK) { /* other error? */ + r = ct_map_err (uptr, st); /* map error */ + break; + } + crc = ct_crc (ct_xb, ct_blnt); /* calculate CRC */ + ct_xb[ct_blnt++] = (crc >> 8) & 0377; /* append to buffer */ + ct_xb[ct_blnt++] = crc & 0377; + uptr->FNC |= SRA_2ND; /* next state */ + sim_activate (uptr, ct_ctime); /* sched next char */ + return SCPE_OK; + + case SRA_READ|SRA_2ND: /* read char */ + if (!ct_read_char ()) /* read, overrun? */ + break; + ct_set_df (TRUE); /* set data flag */ + sim_activate (uptr, ct_ctime); /* sched next char */ + return SCPE_OK; + + case SRA_WRITE: /* write start */ + for (i = 0; i < CT_MAXFR; i++) /* clear buffer */ + ct_xb[i] = 0; + uptr->FNC |= SRA_2ND; /* next state */ + sim_activate (uptr, ct_ctime); /* sched next char */ + return SCPE_OK; + + case SRA_WRITE|SRA_2ND: /* write char */ + if ((ct_bptr < CT_MAXFR) && /* room in buf? */ + ((uptr->pos + ct_bptr) < uptr->capac)) /* room on tape? */ + ct_xb[ct_bptr++] = ct_db; /* store char */ + ct_set_df (TRUE); /* set data flag */ + sim_activate (uptr, ct_ctime); /* sched next char */ + return SCPE_OK; + + case SRA_CRC: /* CRC */ + if (ct_write) { /* write? */ + if ((st = sim_tape_wrrecf (uptr, ct_xb, ct_bptr)))/* write, err? */ + r = ct_map_err (uptr, st); /* map error */ + break; /* write done */ + } + ct_read_char (); /* get second CRC */ + ct_set_df (FALSE); /* set df */ + uptr->FNC |= SRA_2ND; /* next state */ + sim_activate (uptr, ct_ctime); + return SCPE_OK; + + case SRA_CRC|SRA_2ND: /* second read CRC */ + if (ct_bptr != ct_blnt) { /* partial read? */ + crc = ct_crc (ct_xb, ct_bptr); /* actual CRC */ + if (crc != 0) /* must be zero */ + ct_srb |= SRB_CRC; + } + break; /* read done */ + + case SRA_WFG: /* write file gap */ + if ((st = sim_tape_wrtmk (uptr))) /* write tmk, err? */ + r = ct_map_err (uptr, st); /* map error */ + break; + + case SRA_REW: /* rewind */ + sim_tape_rewind (uptr); + ct_srb |= SRB_BEOT; /* set BOT */ + break; + + case SRA_SRB: /* space rev blk */ + if ((st = sim_tape_sprecr (uptr, &tbc))) /* space rev, err? */ + r = ct_map_err (uptr, st); /* map error */ + break; + + case SRA_SRF: /* space rev file */ + while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; + r = ct_map_err (uptr, st); /* map error */ + break; + + case SRA_SFF: /* space fwd file */ + while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; + r = ct_map_err (uptr, st); /* map error */ + break; + + default: /* never get here! */ + return SCPE_IERR; + } /* end case */ + +ct_updsta (uptr); /* update status */ +if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, + ">>CT done: op=%o, statusA = %o, statusB = %o, pos=%d\n", + uptr->FNC, ct_sra, ct_srb, uptr->pos); +return r; +} + +/* Update controller status */ + +uint32 ct_updsta (UNIT *uptr) +{ +int32 srb; + +if (uptr == NULL) { /* unit specified? */ + uptr = ct_busy (); /* use busy unit */ + if ((uptr == NULL) && (ct_sra & SRA_ENAB)) /* none busy? */ + uptr = ct_dev.units + GET_UNIT (ct_sra); /* use sel unit */ + } +else if (ct_srb & SRB_EOF) /* save gap */ + uptr->UST |= UST_GAP; +if (uptr) { /* any unit? */ + ct_srb &= ~(SRB_WLK|SRB_EMP|SRB_RDY); /* clear dyn flags */ + if ((uptr->flags & UNIT_ATT) == 0) /* unattached? */ + ct_srb = (ct_srb | SRB_EMP|SRB_WLK) & ~SRB_REW; /* empty, locked */ + if (!sim_is_active (uptr)) { /* not busy? */ + ct_srb = (ct_srb | SRB_RDY) & ~SRB_REW; /* ready, ~rew */ + } + if (sim_tape_wrp (uptr) || (ct_srb & SRB_REW)) /* locked or rew? */ + ct_srb |= SRB_WLK; /* set locked */ + } +if (ct_sra & SRA_ENAB) /* can TA see TU60? */ + srb = ct_srb; +else srb = 0; /* no */ +if ((ct_sra & SRA_IE) && /* int enabled? */ + (ct_df || (srb & (SRB_ALLERR|SRB_RDY)))) /* any flag? */ + int_req |= INT_CT; /* set int req */ +else int_req &= ~INT_CT; /* no, clr int req */ +return srb; +} + +/* Set data flag */ + +void ct_set_df (t_bool timchk) +{ +if (ct_df && timchk) /* flag still set? */ + ct_srb |= SRB_TIM; +ct_df = 1; /* set data flag */ +if (ct_sra & SRA_IE) /* if ie, int req */ + int_req |= INT_CT; +return; +} + +/* Read character */ + +t_bool ct_read_char (void) +{ +if (ct_bptr < ct_blnt) { /* more chars? */ + ct_db = ct_xb[ct_bptr++]; + return TRUE; + } +ct_db = 0; +ct_srb |= SRB_CRC; /* overrun */ +return FALSE; +} + +/* Test if controller busy */ + +UNIT *ct_busy (void) +{ +uint32 u; +UNIT *uptr; + +for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */ + uptr = ct_dev.units + u; + if (sim_is_active (uptr)) + return uptr; + } +return NULL; +} + +/* Calculate CRC on buffer */ + +uint32 ct_crc (uint8 *buf, uint32 cnt) +{ +uint32 crc, i, j; + +crc = 0; +for (i = 0; i < cnt; i++) { + crc = crc ^ (((uint32) buf[i]) << 8); + for (j = 0; j < 8; j++) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else crc = crc >> 1; + } + } +return crc; +} + +/* Map error status */ + +t_stat ct_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* unattached */ + ct_srb |= SRB_CRC; + case MTSE_OK: /* no error */ + return SCPE_IERR; /* never get here! */ + + case MTSE_TMK: /* end of file */ + ct_srb |= SRB_EOF; + break; + + case MTSE_IOERR: /* IO error */ + ct_srb |= SRB_CRC; /* set crc err */ + if (ct_stopioe) + return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + ct_srb |= SRB_CRC; /* set crc err */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + case MTSE_EOM: /* end of medium */ + ct_srb |= SRB_CRC; /* set crc err */ + break; + + case MTSE_BOT: /* reverse into BOT */ + ct_srb |= SRB_BEOT; /* set BOT */ + break; + + case MTSE_WRP: /* write protect */ + ct_srb |= SRB_WLE; /* set wlk err */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ct_reset (DEVICE *dptr) +{ +uint32 u; +UNIT *uptr; + +ct_sra = 0; +ct_srb = 0; +ct_df = 0; +ct_db = 0; +ct_write = 0; +ct_bptr = 0; +ct_blnt = 0; +int_req = int_req & ~INT_CT; /* clear interrupt */ +for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */ + uptr = ct_dev.units + u; + sim_cancel (uptr); /* cancel activity */ + sim_tape_reset (uptr); /* reset tape */ + } +if (ct_xb == NULL) + ct_xb = (uint8 *) calloc (CT_MAXFR + 2, sizeof (uint8)); +if (ct_xb == NULL) + return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat ct_attach (UNIT *uptr, CONST char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) + return r; +ct_updsta (NULL); +uptr->UST = 0; +return r; +} + +/* Detach routine */ + +t_stat ct_detach (UNIT* uptr) +{ +t_stat r; + +if (!(uptr->flags & UNIT_ATT)) /* check attached */ + return SCPE_OK; +r = sim_tape_detach (uptr); +ct_updsta (NULL); +uptr->UST = 0; +return r; +} + +/* Bootstrap routine */ + +#define BOOT_START 04000 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 01237, /* BOOT, TAD M50 /change CRC to REW */ + 01206, /* CRCCHK, TAD L260 /crc op */ + 06704, /* KLSA /load op */ + 06706, /* KGOA /start */ + 06703, /* KSBF /ready? */ + 05204, /* RDCOD, JMP .-1 /loop */ + 07264, /* L260, CML STA RAL /L = 1, AC = halt */ + 06702, /* KSEN /error? */ + 07610, /* SKP CLA /halt on any error */ + 03211, /* DCA . /except REW or FFG */ + 03636, /* DCA I PTR /TAD I PTR mustn't change L */ + 01205, /* TAD RDCOD /read op */ + 06704, /* KLSA /load op */ + 06706, /* KGOA /start */ + 06701, /* LOOP, KSDF /data ready? */ + 05216, /* JMP .-1 /loop */ + 07002, /* BSW /to upper 6b */ + 07430, /* SZL /second byte? */ + 01636, /* TAD I PTR /yes */ + 07022, /* CML BSW /swap back */ + 03636, /* DCA I PTR /store in mem */ + 07420, /* SNL /done with both bytes? */ + 02236, /* ISZ PTR /yes, bump mem ptr */ + 02235, /* ISZ KNT /done with record? */ + 05215, /* JMP LOOP /next byte */ + 07346, /* STA CLL RTL */ + 07002, /* BSW /AC = 7757 */ + 03235, /* STA KNT /now read 200 byte record */ + 05201, /* JMP CRCCHK /go check CRC */ + 07737, /* KNT, 7737 /1's compl of byte count */ + 03557, /* PTR, 3557 /load point */ + 07730, /* M50, 7730 /CLA SPA SZL */ + }; + +t_stat ct_boot (int32 unitno, DEVICE *dptr) +{ +size_t i; +extern uint16 M[]; + +if ((ct_dib.dev != DEV_CT) || unitno) /* only std devno */ + return STOP_NOTSTD; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = boot_rom[i]; +cpu_set_bootpc (BOOT_START); +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_defs.h Index: src/SIMH/PDP8/pdp8_defs.h ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_defs.h @@ -0,0 +1,258 @@ +/* pdp8_defs.h: PDP-8 simulator definitions + + Copyright (c) 1993-2016, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 18-Sep-16 RMS Added support for 16 additional terminals + 18-Sep-13 RMS Added set_bootpc prototype + 18-Apr-12 RMS Removed separate timer for additional terminals; + Added clock_cosched prototype + 22-May-10 RMS Added check for 64b definitions + 21-Aug-07 RMS Added FPP8 support + 13-Dec-06 RMS Added TA8E support + 30-Oct-06 RMS Added infinite loop stop + 13-Oct-03 RMS Added TSC8-75 support + 04-Oct-02 RMS Added variable device number support + 20-Jan-02 RMS Fixed bug in TTx interrupt enable initialization + 25-Nov-01 RMS Added RL8A support + 16-Sep-01 RMS Added multiple KL support + 18-Mar-01 RMS Added DF32 support + 15-Feb-01 RMS Added DECtape support + 14-Apr-99 RMS Changed t_addr to unsigned + 19-Mar-95 RMS Added dynamic memory size + 02-May-94 RMS Added non-existent memory handling + + The author gratefully acknowledges the help of Max Burnet, Richie Lary, + and Bill Haygood in resolving questions about the PDP-8 +*/ + +#ifndef PDP8_DEFS_H_ +#define PDP8_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +#if defined(USE_INT64) || defined(USE_ADDR64) +#error "PDP-8 does not support 64b values!" +#endif + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_OPBKPT 4 /* Opcode/Instruction breakpoint */ +#define STOP_NOTSTD 5 /* non-std devno */ +#define STOP_DTOFF 6 /* DECtape off reel */ +#define STOP_LOOP 7 /* infinite loop */ + +/* Memory */ + +#define MAXMEMSIZE 32768 /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* IOT subroutine return codes */ + +#define IOT_V_SKP 12 /* skip */ +#define IOT_V_REASON 13 /* reason */ +#define IOT_SKP (1 << IOT_V_SKP) +#define IOT_REASON (1 << IOT_V_REASON) +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* Timers */ + +#define TMR_CLK 0 /* timer 0 = clock */ + +/* Device information block */ + +#define DEV_MAXBLK 8 /* max dev block */ +#define DEV_MAX 64 /* total devices */ + +typedef struct { + uint32 dev; /* device number */ + int32 (*dsp)(int32 IR, int32 dat); /* dispatch */ + } DIB_DSP; + +typedef struct { + uint32 dev; /* base dev number */ + uint32 num; /* number of slots */ + int32 (*dsp[DEV_MAXBLK])(int32 IR, int32 dat); + DIB_DSP *dsp_tbl; /* optional table */ + } DIB; + +/* Standard device numbers */ + +#define DEV_PTR 001 /* paper tape reader */ +#define DEV_PTP 002 /* paper tape punch */ +#define DEV_TTI 003 /* console input */ +#define DEV_TTO 004 /* console output */ +#define DEV_CLK 013 /* clock */ +#define DEV_TSC 036 +#define DEV_KJ8 040 /* extra terminals */ +#define DEV_FPP 055 /* floating point */ +#define DEV_DF 060 /* DF32 */ +#define DEV_RF 060 /* RF08 */ +#define DEV_RL 060 /* RL8A */ +#define DEV_LPT 066 /* line printer */ +#define DEV_MT 070 /* TM8E */ +#define DEV_CT 070 /* TA8E */ +#define DEV_RK 074 /* RK8E */ +#define DEV_RX 075 /* RX8E/RX28 */ +#define DEV_DTA 076 /* TC08 */ +#define DEV_TD8E 077 /* TD8E */ + +/* Extra PTO8/KL8JA devices */ + +#define DEV_TTI1 040 +#define DEV_TTO1 041 +#define DEV_TTI2 042 +#define DEV_TTO2 043 +#define DEV_TTI3 044 +#define DEV_TTO3 045 +#define DEV_TTI4 046 +#define DEV_TTO4 047 +#define DEV_TTI5 034 +#define DEV_TTO5 035 +#define DEV_TTI6 011 +#define DEV_TTO6 012 +#define DEV_TTI7 030 +#define DEV_TTO7 031 +#define DEV_TTI8 032 +#define DEV_TTO8 033 +#define DEV_TTI9 050 +#define DEV_TTO9 051 +#define DEV_TTI10 052 +#define DEV_TTO10 053 +#define DEV_TTI11 054 +#define DEV_TTO11 055 /* conflict: FPP */ +#define DEV_TTI12 056 /* conflict: FPP */ +#define DEV_TTO12 057 +#define DEV_TTI13 070 /* conflict: CT, MT */ +#define DEV_TTO13 071 +#define DEV_TTI14 036 /* conflict: TSC */ +#define DEV_TTO14 037 +#define DEV_TTI15 072 +#define DEV_TTO15 073 +#define DEV_TTI16 006 +#define DEV_TTO16 007 + +/* Interrupt flags + + The interrupt flags consist of three groups: + + 1. Devices with individual interrupt enables. These record + their interrupt requests in device_done and their enables + in device_enable, and must occupy the low bit positions. + + 2. Devices without interrupt enables. These record their + interrupt requests directly in int_req, and must occupy + the middle bit positions. + + 3. Overhead. These exist only in int_req and must occupy the + high bit positions. + + Because the PDP-8 does not have priority interrupts, the order + of devices within groups does not matter. + + Note: all extra KL input and output interrupts must be assigned + to contiguous bits. +*/ + +#define INT_V_START 0 /* enable start */ +#define INT_V_LPT (INT_V_START+0) /* line printer */ +#define INT_V_PTP (INT_V_START+1) /* tape punch */ +#define INT_V_PTR (INT_V_START+2) /* tape reader */ +#define INT_V_TTO (INT_V_START+3) /* terminal */ +#define INT_V_TTI (INT_V_START+4) /* keyboard */ +#define INT_V_CLK (INT_V_START+5) /* clock */ +#define INT_V_TTO1 (INT_V_START+6) /* tto1 */ +//#define INT_V_TTO2 (INT_V_START+7) /* tto2 */ +//#define INT_V_TTO3 (INT_V_START+8) /* tto3 */ +//#define INT_V_TTO4 (INT_V_START+9) /* tto4 */ +#define INT_V_TTI1 (INT_V_START+10) /* tti1 */ +//#define INT_V_TTI2 (INT_V_START+11) /* tti2 */ +//#define INT_V_TTI3 (INT_V_START+12) /* tti3 */ +//#define INT_V_TTI4 (INT_V_START+13) /* tti4 */ +#define INT_V_DIRECT (INT_V_START+14) /* direct start */ +#define INT_V_RX (INT_V_DIRECT+0) /* RX8E */ +#define INT_V_RK (INT_V_DIRECT+1) /* RK8E */ +#define INT_V_RF (INT_V_DIRECT+2) /* RF08 */ +#define INT_V_DF (INT_V_DIRECT+3) /* DF32 */ +#define INT_V_MT (INT_V_DIRECT+4) /* TM8E */ +#define INT_V_DTA (INT_V_DIRECT+5) /* TC08 */ +#define INT_V_RL (INT_V_DIRECT+6) /* RL8A */ +#define INT_V_CT (INT_V_DIRECT+7) /* TA8E int */ +#define INT_V_PWR (INT_V_DIRECT+8) /* power int */ +#define INT_V_UF (INT_V_DIRECT+9) /* user int */ +#define INT_V_TSC (INT_V_DIRECT+10) /* TSC8-75 int */ +#define INT_V_FPP (INT_V_DIRECT+11) /* FPP8 */ +#define INT_V_OVHD (INT_V_DIRECT+12) /* overhead start */ +#define INT_V_NO_ION_PENDING (INT_V_OVHD+0) /* ion pending */ +#define INT_V_NO_CIF_PENDING (INT_V_OVHD+1) /* cif pending */ +#define INT_V_ION (INT_V_OVHD+2) /* interrupts on */ + +#define INT_LPT (1 << INT_V_LPT) +#define INT_PTP (1 << INT_V_PTP) +#define INT_PTR (1 << INT_V_PTR) +#define INT_TTO (1 << INT_V_TTO) +#define INT_TTI (1 << INT_V_TTI) +#define INT_CLK (1 << INT_V_CLK) +#define INT_TTO1 (1 << INT_V_TTO1) +//#define INT_TTO2 (1 << INT_V_TTO2) +//#define INT_TTO3 (1 << INT_V_TTO3) +//#define INT_TTO4 (1 << INT_V_TTO4) +#define INT_TTI1 (1 << INT_V_TTI1) +//#define INT_TTI2 (1 << INT_V_TTI2) +//#define INT_TTI3 (1 << INT_V_TTI3) +//#define INT_TTI4 (1 << INT_V_TTI4) +#define INT_RX (1 << INT_V_RX) +#define INT_RK (1 << INT_V_RK) +#define INT_RF (1 << INT_V_RF) +#define INT_DF (1 << INT_V_DF) +#define INT_MT (1 << INT_V_MT) +#define INT_DTA (1 << INT_V_DTA) +#define INT_RL (1 << INT_V_RL) +#define INT_CT (1 << INT_V_CT) +#define INT_PWR (1 << INT_V_PWR) +#define INT_UF (1 << INT_V_UF) +#define INT_TSC (1 << INT_V_TSC) +#define INT_FPP (1 << INT_V_FPP) +#define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING) +#define INT_NO_CIF_PENDING (1 << INT_V_NO_CIF_PENDING) +#define INT_ION (1 << INT_V_ION) +#define INT_DEV_ENABLE ((1 << INT_V_DIRECT) - 1) /* devices w/enables */ +#define INT_ALL ((1 << INT_V_OVHD) - 1) /* all interrupts */ +#define INT_INIT_ENABLE (INT_TTI+INT_TTO+INT_PTR+INT_PTP+INT_LPT) | \ + (INT_TTI1+INT_TTO1) +#define INT_PENDING (INT_ION+INT_NO_CIF_PENDING+INT_NO_ION_PENDING) +#define INT_UPDATE ((int_req & ~INT_DEV_ENABLE) | (dev_done & int_enable)) + +/* Function prototypes */ + +t_stat set_dev (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat show_dev (FILE *st, UNIT *uptr, int32 val, CONST void *desc); + +void cpu_set_bootpc (int32 pc); + +#endif ADDED src/SIMH/PDP8/pdp8_df.c Index: src/SIMH/PDP8/pdp8_df.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_df.c @@ -0,0 +1,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 + +#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 + pulse 4 = df_sta = df_sta | AC + AC = AC | old_df_sta + 6616 pulse 2 = clear AC, skip if address confirmed + pulse 4 = df_sta = df_sta | AC = 0 (nop) + AC = AC | old_df_sta +*/ + +int32 df61 (int32 IR, int32 AC) +{ +int32 old_df_sta = df_sta; +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) /* DCEA */ + df_sta = df_sta & ~(DFS_DEX | DFS_MEX); /* clear dex, mex */ +if (pulse & 2) /* DSAC */ + AC = ((df_da & DF_WMASK) == GET_POS (df_time))? IOT_SKP: 0; +if (pulse & 4) { + df_sta = df_sta | (AC & (DFS_DEX | DFS_MEX)); /* DEAL */ + AC = AC | old_df_sta; /* DEAC */ + } +return AC; +} + +int32 df62 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) { /* DFSE */ + if ((df_sta & DFS_ERR) == 0) + AC = AC | IOT_SKP; + } +if (pulse & 2) { /* DFSC */ + if (pulse & 4) /* for DMAC */ + AC = AC & ~07777; + else if (df_done) + AC = AC | IOT_SKP; + } +if (pulse & 4) /* DMAC */ + AC = AC | df_da; +return AC; +} + +/* Unit service + + Note that for reads and writes, memory addresses wrap around in the + current field. This code assumes the entire disk is buffered. +*/ + +t_stat df_svc (UNIT *uptr) +{ +int32 pa, t, mex; +uint32 da; +int16 *fbuf = (int16 *) uptr->filebuf; + +UPDATE_PCELL; /* update photocell */ +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + df_done = 1; + int_req = int_req | INT_DF; /* update int req */ + return IORETURN (df_stopioe, SCPE_UNATT); + } + +mex = GET_MEX (df_sta); +da = GET_DEX (df_sta) | df_da; /* form disk addr */ +do { + if (da >= uptr->capac) { /* nx disk addr? */ + df_sta = df_sta | DFS_NXD; + break; + } + M[DF_WC] = (M[DF_WC] + 1) & 07777; /* incr word count */ + M[DF_MA] = (M[DF_MA] + 1) & 07777; /* incr mem addr */ + pa = mex | M[DF_MA]; /* add extension */ + if (uptr->FUNC == DF_READ) { /* read? */ + if (MEM_ADDR_OK (pa)) M[pa] = fbuf[da]; /* if !nxm, read wd */ + } + else { /* write */ + t = (da >> 14) & 07; /* check wr lock */ + if ((df_wlk >> t) & 1) /* locked? set err */ + df_sta = df_sta | DFS_WLS; + else { /* not locked */ + fbuf[da] = M[pa]; /* write word */ + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + } + } + da = (da + 1) & 0377777; /* incr disk addr */ + } while ((M[DF_WC] != 0) && (df_burst != 0)); /* brk if wc, no brst */ + +if ((M[DF_WC] != 0) && ((df_sta & DFS_ERR) == 0)) /* more to do? */ + sim_activate (&df_unit, df_time); /* sched next */ +else { + if (uptr->FUNC != DF_READ) + da = (da - 1) & 0377777; + df_done = 1; /* done */ + int_req = int_req | INT_DF; /* update int req */ + } +df_sta = (df_sta & ~DFS_DEX) | ((da >> (12 - DFS_V_DEX)) & DFS_DEX); +df_da = da & 07777; /* separate disk addr */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat df_reset (DEVICE *dptr) +{ +df_sta = df_da = 0; +df_done = 1; +int_req = int_req & ~INT_DF; /* clear interrupt */ +sim_cancel (&df_unit); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define OS8_START 07750 +#define OS8_LEN (sizeof (os8_rom) / sizeof (int16)) +#define DM4_START 00200 +#define DM4_LEN (sizeof (dm4_rom) / sizeof (int16)) + +static const uint16 os8_rom[] = { + 07600, /* 7750, CLA CLL ; also word count */ + 06603, /* 7751, DMAR ; also address */ + 06622, /* 7752, DFSC ; done? */ + 05352, /* 7753, JMP .-1 ; no */ + 05752 /* 7754, JMP @.-2 ; enter boot */ + }; + +static const uint16 dm4_rom[] = { + 00200, 07600, /* 0200, CLA CLL */ + 00201, 06603, /* 0201, DMAR ; read */ + 00202, 06622, /* 0202, DFSC ; done? */ + 00203, 05202, /* 0203, JMP .-1 ; no */ + 00204, 05600, /* 0204, JMP @.-4 ; enter boot */ + 07750, 07576, /* 7750, 7576 ; word count */ + 07751, 07576 /* 7751, 7576 ; address */ + }; + +t_stat df_boot (int32 unitno, DEVICE *dptr) +{ +size_t i; + +if (sim_switches & SWMASK ('D')) { + for (i = 0; i < DM4_LEN; i = i + 2) + M[dm4_rom[i]] = dm4_rom[i + 1]; + cpu_set_bootpc (DM4_START); + } +else { + for (i = 0; i < OS8_LEN; i++) + M[OS8_START + i] = os8_rom[i]; + cpu_set_bootpc (OS8_START); + } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat df_attach (UNIT *uptr, CONST char *cptr) +{ +uint32 p, sz; +uint32 ds_bytes = DF_DKSIZE * sizeof (int16); + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + p = (sz + ds_bytes - 1) / ds_bytes; + if (p >= DF_NUMDK) + p = DF_NUMDK - 1; + uptr->flags = (uptr->flags & ~UNIT_PLAT) | + (p << UNIT_V_PLAT); + } +uptr->capac = UNIT_GETP (uptr->flags) * DF_DKSIZE; +return attach_unit (uptr, cptr); +} + +/* Change disk size */ + +t_stat df_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +if (val < 0) + return SCPE_IERR; +if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; +uptr->capac = UNIT_GETP (val) * DF_DKSIZE; +uptr->flags = uptr->flags & ~UNIT_AUTO; +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_dt.c Index: src/SIMH/PDP8/pdp8_dt.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_dt.c @@ -0,0 +1,1349 @@ +/* pdp8_dt.c: PDP-8 DECtape simulator + + Copyright (c) 1993-2017, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dt TC08/TU56 DECtape + + 15-Mar-17 RMS Fixed dt_seterr to clear successor states + 17-Sep-13 RMS Changed to use central set_bootpc routine + 23-Jun-06 RMS Fixed switch conflict in ATTACH + 07-Jan-06 RMS Fixed unaligned register access bug (Doug Carman) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 25-Jan-04 RMS Revised for device debug support + 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR + 18-Oct-03 RMS Fixed bugs in read all, tightened timing + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed sizing interaction with save/restore + 17-Oct-02 RMS Fixed bug in end of reel logic + 04-Oct-02 RMS Added DIB, device number support + 12-Sep-02 RMS Added support for 16b format + 30-May-02 RMS Widened POS to 32b + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Changed POS, STATT, LASTT, FLG to arrays + 29-Aug-01 RMS Added casts to PDP-18b packup routine + 17-Jul-01 RMS Moved function prototype + 11-May-01 RMS Fixed bug in reset + 25-Apr-01 RMS Added device enable/disable support + 18-Apr-01 RMS Changed to rewind tape before boot + 19-Mar-01 RMS Changed bootstrap to support 4k disk monitor + 15-Mar-01 RMS Added 129th word to PDP-8 format + + PDP-8 DECtapes are represented in memory by fixed length buffer of 16b words. + Three file formats are supported: + + 18b/36b 256 words per block [256 x 18b] + 16b 256 words per block [256 x 16b] + 12b 129 words per block [129 x 12b] + + When a 16b or 18/36bb DECtape file is read in, it is converted to 12b format. + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape (as + taken from the TD8E formatter) is: + + reverse end zone 8192 reverse end zone codes ~ 10 feet + reverse buffer 200 interblock codes + block 0 + : + block n + forward buffer 200 interblock codes + forward end zone 8192 forward end zone codes ~ 10 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of read all and write all. Read all assumes that the tape has been + conventionally written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 checksum (for reverse reads) + : + trailer word 4 checksum (for forward reads) + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write all writes only the data words and dumps the non-data words in the + bit bucket. +*/ + +#include "pdp8_defs.h" + +#define DT_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_11FMT (1 << UNIT_V_11FMT) +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ +#define WRITTEN u5 /* device buffer is dirty and needs flushing */ +#define DT_WC 07754 /* word count */ +#define DT_CA 07755 /* current addr */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* System independent DECtape constants */ + +#define DT_LPERMC 6 /* lines per mark track */ +#define DT_BLKWD 1 /* blk no word in h/t */ +#define DT_CSMWD 4 /* checksum word in h/t */ +#define DT_HTWRD 5 /* header/trailer words */ +#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ +#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ +#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ +#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ +#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 384 /* block size in 12b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ + +#define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE) +#define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32)) +#define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 129 /* block size in 12b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ +#define D8_FILSIZ (D8_CAPAC * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D8_CAPAC /* default */ +#define DT_WSIZE D8_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u)->pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* Status register A */ + +#define DTA_V_UNIT 9 /* unit select */ +#define DTA_M_UNIT 07 +#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) +#define DTA_V_MOT 7 /* motion */ +#define DTA_M_MOT 03 +#define DTA_V_MODE 6 /* mode */ +#define DTA_V_FNC 3 /* function */ +#define DTA_M_FNC 07 +#define FNC_MOVE 00 /* move */ +#define FNC_SRCH 01 /* search */ +#define FNC_READ 02 /* read */ +#define FNC_RALL 03 /* read all */ +#define FNC_WRIT 04 /* write */ +#define FNC_WALL 05 /* write all */ +#define FNC_WMRK 06 /* write timing */ +#define DTA_V_ENB 2 /* int enable */ +#define DTA_V_CERF 1 /* clr error flag */ +#define DTA_V_CDTF 0 /* clr DECtape flag */ +#define DTA_FWDRV (1u << (DTA_V_MOT + 1)) +#define DTA_STSTP (1u << DTA_V_MOT) +#define DTA_MODE (1u << DTA_V_MODE) +#define DTA_ENB (1u << DTA_V_ENB) +#define DTA_CERF (1u << DTA_V_CERF) +#define DTA_CDTF (1u << DTA_V_CDTF) +#define DTA_RW (07777 & ~(DTA_CERF | DTA_CDTF)) + +#define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT) +#define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) +#define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) + +/* Status register B */ + +#define DTB_V_ERF 11 /* error flag */ +#define DTB_V_MRK 10 /* mark trk err */ +#define DTB_V_END 9 /* end zone err */ +#define DTB_V_SEL 8 /* select err */ +#define DTB_V_PAR 7 /* parity err */ +#define DTB_V_TIM 6 /* timing err */ +#define DTB_V_MEX 3 /* memory extension */ +#define DTB_M_MEX 07 +#define DTB_MEX (DTB_M_MEX << DTB_V_MEX) +#define DTB_V_DTF 0 /* DECtape flag */ +#define DTB_ERF (1u << DTB_V_ERF) +#define DTB_MRK (1u << DTB_V_MRK) +#define DTB_END (1u << DTB_V_END) +#define DTB_SEL (1u << DTB_V_SEL) +#define DTB_PAR (1u << DTB_V_PAR) +#define DTB_TIM (1u << DTB_V_TIM) +#define DTB_DTF (1u << DTB_V_DTF) +#define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \ + DTB_PAR | DTB_TIM) +#define DTB_GETMEX(x) (((x) & DTB_MEX) << (12 - DTB_V_MEX)) + +/* DECtape state */ + +#define DTS_V_MOT 3 /* motion */ +#define DTS_M_MOT 07 +#define DTS_STOP 0 /* stopped */ +#define DTS_DECF 2 /* decel, fwd */ +#define DTS_DECR 3 /* decel, rev */ +#define DTS_ACCF 4 /* accel, fwd */ +#define DTS_ACCR 5 /* accel, rev */ +#define DTS_ATSF 6 /* @speed, fwd */ +#define DTS_ATSR 7 /* @speed, rev */ +#define DTS_DIR 01 /* dir mask */ +#define DTS_V_FNC 0 /* function */ +#define DTS_M_FNC 07 +#define DTS_OFR 7 /* "off reel" */ +#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) +#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) +#define DTS_V_2ND 6 /* next state */ +#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ +#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) +#define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) +#define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ + ((DTS_STA (y, z)) << DTS_V_2ND) +#define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ + ((DTS_STA (y, z)) << DTS_V_3RD) +#define DTS_NXTSTA(x) (x >> DTS_V_2ND) + +/* Operation substates */ + +#define DTO_WCO 1 /* wc overflow */ +#define DTO_SOB 2 /* start of block */ + +/* Logging */ + +#define LOG_MS 001 /* move, search */ +#define LOG_RW 002 /* read, write */ +#define LOG_BL 004 /* block # lblk */ + +#define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \ + int_req = int_req | INT_DTA; \ + else int_req = int_req & ~INT_DTA; +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +extern uint16 M[]; +extern int32 int_req; +extern UNIT cpu_unit; + +int32 dtsa = 0; /* status A */ +int32 dtsb = 0; /* status B */ +int32 dt_ltime = 12; /* interline time */ +int32 dt_dctime = 40000; /* decel time */ +int32 dt_substate = 0; +int32 dt_logblk = 0; +int32 dt_stopoffr = 0; + +int32 dt76 (int32 IR, int32 AC); +int32 dt77 (int32 IR, int32 AC); +t_stat dt_svc (UNIT *uptr); +t_stat dt_reset (DEVICE *dptr); +t_stat dt_attach (UNIT *uptr, CONST char *cptr); +void dt_flush (UNIT *uptr); +t_stat dt_detach (UNIT *uptr); +t_stat dt_boot (int32 unitno, DEVICE *dptr); +void dt_deselect (int32 oldf); +void dt_newsa (int32 newf); +void dt_newfnc (UNIT *uptr, int32 newsta); +t_bool dt_setpos (UNIT *uptr); +void dt_schedez (UNIT *uptr, int32 dir); +void dt_seterr (UNIT *uptr, int32 e); +int32 dt_comobv (int32 val); +int32 dt_csum (UNIT *uptr, int32 blk); +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir); + +/* DT data structures + + dt_dev DT device descriptor + dt_unit DT unit list + dt_reg DT register list + dt_mod DT modifier list +*/ + +DIB dt_dib = { DEV_DTA, 2, { &dt76, &dt77 } }; + +UNIT dt_unit[] = { + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) } + }; + +REG dt_reg[] = { + { ORDATAD (DTSA, dtsa, 12, "status register A") }, + { ORDATAD (DTSB, dtsb, 12, "status register B") }, + { FLDATAD (INT, int_req, INT_V_DTA, "interrupt pending flag") }, + { FLDATAD (ENB, dtsa, DTA_V_ENB, "interrupt enable flag") }, + { FLDATAD (DTF, dtsb, DTB_V_DTF, "DECtape flag") }, + { FLDATAD (ERF, dtsb, DTB_V_ERF, "error flag") }, + { ORDATAD (WC, M[DT_WC], 12, "word count (memory location 7755)"), REG_FIT }, + { ORDATAD (CA, M[DT_CA], 12, "current address (memory location 7754)"), REG_FIT }, + { DRDATAD (LTIME, dt_ltime, 24, "time between lines"), REG_NZ | PV_LEFT }, + { DRDATAD (DCTIME, dt_dctime, 24, "time to decelerate to a full stop"), REG_NZ | PV_LEFT }, + { ORDATAD (SUBSTATE, dt_substate, 2, "read/write command substate") }, + { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, + { URDATAD (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, + DT_NUMDR, PV_LEFT | REG_RO, "position, in lines, units 0 to 7") }, + { URDATAD (STATT, dt_unit[0].STATE, 8, 18, 0, + DT_NUMDR, REG_RO, "unit state, units 0 to 7") }, + { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0, + DT_NUMDR, REG_HRO) }, + { FLDATAD (STOP_OFFR, dt_stopoffr, 0, "stop on off-reel error") }, + { ORDATA (DEVNUM, dt_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB dt_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEBTAB dt_deb[] = { + { "MOTION", LOG_MS }, + { "DATA", LOG_RW }, + { "BLOCK", LOG_BL }, + { NULL, 0 } + }; + +DEVICE dt_dev = { + "DT", dt_unit, dt_reg, dt_mod, + DT_NUMDR, 8, 24, 1, 8, 12, + NULL, NULL, &dt_reset, + &dt_boot, &dt_attach, &dt_detach, + &dt_dib, DEV_DISABLE | DEV_DEBUG, 0, + dt_deb, NULL, NULL + }; + +/* IOT routines */ + +int32 dt76 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; +int32 old_dtsa = dtsa, fnc; +UNIT *uptr; + +if (pulse & 01) /* DTRA */ + AC = AC | dtsa; +if (pulse & 06) { /* select */ + if (pulse & 02) /* DTCA */ + dtsa = 0; + if (pulse & 04) { /* DTXA */ + if ((AC & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR; + if ((AC & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF; + dtsa = dtsa ^ (AC & DTA_RW); + AC = 0; /* clr AC */ + } + if ((old_dtsa ^ dtsa) & DTA_UNIT) + dt_deselect (old_dtsa); + uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */ + fnc = DTA_GETFNC (dtsa); /* get fnc */ + if (((uptr->flags) & UNIT_DIS) || /* disabled? */ + (fnc >= FNC_WMRK) || /* write mark? */ + ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) || + ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT))) + dt_seterr (uptr, DTB_SEL); /* select err */ + else dt_newsa (dtsa); + DT_UPDINT; + } +return AC; +} + +int32 dt77 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +if ((pulse & 01) && (dtsb & (DTB_ERF |DTB_DTF))) /* DTSF */ + AC = IOT_SKP | AC; +if (pulse & 02) /* DTRB */ + AC = AC | dtsb; +if (pulse & 04) { /* DTLB */ + dtsb = (dtsb & ~DTB_MEX) | (AC & DTB_MEX); + AC = AC & ~07777; /* clear AC */ + } +return AC; +} + +/* Unit deselect */ + +void dt_deselect (int32 oldf) +{ +int32 old_unit = DTA_GETUNIT (oldf); +UNIT *uptr = dt_dev.units + old_unit; +int32 old_mot = DTS_GETMOT (uptr->STATE); + +if (old_mot >= DTS_ATSF) /* at speed? */ + dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); +else if (old_mot >= DTS_ACCF) /* accelerating? */ + DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); +return; +} + +/* Command register change + + 1. If change in motion, stop to start + - schedule acceleration + - set function as next state + 2. If change in motion, start to stop + - if not already decelerating (could be reversing), + schedule deceleration + 3. If change in direction, + - if not decelerating, schedule deceleration + - set accelerating (other dir) as next state + - set function as next next state + 4. If not accelerating or at speed, + - schedule acceleration + - set function as next state + 5. If not yet at speed, + - set function as next state + 6. If at speed, + - set function as current state, schedule function +*/ + +void dt_newsa (int32 newf) +{ +int32 new_unit, prev_mot, new_fnc; +int32 prev_mving, new_mving, prev_dir, new_dir; +UNIT *uptr; + +new_unit = DTA_GETUNIT (newf); /* new, old units */ +uptr = dt_dev.units + new_unit; +if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ + dt_seterr (uptr, DTB_SEL); /* no, error */ + return; + } +prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ +prev_mving = prev_mot != DTS_STOP; /* previous moving? */ +prev_dir = prev_mot & DTS_DIR; /* previous dir? */ +new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ +new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ +new_fnc = DTA_GETFNC (newf); /* new function? */ + +if ((prev_mving | new_mving) == 0) /* stop to stop */ + return; + +if (new_mving & ~prev_mving) { /* start? */ + if (dt_setpos (uptr)) /* update pos */ + return; + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* schedule acc */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_mving & ~new_mving) { /* stop? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) /* update pos */ + return; + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); /* schedule decel */ + } + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + return; + } + +if (prev_dir ^ new_dir) { /* dir chg? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) /* update pos */ + return; + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); /* schedule decel */ + } + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ + DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ + return; + } + +if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ + if (dt_setpos (uptr)) /* update pos */ + return; + sim_cancel (uptr); /* cancel cur */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_mot < DTS_ATSF) { /* not at speed? */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ +return; +} + +/* Schedule new DECtape function + + This routine is only called if + - the selected unit is attached + - the selected unit is at speed (forward or backward) + + This routine + - updates the selected unit's position + - updates the selected unit's state + - schedules the new operation +*/ + +void dt_newfnc (UNIT *uptr, int32 newsta) +{ +int32 fnc, dir, blk, unum, relpos, newpos; +uint32 oldpos; + +oldpos = uptr->pos; /* save old pos */ +if (dt_setpos (uptr)) /* update pos */ + return; +uptr->STATE = newsta; /* update state */ +fnc = DTS_GETFNC (uptr->STATE); /* set variables */ +dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; +unum = (int32) (uptr - dt_dev.units); +if (oldpos == uptr->pos) /* bump pos */ + uptr->pos = uptr->pos + (dir? -1: 1); +blk = DT_LIN2BL (uptr->pos, uptr); + +if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ + dt_seterr (uptr, DTB_END); /* set ez flag, stop */ + return; + } +sim_cancel (uptr); /* cancel cur op */ +dt_substate = DTO_SOB; /* substate = block start */ +switch (fnc) { /* case function */ + + case DTS_OFR: /* off reel */ + if (dir) /* rev? < start */ + newpos = -1000; + else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ + break; + + case FNC_MOVE: /* move */ + dt_schedez (uptr, dir); /* sched end zone */ + if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n", + unum, (dir? "backward": "forward")); + return; /* done */ + + case FNC_SRCH: /* search */ + if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? + DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; + else newpos = DT_BLK2LN ((DT_QREZ (uptr)? + 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); + if (DEBUG_PRI (dt_dev, LOG_MS)) + fprintf (sim_deb, ">>DT%d: searching %s]\n", + unum, (dir? "backward": "forward")); + break; + + case FNC_WRIT: /* write */ + case FNC_READ: /* read */ + case FNC_RALL: /* read all */ + case FNC_WALL: /* write all */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) + newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); + break; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dt_seterr (uptr, DTB_SEL); + return; + } + if (dir) + newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? + blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? + blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); + break; + + default: + dt_seterr (uptr, DTB_SEL); /* bad state */ + return; + } + +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/dt_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool dt_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 mot = DTS_GETMOT (uptr->STATE); +int32 unum, delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr->LASTT; /* elapsed time */ +if (ut == 0) /* no time gone? exit */ + return FALSE; +uptr->LASTT = new_time; /* update last time */ +switch (mot & ~DTS_DIR) { /* case on motion */ + + case DTS_STOP: /* stop */ + delta = 0; + break; + + case DTS_DECF: /* slowing */ + ulin = ut / (uint32) dt_ltime; + udelt = dt_dctime / dt_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; + + case DTS_ACCF: /* accelerating */ + ulin = ut / (uint32) dt_ltime; + udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; + + case DTS_ATSF: /* at speed */ + delta = ut / (uint32) dt_ltime; + break; + } + +if (mot & DTS_DIR) /* update pos */ + uptr->pos = uptr->pos - delta; +else uptr->pos = uptr->pos + delta; +if (((int32) uptr->pos < 0) || + ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { + detach_unit (uptr); /* off reel? */ + uptr->STATE = uptr->pos = 0; + unum = (int32) (uptr - dt_dev.units); + if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ + dt_seterr (uptr, DTB_SEL); /* error */ + return TRUE; + } +return FALSE; +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat dt_svc (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr->STATE); +int32 dir = mot & DTS_DIR; +int32 fnc = DTS_GETFNC (uptr->STATE); +int16 *fbuf = (int16 *) uptr->filebuf; +int32 unum = uptr - dt_dev.units; +int32 blk, wrd, ma, relpos, dat; +uint32 ba; + +/* Motion cases + + Decelerating - if next state != stopped, must be accel reverse + Accelerating - next state must be @speed, schedule function + At speed - do functional processing +*/ + +switch (mot) { + + case DTS_DECF: case DTS_DECR: /* decelerating */ + if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); + uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ + if (uptr->STATE) /* not stopped? */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* must be reversing */ + return SCPE_OK; + + case DTS_ACCF: case DTS_ACCR: /* accelerating */ + dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ + return SCPE_OK; + + case DTS_ATSF: case DTS_ATSR: /* at speed */ + break; /* check function */ + + default: /* other */ + dt_seterr (uptr, DTB_SEL); /* state error */ + return SCPE_OK; + } + +/* Functional cases + + Move - must be at end zone + Search - transfer block number, schedule next block + Off reel - detach unit (it must be deselected) +*/ + +if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); +if (DT_QEZ (uptr)) { /* in end zone? */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; + } +blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ +switch (fnc) { /* at speed, check fnc */ + + case FNC_MOVE: /* move */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; + + case FNC_SRCH: /* search */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr word cnt */ + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + if (MEM_ADDR_OK (ma)) /* store block # */ + M[ma] = blk & 07777; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + + case DTS_OFR: /* off reel */ + detach_unit (uptr); /* must be deselected */ + uptr->STATE = uptr->pos = 0; /* no visible action */ + break; + +/* Read has four subcases + + Start of block, not wc ovf - check that DTF is clear, otherwise normal + Normal - increment MA, WC, copy word from tape to memory + if read dir != write dir, bits must be scrambled + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - if end of block reached, timing error, + otherwise, continue to next word +*/ + + case FNC_READ: /* read */ + wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + + case DTO_SOB: /* start of block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + if (DEBUG_PRI (dt_dev, LOG_RW) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n", + unum, blk, (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous": " ")); + dt_substate = 0; + /* fall through */ + case 0: /* normal read */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = fbuf[ba]; /* get tape word */ + if (dir) /* rev? comp obv */ + dat = dt_comobv (dat); + if (MEM_ADDR_OK (ma)) /* mem addr legal? */ + M[ma] = dat; + if (M[DT_WC] == 0) /* wc ovf? */ + dt_substate = DTO_WCO; + /* fall through */ + case DTO_WCO: /* wc ovf, not sob */ + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { + dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + } + break; + + case DTO_WCO | DTO_SOB: /* next block */ + if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + else sim_activate (uptr, DT_WSIZE * dt_ltime); + break; + } + + break; + +/* Write has four subcases + + Start of block, not wc ovf - check that DTF is clear, set block direction + Normal - increment MA, WC, copy word from memory to tape + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + copy 0 to tape + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - schedule end zone +*/ + + case FNC_WRIT: /* write */ + wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + + case DTO_SOB: /* start block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + if (DEBUG_PRI (dt_dev, LOG_RW) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk, + (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous": " ")); + dt_substate = 0; + /* fall through */ + case 0: /* normal write */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + /* fall through */ + case DTO_WCO: /* wc ovflo */ + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = dt_substate? 0: M[ma]; /* get word */ + if (dir) /* rev? comp obv */ + dat = dt_comobv (dat); + fbuf[ba] = dat; /* write word */ + uptr->WRITTEN = TRUE; + if (ba >= uptr->hwmark) + uptr->hwmark = ba + 1; + if (M[DT_WC] == 0) + dt_substate = DTO_WCO; + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { + dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + } + break; + + case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; + } + + break; + +/* Read all has two subcases + + Not word count overflow - increment MA, WC, copy word from tape to memory + Word count overflow - schedule end zone +*/ + + case FNC_RALL: + switch (dt_substate) { /* case on substate */ + + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + dat = fbuf[ba]; /* get tape word */ + if (dir) /* rev? comp obv */ + dat = dt_comobv (dat); + } + else dat = dt_gethdr (uptr, blk, relpos, dir); /* get hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (MEM_ADDR_OK (ma)) /* mem addr legal? */ + M[ma] = dat; + if (M[DT_WC] == 0) + dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; + } /* end case substate */ + + break; + +/* Write all has two subcases + + Not word count overflow - increment MA, WC, copy word from memory to tape + Word count overflow - schedule end zone +*/ + + case FNC_WALL: + switch (dt_substate) { /* case on substate */ + + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dat = M[ma]; /* get mem word */ + if (dir) + dat = dt_comobv (dat); + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + fbuf[ba] = dat; /* write word */ + if (ba >= uptr->hwmark) + uptr->hwmark = ba + 1; + } + /* ignore hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (M[DT_WC] == 0) + dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; + } /* end case substate */ + break; + + default: + dt_seterr (uptr, DTB_SEL); /* impossible state */ + break; + } + +DT_UPDINT; /* update interrupts */ +return SCPE_OK; +} + +/* Reading the header is complicated, because 18b words are being parsed + out 12b at a time. The sequence of word numbers is directionally + sensitive + + Forward Reverse + Word Word Content Word Word Content + (abs) (rel) (abs) (rel) + + 137 8 fwd csm'00 6 6 rev csm'00 + 138 9 0000 5 5 0000 + 139 10 0000 4 4 0000 + 140 11 0000 3 3 0000 + 141 12 00'lo rev blk 2 2 00'lo fwd blk + 142 13 hi rev blk 1 1 hi fwd blk + 143 14 0000 0 0 0000 + 0 0 0000 143 14 0000 + 1 1 0000 142 13 0000 + 2 2 hi fwd blk 141 12 hi rev blk + 3 3 lo fwd blk'00 140 11 lo rev blk'00 + 4 4 0000 139 10 0000 + 5 5 0000 138 9 0000 + 6 6 0000 137 8 0000 + 7 7 rev csm 136 7 00'fwd csm +*/ + +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir) +{ +if (relpos >= DT_HTLIN) + relpos = relpos - (DT_WSIZE * DTU_BSIZE (uptr)); +if (dir) { /* reverse */ + switch (relpos / DT_WSIZE) { + case 6: /* rev csm */ + return 077; + case 2: /* lo fwd blk */ + return dt_comobv ((blk & 077) << 6); + case 1: /* hi fwd blk */ + return dt_comobv (blk >> 6); + case 12: /* hi rev blk */ + return (blk >> 6) & 07777; + case 11: /* lo rev blk */ + return ((blk & 077) << 6); + case 7: /* fwd csum */ + return (dt_comobv (dt_csum (uptr, blk)) << 6); + default: /* others */ + return 07777; + } + } +else { /* forward */ + switch (relpos / DT_WSIZE) { + case 8: /* fwd csum */ + return (dt_csum (uptr, blk) << 6); + case 12: /* lo rev blk */ + return dt_comobv ((blk & 077) << 6); + case 13: /* hi rev blk */ + return dt_comobv (blk >> 6); + case 2: /* hi fwd blk */ + return ((blk >> 6) & 07777); + case 3: /* lo fwd blk */ + return ((blk & 077) << 6); + case 7: /* rev csum */ + return 077; + default: /* others */ + break; + } + } +return 0; +} + +/* Utility routines */ + +/* Set error flag */ + +void dt_seterr (UNIT *uptr, int32 e) +{ +int32 mot = DTS_GETMOT (uptr->STATE); + +dtsa = dtsa & ~DTA_STSTP; /* clear go */ +dtsb = dtsb | DTB_ERF | e; /* set error flag */ +if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ + sim_cancel (uptr); /* cancel activity */ + if (dt_setpos (uptr)) /* update position */ + return; + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ + } +else DTS_SETSTA (mot, 0); /* clear 2nd, 3rd */ +DT_UPDINT; +return; +} + +/* Schedule end zone */ + +void dt_schedez (UNIT *uptr, int32 dir) +{ +int32 newpos; + +if (dir) /* rev? rev ez */ + newpos = DT_EZLIN - DT_WSIZE; +else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Complement obverse routine */ + +int32 dt_comobv (int32 dat) +{ +dat = dat ^ 07777; /* compl obverse */ +dat = ((dat >> 9) & 07) | ((dat >> 3) & 070) | + ((dat & 070) << 3) | ((dat & 07) << 9); +return dat; +} + +/* Checksum routine */ + +int32 dt_csum (UNIT *uptr, int32 blk) +{ +int16 *fbuf = (int16 *) uptr->filebuf; +int32 ba = blk * DTU_BSIZE (uptr); +int32 i, csum, wrd; + +csum = 077; /* init csum */ +for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ + wrd = fbuf[ba + i] ^ 07777; /* get ~word */ + csum = csum ^ (wrd >> 6) ^ wrd; + } +return (csum & 077); +} + +/* Reset routine */ + +t_stat dt_reset (DEVICE *dptr) +{ +int32 i, prev_mot; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ + uptr = dt_dev.units + i; + if (sim_is_running) { /* CAF? */ + prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ + if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ + if (dt_setpos (uptr)) /* update pos */ + continue; + sim_cancel (uptr); + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); + } + } + else { + sim_cancel (uptr); /* sim reset */ + uptr->STATE = 0; + uptr->LASTT = sim_grtime (); + } + } +dtsa = dtsb = 0; /* clear status */ +DT_UPDINT; /* reset interrupt */ +return SCPE_OK; +} + +/* Bootstrap routine + + This is actually the 4K disk monitor bootstrap, which also + works with OS/8. The reverse is not true - the OS/8 bootstrap + doesn't work with the disk monitor. +*/ + +#define BOOT_START 0200 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 07600, /* 200, CLA CLL */ + 01216, /* TAD MVB ; move back */ + 04210, /* JMS DO ; action */ + 01217, /* TAD K7577 ; addr */ + 03620, /* DCA I CA */ + 01222, /* TAD RDF ; read fwd */ + 04210, /* JMS DO ; action */ + 05600, /* JMP I 200 ; enter boot */ + 00000, /* DO, 0 */ + 06766, /* DTCA!DTXA ; start tape */ + 03621, /* DCA I WC ; clear wc */ + 06771, /* DTSF ; wait */ + 05213, /* JMP .-1 */ + 05610, /* JMP I DO */ + 00600, /* MVB, 0600 */ + 07577, /* K7577, 7757 */ + 07755, /* CA, 7755 */ + 07754, /* WC, 7754 */ + 00220 /* RF, 0220 */ + }; + +t_stat dt_boot (int32 unitno, DEVICE *dptr) +{ +size_t i; + +if (unitno) /* only unit 0 */ + return SCPE_ARG; +if (dt_dib.dev != DEV_DTA) /* only std devno */ + return STOP_NOTSTD; +dt_unit[unitno].pos = DT_EZLIN; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = boot_rom[i]; +cpu_set_bootpc (BOOT_START); +return SCPE_OK; +} + +/* Attach routine + + Determine 12b, 16b, or 18b/36b format + Allocate buffer + If 16b or 18b, read 16b or 18b format and convert to 12b in buffer + If 12b, read data into buffer +*/ + +t_stat dt_attach (UNIT *uptr, CONST char *cptr) +{ +uint32 pdp18b[D18_NBSIZE]; +uint16 pdp11b[D18_NBSIZE], *fbuf; +int32 i, k; +int32 u = uptr - dt_dev.units; +t_stat r; +uint32 ba, sz; + +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* fail? */ +if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ + uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; + if (sim_switches & SWMASK ('F')) /* att 18b? */ + uptr->flags = uptr->flags & ~UNIT_8FMT; + else if (sim_switches & SWMASK ('S')) /* att 16b? */ + uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; + else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ + (sz = sim_fsize (uptr->fileref))) { + if (sz == D11_FILSIZ) + uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; + else if (sz > D8_FILSIZ) + uptr->flags = uptr->flags & ~UNIT_8FMT; + } + } +uptr->capac = DTU_CAPAC (uptr); /* set capacity */ +uptr->filebuf = calloc (uptr->capac, sizeof (uint16)); +if (uptr->filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; + } +fbuf = (uint16 *) uptr->filebuf; /* file buffer */ +sim_printf ("%s%d: ", sim_dname (&dt_dev), u); +if (uptr->flags & UNIT_8FMT) + sim_printf ("12b format"); +else if (uptr->flags & UNIT_11FMT) + sim_printf ("16b format"); +else sim_printf ("18b/36b format"); +sim_printf (", buffering file in memory\n"); +uptr->io_flush = dt_flush; +if (uptr->flags & UNIT_8FMT) /* 12b? */ + uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16), + uptr->capac, uptr->fileref); +else { /* 16b/18b */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + if (uptr->flags & UNIT_11FMT) { + k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); + for (i = 0; i < k; i++) + pdp18b[i] = pdp11b[i]; + } + else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); + if (k == 0) + break; + for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0; + for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */ + fbuf[ba] = (pdp18b[k] >> 6) & 07777; + fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) | + ((pdp18b[k + 1] >> 12) & 077); + fbuf[ba + 2] = pdp18b[k + 1] & 07777; + ba = ba + 3; + } /* end blk loop */ + } /* end file loop */ + uptr->hwmark = ba; + } /* end else */ +uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ +uptr->pos = DT_EZLIN; /* beyond leader */ +uptr->LASTT = sim_grtime (); /* last pos update */ +return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If 12b, write buffer to file + If 16b or 18b, convert 12b buffer to 16b or 18b and write to file + Deallocate buffer +*/ +void dt_flush (UNIT* uptr) +{ +uint32 pdp18b[D18_NBSIZE]; +uint16 pdp11b[D18_NBSIZE], *fbuf; +int32 i, k; +uint32 ba; + +if (uptr->WRITTEN && uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ + rewind (uptr->fileref); /* start of file */ + fbuf = (uint16 *) uptr->filebuf; /* file buffer */ + if (uptr->flags & UNIT_8FMT) /* PDP8? */ + fxwrite (uptr->filebuf, sizeof (uint16), /* write file */ + uptr->hwmark, uptr->fileref); + else { /* 16b/18b */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */ + for (k = 0; k < D18_NBSIZE; k = k + 2) { + pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) | + ((uint32) (fbuf[ba + 1] >> 6) & 077); + pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) | + ((uint32) (fbuf[ba + 2] & 07777)); + ba = ba + 3; + } /* end loop blk */ + if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (i = 0; i < D18_NBSIZE; i++) + pdp11b[i] = pdp18b[i]; + fxwrite (pdp11b, sizeof (uint16), + D18_NBSIZE, uptr->fileref); + } + else fxwrite (pdp18b, sizeof (uint32), + D18_NBSIZE, uptr->fileref); + } /* end loop buf */ + } /* end else */ + if (ferror (uptr->fileref)) + sim_perror ("I/O error"); + } +uptr->WRITTEN = FALSE; /* no longer dirty */ +} + +t_stat dt_detach (UNIT* uptr) +{ +int u = (int)(uptr - dt_dev.units); + +if (!(uptr->flags & UNIT_ATT)) /* attached? */ + return SCPE_OK; +if (sim_is_active (uptr)) { + sim_cancel (uptr); + if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { + dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; + DT_UPDINT; + } + uptr->STATE = uptr->pos = 0; + } +if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ + sim_printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); + dt_flush (uptr); + } /* end if hwmark */ +free (uptr->filebuf); /* release buf */ +uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ +uptr->filebuf = NULL; /* clear buf ptr */ +uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */ +uptr->capac = DT_CAPAC; /* default size */ +return detach_unit (uptr); +} ADDED src/SIMH/PDP8/pdp8_fpp.c Index: src/SIMH/PDP8/pdp8_fpp.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_fpp.c @@ -0,0 +1,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. + 1. + 1.1...0 + + Note that 1.0...0 is normalized but considered illegal, since it cannot + be represented as a positive number. When a result is normalized, 1.0...0 + is converted to 1.1...0 with exp+1. +*/ + +#include "pdp8_defs.h" + +extern int32 int_req; +extern uint16 M[]; +extern int32 stop_inst; +extern UNIT cpu_unit; + +#define SEXT12(x) (((x) & 04000)? (x) | ~07777: (x) & 03777) + +/* Index registers are in memory */ + +#define fpp_read_xr(xr) fpp_read (fpp_xra + (xr)) +#define fpp_write_xr(xr,d) fpp_write (fpp_xra + (xr), d) + +/* Command register */ + +#define FPC_DP 04000 /* integer double */ +#define FPC_UNFX 02000 /* exit on fl undf */ +#define FPC_FIXF 01000 /* lock mem field */ +#define FPC_IE 00400 /* int enable */ +#define FPC_V_FAST 4 /* startup bits */ +#define FPC_M_FAST 017 +#define FPC_LOCK 00010 /* lockout */ +#define FPC_V_APTF 0 +#define FPC_M_APTF 07 /* apta field */ +#define FPC_STA (FPC_DP|FPC_LOCK) +#define FPC_GETFAST(x) (((x) >> FPC_V_FAST) & FPC_M_FAST) +#define FPC_GETAPTF(x) (((x) >> FPC_V_APTF) & FPC_M_APTF) + +/* Status register */ + +#define FPS_DP (FPC_DP) /* integer double */ +#define FPS_TRPX 02000 /* trap exit */ +#define FPS_HLTX 01000 /* halt exit */ +#define FPS_DVZX 00400 /* div zero exit */ +#define FPS_IOVX 00200 /* int ovf exit */ +#define FPS_FOVX 00100 /* flt ovf exit */ +#define FPS_UNF 00040 /* underflow */ +#define FPS_XXXM 00020 /* FADDM/FMULM */ +#define FPS_LOCK (FPC_LOCK) /* lockout */ +#define FPS_EP 00004 /* ext prec */ +#define FPS_PAUSE 00002 /* paused */ +#define FPS_RUN 00001 /* running */ + +/* Floating point number: 3-6 words */ + +#define FPN_FRSIGN 04000 +#define FPN_NFR_FP 2 /* std precision */ +#define FPN_NFR_EP 5 /* ext precision */ +#define FPN_NFR_MDS 6 /* mul/div precision */ +#define EXACT (uint32)((fpp_sta & FPS_EP)? FPN_NFR_EP: FPN_NFR_FP) +#define EXTEND ((uint32) FPN_NFR_EP) + +typedef struct { + int32 exp; + uint32 fr[FPN_NFR_MDS+1]; + } FPN; + +uint32 fpp_apta = 0; /* APT pointer */ +uint32 fpp_aptsvf = 0; /* APT saved field */ +uint32 fpp_opa = 0; /* operand pointer */ +uint32 fpp_fpc = 0; /* FP PC */ +uint32 fpp_bra = 0; /* base reg pointer */ +uint32 fpp_xra = 0; /* indx reg pointer */ +uint32 fpp_cmd = 0; /* command */ +uint32 fpp_sta = 0; /* status */ +uint32 fpp_flag = 0; /* flag */ +FPN fpp_ac; /* FAC */ +uint32 fpp_ssf = 0; /* single-step flag */ +uint32 fpp_last_lockbit = 0; /* last lockbit */ + +static FPN fpp_zero = { 0, { 0, 0, 0, 0, 0 } }; +static FPN fpp_one = { 1, { 02000, 0, 0, 0, 0 } }; + +int32 fpp55 (int32 IR, int32 AC); +int32 fpp56 (int32 IR, int32 AC); +void fpp_load_apt (uint32 apta); +void fpp_dump_apt (uint32 apta, uint32 sta); +uint32 fpp_1wd_dir (uint32 ir); +uint32 fpp_2wd_dir (uint32 ir); +uint32 fpp_indir (uint32 ir); +uint32 fpp_ad15 (uint32 hi); +uint32 fpp_adxr (uint32 ir, uint32 base_ad); +void fpp_add (FPN *a, FPN *b, uint32 sub); +void fpp_mul (FPN *a, FPN *b); +void fpp_div (FPN *a, FPN *b); +t_bool fpp_imul (FPN *a, FPN *b); +uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b, uint32 cnt); +void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b, uint32 cnt); +void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b, t_bool fix); +t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b); +uint32 fpp_fr_neg (uint32 *a, uint32 cnt); +int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt); +int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt); +uint32 fpp_fr_abs (uint32 *a, uint32 *b, uint32 cnt); +void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt); +void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt); +void fpp_fr_lsh12 (uint32 *a, uint32 cnt); +void fpp_fr_lsh1 (uint32 *a, uint32 cnt); +void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt); +void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt); +t_bool fpp_cond_met (uint32 cond); +t_bool fpp_norm (FPN *a, uint32 cnt); +void fpp_round (FPN *a); +t_bool fpp_test_xp (FPN *a); +void fpp_copy (FPN *a, FPN *b); +void fpp_zcopy (FPN *a, FPN *b); +void fpp_read_op (uint32 ea, FPN *a); +void fpp_write_op (uint32 ea, FPN *a); +uint32 fpp_read (uint32 ea); +void fpp_write (uint32 ea, uint32 val); +uint32 apt_read (uint32 ea); +void apt_write (uint32 ea, uint32 val); +t_stat fpp_svc (UNIT *uptr); +t_stat fpp_reset (DEVICE *dptr); + +/* FPP data structures + + fpp_dev FPP device descriptor + fpp_unit FPP unit descriptor + fpp_reg FPP register list +*/ + +DIB fpp_dib = { DEV_FPP, 2, { &fpp55, &fpp56 } }; + +UNIT fpp_unit = { UDATA (&fpp_svc, 0, 0) }; + +REG fpp_reg[] = { + { ORDATAD (FPACE, fpp_ac.exp, 12, "floating accumulator") }, + { ORDATAD (FPAC0, fpp_ac.fr[0], 12, "first mantissa") }, + { ORDATAD (FPAC1, fpp_ac.fr[1], 12, "second mantissa") }, + { ORDATAD (FPAC2, fpp_ac.fr[2], 12, "third mantissa") }, + { ORDATAD (FPAC3, fpp_ac.fr[3], 12, "fourth mantissa") }, + { ORDATAD (FPAC4, fpp_ac.fr[4], 12, "fifth mantissa") }, + { ORDATAD (CMD, fpp_cmd, 12, "FPP command register") }, + { ORDATAD (STA, fpp_sta, 12, "status register") }, + { ORDATAD (APTA, fpp_apta, 15, "active parameter table (APT) pointer") }, + { GRDATAD (APTSVF, fpp_aptsvf, 8, 3, 12, "APT field") }, + { ORDATAD (FPC, fpp_fpc, 15, "floating program counter") }, + { ORDATAD (BRA, fpp_bra, 15, "base register") }, + { ORDATAD (XRA, fpp_xra, 15, "pointer to index register 0") }, + { ORDATAD (OPA, fpp_opa, 15, "operand address register") }, + { ORDATAD (SSF, fpp_ssf, 12, "single step flag") }, + { ORDATAD (LASTLOCK, fpp_last_lockbit, 12, "lockout from FPCOM") }, + { FLDATAD (FLAG, fpp_flag, 0, "done flag") }, + { NULL } + }; + +DEVICE fpp_dev = { + "FPP", &fpp_unit, fpp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &fpp_reset, + NULL, NULL, NULL, + &fpp_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 fpp55 (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* FPINT */ + return (fpp_flag? IOT_SKP | AC: AC); /* skip on flag */ + + case 2: /* FPICL */ + fpp_reset (&fpp_dev); /* reset device */ + break; + + case 3: /* FPCOM */ + if (!fpp_flag && !(fpp_sta & FPS_RUN)) { /* flag clr, !run? */ + fpp_cmd = AC; /* load cmd */ + fpp_last_lockbit = fpp_cmd & FPS_LOCK; /* remember lock state */ + fpp_sta = (fpp_sta & ~FPC_STA) | /* copy flags */ + (fpp_cmd & FPC_STA); /* to status */ + } + break; + + case 4: /* FPHLT */ + if (fpp_sta & FPS_RUN) { /* running? */ + if (fpp_sta & FPS_PAUSE) /* paused? */ + fpp_fpc = (fpp_fpc - 1) & ADDRMASK; /* decr FPC */ + fpp_sta &= ~FPS_PAUSE; /* no longer paused */ + sim_cancel (&fpp_unit); /* stop execution */ + fpp_dump_apt (fpp_apta, FPS_HLTX); /* dump APT */ + fpp_ssf = 1; /* assume sstep */ + } + else if (!fpp_flag) + fpp_ssf = 1; /* FPST sing steps */ + if (fpp_sta & FPS_DVZX) /* fix diag timing */ + fpp_sta |= FPS_HLTX; + break; + + case 5: /* FPST */ + if (!fpp_flag && !(fpp_sta & FPS_RUN)) { /* flag clr, !run? */ + if (fpp_ssf) + fpp_sta |= fpp_last_lockbit; + fpp_sta &= ~FPS_HLTX; /* Clear halted */ + fpp_apta = (FPC_GETAPTF (fpp_cmd) << 12) | AC; + fpp_load_apt (fpp_apta); /* load APT */ + fpp_opa = fpp_fpc; + sim_activate (&fpp_unit, 0); /* start unit */ + return IOT_SKP | AC; + } + if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == (FPS_RUN|FPS_PAUSE)) { + fpp_sta &= ~FPS_PAUSE; /* continue */ + sim_activate (&fpp_unit, 0); /* start unit */ + return (IOT_SKP | AC); + } + break; + + case 6: /* FPRST */ + return fpp_sta; + + case 7: /* FPIST */ + if (fpp_flag) { /* if flag set */ + uint32 old_sta = fpp_sta; + fpp_flag = 0; /* clr flag, status */ + fpp_sta &= ~(FPS_DP|FPS_EP|FPS_TRPX|FPS_DVZX|FPS_IOVX|FPS_FOVX|FPS_UNF); + int_req &= ~INT_FPP; /* clr int req */ + return IOT_SKP | old_sta; /* ret old status */ + } + break; + + default: + return (stop_inst << IOT_V_REASON) | AC; + } /* end switch */ + +return AC; +} + +int32 fpp56 (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 7: /* FPEP */ + if ((AC & 04000) && !(fpp_sta & FPS_RUN)) { /* if AC0, not run, */ + fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP; /* set ep */ + AC = 0; + } + break; + + default: + return (stop_inst << IOT_V_REASON) | AC; + } /* end switch */ + +return AC; +} + +/* Service routine */ + +t_stat fpp_svc (UNIT *uptr) +{ +FPN x; +uint32 ir, op, op2, op3, ad, ea, wd; +uint32 i; +int32 sc; + +fpp_ac.exp = SEXT12 (fpp_ac.exp); /* sext AC exp */ +do { /* repeat */ + ir = fpp_read (fpp_fpc); /* get instr */ + fpp_fpc = (fpp_fpc + 1) & ADDRMASK; /* incr FP PC */ + op = (ir >> 7) & 037; /* get op+mode */ + op2 = (ir >> 3) & 017; /* get subop */ + op3 = ir & 07; /* get field/xr */ + fpp_sta &= ~FPS_XXXM; /* not mem op */ + + switch (op) { /* case on op+mode */ + case 000: /* operates */ + + switch (op2) { /* case on subop */ + case 000: /* no-operands */ + switch (op3) { /* case on subsubop */ + + case 0: /* FEXIT */ + /* if already trapped, don't update APT, just update status */ + if (fpp_sta & (FPS_DVZX|FPS_IOVX|FPS_FOVX|FPS_UNF)) + fpp_sta |= FPS_HLTX; + else + fpp_dump_apt (fpp_apta, 0); + break; + + case 1: /* FPAUSE */ + fpp_sta |= FPS_PAUSE; + break; + + case 2: /* FCLA */ + fpp_copy (&fpp_ac, &fpp_zero); /* clear FAC */ + break; + + case 3: /* FNEG */ + fpp_fr_neg (fpp_ac.fr, EXACT); /* do exact length */ + break; + + case 4: /* FNORM */ + if (!(fpp_sta & FPS_DP)) { /* fp or ep only */ + fpp_copy (&x, &fpp_ac); /* copy AC */ + fpp_norm (&x, EXACT); /* do exact length */ + fpp_copy (&fpp_ac, &x); /* copy back */ + } + break; + + case 5: /* STARTF */ + if (fpp_sta & FPS_EP) { /* if ep, */ + fpp_copy (&x, &fpp_ac); /* copy AC */ + fpp_round (&x); /* round */ + fpp_copy (&fpp_ac, &x); /* copy back */ + } + fpp_sta &= ~(FPS_DP|FPS_EP); + break; + + case 6: /* STARTD */ + fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP; + break; + + case 7: /* JAC */ + fpp_fpc = ((fpp_ac.fr[0] & 07) << 12) | fpp_ac.fr[1]; + break; + } + break; + + case 001: /* ALN */ + if (op3 != 0) { /* if xr, */ + wd = fpp_read_xr (op3); /* use val */ + fpp_opa = fpp_xra + op3; + } + else wd = 027; /* else 23 */ + if (!(fpp_sta & FPS_DP)) { /* fp or ep? */ + sc = (SEXT12(wd) - fpp_ac.exp) & 07777; /* alignment */ + sc = SEXT12 (sc); + fpp_ac.exp = SEXT12(wd); /* new exp */ + } + else sc = SEXT12 (wd); /* dp - simple cnt */ + if (sc < 0) /* left? */ + fpp_fr_lshn (fpp_ac.fr, -sc, EXACT); + else fpp_fr_algn (fpp_ac.fr, sc, EXACT); + if (fpp_fr_test (fpp_ac.fr, 0, EXACT) == 0) /* zero? */ + fpp_ac.exp = 0; /* clean exp */ + break; + + case 002: /* ATX */ + if (fpp_sta & FPS_DP) /* dp? */ + fpp_write_xr (op3, fpp_ac.fr[1]); /* xr<-FAC<12:23> */ + else { + fpp_copy (&x, &fpp_ac); /* copy AC */ + sc = 027 - x.exp; /* shift amt */ + if (sc < 0) /* left? */ + fpp_fr_lshn (x.fr, -sc, EXACT); + else fpp_fr_algn (x.fr, sc, EXACT); + fpp_write_xr (op3, x.fr[1]); /* xr<-val<12:23> */ + } + break; + + case 003: /* XTA */ + for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++) + x.fr[i] = 0; /* clear FOP2-4 */ + x.fr[1] = fpp_read_xr (op3); /* get XR value */ + x.fr[0] = (x.fr[1] & 04000)? 07777: 0; + x.exp = 027; /* standard exp */ + if (!(fpp_sta & FPS_DP)) { /* fp or ep? */ + fpp_norm (&x, EXACT); /* normalize */ + } + fpp_copy (&fpp_ac, &x); /* result to AC */ + if (fpp_sta & FPS_DP) /* dp skips exp */ + fpp_ac.exp = x.exp; /* so force copy */ + fpp_opa = fpp_xra + op3; + break; + + case 004: /* NOP */ + break; + + case 005: /* STARTE */ + if (!(fpp_sta & FPS_EP)) { + fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP; + for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++) + fpp_ac.fr[i] = 0; /* clear FAC2-4 */ + } + break; + + case 010: /* LDX */ + wd = fpp_ad15 (0); /* load XR immed */ + fpp_write_xr (op3, wd); + fpp_opa = fpp_xra + op3; + break; + + case 011: /* ADDX */ + wd = fpp_ad15 (0); + wd = wd + fpp_read_xr (op3); /* add to XR immed */ + fpp_write_xr (op3, wd); /* trims to 12b */ + fpp_opa = fpp_xra + op3; + break; + + default: + return stop_inst; + } /* end case subop */ + break; + + case 001: /* FLDA */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &fpp_ac); + break; + + case 002: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &fpp_ac); + if (fpp_sta & FPS_DP) + fpp_opa = ea + 1; + else fpp_opa = ea + 2; + break; + + case 003: + ea = fpp_indir (ir); + fpp_read_op (ea, &fpp_ac); + break; + + case 004: /* jumps and sets */ + ad = fpp_ad15 (op3); /* get 15b address */ + switch (op2) { /* case on subop */ + + case 000: case 001: case 002: case 003: /* cond jump */ + case 004: case 005: case 006: case 007: + if (fpp_cond_met (op2)) /* br if cond */ + fpp_fpc = ad; + break; + + case 010: /* SETX */ + fpp_xra = ad; + break; + + case 011: /* SETB */ + fpp_bra = ad; + break; + + case 012: /* JSA */ + fpp_write (ad, 01030 + (fpp_fpc >> 12)); /* save return */ + fpp_write (ad + 1, fpp_fpc); /* trims to 12b */ + fpp_fpc = (ad + 2) & ADDRMASK; + fpp_opa = fpp_fpc - 1; + break; + + case 013: /* JSR */ + fpp_write (fpp_bra + 1, 01030 + (fpp_fpc >> 12)); + fpp_write (fpp_bra + 2, fpp_fpc); /* trims to 12b */ + fpp_opa = fpp_fpc = ad; + break; + + default: + return stop_inst; + } /* end case subop */ + break; + + case 005: /* FADD */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 0); + break; + + case 006: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 0); + break; + + case 007: + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 0); + break; + + case 010: { /* JNX */ + uint32 xrn = op2 & 07; + ad = fpp_ad15 (op3); /* get 15b addr */ + wd = fpp_read_xr (xrn); /* read xr */ + if (op2 & 010) { /* inc? */ + wd = (wd + 1) & 07777; + fpp_write_xr (xrn, wd); /* ++xr */ + } + if (wd != 0) /* xr != 0? */ + fpp_fpc = ad; /* jump */ + break; + } + case 011: /* FSUB */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 1); + break; + + case 012: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 1); + break; + + case 013: + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 1); + break; + + case 014: /* TRAP3 */ + case 020: /* TRAP4 */ + fpp_opa = fpp_ad15 (op3); + fpp_dump_apt (fpp_apta, FPS_TRPX); + break; + + case 015: /* FDIV */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_div (&fpp_ac, &x); + break; + + case 016: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_div (&fpp_ac, &x); + break; + + case 017: + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_div (&fpp_ac, &x); + break; + + case 021: /* FMUL */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_mul (&fpp_ac, &x); + break; + + case 022: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_mul (&fpp_ac, &x); + break; + + case 023: + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_mul (&fpp_ac, &x); + break; + + case 024: /* LTR */ + fpp_copy (&fpp_ac, (fpp_cond_met (op2 & 07)? &fpp_one: &fpp_zero)); + break; + + case 025: /* FADDM */ + fpp_sta |= FPS_XXXM; + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&x, &fpp_ac, 0); + fpp_write_op (ea, &x); /* store result */ + break; + + case 026: + fpp_sta |= FPS_XXXM; + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&x, &fpp_ac, 0); + fpp_write_op (ea, &x); /* store result */ + break; + + case 027: + fpp_sta |= FPS_XXXM; + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_add (&x, &fpp_ac, 0); + fpp_write_op (ea, &x); /* store result */ + break; + + case 030: /* IMUL/LEA */ + ea = fpp_2wd_dir (ir); /* 2-word direct */ + if (fpp_sta & FPS_DP) { /* dp? */ + fpp_read_op (ea, &x); /* IMUL */ + fpp_imul (&fpp_ac, &x); + } + else { /* LEA */ + fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP; /* set dp */ + fpp_ac.fr[0] = (ea >> 12) & 07; + fpp_ac.fr[1] = ea & 07777; + } + break; + + case 031: /* FSTA */ + ea = fpp_1wd_dir (ir); + fpp_write_op (ea, &fpp_ac); + break; + + case 032: + ea = fpp_2wd_dir (ir); + fpp_write_op (ea, &fpp_ac); + break; + + case 033: + ea = fpp_indir (ir); + fpp_write_op (ea, &fpp_ac); + break; + + case 034: /* IMULI/LEAI */ + ea = fpp_indir (ir); /* 1-word indir */ + if (fpp_sta & FPS_DP) { /* dp? */ + fpp_read_op (ea, &x); /* IMUL */ + fpp_imul (&fpp_ac, &x); + } + else { /* LEA */ + fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP; /* set dp */ + fpp_ac.fr[0] = (ea >> 12) & 07; + fpp_ac.fr[1] = ea & 07777; + fpp_opa = ea; + } + break; + + case 035: /* FMULM */ + fpp_sta |= FPS_XXXM; + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_mul (&x, &fpp_ac); + fpp_write_op (ea, &x); /* store result */ + break; + + case 036: + fpp_sta |= FPS_XXXM; + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_mul (&x, &fpp_ac); + fpp_write_op (ea, &x); /* store result */ + break; + + case 037: + fpp_sta |= FPS_XXXM; + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_mul (&x, &fpp_ac); + fpp_write_op (ea, &x); /* store result */ + break; + } /* end sw op+mode */ + + if (fpp_ssf) { + fpp_dump_apt (fpp_apta, FPS_HLTX); /* dump APT */ + fpp_ssf = 0; + } + + if (sim_interval) + sim_interval = sim_interval - 1; + } while ((sim_interval > 0) && + ((fpp_sta & (FPS_RUN|FPS_PAUSE|FPS_LOCK)) == (FPS_RUN|FPS_LOCK))); +if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == FPS_RUN) + sim_activate (uptr, 1); +fpp_ac.exp &= 07777; /* mask AC exp */ +return SCPE_OK; +} + +/* Address decoding routines */ + +uint32 fpp_1wd_dir (uint32 ir) +{ +uint32 ad; + +ad = fpp_bra + ((ir & 0177) * 3); /* base + 3*7b off */ +if (fpp_sta & FPS_DP) /* dp? skip exp */ + ad = ad + 1; +ad = ad & ADDRMASK; +if (fpp_sta & FPS_DP) + fpp_opa = ad + 1; +else fpp_opa = ad + 2; +return ad; +} + +uint32 fpp_2wd_dir (uint32 ir) +{ +uint32 ad; + +ad = fpp_ad15 (ir); /* get 15b addr */ +return fpp_adxr (ir, ad); /* do indexing */ +} + +uint32 fpp_indir (uint32 ir) +{ +uint32 ad, wd1, wd2; + +ad = fpp_bra + ((ir & 07) * 3); /* base + 3*3b off */ +wd1 = fpp_read (ad + 1); /* bp+off points to */ +wd2 = fpp_read (ad + 2); +ad = ((wd1 & 07) << 12) | wd2; /* indirect ptr */ + +ad = fpp_adxr (ir, ad); /* do indexing */ +if (fpp_sta & FPS_DP) + fpp_opa = ad + 1; +else fpp_opa = ad + 2; +return ad; +} + +uint32 fpp_ad15 (uint32 hi) +{ +uint32 ad; + +ad = ((hi & 07) << 12) | fpp_read (fpp_fpc); /* 15b addr */ +fpp_fpc = (fpp_fpc + 1) & ADDRMASK; /* incr FPC */ +return ad; /* return addr */ +} + +uint32 fpp_adxr (uint32 ir, uint32 base_ad) +{ +uint32 xr, wd; + +xr = (ir >> 3) & 07; +wd = fpp_read_xr (xr); /* get xr */ +if (ir & 0100) { /* increment? */ + wd = (wd + 1) & 07777; /* inc, rewrite */ + fpp_write_xr (xr, wd); + } +if (xr != 0) { /* indexed? */ + if (fpp_sta & FPS_EP) + wd = wd * 6; /* scale by len */ + else if (fpp_sta & FPS_DP) + wd = wd * 2; + else wd = wd * 3; + return (base_ad + wd) & ADDRMASK; /* return index */ + } +else return base_ad & ADDRMASK; /* return addr */ +} + +/* Computation routines */ + +/* Fraction/floating add */ + +void fpp_add (FPN *a, FPN *b, uint32 sub) +{ +FPN x, y, z; +uint32 c, ediff; + +fpp_zcopy (&x, a); /* copy opnds */ +fpp_zcopy (&y, b); +if (sub) /* subtract? */ + fpp_fr_neg (y.fr, EXACT); /* neg B, exact */ +if (fpp_sta & FPS_DP) { /* dp? */ + uint32 cout = fpp_fr_add (z.fr, x.fr, y.fr, EXTEND);/* z = a + b */ + uint32 zsign = z.fr[0] & FPN_FRSIGN; + cout = (cout? 04000: 0); /* make sign bit */ + /* overflow is indicated when signs are equal and overflow does not + match the result sign bit */ + fpp_copy (a, &z); /* result is z */ + if (!((x.fr[0] ^ y.fr[0]) & FPN_FRSIGN) && (cout != zsign)) { + fpp_copy (a, &z); /* copy out result */ + fpp_dump_apt (fpp_apta, FPS_IOVX); /* int ovf? */ + return; + } + } +else { /* fp or ep */ + if (fpp_fr_test (b->fr, 0, EXACT) == 0) /* B == 0? */ + z = x; /* result is A */ + else if (fpp_fr_test (a->fr, 0, EXACT) == 0) /* A == 0? */ + z = y; /* result is B */ + else { /* fp or ep */ + if (x.exp < y.exp) { /* |a| < |b|? */ + z = x; /* exchange ops */ + x = y; + y = z; + } + ediff = x.exp - y.exp; /* exp diff */ + if (ediff <= (uint32) ((fpp_sta & FPS_EP)? 59: 24)) { /* any add? */ + z.exp = x.exp; /* result exp */ + if (ediff != 0) /* any align? */ + fpp_fr_algn (y.fr, ediff, EXTEND); /* align, 60b */ + c = fpp_fr_add (z.fr, x.fr, y.fr, EXTEND); /* add fractions */ + if ((((x.fr[0] ^ y.fr[0]) & FPN_FRSIGN) == 0) && /* same signs? */ + (c || /* carry out? */ + ((~x.fr[0] & z.fr[0] & FPN_FRSIGN)))) { /* + to - change? */ + fpp_fr_rsh1 (z.fr, c << 11, EXTEND); /* rsh, insert cout */ + z.exp = z.exp + 1; /* incr exp */ + } /* end same signs */ + } /* end in range */ + else z = x; /* ovrshift */ + } /* end ops != 0 */ + if (fpp_norm (&z, EXTEND)) /* norm, !exact? */ + fpp_round (&z); /* round */ + fpp_copy (a, &z); /* copy out */ + fpp_test_xp (&z); /* ovf, unf? */ + } /* end else */ +return; +} + +/* Fraction/floating multiply */ + +void fpp_mul (FPN *a, FPN *b) +{ +FPN x, y, z; + +fpp_zcopy (&x, a); /* copy opnds */ +fpp_zcopy (&y, b); +if ((fpp_fr_test(y.fr, 0, EXACT-1) == 0) && (y.fr[EXACT-1] < 2)) { + y.exp = 0; + y.fr[EXACT-1] = 0; +} +if (fpp_sta & FPS_DP) /* dp? */ + fpp_fr_mul (z.fr, x.fr, y.fr, TRUE); /* mult frac */ +else { /* fp or ep */ + fpp_norm (&x, EXACT); + fpp_norm (&y, EXACT); + z.exp = x.exp + y.exp; /* add exp */ + fpp_fr_mul (z.fr, x.fr, y.fr, TRUE); /* mult frac */ + if (fpp_norm (&z, EXTEND)) /* norm, !exact? */ + fpp_round (&z); /* round */ + fpp_copy (a, &z); + if (z.exp > 2047) + fpp_dump_apt (fpp_apta, FPS_FOVX); /* trap */ + return; + } +fpp_copy (a, &z); /* result is z */ +return; +} + +/* Fraction/floating divide */ + +void fpp_div (FPN *a, FPN *b) +{ +FPN x, y, z; + +if (fpp_fr_test (b->fr, 0, EXACT) == 0) { /* divisor 0? */ + fpp_dump_apt (fpp_apta, FPS_DVZX); /* error */ + return; + } +if (fpp_fr_test (a->fr, 0, EXACT) == 0) /* dividend 0? */ + return; /* quotient is 0 */ +fpp_zcopy (&x, a); /* copy opnds */ +fpp_zcopy (&y, b); +if (fpp_sta & FPS_DP) { /* dp? */ + if (fpp_fr_div (z.fr, x.fr, y.fr)) { /* fr div, ovflo? */ + fpp_dump_apt (fpp_apta, FPS_IOVX); /* error */ + return; + } + fpp_copy (a, &z); /* result is z */ + } +else { /* fp or ep */ + fpp_norm (&y, EXACT); /* norm divisor */ + if (fpp_fr_test (x.fr, 04000, EXACT) == 0) { /* divd 1.000...? */ + x.fr[0] = 06000; /* fix */ + x.exp = x.exp + 1; + } + z.exp = x.exp - y.exp; /* calc exp */ + if (fpp_fr_div (z.fr, x.fr, y.fr)) { /* fr div, ovflo? */ + uint32 cin = (a->fr[0] ^ b->fr[0]) & FPN_FRSIGN; + fpp_fr_rsh1 (z.fr, cin, EXTEND); /* rsh, insert sign */ + z.exp = z.exp + 1; /* incr exp */ + } + if (fpp_norm (&z, EXTEND)) /* norm, !exact? */ + fpp_round (&z); /* round */ + fpp_copy (a, &z); + if (z.exp > 2048) { /* underflow? */ + if (fpp_cmd & FPC_UNFX) { /* trap? */ + fpp_dump_apt (fpp_apta, FPS_UNF); + return; + } + } + } +return; +} + +/* Integer multiply - returns true if overflow */ + +t_bool fpp_imul (FPN *a, FPN *b) +{ +uint32 sext; +FPN x, y, z; + +fpp_zcopy (&x, a); /* copy args */ +fpp_zcopy (&y, b); +fpp_fr_mul (z.fr, x.fr, y.fr, FALSE); /* mult fracs */ +a->fr[0] = z.fr[1]; /* low 24b */ +a->fr[1] = z.fr[2]; +if ((a->fr[0] == 0) && (a->fr[1] == 0)) /* fpp zeroes exp */ + a->exp = 0; /* even in dp mode */ +sext = (z.fr[2] & FPN_FRSIGN)? 07777: 0; +if (((z.fr[0] | z.fr[1] | sext) != 0) && /* hi 25b == 0 */ + ((z.fr[0] & z.fr[1] & sext) != 07777)) { /* or 777777774? */ + fpp_dump_apt (fpp_apta, FPS_IOVX); + return TRUE; + } +return FALSE; +} + +/* Auxiliary floating point routines */ + +t_bool fpp_cond_met (uint32 cond) +{ +switch (cond) { + + case 0: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) == 0); + + case 1: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) >= 0); + + case 2: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) <= 0); + + case 3: + return 1; + + case 4: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) != 0); + + case 5: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) < 0); + + case 6: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) > 0); + + case 7: + return (fpp_ac.exp > 027); + } +return 0; +} + +/* Normalization - returns TRUE if rounding possible, FALSE if exact */ + +t_bool fpp_norm (FPN *a, uint32 cnt) +{ +if (fpp_fr_test (a->fr, 0, cnt) == 0) { /* zero? */ + a->exp = 0; /* clean exp */ + return FALSE; /* don't round */ + } +while (((a->fr[0] == 0) && !(a->fr[1] & 04000)) || /* lead 13b same? */ + ((a->fr[0] == 07777) && (a->fr[1] & 04000))) { + fpp_fr_lsh12 (a->fr, cnt); /* move word */ + a->exp = a->exp - 12; + } +while (((a->fr[0] ^ (a->fr[0] << 1)) & FPN_FRSIGN) == 0) { /* until norm */ + fpp_fr_lsh1 (a->fr, cnt); /* shift 1b */ + a->exp = a->exp - 1; + } +if (fpp_fr_test (a->fr, 04000, EXACT) == 0) { /* 4000...0000? */ + a->fr[0] = 06000; /* chg to 6000... */ + a->exp = a->exp + 1; /* with exp+1 */ + return FALSE; /* don't round */ + } +return TRUE; +} + +/* Exact fp number copy */ + +void fpp_copy (FPN *a, FPN *b) +{ +uint32 i; + +if (!(fpp_sta & FPS_DP)) + a->exp = b->exp; +for (i = 0; i < EXACT; i++) + a->fr[i] = b->fr[i]; +return; +} + +/* Zero extended fp number copy (60b) */ + +void fpp_zcopy (FPN *a, FPN *b) +{ +uint32 i; + +a->exp = b->exp; +for (i = 0; i < FPN_NFR_EP; i++) { + if ((i < FPN_NFR_FP) || (fpp_sta & FPS_EP)) + a->fr[i] = b->fr[i]; + else a->fr[i] = 0; + } +a->fr[i++] = 0; +a->fr[i] = 0; +return; +} + +/* Test exp for overflow or underflow, returns TRUE on trap */ + +t_bool fpp_test_xp (FPN *a) +{ +if (a->exp > 2047) { /* overflow? */ + fpp_dump_apt (fpp_apta, FPS_FOVX); /* trap */ + return TRUE; + } +if (a->exp < -2048) { /* underflow? */ + if (fpp_cmd & FPC_UNFX) { /* trap? */ + fpp_dump_apt (fpp_apta, FPS_UNF); + return TRUE; + } + fpp_copy (a, &fpp_zero); /* flush to 0 */ + } +return FALSE; +} + +/* Round dp/fp value */ + +void fpp_round (FPN *a) +{ +int32 i; +uint32 cin, afr0_sign; + +if (fpp_sta & FPS_EP) /* ep? */ + return; /* don't round */ +afr0_sign = a->fr[0] & FPN_FRSIGN; /* save input sign */ +cin = afr0_sign? 03777: 04000; +for (i = FPN_NFR_FP; i >= 0; i--) { /* 3 words */ + a->fr[i] = a->fr[i] + cin; /* add in carry */ + cin = (a->fr[i] >> 12) & 1; + a->fr[i] = a->fr[i] & 07777; + } +if (!(fpp_sta & FPS_DP) && /* fp? */ + (afr0_sign ^ (a->fr[0] & FPN_FRSIGN))) { /* sign change? */ + fpp_fr_rsh1 (a->fr, afr0_sign, EXACT); /* rsh, insert sign */ + a->exp = a->exp + 1; + } +return; +} + +/* N-precision integer routines */ + +/* Fraction add/sub */ + +uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b, uint32 cnt) + +{ +uint32 i, cin; + +for (i = cnt, cin = 0; i > 0; i--) { + c[i - 1] = a[i - 1] + b[i - 1] + cin; + cin = (c[i - 1] >> 12) & 1; + c[i - 1] = c[i - 1] & 07777; + } +return cin; +} + +void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b, uint32 cnt) +{ +uint32 i, cin; + +for (i = cnt, cin = 0; i > 0; i--) { + c[i - 1] = a[i - 1] - b[i - 1] - cin; + cin = (c[i - 1] >> 12) & 1; + c[i - 1] = c[i - 1] & 07777; + } +return; +} + +/* Fraction multiply - always develop 60b, multiply is + either 24b*24b or 60b*60b + + This is a signed multiply. The shift in for signed multiply is + technically ALU_N XOR ALU_V. This can be simplified as follows: + + a-sign c-sign result-sign cout overflow N XOR V = shift in + + 0 0 0 0 0 0 + 0 0 1 0 1 0 + 0 1 0 1 0 0 + 0 1 1 0 0 1 + 1 0 0 1 0 0 + 1 0 1 0 0 1 + 1 1 0 1 1 1 + 1 1 1 1 0 1 + + If a-sign == c-sign, shift-in = a-sign + If a-sign != c-sign, shift-in = result-sign + */ + +void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b, t_bool fix) +{ +uint32 i, cnt, lo, wc, fill, b_sign; + +b_sign = b[0] & FPN_FRSIGN; /* remember b's sign */ + +fpp_fr_fill (c, 0, FPN_NFR_MDS); /* clr answer */ +if (fpp_sta & FPS_EP) /* ep? */ + lo = FPN_NFR_EP; /* low order mpyr word */ +else + lo = FPN_NFR_FP; /* low order mpyr word */ + +if (fix) + fpp_fr_algn (a, 12, FPN_NFR_MDS + 1); /* fill left with sign */ +wc = 2; /* 3 words at start */ +fill = 0; +cnt = lo * 12; /* total steps */ +for (i = 0; i < cnt; i++) { + if ((i % 12) == 0) { + wc++; /* do another word */ + lo--; /* and next mpyr word */ + fpp_fr_algn (c, 24, wc + 1); + c[wc] = 0; + c[0] = c[1] = fill; /* propagate sign */ + } + if (b[lo] & FPN_FRSIGN) /* mpyr bit set? */ + fpp_fr_add(c, a, c, wc); + fill = ((c[0] & FPN_FRSIGN) ? 07777 : 0); /* remember sign */ + fpp_fr_lsh1 (c, wc); /* shift the result */ + fpp_fr_lsh1 (b + lo, 1); /* shift mpcd */ + + } + +if (!fix) /* imul shifts result */ + fpp_fr_rsh1 (c, c[0] & FPN_FRSIGN, EXACT + 1); /* result is 1 wd right */ +if (b_sign) { /* if mpyr was negative */ + if (fix) + fpp_fr_lsh12 (a, FPN_NFR_MDS+1); /* restore a */ + fpp_fr_sub (c, c, a, EXACT); /* adjust result */ + fpp_fr_sub (c, c, a, EXACT); + } + +return; +} + +/* Fraction divide */ + +t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b) +{ +uint32 i, old_c, lo, cnt, sign, b_sign, addsub, limit; +/* Number of words processed by each divide step */ +static uint32 limits[7] = {6, 6, 5, 4, 3, 3, 2}; + +fpp_fr_fill (c, 0, FPN_NFR_MDS); /* clr answer */ +sign = (a[0] ^ b[0]) & FPN_FRSIGN; /* sign of result */ +b_sign = (b[0] & FPN_FRSIGN); +if (a[0] & FPN_FRSIGN) /* |a| */ + fpp_fr_neg (a, EXACT); +if (fpp_sta & FPS_EP) /* ep? 6 words */ + lo = FPN_NFR_EP-1; +else lo = FPN_NFR_FP-1; /* fp, dp? 3 words */ +cnt = (lo + 1) * 12; +addsub = 04000; /* setup first op */ +for (i = 0; i < cnt; i++) { /* loop */ + limit = limits[i / 12]; /* how many wds this time */ + fpp_fr_lsh1 (c, FPN_NFR_MDS); /* shift quotient */ + if (addsub ^ b_sign) /* diff signs, subtr */ + fpp_fr_sub (a, a, b, limit); /* divd - divr */ + else + fpp_fr_add (a, a, b, limit); /* restore */ + if (!(a[0] & FPN_FRSIGN)) { + c[lo] |= 1; /* set quo bit */ + addsub = 04000; /* sign for nxt loop */ + } + else addsub = 0; + fpp_fr_lsh1 (a, limit); /* shift dividend */ + } +old_c = c[0]; /* save ho quo */ +if (sign) /* expect neg ans? */ + fpp_fr_neg (c, EXTEND); /* -quo */ +if (old_c & FPN_FRSIGN) /* sign set before */ + return TRUE; /* neg? */ +return FALSE; +} + +/* Negate - 24b or 60b */ + +uint32 fpp_fr_neg (uint32 *a, uint32 cnt) +{ +uint32 i, cin; + +for (i = cnt, cin = 1; i > 0; i--) { + a[i - 1] = (~a[i - 1] + cin) & 07777; + cin = (cin != 0 && a[i - 1] == 0); + } +return cin; +} + +/* Test (compare to x'0...0) - 24b or 60b */ + +int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt) +{ +uint32 i; + +if (a[0] != v0) + return (a[0] & FPN_FRSIGN)? -1: +1; +for (i = 1; i < cnt; i++) { + if (a[i] != 0) + return (a[0] & FPN_FRSIGN)? -1: +1; + } +return 0; +} + +/* Fraction compare - 24b or 60b */ + +int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt) +{ +uint32 i; + +if ((a[0] ^ b[0]) & FPN_FRSIGN) + return (b[0] & FPN_FRSIGN)? +1: -1; +for (i = 0; i < cnt; i++) { + if (a[i] > b[i]) + return (b[0] & FPN_FRSIGN)? +1: -1; + if (a[i] < b[i]) + return (b[0] & FPN_FRSIGN)? -1: +1; + } +return 0; +} + +/* Fraction fill */ + +void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt) +{ +uint32 i; + +for (i = 0; i < cnt; i++) + a[i] = v; +return; +} + +/* Left shift n (unsigned) */ + +void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt) +{ +uint32 i; + +if (sc >= (cnt * 12)) { /* out of range? */ + fpp_fr_fill (a, 0, cnt); + return; + } +while (sc >= 12) { /* word shift? */ + fpp_fr_lsh12 (a, cnt); + sc = sc - 12; + } +if (sc == 0) /* any more? */ + return; +for (i = 1; i < cnt; i++) /* bit shift */ + a[i - 1] = ((a[i - 1] << sc) | (a[i] >> (12 - sc))) & 07777; +a[cnt - 1] = (a[cnt - 1] << sc) & 07777; +return; +} + +/* Left shift 12b (unsigned) */ + +void fpp_fr_lsh12 (uint32 *a, uint32 cnt) +{ +uint32 i; + +for (i = 1; i < cnt; i++) + a[i - 1] = a[i]; +a[cnt - 1] = 0; +return; +} + +/* Left shift 1b (unsigned) */ + +void fpp_fr_lsh1 (uint32 *a, uint32 cnt) +{ +uint32 i; + +for (i = 1; i < cnt; i++) + a[i - 1] = ((a[i - 1] << 1) | (a[i] >> 11)) & 07777; +a[cnt - 1] = (a[cnt - 1] << 1) & 07777; +return; +} + +/* Right shift 1b, with shift in */ + +void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt) +{ +uint32 i; + +for (i = cnt - 1; i > 0; i--) + a[i] = ((a[i] >> 1) | (a[i - 1] << 11)) & 07777; +a[0] = (a[0] >> 1) | sign; +return; +} + +/* Right shift n (signed) */ + +void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt) +{ +uint32 i, sign; + +sign = (a[0] & FPN_FRSIGN)? 07777: 0; +if (sc >= (cnt * 12)) { /* out of range? */ + fpp_fr_fill (a, sign, cnt); + return; + } +while (sc >= 12) { + for (i = cnt - 1; i > 0; i--) + a[i] = a[i - 1]; + a[0] = sign; + sc = sc - 12; + } +if (sc == 0) + return; +for (i = cnt - 1; i > 0; i--) + a[i] = ((a[i] >> sc) | (a[i - 1] << (12 - sc))) & 07777; +a[0] = ((a[0] >> sc) | (sign << (12 - sc))) & 07777; +return; +} + +/* Read/write routines */ + +void fpp_read_op (uint32 ea, FPN *a) +{ +uint32 i; + +if (!(fpp_sta & FPS_DP)) { + a->exp = fpp_read (ea++); + a->exp = SEXT12 (a->exp); + } +for (i = 0; i < EXACT; i++) + a->fr[i] = fpp_read (ea + i); +return; +} + +void fpp_write_op (uint32 ea, FPN *a) +{ +uint32 i; + +fpp_opa = ea + 2; +if (!(fpp_sta & FPS_DP)) + fpp_write (ea++, a->exp); +for (i = 0; i < EXACT; i++) + fpp_write (ea + i, a->fr[i]); +return; +} + +uint32 fpp_read (uint32 ea) +{ +ea = ea & ADDRMASK; +if (fpp_cmd & FPC_FIXF) + ea = fpp_aptsvf | (ea & 07777); +return M[ea]; +} + +void fpp_write (uint32 ea, uint32 val) +{ +ea = ea & ADDRMASK; +if (fpp_cmd & FPC_FIXF) + ea = fpp_aptsvf | (ea & 07777); +if (MEM_ADDR_OK (ea)) + M[ea] = val & 07777; +return; +} + +uint32 apt_read (uint32 ea) +{ +ea = ea & ADDRMASK; +return M[ea]; +} + +void apt_write (uint32 ea, uint32 val) +{ +ea = ea & ADDRMASK; +if (MEM_ADDR_OK (ea)) + M[ea] = val & 07777; +return; +} + +/* Utility routines */ + +void fpp_load_apt (uint32 ad) +{ +uint32 wd0, i; + +wd0 = apt_read (ad++); +fpp_fpc = ((wd0 & 07) << 12) | apt_read (ad++); +if (FPC_GETFAST (fpp_cmd) != 017) { + fpp_xra = ((wd0 & 00070) << 9) | apt_read (ad++); + fpp_bra = ((wd0 & 00700) << 6) | apt_read (ad++); + fpp_opa = ((wd0 & 07000) << 3) | apt_read (ad++); + fpp_ac.exp = apt_read (ad++); + for (i = 0; i < EXACT; i++) + fpp_ac.fr[i] = apt_read (ad++); + } +fpp_aptsvf = (ad - 1) & 070000; +fpp_sta |= FPS_RUN; +return; +} + +void fpp_dump_apt (uint32 ad, uint32 sta) +{ +uint32 wd0, i; + +wd0 = (fpp_fpc >> 12) & 07; +if (FPC_GETFAST (fpp_cmd) != 017) + wd0 = wd0 | + ((fpp_opa >> 3) & 07000) | + ((fpp_bra >> 6) & 00700) | + ((fpp_xra >> 9) & 00070); +apt_write (ad++, wd0); +apt_write (ad++, fpp_fpc); +if (FPC_GETFAST (fpp_cmd) != 017) { + apt_write (ad++, fpp_xra); + apt_write (ad++, fpp_bra); + apt_write (ad++, fpp_opa); + apt_write (ad++, fpp_ac.exp); + for (i = 0; i < EXACT; i++) + apt_write (ad++, fpp_ac.fr[i]); + } +fpp_sta = (fpp_sta | sta) & ~FPS_RUN; +fpp_flag = 1; +if (fpp_cmd & FPC_IE) + int_req |= INT_FPP; +return; +} + +/* Reset routine */ + +t_stat fpp_reset (DEVICE *dptr) +{ +sim_cancel (&fpp_unit); +fpp_flag = 0; +fpp_last_lockbit = 0; +int_req &= ~INT_FPP; +if (sim_switches & SWMASK ('P')) { + fpp_apta = 0; + fpp_aptsvf = 0; + fpp_fpc = 0; + fpp_bra = 0; + fpp_xra = 0; + fpp_opa = 0; + fpp_ac = fpp_zero; + fpp_ssf = 0; + fpp_sta = 0; + fpp_cmd = 0; + } +else { + fpp_sta &= ~(FPS_DP|FPS_EP|FPS_TRPX|FPS_DVZX|FPS_IOVX|FPS_FOVX|FPS_UNF); + fpp_cmd &= (FPC_DP|FPC_UNFX|FPC_IE); + } + +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_lp.c Index: src/SIMH/PDP8/pdp8_lp.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_lp.c @@ -0,0 +1,188 @@ +/* pdp8_lp.c: PDP-8 line printer simulator + + Copyright (c) 1993-2016, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt LP8E line printer + + 16-Dec-16 DJG Added IOT 6660 to allow WPS WS78 3.4 to print + 19-Jan-07 RMS Added UNIT_TEXT + 25-Apr-03 RMS Revised for extended file support + 04-Oct-02 RMS Added DIB, enable/disable, device number support + 30-May-02 RMS Widened POS to 32b +*/ + +#include "pdp8_defs.h" + +extern int32 int_req, int_enable, dev_done, stop_inst; + +int32 lpt_err = 0; /* error flag */ +int32 lpt_stopioe = 0; /* stop on error */ + +int32 lpt (int32 IR, int32 AC); +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, CONST char *cptr); +t_stat lpt_detach (UNIT *uptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +DIB lpt_dib = { DEV_LPT, 1, { &lpt } }; + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lpt_reg[] = { + { ORDATAD (BUF, lpt_unit.buf, 8,"last data item processed") }, + { FLDATAD (ERR, lpt_err, 0, "error status flag") }, + { FLDATAD (DONE, dev_done, INT_V_LPT, "device done flag") }, + { FLDATAD (ENABLE, int_enable, INT_V_LPT, "interrupt enable flag") }, + { FLDATAD (INT, int_req, INT_V_LPT, "interrupt pending flag") }, + { DRDATAD (POS, lpt_unit.pos, T_ADDR_W, "position in the output file"), PV_LEFT }, + { DRDATAD (TIME, lpt_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT }, + { FLDATAD (STOP_IOE, lpt_stopioe, 0, "stop on I/O error") }, + { ORDATA (DEVNUM, lpt_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB lpt_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, &lpt_detach, + &lpt_dib, DEV_DISABLE + }; + +/* IOT routine */ + +int32 lpt (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* PKSTF */ + dev_done = dev_done | INT_LPT; /* set flag */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 1: /* PSKF */ + return (dev_done & INT_LPT)? IOT_SKP + AC: AC; + + case 2: /* PCLF */ + dev_done = dev_done & ~INT_LPT; /* clear flag */ + int_req = int_req & ~INT_LPT; /* clear int req */ + return AC; + + case 3: /* PSKE */ + return (lpt_err)? IOT_SKP + AC: AC; + + case 6: /* PCLF!PSTB */ + dev_done = dev_done & ~INT_LPT; /* clear flag */ + int_req = int_req & ~INT_LPT; /* clear int req */ + + case 4: /* PSTB */ + lpt_unit.buf = AC & 0177; /* load buffer */ + if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) || + (lpt_unit.buf == 012)) { + sim_activate (&lpt_unit, lpt_unit.wait); + return AC; + } + return (lpt_svc (&lpt_unit) << IOT_V_REASON) + AC; + + case 5: /* PSIE */ + int_enable = int_enable | INT_LPT; /* set enable */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 7: /* PCIE */ + int_enable = int_enable & ~INT_LPT; /* clear enable */ + int_req = int_req & ~INT_LPT; /* clear int req */ + return AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat lpt_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_LPT; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +if ((uptr->flags & UNIT_ATT) == 0) { + lpt_err = 1; + return IORETURN (lpt_stopioe, SCPE_UNATT); + } +fputc (uptr->buf, uptr->fileref); /* print char */ +uptr->pos = ftell (uptr->fileref); +if (ferror (uptr->fileref)) { /* error? */ + sim_perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +lpt_unit.buf = 0; +dev_done = dev_done & ~INT_LPT; /* clear done, int */ +int_req = int_req & ~INT_LPT; +int_enable = int_enable | INT_LPT; /* set enable */ +lpt_err = (lpt_unit.flags & UNIT_ATT) == 0; +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, CONST char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +lpt_err = (lpt_unit.flags & UNIT_ATT) == 0; +return reason; +} + +/* Detach routine */ + +t_stat lpt_detach (UNIT *uptr) +{ +lpt_err = 1; +return detach_unit (uptr); +} ADDED src/SIMH/PDP8/pdp8_mt.c Index: src/SIMH/PDP8/pdp8_mt.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_mt.c @@ -0,0 +1,661 @@ +/* pdp8_mt.c: PDP-8 magnetic tape simulator + + Copyright (c) 1993-2011, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt TM8E/TU10 magtape + + 16-Feb-06 RMS Added tape capacity checking + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 18-Mar-05 RMS Added attached test to detach routine + 25-Apr-03 RMS Revised for extended file support + 29-Mar-03 RMS Added multiformat support + 04-Mar-03 RMS Fixed bug in SKTR + 01-Mar-03 RMS Fixed interrupt handling + Revised for magtape library + 30-Oct-02 RMS Revised BOT handling, added error record handling + 04-Oct-02 RMS Added DIBs, device number support + 30-Aug-02 RMS Revamped error handling + 28-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Added maximum record length test + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Changed UST, POS, FLG to arrays + 25-Apr-01 RMS Added device enable/disable support + 04-Oct-98 RMS V2.4 magtape format + 22-Jan-97 RMS V2.3 magtape format + 01-Jan-96 RMS Rewritten from TM8-E Maintenance Manual + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "pdp8_defs.h" +#include "sim_tape.h" + +#define MT_NUMDR 8 /* #drives */ +#define USTAT u3 /* unit status */ +#define MT_MAXFR (1 << 16) /* max record lnt */ +#define WC_SIZE (1 << 12) /* max word count */ +#define WC_MASK (WC_SIZE - 1) + +/* Command/unit - mt_cu */ + +#define CU_V_UNIT 9 /* unit */ +#define CU_M_UNIT 07 +#define CU_PARITY 00400 /* parity select */ +#define CU_IEE 00200 /* error int enable */ +#define CU_IED 00100 /* done int enable */ +#define CU_V_EMA 3 /* ext mem address */ +#define CU_M_EMA 07 +#define CU_EMA (CU_M_EMA << CU_V_EMA) +#define CU_DTY 00002 /* drive type */ +#define CU_UNPAK 00001 /* 6b vs 8b mode */ +#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) +#define GET_EMA(x) (((x) & CU_EMA) << (12 - CU_V_EMA)) + +/* Function - mt_fn */ + +#define FN_V_FNC 9 /* function */ +#define FN_M_FNC 07 +#define FN_UNLOAD 00 +#define FN_REWIND 01 +#define FN_READ 02 +#define FN_CMPARE 03 +#define FN_WRITE 04 +#define FN_WREOF 05 +#define FN_SPACEF 06 +#define FN_SPACER 07 +#define FN_ERASE 00400 /* erase */ +#define FN_CRC 00200 /* read CRC */ +#define FN_GO 00100 /* go */ +#define FN_INC 00040 /* incr mode */ +#define FN_RMASK 07700 /* readable bits */ +#define GET_FNC(x) (((x) >> FN_V_FNC) & FN_M_FNC) + +/* Status - stored in mt_sta or (*) uptr->USTAT */ + +#define STA_ERR (04000 << 12) /* error */ +#define STA_REW (02000 << 12) /* *rewinding */ +#define STA_BOT (01000 << 12) /* *start of tape */ +#define STA_REM (00400 << 12) /* *offline */ +#define STA_PAR (00200 << 12) /* parity error */ +#define STA_EOF (00100 << 12) /* *end of file */ +#define STA_RLE (00040 << 12) /* rec lnt error */ +#define STA_DLT (00020 << 12) /* data late */ +#define STA_EOT (00010 << 12) /* *end of tape */ +#define STA_WLK (00004 << 12) /* *write locked */ +#define STA_CPE (00002 << 12) /* compare error */ +#define STA_ILL (00001 << 12) /* illegal */ +#define STA_9TK 00040 /* 9 track */ +/* #define STA_BAD 00020 *//* bad tape?? */ +#define STA_INC 00010 /* increment error */ +#define STA_LAT 00004 /* lateral par error */ +#define STA_CRC 00002 /* CRC error */ +#define STA_LON 00001 /* long par error */ + +#define STA_CLR (FN_RMASK | 00020) /* always clear */ +#define STA_DYN (STA_REW | STA_BOT | STA_REM | STA_EOF | \ + STA_EOT | STA_WLK) /* kept in USTAT */ + +extern uint16 M[]; +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; + +int32 mt_cu = 0; /* command/unit */ +int32 mt_fn = 0; /* function */ +int32 mt_ca = 0; /* current address */ +int32 mt_wc = 0; /* word count */ +int32 mt_sta = 0; /* status register */ +int32 mt_db = 0; /* data buffer */ +int32 mt_done = 0; /* mag tape flag */ +int32 mt_time = 10; /* record latency */ +int32 mt_stopioe = 1; /* stop on error */ +uint8 *mtxb = NULL; /* transfer buffer */ + +int32 mt70 (int32 IR, int32 AC); +int32 mt71 (int32 IR, int32 AC); +int32 mt72 (int32 IR, int32 AC); +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, CONST char *cptr); +t_stat mt_detach (UNIT *uptr); +int32 mt_updcsta (UNIT *uptr); +int32 mt_ixma (int32 xma); +t_stat mt_map_err (UNIT *uptr, t_stat st); +t_stat mt_vlock (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +UNIT *mt_busy (void); +void mt_set_done (void); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +DIB mt_dib = { DEV_MT, 3, { &mt70, &mt71, &mt72 } }; + +UNIT mt_unit[] = { + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } + }; + +REG mt_reg[] = { + { ORDATAD (CMD, mt_cu, 12, "command") }, + { ORDATAD (FNC, mt_fn, 12, "function") }, + { ORDATAD (CA, mt_ca, 12, "memory address") }, + { ORDATAD (WC, mt_wc, 12, "word count") }, + { ORDATAD (DB, mt_db, 12, "data buffer") }, + { GRDATAD (STA, mt_sta, 8, 12, 12, "status buffer") }, + { ORDATAD (STA2, mt_sta, 6, "secondary status") }, + { FLDATAD (DONE, mt_done, 0, "device done flag") }, + { FLDATAD (INT, int_req, INT_V_MT, "interrupt pending flag") }, + { FLDATAD (STOP_IOE, mt_stopioe, 0, "stop on I/O error") }, + { DRDATAD (TIME, mt_time, 24, "record delay"), PV_LEFT }, + { URDATAD (UST, mt_unit[0].USTAT, 8, 16, 0, MT_NUMDR, 0, "unit status, units 0 to 7") }, + { URDATAD (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR, PV_LEFT | REG_RO, "position, units 0 to 7") }, + { FLDATA (DEVNUM, mt_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB mt_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &mt_vlock }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &mt_vlock }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &mt_detach, + &mt_dib, DEV_DISABLE | DEV_TAPE + }; + +/* IOT routines */ + +int32 mt70 (int32 IR, int32 AC) +{ +int32 f; +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* LWCR */ + mt_wc = AC; /* load word count */ + return 0; + + case 2: /* CWCR */ + mt_wc = 0; /* clear word count */ + return AC; + + case 3: /* LCAR */ + mt_ca = AC; /* load mem address */ + return 0; + + case 4: /* CCAR */ + mt_ca = 0; /* clear mem address */ + return AC; + + case 5: /* LCMR */ + if (mt_busy ()) /* busy? illegal op */ + mt_sta = mt_sta | STA_ILL | STA_ERR; + mt_cu = AC; /* load command reg */ + mt_updcsta (mt_dev.units + GET_UNIT (mt_cu)); + return 0; + + case 6: /* LFGR */ + if (mt_busy ()) /* busy? illegal op */ + mt_sta = mt_sta | STA_ILL | STA_ERR; + mt_fn = AC; /* load function */ + if ((mt_fn & FN_GO) == 0) { /* go set? */ + mt_updcsta (uptr); /* update status */ + return 0; + } + f = GET_FNC (mt_fn); /* get function */ + if (((uptr->flags & UNIT_ATT) == 0) || + sim_is_active (uptr) || + (((f == FN_WRITE) || (f == FN_WREOF)) && sim_tape_wrp (uptr)) + || (((f == FN_SPACER) || (f == FN_REWIND)) && sim_tape_bot (uptr))) { + mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal op error */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return 0; + } + uptr->USTAT = uptr->USTAT & STA_WLK; /* clear status */ + if (f == FN_UNLOAD) { /* unload? */ + detach_unit (uptr); /* set offline */ + uptr->USTAT = STA_REW | STA_REM; /* rewinding, off */ + mt_set_done (); /* set done */ + } + else if (f == FN_REWIND) { /* rewind */ + uptr->USTAT = uptr->USTAT | STA_REW; /* rewinding */ + mt_set_done (); /* set done */ + } + else mt_done = 0; /* clear done */ + mt_updcsta (uptr); /* update status */ + sim_activate (uptr, mt_time); /* start io */ + return 0; + + case 7: /* LDBR */ + if (mt_busy ()) /* busy? illegal op */ + mt_sta = mt_sta | STA_ILL | STA_ERR; + mt_db = AC; /* load buffer */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return 0; + } /* end switch */ + +return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ +} + +int32 mt71 (int32 IR, int32 AC) +{ +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* RWCR */ + return mt_wc; /* read word count */ + + case 2: /* CLT */ + mt_reset (&mt_dev); /* reset everything */ + return AC; + + case 3: /* RCAR */ + return mt_ca; /* read mem address */ + + case 4: /* RMSR */ + return ((mt_updcsta (uptr) >> 12) & 07777); /* read status */ + + case 5: /* RCMR */ + return mt_cu; /* read command */ + + case 6: /* RFSR */ + return (((mt_fn & FN_RMASK) | (mt_updcsta (uptr) & ~FN_RMASK)) + & 07777); /* read function */ + + case 7: /* RDBR */ + return mt_db; /* read data buffer */ + } + +return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ +} + +int32 mt72 (int32 IR, int32 AC) +{ +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* SKEF */ + return (mt_sta & STA_ERR)? IOT_SKP + AC: AC; + + case 2: /* SKCB */ + return (!mt_busy ())? IOT_SKP + AC: AC; + + case 3: /* SKJD */ + return mt_done? IOT_SKP + AC: AC; + + case 4: /* SKTR */ + return (!sim_is_active (uptr) && + (uptr->flags & UNIT_ATT))? IOT_SKP + AC: AC; + + case 5: /* CLF */ + if (!sim_is_active (uptr)) mt_reset (&mt_dev); /* if TUR, zap */ + else { /* just ctrl zap */ + mt_sta = 0; /* clear status */ + mt_done = 0; /* clear done */ + mt_updcsta (uptr); /* update status */ + } + return AC; + } /* end switch */ + +return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt +*/ + +t_stat mt_svc (UNIT *uptr) +{ +int32 f, i, p, u, wc, xma; +t_mtrlnt tbc, cbc; +t_bool passed_eot; +uint16 c, c1, c2; +t_stat st, r = SCPE_OK; + +u = (int32) (uptr - mt_dev.units); /* get unit number */ +f = GET_FNC (mt_fn); /* get command */ +xma = GET_EMA (mt_cu) + mt_ca; /* get mem addr */ +wc = WC_SIZE - mt_wc; /* get wc */ + +if (uptr->USTAT & STA_REW) { /* rewind? */ + sim_tape_rewind (uptr); /* update position */ + if (uptr->flags & UNIT_ATT) /* still on line? */ + uptr->USTAT = (uptr->USTAT & STA_WLK) | STA_BOT; + else uptr->USTAT = STA_REM; + if (u == GET_UNIT (mt_cu)) { /* selected? */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + } + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */ + uptr->USTAT = STA_REM; /* unit off line */ + mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal operation */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return IORETURN (mt_stopioe, SCPE_UNATT); + } + +passed_eot = sim_tape_eot (uptr); /* passed eot? */ +switch (f) { /* case on function */ + + case FN_READ: /* read */ + case FN_CMPARE: /* read/compare */ + st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */ + if (st == MTSE_RECE) /* rec in err? */ + mt_sta = mt_sta | STA_PAR | STA_ERR; + else if (st != MTSE_OK) { /* other error? */ + r = mt_map_err (uptr, st); /* map error */ + mt_sta = mt_sta | STA_RLE | STA_ERR; /* err, eof/eom, tmk */ + break; + } + cbc = (mt_cu & CU_UNPAK)? wc: wc * 2; /* expected bc */ + if (tbc != cbc) /* wrong size? */ + mt_sta = mt_sta | STA_RLE | STA_ERR; + if (tbc < cbc) { /* record small? */ + cbc = tbc; /* use smaller */ + wc = (mt_cu & CU_UNPAK)? cbc: (cbc + 1) / 2; + } + for (i = p = 0; i < wc; i++) { /* copy buffer */ + xma = mt_ixma (xma); /* increment xma */ + mt_wc = (mt_wc + 1) & 07777; /* incr word cnt */ + if (mt_cu & CU_UNPAK) c = mtxb[p++]; + else { + c1 = mtxb[p++] & 077; + c2 = mtxb[p++] & 077; + c = (c1 << 6) | c2; + } + if ((f == FN_READ) && MEM_ADDR_OK (xma)) + M[xma] = c; + else if ((f == FN_CMPARE) && (M[xma] != c)) { + mt_sta = mt_sta | STA_CPE | STA_ERR; + break; + } + } + break; + + case FN_WRITE: /* write */ + tbc = (mt_cu & CU_UNPAK)? wc: wc * 2; + for (i = p = 0; i < wc; i++) { /* copy buf to tape */ + xma = mt_ixma (xma); /* incr mem addr */ + if (mt_cu & CU_UNPAK) + mtxb[p++] = M[xma] & 0377; + else { + mtxb[p++] = (M[xma] >> 6) & 077; + mtxb[p++] = M[xma] & 077; + } + } + if ((st = sim_tape_wrrecf (uptr, mtxb, tbc))) { /* write rec, err? */ + r = mt_map_err (uptr, st); /* map error */ + xma = GET_EMA (mt_cu) + mt_ca; /* restore xma */ + } + else mt_wc = 0; /* ok, clear wc */ + break; + + case FN_WREOF: + if ((st = sim_tape_wrtmk (uptr))) /* write tmk, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; + + case FN_SPACEF: /* space forward */ + do { + mt_wc = (mt_wc + 1) & 07777; /* incr wc */ + if ((st = sim_tape_sprecf (uptr, &tbc))) { /* space rec fwd, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; /* stop */ + } + } while ((mt_wc != 0) && (passed_eot || !sim_tape_eot (uptr))); + break; + + case FN_SPACER: /* space reverse */ + do { + mt_wc = (mt_wc + 1) & 07777; /* incr wc */ + if ((st = sim_tape_sprecr (uptr, &tbc))) { /* space rec rev, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; /* stop */ + } + } while (mt_wc != 0); + break; + } /* end case */ + +if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ + uptr->USTAT = uptr->USTAT | STA_EOT; +mt_cu = (mt_cu & ~CU_EMA) | ((xma >> (12 - CU_V_EMA)) & CU_EMA); +mt_ca = xma & 07777; /* update mem addr */ +mt_set_done (); /* set done */ +mt_updcsta (uptr); /* update status */ +return r; +} + +/* Update controller status */ + +int32 mt_updcsta (UNIT *uptr) +{ +mt_sta = (mt_sta & ~(STA_DYN | STA_CLR)) | (uptr->USTAT & STA_DYN); +if (((mt_sta & STA_ERR) && (mt_cu & CU_IEE)) || + (mt_done && (mt_cu & CU_IED))) + int_req = int_req | INT_MT; +else int_req = int_req & ~INT_MT; +return mt_sta; +} + +/* Test if controller busy */ + +UNIT *mt_busy (void) +{ +int32 u; +UNIT *uptr; + +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + if (sim_is_active (uptr) && ((uptr->USTAT & STA_REW) == 0)) + return uptr; + } +return NULL; +} + +/* Increment extended memory address */ + +int32 mt_ixma (int32 xma) /* incr extended ma */ +{ +int32 v; + +v = ((xma + 1) & 07777) | (xma & 070000); /* wrapped incr */ +if (mt_fn & FN_INC) { /* increment mode? */ + if (xma == 077777) /* at limit? error */ + mt_sta = mt_sta | STA_INC | STA_ERR; + else v = xma + 1; /* else 15b incr */ + } +return v; +} + +/* Set done */ + +void mt_set_done (void) +{ +mt_done = 1; /* set done */ +mt_fn = mt_fn & ~(FN_CRC | FN_GO | FN_INC); /* clear func<4:6> */ +return; +} + +/* Map tape error status */ + +t_stat mt_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* unattached */ + mt_sta = mt_sta | STA_ILL | STA_ERR; + case MTSE_OK: /* no error */ + return SCPE_IERR; /* never get here! */ + + case MTSE_TMK: /* end of file */ + uptr->USTAT = uptr->USTAT | STA_EOF; /* set EOF */ + mt_sta = mt_sta | STA_ERR; + break; + + case MTSE_IOERR: /* IO error */ + mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ + if (mt_stopioe) + return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + case MTSE_EOM: /* end of medium */ + mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ + break; + + case MTSE_BOT: /* reverse into BOT */ + uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */ + mt_sta = mt_sta | STA_ERR; + break; + + case MTSE_WRP: /* write protect */ + mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal operation */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +mt_cu = mt_fn = mt_wc = mt_ca = mt_db = mt_sta = mt_done = 0; +int_req = int_req & ~INT_MT; /* clear interrupt */ +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + sim_cancel (uptr); /* cancel activity */ + sim_tape_reset (uptr); /* reset tape */ + if (uptr->flags & UNIT_ATT) uptr->USTAT = + (sim_tape_bot (uptr)? STA_BOT: 0) | + (sim_tape_wrp (uptr)? STA_WLK: 0); + else uptr->USTAT = STA_REM; + } +if (mtxb == NULL) + mtxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); +if (mtxb == NULL) + return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat mt_attach (UNIT *uptr, CONST char *cptr) +{ +t_stat r; +int32 u = uptr - mt_dev.units; /* get unit number */ + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) + return r; +uptr->USTAT = STA_BOT | (sim_tape_wrp (uptr)? STA_WLK: 0); +if (u == GET_UNIT (mt_cu)) + mt_updcsta (uptr); +return r; +} + +/* Detach routine */ + +t_stat mt_detach (UNIT* uptr) +{ +int32 u = uptr - mt_dev.units; /* get unit number */ + +if (!(uptr->flags & UNIT_ATT)) /* check for attached */ + return SCPE_OK; +if (!sim_is_active (uptr)) + uptr->USTAT = STA_REM; +if (u == GET_UNIT (mt_cu)) + mt_updcsta (uptr); +return sim_tape_detach (uptr); +} + +/* Write lock/enable routine */ + +t_stat mt_vlock (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 u = uptr - mt_dev.units; /* get unit number */ + +if ((uptr->flags & UNIT_ATT) && (val || sim_tape_wrp (uptr))) + uptr->USTAT = uptr->USTAT | STA_WLK; +else uptr->USTAT = uptr->USTAT & ~STA_WLK; +if (u == GET_UNIT (mt_cu)) + mt_updcsta (uptr); +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_pt.c Index: src/SIMH/PDP8/pdp8_pt.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_pt.c @@ -0,0 +1,293 @@ +/* pdp8_pt.c: PDP-8 paper tape reader/punch simulator + + Copyright (c) 1993-2017, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr,ptp PC8E paper tape reader/punch + + 13-Mar-17 RMS Annotated fall through in switch + 17-Mar-13 RMS Modified to use central set_bootpc routine + 25-Apr-03 RMS Revised for extended file support + 04-Oct-02 RMS Added DIBs + 30-May-02 RMS Widened POS to 32b + 30-Nov-01 RMS Added read only unit support + 30-Mar-98 RMS Added RIM loader as PTR bootstrap +*/ + +#include "pdp8_defs.h" + +extern int32 int_req, int_enable, dev_done, stop_inst; + +int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ + +int32 ptr (int32 IR, int32 AC); +int32 ptp (int32 IR, int32 AC); +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno, DEVICE *dptr); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_reg PTR register list +*/ + +DIB ptr_dib = { DEV_PTR, 1, { &ptr } }; + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT + }; + +REG ptr_reg[] = { + { ORDATAD (BUF, ptr_unit.buf, 8, "last data item processed") }, + { FLDATAD (DONE, dev_done, INT_V_PTR, "device done flag") }, + { FLDATAD (ENABLE, int_enable, INT_V_PTR, "interrupt enable flag") }, + { FLDATAD (INT, int_req, INT_V_PTR, "interrupt pending flag") }, + { DRDATAD (POS, ptr_unit.pos, T_ADDR_W, "position in the input file"), PV_LEFT }, + { DRDATAD (TIME, ptr_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT }, + { FLDATAD (STOP_IOE, ptr_stopioe, 0, "stop on I/O error") }, + { NULL } + }; + +MTAB ptr_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, + { 0 } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, NULL, NULL, + &ptr_dib, 0 }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_reg PTP register list +*/ + +DIB ptp_dib = { DEV_PTP, 1, { &ptp } }; + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { ORDATAD (BUF, ptp_unit.buf, 8, "last data item processed") }, + { FLDATAD (DONE, dev_done, INT_V_PTP, "device done flag") }, + { FLDATAD (ENABLE, int_enable, INT_V_PTP, "interrupt enable flag") }, + { FLDATAD (INT, int_req, INT_V_PTP, "interrupt pending flag") }, + { DRDATAD (POS, ptp_unit.pos, T_ADDR_W, "position in the output file"), PV_LEFT }, + { DRDATAD (TIME, ptp_unit.wait, 24, "time from I/O initiation to interrupt"), PV_LEFT }, + { FLDATAD (STOP_IOE, ptp_stopioe, 0, "stop on I/O error") }, + { NULL } + }; + +MTAB ptp_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, + { 0 } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL, + &ptp_dib, 0 + }; + +/* Paper tape reader: IOT routine */ + +int32 ptr (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* RPE */ + int_enable = int_enable | (INT_PTR+INT_PTP); /* set enable */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 1: /* RSF */ + return (dev_done & INT_PTR)? IOT_SKP + AC: AC; + + case 6: /* RFC!RRB */ + sim_activate (&ptr_unit, ptr_unit.wait); /* activate */ + /* fall through */ + case 2: /* RRB */ + dev_done = dev_done & ~INT_PTR; /* clear flag */ + int_req = int_req & ~INT_PTR; /* clear int req */ + return (AC | ptr_unit.buf); /* or data to AC */ + + case 4: /* RFC */ + sim_activate (&ptr_unit, ptr_unit.wait); + dev_done = dev_done & ~INT_PTR; /* clear flag */ + int_req = int_req & ~INT_PTR; /* clear int req */ + return AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if ((temp = getc (ptr_unit.fileref)) == EOF) { + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) + sim_printf ("PTR end of file\n"); + else return SCPE_OK; + } + else sim_perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; + } +dev_done = dev_done | INT_PTR; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +ptr_unit.buf = temp & 0377; +ptr_unit.pos = ptr_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_unit.buf = 0; +dev_done = dev_done & ~INT_PTR; /* clear done, int */ +int_req = int_req & ~INT_PTR; +int_enable = int_enable | INT_PTR; /* set enable */ +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* PCE */ + int_enable = int_enable & ~(INT_PTR+INT_PTP); /* clear enables */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 1: /* PSF */ + return (dev_done & INT_PTP)? IOT_SKP + AC: AC; + + case 2: /* PCF */ + dev_done = dev_done & ~INT_PTP; /* clear flag */ + int_req = int_req & ~INT_PTP; /* clear int req */ + return AC; + + case 6: /* PLS */ + dev_done = dev_done & ~INT_PTP; /* clear flag */ + int_req = int_req & ~INT_PTP; /* clear int req */ + case 4: /* PPC */ + ptp_unit.buf = AC & 0377; /* load punch buf */ + sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ + return AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_PTP; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { + sim_perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; + } +ptp_unit.pos = ptp_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; +dev_done = dev_done & ~INT_PTP; /* clear done, int */ +int_req = int_req & ~INT_PTP; +int_enable = int_enable | INT_PTP; /* set enable */ +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 07756 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 06014, /* 7756, RFC */ + 06011, /* 7757, LOOP, RSF */ + 05357, /* JMP .-1 */ + 06016, /* RFC RRB */ + 07106, /* CLL RTL*/ + 07006, /* RTL */ + 07510, /* SPA*/ + 05374, /* JMP 7774 */ + 07006, /* RTL */ + 06011, /* RSF */ + 05367, /* JMP .-1 */ + 06016, /* RFC RRB */ + 07420, /* SNL */ + 03776, /* DCA I 7776 */ + 03376, /* 7774, DCA 7776 */ + 05357, /* JMP 7757 */ + 00000, /* 7776, 0 */ + 05301 /* 7777, JMP 7701 */ + }; + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +size_t i; +extern uint16 M[]; + +if (ptr_dib.dev != DEV_PTR) /* only std devno */ + return STOP_NOTSTD; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = boot_rom[i]; +cpu_set_bootpc (BOOT_START); +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_rf.c Index: src/SIMH/PDP8/pdp8_rf.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_rf.c @@ -0,0 +1,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 + +#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ +#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ +#define UNIT_M_PLAT 03 +#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) + +/* Constants */ + +#define RF_NUMWD 2048 /* words/track */ +#define RF_NUMTR 128 /* tracks/disk */ +#define RF_DKSIZE (RF_NUMTR * RF_NUMWD) /* words/disk */ +#define RF_NUMDK 4 /* disks/controller */ +#define RF_WC 07750 /* word count */ +#define RF_MA 07751 /* mem address */ +#define RF_WMASK (RF_NUMWD - 1) /* word mask */ + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ +#define RF_READ 2 /* read */ +#define RF_WRITE 4 /* write */ + +/* Status register */ + +#define RFS_PCA 04000 /* photocell status */ +#define RFS_DRE 02000 /* data req enable */ +#define RFS_WLS 01000 /* write lock status */ +#define RFS_EIE 00400 /* error int enable */ +#define RFS_PIE 00200 /* photocell int enb */ +#define RFS_CIE 00100 /* done int enable */ +#define RFS_MEX 00070 /* memory extension */ +#define RFS_DRL 00004 /* data late error */ +#define RFS_NXD 00002 /* non-existent disk */ +#define RFS_PER 00001 /* parity error */ +#define RFS_ERR (RFS_WLS + RFS_DRL + RFS_NXD + RFS_PER) +#define RFS_V_MEX 3 + +#define GET_MEX(x) (((x) & RFS_MEX) << (12 - RFS_V_MEX)) +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) RF_NUMWD))) +#define UPDATE_PCELL if (GET_POS(rf_time) < 6) rf_sta = rf_sta | RFS_PCA; \ + else rf_sta = rf_sta & ~RFS_PCA +#define RF_INT_UPDATE if ((rf_done && (rf_sta & RFS_CIE)) || \ + ((rf_sta & RFS_ERR) && (rf_sta & RFS_EIE)) || \ + ((rf_sta & RFS_PCA) && (rf_sta & RFS_PIE))) \ + int_req = int_req | INT_RF; \ + else int_req = int_req & ~INT_RF + +extern uint16 M[]; +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; + +int32 rf_sta = 0; /* status register */ +int32 rf_da = 0; /* disk address */ +int32 rf_done = 0; /* done flag */ +int32 rf_wlk = 0; /* write lock */ +int32 rf_time = 10; /* inter-word time */ +int32 rf_burst = 1; /* burst mode flag */ +int32 rf_stopioe = 1; /* stop on error */ + +int32 rf60 (int32 IR, int32 AC); +int32 rf61 (int32 IR, int32 AC); +int32 rf62 (int32 IR, int32 AC); +int32 rf64 (int32 IR, int32 AC); +t_stat rf_svc (UNIT *uptr); +t_stat pcell_svc (UNIT *uptr); +t_stat rf_reset (DEVICE *dptr); +t_stat rf_boot (int32 unitno, DEVICE *dptr); +t_stat rf_attach (UNIT *uptr, CONST char *cptr); +t_stat rf_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); + +/* RF08 data structures + + rf_dev RF device descriptor + rf_unit RF unit descriptor + pcell_unit photocell timing unit (orphan) + rf_reg RF register list +*/ + +DIB rf_dib = { DEV_RF, 5, { &rf60, &rf61, &rf62, NULL, &rf64 } }; + +UNIT rf_unit = { + UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+ + UNIT_BUFABLE+UNIT_MUSTBUF, RF_DKSIZE) + }; + +UNIT pcell_unit = { UDATA (&pcell_svc, 0, 0) }; + +REG rf_reg[] = { + { ORDATAD (STA, rf_sta, 12, "status") }, + { ORDATAD (DA, rf_da, 20, "low order disk address") }, + { ORDATAD (WC, M[RF_WC], 12, "word count (in memory)"), REG_FIT }, + { ORDATAD (MA, M[RF_MA], 12, "memory address (in memory)"), REG_FIT }, + { FLDATAD (DONE, rf_done, 0, "device done flag") }, + { FLDATAD (INT, int_req, INT_V_RF, "interrupt pending flag") }, + { ORDATAD (WLK, rf_wlk, 32, "write lock switches") }, + { DRDATAD (TIME, rf_time, 24, "rotational delay, per word"), REG_NZ + PV_LEFT }, + { FLDATAD (BURST, rf_burst, 0, "burst flag") }, + { FLDATAD (STOP_IOE, rf_stopioe, 0, "stop on I/O error") }, + { DRDATA (CAPAC, rf_unit.capac, 21), REG_HRO }, + { ORDATA (DEVNUM, rf_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rf_mod[] = { + { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size }, + { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size }, + { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size }, + { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size }, + { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE rf_dev = { + "RF", &rf_unit, rf_reg, rf_mod, + 1, 8, 20, 1, 8, 12, + NULL, NULL, &rf_reset, + &rf_boot, &rf_attach, NULL, + &rf_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 rf60 (int32 IR, int32 AC) +{ +int32 t; +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) { /* DCMA */ + rf_da = rf_da & ~07777; /* clear DAR<8:19> */ + rf_done = 0; /* clear done */ + rf_sta = rf_sta & ~RFS_ERR; /* clear errors */ + RF_INT_UPDATE; /* update int req */ + } +if (pulse & 6) { /* DMAR, DMAW */ + rf_da = rf_da | AC; /* DAR<8:19> |= AC */ + rf_unit.FUNC = pulse & ~1; /* save function */ + t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new loc */ + if (t < 0) /* wrap around? */ + t = t + RF_NUMWD; + sim_activate (&rf_unit, t * rf_time); /* schedule op */ + AC = 0; /* clear AC */ + } +return AC; +} + +int32 rf61 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +switch (pulse) { /* decode IR<9:11> */ + + case 1: /* DCIM */ + rf_sta = rf_sta & 07007; /* clear STA<3:8> */ + int_req = int_req & ~INT_RF; /* clear int req */ + sim_cancel (&pcell_unit); /* cancel photocell */ + return AC; + + case 2: /* DSAC */ + return ((rf_da & RF_WMASK) == GET_POS (rf_time))? IOT_SKP: 0; + + case 5: /* DIML */ + rf_sta = (rf_sta & 07007) | (AC & 0770); /* STA<3:8> <- AC */ + if (rf_sta & RFS_PIE) /* photocell int? */ + sim_activate (&pcell_unit, (RF_NUMWD - GET_POS (rf_time)) * + rf_time); + else sim_cancel (&pcell_unit); + RF_INT_UPDATE; /* update int req */ + return 0; /* clear AC */ + + case 6: /* DIMA */ + return rf_sta; /* AC <- STA<0:11> */ + } + +return AC; +} + +int32 rf62 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) { /* DFSE */ + if (rf_sta & RFS_ERR) + AC = AC | IOT_SKP; + } +if (pulse & 2) { /* DFSC */ + if (pulse & 4) /* for DMAC */ + AC = AC & ~07777; + else if (rf_done) + AC = AC | IOT_SKP; + } +if (pulse & 4) /* DMAC */ + AC = AC | (rf_da & 07777); +return AC; +} + +int32 rf64 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +switch (pulse) { /* decode IR<9:11> */ + + case 1: /* DCXA */ + rf_da = rf_da & 07777; /* clear DAR<0:7> */ + break; + + case 3: /* DXAL */ + rf_da = rf_da & 07777; /* clear DAR<0:7> */ + case 2: /* DXAL w/o clear */ + rf_da = rf_da | ((AC & 0377) << 12); /* DAR<0:7> |= AC */ + AC = 0; /* clear AC */ + break; + + case 5: /* DXAC */ + AC = 0; /* clear AC */ + case 4: /* DXAC w/o clear */ + AC = AC | ((rf_da >> 12) & 0377); /* AC |= DAR<0:7> */ + break; + + default: + AC = (stop_inst << IOT_V_REASON) + AC; + break; + } /* end switch */ + +if ((uint32) rf_da >= rf_unit.capac) + rf_sta = rf_sta | RFS_NXD; +else rf_sta = rf_sta & ~RFS_NXD; +RF_INT_UPDATE; +return AC; +} + +/* Unit service + + Note that for reads and writes, memory addresses wrap around in the + current field. This code assumes the entire disk is buffered. +*/ + +t_stat rf_svc (UNIT *uptr) +{ +int32 pa, t, mex; +int16 *fbuf = (int16 *) uptr->filebuf; + +UPDATE_PCELL; /* update photocell */ +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + rf_sta = rf_sta | RFS_NXD; + rf_done = 1; + RF_INT_UPDATE; /* update int req */ + return IORETURN (rf_stopioe, SCPE_UNATT); + } + +mex = GET_MEX (rf_sta); +do { + if ((uint32) rf_da >= rf_unit.capac) { /* disk overflow? */ + rf_sta = rf_sta | RFS_NXD; + break; + } + M[RF_WC] = (M[RF_WC] + 1) & 07777; /* incr word count */ + M[RF_MA] = (M[RF_MA] + 1) & 07777; /* incr mem addr */ + pa = mex | M[RF_MA]; /* add extension */ + if (uptr->FUNC == RF_READ) { /* read? */ + if (MEM_ADDR_OK (pa)) /* if !nxm */ + M[pa] = fbuf[rf_da]; /* read word */ + } + else { /* write */ + t = ((rf_da >> 15) & 030) | ((rf_da >> 14) & 07); + if ((rf_wlk >> t) & 1) /* write locked? */ + rf_sta = rf_sta | RFS_WLS; + else { /* not locked */ + fbuf[rf_da] = M[pa]; /* write word */ + if (((uint32) rf_da) >= uptr->hwmark) + uptr->hwmark = rf_da + 1; + } + } + rf_da = (rf_da + 1) & 03777777; /* incr disk addr */ + } while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */ + +if ((M[RF_WC] != 0) && ((rf_sta & RFS_ERR) == 0)) /* more to do? */ + sim_activate (&rf_unit, rf_time); /* sched next */ +else { + rf_done = 1; /* done */ + RF_INT_UPDATE; /* update int req */ + } +return SCPE_OK; +} + +/* Photocell unit service */ + +t_stat pcell_svc (UNIT *uptr) +{ +rf_sta = rf_sta | RFS_PCA; /* set photocell */ +if (rf_sta & RFS_PIE) { /* int enable? */ + sim_activate (&pcell_unit, RF_NUMWD * rf_time); + int_req = int_req | INT_RF; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat rf_reset (DEVICE *dptr) +{ +rf_sta = rf_da = 0; +rf_done = 1; +int_req = int_req & ~INT_RF; /* clear interrupt */ +sim_cancel (&rf_unit); +sim_cancel (&pcell_unit); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define OS8_START 07750 +#define OS8_LEN (sizeof (os8_rom) / sizeof (int16)) +#define DM4_START 00200 +#define DM4_LEN (sizeof (dm4_rom) / sizeof (int16)) + +static const uint16 os8_rom[] = { + 07600, /* 7750, CLA CLL ; also word count */ + 06603, /* 7751, DMAR ; also address */ + 06622, /* 7752, DFSC ; done? */ + 05352, /* 7753, JMP .-1 ; no */ + 05752 /* 7754, JMP @.-2 ; enter boot */ + }; + +static const uint16 dm4_rom[] = { + 00200, 07600, /* 0200, CLA CLL */ + 00201, 06603, /* 0201, DMAR ; read */ + 00202, 06622, /* 0202, DFSC ; done? */ + 00203, 05202, /* 0203, JMP .-1 ; no */ + 00204, 05600, /* 0204, JMP @.-4 ; enter boot */ + 07750, 07576, /* 7750, 7576 ; word count */ + 07751, 07576 /* 7751, 7576 ; address */ + }; + +t_stat rf_boot (int32 unitno, DEVICE *dptr) +{ +size_t i; + +if (rf_dib.dev != DEV_RF) /* only std devno */ + return STOP_NOTSTD; +if (sim_switches & SWMASK ('D')) { + for (i = 0; i < DM4_LEN; i = i + 2) + M[dm4_rom[i]] = dm4_rom[i + 1]; + cpu_set_bootpc (DM4_START); + } +else { + for (i = 0; i < OS8_LEN; i++) + M[OS8_START + i] = os8_rom[i]; + cpu_set_bootpc (OS8_START); + } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rf_attach (UNIT *uptr, CONST char *cptr) +{ +uint32 sz, p; +uint32 ds_bytes = RF_DKSIZE * sizeof (int16); + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + p = (sz + ds_bytes - 1) / ds_bytes; + if (p >= RF_NUMDK) + p = RF_NUMDK - 1; + uptr->flags = (uptr->flags & ~UNIT_PLAT) | + (p << UNIT_V_PLAT); + } +uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE; +return attach_unit (uptr, cptr); +} + +/* Change disk size */ + +t_stat rf_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +if (val < 0) + return SCPE_IERR; +if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; +uptr->capac = UNIT_GETP (val) * RF_DKSIZE; +uptr->flags = uptr->flags & ~UNIT_AUTO; +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_rk.c Index: src/SIMH/PDP8/pdp8_rk.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_rk.c @@ -0,0 +1,463 @@ +/* pdp8_rk.c: RK8E cartridge disk simulator + + Copyright (c) 1993-2013, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rk RK8E/RK05 cartridge disk + + 17-Sep-13 RMS Changed to use central set_bootpc routine + 18-Mar-13 RMS Raised RK_MIN so that RKLFMT will work (Mark Pizzolato) + 25-Apr-03 RMS Revised for extended file support + 04-Oct-02 RMS Added DIB, device number support + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Converted FLG to array, made register names consistent + 25-Apr-01 RMS Added device enable/disable support + 29-Jun-96 RMS Added unit enable/disable support +*/ + +#include "pdp8_defs.h" + +/* Constants */ + +#define RK_NUMSC 16 /* sectors/surface */ +#define RK_NUMSF 2 /* surfaces/cylinder */ +#define RK_NUMCY 203 /* cylinders/drive */ +#define RK_NUMWD 256 /* words/sector */ +#define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) + /* words/drive */ +#define RK_NUMDR 4 /* drives/controller */ +#define RK_M_NUMDR 03 + +/* Flags in the unit flags word */ + +#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */ +#define UNIT_HWLK (1 << UNIT_V_HWLK) +#define UNIT_SWLK (1 << UNIT_V_SWLK) +#define UNIT_WPRT (UNIT_HWLK|UNIT_SWLK|UNIT_RO) /* write protect */ + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ + +/* Status register */ + +#define RKS_DONE 04000 /* transfer done */ +#define RKS_HMOV 02000 /* heads moving */ +#define RKS_SKFL 00400 /* drive seek fail */ +#define RKS_NRDY 00200 /* drive not ready */ +#define RKS_BUSY 00100 /* control busy error */ +#define RKS_TMO 00040 /* timeout error */ +#define RKS_WLK 00020 /* write lock error */ +#define RKS_CRC 00010 /* CRC error */ +#define RKS_DLT 00004 /* data late error */ +#define RKS_STAT 00002 /* drive status error */ +#define RKS_CYL 00001 /* cyl address error */ +#define RKS_ERR (RKS_BUSY+RKS_TMO+RKS_WLK+RKS_CRC+RKS_DLT+RKS_STAT+RKS_CYL) + +/* Command register */ + +#define RKC_M_FUNC 07 /* function */ +#define RKC_READ 0 +#define RKC_RALL 1 +#define RKC_WLK 2 +#define RKC_SEEK 3 +#define RKC_WRITE 4 +#define RKC_WALL 5 +#define RKC_V_FUNC 9 +#define RKC_IE 00400 /* interrupt enable */ +#define RKC_SKDN 00200 /* set done on seek done */ +#define RKC_HALF 00100 /* 128W sector */ +#define RKC_MEX 00070 /* memory extension */ +#define RKC_V_MEX 3 +#define RKC_M_DRV 03 /* drive select */ +#define RKC_V_DRV 1 +#define RKC_CYHI 00001 /* high cylinder addr */ + +#define GET_FUNC(x) (((x) >> RKC_V_FUNC) & RKC_M_FUNC) +#define GET_DRIVE(x) (((x) >> RKC_V_DRV) & RKC_M_DRV) +#define GET_MEX(x) (((x) & RKC_MEX) << (12 - RKC_V_MEX)) + +/* Disk address */ + +#define RKD_V_SECT 0 /* sector */ +#define RKD_M_SECT 017 +#define RKD_V_SUR 4 /* surface */ +#define RKD_M_SUR 01 +#define RKD_V_CYL 5 /* cylinder */ +#define RKD_M_CYL 0177 +#define GET_CYL(x,y) ((((x) & RKC_CYHI) << (12-RKD_V_CYL)) | \ + (((y) >> RKD_V_CYL) & RKD_M_CYL)) +#define GET_DA(x,y) ((((x) & RKC_CYHI) << 12) | y) + +/* Reset commands */ + +#define RKX_CLS 0 /* clear status */ +#define RKX_CLC 1 /* clear control */ +#define RKX_CLD 2 /* clear drive */ +#define RKX_CLSA 3 /* clear status alt */ + +#define RK_INT_UPDATE if (((rk_sta & (RKS_DONE + RKS_ERR)) != 0) && \ + ((rk_cmd & RKC_IE) != 0)) \ + int_req = int_req | INT_RK; \ + else int_req = int_req & ~INT_RK +#define RK_MIN 50 +#define MAX(x,y) (((x) > (y))? (x): (y)) + +extern uint16 M[]; +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; + +int32 rk_busy = 0; /* controller busy */ +int32 rk_sta = 0; /* status register */ +int32 rk_cmd = 0; /* command register */ +int32 rk_da = 0; /* disk address */ +int32 rk_ma = 0; /* memory address */ +int32 rk_swait = 10, rk_rwait = 10; /* seek, rotate wait */ +int32 rk_stopioe = 1; /* stop on error */ + +int32 rk (int32 IR, int32 AC); +t_stat rk_svc (UNIT *uptr); +t_stat rk_reset (DEVICE *dptr); +t_stat rk_boot (int32 unitno, DEVICE *dptr); +void rk_go (int32 function, int32 cylinder); + +/* RK-8E data structures + + rk_dev RK device descriptor + rk_unit RK unit list + rk_reg RK register list + rk_mod RK modifiers list +*/ + +DIB rk_dib = { DEV_RK, 1, { &rk } }; + +UNIT rk_unit[] = { + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) } + }; + +REG rk_reg[] = { + { ORDATAD (RKSTA, rk_sta, 12, "status") }, + { ORDATAD (RKCMD, rk_cmd, 12, "disk command") }, + { ORDATAD (RKDA, rk_da, 12, "disk address") }, + { ORDATAD (RKMA, rk_ma, 12, "current memory address") }, + { FLDATAD (BUSY, rk_busy, 0, "control busy flag") }, + { FLDATAD (INT, int_req, INT_V_RK, "interrupt pending flag") }, + { DRDATAD (STIME, rk_swait, 24, "seek time, per cylinder"), PV_LEFT }, + { DRDATAD (RTIME, rk_rwait, 24, "rotational delay"), PV_LEFT }, + { FLDATAD (STOP_IOE, rk_stopioe, 0, "stop on I/O error") }, + { ORDATA (DEVNUM, rk_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rk_mod[] = { + { UNIT_HWLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE rk_dev = { + "RK", rk_unit, rk_reg, rk_mod, + RK_NUMDR, 8, 24, 1, 8, 12, + NULL, NULL, &rk_reset, + &rk_boot, NULL, NULL, + &rk_dib, DEV_DISABLE + }; + +/* IOT routine */ + +int32 rk (int32 IR, int32 AC) +{ +int32 i; +UNIT *uptr; + +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* unused */ + return (stop_inst << IOT_V_REASON) + AC; + + case 1: /* DSKP */ + return (rk_sta & (RKS_DONE + RKS_ERR))? /* skip on done, err */ + IOT_SKP + AC: AC; + + case 2: /* DCLR */ + rk_sta = 0; /* clear status */ + switch (AC & 03) { /* decode AC<10:11> */ + + case RKX_CLS: /* clear status */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + case RKX_CLSA: /* clear status alt */ + break; + + case RKX_CLC: /* clear control */ + rk_cmd = rk_busy = 0; /* clear registers */ + rk_ma = rk_da = 0; + for (i = 0; i < RK_NUMDR; i++) + sim_cancel (&rk_unit[i]); + break; + + case RKX_CLD: /* reset drive */ + if (rk_busy != 0) + rk_sta = rk_sta | RKS_BUSY; + else rk_go (RKC_SEEK, 0); /* seek to 0 */ + break; + } /* end switch AC */ + break; + + case 3: /* DLAG */ + if (rk_busy != 0) + rk_sta = rk_sta | RKS_BUSY; + else { + rk_da = AC; /* load disk addr */ + rk_go (GET_FUNC (rk_cmd), GET_CYL (rk_cmd, rk_da)); + } + break; + + case 4: /* DLCA */ + if (rk_busy != 0) + rk_sta = rk_sta | RKS_BUSY; + else rk_ma = AC; /* load curr addr */ + break; + + case 5: /* DRST */ + uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */ + rk_sta = rk_sta & ~(RKS_HMOV + RKS_NRDY); /* clear dynamic */ + if ((uptr->flags & UNIT_ATT) == 0) + rk_sta = rk_sta | RKS_NRDY; + if (sim_is_active (uptr)) + rk_sta = rk_sta | RKS_HMOV; + return rk_sta; + + case 6: /* DLDC */ + if (rk_busy != 0) + rk_sta = rk_sta | RKS_BUSY; + else { + rk_cmd = AC; /* load command */ + rk_sta = 0; /* clear status */ + } + break; + + case 7: /* DMAN */ + break; + } /* end case pulse */ + +RK_INT_UPDATE; /* update int req */ +return 0; /* clear AC */ +} + +/* Initiate new function + + Called with function, cylinder, to allow recalibrate as well as + load and go to be processed by this routine. + + Assumes that the controller is idle, and that updating of interrupt + request will be done by the caller. +*/ + +void rk_go (int32 func, int32 cyl) +{ +int32 t; +UNIT *uptr; + +if (func == RKC_RALL) /* all? use standard */ + func = RKC_READ; +if (func == RKC_WALL) +func = RKC_WRITE; +uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */ +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT; + return; + } +if (sim_is_active (uptr) || (cyl >= RK_NUMCY)) { /* busy or bad cyl? */ + rk_sta = rk_sta | RKS_DONE | RKS_STAT; + return; + } +if ((func == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) { + rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */ + return; + } +if (func == RKC_WLK) { /* write lock? */ + uptr->flags = uptr->flags | UNIT_SWLK; + rk_sta = rk_sta | RKS_DONE; + return; + } +t = abs (cyl - uptr->CYL) * rk_swait; /* seek time */ +if (func == RKC_SEEK) { /* seek? */ + sim_activate (uptr, MAX (RK_MIN, t)); /* schedule */ + rk_sta = rk_sta | RKS_DONE; /* set done */ + } +else { + sim_activate (uptr, t + rk_rwait); /* schedule */ + rk_busy = 1; /* set busy */ + } +uptr->FUNC = func; /* save func */ +uptr->CYL = cyl; /* put on cylinder */ +return; +} + +/* Unit service + + If seek, complete seek command + Else complete data transfer command + + The unit control block contains the function and cylinder address for + the current command. + + Note that memory addresses wrap around in the current field. +*/ + +static uint16 fill[RK_NUMWD/2] = { 0 }; +t_stat rk_svc (UNIT *uptr) +{ +int32 err, wc, wc1, awc, swc, pa, da; +UNIT *seluptr; + +if (uptr->FUNC == RKC_SEEK) { /* seek? */ + seluptr = rk_dev.units + GET_DRIVE (rk_cmd); /* see if selected */ + if ((uptr == seluptr) && ((rk_cmd & RKC_SKDN) != 0)) { + rk_sta = rk_sta | RKS_DONE; + RK_INT_UPDATE; + } + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* not att? abort */ + rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT; + rk_busy = 0; + RK_INT_UPDATE; + return IORETURN (rk_stopioe, SCPE_UNATT); + } + +if ((uptr->FUNC == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) { + rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */ + rk_busy = 0; + RK_INT_UPDATE; + return SCPE_OK; + } + +pa = GET_MEX (rk_cmd) | rk_ma; /* phys address */ +da = GET_DA (rk_cmd, rk_da) * RK_NUMWD * sizeof (int16);/* disk address */ +swc = wc = (rk_cmd & RKC_HALF)? RK_NUMWD / 2: RK_NUMWD; /* get transfer size */ +if ((wc1 = ((rk_ma + wc) - 010000)) > 0) /* if wrap, limit */ + wc = wc - wc1; +err = fseek (uptr->fileref, da, SEEK_SET); /* locate sector */ + +if ((uptr->FUNC == RKC_READ) && (err == 0) && MEM_ADDR_OK (pa)) { /* read? */ + awc = fxread (&M[pa], sizeof (int16), wc, uptr->fileref); + for ( ; awc < wc; awc++) /* fill if eof */ + M[pa + awc] = 0; + err = ferror (uptr->fileref); + if ((wc1 > 0) && (err == 0)) { /* field wraparound? */ + pa = pa & 070000; /* wrap phys addr */ + awc = fxread (&M[pa], sizeof (int16), wc1, uptr->fileref); + for ( ; awc < wc1; awc++) /* fill if eof */ + M[pa + awc] = 0; + err = ferror (uptr->fileref); + } + } + +if ((uptr->FUNC == RKC_WRITE) && (err == 0)) { /* write? */ + fxwrite (&M[pa], sizeof (int16), wc, uptr->fileref); + err = ferror (uptr->fileref); + if ((wc1 > 0) && (err == 0)) { /* field wraparound? */ + pa = pa & 070000; /* wrap phys addr */ + fxwrite (&M[pa], sizeof (int16), wc1, uptr->fileref); + err = ferror (uptr->fileref); + } + if ((rk_cmd & RKC_HALF) && (err == 0)) { /* fill half sector */ + fxwrite (fill, sizeof (int16), RK_NUMWD/2, uptr->fileref); + err = ferror (uptr->fileref); + } + } + +rk_ma = (rk_ma + swc) & 07777; /* incr mem addr reg */ +rk_sta = rk_sta | RKS_DONE; /* set done */ +rk_busy = 0; +RK_INT_UPDATE; + +if (err != 0) { + sim_perror ("RK I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat rk_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rk_cmd = rk_ma = rk_da = rk_sta = rk_busy = 0; +int_req = int_req & ~INT_RK; /* clear interrupt */ +for (i = 0; i < RK_NUMDR; i++) { /* stop all units */ + uptr = rk_dev.units + i; + sim_cancel (uptr); + uptr->flags = uptr->flags & ~UNIT_SWLK; + uptr->CYL = uptr->FUNC = 0; + } +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 023 +#define BOOT_UNIT 032 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 06007, /* 23, CAF */ + 06744, /* 24, DLCA ; addr = 0 */ + 01032, /* 25, TAD UNIT ; unit no */ + 06746, /* 26, DLDC ; command, unit */ + 06743, /* 27, DLAG ; disk addr, go */ + 01032, /* 30, TAD UNIT ; unit no, for OS */ + 05031, /* 31, JMP . */ + 00000 /* UNIT, 0 ; in bits <9:10> */ + }; + +t_stat rk_boot (int32 unitno, DEVICE *dptr) +{ +size_t i; + +if (rk_dib.dev != DEV_RK) /* only std devno */ + return STOP_NOTSTD; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = boot_rom[i]; +M[BOOT_UNIT] = (unitno & RK_M_NUMDR) << 1; +cpu_set_bootpc (BOOT_START); +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_rl.c Index: src/SIMH/PDP8/pdp8_rl.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_rl.c @@ -0,0 +1,703 @@ +/* pdp8_rl.c: RL8A cartridge disk simulator + + Copyright (c) 1993-2013, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rl RL8A cartridge disk + + 17-Sep-13 RMS Changed to use central set_bootpc routine + 25-Oct-05 RMS Fixed IOT 61 decode bug (David Gesswein) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 04-Jan-04 RMS Changed attach routine to use sim_fsize + 25-Apr-03 RMS Revised for extended file support + 04-Oct-02 RMS Added DIB, device number support + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Cloned from RL11 + + The RL8A is a four drive cartridge disk subsystem. An RL01 drive + consists of 256 cylinders, each with 2 surfaces containing 40 sectors + of 256 bytes. An RL02 drive has 512 cylinders. + + The RL8A controller has several serious complications. + - Seeking is relative to the current disk address; this requires + keeping accurate track of the current cylinder. + - The RL8A will not switch heads or cross cylinders during transfers. + - The RL8A operates in 8b and 12b mode, like the RX8E; in 12b mode, it + packs 2 12b words into 3 bytes, creating a 170 "word" sector with + one wasted byte. Multi-sector transfers in 12b mode don't work. +*/ + +#include "pdp8_defs.h" + +/* Constants */ + +#define RL_NUMBY 256 /* 8b bytes/sector */ +#define RL_NUMSC 40 /* sectors/surface */ +#define RL_NUMSF 2 /* surfaces/cylinder */ +#define RL_NUMCY 256 /* cylinders/drive */ +#define RL_NUMDR 4 /* drives/controller */ +#define RL_MAXFR (1 << 12) /* max transfer */ +#define RL01_SIZE (RL_NUMCY*RL_NUMSF*RL_NUMSC*RL_NUMBY) /* words/drive */ +#define RL02_SIZE (RL01_SIZE * 2) /* words/drive */ +#define RL_BBMAP 014 /* sector for bblk map */ +#define RL_BBID 0123 /* ID for bblk map */ + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write lock */ +#define UNIT_V_RL02 (UNIT_V_UF + 1) /* RL01 vs RL02 */ +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize enable */ +#define UNIT_V_DUMMY (UNIT_V_UF + 3) /* dummy flag */ +#define UNIT_DUMMY (1u << UNIT_V_DUMMY) +#define UNIT_WLK (1u << UNIT_V_WLK) +#define UNIT_RL02 (1u << UNIT_V_RL02) +#define UNIT_AUTO (1u << UNIT_V_AUTO) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* Parameters in the unit descriptor */ + +#define TRK u3 /* current cylinder */ +#define STAT u4 /* status */ + +/* RLDS, NI = not implemented, * = kept in STAT, ^ = kept in TRK */ + +#define RLDS_LOAD 0 /* no cartridge */ +#define RLDS_LOCK 5 /* lock on */ +#define RLDS_BHO 0000010 /* brushes home NI */ +#define RLDS_HDO 0000020 /* heads out NI */ +#define RLDS_CVO 0000040 /* cover open NI */ +#define RLDS_HD 0000100 /* head select ^ */ +#define RLDS_RL02 0000200 /* RL02 */ +#define RLDS_DSE 0000400 /* drv sel err NI */ +#define RLDS_VCK 0001000 /* vol check * */ +#define RLDS_WGE 0002000 /* wr gate err * */ +#define RLDS_SPE 0004000 /* spin err * */ +#define RLDS_STO 0010000 /* seek time out NI */ +#define RLDS_WLK 0020000 /* wr locked */ +#define RLDS_HCE 0040000 /* hd curr err NI */ +#define RLDS_WDE 0100000 /* wr data err NI */ +#define RLDS_ATT (RLDS_HDO+RLDS_BHO+RLDS_LOCK) /* att status */ +#define RLDS_UNATT (RLDS_CVO+RLDS_LOAD) /* unatt status */ +#define RLDS_ERR (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \ + RLDS_VCK+RLDS_DSE) /* errors bits */ + +/* RLCSA, seek = offset/rw = address (also uptr->TRK) */ + +#define RLCSA_DIR 04000 /* direction */ +#define RLCSA_HD 02000 /* head select */ +#define RLCSA_CYL 00777 /* cyl offset */ +#define GET_CYL(x) ((x) & RLCSA_CYL) +#define GET_TRK(x) ((((x) & RLCSA_CYL) * RL_NUMSF) + \ + (((x) & RLCSA_HD)? 1: 0)) +#define GET_DA(x) ((GET_TRK(x) * RL_NUMSC) + rlsa) + +/* RLCSB, function/unit select */ + +#define RLCSB_V_FUNC 0 /* function */ +#define RLCSB_M_FUNC 07 +#define RLCSB_MNT 0 +#define RLCSB_CLRD 1 +#define RLCSB_GSTA 2 +#define RLCSB_SEEK 3 +#define RLCSB_RHDR 4 +#define RLCSB_WRITE 5 +#define RLCSB_READ 6 +#define RLCSB_RNOHDR 7 +#define RLCSB_V_MEX 3 /* memory extension */ +#define RLCSB_M_MEX 07 +#define RLCSB_V_DRIVE 6 /* drive */ +#define RLCSB_M_DRIVE 03 +#define RLCSB_V_IE 8 /* int enable */ +#define RLCSB_IE (1u << RLCSB_V_IE) +#define RLCSB_8B 01000 /* 12b/8b */ +#define RCLS_MNT 02000 /* maint NI */ +#define RLCSB_RW 0001777 /* read/write */ +#define GET_FUNC(x) (((x) >> RLCSB_V_FUNC) & RLCSB_M_FUNC) +#define GET_MEX(x) (((x) >> RLCSB_V_MEX) & RLCSB_M_MEX) +#define GET_DRIVE(x) (((x) >> RLCSB_V_DRIVE) & RLCSB_M_DRIVE) + +/* RLSA, disk sector */ + +#define RLSA_V_SECT 6 /* sector */ +#define RLSA_M_SECT 077 +#define GET_SECT(x) (((x) >> RLSA_V_SECT) & RLSA_M_SECT) + +/* RLER, error register */ + +#define RLER_DRDY 00001 /* drive ready */ +#define RLER_DRE 00002 /* drive error */ +#define RLER_HDE 01000 /* header error */ +#define RLER_INCMP 02000 /* incomplete */ +#define RLER_ICRC 04000 /* CRC error */ +#define RLER_MASK 07003 + +/* RLSI, silo register, used only in read header */ + +#define RLSI_V_TRK 6 /* track */ + +extern uint16 M[]; +extern int32 int_req; +extern UNIT cpu_unit; + +uint8 *rlxb = NULL; /* xfer buffer */ +int32 rlcsa = 0; /* control/status A */ +int32 rlcsb = 0; /* control/status B */ +int32 rlma = 0; /* memory address */ +int32 rlwc = 0; /* word count */ +int32 rlsa = 0; /* sector address */ +int32 rler = 0; /* error register */ +int32 rlsi = 0, rlsi1 = 0, rlsi2 = 0; /* silo queue */ +int32 rl_lft = 0; /* silo left/right */ +int32 rl_done = 0; /* done flag */ +int32 rl_erf = 0; /* error flag */ +int32 rl_swait = 10; /* seek wait */ +int32 rl_rwait = 10; /* rotate wait */ +int32 rl_stopioe = 1; /* stop on error */ + +int32 rl60 (int32 IR, int32 AC); +int32 rl61 (int32 IR, int32 AC); +t_stat rl_svc (UNIT *uptr); +t_stat rl_reset (DEVICE *dptr); +void rl_set_done (int32 error); +t_stat rl_boot (int32 unitno, DEVICE *dptr); +t_stat rl_attach (UNIT *uptr, CONST char *cptr); +t_stat rl_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat rl_set_bad (UNIT *uptr, int32 val, CONST char *cptr, void *desc); + +/* RL8A data structures + + rl_dev RL device descriptor + rl_unit RL unit list + rl_reg RL register list + rl_mod RL modifier list +*/ + +DIB rl_dib = { DEV_RL, 2, { &rl60, &rl61 } }; + +UNIT rl_unit[] = { + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE, RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE, RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE, RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE, RL01_SIZE) } + }; + +REG rl_reg[] = { + { ORDATAD (RLCSA, rlcsa, 12, "control/status A") }, + { ORDATAD (RLCSB, rlcsb, 12, "control/status B") }, + { ORDATAD (RLMA, rlma, 12, "memory address") }, + { ORDATAD (RLWC, rlwc, 12, "word count") }, + { ORDATAD (RLSA, rlsa, 6, "sector address") }, + { ORDATAD (RLER, rler, 12, "error flags") }, + { ORDATAD (RLSI, rlsi, 16, "silo top word") }, + { ORDATAD (RLSI1, rlsi1, 16, "silo second word") }, + { ORDATAD (RLSI2, rlsi2, 16, "silo third word") }, + { FLDATAD (RLSIL, rl_lft, 0, "silo read left/right flag") }, + { FLDATAD (INT, int_req, INT_V_RL, "interrupt request") }, + { FLDATAD (DONE, rl_done, INT_V_RL, "done flag") }, + { FLDATA (IE, rlcsb, RLCSB_V_IE) }, + { FLDATAD (ERR, rl_erf, 0, "composite error flag") }, + { DRDATAD (STIME, rl_swait, 24, "seek time, per cylinder"), PV_LEFT }, + { DRDATAD (RTIME, rl_rwait, 24, "rotational delay"), PV_LEFT }, + { URDATA (CAPAC, rl_unit[0].capac, 10, T_ADDR_W, 0, + RL_NUMDR, PV_LEFT + REG_HRO) }, + { FLDATAD (STOP_IOE, rl_stopioe, 0, "stop on I/O error") }, + { ORDATA (DEVNUM, rl_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rl_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad }, + { (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL }, + { (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size }, + { (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE rl_dev = { + "RL", rl_unit, rl_reg, rl_mod, + RL_NUMDR, 8, 24, 1, 8, 8, + NULL, NULL, &rl_reset, + &rl_boot, &rl_attach, NULL, + &rl_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 rl60 (int32 IR, int32 AC) +{ +int32 curr, offs, newc, maxc; +UNIT *uptr; + +switch (IR & 07) { /* case IR<9:11> */ + + case 0: /* RLDC */ + rl_reset (&rl_dev); /* reset device */ + break; + + case 1: /* RLSD */ + if (rl_done) /* skip if done */ + AC = IOT_SKP; + else AC = 0; + rl_done = 0; /* clear done */ + int_req = int_req & ~INT_RL; /* clear intr */ + return AC; + + case 2: /* RLMA */ + rlma = AC; + break; + + case 3: /* RLCA */ + rlcsa = AC; + break; + + case 4: /* RLCB */ + rlcsb = AC; + rl_done = 0; /* clear done */ + rler = rl_erf = 0; /* clear errors */ + int_req = int_req & ~INT_RL; /* clear intr */ + rl_lft = 0; /* clear silo ptr */ + uptr = rl_dev.units + GET_DRIVE (rlcsb); /* select unit */ + switch (GET_FUNC (rlcsb)) { /* case on func */ + + case RLCSB_CLRD: /* clear drive */ + uptr->STAT = uptr->STAT & ~RLDS_ERR; /* clear errors */ + case RLCSB_MNT: /* mnt */ + rl_set_done (0); + break; + + case RLCSB_SEEK: /* seek */ + curr = GET_CYL (uptr->TRK); /* current cylinder */ + offs = GET_CYL (rlcsa); /* offset */ + if (rlcsa & RLCSA_DIR) { /* in or out? */ + newc = curr + offs; /* out */ + maxc = (uptr->flags & UNIT_RL02)? + RL_NUMCY * 2: RL_NUMCY; + if (newc >= maxc) newc = maxc - 1; + } + else { + newc = curr - offs; /* in */ + if (newc < 0) newc = 0; + } + uptr->TRK = newc | (rlcsa & RLCSA_HD); + sim_activate (uptr, rl_swait * abs (newc - curr)); + break; + + default: /* data transfer */ + sim_activate (uptr, rl_swait); /* activate unit */ + break; + } /* end switch func */ + break; + + case 5: /* RLSA */ + rlsa = GET_SECT (AC); + break; + + case 6: /* spare */ + return 0; + + case 7: /* RLWC */ + rlwc = AC; + break; + } /* end switch pulse */ + +return 0; /* clear AC */ +} + +int32 rl61 (int32 IR, int32 AC) +{ +int32 dat; +UNIT *uptr; + +switch (IR & 07) { /* case IR<9:11> */ + + case 0: /* RRER */ + uptr = rl_dev.units + GET_DRIVE (rlcsb); /* select unit */ + if (!sim_is_active (uptr) && /* update drdy */ + (uptr->flags & UNIT_ATT)) + rler = rler | RLER_DRDY; + else rler = rler & ~RLER_DRDY; + dat = rler & RLER_MASK; + break; + + case 1: /* RRWC */ + dat = rlwc; + break; + + case 2: /* RRCA */ + dat = rlcsa; + break; + + case 3: /* RRCB */ + dat = rlcsb; + break; + + case 4: /* RRSA */ + dat = (rlsa << RLSA_V_SECT) & 07777; + break; + + case 5: /* RRSI */ + if (rl_lft) { /* silo left? */ + dat = (rlsi >> 8) & 0377; /* get left 8b */ + rlsi = rlsi1; /* ripple */ + rlsi1 = rlsi2; + } + else dat = rlsi & 0377; /* get right 8b */ + rl_lft = rl_lft ^ 1; /* change side */ + break; + + case 6: /* spare */ + return AC; + + case 7: /* RLSE */ + if (rl_erf) /* skip if err */ + dat = IOT_SKP | AC; + else dat = AC; + rl_erf = 0; + break; + } /* end switch pulse */ + +return dat; +} + +/* Service unit timeout + + If seek in progress, complete seek command + Else complete data transfer command + + The unit control block contains the function and cylinder for + the current command. +*/ + +t_stat rl_svc (UNIT *uptr) +{ +int32 err, wc, maxc; +int32 i, j, func, da, bc, wbc; +uint32 ma; + +func = GET_FUNC (rlcsb); /* get function */ +if (func == RLCSB_GSTA) { /* get status? */ + rlsi = uptr->STAT | + ((uptr->TRK & RLCSA_HD)? RLDS_HD: 0) | + ((uptr->flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT); + if (uptr->flags & UNIT_RL02) + rlsi = rlsi | RLDS_RL02; + if (uptr->flags & UNIT_WPRT) + rlsi = rlsi | RLDS_WLK; + rlsi2 = rlsi1 = rlsi; + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + uptr->STAT = uptr->STAT | RLDS_SPE; /* spin error */ + rl_set_done (RLER_INCMP); /* flag error */ + return IORETURN (rl_stopioe, SCPE_UNATT); + } + +if ((func == RLCSB_WRITE) && (uptr->flags & UNIT_WPRT)) { + uptr->STAT = uptr->STAT | RLDS_WGE; /* write and locked */ + rl_set_done (RLER_DRE); /* flag error */ + return SCPE_OK; + } + +if (func == RLCSB_SEEK) { /* seek? */ + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if (func == RLCSB_RHDR) { /* read header? */ + rlsi = (GET_TRK (uptr->TRK) << RLSI_V_TRK) | rlsa; + rlsi1 = rlsi2 = 0; + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if (((func != RLCSB_RNOHDR) && (GET_CYL (uptr->TRK) != GET_CYL (rlcsa))) + || (rlsa >= RL_NUMSC)) { /* bad cyl or sector? */ + rl_set_done (RLER_HDE | RLER_INCMP); /* flag error */ + return SCPE_OK; + } + +ma = (GET_MEX (rlcsb) << 12) | rlma; /* get mem addr */ +da = GET_DA (rlcsa) * RL_NUMBY; /* get disk addr */ +wc = 010000 - rlwc; /* get true wc */ +if (rlcsb & RLCSB_8B) { /* 8b mode? */ + bc = wc; /* bytes to xfr */ + maxc = (RL_NUMSC - rlsa) * RL_NUMBY; /* max transfer */ + if (bc > maxc) /* trk ovrun? limit */ + wc = bc = maxc; + } +else { + bc = ((wc * 3) + 1) / 2; /* 12b mode */ + if (bc > RL_NUMBY) { /* > 1 sector */ + bc = RL_NUMBY; /* cap xfer */ + wc = (RL_NUMBY * 2) / 3; + } + } + +err = fseek (uptr->fileref, da, SEEK_SET); + +if ((func >= RLCSB_READ) && (err == 0) && /* read (no hdr)? */ + MEM_ADDR_OK (ma)) { /* valid bank? */ + i = fxread (rlxb, sizeof (int8), bc, uptr->fileref); + err = ferror (uptr->fileref); + for ( ; i < bc; i++) /* fill buffer */ + rlxb[i] = 0; + for (i = j = 0; i < wc; i++) { /* store buffer */ + if (rlcsb & RLCSB_8B) /* 8b mode? */ + M[ma] = rlxb[i] & 0377; /* store */ + else if (i & 1) { /* odd wd 12b? */ + M[ma] = ((rlxb[j + 1] >> 4) & 017) | + (((uint16) rlxb[j + 2]) << 4); + j = j + 3; + } + else M[ma] = rlxb[j] | /* even wd 12b */ + ((((uint16) rlxb[j + 1]) & 017) << 8); + ma = (ma & 070000) + ((ma + 1) & 07777); + } /* end for */ + } /* end if wr */ + +if ((func == RLCSB_WRITE) && (err == 0)) { /* write? */ + for (i = j = 0; i < wc; i++) { /* fetch buffer */ + if (rlcsb & RLCSB_8B) /* 8b mode? */ + rlxb[i] = M[ma] & 0377; /* fetch */ + else if (i & 1) { /* odd wd 12b? */ + rlxb[j + 1] = rlxb[j + 1] | ((M[ma] & 017) << 4); + rlxb[j + 2] = ((M[ma] >> 4) & 0377); + j = j + 3; + } + else { /* even wd 12b */ + rlxb[j] = M[ma] & 0377; + rlxb[j + 1] = (M[ma] >> 8) & 017; + } + ma = (ma & 070000) + ((ma + 1) & 07777); + } /* end for */ + wbc = (bc + (RL_NUMBY - 1)) & ~(RL_NUMBY - 1); /* clr to */ + for (i = bc; i < wbc; i++) /* end of blk */ + rlxb[i] = 0; + fxwrite (rlxb, sizeof (int8), wbc, uptr->fileref); + err = ferror (uptr->fileref); + } /* end write */ + +rlwc = (rlwc + wc) & 07777; /* final word count */ +if (rlwc != 0) /* completed? */ + rler = rler | RLER_INCMP; +rlma = (rlma + wc) & 07777; /* final word addr */ +rlsa = rlsa + ((bc + (RL_NUMBY - 1)) / RL_NUMBY); +rl_set_done (0); + +if (err != 0) { /* error? */ + sim_perror ("RL I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Set done and possibly errors */ + +void rl_set_done (int32 status) +{ +rl_done = 1; +rler = rler | status; +if (rler) + rl_erf = 1; +if (rlcsb & RLCSB_IE) + int_req = int_req | INT_RL; +else int_req = int_req & ~INT_RL; +return; +} + +/* Device reset + + Note that the RL8A does NOT recalibrate its drives on RESET +*/ + +t_stat rl_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rlcsa = rlcsb = rlsa = rler = 0; +rlma = rlwc = 0; +rlsi = rlsi1 = rlsi2 = 0; +rl_lft = 0; +rl_done = 0; +rl_erf = 0; +int_req = int_req & ~INT_RL; +for (i = 0; i < RL_NUMDR; i++) { + uptr = rl_dev.units + i; + sim_cancel (uptr); + uptr->STAT = 0; + } +if (rlxb == NULL) + rlxb = (uint8 *) calloc (RL_MAXFR, sizeof (uint8)); +if (rlxb == NULL) + return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rl_attach (UNIT *uptr, CONST char *cptr) +{ +uint32 p; +t_stat r; + +uptr->capac = (uptr->flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) /* error? */ + return r; +uptr->TRK = 0; /* cyl 0 */ +uptr->STAT = RLDS_VCK; /* new volume */ +if ((p = sim_fsize (uptr->fileref)) == 0) { /* new disk image? */ + if (uptr->flags & UNIT_RO) + return SCPE_OK; + return rl_set_bad (uptr, 0, NULL, NULL); + } +if ((uptr->flags & UNIT_AUTO) == 0) /* autosize? */ + return r; +if (p > (RL01_SIZE * sizeof (int16))) { + uptr->flags = uptr->flags | UNIT_RL02; + uptr->capac = RL02_SIZE; + } +else { + uptr->flags = uptr->flags & ~UNIT_RL02; + uptr->capac = RL01_SIZE; + } +return SCPE_OK; +} + +/* Set size routine */ + +t_stat rl_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; +uptr->capac = (val & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +return SCPE_OK; +} + +/* Factory bad block table creation routine + + This routine writes the OS/8 specific bad block map in track 0, sector 014 (RL_BBMAP): + + words 0 magic number = 0123 (RL_BBID) + words 1-n block numbers + : + words n+1 end of table = 0 + + Inputs: + uptr = pointer to unit + val = ignored + Outputs: + sta = status code +*/ + +t_stat rl_set_bad (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 i, da = RL_BBMAP * RL_NUMBY; + +if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; +if (uptr->flags & UNIT_RO) + return SCPE_RO; +if (!get_yn ("Create bad block table? [N]", FALSE)) + return SCPE_OK; +if (fseek (uptr->fileref, da, SEEK_SET)) + return SCPE_IOERR; +rlxb[0] = RL_BBID; +for (i = 1; i < RL_NUMBY; i++) + rlxb[i] = 0; +fxwrite (rlxb, sizeof (uint8), RL_NUMBY, uptr->fileref); +if (ferror (uptr->fileref)) + return SCPE_IOERR; +return SCPE_OK; +} + +/* Bootstrap */ + +#define BOOT_START 1 /* start */ +#define BOOT_UNIT 02006 /* unit number */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 06600, /* BT, RLDC ; reset */ + 07201, /* 02, CLA IAC ; clr drv = 1 */ + 04027, /* 03, JMS GO ; do io */ + 01004, /* 04, TAD 4 ; rd hdr fnc */ + 04027, /* 05, JMS GO ; do io */ + 06615, /* 06, RRSI ; rd hdr lo */ + 07002, /* 07, BSW ; swap */ + 07012, /* 10, RTR ; lo cyl to L */ + 06615, /* 11, RRSI ; rd hdr hi */ + 00025, /* 12, AND 25 ; mask = 377 */ + 07004, /* 13, RTL ; get cyl */ + 06603, /* 14, RLCA ; set addr */ + 07325, /* 15, CLA STL IAC RAL ; seek = 3 */ + 04027, /* 16, JMS GO ; do io */ + 07332, /* 17, CLA STL RTR ; dir in = 2000 */ + 06605, /* 20, RLSA ; sector */ + 01026, /* 21, TAD (-200) ; one sector */ + 06607, /* 22, RLWC ; word cnt */ + 07327, /* 23, CLA STL IAC RTL ; read = 6*/ + 04027, /* 24, JMS GO ; do io */ + 00377, /* 25, JMP 377 ; start */ + 07600, /* 26, -200 ; word cnt */ + 00000, /* GO, 0 ; subr */ + 06604, /* 30, RLCB ; load fnc */ + 06601, /* 31, RLSD ; wait */ + 05031, /* 32, JMP .-1 ; */ + 06617, /* 33, RLSE ; error? */ + 05427, /* 34, JMP I GO ; no, ok */ + 05001 /* 35, JMP BT ; restart */ + }; + + +t_stat rl_boot (int32 unitno, DEVICE *dptr) +{ +size_t i; + +if (unitno) /* only unit 0 */ + return SCPE_ARG; +if (rl_dib.dev != DEV_RL) /* only std devno */ + return STOP_NOTSTD; +rl_unit[unitno].TRK = 0; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = boot_rom[i]; +cpu_set_bootpc (BOOT_START); +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_rx.c Index: src/SIMH/PDP8/pdp8_rx.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_rx.c @@ -0,0 +1,754 @@ +/* pdp8_rx.c: RX8E/RX01, RX28/RX02 floppy disk simulator + + Copyright (c) 1993-2013, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rx RX8E/RX01, RX28/RX02 floppy disk + + 17-Sep-13 RMS Changed to use central set_bootpc routine + 03-Sep-13 RMS Added explicit void * cast + 15-May-06 RMS Fixed bug in autosize attach (Dave Gesswein) + 04-Jan-04 RMS Changed sim_fsize calling sequence + 05-Nov-03 RMS Fixed bug in RX28 read status (Charles Dickman) + 26-Oct-03 RMS Cleaned up buffer copy code, fixed double density write + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed variable size interaction with save/restore + 03-Mar-03 RMS Fixed autosizing + 08-Oct-02 RMS Added DIB, device number support + Fixed reset to work with disabled device + 15-Sep-02 RMS Added RX28/RX02 support + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Converted FLG to array + 17-Jul-01 RMS Fixed warning from VC++ 6 + 26-Apr-01 RMS Added device enable/disable support + 13-Apr-01 RMS Revised for register arrays + 14-Apr-99 RMS Changed t_addr to unsigned + 15-Aug-96 RMS Fixed bug in LCD + + An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B. + An RX02 diskette consists of 77 tracks, each with 26 sectors of 128B + (single density) or 256B (double density). Tracks are numbered 0-76, + sectors 1-26. The RX8E (RX28) can store data in 8b mode or 12b mode. + In 8b mode, the controller reads or writes 128 bytes (128B or 256B) + per sector. In 12b mode, it reads or writes 64 (64 or 128) 12b words + per sector. The 12b words are bit packed into the first 96 (192) bytes + of the sector; the last 32 (64) bytes are zeroed on writes. +*/ + +#include "pdp8_defs.h" + +#define RX_NUMTR 77 /* tracks/disk */ +#define RX_M_TRACK 0377 +#define RX_NUMSC 26 /* sectors/track */ +#define RX_M_SECTOR 0177 /* cf Jones!! */ +#define RX_NUMBY 128 /* bytes/sector */ +#define RX2_NUMBY 256 +#define RX_NUMWD (RX_NUMBY / 2) /* words/sector */ +#define RX2_NUMWD (RX2_NUMBY / 2) +#define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */ +#define RX2_SIZE (RX_NUMTR * RX_NUMSC * RX2_NUMBY) +#define RX_NUMDR 2 /* drives/controller */ +#define RX_M_NUMDR 01 +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DEN (UNIT_V_UF + 1) /* double density */ +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ +#define UNIT_WLK (1u << UNIT_V_WLK) +#define UNIT_DEN (1u << UNIT_V_DEN) +#define UNIT_AUTO (1u << UNIT_V_AUTO) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +#define IDLE 0 /* idle state */ +#define CMD8 1 /* 8b cmd, ho next */ +#define RWDS 2 /* rw, sect next */ +#define RWDT 3 /* rw, track next */ +#define RWXFR 4 /* rw, transfer */ +#define FILL 5 /* fill buffer */ +#define EMPTY 6 /* empty buffer */ +#define SDCNF 7 /* set dens, conf next */ +#define SDXFR 8 /* set dens, transfer */ +#define CMD_COMPLETE 9 /* set done next */ +#define INIT_COMPLETE 10 /* init compl next */ + +#define RXCS_V_FUNC 1 /* function */ +#define RXCS_M_FUNC 7 +#define RXCS_FILL 0 /* fill buffer */ +#define RXCS_EMPTY 1 /* empty buffer */ +#define RXCS_WRITE 2 /* write sector */ +#define RXCS_READ 3 /* read sector */ +#define RXCS_SDEN 4 /* set density (RX28) */ +#define RXCS_RXES 5 /* read status */ +#define RXCS_WRDEL 6 /* write del data */ +#define RXCS_ECODE 7 /* read error code */ +#define RXCS_DRV 0020 /* drive */ +#define RXCS_MODE 0100 /* mode */ +#define RXCS_MAINT 0200 /* maintenance */ +#define RXCS_DEN 0400 /* density (RX28) */ +#define RXCS_GETFNC(x) (((x) >> RXCS_V_FUNC) & RXCS_M_FUNC) + +#define RXES_CRC 0001 /* CRC error NI */ +#define RXES_ID 0004 /* init done */ +#define RXES_RX02 0010 /* RX02 (RX28) */ +#define RXES_DERR 0020 /* density err (RX28) */ +#define RXES_DEN 0040 /* density (RX28) */ +#define RXES_DD 0100 /* deleted data */ +#define RXES_DRDY 0200 /* drive ready */ + +#define TRACK u3 /* current track */ +#define READ_RXDBR ((rx_csr & RXCS_MODE)? AC | (rx_dbr & 0377): rx_dbr) +#define CALC_DA(t,s,b) (((t) * RX_NUMSC) + ((s) - 1)) * b + +extern int32 int_req, int_enable, dev_done; + +int32 rx_28 = 0; /* controller type */ +int32 rx_tr = 0; /* xfer ready flag */ +int32 rx_err = 0; /* error flag */ +int32 rx_csr = 0; /* control/status */ +int32 rx_dbr = 0; /* data buffer */ +int32 rx_esr = 0; /* error status */ +int32 rx_ecode = 0; /* error code */ +int32 rx_track = 0; /* desired track */ +int32 rx_sector = 0; /* desired sector */ +int32 rx_state = IDLE; /* controller state */ +int32 rx_cwait = 100; /* command time */ +int32 rx_swait = 10; /* seek, per track */ +int32 rx_xwait = 1; /* tr set time */ +int32 rx_stopioe = 0; /* stop on error */ +uint8 rx_buf[RX2_NUMBY] = { 0 }; /* sector buffer */ +int32 rx_bptr = 0; /* buffer pointer */ + +int32 rx (int32 IR, int32 AC); +t_stat rx_svc (UNIT *uptr); +t_stat rx_reset (DEVICE *dptr); +t_stat rx_boot (int32 unitno, DEVICE *dptr); +t_stat rx_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat rx_attach (UNIT *uptr, CONST char *cptr); +void rx_cmd (void); +void rx_done (int32 esr_flags, int32 new_ecode); +t_stat rx_settype (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, CONST void *desc); + +/* RX8E data structures + + rx_dev RX device descriptor + rx_unit RX unit list + rx_reg RX register list + rx_mod RX modifier list +*/ + +DIB rx_dib = { DEV_RX, 1, { &rx } }; + +UNIT rx_unit[] = { + { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+ + UNIT_ROABLE, RX_SIZE) }, + { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+ + UNIT_ROABLE, RX_SIZE) } + }; + +REG rx_reg[] = { + { ORDATAD (RXCS, rx_csr, 12, "status") }, + { ORDATAD (RXDB, rx_dbr, 12, "data buffer") }, + { ORDATAD (RXES, rx_esr, 12, "error status") }, + { ORDATA (RXERR, rx_ecode, 8) }, + { ORDATAD (RXTA, rx_track, 8, "current track") }, + { ORDATAD (RXSA, rx_sector, 8, "current sector") }, + { DRDATAD (STAPTR, rx_state, 4, "controller state"), REG_RO }, + { DRDATAD (BUFPTR, rx_bptr, 8, "buffer pointer") }, + { FLDATAD (TR, rx_tr, 0, "transfer ready flag") }, + { FLDATAD (ERR, rx_err, 0, "error flag") }, + { FLDATAD (DONE, dev_done, INT_V_RX, "done flag") }, + { FLDATAD (ENABLE, int_enable, INT_V_RX, "interrupt enable flag") }, + { FLDATAD (INT, int_req, INT_V_RX, "interrupt pending flag") }, + { DRDATAD (CTIME, rx_cwait, 24, "command completion time"), PV_LEFT }, + { DRDATAD (STIME, rx_swait, 24, "seek time per track"), PV_LEFT }, + { DRDATAD (XTIME, rx_xwait, 24, "transfer ready delay"), PV_LEFT }, + { FLDATAD (STOP_IOE, rx_stopioe, 0, "stop on I/O error") }, + { BRDATAD (SBUF, rx_buf, 8, 8, RX2_NUMBY, "sector buffer array") }, + { FLDATA (RX28, rx_28, 0), REG_HRO }, + { URDATA (CAPAC, rx_unit[0].capac, 10, T_ADDR_W, 0, + RX_NUMDR, REG_HRO | PV_LEFT) }, + { ORDATA (DEVNUM, rx_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rx_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "RX28", &rx_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "RX8E", &rx_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &rx_showtype, NULL }, + { (UNIT_DEN+UNIT_ATT), UNIT_ATT, "single density", NULL, NULL }, + { (UNIT_DEN+UNIT_ATT), (UNIT_DEN+UNIT_ATT), "double density", NULL, NULL }, + { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), 0, "single density", NULL, NULL }, + { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), UNIT_DEN, "double density", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DEN), 0, NULL, "SINGLE", &rx_set_size }, + { (UNIT_AUTO+UNIT_DEN), UNIT_DEN, NULL, "DOUBLE", &rx_set_size }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE rx_dev = { + "RX", rx_unit, rx_reg, rx_mod, + RX_NUMDR, 8, 20, 1, 8, 8, + NULL, NULL, &rx_reset, + &rx_boot, &rx_attach, NULL, + &rx_dib, DEV_DISABLE + }; + +/* IOT routine */ + +int32 rx (int32 IR, int32 AC) +{ +int32 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* get drive number */ + +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* unused */ + break; + + case 1: /* LCD */ + if (rx_state != IDLE) /* ignore if busy */ + return AC; + dev_done = dev_done & ~INT_RX; /* clear done, int */ + int_req = int_req & ~INT_RX; + rx_tr = rx_err = 0; /* clear flags */ + rx_bptr = 0; /* clear buf pointer */ + if (rx_28 && (AC & RXCS_MODE)) { /* RX28 8b mode? */ + rx_dbr = rx_csr = AC & 0377; /* save 8b */ + rx_tr = 1; /* xfer is ready */ + rx_state = CMD8; /* wait for part 2 */ + } + else { + rx_dbr = rx_csr = AC; /* save new command */ + rx_cmd (); /* issue command */ + } + return 0; /* clear AC */ + + case 2: /* XDR */ + switch (rx_state & 017) { /* case on state */ + + case EMPTY: /* emptying buffer */ + sim_activate (&rx_unit[drv], rx_xwait); /* sched xfer */ + return READ_RXDBR; /* return data reg */ + + case CMD8: /* waiting for cmd */ + rx_dbr = AC & 0377; + rx_csr = (rx_csr & 0377) | ((AC & 017) << 8); + rx_cmd (); + break; + + case RWDS:case RWDT:case FILL:case SDCNF: /* waiting for data */ + rx_dbr = AC; /* save data */ + sim_activate (&rx_unit[drv], rx_xwait); /* schedule */ + break; + + default: /* default */ + return READ_RXDBR; /* return data reg */ + } + break; + + case 3: /* STR */ + if (rx_tr != 0) { + rx_tr = 0; + return IOT_SKP + AC; + } + break; + + case 4: /* SER */ + if (rx_err != 0) { + rx_err = 0; + return IOT_SKP + AC; + } + break; + + case 5: /* SDN */ + if ((dev_done & INT_RX) != 0) { + dev_done = dev_done & ~INT_RX; + int_req = int_req & ~INT_RX; + return IOT_SKP + AC; + } + break; + + case 6: /* INTR */ + if (AC & 1) + int_enable = int_enable | INT_RX; + else int_enable = int_enable & ~INT_RX; + int_req = INT_UPDATE; + break; + + case 7: /* INIT */ + rx_reset (&rx_dev); /* reset device */ + break; + } /* end case pulse */ + +return AC; +} + +void rx_cmd (void) +{ +int32 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* get drive number */ + +switch (RXCS_GETFNC (rx_csr)) { /* decode command */ + + case RXCS_FILL: + rx_state = FILL; /* state = fill */ + rx_tr = 1; /* xfer is ready */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + break; + + case RXCS_EMPTY: + rx_state = EMPTY; /* state = empty */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + sim_activate (&rx_unit[drv], rx_xwait); /* sched xfer */ + break; + + case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL: + rx_state = RWDS; /* state = get sector */ + rx_tr = 1; /* xfer is ready */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + break; + + case RXCS_SDEN: + if (rx_28) { /* RX28? */ + rx_state = SDCNF; /* state = get conf */ + rx_tr = 1; /* xfer is ready */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + break; + } /* else fall thru */ + default: + rx_state = CMD_COMPLETE; /* state = cmd compl */ + sim_activate (&rx_unit[drv], rx_cwait); /* sched done */ + break; + } /* end switch func */ + +return; +} + +/* Unit service; the action to be taken depends on the transfer state: + + IDLE Should never get here + RWDS Save sector, set TR, set RWDT + RWDT Save track, set RWXFR + RWXFR Read/write buffer + FILL copy dbr to rx_buf[rx_bptr], advance ptr + if rx_bptr > max, finish command, else set tr + EMPTY if rx_bptr > max, finish command, else + copy rx_buf[rx_bptr] to dbr, advance ptr, set tr + CMD_COMPLETE copy requested data to dbr, finish command + INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command + + For RWDT and CMD_COMPLETE, the input argument is the selected drive; + otherwise, it is drive 0. +*/ + +t_stat rx_svc (UNIT *uptr) +{ +int32 i, func, byptr, bps, wps; +int8 *fbuf = (int8 *) uptr->filebuf; +uint32 da; +#define PTR12(x) (((x) + (x) + (x)) >> 1) + +if (rx_28 && (uptr->flags & UNIT_DEN)) /* RX28 and double density? */ + bps = RX2_NUMBY; /* double bytes/sector */ +else bps = RX_NUMBY; /* RX8E, normal count */ +wps = bps / 2; +func = RXCS_GETFNC (rx_csr); /* get function */ +switch (rx_state) { /* case on state */ + + case IDLE: /* idle */ + return SCPE_IERR; + + case EMPTY: /* empty buffer */ + if (rx_csr & RXCS_MODE) { /* 8b xfer? */ + if (rx_bptr >= bps) { /* done? */ + rx_done (0, 0); /* set done */ + break; /* and exit */ + } + rx_dbr = rx_buf[rx_bptr]; /* else get data */ + } + else { + byptr = PTR12 (rx_bptr); /* 12b xfer */ + if (rx_bptr >= wps) { /* done? */ + rx_done (0, 0); /* set done */ + break; /* and exit */ + } + rx_dbr = (rx_bptr & 1)? /* get data */ + ((rx_buf[byptr] & 017) << 8) | rx_buf[byptr + 1]: + (rx_buf[byptr] << 4) | ((rx_buf[byptr + 1] >> 4) & 017); + } + rx_bptr = rx_bptr + 1; + rx_tr = 1; + break; + + case FILL: /* fill buffer */ + if (rx_csr & RXCS_MODE) { /* 8b xfer? */ + rx_buf[rx_bptr] = rx_dbr; /* fill buffer */ + rx_bptr = rx_bptr + 1; + if (rx_bptr < bps) /* if more, set xfer */ + rx_tr = 1; + else rx_done (0, 0); /* else done */ + } + else { + byptr = PTR12 (rx_bptr); /* 12b xfer */ + if (rx_bptr & 1) { /* odd or even? */ + rx_buf[byptr] = (rx_buf[byptr] & 0360) | ((rx_dbr >> 8) & 017); + rx_buf[byptr + 1] = rx_dbr & 0377; + } + else { + rx_buf[byptr] = (rx_dbr >> 4) & 0377; + rx_buf[byptr + 1] = (rx_dbr & 017) << 4; + } + rx_bptr = rx_bptr + 1; + if (rx_bptr < wps) /* if more, set xfer */ + rx_tr = 1; + else { + for (i = PTR12 (wps); i < bps; i++) + rx_buf[i] = 0; /* else fill sector */ + rx_done (0, 0); /* set done */ + } + } + break; + + case RWDS: /* wait for sector */ + rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */ + rx_tr = 1; /* set xfer ready */ + rx_state = RWDT; /* advance state */ + return SCPE_OK; + + case RWDT: /* wait for track */ + rx_track = rx_dbr & RX_M_TRACK; /* save track */ + rx_state = RWXFR; + sim_activate (uptr, /* sched done */ + rx_swait * abs (rx_track - uptr->TRACK)); + return SCPE_OK; + + case RWXFR: /* transfer */ + if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (0, 0110); /* done, error */ + return IORETURN (rx_stopioe, SCPE_UNATT); + } + if (rx_track >= RX_NUMTR) { /* bad track? */ + rx_done (0, 0040); /* done, error */ + break; + } + uptr->TRACK = rx_track; /* now on track */ + if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */ + rx_done (0, 0070); /* done, error */ + break; + } + if (rx_28 && /* RX28? */ + (((uptr->flags & UNIT_DEN) != 0) ^ + ((rx_csr & RXCS_DEN) != 0))) { /* densities agree? */ + rx_done (RXES_DERR, 0240); /* no, error */ + break; + } + da = CALC_DA (rx_track, rx_sector, bps); /* get disk address */ + if (func == RXCS_WRDEL) /* del data? */ + rx_esr = rx_esr | RXES_DD; + if (func == RXCS_READ) { /* read? */ + for (i = 0; i < bps; i++) rx_buf[i] = fbuf[da + i]; + } + else { /* write */ + if (uptr->flags & UNIT_WPRT) { /* locked? */ + rx_done (0, 0100); /* done, error */ + break; + } + for (i = 0; i < bps; i++) + fbuf[da + i] = rx_buf[i]; + da = da + bps; + if (da > uptr->hwmark) + uptr->hwmark = da; + } + rx_done (0, 0); /* done */ + break; + + case SDCNF: /* confirm set density */ + if ((rx_dbr & 0377) != 0111) { /* confirmed? */ + rx_done (0, 0250); /* no, error */ + break; + } + rx_state = SDXFR; /* next state */ + sim_activate (uptr, rx_cwait * 100); /* schedule operation */ + break; + + case SDXFR: /* erase disk */ + for (i = 0; i < (int32) uptr->capac; i++) + fbuf[i] = 0; + uptr->hwmark = uptr->capac; + if (rx_csr & RXCS_DEN) + uptr->flags = uptr->flags | UNIT_DEN; + else uptr->flags = uptr->flags & ~UNIT_DEN; + rx_done (0, 0); + break; + + case CMD_COMPLETE: /* command complete */ + if (func == RXCS_ECODE) { /* read ecode? */ + rx_dbr = rx_ecode; /* set dbr */ + rx_done (0, -1); /* don't update */ + } + else if (rx_28) { /* no, read sta; RX28? */ + rx_esr = rx_esr & ~RXES_DERR; /* assume dens match */ + if (((uptr->flags & UNIT_DEN) != 0) ^ /* densities mismatch? */ + ((rx_csr & RXCS_DEN) != 0)) + rx_done (RXES_DERR, 0240); /* yes, error */ + else rx_done (0, 0); /* no, ok */ + } + else rx_done (0, 0); /* RX8E status */ + break; + + case INIT_COMPLETE: /* init complete */ + rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */ + rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */ + if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (RXES_ID, 0010); /* init done, error */ + break; + } + da = CALC_DA (1, 1, bps); /* track 1, sector 1 */ + for (i = 0; i < bps; i++) /* read sector */ + rx_buf[i] = fbuf[da + i]; + rx_done (RXES_ID, 0); /* set done */ + if ((rx_unit[1].flags & UNIT_ATT) == 0) + rx_ecode = 0020; + break; + } /* end case state */ + +return SCPE_OK; +} + +/* Command complete. Set done and put final value in interface register, + return to IDLE state. +*/ + +void rx_done (int32 esr_flags, int32 new_ecode) +{ +int32 drv = (rx_csr & RXCS_DRV)? 1: 0; + +rx_state = IDLE; /* now idle */ +dev_done = dev_done | INT_RX; /* set done */ +int_req = INT_UPDATE; /* update ints */ +rx_esr = (rx_esr | esr_flags) & ~(RXES_DRDY|RXES_RX02|RXES_DEN); +if (rx_28) /* RX28? */ + rx_esr = rx_esr | RXES_RX02; +if (rx_unit[drv].flags & UNIT_ATT) { /* update drv rdy */ + rx_esr = rx_esr | RXES_DRDY; + if (rx_unit[drv].flags & UNIT_DEN) /* update density */ + rx_esr = rx_esr | RXES_DEN; + } +if (new_ecode > 0) /* test for error */ + rx_err = 1; +if (new_ecode < 0) /* don't update? */ + return; +rx_ecode = new_ecode; /* update ecode */ +rx_dbr = rx_esr; /* update RXDB */ +return; +} + +/* Reset routine. The RX is one of the few devices that schedules + an I/O transfer as part of its initialization */ + +t_stat rx_reset (DEVICE *dptr) +{ +rx_dbr = rx_csr = 0; /* 12b mode, drive 0 */ +rx_esr = rx_ecode = 0; /* clear error */ +rx_tr = rx_err = 0; /* clear flags */ +rx_track = rx_sector = 0; /* clear address */ +rx_state = IDLE; /* ctrl idle */ +dev_done = dev_done & ~INT_RX; /* clear done, int */ +int_req = int_req & ~INT_RX; +int_enable = int_enable & ~INT_RX; +sim_cancel (&rx_unit[1]); /* cancel drive 1 */ +if (dptr->flags & DEV_DIS) /* disabled? */ + sim_cancel (&rx_unit[0]); +else if (rx_unit[0].flags & UNIT_BUF) { /* attached? */ + rx_state = INIT_COMPLETE; /* yes, sched init */ + sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK)); + } +else rx_done (rx_esr | RXES_ID, 0010); /* no, error */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rx_attach (UNIT *uptr, CONST char *cptr) +{ +uint32 sz; + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + if (sz > RX_SIZE) + uptr->flags = uptr->flags | UNIT_DEN; + else uptr->flags = uptr->flags & ~UNIT_DEN; + } +uptr->capac = (uptr->flags & UNIT_DEN)? RX2_SIZE: RX_SIZE; +return attach_unit (uptr, cptr); +} + +/* Set size routine */ + +t_stat rx_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) + return SCPE_ALATT; +if ((rx_28 == 0) && val) /* not on RX8E */ + return SCPE_NOFNC; +uptr->capac = val? RX2_SIZE: RX_SIZE; +return SCPE_OK; +} + +/* Set controller type */ + +t_stat rx_settype (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 i; + +if ((val < 0) || (val > 1) || (cptr != NULL)) + return SCPE_ARG; +if (val == rx_28) + return SCPE_OK; +for (i = 0; i < RX_NUMDR; i++) { + if (rx_unit[i].flags & UNIT_ATT) + return SCPE_ALATT; + } +for (i = 0; i < RX_NUMDR; i++) { + if (val) + rx_unit[i].flags = rx_unit[i].flags | UNIT_DEN | UNIT_AUTO; + else rx_unit[i].flags = rx_unit[i].flags & ~(UNIT_DEN | UNIT_AUTO); + rx_unit[i].capac = val? RX2_SIZE: RX_SIZE; + } +rx_28 = val; +return SCPE_OK; +} + +/* Show controller type */ + +t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +if (rx_28) fprintf (st, "RX28"); +else fprintf (st, "RX8E"); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 022 +#define BOOT_ENTRY 022 +#define BOOT_INST 060 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) +#define BOOT2_START 020 +#define BOOT2_ENTRY 033 +#define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 06755, /* 22, SDN */ + 05022, /* 23, JMP .-1 */ + 07126, /* 24, CLL CML RTL ; read command + */ + 01060, /* 25, TAD UNIT ; unit no */ + 06751, /* 26, LCD ; load read+unit */ + 07201, /* 27, CLA IAC ; AC = 1 */ + 04053, /* 30, JMS LOAD ; load sector */ + 04053, /* 31, JMS LOAD ; load track */ + 07104, /* 32, CLL RAL ; AC = 2 */ + 06755, /* 33, SDN */ + 05054, /* 34, JMP LOAD+1 */ + 06754, /* 35, SER */ + 07450, /* 36, SNA ; more to do? */ + 07610, /* 37, CLA SKP ; error */ + 05046, /* 40, JMP 46 ; go empty */ + 07402, /* 41-45, HALT ; error */ + 07402, + 07402, + 07402, + 07402, + 06751, /* 46, LCD ; load empty */ + 04053, /* 47, JMS LOAD ; get data */ + 03002, /* 50, DCA 2 ; store */ + 02050, /* 51, ISZ 50 ; incr store */ + 05047, /* 52, JMP 47 ; loop until done */ + 00000, /* LOAD, 0 */ + 06753, /* 54, STR */ + 05033, /* 55, JMP 33 */ + 06752, /* 56, XDR */ + 05453, /* 57, JMP I LOAD */ + 07024, /* UNIT, CML RAL ; for unit 1 */ + 06030 /* 61, KCC */ + }; + +static const uint16 boot2_rom[] = { + 01061, /* READ, TAD UNIT ; next unit+den */ + 01046, /* 21, TAD CON360 ; add in 360 */ + 00060, /* 22, AND CON420 ; mask to 420 */ + 03061, /* 23, DCA UNIT ; 400,420,0,20... */ + 07327, /* 24, STL CLA IAC RTL ; AC = 6 = read */ + 01061, /* 25, TAD UNIT ; +unit+den */ + 06751, /* 26, LCD ; load cmd */ + 07201, /* 27, CLA IAC; ; AC = 1 = trksec */ + 04053, /* 30, JMS LOAD ; load trk */ + 04053, /* 31, JMS LOAD ; load sec */ + 07004, /* CN7004, RAL ; AC = 2 = empty */ + 06755, /* START, SDN ; done? */ + 05054, /* 34, JMP LOAD+1 ; check xfr */ + 06754, /* 35, SER ; error? */ + 07450, /* 36, SNA ; AC=0 on start */ + 05020, /* 37, JMP RD ; try next den,un */ + 01061, /* 40, TAD UNIT ; +unit+den */ + 06751, /* 41, LCD ; load cmd */ + 01061, /* 42, TAD UNIT ; set 60 for sec boot */ + 00046, /* 43, AND CON360 ; only density */ + 01032, /* 44, TAD CN7004 ; magic */ + 03060, /* 45, DCA 60 */ + 00360, /* CON360, 360 ; NOP */ + 04053, /* 47, JMS LOAD ; get data */ + 03002, /* 50, DCA 2 ; store */ + 02050, /* 51, ISZ .-1 ; incr store */ + 05047, /* 52, JMP .-3 ; loop until done */ + 00000, /* LOAD, 0 */ + 06753, /* 54, STR ; xfr ready? */ + 05033, /* 55, JMP 33 ; no, chk done */ + 06752, /* 56, XDR ; get word */ + 05453, /* 57, JMP I 53 ; return */ + 00420, /* CON420, 420 ; toggle */ + 00020 /* UNIT, 20 ; unit+density */ + }; + +t_stat rx_boot (int32 unitno, DEVICE *dptr) +{ +size_t i; +extern uint16 M[]; + +if (rx_dib.dev != DEV_RX) /* only std devno */ + return STOP_NOTSTD; +if (rx_28) { + for (i = 0; i < BOOT2_LEN; i++) + M[BOOT2_START + i] = boot2_rom[i]; + cpu_set_bootpc (BOOT2_ENTRY); + } +else { + for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = boot_rom[i]; + M[BOOT_INST] = unitno? 07024: 07004; + cpu_set_bootpc (BOOT_ENTRY); + } +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_sys.c Index: src/SIMH/PDP8/pdp8_sys.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_sys.c @@ -0,0 +1,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 + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE tsc_dev; +extern DEVICE fpp_dev; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE clk_dev, lpt_dev; +extern DEVICE rk_dev, rl_dev; +extern DEVICE rx_dev; +extern DEVICE df_dev, rf_dev; +extern DEVICE dt_dev, td_dev; +extern DEVICE mt_dev, ct_dev; +extern DEVICE ttix_dev, ttox_dev; +extern REG cpu_reg[]; +extern uint16 M[]; + +t_stat fprint_sym_fpp (FILE *of, t_value *val); +t_stat parse_sym_fpp (CONST char *cptr, t_value *val); +CONST char *parse_field (CONST char *cptr, uint32 max, uint32 *val, uint32 c); +CONST char *parse_fpp_xr (CONST char *cptr, uint32 *xr, t_bool inc); +int32 test_fpp_addr (uint32 ad, uint32 max); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_consoles array of pointers to consoles (if more than one) + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "PDP-8"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { + &cpu_dev, + &tsc_dev, + &fpp_dev, + &clk_dev, + &ptr_dev, + &ptp_dev, + &tti_dev, + &tto_dev, + &ttix_dev, + &ttox_dev, + &lpt_dev, + &rk_dev, + &rl_dev, + &rx_dev, + &df_dev, + &rf_dev, + &dt_dev, + &td_dev, + &mt_dev, + &ct_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unimplemented instruction", + "HALT instruction", + "Breakpoint", + "Opcode Breakpoint", + "Non-standard device number", + "DECtape off reel", + "Infinite loop" + }; + +/* Ambiguous device list - these devices have overlapped IOT codes */ + +DEVICE *amb_dev[] = { + &rl_dev, + &ct_dev, + &td_dev, + NULL + }; + +#define AMB_RL (1 << 12) +#define AMB_CT (2 << 12) +#define AMB_TD (3 << 12) + +/* RIM loader format consists of alternating pairs of addresses and 12-bit + words. It can only operate in field 0 and is not checksummed. +*/ + +t_stat sim_load_rim (FILE *fi) +{ +int32 origin, hi, lo, wd; + +origin = 0200; +do { /* skip leader */ + if ((hi = getc (fi)) == EOF) + return SCPE_FMT; + } while ((hi == 0) || (hi >= 0200)); +do { /* data block */ + if ((lo = getc (fi)) == EOF) + return SCPE_FMT; + wd = (hi << 6) | lo; + if (wd > 07777) + origin = wd & 07777; + else M[origin++ & 07777] = wd; + if ((hi = getc (fi)) == EOF) + return SCPE_FMT; + } while (hi < 0200); /* until trailer */ +return SCPE_OK; +} + +/* BIN loader format consists of a string of 12-bit words (made up from + 7-bit characters) between leader and trailer (200). The last word on + tape is the checksum. A word with the "link" bit set is a new origin; + a character > 0200 indicates a change of field. +*/ + +int32 sim_bin_getc (FILE *fi, uint32 *newf) +{ +int32 c, rubout; + +rubout = 0; /* clear toggle */ +while ((c = getc (fi)) != EOF) { /* read char */ + if (rubout) /* toggle set? */ + rubout = 0; /* clr, skip */ + else if (c == 0377) /* rubout? */ + rubout = 1; /* set, skip */ + else if (c > 0200) /* channel 8 set? */ + *newf = (c & 070) << 9; /* change field */ + else return c; /* otherwise ok */ + } +return EOF; +} + +t_stat sim_load_bin (FILE *fi) +{ +int32 hi, lo, wd, csum, t; +uint32 field, newf, origin; +int32 sections_read = 0; + +for (;;) { + csum = origin = field = newf = 0; /* init */ + do { /* skip leader */ + if ((hi = sim_bin_getc (fi, &newf)) == EOF) { + if (sections_read != 0) { + sim_printf ("%d sections sucessfully read\n\r", sections_read); + return SCPE_OK; + } + else + return SCPE_FMT; + } + } while ((hi == 0) || (hi >= 0200)); + for (;;) { /* data blocks */ + if ((lo = sim_bin_getc (fi, &newf)) == EOF) /* low char */ + return SCPE_FMT; + wd = (hi << 6) | lo; /* form word */ + t = hi; /* save for csum */ + if ((hi = sim_bin_getc (fi, &newf)) == EOF) /* next char */ + return SCPE_FMT; + if (hi == 0200) { /* end of tape? */ + if ((csum - wd) & 07777) { /* valid csum? */ + if (sections_read != 0) + sim_printf ("%d sections sucessfully read\n\r", sections_read); + return SCPE_CSUM; + } + if (!(sim_switches & SWMASK ('A'))) /* Load all sections? */ + return SCPE_OK; + sections_read++; + break; + } + csum = csum + t + lo; /* add to csum */ + if (wd > 07777) /* chan 7 set? */ + origin = wd & 07777; /* new origin */ + else { /* no, data */ + if ((field | origin) >= MEMSIZE) + return SCPE_NXM; + M[field | origin] = wd; + origin = (origin + 1) & 07777; + } + field = newf; /* update field */ + } + } +return SCPE_IERR; +} + +/* Binary loader + Two loader formats are supported: RIM loader (-r) and BIN (-b) loader. */ + +t_stat sim_load (FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) +{ +if ((*cptr != 0) || (flag != 0)) + return SCPE_ARG; +if ((sim_switches & SWMASK ('R')) || /* RIM format? */ + (match_ext (fnam, "RIM") && !(sim_switches & SWMASK ('B')))) + return sim_load_rim (fileref); +else return sim_load_bin (fileref); /* no, BIN */ +} + +/* Symbol tables */ + +#define I_V_FL 18 /* flag start */ +#define I_M_FL 07 /* flag mask */ +#define I_V_NPN 0 /* no operand */ +#define I_V_FLD 1 /* field change */ +#define I_V_MRF 2 /* mem ref */ +#define I_V_IOT 3 /* general IOT */ +#define I_V_OP1 4 /* operate 1 */ +#define I_V_OP2 5 /* operate 2 */ +#define I_V_OP3 6 /* operate 3 */ +#define I_V_IOA 7 /* ambiguous IOT */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_FLD (I_V_FLD << I_V_FL) +#define I_MRF (I_V_MRF << I_V_FL) +#define I_IOT (I_V_IOT << I_V_FL) +#define I_OP1 (I_V_OP1 << I_V_FL) +#define I_OP2 (I_V_OP2 << I_V_FL) +#define I_OP3 (I_V_OP3 << I_V_FL) +#define I_IOA (I_V_IOA << I_V_FL) + +static const int32 masks[] = { + 07777, 07707, 07000, 07000, + 07416, 07571, 017457, 077777, + }; + +/* Ambiguous device mnemonics must precede default mnemonics */ + +static const char *opcode[] = { + "SKON", "ION", "IOF", "SRQ", /* std IOTs */ + "GTF", "RTF", "SGT", "CAF", + "RPE", "RSF", "RRB", "RFC", "RFC RRB", /* reader/punch */ + "PCE", "PSF", "PCF", "PPC", "PLS", + "KCF", "KSF", "KCC", "KRS", "KIE", "KRB", /* console */ + "TLF", "TSF", "TCF", "TPC", "SPI", "TLS", + "SBE", "SPL", "CAL", /* power fail */ + "CLEI", "CLDI", "CLSC", "CLLE", "CLCL", "CLSK", /* clock */ + "CINT", "RDF", "RIF", "RIB", /* mem mmgt */ + "RMF", "SINT", "CUF", "SUF", + "RLDC", "RLSD", "RLMA", "RLCA", /* RL - ambiguous */ + "RLCB", "RLSA", "RLWC", + "RRER", "RRWC", "RRCA", "RRCB", + "RRSA", "RRSI", "RLSE", + "KCLR", "KSDR", "KSEN", "KSBF", /* CT - ambiguous */ + "KLSA", "KSAF", "KGOA", "KRSB", + "SDSS", "SDST", "SDSQ", /* TD - ambiguous */ + "SDLC", "SDLD", "SDRC", "SDRD", + "ADCL", "ADLM", "ADST", "ADRB", /* A/D */ + "ADSK", "ADSE", "ADLE", "ADRS", + "DCMA", "DMAR", "DMAW", /* DF/RF */ + "DCIM", "DSAC", "DIML", "DIMA", + "DCEA", "DEAL", "DEAC", + "DFSE", "DFSC", "DISK", "DMAC", + "DCXA", "DXAL", "DXAC", + "PKSTF", "PSKF", "PCLF", "PSKE", /* LPT */ + "PSTB", "PSIE", "PCLF PSTB", "PCIE", + "LWCR", "CWCR", "LCAR", /* MT */ + "CCAR", "LCMR", "LFGR", "LDBR", + "RWCR", "CLT", "RCAR", + "RMSR", "RCMR", "RFSR", "RDBR", + "SKEF", "SKCB", "SKJD", "SKTR", "CLF", + "DSKP", "DCLR", "DLAG", /* RK */ + "DLCA", "DRST", "DLDC", "DMAN", + "LCD", "XDR", "STR", /* RX */ + "SER", "SDN", "INTR", "INIT", + "DTRA", "DTCA", "DTXA", "DTLA", /* DT */ + "DTSF", "DTRB", "DTLB", + "ETDS", "ESKP", "ECTF", "ECDF", /* TSC75 */ + "ERTB", "ESME", "ERIOT", "ETEN", + "FFST", "FPINT", "FPICL", "FPCOM", /* FPP8 */ + "FPHLT", "FPST", "FPRST", "FPIST", + "FMODE", "FMRB", + "FMRP", "FMDO", "FPEP", + + "CDF", "CIF", "CIF CDF", + "AND", "TAD", "ISZ", "DCA", "JMS", "JMP", "IOT", + "NOP", "NOP2", "NOP3", "SWAB", "SWBA", + "STL", "GLK", "STA", "LAS", "CIA", + "BSW", "RAL", "RTL", "RAR", "RTR", "RAL RAR", "RTL RTR", + "SKP", "SNL", "SZL", + "SZA", "SNA", "SZA SNL", "SNA SZL", + "SMA", "SPA", "SMA SNL", "SPA SZL", + "SMA SZA", "SPA SNA", "SMA SZA SNL", "SPA SNA SZL", + "SCL", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR", + "SCA", "SCA SCL", "SCA MUY", "SCA DVI", + "SCA NMI", "SCA SHL", "SCA ASR", "SCA LSR", + "ACS", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR", + "SCA", "DAD", "DST", "SWBA", + "DPSZ", "DPIC", "DCIM", "SAM", + "CLA", "CLL", "CMA", "CML", "IAC", /* encode only */ + "CLA", "OAS", "HLT", + "CLA", "MQA", "MQL", + NULL, NULL, NULL, NULL, /* decode only */ + NULL + }; + +static const int32 opc_val[] = { + 06000+I_NPN, 06001+I_NPN, 06002+I_NPN, 06003+I_NPN, + 06004+I_NPN, 06005+I_NPN, 06006+I_NPN, 06007+I_NPN, + 06010+I_NPN, 06011+I_NPN, 06012+I_NPN, 06014+I_NPN, 06016+I_NPN, + 06020+I_NPN, 06021+I_NPN, 06022+I_NPN, 06024+I_NPN, 06026+I_NPN, + 06030+I_NPN, 06031+I_NPN, 06032+I_NPN, 06034+I_NPN, 06035+I_NPN, 06036+I_NPN, + 06040+I_NPN, 06041+I_NPN, 06042+I_NPN, 06044+I_NPN, 06045+I_NPN, 06046+I_NPN, + 06101+I_NPN, 06102+I_NPN, 06103+I_NPN, + 06131+I_NPN, 06132+I_NPN, 06133+I_NPN, 06135+I_NPN, 06136+I_NPN, 06137+I_NPN, + 06204+I_NPN, 06214+I_NPN, 06224+I_NPN, 06234+I_NPN, + 06244+I_NPN, 06254+I_NPN, 06264+I_NPN, 06274+I_NPN, + 06600+I_IOA+AMB_RL, 06601+I_IOA+AMB_RL, 06602+I_IOA+AMB_RL, 06603+I_IOA+AMB_RL, + 06604+I_IOA+AMB_RL, 06605+I_IOA+AMB_RL, 06607+I_IOA+AMB_RL, + 06610+I_IOA+AMB_RL, 06611+I_IOA+AMB_RL, 06612+I_IOA+AMB_RL, 06613+I_IOA+AMB_RL, + 06614+I_IOA+AMB_RL, 06615+I_IOA+AMB_RL, 06617+I_IOA+AMB_RL, + 06700+I_IOA+AMB_CT, 06701+I_IOA+AMB_CT, 06702+I_IOA+AMB_CT, 06703+I_IOA+AMB_CT, + 06704+I_IOA+AMB_CT, 06705+I_IOA+AMB_CT, 06706+I_IOA+AMB_CT, 06707+I_IOA+AMB_CT, + 06771+I_IOA+AMB_TD, 06772+I_IOA+AMB_TD, 06773+I_IOA+AMB_TD, + 06774+I_IOA+AMB_TD, 06775+I_IOA+AMB_TD, 06776+I_IOA+AMB_TD, 06777+I_IOA+AMB_TD, + 06530+I_NPN, 06531+I_NPN, 06532+I_NPN, 06533+I_NPN, /* AD */ + 06534+I_NPN, 06535+I_NPN, 06536+I_NPN, 06537+I_NPN, + 06660+I_NPN, 06601+I_NPN, 06603+I_NPN, 06605+I_NPN, /* DF/RF */ + 06611+I_NPN, 06612+I_NPN, 06615+I_NPN, 06616+I_NPN, + 06611+I_NPN, 06615+I_NPN, 06616+I_NPN, + 06621+I_NPN, 06622+I_NPN, 06623+I_NPN, 06626+I_NPN, + 06641+I_NPN, 06643+I_NPN, 06645+I_NPN, + 06661+I_NPN, 06662+I_NPN, 06663+I_NPN, /* LPT */ + 06664+I_NPN, 06665+I_NPN, 06666+I_NPN, 06667+I_NPN, + 06701+I_NPN, 06702+I_NPN, 06703+I_NPN, /* MT */ + 06704+I_NPN, 06705+I_NPN, 06706+I_NPN, 06707+I_NPN, + 06711+I_NPN, 06712+I_NPN, 06713+I_NPN, + 06714+I_NPN, 06715+I_NPN, 06716+I_NPN, 06717+I_NPN, + 06721+I_NPN, 06722+I_NPN, 06723+I_NPN, 06724+I_NPN, 06725+I_NPN, + 06741+I_NPN, 06742+I_NPN, 06743+I_NPN, /* RK */ + 06744+I_NPN, 06745+I_NPN, 06746+I_NPN, 06747+I_NPN, + 06751+I_NPN, 06752+I_NPN, 06753+I_NPN, /* RX */ + 06754+I_NPN, 06755+I_NPN, 06756+I_NPN, 06757+I_NPN, + 06761+I_NPN, 06762+I_NPN, 06764+I_NPN, 06766+I_NPN, /* DT */ + 06771+I_NPN, 06772+I_NPN, 06774+I_NPN, + 06360+I_NPN, 06361+I_NPN, 06362+I_NPN, 06363+I_NPN, /* TSC */ + 06364+I_NPN, 06365+I_NPN, 06366+I_NPN, 06367+I_NPN, + 06550+I_NPN, 06551+I_NPN, 06552+I_NPN, 06553+I_NPN, /* FPP8 */ + 06554+I_NPN, 06555+I_NPN, 06556+I_NPN, 06557+I_NPN, + 06561+I_NPN, 06563+I_NPN, + 06564+I_NPN, 06565+I_NPN, 06567+I_NPN, + + 06201+I_FLD, 06202+I_FLD, 06203+I_FLD, + 00000+I_MRF, 01000+I_MRF, 02000+I_MRF, 03000+I_MRF, + 04000+I_MRF, 05000+I_MRF, 06000+I_IOT, + 07000+I_NPN, 07400+I_NPN, 07401+I_NPN, 07431+I_NPN, 07447+I_NPN, + 07120+I_NPN, 07204+I_NPN, 07240+I_NPN, 07604+I_NPN, 07041+I_NPN, + 07002+I_OP1, 07004+I_OP1, 07006+I_OP1, + 07010+I_OP1, 07012+I_OP1, 07014+I_OP1, 07016+I_OP1, + 07410+I_OP2, 07420+I_OP2, 07430+I_OP2, + 07440+I_OP2, 07450+I_OP2, 07460+I_OP2, 07470+I_OP2, + 07500+I_OP2, 07510+I_OP2, 07520+I_OP2, 07530+I_OP2, + 07540+I_OP2, 07550+I_OP2, 07560+I_OP2, 07570+I_OP2, + 07403+I_OP3, 07405+I_OP3, 07407+I_OP3, + 07411+I_OP3, 07413+I_OP3, 07415+I_OP3, 07417+I_OP3, + 07441+I_OP3, 07443+I_OP3, 07445+I_OP3, 07447+I_OP3, + 07451+I_OP3, 07453+I_OP3, 07455+I_OP3, 07457+I_OP3, + 017403+I_OP3, 017405+I_OP3, 0174017+I_OP3, + 017411+I_OP3, 017413+I_OP3, 017415+I_OP3, 017417+I_OP3, + 017441+I_OP3, 017443+I_OP3, 017445+I_OP3, 017447+I_OP3, + 017451+I_OP3, 017453+I_OP3, 017455+I_OP3, 017457+I_OP3, + 07200+I_OP1, 07100+I_OP1, 07040+I_OP1, 07020+I_OP1, 07001+I_OP1, + 07600+I_OP2, 07404+I_OP2, 07402+I_OP2, + 07601+I_OP3, 07501+I_OP3, 07421+I_OP3, + 07000+I_OP1, 07400+I_OP2, 07401+I_OP3, 017401+I_OP3, + -1 + }; + +/* Symbol tables for FPP-8 */ + +#define F_V_FL 18 /* flag start */ +#define F_M_FL 017 /* flag mask */ +#define F_V_NOP12 0 /* no opnd 12b */ +#define F_V_NOP9 1 /* no opnd 9b */ +#define F_V_AD15 2 /* 15b dir addr */ +#define F_V_AD15X 3 /* 15b dir addr indx */ +#define F_V_IMMX 4 /* 12b immm indx */ +#define F_V_X 5 /* index */ +#define F_V_MRI 6 /* mem ref ind */ +#define F_V_MR1D 7 /* mem ref dir 1 word */ +#define F_V_MR2D 8 /* mem ref dir 2 word */ +#define F_V_LEMU 9 /* LEA/IMUL */ +#define F_V_LEMUI 10 /* LEAI/IMULI */ +#define F_V_LTR 11 /* LTR */ +#define F_V_MRD 12 /* mem ref direct (enc) */ +#define F_NOP12 (F_V_NOP12 << F_V_FL) +#define F_NOP9 (F_V_NOP9 << F_V_FL) +#define F_AD15 (F_V_AD15 << F_V_FL) +#define F_AD15X (F_V_AD15X << F_V_FL) +#define F_IMMX (F_V_IMMX << F_V_FL) +#define F_X (F_V_X << F_V_FL) +#define F_MRI (F_V_MRI << F_V_FL) +#define F_MR1D (F_V_MR1D << F_V_FL) +#define F_MR2D (F_V_MR2D << F_V_FL) +#define F_LEMU (F_V_LEMU << F_V_FL) +#define F_LEMUI (F_V_LEMUI << F_V_FL) +#define F_LTR (F_V_LTR << F_V_FL) +#define F_MRD (F_V_MRD << F_V_FL) + +static const uint32 fmasks[] = { + 07777, 07770, 07770, 07600, + 07770, 07770, 07600, 07600, + 07600, 017600, 017600, 07670, + 07777 + }; + +/* Memory references are encode dir / decode 1D / decode 2D / indirect */ + +static const char *fopcode[] = { + "FEXIT", "FPAUSE", "FCLA", "FNEG", + "FNORM", "STARTF", "STARTD", "JAC", + "ALN", "ATX", "XTA", + "FNOP", "STARTE", + "LDX", "ADDX", + "FLDA", "FLDA", "FLDA", "FLDAI", + "JEQ", "JGE", "JLE", "JA", + "JNE", "JLT", "JGT", "JAL", + "SETX", "SETB", "JSA", "JSR", + "FADD", "FADD", "FADD", "FADDI", + "JNX", + "FSUB", "FSUB", "FSUB", "FSUBI", + "TRAP3", + "FDIV", "FDIV", "FDIV", "FDIVI", + "TRAP4", + "FMUL", "FMUL", "FMUL", "FMULI", + "LTREQ", "LTRGE", "LTRLE", "LTRA", + "LTRNE", "LTRLT", "LTRGT", "LTRAL", + "FADDM", "FADDM", "FADDM", "FADDMI", + "IMUL", "LEA", + "FSTA", "FSTA", "FSTA", "FSTAI", + "IMULI", "LEAI", + "FMULM", "FMULM", "FMULM", "FMULMI", + NULL + }; + +static const int32 fop_val[] = { + 00000+F_NOP12, 00001+F_NOP12, 00002+F_NOP12, 00003+F_NOP12, + 00004+F_NOP12, 00005+F_NOP12, 00006+F_NOP12, 00007+F_NOP12, + 00010+F_X, 00020+F_X, 00030+F_X, + 00040+F_NOP9, 00050+F_NOP9, + 00100+F_IMMX, 00110+F_IMMX, + 00000+F_MRD, 00200+F_MR1D, 00400+F_MR2D, 00600+F_MRI, + 01000+F_AD15, 01010+F_AD15, 01020+F_AD15, 01030+F_AD15, + 01040+F_AD15, 01050+F_AD15, 01060+F_AD15, 01070+F_AD15, + 01100+F_AD15, 01110+F_AD15, 01120+F_AD15, 01130+F_AD15, + 01000+F_MRD, 01200+F_MR1D, 01400+F_MR2D, 01600+F_MRI, + 02000+F_AD15X, + 02000+F_MRD, 02200+F_MR1D, 02400+F_MR2D, 02600+F_MRI, + 03000+F_AD15, + 03000+F_MRD, 03200+F_MR1D, 03400+F_MR2D, 03600+F_MRI, + 04000+F_AD15, + 04000+F_MRD, 04200+F_MR1D, 04400+F_MR2D, 04600+F_MRI, + 05000+F_LTR, 05010+F_LTR, 05020+F_LTR, 05030+F_LTR, + 05040+F_LTR, 05050+F_LTR, 05060+F_LTR, 05070+F_LTR, + 05000+F_MRD, 05200+F_MR1D, 05400+F_MR2D, 05600+F_MRI, + 016000+F_LEMU, 006000+F_LEMU, + 06000+F_MRD, 06200+F_MR1D, 06400+F_MR2D, 06600+F_MRI, + 017000+F_LEMUI, 007000+F_LEMUI, + 07000+F_MRD, 07200+F_MR1D, 07400+F_MR2D, 07600+F_MRI, + -1 + }; + +/* Operate decode + + Inputs: + *of = output stream + inst = mask bits + Class = instruction class code + sp = space needed? + Outputs: + status = space needed +*/ + +int32 fprint_opr (FILE *of, int32 inst, int32 Class, int32 sp) +{ +int32 i, j; + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == Class) && (opc_val[i] & inst)) { /* same class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; + } + } +return sp; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to data + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) +#define SIXTOASC(x) (((x) >= 040)? (x): (x) + 0100) +#define TSSTOASC(x) ((x) + 040) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, sp, inst, disp, opc; +extern int32 emode; +t_stat r; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +inst = val[0]; +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) + return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* characters? */ + fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", SIXTOASC (inst & 077)); + return SCPE_OK; + } +if (sw & SWMASK ('T')) { /* TSS8 packed? */ + fprintf (of, "%c", TSSTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", TSSTOASC (inst & 077)); + return SCPE_OK; + } +if ((sw & SWMASK ('F')) && /* FPP8? */ + ((r = fprint_sym_fpp (of, val)) != SCPE_ARG)) + return r; +if (!(sw & SWMASK ('M'))) + return SCPE_ARG; + +/* Instruction decode */ + +opc = (inst >> 9) & 07; /* get major opcode */ +if (opc == 07) /* operate? */ + inst = inst | ((emode & 1) << 12); /* include EAE mode */ +if (opc == 06) { /* IOT? */ + DEVICE *dptr; + DIB *dibp; + uint32 dno = (inst >> 3) & 077; + for (i = 0; (dptr = amb_dev[i]) != NULL; i++) { /* check amb devices */ + if ((dptr->ctxt == NULL) || /* no DIB or */ + (dptr->flags & DEV_DIS)) continue; /* disabled? skip */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if ((dno >= dibp->dev) || /* IOT for this dev? */ + (dno < (dibp->dev + dibp->num))) { + inst = inst | ((i + 1) << 12); /* disambiguate */ + break; /* done */ + } + } + } + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 077777) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + + case I_V_NPN: case I_V_IOA: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + + case I_V_FLD: /* field change */ + fprintf (of, "%s %-o", opcode[i], (inst >> 3) & 07); + break; + + case I_V_MRF: /* mem ref */ + disp = inst & 0177; /* displacement */ + fprintf (of, "%s%s", opcode[i], ((inst & 00400)? " I ": " ")); + if (inst & 0200) { /* current page? */ + if (cflag) + fprintf (of, "%-o", (addr & 07600) | disp); + else fprintf (of, "C %-o", disp); + } + else fprintf (of, "%-o", disp); /* page zero */ + break; + + case I_V_IOT: /* IOT */ + fprintf (of, "%s %-o", opcode[i], inst & 0777); + break; + + case I_V_OP1: /* operate group 1 */ + sp = fprint_opr (of, inst & 0361, j, 0); + if (opcode[i]) + fprintf (of, (sp? " %s": "%s"), opcode[i]); + break; + + case I_V_OP2: /* operate group 2 */ + if (opcode[i]) + fprintf (of, "%s", opcode[i]); /* skips */ + fprint_opr (of, inst & 0206, j, opcode[i] != NULL); + break; + + case I_V_OP3: /* operate group 3 */ + sp = fprint_opr (of, inst & 0320, j, 0); + if (opcode[i]) + fprintf (of, (sp? " %s": "%s"), opcode[i]); + break; + } /* end case */ + + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (CONST char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +uint32 cflag, d, i, j, k; +t_stat r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + val[0] = (t_value) cptr[0] | 0200; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + val[0] = (((t_value) cptr[0] & 077) << 6) | + ((t_value) cptr[1] & 077); + return SCPE_OK; + } +if ((sw & SWMASK ('T')) || ((*cptr == '"') && cptr++)) { /* TSS8 string? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + val[0] = (((t_value) (cptr[0] - 040) & 077) << 6) | + ((t_value) (cptr[1] - 040) & 077); + return SCPE_OK; + } +if ((r = parse_sym_fpp (cptr, val)) != SCPE_ARG) /* FPP8 inst? */ + return r; + +/* Instruction parse */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) + return SCPE_ARG; +val[0] = opc_val[i] & 07777; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_IOT: /* IOT */ + if ((cptr = parse_field (cptr, 0777, &d, 0)) == NULL) + return SCPE_ARG; /* get dev+pulse */ + val[0] = val[0] | d; + break; + + case I_V_FLD: /* field */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if (opcode[i] != NULL) { + k = (opc_val[i] >> I_V_FL) & I_M_FL; + if (k != j) + return SCPE_ARG; + val[0] = val[0] | (opc_val[i] & 07777); + } + else { + d = get_uint (gbuf, 8, 07, &r); + if (r != SCPE_OK) + return SCPE_ARG; + val[0] = val[0] | (d << 3); + break; + } + } + break; + + case I_V_MRF: /* mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if (strcmp (gbuf, "I") == 0) { /* indirect? */ + val[0] = val[0] | 0400; + cptr = get_glyph (cptr, gbuf, 0); + } + if ((k = (strcmp (gbuf, "C") == 0)) || (strcmp (gbuf, "Z") == 0)) { + if ((cptr = parse_field (cptr, 0177, &d, 0)) == NULL) + return SCPE_ARG; + val[0] = val[0] | d | (k? 0200: 0); + } + else { + d = get_uint (gbuf, 8, 07777, &r); + if (r != SCPE_OK) + return SCPE_ARG; + if (d <= 0177) + val[0] = val[0] | d; + else if (cflag && (((addr ^ d) & 07600) == 0)) + val[0] = val[0] | (d & 0177) | 0200; + else return SCPE_ARG; + } + break; + + case I_V_OP1: case I_V_OP2: case I_V_OP3: /* operates */ + case I_V_NPN: case I_V_IOA: + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + k = opc_val[i] & 07777; + if ((opcode[i] == NULL) || (((k ^ val[0]) & 07000) != 0)) + return SCPE_ARG; + val[0] = val[0] | k; + } + break; + } /* end case */ + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} + +/* FPP8 instruction decode */ + +t_stat fprint_sym_fpp (FILE *of, t_value *val) +{ +uint32 wd1, wd2, xr4b, xr3b, ad15; +uint32 i, j; +extern uint32 fpp_bra, fpp_cmd; + +wd1 = (uint32) val[0] | ((fpp_cmd & 04000) << 1); +wd2 = (uint32) val[1]; +xr4b = (wd1 >> 3) & 017; +xr3b = wd1 & 07; +ad15 = (xr3b << 12) | wd2; + +for (i = 0; fop_val[i] >= 0; i++) { /* loop thru ops */ + j = (fop_val[i] >> F_V_FL) & F_M_FL; /* get class */ + if ((fop_val[i] & 017777) == (wd1 & fmasks[j])) { /* match? */ + + switch (j) { /* case on class */ + case F_V_NOP12: + case F_V_NOP9: + case F_V_LTR: /* no operands */ + fprintf (of, "%s", fopcode[i]); + break; + + case F_V_X: /* index */ + fprintf (of, "%s %o", fopcode[i], xr3b); + break; + + case F_V_IMMX: /* index imm */ + fprintf (of, "%s %-o,%o", fopcode[i], wd2, xr3b); + return -1; /* extra word */ + + case F_V_AD15: /* 15b address */ + fprintf (of, "%s %-o", fopcode[i], ad15); + return -1; /* extra word */ + + case F_V_AD15X: /* 15b addr, indx */ + fprintf (of, "%s %-o", fopcode[i], ad15); + if (xr4b >= 010) + fprintf (of, ",%o+", xr4b & 7); + else fprintf (of, ",%o", xr4b); + return -1; /* extra word */ + + case F_V_MR1D: /* 1 word direct */ + ad15 = (fpp_bra + (3 * (wd1 & 0177))) & ADDRMASK; + fprintf (of, "%s %-o", fopcode[i], ad15); + break; + + case F_V_LEMU: + case F_V_MR2D: /* 2 word direct */ + fprintf (of, "%s %-o", fopcode[i], ad15); + if (xr4b >= 010) + fprintf (of, ",%o+", xr4b & 7); + else if (xr4b != 0) + fprintf (of, ",%o", xr4b); + return -1; /* extra word */ + + case F_V_LEMUI: + case F_V_MRI: /* indirect */ + ad15 = (fpp_bra + (3 * xr3b)) & ADDRMASK; + fprintf (of, "%s %-o", fopcode[i], ad15); + if (xr4b >= 010) + fprintf (of, ",%o+", xr4b & 7); + else if (xr4b != 0) + fprintf (of, ",%o", xr4b); + break; + + case F_V_MRD: /* encode only */ + return SCPE_IERR; + } + + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* FPP8 instruction parse */ + +t_stat parse_sym_fpp (CONST char *cptr, t_value *val) +{ +uint32 i, j, ad, xr; +int32 broff, nwd; +char gbuf[CBUFSIZE]; + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (fopcode[i] != NULL) && (strcmp (fopcode[i], gbuf) != 0) ; i++) ; +if (fopcode[i] == NULL) return SCPE_ARG; +val[0] = fop_val[i] & 07777; /* get value */ +j = (fop_val[i] >> F_V_FL) & F_M_FL; /* get class */ +xr = 0; +nwd = 0; + +switch (j) { /* case on class */ + + case F_V_NOP12: + case F_V_NOP9: + case F_V_LTR: /* no operands */ + break; + + case F_V_X: /* 3b XR */ + if ((cptr = parse_field (cptr, 07, &xr, 0)) == NULL) + return SCPE_ARG; + val[0] |= xr; + break; + + case F_V_IMMX: /* 12b, XR */ + if ((cptr = parse_field (cptr, 07777, &ad, ',')) == NULL) + return SCPE_ARG; + if ((*cptr == 0) || + ((cptr = parse_fpp_xr (cptr, &xr, FALSE)) == NULL)) + return SCPE_ARG; + val[0] |= xr; + val[++nwd] = ad; + break; + + case F_V_AD15: /* 15b addr */ + if ((cptr = parse_field (cptr, 077777, &ad, 0)) == NULL) + return SCPE_ARG; + val[0] |= (ad >> 12) & 07; + val[++nwd] = ad & 07777; + break; + + case F_V_AD15X: /* 15b addr, idx */ + if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) + return SCPE_ARG; + if ((*cptr == 0) || + ((cptr = parse_fpp_xr (cptr, &xr, FALSE)) == NULL)) + return SCPE_ARG; + val[0] |= ((xr << 3) | ((ad >> 12) & 07)); + val[++nwd] = ad & 07777; + break; + + case F_V_LEMUI: + case F_V_MRI: /* indirect */ + if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) + return SCPE_ARG; + if ((*cptr != 0) && + ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL)) + return SCPE_ARG; + if ((broff = test_fpp_addr (ad, 07)) < 0) + return SCPE_ARG; + val[0] |= ((xr << 3) | broff); + break; + + case F_V_MRD: /* direct */ + if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) + return SCPE_ARG; + if (((broff = test_fpp_addr (ad, 0177)) < 0) || + (*cptr != 0)) { + if ((*cptr != 0) && + ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL)) + return SCPE_ARG; + val[0] |= (00400 | (xr << 3) | ((ad >> 12) & 07)); + val[++nwd] = ad & 07777; + } + else val[0] |= (00200 | broff); + break; + + case F_V_LEMU: + if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) + return SCPE_ARG; + if ((*cptr != 0) && + ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL)) + return SCPE_ARG; + val[0] |= ((xr << 3) | ((ad >> 12) & 07)); + val[++nwd] = ad & 07777; + break; + + case F_V_MR1D: + case F_V_MR2D: + return SCPE_IERR; + } /* end case */ + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return -nwd; +} + +/* Parse field */ + +CONST char *parse_field (CONST char *cptr, uint32 max, uint32 *val, uint32 c) +{ +char gbuf[CBUFSIZE]; +t_stat r; + +cptr = get_glyph (cptr, gbuf, c); /* get field */ +*val = get_uint (gbuf, 8, max, &r); +if (r != SCPE_OK) + return NULL; +return cptr; +} + +/* Parse index register */ + +CONST char *parse_fpp_xr (CONST char *cptr, uint32 *xr, t_bool inc) +{ +char gbuf[CBUFSIZE]; +uint32 len; +t_stat r; + +cptr = get_glyph (cptr, gbuf, 0); /* get field */ +len = strlen (gbuf); +if (gbuf[len - 1] == '+') { + if (!inc) + return NULL; + gbuf[len - 1] = 0; + *xr = 010; + } +else *xr = 0; +*xr += get_uint (gbuf, 8, 7, &r); +if (r != SCPE_OK) + return NULL; +return cptr; +} + +/* Test address in range of base register */ + +int32 test_fpp_addr (uint32 ad, uint32 max) +{ +uint32 off; +extern uint32 fpp_bra; + +off = ad - fpp_bra; +if (((off % 3) != 0) || + (off > (max * 3))) + return -1; +return ((int32) off / 3); +} ADDED src/SIMH/PDP8/pdp8_td.c Index: src/SIMH/PDP8/pdp8_td.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_td.c @@ -0,0 +1,955 @@ +/* pdp8_td.c: PDP-8 simple DECtape controller (TD8E) simulator + + Copyright (c) 1993-2013, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module was inspired by Gerold Pauler's TD8E simulator for Doug Jones' + PDP8 simulator but tracks the hardware implementation more closely. + + td TD8E/TU56 DECtape + + 17-Sep-13 RMS Changed to use central set_bootpc routine + 23-Mar-11 RMS Fixed SDLC to clear AC (from Dave Gesswein) + 23-Jun-06 RMS Fixed switch conflict in ATTACH + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR + + PDP-8 DECtapes are represented in memory by fixed length buffer of 12b words. + Three file formats are supported: + + 18b/36b 256 words per block [256 x 18b] + 16b 256 words per block [256 x 16b] + 12b 129 words per block [129 x 12b] + + When a 16b or 18/36b DECtape file is read in, it is converted to 12b format. + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape (as + taken from the TD8E formatter) is: + + reverse end zone 8192 reverse end zone codes ~ 10 feet + reverse buffer 200 interblock codes + block 0 + : + block n + forward buffer 200 interblock codes + forward end zone 8192 forward end zone codes ~ 10 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of non-data words. Read assumes that the tape has been conventionally + written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 checksum (for reverse reads) + : + trailer word 4 checksum (for forward reads) + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write modifies only the data words and dumps the non-data words in the + bit bucket. +*/ + +#include "pdp8_defs.h" + +#define DT_NUMDR 2 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_11FMT (1 << UNIT_V_11FMT) +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ +#define WRITTEN u5 /* device buffer is dirty and needs flushing */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* System independent DECtape constants */ + +#define DT_LPERMC 6 /* lines per mark track */ +#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ +#define DT_BFLIN (200 * DT_LPERMC) /* end zone buffer */ +#define DT_HTLIN (5 * DT_LPERMC) /* lines per hdr/trlr */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 384 /* block size in 12b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ + +#define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE) +#define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32)) +#define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 129 /* block size in 12b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ +#define D8_FILSIZ (D8_CAPAC * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D8_CAPAC /* default */ +#define DT_WSIZE D8_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) + +/* Command register */ + +#define TDC_UNIT 04000 /* unit select */ +#define TDC_FWDRV 02000 /* fwd/rev */ +#define TDC_STPGO 01000 /* stop/go */ +#define TDC_RW 00400 /* read/write */ +#define TDC_MASK 07400 /* implemented */ +#define TDC_GETUNIT(x) (((x) & TDC_UNIT)? 1: 0) + +/* Status register */ + +#define TDS_WLO 00200 /* write lock */ +#define TDS_TME 00100 /* timing/sel err */ + +/* Mark track register and codes */ + +#define MTK_MASK 077 +#define MTK_REV_END 055 /* rev end zone */ +#define MTK_INTER 025 /* interblock */ +#define MTK_FWD_BLK 026 /* fwd block */ +#define MTK_REV_GRD 032 /* reverse guard */ +#define MTK_FWD_PRE 010 /* lock, etc */ +#define MTK_DATA 070 /* data */ +#define MTK_REV_PRE 073 /* lock, etc */ +#define MTK_FWD_GRD 051 /* fwd guard */ +#define MTK_REV_BLK 045 /* rev block */ +#define MTK_FWD_END 022 /* fwd end zone */ + +/* DECtape state */ + +#define STA_STOP 0 /* stopped */ +#define STA_DEC 2 /* decelerating */ +#define STA_ACC 4 /* accelerating */ +#define STA_UTS 6 /* up to speed */ +#define STA_DIR 1 /* fwd/rev */ + +#define ABS(x) (((x) < 0)? (-(x)): (x)) +#define MTK_BIT(c,p) (((c) >> (DT_LPERMC - 1 - ((p) % DT_LPERMC))) & 1) + +/* State and declarations */ + +int32 td_cmd = 0; /* command */ +int32 td_dat = 0; /* data */ +int32 td_mtk = 0; /* mark track */ +int32 td_slf = 0; /* single line flag */ +int32 td_qlf = 0; /* quad line flag */ +int32 td_tme = 0; /* timing error flag */ +int32 td_csum = 0; /* save check sum */ +int32 td_qlctr = 0; /* quad line ctr */ +int32 td_ltime = 20; /* interline time */ +int32 td_dctime = 40000; /* decel time */ +int32 td_stopoffr = 0; +static uint8 tdb_mtk[DT_NUMDR][D18_LPERB]; /* mark track bits */ + +int32 td77 (int32 IR, int32 AC); +t_stat td_svc (UNIT *uptr); +t_stat td_reset (DEVICE *dptr); +t_stat td_attach (UNIT *uptr, CONST char *cptr); +void td_flush (UNIT *uptr); +t_stat td_detach (UNIT *uptr); +t_stat td_boot (int32 unitno, DEVICE *dptr); +t_bool td_newsa (int32 newf); +t_bool td_setpos (UNIT *uptr); +int32 td_header (UNIT *uptr, int32 blk, int32 line); +int32 td_trailer (UNIT *uptr, int32 blk, int32 line); +int32 td_read (UNIT *uptr, int32 blk, int32 line); +void td_write (UNIT *uptr, int32 blk, int32 line, int32 datb); +int32 td_set_mtk (int32 code, int32 u, int32 k); +t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, CONST void *desc); + +extern uint16 M[]; + +/* TD data structures + + td_dev DT device descriptor + td_unit DT unit list + td_reg DT register list + td_mod DT modifier list +*/ + +DIB td_dib = { DEV_TD8E, 1, { &td77 } }; + +UNIT td_unit[] = { + { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) } + }; + +REG td_reg[] = { + { GRDATAD (TDCMD, td_cmd, 8, 4, 8, "command register") }, + { ORDATAD (TDDAT, td_dat, 12, "data register") }, + { ORDATAD (TDMTK, td_mtk, 6, "mark track register") }, + { FLDATAD (TDSLF, td_slf, 0, "single line flag") }, + { FLDATAD (TDQLF, td_qlf, 0, "quad line flag") }, + { FLDATAD (TDTME, td_tme, 0, "timing error flag") }, + { ORDATAD (TDQL, td_qlctr, 2, "quad line counter") }, + { ORDATA (TDCSUM, td_csum, 6), REG_RO }, + { DRDATAD (LTIME, td_ltime, 31, "time between lines"), REG_NZ | PV_LEFT }, + { DRDATAD (DCTIME, td_dctime, 31, "time to decelerate to a full stop"), REG_NZ | PV_LEFT }, + { URDATAD (POS, td_unit[0].pos, 10, T_ADDR_W, 0, + DT_NUMDR, PV_LEFT | REG_RO, "positions, in lines, units 0 and 1") }, + { URDATAD (STATT, td_unit[0].STATE, 8, 18, 0, + DT_NUMDR, REG_RO, "unit state, units 0 and 1") }, + { URDATA (LASTT, td_unit[0].LASTT, 10, 32, 0, + DT_NUMDR, REG_HRO) }, + { FLDATAD (STOP_OFFR, td_stopoffr, 0, "stop on off-reel error") }, + { ORDATA (DEVNUM, td_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB td_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "POSITION", NULL, NULL, &td_show_pos }, + { 0 } + }; + +DEVICE td_dev = { + "TD", td_unit, td_reg, td_mod, + DT_NUMDR, 8, 24, 1, 8, 12, + NULL, NULL, &td_reset, + &td_boot, &td_attach, &td_detach, + &td_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 td77 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; +int32 u = TDC_GETUNIT (td_cmd); /* get unit */ +int32 diff, t; + +switch (pulse) { + + case 01: /* SDSS */ + if (td_slf) + return AC | IOT_SKP; + break; + + case 02: /* SDST */ + if (td_tme) + return AC | IOT_SKP; + break; + + case 03: /* SDSQ */ + if (td_qlf) + return AC | IOT_SKP; + break; + + case 04: /* SDLC */ + td_tme = 0; /* clear tim err */ + diff = (td_cmd ^ AC) & TDC_MASK; /* cmd changes */ + td_cmd = AC & TDC_MASK; /* update cmd */ + if ((diff != 0) && (diff != TDC_RW)) { /* signif change? */ + if (td_newsa (td_cmd)) /* new command */ + return AC | (IORETURN (td_stopoffr, STOP_DTOFF) << IOT_V_REASON); + } + AC = 0; + break; + + case 05: /* SDLD */ + td_slf = 0; /* clear flags */ + td_qlf = 0; + td_qlctr = 0; + td_dat = AC; /* load data reg */ + break; + + case 06: /* SDRC */ + td_slf = 0; /* clear flags */ + td_qlf = 0; + td_qlctr = 0; + t = td_cmd | td_mtk; /* form status */ + if (td_tme || !(td_unit[u].flags & UNIT_ATT)) /* tim/sel err? */ + t = t | TDS_TME; + if (td_unit[u].flags & UNIT_WPRT) /* write locked? */ + t = t | TDS_WLO; + return t; /* return status */ + + case 07: /* SDRD */ + td_slf = 0; /* clear flags */ + td_qlf = 0; + td_qlctr = 0; + return td_dat; /* return data */ + } + +return AC; +} + +/* Command register change (start/stop, forward/reverse, new unit) + + 1. If change in motion, stop to start + - schedule up to speed + - set function as next state + 2. If change in motion, start to stop, or change in direction + - schedule stop +*/ + +t_bool td_newsa (int32 newf) +{ +int32 prev_mving, new_mving, prev_dir, new_dir; +UNIT *uptr; + +uptr = td_dev.units + TDC_GETUNIT (newf); /* new unit */ +if ((uptr->flags & UNIT_ATT) == 0) /* new unit attached? */ + return FALSE; + +new_mving = ((newf & TDC_STPGO) != 0); /* new moving? */ +prev_mving = (uptr->STATE != STA_STOP); /* previous moving? */ +new_dir = ((newf & TDC_FWDRV) != 0); /* new dir? */ +prev_dir = ((uptr->STATE & STA_DIR) != 0); /* previous dir? */ + +td_mtk = 0; /* mark trk reg cleared */ + +if (!prev_mving && !new_mving) /* stop from stop? */ + return FALSE; + +if (new_mving && !prev_mving) { /* start from stop? */ + if (td_setpos (uptr)) /* update pos */ + return TRUE; + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, td_dctime - (td_dctime >> 2)); /* sched accel */ + uptr->STATE = STA_ACC | new_dir; /* set status */ + td_slf = td_qlf = td_qlctr = 0; /* clear state */ + return FALSE; + } + +if ((prev_mving && !new_mving) || /* stop from moving? */ + (prev_dir != new_dir)) { /* dir chg while moving? */ + if (uptr->STATE >= STA_ACC) { /* not stopping? */ + if (td_setpos (uptr)) /* update pos */ + return TRUE; + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, td_dctime); /* schedule decel */ + uptr->STATE = STA_DEC | prev_dir; /* set status */ + td_slf = td_qlf = td_qlctr = 0; /* clear state */ + } + return FALSE; + } + +return FALSE; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/td_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool td_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr->LASTT; /* elapsed time */ +if (ut == 0) /* no time gone? exit */ + return FALSE; +uptr->LASTT = new_time; /* update last time */ +switch (uptr->STATE & ~STA_DIR) { /* case on motion */ + + case STA_STOP: /* stop */ + delta = 0; + break; + + case STA_DEC: /* slowing */ + ulin = ut / (uint32) td_ltime; + udelt = td_dctime / td_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; + + case STA_ACC: /* accelerating */ + ulin = ut / (uint32) td_ltime; + udelt = (td_dctime - (td_dctime >> 2)) / td_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; + + case STA_UTS: /* at speed */ + delta = ut / (uint32) td_ltime; + break; + } + +if (uptr->STATE & STA_DIR) /* update pos */ + uptr->pos = uptr->pos - delta; +else uptr->pos = uptr->pos + delta; +if (((int32) uptr->pos < 0) || + ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { + detach_unit (uptr); /* off reel */ + sim_cancel (uptr); /* no timing pulses */ + return TRUE; + } +return FALSE; +} + +/* Unit service - unit is either changing speed, or it is up to speed */ + +t_stat td_svc (UNIT *uptr) +{ +int32 mot = uptr->STATE & ~STA_DIR; +int32 dir = uptr->STATE & STA_DIR; +int32 unum = uptr - td_dev.units; +int32 su = TDC_GETUNIT (td_cmd); +int32 mtkb, datb; + +/* Motion cases + + Decelerating - if go, next state must be accel as specified by td_cmd + Accelerating - next state must be up to speed, fall through + Up to speed - process line */ + +if (mot == STA_STOP) /* stopped? done */ + return SCPE_OK; +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + uptr->STATE = uptr->pos = 0; /* also done */ + return SCPE_UNATT; + } + +switch (mot) { /* case on motion */ + + case STA_DEC: /* deceleration */ + if (td_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (td_stopoffr, STOP_DTOFF); + if ((unum != su) || !(td_cmd & TDC_STPGO)) /* not sel or stop? */ + uptr->STATE = 0; /* stop */ + else { /* selected and go */ + uptr->STATE = STA_ACC | /* accelerating */ + ((td_cmd & TDC_FWDRV)? STA_DIR: 0); /* in new dir */ + sim_activate (uptr, td_dctime - (td_dctime >> 2)); + } + return SCPE_OK; + + case STA_ACC: /* accelerating */ + if (td_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (td_stopoffr, STOP_DTOFF); + uptr->STATE = STA_UTS | dir; /* set up to speed */ + break; + + case STA_UTS: /* up to speed */ + if (dir) /* adjust position */ + uptr->pos = uptr->pos - 1; + else uptr->pos = uptr->pos + 1; + uptr->LASTT = sim_grtime (); /* save time */ + if (((int32) uptr->pos < 0) || /* off reel? */ + (uptr->pos >= (((uint32) DTU_FWDEZ (uptr)) + DT_EZLIN))) { + detach_unit (uptr); + return IORETURN (td_stopoffr, STOP_DTOFF); + } + break; /* check function */ + } + +/* At speed - process the current line + + Once the TD8E is running at speed, it operates line by line. If reading, + the current mark track bit is shifted into the mark track register, and + the current data nibble (3b) is shifted into the data register. If + writing, the current mark track bit is shifted into the mark track + register, the top nibble from the data register is written to tape, and + the data register is shifted up. The complexity here comes from + synthesizing the mark track, based on tape position, and the header data. */ + +sim_activate (uptr, td_ltime); /* sched next line */ +if (unum != su) /* not sel? done */ + return SCPE_OK; +td_slf = 1; /* set single */ +td_qlctr = (td_qlctr + 1) % DT_WSIZE; /* count words */ +if (td_qlctr == 0) { /* lines mod 4? */ + if (td_qlf) { /* quad line set? */ + td_tme = 1; /* timing error */ + td_cmd = td_cmd & ~TDC_RW; /* clear write */ + } + else td_qlf = 1; /* no, set quad */ + } + +datb = 0; /* assume no data */ +if (uptr->pos < (DT_EZLIN - DT_BFLIN)) /* rev end zone? */ + mtkb = MTK_BIT (MTK_REV_END, uptr->pos); +else if (uptr->pos < DT_EZLIN) /* rev buffer? */ + mtkb = MTK_BIT (MTK_INTER, uptr->pos); +else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */ + int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */ + int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */ + if (lineno < DT_HTLIN) { /* header? */ + if ((td_cmd & TDC_RW) == 0) /* read? */ + datb = td_header (uptr, blkno, lineno); /* get nibble */ + } + else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) { /* data? */ + if (td_cmd & TDC_RW) /* write? */ + td_write (uptr, blkno, /* write data nibble */ + lineno - DT_HTLIN, /* data rel line num */ + (td_dat >> 9) & 07); + else datb = td_read (uptr, blkno, /* no, read */ + lineno - DT_HTLIN); + } + else if ((td_cmd & TDC_RW) == 0) /* trailer; read? */ + datb = td_trailer (uptr, blkno, lineno - /* get trlr nibble */ + (DTU_LPERB (uptr) - DT_HTLIN)); + mtkb = tdb_mtk[unum][lineno]; + } +else if (uptr->pos < (((uint32) DTU_FWDEZ (uptr)) + DT_BFLIN)) + mtkb = MTK_BIT (MTK_INTER, uptr->pos); /* fwd buffer? */ +else mtkb = MTK_BIT (MTK_FWD_END, uptr->pos); /* fwd end zone */ + +if (dir) { /* reverse? */ + mtkb = mtkb ^ 01; /* complement mark bit, */ + datb = datb ^ 07; /* data bits */ + } +td_mtk = ((td_mtk << 1) | mtkb) & MTK_MASK; /* shift mark reg */ +td_dat = ((td_dat << 3) | datb) & 07777; /* shift data reg */ +return SCPE_OK; +} + +/* Header read - reads out 18b words in 3b increments + + word lines contents + 0 0-5 0 + 1 6-11 block number + 2 12-17 0 + 3 18-23 0 + 4 24-29 reverse checksum (0777777) +*/ + +int32 td_header (UNIT *uptr, int32 blk, int32 line) +{ +int32 nibp; + +switch (line) { + + case 8: case 9: case 10: case 11: /* block num */ + nibp = 3 * (DT_LPERMC - 1 - (line % DT_LPERMC)); + return (blk >> nibp) & 07; + + case 24: case 25: case 26: case 27: case 28: case 29: /* rev csum */ + return 07; /* 777777 */ + + default: + return 0; + } +} + +/* Trailer read - reads out 18b words in 3b increments + Checksum is stored to avoid double calculation + + word lines contents + 0 0-5 forward checksum (lines 0-1, rest 0) + 1 6-11 0 + 2 12-17 0 + 3 18-23 reverse block mark + 4 24-29 0 + + Note that the reverse block mark (when read forward) appears + as the complement obverse (3b nibbles swapped end for end and + complemented). +*/ + +int32 td_trailer (UNIT *uptr, int32 blk, int32 line) +{ +int32 nibp, i, ba; +int16 *fbuf= (int16 *) uptr->filebuf; + +switch (line) { + + case 0: + td_csum = 07777; /* init csum */ + ba = blk * DTU_BSIZE (uptr); + for (i = 0; i < DTU_BSIZE (uptr); i++) /* loop thru buf */ + td_csum = (td_csum ^ ~fbuf[ba + i]) & 07777; + td_csum = ((td_csum >> 6) ^ td_csum) & 077; + return (td_csum >> 3) & 07; + + case 1: + return (td_csum & 07); + + case 18: case 19: case 20: case 21: + nibp = 3 * (line % DT_LPERMC); + return ((blk >> nibp) & 07) ^ 07; + + default: + return 0; + } +} + +/* Data read - convert block number/data line # to offset in data array */ + +int32 td_read (UNIT *uptr, int32 blk, int32 line) +{ +int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */ +uint32 ba = blk * DTU_BSIZE (uptr); /* block base */ +int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */ + +ba = ba + (line / DT_WSIZE); /* block addr */ +return (fbuf[ba] >> nibp) & 07; /* get data nibble */ +} + +/* Data write - convert block number/data line # to offset in data array */ + +void td_write (UNIT *uptr, int32 blk, int32 line, int32 dat) +{ +int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */ +uint32 ba = blk * DTU_BSIZE (uptr); /* block base */ +int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */ + +ba = ba + (line / DT_WSIZE); /* block addr */ +fbuf[ba] = (fbuf[ba] & ~(07 << nibp)) | (dat << nibp); /* upd data nibble */ +uptr->WRITTEN = TRUE; +if (ba >= uptr->hwmark) /* upd length */ + uptr->hwmark = ba + 1; +return; +} + +/* Reset routine */ + +t_stat td_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ + uptr = td_dev.units + i; + if (sim_is_running) { /* CAF? */ + if (uptr->STATE >= STA_ACC) { /* accel or uts? */ + if (td_setpos (uptr)) /* update pos */ + continue; + sim_cancel (uptr); + sim_activate (uptr, td_dctime); /* sched decel */ + uptr->STATE = STA_DEC | (uptr->STATE & STA_DIR); + } + } + else { + sim_cancel (uptr); /* sim reset */ + uptr->STATE = 0; + uptr->LASTT = sim_grtime (); + } + } +td_slf = td_qlf = td_qlctr = 0; /* clear state */ +td_cmd = td_dat = td_mtk = 0; +td_csum = 0; +return SCPE_OK; +} + +/* Bootstrap routine - OS/8 only + + 1) Read reverse until reverse end zone (mark track is complement obverse) + 2) Read forward until mark track code 031. This is a composite code from + the last 4b of the forward block number and the first two bits of the + reverse guard (01 -0110 01- 1010). There are 16 lines before the first + data word. + 3) Store data words from 7354 to end of page. This includes header and + trailer words. + 4) Continue at location 7400. +*/ + +#define BOOT_START 07300 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 01312, /* ST, TAD L4MT ;=2000, reverse */ + 04312, /* JMS L4MT ; rev lk for 022 */ + 04312, /* JMS L4MT ; fwd lk for 031 */ + 06773, /* DAT, SDSQ ; wait for 12b */ + 05303, /* JMP .-1 */ + 06777, /* SDRD ; read word */ + 03726, /* DCA I BUF ; store */ + 02326, /* ISZ BUF ; incr ptr */ + 05303, /* JMP DAT ; if not 0, cont */ + 05732, /* JMP I SCB ; jump to boot */ + 02000, /* L4MT,2000 ; overwritten */ + 01300, /* TAD ST ; =1312, go */ + 06774, /* SDLC ; new command */ + 06771, /* MTK, SDSS ; wait for mark */ + 05315, /* JMP .-1 */ + 06776, /* SDRC ; get mark code */ + 00331, /* AND K77 ; mask to 6b */ + 01327, /* CMP, TAD MCD ; got target code? */ + 07640, /* SZA CLA ; skip if yes */ + 05315, /* JMP MTK ; wait for mark */ + 02321, /* ISZ CMP ; next target */ + 05712, /* JMP I L4MT ; exit */ + 07354, /* BUF, 7354 ; loading point */ + 07756, /* MCD, -22 ; target 1 */ + 07747, /* -31 ; target 2 */ + 00077, /* 77 ; mask */ + 07400 /* SCB, 7400 ; secondary boot */ + }; + +t_stat td_boot (int32 unitno, DEVICE *dptr) +{ +size_t i; + +if (unitno) + return SCPE_ARG; /* only unit 0 */ +if (td_dib.dev != DEV_TD8E) + return STOP_NOTSTD; /* only std devno */ +td_unit[unitno].pos = DT_EZLIN; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = boot_rom[i]; +cpu_set_bootpc (BOOT_START); +return SCPE_OK; +} + +/* Attach routine + + Determine 12b, 16b, or 18b/36b format + Allocate buffer + If 16b or 18b, read 16b or 18b format and convert to 12b in buffer + If 12b, read data into buffer + Set up mark track bit array +*/ + +t_stat td_attach (UNIT *uptr, CONST char *cptr) +{ +uint32 pdp18b[D18_NBSIZE]; +uint16 pdp11b[D18_NBSIZE], *fbuf; +int32 i, k, mtkpb; +int32 u = uptr - td_dev.units; +t_stat r; +uint32 ba, sz; + +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) /* fail? */ + return r; +if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ + uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; + if (sim_switches & SWMASK ('F')) /* att 18b? */ + uptr->flags = uptr->flags & ~UNIT_8FMT; + else if (sim_switches & SWMASK ('S')) /* att 16b? */ + uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; + else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ + (sz = sim_fsize (uptr->fileref))) { + if (sz == D11_FILSIZ) + uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; + else if (sz > D8_FILSIZ) + uptr->flags = uptr->flags & ~UNIT_8FMT; + } + } +uptr->capac = DTU_CAPAC (uptr); /* set capacity */ +uptr->filebuf = calloc (uptr->capac, sizeof (int16)); +if (uptr->filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; + } +fbuf = (uint16 *) uptr->filebuf; /* file buffer */ +sim_printf ("%s%d: ", sim_dname (&td_dev), u); +if (uptr->flags & UNIT_8FMT) + sim_printf ("12b format"); +else if (uptr->flags & UNIT_11FMT) + sim_printf ("16b format"); +else sim_printf ("18b/36b format"); +sim_printf (", buffering file in memory\n"); +uptr->io_flush = td_flush; +if (uptr->flags & UNIT_8FMT) /* 12b? */ + uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16), + uptr->capac, uptr->fileref); +else { /* 16b/18b */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + if (uptr->flags & UNIT_11FMT) { + k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); + for (i = 0; i < k; i++) + pdp18b[i] = pdp11b[i]; + } + else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); + if (k == 0) + break; + for ( ; k < D18_NBSIZE; k++) + pdp18b[k] = 0; + for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */ + fbuf[ba] = (pdp18b[k] >> 6) & 07777; + fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) | + ((pdp18b[k + 1] >> 12) & 077); + fbuf[ba + 2] = pdp18b[k + 1] & 07777; + ba = ba + 3; + } /* end blk loop */ + } /* end file loop */ + uptr->hwmark = ba; + } /* end else */ +uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ +uptr->pos = DT_EZLIN; /* beyond leader */ +uptr->LASTT = sim_grtime (); /* last pos update */ +uptr->STATE = STA_STOP; /* stopped */ + +mtkpb = (DTU_BSIZE (uptr) * DT_WSIZE) / DT_LPERMC; /* mtk codes per blk */ +k = td_set_mtk (MTK_INTER, u, 0); /* fill mark track */ +k = td_set_mtk (MTK_FWD_BLK, u, k); /* bit array */ +k = td_set_mtk (MTK_REV_GRD, u, k); +for (i = 0; i < 4; i++) + k = td_set_mtk (MTK_FWD_PRE, u, k); +for (i = 0; i < (mtkpb - 4); i++) + k = td_set_mtk (MTK_DATA, u, k); +for (i = 0; i < 4; i++) + k = td_set_mtk (MTK_REV_PRE, u, k); +k = td_set_mtk (MTK_FWD_GRD, u, k); +k = td_set_mtk (MTK_REV_BLK, u, k); +k = td_set_mtk (MTK_INTER, u, k); +return SCPE_OK; +} + +/* Detach routine + + If 12b, write buffer to file + If 16b or 18b, convert 12b buffer to 16b or 18b and write to file + Deallocate buffer +*/ + +void td_flush (UNIT* uptr) +{ +uint32 pdp18b[D18_NBSIZE]; +uint16 pdp11b[D18_NBSIZE], *fbuf; +int32 i, k; +uint32 ba; + +if (uptr->WRITTEN && uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ + rewind (uptr->fileref); /* start of file */ + fbuf = (uint16 *) uptr->filebuf; /* file buffer */ + if (uptr->flags & UNIT_8FMT) /* PDP8? */ + fxwrite (uptr->filebuf, sizeof (uint16), /* write file */ + uptr->hwmark, uptr->fileref); + else { /* 16b/18b */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */ + for (k = 0; k < D18_NBSIZE; k = k + 2) { + pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) | + ((uint32) (fbuf[ba + 1] >> 6) & 077); + pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) | + ((uint32) (fbuf[ba + 2] & 07777)); + ba = ba + 3; + } /* end loop blk */ + if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (i = 0; i < D18_NBSIZE; i++) + pdp11b[i] = pdp18b[i]; + fxwrite (pdp11b, sizeof (uint16), + D18_NBSIZE, uptr->fileref); + } + else fxwrite (pdp18b, sizeof (uint32), + D18_NBSIZE, uptr->fileref); + } /* end loop buf */ + } /* end else */ + if (ferror (uptr->fileref)) + sim_perror ("I/O error"); + } +uptr->WRITTEN = FALSE; /* no longer dirty */ +} + +t_stat td_detach (UNIT* uptr) +{ +int u = (int)(uptr - td_dev.units); + +if (!(uptr->flags & UNIT_ATT)) + return SCPE_OK; +if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ + sim_printf ("%s%d: writing buffer to file\n", sim_dname (&td_dev), u); + td_flush (uptr); + } /* end if hwmark */ +free (uptr->filebuf); /* release buf */ +uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ +uptr->filebuf = NULL; /* clear buf ptr */ +uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */ +uptr->capac = DT_CAPAC; /* default size */ +uptr->pos = uptr->STATE = 0; +sim_cancel (uptr); /* no more pulses */ +return detach_unit (uptr); +} + +/* Set mark track code into bit array */ + +int32 td_set_mtk (int32 code, int32 u, int32 k) +{ +int32 i; + +for (i = 5; i >= 0; i--) + tdb_mtk[u][k++] = (code >> i) & 1; +return k; +} + +/* Show position */ + +t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; +if (uptr->pos < DT_EZLIN) /* rev end zone? */ + fprintf (st, "Reverse end zone\n"); +else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */ + int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */ + int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */ + fprintf (st, "Block %d, line %d, ", blkno, lineno); + if (lineno < DT_HTLIN) /* header? */ + fprintf (st, "header cell %d, nibble %d\n", + lineno / DT_LPERMC, lineno % DT_LPERMC); + else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) /* data? */ + fprintf (st, "data word %d, nibble %d\n", + (lineno - DT_HTLIN) / DT_WSIZE, (lineno - DT_HTLIN) % DT_WSIZE); + else fprintf (st, "trailer cell %d, nibble %d\n", + (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) / DT_LPERMC, + (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) % DT_LPERMC); + } +else fprintf (st, "Forward end zone\n"); /* fwd end zone */ +return SCPE_OK; +} + ADDED src/SIMH/PDP8/pdp8_tsc.c Index: src/SIMH/PDP8/pdp8_tsc.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_tsc.c @@ -0,0 +1,158 @@ +/* pdp8_tsc.c: PDP-8 ETOS timesharing option board (TSC8-75) + + Copyright (c) 2003-2011, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module is based on Bernhard Baehr's PDP-8/E simulator + + PDP-8/E Simulator Source Code + + Copyright ) 2001-2003 Bernhard Baehr + + TSC8iots.c - IOTs for the TSC8-75 Board plugin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + tsc TSC8-75 option board +*/ + +#include "pdp8_defs.h" + +extern int32 int_req; +extern int32 SF; +extern int32 tsc_ir; /* "ERIOT" */ +extern int32 tsc_pc; /* "ERTB" */ +extern int32 tsc_cdf; /* "ECDF" */ +extern int32 tsc_enb; /* enable */ + +#define UNIT_V_SN699 (UNIT_V_UF + 0) /* SN 699 or above */ +#define UNIT_SN699 (1 << UNIT_V_SN699) + +int32 tsc (int32 IR, int32 AC); +t_stat tsc_reset (DEVICE *dptr); + +/* TSC data structures + + tsc_dev TSC device descriptor + tsc_unit TSC unit descriptor + tsc_reg TSC register list +*/ + +DIB tsc_dib = { DEV_TSC, 1, { &tsc } }; + +UNIT tsc_unit = { UDATA (NULL, UNIT_SN699, 0) }; + +REG tsc_reg[] = { + { ORDATAD (IR, tsc_ir, 12, "most recently trapped instruction") }, + { ORDATAD (PC, tsc_pc, 12, "PC of most recently trapped instruction") }, + { FLDATAD (CDF, tsc_cdf, 0, "1 if trapped instruction is CDF, 0 otherwise") }, + { FLDATAD (ENB, tsc_enb, 0, "interrupt enable flag") }, + { FLDATAD (INT, int_req, INT_V_TSC, "interrupt pending flag") }, + { NULL } + }; + +MTAB tsc_mod[] = { + { UNIT_SN699, UNIT_SN699, "ESME", "ESME", NULL }, + { UNIT_SN699, 0, "no ESME", "NOESME", NULL }, + { 0 } + }; + +DEVICE tsc_dev = { + "TSC", &tsc_unit, tsc_reg, tsc_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tsc_reset, + NULL, NULL, NULL, + &tsc_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routine */ + +int32 tsc (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* ETDS */ + tsc_enb = 0; /* disable int req */ + int_req = int_req & ~INT_TSC; /* clear flag */ + break; + + case 1: /* ESKP */ + return (int_req & INT_TSC)? IOT_SKP + AC: AC; /* skip on int req */ + + case 2: /* ECTF */ + int_req = int_req & ~INT_TSC; /* clear int req */ + break; + + case 3: /* ECDF */ + AC = AC | ((tsc_ir >> 3) & 07); /* read "ERIOT"<6:8> */ + if (tsc_cdf) /* if cdf, skip */ + AC = AC | IOT_SKP; + tsc_cdf = 0; + break; + + case 4: /* ERTB */ + return tsc_pc; + + case 5: /* ESME */ + if (tsc_unit.flags & UNIT_SN699) { /* enabled? */ + if (tsc_cdf && ((tsc_ir & 070) >> 3) == (SF & 07)) { + AC = AC | IOT_SKP; + tsc_cdf = 0; + } + } + break; + + case 6: /* ERIOT */ + return tsc_ir; + + case 7: /* ETEN */ + tsc_enb = 1; + break; + } /* end switch */ + +return AC; +} + +/* Reset routine */ + +t_stat tsc_reset (DEVICE *dptr) +{ +tsc_ir = 0; +tsc_pc = 0; +tsc_cdf = 0; +tsc_enb = 0; +int_req = int_req & ~INT_TSC; +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_tt.c Index: src/SIMH/PDP8/pdp8_tt.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_tt.c @@ -0,0 +1,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 + +extern int32 int_req, int_enable, dev_done, stop_inst; +extern int32 tmxr_poll; + +int32 tti (int32 IR, int32 AC); +int32 tto (int32 IR, int32 AC); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc); + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list + tti_mod TTI modifiers list +*/ + +DIB tti_dib = { DEV_TTI, 1, { &tti } }; + +UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE|TT_MODE_KSR, 0), SERIAL_IN_WAIT }; + +REG tti_reg[] = { + { ORDATAD (BUF, tti_unit.buf, 8, "last data item processed") }, + { FLDATAD (DONE, dev_done, INT_V_TTI, "device done flag") }, + { FLDATAD (ENABLE, int_enable, INT_V_TTI, "interrupt enable flag") }, + { FLDATAD (INT, int_req, INT_V_TTI, "interrupt pending flag") }, + { DRDATAD (POS, tti_unit.pos, T_ADDR_W, "number of characters input"), PV_LEFT }, + { DRDATAD (TIME, tti_unit.wait, 24, "input polling interval (if 0, the keyboard is polled synchronously with the clock)"), PV_LEFT+REG_NZ }, + { NULL } + }; + +MTAB tti_mod[] = { + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev, NULL }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL, + &tti_dib, 0 + }; + +uint32 tti_buftime; /* time input character arrived */ + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +DIB tto_dib = { DEV_TTO, 1, { &tto } }; + +UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATAD (BUF, tto_unit.buf, 8, "last date item processed") }, + { FLDATAD (DONE, dev_done, INT_V_TTO, "device done flag") }, + { FLDATAD (ENABLE, int_enable, INT_V_TTO, "interrupt enable flag") }, + { FLDATAD (INT, int_req, INT_V_TTO, "interrupt pending flag") }, + { DRDATAD (POS, tto_unit.pos, T_ADDR_W, "number of characters output"), PV_LEFT }, + { DRDATAD (TIME, tto_unit.wait, 24, "time form I/O initiation to interrupt"), PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL, + &tto_dib, 0 + }; + +/* Terminal input: IOT routine */ + +int32 tti (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + case 0: /* KCF */ + dev_done = dev_done & ~INT_TTI; /* clear flag */ + int_req = int_req & ~INT_TTI; + return AC; + + case 1: /* KSF */ + return (dev_done & INT_TTI)? IOT_SKP + AC: AC; + + case 2: /* KCC */ + dev_done = dev_done & ~INT_TTI; /* clear flag */ + int_req = int_req & ~INT_TTI; + return 0; /* clear AC */ + + case 4: /* KRS */ + return (AC | tti_unit.buf); /* return buffer */ + + case 5: /* KIE */ + if (AC & 1) + int_enable = int_enable | (INT_TTI+INT_TTO); + else int_enable = int_enable & ~(INT_TTI+INT_TTO); + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 6: /* KRB */ + dev_done = dev_done & ~INT_TTI; /* clear flag */ + int_req = int_req & ~INT_TTI; + sim_activate_abs (&tti_unit, tti_unit.wait); /* check soon for more input */ + return (tti_unit.buf); /* return buffer */ + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 c; + +sim_clock_coschedule (uptr, tmxr_poll); /* continue poll */ +if ((dev_done & INT_TTI) && /* prior character still pending and < 500ms? */ + ((sim_os_msec () - tti_buftime) < 500)) + return SCPE_OK; +if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */ + return c; +if (c & SCPE_BREAK) /* break? */ + uptr->buf = 0; +else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); +tti_buftime = sim_os_msec (); +uptr->pos = uptr->pos + 1; +dev_done = dev_done | INT_TTI; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tti_reset (DEVICE *dptr) +{ +tmxr_set_console_units (&tti_unit, &tto_unit); +tti_unit.buf = 0; +dev_done = dev_done & ~INT_TTI; /* clear done, int */ +int_req = int_req & ~INT_TTI; +int_enable = int_enable | INT_TTI; /* set enable */ +if (!sim_is_running) /* RESET (not CAF)? */ + sim_activate (&tti_unit, tmxr_poll); +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* TLF */ + dev_done = dev_done | INT_TTO; /* set flag */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 1: /* TSF */ + return (dev_done & INT_TTO)? IOT_SKP + AC: AC; + + case 2: /* TCF */ + dev_done = dev_done & ~INT_TTO; /* clear flag */ + int_req = int_req & ~INT_TTO; /* clear int req */ + return AC; + + case 5: /* SPI */ + return (int_req & (INT_TTI+INT_TTO))? IOT_SKP + AC: AC; + + case 6: /* TLS */ + dev_done = dev_done & ~INT_TTO; /* clear flag */ + int_req = int_req & ~INT_TTO; /* clear int req */ + case 4: /* TPC */ + sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ + tto_unit.buf = AC; /* load buffer */ + return AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); +if (c >= 0) { + if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output char; error? */ + sim_activate (uptr, uptr->wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* if !stall, report */ + } + } +dev_done = dev_done | INT_TTO; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +uptr->pos = uptr->pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; +dev_done = dev_done & ~INT_TTO; /* clear done, int */ +int_req = int_req & ~INT_TTO; +int_enable = int_enable | INT_TTO; /* set enable */ +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat tty_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; +tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; +return SCPE_OK; +} ADDED src/SIMH/PDP8/pdp8_ttx.c Index: src/SIMH/PDP8/pdp8_ttx.c ================================================================== --- /dev/null +++ src/SIMH/PDP8/pdp8_ttx.c @@ -0,0 +1,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 + +#define TTX_MAXL 16 +#define TTX_INIL 4 + +#define TTX_GETLN(x) (((x) >> 4) & TTX_MASK) + +extern int32 int_req, int_enable, dev_done, stop_inst; +extern int32 tmxr_poll; + +uint32 ttix_done = 0; /* input ready flags */ +uint32 ttox_done = 0; /* output ready flags */ +uint32 ttx_enbl = 0; /* intr enable flags */ +uint8 ttix_buf[TTX_MAXL] = { 0 }; /* input buffers */ +uint8 ttox_buf[TTX_MAXL] = { 0 }; /* output buffers */ +TMLN ttx_ldsc[TTX_MAXL] = { {0} }; /* line descriptors */ +TMXR ttx_desc = { TTX_INIL, 0, 0, ttx_ldsc }; /* mux descriptor */ +#define ttx_lines ttx_desc.lines + +int32 ttix (int32 IR, int32 AC); +int32 ttox (int32 IR, int32 AC); +t_stat ttix_svc (UNIT *uptr); +t_stat ttox_svc (UNIT *uptr); +int32 ttx_getln (int32 inst); +void ttx_new_flags (uint32 newi, uint32 newo, uint32 newe); +t_stat ttx_reset (DEVICE *dptr); +t_stat ttx_attach (UNIT *uptr, CONST char *cptr); +t_stat ttx_detach (UNIT *uptr); +void ttx_reset_ln (int32 i); +t_stat ttx_vlines (UNIT *uptr, int32 val, CONST char *cptr, void *desc); +t_stat ttx_show_devno (FILE *st, UNIT *uptr, int32 val, CONST void *desc); + +#define TTIX_SET_DONE(ln) ttx_new_flags (ttix_done | (1u << (ln)), ttox_done, ttx_enbl) +#define TTIX_CLR_DONE(ln) ttx_new_flags (ttix_done & ~(1u << (ln)), ttox_done, ttx_enbl) +#define TTIX_TST_DONE(ln) ((ttix_done & (1u << (ln))) != 0) +#define TTOX_SET_DONE(ln) ttx_new_flags (ttix_done, ttox_done | (1u << (ln)), ttx_enbl) +#define TTOX_CLR_DONE(ln) ttx_new_flags (ttix_done, ttox_done & ~(1u << (ln)), ttx_enbl) +#define TTOX_TST_DONE(ln) ((ttox_done & (1u << (ln))) != 0) +#define TTX_SET_ENBL(ln) ttx_new_flags (ttix_done, ttox_done, ttx_enbl | (1u << (ln))) +#define TTX_CLR_ENBL(ln) ttx_new_flags (ttix_done, ttox_done, ttx_enbl & ~(1u << (ln))) +#define TTX_TST_ENBL(ln) ((ttx_enbl & (1u << (ln))) != 0) + +/* TTIx data structures + + ttix_dev TTIx device descriptor + ttix_unit TTIx unit descriptor + ttix_reg TTIx register list + ttix_mod TTIx modifiers list +*/ + +DIB_DSP ttx_dsp[TTX_MAXL * 2] = { + { DEV_TTI1, &ttix }, { DEV_TTO1, &ttox }, + { DEV_TTI2, &ttix }, { DEV_TTO2, &ttox }, + { DEV_TTI3, &ttix }, { DEV_TTO3, &ttox }, + { DEV_TTI4, &ttix }, { DEV_TTO4, &ttox }, + { DEV_TTI5, &ttix }, { DEV_TTO5, &ttox }, + { DEV_TTI6, &ttix }, { DEV_TTO6, &ttox }, + { DEV_TTI7, &ttix }, { DEV_TTO7, &ttox }, + { DEV_TTI8, &ttix }, { DEV_TTO8, &ttox }, + { DEV_TTI9, &ttix }, { DEV_TTO9, &ttox }, + { DEV_TTI10, &ttix }, { DEV_TTO10, &ttox }, + { DEV_TTI11, &ttix }, { DEV_TTO11, &ttox }, + { DEV_TTI12, &ttix }, { DEV_TTO12, &ttox }, + { DEV_TTI13, &ttix }, { DEV_TTO13, &ttox }, + { DEV_TTI14, &ttix }, { DEV_TTO14, &ttox }, + { DEV_TTI15, &ttix }, { DEV_TTO15, &ttox }, + { DEV_TTI16, &ttix }, { DEV_TTO16, &ttox } + }; + +DIB ttx_dib = { DEV_TTI1, TTX_INIL * 2, { &ttix, &ttox }, ttx_dsp }; + +UNIT ttix_unit = { UDATA (&ttix_svc, UNIT_IDLE|UNIT_ATTABLE, 0), SERIAL_IN_WAIT }; + +REG ttix_reg[] = { + { BRDATAD (BUF, ttix_buf, 8, 8, TTX_MAXL, "input buffer, lines 0 to 15") }, + { ORDATAD (DONE, ttix_done, TTX_MAXL, "device done flag (line 0 rightmost)") }, + { ORDATAD (ENABLE, ttx_enbl, TTX_MAXL, "interrupt enable flag") }, + { FLDATA (SUMDONE, dev_done, INT_V_TTI1), REG_HRO }, + { FLDATA (SUMENABLE, int_enable, INT_V_TTI1), REG_HRO }, + { DRDATAD (TIME, ttix_unit.wait, 24, "initial polling interval"), REG_NZ + PV_LEFT }, + { DRDATA (LINES, ttx_desc.lines, 6), REG_HRO }, + { NULL } + }; + +MTAB ttix_mod[] = { + { MTAB_VDV, 0, "LINES", "LINES", &ttx_vlines, &tmxr_show_lines, (void *) &ttx_desc }, + { MTAB_VDV, 0, "DEVNO", NULL, NULL, &ttx_show_devno, (void *) &ttx_desc }, + { UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, NULL, &tmxr_show_summ, (void *) &ttx_desc }, + { MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &ttx_desc }, + { MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &ttx_desc }, + { MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &ttx_desc }, + { 0 } + }; + +/* debugging bitmaps */ +#define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */ +#define DBG_RCV TMXR_DBG_RCV /* display Received Data */ +#define DBG_RET TMXR_DBG_RET /* display Returned Received Data */ +#define DBG_CON TMXR_DBG_CON /* display connection activities */ +#define DBG_TRC TMXR_DBG_TRC /* display trace routine calls */ + +DEBTAB ttx_debug[] = { + {"XMT", DBG_XMT, "Transmitted Data"}, + {"RCV", DBG_RCV, "Received Data"}, + {"RET", DBG_RET, "Returned Received Data"}, + {"CON", DBG_CON, "connection activities"}, + {"TRC", DBG_TRC, "trace routine calls"}, + {0} +}; + +DEVICE ttix_dev = { + "TTIX", &ttix_unit, ttix_reg, ttix_mod, + 1, 10, 31, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &ttx_reset, + NULL, &ttx_attach, &ttx_detach, + &ttx_dib, DEV_MUX | DEV_DISABLE | DEV_DEBUG, + 0, ttx_debug + }; + +/* TTOx data structures + + ttox_dev TTOx device descriptor + ttox_unit TTOx unit descriptor + ttox_reg TTOx register list +*/ + +UNIT ttox_unit[] = { + { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC+UNIT_DIS, 0), SERIAL_OUT_WAIT } + }; + +REG ttox_reg[] = { + { BRDATAD (BUF, ttox_buf, 8, 8, TTX_MAXL, "last data item processed, lines 0 to 3") }, + { ORDATAD (DONE, ttox_done, TTX_MAXL, "device done flag (line 0 rightmost)") }, + { ORDATAD (ENABLE, ttx_enbl, TTX_MAXL, "interrupt enable flag") }, + { FLDATA (SUMDONE, dev_done, INT_V_TTO1), REG_HRO }, + { FLDATA (SUMENABLE, int_enable, INT_V_TTO1), REG_HRO }, + { URDATAD (TIME, ttox_unit[0].wait, 10, 24, 0, + TTX_MAXL, PV_LEFT, "line from I/O initiation to interrupt, lines 0 to 3") }, + { NULL } + }; + +MTAB ttox_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { MTAB_VDV, 0, "DEVNO", NULL, NULL, &ttx_show_devno, &ttx_desc }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &ttx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &ttx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &ttx_desc }, + { 0 } + }; + +DEVICE ttox_dev = { + "TTOX", ttox_unit, ttox_reg, ttox_mod, + TTX_MAXL, 10, 31, 1, 8, 8, + NULL, NULL, &ttx_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DEBUG, + 0, ttx_debug + }; + +/* Terminal input: IOT routine */ + +int32 ttix (int32 inst, int32 AC) +{ +int32 pulse = inst & 07; /* IOT pulse */ +int32 ln = ttx_getln (inst); /* line # */ + +if (ln < 0) /* bad line #? */ + return (SCPE_IERR << IOT_V_REASON) | AC; + +switch (pulse) { /* case IR<9:11> */ + + case 0: /* KCF */ + TTIX_CLR_DONE (ln); /* clear flag */ + break; + + case 1: /* KSF */ + return (TTIX_TST_DONE (ln))? IOT_SKP | AC: AC; + + case 2: /* KCC */ + TTIX_CLR_DONE (ln); /* clear flag */ + sim_activate_abs (&ttix_unit, ttix_unit.wait); /* check soon for more input */ + return 0; /* clear AC */ + + case 4: /* KRS */ + return (AC | ttix_buf[ln]); /* return buf */ + + case 5: /* KIE */ + if (AC & 1) + TTX_SET_ENBL (ln); + else TTX_CLR_ENBL (ln); + break; + + case 6: /* KRB */ + TTIX_CLR_DONE (ln); /* clear flag */ + sim_activate_abs (&ttix_unit, ttix_unit.wait); /* check soon for more input */ + return ttix_buf[ln]; /* return buf */ + + default: + return (stop_inst << IOT_V_REASON) | AC; + } /* end switch */ + +return AC; +} + +/* Unit service */ + +t_stat ttix_svc (UNIT *uptr) +{ +int32 ln, c, temp; + +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return SCPE_OK; +sim_clock_coschedule (uptr, tmxr_poll); /* continue poll */ +ln = tmxr_poll_conn (&ttx_desc); /* look for connect */ +if (ln >= 0) /* got one? */ + ttx_ldsc[ln].rcve = 1; /* set rcv enable */ +tmxr_poll_rx (&ttx_desc); /* poll for input */ +for (ln = 0; ln < ttx_lines; ln++) { /* loop thru lines */ + if (ttx_ldsc[ln].conn) { /* connected? */ + if (TTIX_TST_DONE (ln)) /* last char still pending? */ + continue; + if ((temp = tmxr_getc_ln (&ttx_ldsc[ln]))) { /* get char */ + if (temp & SCPE_BREAK) /* break? */ + c = 0; + else c = sim_tt_inpcvt (temp, TT_GET_MODE (ttox_unit[ln].flags)); + ttix_buf[ln] = c; + TTIX_SET_DONE (ln); /* set flag */ + } + } + } +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 ttox (int32 inst, int32 AC) +{ +int32 pulse = inst & 07; /* pulse */ +int32 ln = ttx_getln (inst); /* line # */ + +if (ln < 0) /* bad line #? */ + return (SCPE_IERR << IOT_V_REASON) | AC; + +switch (pulse) { /* case IR<9:11> */ + + case 0: /* TLF */ + TTOX_SET_DONE (ln); /* set flag */ + break; + + case 1: /* TSF */ + return (TTOX_TST_DONE (ln))? IOT_SKP | AC: AC; + + case 2: /* TCF */ + TTOX_CLR_DONE (ln); /* clear flag */ + break; + + case 5: /* SPI */ + if ((TTIX_TST_DONE (ln) || TTOX_TST_DONE (ln)) /* either done set */ + && TTX_TST_ENBL (ln)) /* and enabled? */ + return IOT_SKP | AC; + return AC; + + case 6: /* TLS */ + TTOX_CLR_DONE (ln); /* clear flag */ + case 4: /* TPC */ + sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); /* activate */ + ttox_buf[ln] = AC & 0377; /* load buffer */ + break; + + default: + return (stop_inst << IOT_V_REASON) | AC; + } /* end switch */ + +return AC; +} + +/* Unit service */ + +t_stat ttox_svc (UNIT *uptr) +{ +int32 c, ln = uptr - ttox_unit; /* line # */ + +if (ttx_ldsc[ln].conn) { /* connected? */ + if (ttx_ldsc[ln].xmte) { /* tx enabled? */ + TMLN *lp = &ttx_ldsc[ln]; /* get line */ + c = sim_tt_outcvt (ttox_buf[ln], TT_GET_MODE (ttox_unit[ln].flags)); + if (c >= 0) /* output char */ + tmxr_putc_ln (lp, c); + tmxr_poll_tx (&ttx_desc); /* poll xmt */ + } + else { + tmxr_poll_tx (&ttx_desc); /* poll xmt */ + sim_activate (uptr, ttox_unit[ln].wait); /* wait */ + return SCPE_OK; + } + } +TTOX_SET_DONE (ln); /* set done */ +return SCPE_OK; +} + +/* Flag routine + + Global dev_done is used as a master interrupt; therefore, global + int_enable must always be set +*/ + +void ttx_new_flags (uint32 newidone, uint32 newodone, uint32 newenbl) +{ +ttix_done = newidone; +ttox_done = newodone; +ttx_enbl = newenbl; +if ((ttix_done & ttx_enbl) != 0) + dev_done |= INT_TTI1; +else dev_done &= ~INT_TTI1; +if ((ttox_done & ttx_enbl) != 0) + dev_done |= INT_TTO1; +else dev_done &= ~INT_TTO1; +int_enable |= (INT_TTI1 | INT_TTO1); +int_req = INT_UPDATE; +return; +} + +/* Compute relative line number, based on table of device numbers */ + +int32 ttx_getln (int32 inst) +{ +int32 i; +int32 device = (inst >> 3) & 077; /* device = IR<3:8> */ + +for (i = 0; i < (ttx_lines * 2); i++) { /* loop thru disp tbl */ + if (device == ttx_dsp[i].dev) /* dev # match? */ + return (i >> 1); /* return line # */ + } +return -1; +} + +/* Reset routine */ + +t_stat ttx_reset (DEVICE *dptr) +{ +int32 ln; + +if (dptr->flags & DEV_DIS) { /* sync enables */ + ttix_dev.flags |= DEV_DIS; + ttox_dev.flags |= DEV_DIS; + } +else { + ttix_dev.flags &= ~DEV_DIS; + ttox_dev.flags &= ~DEV_DIS; + } +if (ttix_unit.flags & UNIT_ATT) /* if attached, */ + sim_activate (&ttix_unit, tmxr_poll); /* activate */ +else sim_cancel (&ttix_unit); /* else stop */ +for (ln = 0; ln < TTX_MAXL; ln++) /* for all lines */ + ttx_reset_ln (ln); /* reset line */ +int_enable |= (INT_TTI1 | INT_TTO1); /* set master enable */ +return SCPE_OK; +} + +/* Reset line n */ + +void ttx_reset_ln (int32 ln) +{ +uint32 mask = (1u << ln); + +ttix_buf[ln] = 0; /* clr buf */ +ttox_buf[ln] = 0; /* clr done, set enbl */ +ttx_new_flags (ttix_done & ~mask, ttox_done & ~mask, ttx_enbl | mask); +sim_cancel (&ttox_unit[ln]); /* stop output */ +return; +} + +/* Attach master unit */ + +t_stat ttx_attach (UNIT *uptr, CONST char *cptr) +{ +t_stat r; + +r = tmxr_attach (&ttx_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) /* error */ + return r; +sim_activate (uptr, 0); /* start poll at once */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat ttx_detach (UNIT *uptr) +{ +int32 i; +t_stat r; + +r = tmxr_detach (&ttx_desc, uptr); /* detach */ +for (i = 0; i < TTX_MAXL; i++) /* all lines, */ + ttx_ldsc[i].rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Change number of lines */ + +t_stat ttx_vlines (UNIT *uptr, int32 val, CONST char *cptr, void *desc) +{ +int32 newln, i, t; +t_stat r; + +if (cptr == NULL) + return SCPE_ARG; +newln = get_uint (cptr, 10, TTX_MAXL, &r); +if ((r != SCPE_OK) || (newln == ttx_lines)) + return r; +if (newln == 0) + return SCPE_ARG; +if (newln < ttx_lines) { + for (i = newln, t = 0; i < ttx_lines; i++) + t = t | ttx_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < ttx_lines; i++) { + if (ttx_ldsc[i].conn) { + tmxr_linemsg (&ttx_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&ttx_ldsc[i]); /* reset line */ + } + ttox_unit[i].flags |= UNIT_DIS; + ttx_reset_ln (i); + } + } +else { + for (i = ttx_lines; i < newln; i++) { + ttox_unit[i].flags &= ~UNIT_DIS; + ttx_reset_ln (i); + } + } +ttx_lines = newln; +ttx_dib.num = newln * 2; +return SCPE_OK; +} + +/* Show device numbers */ +t_stat ttx_show_devno (FILE *st, UNIT *uptr, int32 val, CONST void *desc) +{ +int32 i, dev_offset; +DEVICE *dptr; + +if (uptr == NULL) + return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) + return SCPE_IERR; +/* Select correct devno entry for Input or Output device */ +if (dptr->name[2] == 'O') + dev_offset = 1; +else + dev_offset = 0; + +fprintf(st, "devno="); +for (i = 0; i < ttx_lines; i++) { + fprintf(st, "%02o%s", ttx_dsp[i*2+dev_offset].dev, i < ttx_lines-1 ? + "," : ""); +} +return SCPE_OK; +} + ADDED src/SIMH/scp.c Index: src/SIMH/scp.c ================================================================== --- /dev/null +++ src/SIMH/scp.c @@ -0,0 +1,12792 @@ +/* scp.c: simulator control program + + 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 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. + + ---------------------------------------------------------------------------- + + 08-Mar-16 RMS Added shutdown flag for detach_all + 20-Mar-12 MP Fixes to "SHOW SHOW" commands + 06-Jan-12 JDB Fixed "SHOW DEVICE" with only one enabled unit (Dave Bryan) + 25-Sep-11 MP Added the ability for a simulator built with + SIM_ASYNCH_IO to change whether I/O is actually done + asynchronously by the new scp command SET ASYNCH and + SET NOASYNCH + 22-Sep-11 MP Added signal catching of SIGHUP and SIGTERM to cause + simulator STOP. This allows an externally signalled + event (i.e. system shutdown, or logoff) to signal a + running simulator of these events and to allow + reasonable actions to be taken. This will facilitate + running a simulator as a 'service' on *nix platforms, + given a sufficiently flexible simulator .ini file. + 20-Apr-11 MP Added expansion of %STATUS% and %TSTATUS% in do command + arguments. STATUS is the numeric value of the last + command error status and TSTATUS is the text message + relating to the last command error status + 17-Apr-11 MP Changed sim_rest to defer attaching devices until after + device register contents have been restored since some + attach activities may reference register contained info. + 29-Jan-11 MP Adjusted sim_debug to: + - include the simulator timestamp (sim_gtime) + as part of the prefix for each line of output + - write complete lines at a time (avoid asynch I/O issues). + 05-Jan-11 MP Added Asynch I/O support + 22-Jan-11 MP Added SET ON, SET NOON, ON, GOTO and RETURN command support + 13-Jan-11 MP Added "SHOW SHOW" and "SHOW SHOW" commands + 05-Jan-11 RMS Fixed bug in deposit stride for numeric input (John Dundas) + 23-Dec-10 RMS Clarified some help messages (Mark Pizzolato) + 08-Nov-10 RMS Fixed handling of DO with no arguments (Dave Bryan) + 22-May-10 RMS Added *nix READLINE support (Mark Pizzolato) + 08-Feb-09 RMS Fixed warnings in help printouts + 29-Dec-08 RMS Fixed implementation of MTAB_NC + 24-Nov-08 RMS Revised RESTORE unit logic for consistency + 05-Sep-08 JDB "detach_all" ignores error status returns if shutting down + 17-Aug-08 RMS Revert RUN/BOOT to standard, rather than powerup, reset + 25-Jul-08 JDB DO cmd missing params now default to null string + 29-Jun-08 JDB DO cmd sub_args now allows "\\" to specify literal backslash + 04-Jun-08 JDB label the patch delta more clearly + 31-Mar-08 RMS Fixed bug in local/global register search (Mark Pizzolato) + Fixed bug in restore of RO units (Mark Pizzolato) + 06-Feb-08 RMS Added SET/SHO/NO BR with default argument + 18-Jul-07 RMS Modified match_ext for VMS ext;version support + 28-Apr-07 RMS Modified sim_instr invocation to call sim_rtcn_init_all + Fixed bug in get_sim_opt + Fixed bug in restoration with changed memory size + 08-Mar-07 JDB Fixed breakpoint actions in DO command file processing + 30-Jan-07 RMS Fixed bugs in get_ipaddr + 17-Oct-06 RMS Added idle support + 04-Oct-06 JDB DO cmd failure now echoes cmd unless -q + 30-Aug-06 JDB detach_unit returns SCPE_UNATT if not attached + 14-Jul-06 RMS Added sim_activate_abs + 02-Jun-06 JDB Fixed do_cmd to exit nested files on assertion failure + Added -E switch to do_cmd to exit on any error + 14-Feb-06 RMS Upgraded save file format to V3.5 + 18-Jan-06 RMS Added fprint_stopped_gen + Added breakpoint spaces + Fixed unaligned register access (Doug Carman) + 22-Sep-05 RMS Fixed declarations (Sterling Garwood) + 30-Aug-05 RMS Revised to trim trailing spaces on file names + 25-Aug-05 RMS Added variable default device support + 23-Aug-05 RMS Added Linux line history support + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 01-May-05 RMS Revised syntax for SET DEBUG (Dave Bryan) + 22-Mar-05 JDB Modified DO command to allow ten-level nesting + 18-Mar-05 RMS Moved DETACH tests into detach_unit (Dave Bryan) + Revised interface to fprint_sym, fparse_sym + 13-Mar-05 JDB ASSERT now requires a conditional operator + 07-Feb-05 RMS Added ASSERT command (Dave Bryan) + 02-Feb-05 RMS Fixed bug in global register search + 26-Dec-04 RMS Qualified SAVE examine, RESTORE deposit with SIM_SW_REST + 10-Nov-04 JDB Fixed logging of errors from cmds in "do" file + 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy + Renamed unit OFFLINE/ONLINE to DISABLED/ENABLED (Dave Bryan) + Revised to flush output files after simulation stop (Dave Bryan) + 15-Oct-04 RMS Fixed HELP to suppress duplicate descriptions + 27-Sep-04 RMS Fixed comma-separation options in set (David Bryan) + 09-Sep-04 RMS Added -p option for RESET + 13-Aug-04 RMS Qualified RESTORE detach with SIM_SW_REST + 17-Jul-04 JDB DO cmd file open failure retries with ".sim" appended + 17-Jul-04 RMS Added ECHO command (Dave Bryan) + 12-Jul-04 RMS Fixed problem ATTACHing to read only files + (John Dundas) + 28-May-04 RMS Added SET/SHOW CONSOLE + 14-Feb-04 RMS Updated SAVE/RESTORE (V3.2) + RMS Added debug print routines (Dave Hittner) + RMS Added sim_vm_parse_addr and sim_vm_fprint_addr + RMS Added REG_VMAD support + RMS Split out libraries + RMS Moved logging function to SCP + RMS Exposed step counter interface(s) + RMS Fixed double logging of SHOW BREAK (Mark Pizzolato) + RMS Fixed implementation of REG_VMIO + RMS Added SET/SHOW DEBUG, SET/SHOW DEBUG, + SHOW MODIFIERS, SHOW RADIX + RMS Changed sim_fsize to take uptr argument + 29-Dec-03 RMS Added Telnet console output stall support + 01-Nov-03 RMS Cleaned up implicit detach on attach/restore + Fixed bug in command line read while logging (Mark Pizzolato) + 01-Sep-03 RMS Fixed end-of-file problem in dep, idep + Fixed error on trailing spaces in dep, idep + 15-Jul-03 RMS Removed unnecessary test in reset_all + 15-Jun-03 RMS Added register flag REG_VMIO + 25-Apr-03 RMS Added extended address support (V3.0) + Fixed bug in SAVE (Peter Schorn) + Added u5, u6 fields + Added logical name support + 03-Mar-03 RMS Added sim_fsize + 27-Feb-03 RMS Fixed bug in multiword deposits to files + 08-Feb-03 RMS Changed sim_os_sleep to void, match_ext to char* + Added multiple actions, .ini file support + Added multiple switch evaluations per line + 07-Feb-03 RMS Added VMS support for ! (Mark Pizzolato) + 01-Feb-03 RMS Added breakpoint table extension, actions + 14-Jan-03 RMS Added missing function prototypes + 10-Jan-03 RMS Added attach/restore flag, dynamic memory size support, + case sensitive SET options + 22-Dec-02 RMS Added ! (OS command) feature (Mark Pizzolato) + 17-Dec-02 RMS Added get_ipaddr + 02-Dec-02 RMS Added EValuate command + 16-Nov-02 RMS Fixed bug in register name match algorithm + 13-Oct-02 RMS Fixed Borland compiler warnings (Hans Pufal) + 05-Oct-02 RMS Fixed bugs in set_logon, ssh_break (David Hittner) + Added support for fixed buffer devices + Added support for Telnet console, removed VT support + Added help + Added VMS file optimizations (Robert Alan Byer) + Added quiet mode, DO with parameters, GUI interface, + extensible commands (Brian Knittel) + Added device enable/disable commands + 14-Jul-02 RMS Fixed exit bug in do, added -v switch (Brian Knittel) + 17-May-02 RMS Fixed bug in fxread/fxwrite error usage (found by + Norm Lastovic) + 02-May-02 RMS Added VT emulation interface, changed {NO}LOG to SET {NO}LOG + 22-Apr-02 RMS Fixed laptop sleep problem in clock calibration, added + magtape record length error (Jonathan Engdahl) + 26-Feb-02 RMS Fixed initialization bugs in do_cmd, get_aval + (Brian Knittel) + 10-Feb-02 RMS Fixed problem in clock calibration + 06-Jan-02 RMS Moved device enable/disable to simulators + 30-Dec-01 RMS Generalized timer packaged, added circular arrays + 19-Dec-01 RMS Fixed DO command bug (John Dundas) + 07-Dec-01 RMS Implemented breakpoint package + 05-Dec-01 RMS Fixed bug in universal register logic + 03-Dec-01 RMS Added read-only units, extended SET/SHOW, universal registers + 24-Nov-01 RMS Added unit-based registers + 16-Nov-01 RMS Added DO command + 28-Oct-01 RMS Added relative range addressing + 08-Oct-01 RMS Added SHOW VERSION + 30-Sep-01 RMS Relaxed attach test in BOOT + 27-Sep-01 RMS Added queue count routine, fixed typo in ex/mod + 17-Sep-01 RMS Removed multiple console support + 07-Sep-01 RMS Removed conditional externs on function prototypes + Added special modifier print + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze (V2.7) + 18-Jul-01 RMS Minor changes for Macintosh port + 12-Jun-01 RMS Fixed bug in big-endian I/O (Dave Conroy) + 27-May-01 RMS Added multiple console support + 16-May-01 RMS Added logging + 15-May-01 RMS Added features from Tim Litt + 12-May-01 RMS Fixed missing return in disable_cmd + 25-Mar-01 RMS Added ENABLE/DISABLE + 14-Mar-01 RMS Revised LOAD/DUMP interface (again) + 05-Mar-01 RMS Added clock calibration support + 05-Feb-01 RMS Fixed bug, DETACH buffered unit with hwmark = 0 + 04-Feb-01 RMS Fixed bug, RESTORE not using device's attach routine + 21-Jan-01 RMS Added relative time + 22-Dec-00 RMS Fixed find_device for devices ending in numbers + 08-Dec-00 RMS V2.5a changes + 30-Oct-00 RMS Added output file option to examine + 11-Jul-99 RMS V2.5 changes + 13-Apr-99 RMS Fixed handling of 32b addresses + 04-Oct-98 RMS V2.4 changes + 20-Aug-98 RMS Added radix commands + 05-Jun-98 RMS Fixed bug in ^D handling for UNIX + 10-Apr-98 RMS Added switches to all commands + 26-Oct-97 RMS Added search capability + 25-Jan-97 RMS Revised data types + 23-Jan-97 RMS Added bi-endian I/O + 06-Sep-96 RMS Fixed bug in variable length IEXAMINE + 16-Jun-96 RMS Changed interface to parse/print_sym + 06-Apr-96 RMS Added error checking in reset all + 07-Jan-96 RMS Added register buffers in save/restore + 11-Dec-95 RMS Fixed ordering bug in save/restore + 22-May-95 RMS Added symbolic input + 13-Apr-95 RMS Added symbolic printouts +*/ + +/* Macros and data structures */ + +#define NOT_MUX_USING_CODE /* sim_tmxr library provider or agnostic */ + +#define IN_SCP_C 1 /* Include from scp.c */ + +#include "sim_defs.h" +#include "sim_rev.h" +#include "sim_disk.h" +#include "sim_tape.h" +#include "sim_ether.h" +#include "sim_serial.h" +#include "sim_video.h" +#include "sim_sock.h" +#include "sim_frontpanel.h" +#include +#include +#include +#include +#if defined(_WIN32) +#include +#include +#include +#else +#include +#endif +#include +#include + +#if defined(HAVE_DLOPEN) /* Dynamic Readline support */ +#include +#endif + +#ifdef PIDP8I +#include +#endif + +#ifndef MAX +#define MAX(a,b) (((a) >= (b)) ? (a) : (b)) +#endif + +/* search logical and boolean ops */ + +#define SCH_OR 0 /* search logicals */ +#define SCH_AND 1 +#define SCH_XOR 2 +#define SCH_E 0 /* search booleans */ +#define SCH_N 1 +#define SCH_G 2 +#define SCH_L 3 +#define SCH_EE 4 +#define SCH_NE 5 +#define SCH_GE 6 +#define SCH_LE 7 + +#define MAX_DO_NEST_LVL 20 /* DO cmd nesting level */ +#define SRBSIZ 1024 /* save/restore buffer */ +#define SIM_BRK_INILNT 4096 /* bpt tbl length */ +#define SIM_BRK_ALLTYP 0xFFFFFFFB +#define UPDATE_SIM_TIME \ + if (1) { \ + int32 _x; \ + AIO_LOCK; \ + if (sim_clock_queue == QUEUE_LIST_END) \ + _x = noqueue_time; \ + else \ + _x = sim_clock_queue->time; \ + sim_time = sim_time + (_x - sim_interval); \ + sim_rtime = sim_rtime + ((uint32) (_x - sim_interval)); \ + if (sim_clock_queue == QUEUE_LIST_END) \ + noqueue_time = sim_interval; \ + else \ + sim_clock_queue->time = sim_interval; \ + AIO_UNLOCK; \ + } \ + else \ + (void)0 \ + +#define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT]) +#define SZ_R(rp) \ + (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT]) +#if defined (USE_INT64) +#define SZ_LOAD(sz,v,mb,j) \ + if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \ + else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \ + else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \ + else v = *(((t_uint64 *) mb) + ((uint32) j)); +#define SZ_STORE(sz,v,mb,j) \ + if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \ + else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \ + else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \ + else *(((t_uint64 *) mb) + ((uint32) j)) = v; +#else +#define SZ_LOAD(sz,v,mb,j) \ + if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \ + else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \ + else v = *(((uint32 *) mb) + ((uint32) j)); +#define SZ_STORE(sz,v,mb,j) \ + if (sz == sizeof (uint8)) *(((uint8 *) mb) + ((uint32) j)) = (uint8) v; \ + else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \ + else *(((uint32 *) mb) + ((uint32) j)) = v; +#endif +#define GET_SWITCHES(cp) \ + if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW +#define GET_RADIX(val,dft) \ + if (sim_switches & SWMASK ('O')) val = 8; \ + else if (sim_switches & SWMASK ('D')) val = 10; \ + else if (sim_switches & SWMASK ('H')) val = 16; \ + else if ((sim_switch_number >= 2) && (sim_switch_number <= 36)) val = sim_switch_number; \ + else val = dft; + +/* Asynch I/O support */ +#if defined (SIM_ASYNCH_IO) +pthread_mutex_t sim_asynch_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t sim_asynch_wake = PTHREAD_COND_INITIALIZER; + +pthread_mutex_t sim_timer_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t sim_timer_wake = PTHREAD_COND_INITIALIZER; +pthread_mutex_t sim_tmxr_poll_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t sim_tmxr_poll_cond = PTHREAD_COND_INITIALIZER; +int32 sim_tmxr_poll_count; +pthread_t sim_asynch_main_threadid; +UNIT * volatile sim_asynch_queue; +t_bool sim_asynch_enabled = TRUE; +int32 sim_asynch_check; +int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */ +int32 sim_asynch_inst_latency = 20; /* assume 5 mip simulator */ + +int sim_aio_update_queue (void) +{ +int migrated = 0; + +AIO_ILOCK; +if (AIO_QUEUE_VAL != QUEUE_LIST_END) { /* List !Empty */ + UNIT *q, *uptr; + int32 a_event_time; + do { /* Grab current queue */ + q = AIO_QUEUE_VAL; + } while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q)); + while (q != QUEUE_LIST_END) { /* List !Empty */ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Migrating Asynch event for %s after %d instructions\n", sim_uname(q), q->a_event_time); + ++migrated; + uptr = q; + q = q->a_next; + uptr->a_next = NULL; /* hygiene */ + if (uptr->a_activate_call != &sim_activate_notbefore) { + a_event_time = uptr->a_event_time-((sim_asynch_inst_latency+1)/2); + if (a_event_time < 0) + a_event_time = 0; + } + else + a_event_time = uptr->a_event_time; + AIO_IUNLOCK; + uptr->a_activate_call (uptr, a_event_time); + if (uptr->a_check_completion) { + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Calling Completion Check for asynch event on %s\n", sim_uname(uptr)); + uptr->a_check_completion (uptr); + } + AIO_ILOCK; + } + } +AIO_IUNLOCK; +return migrated; +} + +void sim_aio_activate (ACTIVATE_API caller, UNIT *uptr, int32 event_time) +{ +AIO_ILOCK; +sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch event for %s after %d instructions\n", sim_uname(uptr), event_time); +if (uptr->a_next) { + uptr->a_activate_call = sim_activate_abs; + } +else { + UNIT *q; + uptr->a_event_time = event_time; + uptr->a_activate_call = caller; + do { + q = AIO_QUEUE_VAL; + uptr->a_next = q; /* Mark as on list */ + } while (q != AIO_QUEUE_SET(uptr, q)); + } +AIO_IUNLOCK; +sim_asynch_check = 0; /* try to force check */ +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); + } +} +#else +t_bool sim_asynch_enabled = FALSE; +#endif + +/* 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 void (*sim_vm_init) (void); +char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL; +void (*sim_vm_post) (t_bool from_scp) = NULL; +CTAB *sim_vm_cmd = NULL; +void (*sim_vm_sprint_addr) (char *buf, DEVICE *dptr, t_addr addr) = NULL; +void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL; +t_addr (*sim_vm_parse_addr) (DEVICE *dptr, CONST char *cptr, CONST char **tptr) = NULL; +t_value (*sim_vm_pc_value) (void) = NULL; +t_bool (*sim_vm_is_subroutine_call) (t_addr **ret_addrs) = NULL; +t_bool (*sim_vm_fprint_stopped) (FILE *st, t_stat reason) = NULL; + +/* Prototypes */ + +/* Set and show command processors */ + +t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat set_dev_enbdis (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 set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat ssh_break (FILE *st, const char *cptr, int32 flg); +t_stat show_cmd_fi (FILE *ofile, int32 flag, CONST char *cptr); +t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_dev_radix (FILE *st, 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); +t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_dev_show_commands (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_default (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat show_device (FILE *st, DEVICE *dptr, int32 flag); +t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag); +t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg, int32 *toks); +t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, CONST char *cptr, int32 flag); +t_stat sim_save (FILE *sfile); +t_stat sim_rest (FILE *rfile); + +/* Breakpoint package */ + +t_stat sim_brk_init (void); +t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, CONST char *act); +t_stat sim_brk_clr (t_addr loc, int32 sw); +t_stat sim_brk_clrall (int32 sw); +t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw); +t_stat sim_brk_showall (FILE *st, int32 sw); +CONST char *sim_brk_getact (char *buf, int32 size); +BRKTAB *sim_brk_new (t_addr loc, uint32 btyp); +char *sim_brk_clract (void); + +FILE *stdnul; + +/* Command support routines */ + +SCHTAB *get_rsearch (CONST char *cptr, int32 radix, SCHTAB *schptr); +SCHTAB *get_asearch (CONST char *cptr, int32 radix, SCHTAB *schptr); +int32 test_search (t_value *val, SCHTAB *schptr); +static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char); +typedef enum { + SW_ERROR, /* Parse Error */ + SW_BITMASK, /* Bitmask Value or Not a switch */ + SW_NUMBER /* Numeric Value */ + } SWITCH_PARSE; +SWITCH_PARSE get_switches (const char *cptr, int32 *sw_val, int32 *sw_number); +CONST char *get_sim_sw (CONST char *cptr); +t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr); +t_value get_rval (REG *rptr, uint32 idx); +void put_rval (REG *rptr, uint32 idx, t_value val); +void fprint_help (FILE *st); +void fprint_stopped (FILE *st, t_stat r); +void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr); +void fprint_sep (FILE *st, int32 *tokens); +char *read_line (char *ptr, int32 size, FILE *stream); +char *read_line_p (const char *prompt, char *ptr, int32 size, FILE *stream); +REG *find_reg_glob (CONST char *ptr, CONST char **optr, DEVICE **gdptr); +REG *find_reg_glob_reason (CONST char *cptr, CONST char **optr, DEVICE **gdptr, t_stat *stat); +char *sim_trim_endspc (char *cptr); + +/* Forward references */ + +t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, const char *cptr); +t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr); +t_bool qdisable (DEVICE *dptr); +t_stat attach_err (UNIT *uptr, t_stat stat); +t_stat detach_all (int32 start_device, t_bool shutdown); +t_stat assign_device (DEVICE *dptr, const char *cptr); +t_stat deassign_device (DEVICE *dptr); +t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, CONST char *aptr); +t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, CONST char *cptr, + REG *lowr, REG *highr, uint32 lows, uint32 highs); +t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx); +t_stat dep_reg (int32 flag, CONST char *cptr, REG *rptr, uint32 idx); +t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, const char *cptr, + t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr); +t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr); +t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr, + UNIT *uptr, int32 dfltinc); +void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs); +t_stat step_svc (UNIT *ptr); +t_stat expect_svc (UNIT *ptr); +t_stat shift_args (char *do_arg[], size_t arg_count); +t_stat set_on (int32 flag, CONST char *cptr); +t_stat set_verify (int32 flag, CONST char *cptr); +t_stat set_message (int32 flag, CONST char *cptr); +t_stat set_quiet (int32 flag, CONST char *cptr); +t_stat set_asynch (int32 flag, CONST char *cptr); +t_stat sim_show_asynch (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr); +t_stat do_cmd_label (int32 flag, CONST char *cptr, CONST char *label); +void int_handler (int signal); +t_stat set_prompt (int32 flag, CONST char *cptr); +t_stat sim_set_asynch (int32 flag, CONST char *cptr); +t_stat sim_set_environment (int32 flag, CONST char *cptr); +static const char *get_dbg_verb (uint32 dbits, DEVICE* dptr); + +/* Global data */ + +DEVICE *sim_dflt_dev = NULL; +UNIT *sim_clock_queue = QUEUE_LIST_END; +int32 sim_interval = 0; +int32 sim_switches = 0; +int32 sim_switch_number = 0; +FILE *sim_ofile = NULL; +TMLN *sim_oline = NULL; +MEMFILE *sim_mfile = NULL; +SCHTAB *sim_schrptr = FALSE; +SCHTAB *sim_schaptr = FALSE; +DEVICE *sim_dfdev = NULL; +UNIT *sim_dfunit = NULL; +DEVICE **sim_internal_devices = NULL; +uint32 sim_internal_device_count = 0; +int32 sim_opt_out = 0; +int32 sim_is_running = 0; +t_bool sim_processing_event = FALSE; +uint32 sim_brk_summ = 0; +uint32 sim_brk_types = 0; +BRKTYPTAB *sim_brk_type_desc = NULL; /* type descriptions */ +uint32 sim_brk_dflt = 0; +uint32 sim_brk_match_type; +t_addr sim_brk_match_addr; +char *sim_brk_act[MAX_DO_NEST_LVL]; +char *sim_brk_act_buf[MAX_DO_NEST_LVL]; +BRKTAB **sim_brk_tab = NULL; +int32 sim_brk_ent = 0; +int32 sim_brk_lnt = 0; +int32 sim_brk_ins = 0; +int32 sim_quiet = 0; +int32 sim_step = 0; +char *sim_sub_instr = NULL; +char *sim_sub_instr_buf = NULL; +size_t sim_sub_instr_size = 0; +size_t *sim_sub_instr_off = NULL; +static double sim_time; +static uint32 sim_rtime; +static int32 noqueue_time; +volatile int32 stop_cpu = 0; +static char **sim_argv; +t_value *sim_eval = NULL; +static t_value sim_last_val; +static t_addr sim_last_addr; +FILE *sim_log = NULL; /* log file */ +FILEREF *sim_log_ref = NULL; /* log file file reference */ +FILE *sim_deb = NULL; /* debug file */ +FILEREF *sim_deb_ref = NULL; /* debug file file reference */ +int32 sim_deb_switches = 0; /* debug switches */ +struct timespec sim_deb_basetime; /* debug timestamp relative base time */ +char *sim_prompt = NULL; /* prompt string */ +static FILE *sim_gotofile; /* the currently open do file */ +static int32 sim_goto_line[MAX_DO_NEST_LVL+1]; /* the current line number in the currently open do file */ +static int32 sim_do_echo = 0; /* the echo status of the currently open do file */ +static int32 sim_show_message = 1; /* the message display status of the currently open do file */ +static int32 sim_on_inherit = 0; /* the inherit status of on state and conditions when executing do files */ +static int32 sim_do_depth = 0; +static t_bool sim_cmd_echoed = FALSE; /* Command was emitted already prior to message output */ + +static int32 sim_on_check[MAX_DO_NEST_LVL+1]; +static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1]; +static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE]; +static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1]; +static const char *sim_do_label[MAX_DO_NEST_LVL+1]; + +t_stat sim_last_cmd_stat; /* Command Status */ + +static SCHTAB sim_stabr; /* Register search specifier */ +static SCHTAB sim_staba; /* Memory search specifier */ + +static DEBTAB sim_dflt_debug[] = { + {"EVENT", SIM_DBG_EVENT, "Event Dispatching"}, + {"ACTIVATE", SIM_DBG_ACTIVATE, "Event Scheduling"}, + {"AIO_QUEUE", SIM_DBG_AIO_QUEUE, "Asynchronous Event Queueing"}, + {0} +}; + +static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0) }; +static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0) }; +#if defined USE_INT64 +static const char *sim_si64 = "64b data"; +#else +static const char *sim_si64 = "32b data"; +#endif +#if defined USE_ADDR64 +static const char *sim_sa64 = "64b addresses"; +#else +static const char *sim_sa64 = "32b addresses"; +#endif +const char *sim_savename = sim_name; /* Simulator Name used in SAVE/RESTORE images */ + +/* Tables and strings */ + +const char save_vercur[] = "V4.0"; +const char save_ver40[] = "V4.0"; +const char save_ver35[] = "V3.5"; +const char save_ver32[] = "V3.2"; +const char save_ver30[] = "V3.0"; +const struct scp_error { + const char *code; + const char *message; + } scp_errors[1+SCPE_MAX_ERR-SCPE_BASE] = + {{"NXM", "Address space exceeded"}, + {"UNATT", "Unit not attached"}, + {"IOERR", "I/O error"}, + {"CSUM", "Checksum error"}, + {"FMT", "Format error"}, + {"NOATT", "Unit not attachable"}, + {"OPENERR", "File open error"}, + {"MEM", "Memory exhausted"}, + {"ARG", "Invalid argument"}, + {"STEP", "Step expired"}, + {"UNK", "Unknown command"}, + {"RO", "Read only argument"}, + {"INCOMP", "Command not completed"}, + {"STOP", "Simulation stopped"}, + {"EXIT", "Goodbye"}, + {"TTIERR", "Console input I/O error"}, + {"TTOERR", "Console output I/O error"}, + {"EOF", "End of file"}, + {"REL", "Relocation error"}, + {"NOPARAM", "No settable parameters"}, + {"ALATT", "Unit already attached"}, + {"TIMER", "Hardware timer error"}, + {"SIGERR", "Signal handler setup error"}, + {"TTYERR", "Console terminal setup error"}, + {"SUB", "Subscript out of range"}, + {"NOFNC", "Command not allowed"}, + {"UDIS", "Unit disabled"}, + {"NORO", "Read only operation not allowed"}, + {"INVSW", "Invalid switch"}, + {"MISVAL", "Missing value"}, + {"2FARG", "Too few arguments"}, + {"2MARG", "Too many arguments"}, + {"NXDEV", "Non-existent device"}, + {"NXUN", "Non-existent unit"}, + {"NXREG", "Non-existent register"}, + {"NXPAR", "Non-existent parameter"}, + {"NEST", "Nested DO command limit exceeded"}, + {"IERR", "Internal error"}, + {"MTRLNT", "Invalid magtape record length"}, + {"LOST", "Console Telnet connection lost"}, + {"TTMO", "Console Telnet connection timed out"}, + {"STALL", "Console Telnet output stall"}, + {"AFAIL", "Assertion failed"}, + {"INVREM", "Invalid remote console command"}, + {"NOTATT", "Not attached"}, + {"EXPECT", "Expect matched"}, + {"AMBREG", "Ambiguous register name"}, + {"REMOTE", "remote console command"}, + }; + +const size_t size_map[] = { sizeof (int8), + sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32) +#if defined (USE_INT64) + , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64) +#endif +}; + +const t_value width_mask[] = { 0, + 0x1, 0x3, 0x7, 0xF, + 0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, + 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, + 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF +#if defined (USE_INT64) + , 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF, + 0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF, + 0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF, + 0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF, + 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF +#endif + }; + +static const char simh_help[] = + /***************** 80 character line width template *************************/ + "1Commands\n" +#define HLP_RESET "*Commands Resetting Devices" + /***************** 80 character line width template *************************/ + "2Resetting Devices\n" + " The RESET command (abbreviation RE) resets a device or the entire simulator\n" + " to a predefined condition. If switch -p is specified, the device is reset\n" + " to its power-up state:\n\n" + "++RESET reset all devices\n" + "++RESET -p powerup all devices\n" + "++RESET ALL reset all devices\n" + "++RESET reset specified device\n\n" + " Typically, RESET stops any in-progress I/O operation, clears any interrupt\n" + " request, and returns the device to a quiescent state. It does not clear\n" + " main memory or affect I/O connections.\n" +#define HLP_EXAMINE "*Commands Examining_and_Changing_State" +#define HLP_IEXAMINE "*Commands Examining_and_Changing_State" +#define HLP_DEPOSIT "*Commands Examining_and_Changing_State" +#define HLP_IDEPOSIT "*Commands Examining_and_Changing_State" + /***************** 80 character line width template *************************/ + "2Examining and Changing State\n" + " There are four commands to examine and change state:\n\n" + "++EXAMINE (abbreviated E) examines state\n" + "++DEPOSIT (abbreviated D) changes state\n" + "++IEXAMINE (interactive examine, abbreviated IE) examines state and allows\n" + "++++the user to interactively change it\n" + "++IDEPOSIT (interactive deposit, abbreviated ID) allows the user to\n" + "++++interactively change state\n\n" + " All four commands take the form\n\n" + "++command {modifiers} \n\n" + " Deposit must also include a deposit value at the end of the command.\n\n" + " There are four kinds of modifiers: switches, device/unit name, search\n" + " specifier, and for EXAMINE, output file. Switches have been described\n" + " previously. A device/unit name identifies the device and unit whose\n" + " address space is to be examined or modified. If no device is specified,\n" + " the CPU (main memory)is selected; if a device but no unit is specified,\n" + " unit 0 of the device is selected.\n\n" + " The search specifier provides criteria for testing addresses or registers\n" + " to see if they should be processed. A specifier consists of a logical\n" + " operator, a relational operator, or both, optionally separated by spaces.\n\n" + "++{ } \n\n" + /***************** 80 character line width template *************************/ + " where the logical operator is & (and), | (or), or ^ (exclusive or), and the\n" + " relational operator is = or == (equal), ! or != (not equal), >= (greater\n" + " than or equal), > (greater than), <= (less than or equal), or < (less than).\n" + " If a logical operator is specified without a relational operator, it is\n" + " ignored. If a relational operator is specified without a logical operator,\n" + " no logical operation is performed. All comparisons are unsigned.\n\n" + " The output file modifier redirects command output to a file instead of the\n" + " console. An output file modifier consists of @ followed by a valid file\n" + " name.\n\n" + " Modifiers may be specified in any order. If multiple modifiers of the\n" + " same type are specified, later modifiers override earlier modifiers. Note\n" + " that if the device/unit name comes after the search specifier, the search\n" + " values will interpreted in the radix of the CPU, rather than of the\n" + " device/unit.\n\n" + " The \"object list\" consists of one or more of the following, separated by\n" + " commas:\n\n" + /***************** 80 character line width template *************************/ + "++register the specified register\n" + "++register[sub1-sub2] the specified register array locations,\n" + "++ starting at location sub1 up to and\n" + "++ including location sub2\n" + "++register[sub1/length] the specified register array locations,\n" + "++ starting at location sub1 up to but\n" + "++ not including sub1+length\n" + "++register[ALL] all locations in the specified register\n" + "++ array\n" + "++register1-register2 all the registers starting at register1\n" + "++ up to and including register2\n" + "++address the specified location\n" + "++address1-address2 all locations starting at address1 up to\n" + "++ and including address2\n" + "++address/length all location starting at address up to\n" + "++ but not including address+length\n" + "++STATE all registers in the device\n" + "++ALL all locations in the unit\n" + "++$ the last value displayed by an EXAMINE command\n" + " interpreted as an address\n" + "3Switches\n" + " Switches can be used to control the format of display information:\n\n" + /***************** 80 character line width template *************************/ + "++-a display as ASCII\n" + "++-c display as character string\n" + "++-m display as instruction mnemonics\n" + "++-o or -8 display as octal\n" + "++-d or -10 display as decimal\n" + "++-h or -16 display as hexadecimal\n" + "++-2 display as binary\n\n" + " The simulators typically accept symbolic input (see documentation with each\n" + " simulator).\n\n" + "3Examples\n" + " Examples:\n\n" + "++ex 1000-1100 examine 1000 to 1100\n" + "++de PC 1040 set PC to 1040\n" + "++ie 40-50 interactively examine 40:50\n" + "++ie >1000 40-50 interactively examine the subset\n" + "++ of locations 40:50 that are >1000\n" + "++ex rx0 50060 examine 50060, RX unit 0\n" + "++ex rx sbuf[3-6] examine SBUF[3] to SBUF[6] in RX\n" + "++de all 0 set main memory to 0\n" + "++de &77>0 0 set all addresses whose low order\n" + "++ bits are non-zero to 0\n" + "++ex -m @memdump.txt 0-7777 dump memory to file\n\n" + " Note: to terminate an interactive command, simply type a bad value\n" + " (eg, XYZ) when input is requested.\n" +#define HLP_EVALUATE "*Commands Evaluating_Instructions" + /***************** 80 character line width template *************************/ + "2Evaluating Instructions\n" + " The EVAL command evaluates a symbolic expression and returns the equivalent\n" + " numeric value. This is useful for obtaining numeric arguments for a search\n" + " command:\n\n" + "++EVAL \n" + /***************** 80 character line width template *************************/ + "2Loading and Saving Programs\n" +#define HLP_LOAD "*Commands Loading_and_Saving_Programs LOAD" + "3LOAD\n" + " The LOAD command (abbreviation LO) loads a file in binary loader format:\n\n" + "++LOAD {implementation options}\n\n" + " The types of formats supported are implementation specific. Options (such\n" + " as load within range) are also implementation specific.\n\n" +#define HLP_DUMP "*Commands Loading_and_Saving_Programs DUMP" + "3DUMP\n" + " The DUMP command (abbreviation DU) dumps memory in binary loader format:\n\n" + "++DUMP {implementation options}\n\n" + " The types of formats supported are implementation specific. Options (such\n" + " as dump within range) are also implementation specific.\n" + /***************** 80 character line width template *************************/ + "2Saving and Restoring State\n" +#define HLP_SAVE "*Commands Saving_and_Restoring_State SAVE" + "3SAVE\n" + " The SAVE command (abbreviation SA) save the complete state of the simulator\n" + " to a file. This includes the contents of main memory and all registers,\n" + " and the I/O connections of devices:\n\n" + "++SAVE \n\n" +#define HLP_RESTORE "*Commands Saving_and_Restoring_State RESTORE" + "3RESTORE\n" + " The RESTORE command (abbreviation REST, alternately GET) restores a\n" + " previously saved simulator state:\n\n" + "++RESTORE \n" + "4Switches\n" + " Switches can influence the output and behavior of the RESTORE command\n\n" + "++-Q Suppresses version warning messages\n" + "++-D Suppress detaching and attaching devices during a restore\n" + "++-F Overrides the related file timestamp validation check\n" + "\n" + "4Notes:\n" + " 1) SAVE file format compresses zeroes to minimize file size.\n" + " 2) The simulator can't restore active incoming telnet sessions to\n" + " multiplexer devices, but the listening ports will be restored across a\n" + " save/restore.\n" + /***************** 80 character line width template *************************/ + "2Running A Simulated Program\n" +#define HLP_RUN "*Commands Running_A_Simulated_Program RUN" + "3RUN {start_pc_addr} {UNTIL stop_pc_addr|\"output-string\"}\n" + " The RUN command (abbreviated RU) resets all devices, deposits its argument\n" + " (if given) in the PC, and starts execution. If no argument is given,\n" + " execution starts at the current PC.\n\n" + " The optional UNTIL argument specifies a stop criteria for execution.\n" + " There are two forms of execution stop criteria:\n" + "+1. A temporary breakpoint (which exists only until it is encountered).\n" + "+2. A string which will stop execution when the simulator has output\n" + "++the indicated string.\n" +#define HLP_GO "*Commands Running_A_Simulated_Program GO" + "3GO {start_pc_addr} {UNTIL stop_pc_addr|\"output-string\"}\n" + " The GO command does not reset devices, deposits its argument (if given)\n" + " in the PC, and starts execution. If no argument is given, execution\n" + " starts at the current PC.\n\n" + " The optional UNTIL argument specifies a stop criteria for execution.\n" + " There are two forms of execution stop criteria:\n" + "+1. A temporary breakpoint (which exists only until it is encountered).\n" + "+2. A string which will stop execution when the simulator has output\n" + "++the indicated string.\n" +#define HLP_CONTINUE "*Commands Running_A_Simulated_Program CONTINUE" + "3CONTINUE\n" + " The CONT command (abbreviated CO) does not reset devices and resumes\n" + " execution at the current PC.\n" +#define HLP_STEP "*Commands Running_A_Simulated_Program STEP" + "3STEP\n" + " The STEP command (abbreviated S) resumes execution at the current PC for\n" + " the number of instructions given by its argument. If no argument is\n" + " supplied, one instruction is executed.\n" + "4Switches\n" + " If the STEP command is invoked with the -T switch, the step command will\n" + " cause execution to run for microseconds rather than instructions.\n" +#define HLP_NEXT "*Commands Running_A_Simulated_Program NEXT" + "3NEXT\n" + " The NEXT command (abbreviated N) resumes execution at the current PC for\n" + " one instruction, attempting to execute through a subroutine calls.\n" + " If the next instruction to be executed is not a subroutine call,\n" + " one instruction is executed.\n" +#define HLP_BOOT "*Commands Running_A_Simulated_Program BOOT" + "3BOOT\n" + " The BOOT command (abbreviated BO) resets all devices and bootstraps the\n" + " device and unit given by its argument. If no unit is supplied, unit 0 is\n" + " bootstrapped. The specified unit must be attached.\n" + /***************** 80 character line width template *************************/ + "2Stopping The Simulator\n" + " Programs run until the simulator detects an error or stop condition, or\n" + " until the user forces a stop condition.\n" + "3Simulator Detected Stop Conditions\n" + " These simulator-detected conditions stop simulation:\n\n" + "++- HALT instruction. If a HALT instruction is decoded, simulation stops.\n" + "++- Breakpoint. The simulator may support breakpoints (see below).\n" + "++- I/O error. If an I/O error occurs during simulation of an I/O\n" + "+++operation, and the device stop-on-I/O-error flag is set, simulation\n" + "+++usually stops.\n\n" + "++- Processor condition. Certain processor conditions can stop\n" + "+++simulation; these are described with the individual simulators.\n" + "3User Specified Stop Conditions\n" + " Typing the interrupt character stops simulation. The interrupt character\n" + " is defined by the WRU (where are you) console option and is initially set\n" + " to 005 (^E).\n\n" + /***************** 80 character line width template *************************/ +#define HLP_BREAK "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK" +#define HLP_NOBREAK "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions BREAK" + "4Breakpoints\n" + " A simulator may offer breakpoint capability. A simulator may define\n" + " breakpoints of different types, identified by letter (for example, E for\n" + " execution, R for read, W for write, etc). At the moment, most simulators\n" + " support only E (execution) breakpoints.\n\n" + " Associated with a breakpoint are a count and, optionally, one or more\n" + " actions. Each time the breakpoint is taken, the associated count is\n" + " decremented. If the count is less than or equal to 0, the breakpoint\n" + " occurs; otherwise, it is deferred. When the breakpoint occurs, the\n" + " optional actions are automatically executed.\n\n" + " A breakpoint is set by the BREAK or the SET BREAK commands:\n\n" + "++BREAK {-types} {{[count]},{addr range...}}{;action;action...}\n" + "++SET BREAK {-types} {{[count]},{addr range...}}{;action;action...}\n\n" + " If no type is specified, the simulator-specific default breakpoint type\n" + " (usually E for execution) is used. If no address range is specified, the\n" + " current PC is used. As with EXAMINE and DEPOSIT, an address range may be a\n" + " single address, a range of addresses low-high, or a relative range of\n" + " address/length.\n" + /***************** 80 character line width template *************************/ + "5Displaying Breakpoints\n" + " Currently set breakpoints can be displayed with the SHOW BREAK command:\n\n" + "++SHOW {-C} {-types} BREAK {ALL|{,...}}\n\n" + " Locations with breakpoints of the specified type are displayed.\n\n" + " The -C switch displays the selected breakpoint(s) formatted as commands\n" + " which may be subsequently used to establish the same breakpoint(s).\n\n" + "5Removing Breakpoints\n" + " Breakpoints can be cleared by the NOBREAK or the SET NOBREAK commands.\n" + "5Examples\n" + "++BREAK set E break at current PC\n" + "++BREAK -e 200 set E break at 200\n" + "++BREAK 2000/2[2] set E breaks at 2000,2001 with count = 2\n" + "++BREAK 100;EX AC;D MQ 0 set E break at 100 with actions EX AC and\n" + "+++++++++D MQ 0\n" + "++BREAK 100; delete action on break at 100\n\n" + /***************** 80 character line width template *************************/ +#define HLP_DEBUG "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions DEBUG" +#define HLP_NODEBUG "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions DEBUG" + "4Debug\n" + " The DEBUG snd NODEBUG commands are aliases for the \"SET DEBUG\" and\n" + " \"SET NODEBUG\" commands. Additionally, support is provided that is\n" + " equivalent to the \"SET DEBUG=opt1{;opt2}\" and\n" + " \"SET NODEBUG=opt1{;opt2}\" commands.\n\n" + /***************** 80 character line width template *************************/ + "2Connecting and Disconnecting Devices\n" + " Except for main memory and network devices, units are simulated as\n" + " unstructured binary disk files in the host file system. Before using a\n" + " simulated unit, the user must specify the file to be accessed by that unit.\n" +#define HLP_ATTACH "*Commands Connecting_and_Disconnecting_Devices ATTACH" + "3ATTACH\n" + " The ATTACH (abbreviation AT) command associates a unit and a file:\n" + "++ATTACH \n\n" + " Some devices have more detailed or specific help available with:\n\n" + "++HELP ATTACH\n\n" + "4Switches\n" + "5-n\n" + " If the -n switch is specified when an attach is executed, a new file is\n" + " created, and an appropriate message is printed.\n" + "5-e\n" + " If the file does not exist, and the -e switch was not specified, a new\n" + " file is created, and an appropriate message is printed. If the -e switch\n" + " was specified, a new file is not created, and an error message is printed.\n" + "5-r\n" + " If the -r switch is specified, or the file is write protected, ATTACH tries\n" + " to open the file read only. If the file does not exist, or the unit does\n" + " not support read only operation, an error occurs. Input-only devices, such\n" + " as paper-tape readers, and devices with write lock switches, such as disks\n" + " and tapes, support read only operation; other devices do not. If a file is\n" + " attached read only, its contents can be examined but not modified.\n" + "5-q\n" + " If the -q switch is specified when creating a new file (-n) or opening one\n" + " read only (-r), any messages announcing these facts will be suppressed.\n" + "5-f\n" + " For simulated magnetic tapes, the ATTACH command can specify the format of\n" + " the attached tape image file:\n\n" + "++ATTACH -f \n\n" + " The currently supported tape image file formats are:\n\n" + "++SIMH SIMH simulator format\n" + "++E11 E11 simulator format\n" + "++TPC TPC format\n" + "++P7B Pierce simulator 7-track format\n\n" + /***************** 80 character line width template *************************/ + " For some simulated disk devices, the ATTACH command can specify the format\n" + " of the attached disk image file:\n\n" + "++ATTACH -f \n\n" + " The currently supported disk image file formats are:\n\n" + "++SIMH SIMH simulator format\n" + "++VHD Virtual Disk format\n" + "++RAW platform specific access to physical disk or\n" + "++ CDROM drives\n" + " The disk format can also be set with the SET command prior to ATTACH:\n\n" + "++SET FORMAT=\n" + "++ATT \n\n" + /***************** 80 character line width template *************************/ + " The format of an attached tape or disk file can be displayed with the SHOW\n" + " command:\n" + "++SHOW FORMAT\n" + " For Telnet-based terminal emulation devices, the ATTACH command associates\n" + " the master unit with a TCP/IP listening port:\n\n" + "++ATTACH \n\n" + " The port is a decimal number between 1 and 65535 that is not already used\n" + " other TCP/IP applications.\n" + " For Ethernet emulators, the ATTACH command associates the simulated Ethernet\n" + " with a physical Ethernet device:\n\n" + "++ATTACH \n" + /***************** 80 character line width template *************************/ +#define HLP_DETACH "*Commands Connecting_and_Disconnecting_Devices DETACH" + "3DETACH\n" + " The DETACH (abbreviation DET) command breaks the association between a unit\n" + " and a file, port, or network device:\n\n" + "++DETACH ALL detach all units\n" + "++DETACH detach specified unit\n" + " The EXIT command performs an automatic DETACH ALL.\n" + "2Controlling Simulator Operating Environment\n" + "3Working Directory\n" +#define HLP_CD "*Commands Controlling_Simulator_Operating_Environment Working_Directory CD" + "4CD\n" + " Set the current working directory:\n" + "++CD path\n" + "4SET_DEFAULT\n" + " Set the current working directory:\n" + "++SET DEFAULT path\n" +#define HLP_PWD "*Commands Controlling_Simulator_Operating_Environment Working_Directory PWD" + "4PWD\n" + "++PWD\n" + " Display the current working directory:\n" + "2Listing Files\n" +#define HLP_DIR "*Commands Listing_Files DIR" + "3DIR\n" + "++DIR {path} list directory files\n" +#define HLP_LS "*Commands Listing_Files LS" + "3LS\n" + "++LS {path} list directory files\n" + "2Displaying Files\n" +#define HLP_TYPE "*Commands Displaying_Files TYPE" + "3TYPE\n" + "++TYPE file display a file contents\n" +#define HLP_CAT "*Commands Displaying_Files CAT" + "3CAT\n" + "++CAT file display a file contents\n" + "2Removing Files\n" +#define HLP_DELETE "*Commands Removing_Files DEL" + "3DELETE\n" + "++DEL{ete} file deletes a file\n" +#define HLP_RM "*Commands Removing_Files RM" + "3RM\n" + "++RM file deletes a file\n" + "2Copying Files\n" +#define HLP_COPY "*Commands Copying_Files COPY" + "3COPY\n" + "++COPY sfile dfile copies a file\n" +#define HLP_CP "*Commands Copying_Files CP" + "3CP\n" + "++CP sfile dfile copies a file\n" +#define HLP_SET "*Commands SET" + "2SET\n" + /***************** 80 character line width template *************************/ +#define HLP_SET_CONSOLE "*Commands SET CONSOLE" + "3Console\n" + "+set console arg{,arg...} set console options\n" + "+set console WRU=value specify console drop to simh character\n" + "+set console BRK=value specify console Break character\n" + "+set console DEL=value specify console delete character\n" + "+set console PCHAR=bitmask bit mask of printable characters in\n" + "++++++++ range [31,0]\n" + "+set console SPEED=speed{*factor}\n" + "++++++++ specify console input data rate\n" + "+set console TELNET=port specify console telnet port\n" + "+set console TELNET=LOG=log_file\n" + "++++++++ specify console telnet logging to the\n" + "++++++++ specified destination {LOG,STDOUT,STDERR,\n" + "++++++++ DEBUG or filename)\n" + "+set console TELNET=NOLOG disables console telnet logging\n" + "+set console TELNET=BUFFERED[=bufsize]\n" + "++++++++ specify console telnet buffering\n" + "+set console TELNET=NOBUFFERED\n" + "++++++++ disables console telnet buffering\n" + "+set console TELNET=UNBUFFERED\n" + "++++++++ disables console telnet buffering\n" + "+set console NOTELNET disable console telnet\n" + "+set console SERIAL=serialport[;config]\n" + "++++++++ specify console serial port and optionally\n" + "++++++++ the port config (i.e. ;9600-8n1)\n" + "+set console NOSERIAL disable console serial session\n" + "+set console SPEED=nn{*fac} specifies the maximum console port input rate\n" + /***************** 80 character line width template *************************/ +#define HLP_SET_REMOTE "*Commands SET REMOTE" + "3Remote\n" + "+set remote TELNET=port specify remote console telnet port\n" + "+set remote NOTELNET disables remote console\n" + "+set remote BUFFERSIZE=bufsize\n" + "++++++++ specify remote console command output buffer\n" + "++++++++ size\n" + "+set remote CONNECTIONS=n specify number of concurrent remote\n" + "++++++++ console sessions\n" + "+set remote TIMEOUT=n specify number of seconds without input\n" + "++++++++ before automatic continue\n" + "+set remote MASTER enable master mode remote console\n" + "+set remote NOMASTER disable remote master mode console\n" +#define HLP_SET_DEFAULT "*Commands SET Working_Directory" + "3Working Directory\n" + "+set default set the current directory\n" + "+cd set the current directory\n" +#define HLP_SET_LOG "*Commands SET Log" + "3Log\n" + " Interactions with the simulator session (at the \"sim>\" prompt\n" + " can be recorded to a log file\n\n" + "+set log log_file specify the log destination\n" + "++++++++ (STDOUT,DEBUG or filename)\n" + "+set nolog disables any currently active logging\n" + "4Switches\n" + " By default, log output is written at the end of the specified log file.\n" + " A new log file can created if the -N switch is used on the command line.\n" +#define HLP_SET_DEBUG "*Commands SET Debug" + /***************** 80 character line width template *************************/ + "3Debug\n" + "+set debug debug_file specify the debug destination\n" + "++++++++ (STDOUT,STDERR,LOG or filename)\n" + "+set nodebug disables any currently active debug output\n" + "4Switches\n" + " Debug message output contains a timestamp which indicates the number of\n" + " simulated instructions which have been executed prior to the debug event.\n\n" + " Debug message output can be enhanced to contain additional, potentially\n" + " useful information.\n" + "5-T\n" + " The -T switch causes debug output to contain a time of day displayed\n" + " as hh:mm:ss.msec.\n" + "5-A\n" + " The -A switch causes debug output to contain a time of day displayed\n" + " as seconds.msec.\n" + "5-R\n" + " The -R switch causes the time of day displayed due to the -T or -A\n" + " switches to be relative to the start time of debugging. If neither\n" + " -T or -A is explicitly specified, -T is implied.\n" + "5-P\n" + " The -P switch adds the output of the PC (Program Counter) to each debug\n" + " message.\n" + "5-N\n" + " The -N switch causes a new/empty file to be written to. The default\n" + " is to append to an existing debug log file.\n" + "5-D\n" + " The -D switch causes data blob output to also display the data as\n" + " RADIX-50 characters.\n" + "5-E\n" + " The -E switch causes data blob output to also display the data as\n" + " EBCDIC characters.\n" +#define HLP_SET_BREAK "*Commands SET Breakpoints" + "3Breakpoints\n" + "+set break set breakpoints\n" + "+set nobreak clear breakpoints\n" + /***************** 80 character line width template *************************/ +#define HLP_SET_THROTTLE "*Commands SET Throttle" + "3Throttle\n" + "+set throttle {x{M|K|%%}}|{x/t}\n" + "++++++++ set simulation rate\n" + "+set nothrottle set simulation rate to maximum\n" +#define HLP_SET_CLOCKS "*Commands SET Clocks" + "3Clock\n" +#if defined (SIM_ASYNCH_CLOCKS) + "+set clock asynch enable asynchronous clocks\n" + "+set clock noasynch disable asynchronous clocks\n" +#endif + "+set clock nocatchup disable catchup clock ticks\n" + "+set clock catchup enable catchup clock ticks\n" + "+set clock calib=n%% specify idle calibration skip %%\n" + "+set clock stop=n stop execution after n instructions\n\n" + " The set clock stop command allows execution to have a bound when\n" + " execution starts with a BOOT, NEXT or CONTINUE command.\n" +#define HLP_SET_ASYNCH "*Commands SET Asynch" + "3Asynch\n" + "+set asynch enable asynchronous I/O\n" + "+set noasynch disable asynchronous I/O\n" +#define HLP_SET_ENVIRON "*Commands SET Environment" + "3Environment\n" + "4Explicitily Changing A Variable\n" + "+set environment name=val set environment variable\n" + "+set environment name clear environment variable\n" + "4Gathering Input From A User\n" + " Input from a user can be obtained by:\n\n" + "+set environment -p \"Prompt String\" name=default\n\n" + " The -p switch indicates that the user should be prompted\n" + " with the indicated prompt string and the input provided\n" + " will be saved in the environment variable 'name'. If no\n" + " input is provided, the value specified as 'default' will be\n" + " used.\n" +#define HLP_SET_ON "*Commands SET Command_Status_Trap_Dispatching" + "3Command Status Trap Dispatching\n" + "+set on enables error checking after command\n" + "++++++++ execution\n" + "+set noon disables error checking after command\n" + "++++++++ execution\n" + "+set on inherit enables inheritance of ON state and\n" + "++++++++ actions into do command files\n" + "+set on noinherit disables inheritance of ON state and\n" + "++++++++ actions into do command files\n" +#define HLP_SET_VERIFY "*Commands SET Command_Execution_Display" +#define HLP_SET_VERIFY "*Commands SET Command_Execution_Display" + "3Command Execution Display\n" + "+set verify re-enables display of command file\n" + "++++++++ processed commands\n" + "+set verbose re-enables display of command file\n" + "++++++++ processed commands\n" + "+set noverify disables display of command file processed\n" + "++++++++ commands\n" + "+set noverbose disables display of command file processed\n" + "++++++++ commands\n" +#define HLP_SET_MESSAGE "*Commands SET Command_Error_Status_Display" + "3Command Error Status Display\n" + "+set message re-enables display of command file error\n" + "++++++++ messages\n" + "+set nomessage disables display of command file error\n" + "++++++++ messages\n" +#define HLP_SET_QUIET "*Commands SET Command_Output_Display" + "3Command Output Display\n" + "+set quiet disables suppression of some output and\n" + "++++++++ messages\n" + "+set noquiet re-enables suppression of some output and\n" + "++++++++ messages\n" +#define HLP_SET_PROMPT "*Commands SET Command_Prompt" + "3Command Prompt\n" + "+set prompt \"string\" sets an alternate simulator prompt string\n" + "3Device and Unit\n" + "+set OCT|DEC|HEX|BIN set device display radix\n" + "+set ENABLED enable device\n" + "+set DISABLED disable device\n" + "+set DEBUG{=arg} set device debug flags\n" + "+set NODEBUG={arg} clear device debug flags\n" + "+set arg{,arg...} set device parameters (see show modifiers)\n" + "+set ENABLED enable unit\n" + "+set DISABLED disable unit\n" + "+set arg{,arg...} set unit parameters (see show modifiers)\n" + "+help set displays the device specific set commands\n" + "++++++++ available\n" + /***************** 80 character line width template *************************/ +#define HLP_SHOW "*Commands SHOW" + "2SHOW\n" + "+sh{ow} {-c} br{eak} show breakpoints\n" + "+sh{ow} con{figuration} show configuration\n" + "+sh{ow} cons{ole} {arg} show console options\n" + "+sh{ow} {-ei} dev{ices} show devices\n" + "+sh{ow} fea{tures} show system devices with descriptions\n" + "+sh{ow} m{odifiers} show modifiers for all devices\n" + "+sh{ow} s{how} show SHOW commands for all devices\n" + "+sh{ow} n{ames} show logical names\n" + "+sh{ow} q{ueue} show event queue\n" + "+sh{ow} ti{me} show simulated time\n" + "+sh{ow} th{rottle} show simulation rate\n" + "+sh{ow} a{synch} show asynchronouse I/O state\n" + "+sh{ow} ve{rsion} show simulator version\n" + "+sh{ow} def{ault} show current directory\n" + "+sh{ow} re{mote} show remote console configuration\n" + "+sh{ow} RADIX show device display radix\n" + "+sh{ow} DEBUG show device debug flags\n" + "+sh{ow} MODIFIERS show device modifiers\n" + "+sh{ow} NAMES show device logical name\n" + "+sh{ow} SHOW show device SHOW commands\n" + "+sh{ow} {arg,...} show device parameters\n" + "+sh{ow} {arg,...} show unit parameters\n" + "+sh{ow} ethernet show ethernet devices\n" + "+sh{ow} serial show serial devices\n" + "+sh{ow} multiplexer show open multiplexer devices\n" +#if defined(USE_SIM_VIDEO) + "+sh{ow} video show video capabilities\n" +#endif + "+sh{ow} clocks show calibrated timer information\n" + "+sh{ow} throttle show throttle info\n" + "+sh{ow} on show on condition actions\n" + "+h{elp} show displays the device specific show commands\n" + "++++++++ available\n" +#define HLP_SHOW_CONFIG "*Commands SHOW" +#define HLP_SHOW_DEVICES "*Commands SHOW" +#define HLP_SHOW_FEATURES "*Commands SHOW" +#define HLP_SHOW_QUEUE "*Commands SHOW" +#define HLP_SHOW_TIME "*Commands SHOW" +#define HLP_SHOW_MODIFIERS "*Commands SHOW" +#define HLP_SHOW_NAMES "*Commands SHOW" +#define HLP_SHOW_SHOW "*Commands SHOW" +#define HLP_SHOW_VERSION "*Commands SHOW" +#define HLP_SHOW_DEFAULT "*Commands SHOW" +#define HLP_SHOW_CONSOLE "*Commands SHOW" +#define HLP_SHOW_REMOTE "*Commands SHOW" +#define HLP_SHOW_BREAK "*Commands SHOW" +#define HLP_SHOW_LOG "*Commands SHOW" +#define HLP_SHOW_DEBUG "*Commands SHOW" +#define HLP_SHOW_THROTTLE "*Commands SHOW" +#define HLP_SHOW_ASYNCH "*Commands SHOW" +#define HLP_SHOW_ETHERNET "*Commands SHOW" +#define HLP_SHOW_SERIAL "*Commands SHOW" +#define HLP_SHOW_MULTIPLEXER "*Commands SHOW" +#define HLP_SHOW_VIDEO "*Commands SHOW" +#define HLP_SHOW_CLOCKS "*Commands SHOW" +#define HLP_SHOW_ON "*Commands SHOW" +#define HLP_SHOW_SEND "*Commands SHOW" +#define HLP_SHOW_EXPECT "*Commands SHOW" +#define HLP_HELP "*Commands HELP" + /***************** 80 character line width template *************************/ + "2HELP\n" + "+h{elp} type this message\n" + "+h{elp} type help for command\n" + "+h{elp} type help for device\n" + "+h{elp} registers type help for device register variables\n" + "+h{elp} attach type help for device specific ATTACH command\n" + "+h{elp} set type help for device specific SET commands\n" + "+h{elp} show type help for device specific SHOW commands\n" + "+h{elp} type help for device specific command\n" + /***************** 80 character line width template *************************/ + "2Altering The Simulated Configuration\n" + " In most simulators, the SET DISABLED command removes the\n" + " specified device from the configuration. A DISABLED device is invisible\n" + " to running programs. The device can still be RESET, but it cannot be\n" + " ATTAChed, DETACHed, or BOOTed. SET ENABLED restores a disabled\n" + " device to a configuration.\n\n" + " Most multi-unit devices allow units to be enabled or disabled:\n\n" + "++SET ENABLED\n" + "++SET DISABLED\n\n" + " When a unit is disabled, it will not be displayed by SHOW DEVICE.\n\n" +#define HLP_ASSIGN "*Commands Logical_Names" +#define HLP_DEASSIGN "*Commands Logical_Names" + "2Logical Names\n" + " The standard device names can be supplemented with logical names. Logical\n" + " names must be unique within a simulator (that is, they cannot be the same\n" + " as an existing device name). To assign a logical name to a device:\n\n" + "++ASSIGN assign log-name to device\n\n" + " To remove a logical name:\n\n" + "++DEASSIGN remove logical name\n\n" + " To show the current logical name assignment:\n\n" + "++SHOW NAMES show logical name, if any\n\n" + " To show all logical names:\n\n" + "++SHOW NAMES\n\n" + /***************** 80 character line width template *************************/ +#define HLP_DO "*Commands Executing_Command_Files" + "2Executing Command Files\n" + " The simulator can execute command files with the DO command:\n\n" + "++DO {arguments...} execute commands in file\n\n" + " The DO command allows command files to contain substitutable arguments.\n" + " The string %%n, where n is between 1 and 9, is replaced with argument n\n" + " from the DO command line. The string %%0 is replaced with .\n" + " The string %%* is replaced by the whole set of arguments (%%1 ... %%9).\n" + " The sequences \\%% and \\\\ are replaced with the literal characters %% and \\,\n" + " respectively. Arguments with spaces can be enclosed in matching single\n" + " or double quotation marks.\n\n" + " DO commands may be nested up to ten invocations deep.\n\n" + "3Switches\n" + " If the switch -v is specified, the commands in the file are echoed before\n" + " they are executed.\n\n" + " If the switch -e is specified, command processing (including nested command\n" + " invocations) will be aborted if a command error is encountered.\n" + " (Simulation stop never abort processing; use ASSERT to catch unexpected\n" + " stops.) Without the switch, all errors except ASSERT failures will be\n" + " ignored, and command processing will continue.\n\n" + " If the switch -o is specified, the on conditions and actions from the\n" + " calling command file will be inherited in the command file being invoked.\n" + " If the switch -q is specified, the quiet mode will be explicitly enabled\n" + " for the called command file, otherwise quiet mode is inherited from the\n" + " calling context.\n" + /***************** 80 character line width template *************************/ + "3Variable_Insertion\n" + " Built In variables %%DATE%%, %%TIME%%, %%DATETIME%%, %%LDATE%%, %%LTIME%%,\n" + " %%CTIME%%, %%DATE_YYYY%%, %%DATE_YY%%, %%DATE_YC%%, %%DATE_MM%%, %%DATE_MMM%%,\n" + " %%DATE_MONTH%%, %%DATE_DD%%, %%DATE_D%%, %%DATE_WYYYY%%, %%DATE_WW%%,\n" + " %%TIME_HH%%, %%TIME_MM%%, %%TIME_SS%%, %%STATUS%%, %%TSTATUS%%, %%SIM_VERIFY%%,\n" + " %%SIM_QUIET%%, %%SIM_MESSAGE%% %%SIM_MESSAGE%%\n" + " %%SIM_NAME%%, %%SIM_BIN_NAME%%, %%SIM_BIN_PATH%%m %%SIM_OSTYPE%%\n\n" + "+Token %%0 expands to the command file name.\n" + "+Token %%n (n being a single digit) expands to the n'th argument\n" + "+Token %%* expands to the whole set of arguments (%%1 ... %%9)\n\n" + "+The input sequence \"%%%%\" represents a literal \"%%\". All other\n" + "+character combinations are rendered literally.\n\n" + "+Omitted parameters result in null-string substitutions.\n\n" + "+Tokens preceeded and followed by %% characters are expanded as environment\n" + "+variables, and if an environment variable isn't found then it can be one of\n" + "+several special variables:\n\n" + "++%%DATE%% yyyy-mm-dd\n" + "++%%TIME%% hh:mm:ss\n" + "++%%DATETIME%% yyyy-mm-ddThh:mm:ss\n" + "++%%LDATE%% mm/dd/yy (Locale Formatted)\n" + "++%%LTIME%% hh:mm:ss am/pm (Locale Formatted)\n" + "++%%CTIME%% Www Mmm dd hh:mm:ss yyyy (Locale Formatted)\n" + "++%%UTIME%% nnnn (Unix time - seconds since 1/1/1970)\n" + "++%%DATE_YYYY%% yyyy (0000-9999)\n" + "++%%DATE_YY%% yy (00-99)\n" + "++%%DATE_MM%% mm (01-12)\n" + "++%%DATE_MMM%% mmm (JAN-DEC)\n" + "++%%DATE_MONTH%% month (January-December)\n" + "++%%DATE_DD%% dd (01-31)\n" + "++%%DATE_WW%% ww (01-53) ISO 8601 week number\n" + "++%%DATE_WYYYY%% yyyy (0000-9999) ISO 8601 week year number\n" + "++%%DATE_D%% d (1-7) ISO 8601 day of week\n" + "++%%DATE_JJJ%% jjj (001-366) day of year\n" + "++%%DATE_19XX_YY%% yy A year prior to 2000 with the same\n" + "++++++++++ calendar days as the current year\n" + "++%%DATE_19XX_YYYY%% yyyy A year prior to 2000 with the same\n" + "++++++++++ calendar days as the current year\n" + "++%%TIME_HH%% hh (00-23)\n" + "++%%TIME_MM%% mm (00-59)\n" + "++%%TIME_SS%% ss (00-59)\n" + "++%%STATUS%% Status value from the last command executed\n" + "++%%TSTATUS%% The text form of the last status value\n" + "++%%SIM_VERIFY%% The Verify/Verbose mode of the current Do command file\n" + "++%%SIM_VERBOSE%% The Verify/Verbose mode of the current Do command file\n" + "++%%SIM_QUIET%% The Quiet mode of the current Do command file\n" + "++%%SIM_MESSAGE%% The message display status of the current Do command file\n" + "++%%SIM_NAME%% The name of the current simulator\n" + "++%%SIM_BIN_NAME%% The program name of the current simulator\n" + "++%%SIM_BIN_PATH%% The program path that invoked the current simulator\n" + "++%%SIM_OSTYPE%% The Operating System running the current simulator\n\n" + "+Environment variable lookups are done first with the precise name between\n" + "+the %% characters and if that fails, then the name between the %% characters\n" + "+is upcased and a lookup of that valus is attempted.\n\n" + "+The first Space delimited token on the line is extracted in uppercase and\n" + "+then looked up as an environment variable. If found it the value is\n" + "+supstituted for the original string before expanding everything else. If\n" + "+it is not found, then the original beginning token on the line is left\n" + "+untouched.\n" +#define HLP_GOTO "*Commands Executing_Command_Files GOTO" + "3GOTO\n" + " Commands in a command file execute in sequence until either an error\n" + " trap occurs (when a command completes with an error status), or when an\n" + " explict request is made to start command execution elsewhere with the\n" + " GOTO command:\n\n" + "++GOTO