Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -87,23 +87,21 @@ PIDP8I_DIRS := lib/pidp8i/dirs.py PIDP8I_DIN := @srcdir@/$(PIDP8I_DIRS).in GENNED_PY := \ lib/pidp8i/__init__.py \ lib/pidp8i/ips.py \ + lib/pidp8i/os8opts.py \ $(PIDP8I_DIRS) SIMH_PY := lib/simh.py SIMH_PY_SRC := @srcdir@/$(SIMH_PY) -MKOS8 := @srcdir@/libexec/mkos8 -MKOS8_LIB := @srcdir@/lib/mkos8 -MKOS8_PY := \ - $(MKOS8_LIB)/__init__.py \ - $(MKOS8_LIB)/argparser.py \ - lib/mkos8/opts.py -MKOS8_PY_ALL := $(GENNED_PY) $(MKOS8_PY) $(SIMH_PY_SRC) -MKOS8_SRCS := $(MKOS8) $(MKOS8_PY_ALL) $(PIDP8I_DIN) +OS8RUN = bin/os8-run +OS8SCRIPT := lib/os8script.py +OS8CP := bin/os8-cp +OS8RUN_PY_ALL := $(GENNED_PY) $(OS8SCRIPT) $(SIMH_PY_SRC) +OS8RUN_SRCS := $(OS8RUN) $(OS8RUN_PY_ALL) $(PIDP8I_DIN) # If you edit this directory list, you should probably also edit # the argument list to the mkadrules call in auto.def! BUILDDIRS := \ bin \ @@ -114,11 +112,11 @@ 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 +INSTDIRS := bin etc lib/pidp8i libexec share/boot share/media share/include share/man/man1 SIM_OBJS := \ obj/pidp8i/gpio-common.o \ obj/pidp8i/main.o \ obj/SIMH/PDP8/pdp8_df.o \ @@ -182,10 +180,12 @@ ifeq (@BUILD_DEEPER_THOUGHT@, 1) BINS += bin/deeper endif +TEST_TMP_BINS = bin/ptp2txt bin/pidp8i-sim bin/os8-run + LIBS = -lm -ldl -lpthread ASM_PTS := $(wildcard @srcdir@/src/asm/*.pal) ASM_PTS := $(subst @srcdir@/src/asm,bin,$(ASM_PTS)) ASM_PTS := $(ASM_PTS:.pal=-pal.pt) @@ -204,23 +204,24 @@ # List of *.in files from auto.def file, except for this present file # (Makefile.in) which is handled separately. This list should only # change when the list of "make-template" calls in auto.def changes. # -# The MKOS8_INFILES set of files are those which, if changed, require +# The OS8RUN_INFILES set of files are those which, if changed, require # rebuilding the OS/8 media. Touching the combined set merely causes # a reconfig and build. # # The PRECIOUS set are those whose outfiles we want make(1) to treat as # "precious", meaning it won't delete files generated by a target if the # rule being processed to create that target fails. # # The rest have no special treatment. -MKOS8_INFILES = \ +OS8RUN_INFILES = \ @srcdir@/lib/pidp8i/__init__.py.in \ @srcdir@/lib/pidp8i/ips.py.in \ @srcdir@/media/os8/init.tx.in \ + @srcdir@/media/os8/3finit.tx.in \ @srcdir@/src/pidp8i/main.c.in \ $(PIDP8I_DIN) PRECIOUS_INFILES = \ @srcdir@/Makefile.in \ @srcdir@/examples/Makefile.in \ @@ -235,26 +236,56 @@ @srcdir@/boot/3.script.in \ @srcdir@/boot/4.script.in \ @srcdir@/boot/6.script.in \ @srcdir@/boot/7.script.in \ @srcdir@/boot/run.script.in \ + @srcdir@/boot/run-v3f.script.in \ @srcdir@/etc/pidp8i.service.in \ @srcdir@/etc/sudoers.in \ @srcdir@/etc/usb-mount@.service.in \ @srcdir@/src/pidp8i/gpio-common.c.in \ @srcdir@/tools/simh-update.in \ - $(MKOS8_INFILES) -MKOS8_OUTFILES := $(subst @srcdir@/,,$(MKOS8_INFILES)) -MKOS8_OUTFILES := $(subst .in,,$(MKOS8_OUTFILES)) + $(OS8CP) \ + $(OS8RUN_INFILES) +OS8RUN_OUTFILES := $(subst @srcdir@/,,$(OS8RUN_INFILES)) +OS8RUN_OUTFILES := $(subst .in,,$(OS8RUN_OUTFILES)) PRECIOUS_OUTFILES := $(subst @srcdir@/,,$(PRECIOUS_INFILES)) PRECIOUS_OUTFILES := $(subst .in,,$(PRECIOUS_OUTFILES)) OUTFILES := $(subst @srcdir@/,,$(INFILES)) OUTFILES := $(subst .in,,$(OUTFILES)) -OS8_BIN_RK05 = bin/os8v3d-bin.rk05 +OS8_DIST_RK05 = bin/v3d-dist.rk05 OS8_SRC_RK05 = @OS8_SRC_RK05@ -OS8_RK05S = $(OS8_BIN_RK05) $(OS8_SRC_RK05) +OS8_BOOT_DISK = bin/@OS8_BOOT_DISK@ +OS8_RK05S = $(OS8_DIST_RK05) $(OS8_BOOT_DISK) $(OS8_SRC_RK05) + +OS8_SCRIPTS_DIR = @srcdir@/media/os8/scripts + +V3D_DIST_SCRIPT = $(OS8_SCRIPTS_DIR)/v3d-dist-rk05.os8 +V3D_SRC_SCRIPT = $(OS8_SCRIPTS_DIR)/v3d-src-rk05.os8 +V3D_RK05_SCRIPT = $(OS8_SCRIPTS_DIR)/v3d-rk05.os8 + +OS8CP := bin/os8-cp + +V3F_SRCDIR = @srcdir@/src/os8/v3f +V3F_BUILD_RK05 = obj/v3f-build.rk05 +V3F_MANIFEST = actions.txt + +V3F_MADE_RK05 = bin/v3f-made.rk05 + +V3F_MAKER = $(OS8_SCRIPTS_DIR)/v3f-control.os8 + +ALL_TU56_SCRIPT = $(OS8_SCRIPTS_DIR)/all-tu56.os8 + +CUSP_COPYIN_SCRIPT = $(OS8_SCRIPTS_DIR)/cusp-copyin.os8 + +OS8_BOOT_TAPE = bin/@OS8_BOOT_TAPE@ +V3D_TC08_TU56 = bin/v3d-tc08.tu56 +V3D_TD12K_TU56 = bin/v3d-td12k.tu56 +V3F_TC08_TU56 = bin/v3f-tc08.tu56 +V3F_TD12K_TU56 = bin/v3f-td12k.tu56 +V3F_BOOT_TAPE= bin/v3f-@OS8_TAPE_DEVICE@.tu56 CLTXT = /boot/cmdline.txt ADF := adrules.mk @@ -266,19 +297,22 @@ all: \ $(OUTFILES) $(PRECIOUS_OUTFILES) \ $(BUILDDIRS) $(BINS) $(BOOTSCRIPTS) $(LISTINGS) \ $(ASM_PTS) $(FC_EX_PTS) $(PAL_EX_PTS) \ - $(OS8_RK05S) + $(OS8_RK05S) $(V3F_MADE_RK05) $(V3D_TC08_TU56) \ + $(V3D_TD12K_TU56) $(V3F_TC08_TU56) $(V3F_TD12K_TU56) clean: @rm -f $(BINS) $(BOOTSCRIPTS) $(ASM_PTS) $(PAL_EX_PTS) $(LISTINGS) \ $(OUTFILES) $(ADF) \ + $(OS8_RK05S) $(V3F_MADE_RK05) $(V3F_BUILD_RK05) $(V3D_TC08_TU56) \ + $(V3D_TD12K_TU56) $(V3F_TC08_TU56) $(V3F_TD12K_TU56) \ config.log cscope.out tags \ - bin/*.pt bin/*.rk05 bin/*.save bin/txt2ptp \ - lib/*.pyc lib/*/*.pyc lib/mkos8/opts.py \ - obj/*.log obj/*.pt obj/mkos8.opts \ + bin/*.pt bin/*.save bin/txt2ptp \ + lib/*.pyc lib/*/*.pyc lib/pidp8i/dirs.py lib/pidp8i/ips.py \ + obj/*.log obj/*.pt obj/os8.opts \ src/config.h \ @srcdir@/examples/*.err @find obj \( -name \*.o -o -name \*.d \) -delete @-rmdir -p $(BUILDDIRS) 2> /dev/null || true @@ -288,11 +322,11 @@ config.log \ autosetup/jimsh0 \ src/config.h ctags tags: - ctags -R @srcdir@/src @srcdir@/lib @srcdir@/libexec/mkos8 + ctags -R @srcdir@/src @srcdir@/lib ifeq (@HAVE_PROG_CSCOPE@, 1) @cscope -bR -s@srcdir@ endif install: all instdirs @@ -317,11 +351,11 @@ for f in @prefix@/bin/pidp8i-* ; do \ echo "Setting real-time priority capabilities on $$(basename $$f)..." ; \ /sbin/setcap 'cap_sys_nice=eip' $$f ; \ done \ ) || true - @test -e @MEDIADIR@/os8/os8.tu56 && \ + @test -e @MEDIADIR@/os8/local.tu56 && \ echo "Skipping media image and SIMH boot script reinstall." || \ $(MAKE) mediainstall @# If this is a Debian-type system, install needed helper programs @test -x /usr/bin/apt-get -a ! -x /usr/bin/screen && apt-get -y install screen || true @@ -402,14 +436,14 @@ @# Install palbart stuff @@INSTALL@ -m 755 bin/palbart @prefix@/bin @@INSTALL@ -m 644 @srcdir@/src/palbart/palbart.1 @prefix@/share/man/man1 - @# Install mkos8 and its dependencies - @echo "Installing mkos8..." - @@INSTALL@ -m 775 -g @INSTGRP@ $(MKOS8) @prefix@/libexec - @( for src in $(MKOS8_PY_ALL) ; do \ + @# Install os8-run and its dependencies + @echo "Installing os8run..." + @@INSTALL@ -m 775 -g @INSTGRP@ $(OS8RUN) @prefix@/bin + @( for src in $(OS8RUN_PY_ALL) ; do \ test -e $$src || src=@srcdir@/$$src ; \ dest=@prefix@/$$(echo $$src | sed -e 's_^@srcdir@/__') ; \ echo "Installing $$src to $$dest..." ; \ @INSTALL@ -m 644 -g @INSTGRP@ -D $${src} $${dest} ; \ @INSTALL@ -m 644 -g @INSTGRP@ -D $${src}c $${dest}c ; \ @@ -427,25 +461,29 @@ mediainstall: instdirs @echo "[Re]installing OS and program media..." @cd @srcdir@ ; \ find media \( \ + -name \*.os8 -o \ -name \*.bin -o \ -name \*.dsk -o \ -name \*.rk05 -o \ -name \*.tu56 \ \) -exec @INSTALL@ -D -m 664 -o @INSTUSR@ -g @INSTGRP@ {} @ABSPREFIX@/share/{} \; - @@INSTALL@ -m 644 -o @INSTUSR@ -g @INSTGRP@ bin/os8v3d-*.rk05 @ABSPREFIX@/share/media/os8 + @@INSTALL@ -m 644 -o @INSTUSR@ -g @INSTGRP@ bin/*.rk05 @ABSPREFIX@/share/media/os8 + @@INSTALL@ -m 644 -o @INSTUSR@ -g @INSTGRP@ bin/*.tu56 @ABSPREFIX@/share/media/os8 @@INSTALL@ -m 664 -o @INSTUSR@ -g @INSTGRP@ boot/*.script @BOOTDIR@ -# No-dependencies versions of the bin/os8v3d-*.rk05 targets used by -# tools/test-mkos8, because there's no point rebuilding the simulator -# on each iteration. -os8-bin: - $(MKOS8)@MKOS8_OPTS@ bin -os8-patched: - $(MKOS8)@MKOS8_OPTS@ bin patch +# For reasons I do not understand, the two runs of os8-run create new +# log files rather than appending the second run to the first file. +# We need both logs, so we take steps to get them with mv, and cat. +os8-sys: test-tmp-tools + $(OS8RUN)@OS8_OPTS@ $(V3D_DIST_SCRIPT) + mv obj/os8-run.log obj/os8-run1.log + $(OS8RUN)@OS8_OPTS@ $(V3D_RK05_SCRIPT) + mv obj/os8-run.log obj/os8-run2.log + cat obj/os8-run1.log obj/os8-run2.log >obj/os8-run.log reconfig: @AUTOREMAKE@ release: all @@ -452,18 +490,47 @@ @srcdir@/tools/mkrel run: $(SIM) $(SIM) boot/run.script +runf run-v3f: $(SIM) $(V3F_BOOT_TAPE) + $(SIM) boot/run-v3f.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 +# Enable this target only if all of the test-os8-run prereqs +# were installed at configure time. +ifeq (@CAN_TEST_OS8_RUN@, 1) +test-os8-run: + tools/test-os8-run +endif + +test-tmp-tools: + @echo "Installing os8run dependencies..." + @( for src in $(OS8RUN_PY_ALL) ; do \ + test -e $$src || src=@srcdir@/$$src ; \ + dest=./$$(echo $$src | sed -e 's_^@srcdir@/__') ; \ + echo "Installing $$src to $$dest..." ; \ + @INSTALL@ -m 644 -g @INSTGRP@ -D $${src} $${dest} ; \ + @INSTALL@ -m 644 -g @INSTGRP@ -D $${src}c $${dest}c ; \ + done \ + ) + @( for src in $(TEST_TMP_BINS) ; do \ + test -e $$src || src=@srcdir@/$$src ; \ + dest=./$$(echo $$src | sed -e 's_^@srcdir@/__') ; \ + echo "Installing $$src to $$dest..." ; \ + @INSTALL@ -m 755 -g @INSTGRP@ -D $${src} $${dest} ; \ + done \ + ) + @( cd ./bin ; \ + echo "Installing txt2ptp symlink..." ; \ + ln -f ptp2txt txt2ptp ; \ + ) # Build the OS/8 binary media needed by 0.script. # # We use order-only prerequisites for the simulator here because we only @@ -478,32 +545,72 @@ # anyway!) The thing is, we only want the RK05 bin media rebuilt when # the configure --*-os8-* options change. *That* is when we care about # the updated init.tx file, not before. We needn't even make it an # order-only prereq because configure and the INFILES rules above ensure # that it always exists. -OS8_BIN_SRCS := \ - $(MKOS8) $(PIDP8I_DIN) \ +V3D_DIST_SRCS := \ + $(OS8RUN) $(PIDP8I_DIN) \ lib/pidp8i/dirs.py \ + $(V3D_DIST_SCRIPT) \ @srcdir@/media/os8/al-*-ba-*.tu56 \ @srcdir@/media/os8/subsys/*.tu56 -ifneq (@MKOS8_BIN_PATCHES@,) - OS8_BIN_SRCS += @srcdir@/media/os8/patches/patch_list.txt -endif -$(OS8_BIN_RK05): $(OS8_BIN_SRCS) | $(SIM) $(MKOS8_OUTFILES) - $(MKOS8)@MKOS8_OPTS@ bin @MKOS8_BIN_PATCHES@ + +# Temporary: We depend on ALL the patches, but really we've picked them over. +V3D_PATCHES := \ + @srcdir@/media/os8/patches/*.patch8 + +# Dependency on the contents of the v3f source directory. +V3F_SOURCES := \ + @srcdir@/src/os8/v3f/*.PA \ + @srcdir@/src/os8/v3f/*.MA \ + @srcdir@/src/os8/v3f/*.BI + + +$(OS8_DIST_RK05): $(V3D_DIST_SRCS) | $(SIM) $(OS8RUN_OUTFILES) + $(OS8RUN)@OS8_OPTS@ $(V3D_DIST_SCRIPT) + +$(OS8_BOOT_DISK): $(V3D_RK05_SCRIPT) $(OS8_DIST_RK05) $(V3D_PATCHES) | $(SIM) $(OS8RUN_OUTFILES) + $(OS8RUN)@OS8_OPTS@ $(V3D_RK05_SCRIPT) + # Also build an OS/8 source disk, as a convenience to avoid the # need to mount up the 7 source tapes in succession. # # Using an order-only dependency for the simulator and the bin disk: we # only need *a* version of each, they don't have to be recent! OS8_SRC_SRCS = \ - $(MKOS8) $(PIDP8I_DIN) \ + $(OS8RUN) $(PIDP8I_DIN) \ lib/pidp8i/dirs.py \ + $(V3D_SRC_SCRIPT) \ @srcdir@/media/os8/al-*-sa-*.tu56 -$(OS8_SRC_RK05): $(OS8_SRC_SRCS) | $(SIM) $(OS8_BIN_RK05) - $(MKOS8)@MKOS8_OPTS@ src + +$(OS8_SRC_RK05): $(OS8_DIST_RK05) $(OS8_SRC_SRCS) | $(SIM) $(OS8_BOOT_DISK) + $(OS8RUN)@OS8_OPTS@ $(V3D_SRC_SCRIPT) + +# Build the source disk for OS/8 V3F +$(V3F_BUILD_RK05): $(V3F_SRCDIR)/$(V3F_MANIFEST) $(V3F_SOURCES) | $(OS8_BOOT_DISK) + cd $(V3F_SRCDIR); @builddir@/$(OS8CP) -v --action-file $(V3F_MANIFEST) + +# Make a disk with binaries assembled from the V3F source disk +$(V3F_MADE_RK05): $(V3F_BUILD_RK05) $(V3F_MAKER) + $(OS8RUN) $(V3F_MAKER) + +# Make a bootable OS/8 v3f TCO8 DECtape image +$(V3F_TC08_TU56): $(V3F_MADE_RK05) $(ALL_TU56_SCRIPT) $(CUSP_COPYIN_SCRIPT) + $(OS8RUN) $(ALL_TU56_SCRIPT) --enable v3f + +# Make a bootable OS/8 v3f TD8E 12K DECtape image +$(V3F_TD12K_TU56): $(V3F_MADE_RK05) $(ALL_TU56_SCRIPT) $(CUSP_COPYIN_SCRIPT) + $(OS8RUN) $(ALL_TU56_SCRIPT) --enable v3f --enable td12k + +# Make a bootable OS/8 v3d TCO8 DECtape image +$(V3D_TC08_TU56): $(ALL_TU56_SCRIPT) $(CUSP_COPYIN_SCRIPT) | $(OS8_BOOT_DISK) + $(OS8RUN) $(ALL_TU56_SCRIPT) + +# Make a bootable OS/8 v3d TD8E 12K DECtape image +$(V3D_TD12K_TU56): $(ALL_TU56_SCRIPT) $(CUSP_COPYIN_SCRIPT) | $(OS8_BOOT_DISK) + $(OS8RUN) $(ALL_TU56_SCRIPT) --enable td12k # Rule for building PAL assembly language programs in src/asm/*.pal. obj/%.lst bin/%-pal.pt: @srcdir@/src/asm/%.pal bin/palbart bin/palbart -lr $< || cat obj/$*.err mv @srcdir@/src/asm/$*.lst obj @@ -571,10 +678,12 @@ # configure script in parallel up to the limit of -j or the number of # files in INFILES, whichever is lower. Order-only prerequisites can't # help here (|) as that only affects the right hand side. ifeq ($(findstring clean,$(MAKECMDGOALS)),) media/os8/init.tx: $(INFILES) $(PRECIOUS_INFILES) @AUTODEPS@ + @AUTOREMAKE@ && $(MAKE) +media/os8/3finit.tx: $(INFILES) $(PRECIOUS_INFILES) @AUTODEPS@ @AUTOREMAKE@ && $(MAKE) # Also do it if the autodep tool is newer than its output, suggesting # that if re-run, it would generate different output. $(ADF): @srcdir@/tools/mkadrules Index: README.md ================================================================== --- README.md +++ README.md @@ -415,14 +415,10 @@ normally installed to `RKA0:` * **--disable-os8-macrel** - Leave the MACREL v2 assembler and its associated FUTIL V8B tool out. -* **--disable-os8-patches** - Do not apply any of the OS/8 V3D - patches published by DEC. See the [documentation][os8p] for this - option for more information. - * **--disable-os8-src** - Do not build the `os8v3d-src.rk05` disk image from the OS/8 source tapes. This is not controlled by `--os8-minimal` because that only affects `os8v3d-bin.rk05`. * **--disable-os8-uwfocal** - Leave out the U/W FOCAL V4E programming @@ -623,16 +619,21 @@ [oce]: https://tangentsoft.com/pidp8i/wiki?name=OS/8+Console+TTY+Setup ### Patches -After the baseline disk image is created, `mkos8` makes a copy of it as -`os8v3d-patched.rk05` and applies a [carefully selected set of official -DEC patches][os8p] to it unless you give the `--disable-os8-patches` -configuration option. The IF=0 and IF=7 boot options boot from the -patched disk unless you give that option. +The build process creates a baseline disk image called `v3d-dist.rk05`. +This is considered a read-only master. Then a copy is made called +`v3d.rk05`. This is the default OS/8 rk05 image assigned to the IF=0 and IF=7 +boot options. +In keeping with the standards of good systm management +this image incorporates all mandatory patches, as well as +optional patches appropriate to proper operation of the system. +For details on the available patches, the selection criteria, +and information about other optional patches see the [OS/8 system patches][os8p] +document. [bdt2]: https://tangentsoft.com/pidp8i/file/media/os8/al-4712c-ba-os8-v3d-2.1978.tu56 [cl]: https://tangentsoft.com/pidp8i/doc/trunk/ChangeLog.md [cs]: https://tangentsoft.com/pidp8i/doc/trunk/doc/class-simh.md [mkos8]: https://tangentsoft.com/pidp8i/doc/trunk/libexec/mkos8 Index: auto.def ================================================================== --- auto.def +++ auto.def @@ -87,18 +87,31 @@ debug-mode => "create a debug build (default is release)" lowercase: => "select how lowercase input is to be handled" no-lamp-simulator => "use simple LED driver instead of incandescent lamp simulator" os8-focal=1 => "leave FOCAL 69 and U/W FOCAL off the built OS/8 RK05 image" os8-minimal => "set all --disable-os8-* options, giving minimal OS/8 bin disk" - os8-patches=1 => "do not apply DEC patches to the built OS/8 RK05 bin image" - os8-src=1 => "do not build os8v3d-src.rk05 from OS/8 source tapes" + os8-src=1 => "do not build v3d-src.rk05 from OS/8 source tapes" serial-mod => "use GPIO drive scheme suitable for Oscar Vermeulen's serial mod method" throttle: => "override the throttle values in the boot scripts" usb-automount=1 => "do not automatically mount USB drives for SING_STEP + DF" + boot-tape-config: => "Boot tape configuration: tc08, or td12k" + boot-tape-version: => "OS/8 version for boot tape, either v3d or v3f" } options $alloptions + +# use btc and btv to set defaults because I can't get the documented +# syntax of := to work. + +# Default boot-tape-config is tc08 +set btc [opt-val boot-tape-config] +if {$btc eq ""} {set btc "tc08"} + +# Default boot-tape-version is v3d +set btv [opt-val boot-tape-version] +if {$btv eq ""} {set btv "v3d"} + define USB_AUTOMOUNT [opt-bool usb-automount] # Make our own array of os8 options so we can apply the meta options foreach {opt def desc} $os8opts { set os8bool($opt) [opt-bool os8-$opt] @@ -121,37 +134,37 @@ set $os8bool(fortran-ii) 1 msg-result "WARNING: Re-enabling OS/8 FORTRAN II: OS/8 CC8 requires it." } # Now build the options to mkos8 -set mkos8_opts "" +set os8_opts "" foreach {opt def desc} $os8opts { set val $os8bool($opt) if {$os8min} { # Minimal mode disables everything optional. # # We can't have "except for" logic because we can't tell # explicitly-given options from implicit ones. set act " not" - if {$def} { append mkos8_opts " --disable-$opt" } + if {$def} { append os8_opts " --disable $opt" } } elseif {$val} { set act "" if {!$def} { # User is enabling a normally-disabled option, so pass it along # to mkos8 explicitly. - append mkos8_opts " --enable-$opt" + append os8_opts " --enable $opt" } } else { set act " not" if {$def} { - append mkos8_opts " --disable-$opt" + append os8_opts " --disable $opt" } } msg-result "Will$act add $desc to the built OS/8 RK05 image" } -# And generate mkos8/opts.py +# And generate lib/pidp8i/os8opts.py set os "# GENERATED BY auto.def; DO NOT MODIFY.\n\nopts = {\n" foreach {opt def desc} $os8opts { set act [expr {$def ? "Leave $desc off" : "Add $desc to"}] append os "\t\"" $opt "\": \[ $def,\ \"$act the built OS/8 RK05 image\" \],\n" @@ -158,25 +171,34 @@ } append os "}\n" ; # saved later! # Handle the OS/8 options not passed to mkos8 generically by above. if {$os8src} { - msg-result "Building os8v3d-src.rk05 from OS/8 source tapes." - define OS8_SRC_RK05 bin/os8v3d-src.rk05 + msg-result "Building v3d-src.rk05 from OS/8 source tapes." + define OS8_SRC_RK05 bin/v3d-src.rk05 } else { - msg-result "Will not build os8v3d-src.rk05 from OS/8 source tapes." + msg-result "Will not build v3d-src.rk05 from OS/8 source tapes." define OS8_SRC_RK05 {} } -if {$os8min || ![opt-bool os8-patches]} { - msg-result "Will not patch OS/8 on the RK05 bin disk." - define OS8_BOOT_DISK "os8v3d-bin.rk05" - define MKOS8_BIN_PATCHES {} +# Define our boot image. (Note, we ALWAYS apply patches.) +define OS8_BOOT_DISK "v3d.rk05" + +# Compose our boot tape image name from boot-tape-config and boot-tape-version +# Pull defaults from $btc for boot-tape-config, and $btv for boot-tape-version. + +set btn "$btv-$btc.tu56" + +msg-result "Boot tape name: $btn" + +define OS8_BOOT_TAPE $btn +define OS8_TAPE_DEVICE $btc +if {$btc eq "tc08"} { + define SIMH_TAPE_DEVICE "dt" } else { - msg-result "Will apply OS/8 V3D patches to the RK05 bin disk." - define OS8_BOOT_DISK "os8v3d-patched.rk05" - define MKOS8_BIN_PATCHES patch -} + define SIMH_TAPE_DEVICE "td" +} + # React to remaining chosen command line options if {[opt-bool alt-serial-mod]} { if {[opt-bool serial-mod]} { user-error "You cannot request both --alt-serial-mod and --serial-mod!" @@ -210,29 +232,31 @@ if {$lv == ""} { set lv "auto" } if {$lv == "auto"} { define SIMH_PASS_LOWERCASE } elseif {$lv == "pass"} { define SIMH_PASS_LOWERCASE - append mkos8_opts " --disable-lcmod" + append os8_opts " --disable lcmod" } elseif {$lv == "upper"} { - append mkos8_opts " --disable-lcmod" + append os8_opts " --disable lcmod" } else { user-error "Legal values for --lowercase are {auto,pass,upper}." } msg-result "Lowercase handling set to '$lv' mode." # We've purposely held off exporting the mkos8 option set until now # because some of the configuration options can affect the option set. -define MKOS8_OPTS $mkos8_opts -if {![file exists "lib/mkos8"]} { file mkdir "lib/mkos8" } -write-if-changed "lib/mkos8/opts.py" $os +define OS8_OPTS $os8_opts +if {![file exists "lib/pidp8i"]} { file mkdir "lib/pidp8i" } +if {![file exists "obj/pidp8i"]} { file mkdir "obj/pidp8i" } +if {![file exists "obj/SIMH/PDP8"]} { file mkdir "obj/SIMH/PDP8" } +write-if-changed "lib/pidp8i/os8opts.py" $os # Force a rebuild of the OS/8 media if the option set changed. if {![file exists "obj"]} { file mkdir "obj" } -write-if-changed "obj/mkos8.opts" $mkos8_opts { - file delete -force bin/os8v3d-bin.rk05 - msg-result "mkos8 options changed; will rebuild OS/8 disk images." +write-if-changed "obj/os8.opts" $os8_opts { + file delete -force bin/v3d-dist.rk05 + msg-result "os8 options changed; will rebuild OS/8 disk images." } # High-level definitions set builddir [get-define builddir] set srcdir [get-define srcdir] @@ -396,11 +420,11 @@ catch {exec hostname} host set user $::env(USER) define BUILDUSER "$user@$host" define BUILDTS [clock format [clock seconds] -format "%Y.%m.%d at %T %Z"] -# The mkos8 script requires Python and some non-core modules. +# The os8-run script requires Python and some non-core modules. set status [catch {exec python -c exit} result] if {$status != 0} { user-error "Python does not appear to be installed here. It is required." } msg-result "Python is installed here." @@ -424,10 +448,30 @@ append msg "\nOR:\n" append msg "\n sudo apt install python-pkg-resources\n" user-error $msg } msg-result "Python module pkg_resources is installed here." + +# Check for Perl and that it can run the test corpus builder. Not fatal +# if it can't, since only developers and deep testers need it. +set status [catch {exec perl -e exit} result] +if {$status == 0} { + set status [catch {exec perl -c "[get-define srcdir]/tools/test-os8-run" 2> /dev/null} result] + if {$status == 0} { + set status [catch {exec lz4 -h 2> /dev/null} result] + if {$status == 0} { + msg-result "All test-os8-run prereqs available here." + define CAN_TEST_OS8_RUN 1 + } else { + msg-result "Cannot test-os8-run without lz4; try apt install liblz4-tool." + } + } else { + msg-result "Cannot test-os8-run: fix with 'sudo cpanm --installdeps .'" + } +} else { + msg-result "Cannot test-os8-run: Perl is not available." +} # Build Deeper Thought if we find it here if {[file exists "[get-define srcdir]/src/misc/deeper.c"]} { set ls [string toupper "[get-define LED_DRIVER_MODULE]ls"] msg-result "Found Deeper Thought; building it against $ls GPIO module" @@ -466,30 +510,33 @@ make-config-header src/config.h \ -auto {ENABLE_* HAVE_* PACKAGE_* SIZEOF_*} \ -bare {ILS_MODE PCB_*} make-template Makefile.in make-template bin/pidp8i.in +make-template bin/os8-cp.in make-template boot/0.script.in make-template boot/2.script.in make-template boot/3.script.in make-template boot/4.script.in make-template boot/6.script.in make-template boot/7.script.in make-template boot/run.script.in +make-template boot/run-v3f.script.in make-template boot/tss8.script.in make-template etc/pidp8i.service.in make-template etc/sudoers.in make-template etc/usb-mount@.service.in make-template examples/Makefile.in make-template lib/pidp8i/__init__.py.in make-template lib/pidp8i/dirs.py.in make-template lib/pidp8i/ips.py.in make-template media/os8/init.tx.in +make-template media/os8/3finit.tx.in make-template src/Makefile.in make-template src/cc8/Makefile.in make-template src/cc8/os8/Makefile.in make-template src/pidp8i/gpio-common.c.in make-template src/pidp8i/main.c.in make-template src/SIMH/Makefile.in make-template src/SIMH/PDP8/Makefile.in make-template tools/simh-update.in -exec chmod +x "$builddir/bin/pidp8i" "$builddir/tools/simh-update" +exec chmod +x "$builddir/bin/pidp8i" "$builddir/tools/simh-update" "$builddir/bin/os8-cp" ADDED bin/os8-cp.in Index: bin/os8-cp.in ================================================================== --- /dev/null +++ bin/os8-cp.in @@ -0,0 +1,764 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +######################################################################## +# Generalized facility to manipulate os8 device images from the POSIX +# (host) side using OS/8 system programs under SIMH. +# +# See USAGE message below for details. +# +# Copyright © 2018 by Bill Cattey and Warren Young +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the names of the authors above +# shall not be used in advertising or otherwise to promote the sale, +# use or other dealings in this Software without prior written +# authorization from those authors. +######################################################################## + +# Bring in just the basics so we can bring in our local modules +import os +import sys + +sys.path.insert (0, os.path.dirname (__file__) + '/../lib') +sys.path.insert (0, os.getcwd () + '/lib') + +# Our local modules +from pidp8i import * +from simh import * + +# Other global Python modules +import glob +import subprocess +import string +import re + + +#### GLOBALS AND CONSTANTS ############################################# + +progmsg = True +DEBUG = False +VERBOSE = False +QUIET = False + +# Default RK05 system image to attach if no sys specified. +_default_sys_image = "@OS8_BOOT_DISK@" +_default_sys_path = dirs.os8mo + _default_sys_image +_default_att_spec = ["rk", "0", _default_sys_path] + +# Regex for parsing an argument string into a sys device +_dev_arg_regex_str = "-(rk|td|dt|rx)(\d?)(s)?" +_dev_arg_re = re.compile(_dev_arg_regex_str) + +# Regex for parsing an action file att string into a sys device. Note +# reuse of regex string from above. +_dev_actfile_regex_str = _dev_arg_regex_str[1:] # strip - +_dev_actfile_re = re.compile (_dev_actfile_regex_str) + +# Map of SIMH device names to OS/8 device name prefixes. +_os8_from_simh_dev = {"rk" : "RK", "td" : "DTA", "dt" : "DTA", "rx" : "RX"} + +_os8_partitions = {"RK": ["A", "B"]} + +# OS/8 file name matching regex +_os8_file_re = re.compile("(\S+):(\S+)?") + +# PIP option flags we support and pass thru. PIP has other options we +# do not try to map to our program functionality. The key bit is the +# _pip_option_info dict. The various transforms below it prevent both +# redundant code up here and repeated recomputation below. +_pip_option_info = { + 'a': 'ASCII', + 'b': 'binary', + 'i': 'image', + 'y': 'yank system head', + 'z': 'zero device' +} +_valid_pip_options = sorted(_pip_option_info.keys()) +_arg_to_option = dict(zip( + [ '-' + opt for opt in _valid_pip_options ], + _valid_pip_options +)) +_option_to_pip = dict(zip( + _valid_pip_options, + [ '/' + opt.upper() for opt in _valid_pip_options ] +)) + +# Matches if the string begins with a dollar sign, and has at least +# one slash, returning the string between the dollar sign and the +# first slash in group 1 and the rest in group 2. +# No whitespace in the string. +_expandable_re = re.compile ("^\$([^/\s]+)/(\S*)$") + + +#### abort_prog ######################################################## +# Print err_string and exit with -1 return status. + +def abort_prog (err_str): + print "Abort: " + err_str + sys.exit(-1) + + +#### parse_attach ###################################################### +# Parser for OS/8 attach spec. + +def parse_attach (action_plan, match, imagename): + if match.group(2) == None or match.group(2) == "": + abort_prog ("Need unit number for: " + match.group(1) + ".") + + image_spec = [match.group(1), match.group(2), imagename] + if match.group(3) == 's': + if action_plan ["sys"] != None: + print ("Already specified system device. Ignoring sys mount of: " + imagename) + else: + action_plan["sys"] = image_spec + else: + action_plan["mount"].append(image_spec) + + +#### path_expand ####################################################### +# Originally developed in os8script.py, but useful here too. +# +# Simple minded variable substitution in a path. +# A path beginning with a dollar sign parses the characters between +# the dollar sign and the first slash seen becomes a name to +# expand with a couple local names: $home and the anchor directories +# defined in lib/pidp8i/dirs.py. +# We abort if the path expansion fails. + +def path_expand (path): + m = re.match(_expandable_re, path) + if m == None: return path + var = m.group(1) + + val = getattr (dirs, var, None) + if val != None: + return os.path.join(val,m.group(2)) + else: + abort_prog ("{$" + var + "} is not a valid path expansion in " + path) + + + +#### parse_action_file ################################################# +# +# This option allows creation of the action_plan from a file +# instead of by indivitual arguments. Think of it as a batch mode. +# +# Figuring out how to mix command lines args and the action_file +# would be tricky since the args are globbed, analyzed and then +# turned into an action plan. +# +# For now, the action file REPLACES the action plan of any other +# command line arguments before --action-file, and no other +# command line arguments are bothered with. +# +# The action file is 3 columns separated by one or more whitepace chars. +# +# If the first column is "att" it's an attach command +# and is parsed the same way as the attach args are parsed +# (Except we don't have a dash.) +# +# Otherwise the 3 columns are: +# +# option source destination +# +# option is one of the valid OS/8 file options: +# a, b, i, y, z +# +# Example: +# +# att rk0sbootdisk.rk05 +# att td0 boing.tu56 +# a pidp8i.in DTA0: +# b ac-mq-blinker.pal.pt DTA0: +# +# A dollar sign appearing in a POSIX pathname is expanded with substitutions +# from dirs.py. + +def parse_action_file(fname): + try: + manifest = open(fname, "r") + except IOError: + print fname + " not found. Skipping." + return None + + ioline_re = re.compile("(\S+)\s+(\S+)\s+(\S+)") + + action_plan = {} + action_plan["sys"] = None + action_plan["mount"] = [] + action_plan["copy"] = [] + + for line in manifest: + ioline = line.strip() + if DEBUG: print "parse_action_file: ioline: " + ioline + if ioline == "": continue + if ioline[0] == '#': continue # Allow comments + + m=re.match(ioline_re, ioline) + if m== None: + print "Ignoring line: " + ioline + continue + + option = m.group(1) + source = m.group(2) + destination = m.group(3) + + if option == "att": + m = re.match(_dev_actfile_re, source) + if m== None: + abort_prog ("Could not parse attach spec: " + source) + parse_attach (action_plan, m, path_expand(destination)) + else: + if len(option) != 1: + print "Format options are only 1 letter in size. Ignoring line: " + ioline + elif option[0] not in _valid_pip_options: + print "Unrecognize option in line: " + ioline + elif source == None: + print "Null value of source. Ignoring line: " + ioline + elif destination == None: + print "Null value of destination. Ignoring line: " + ioline + else: + append_copy(action_plan, option, source, destination) + + return action_plan + + +#### is_directory ###################################################### +# +# Returns True if the passed path looks like a directory. +# +# This is used at a level where we may still be unsure whether the given +# path refers to something on the OS/8 or the host side, so we first +# determine whether the path looks like an OS/8 or host-side path. If +# it looks like an OS/8 path, we consider it a "directory" if it names a +# device name, lacking a file name part after it. Otherwise, we use the +# local OS's "is a directory" path check. + +def is_directory(path): + if DEBUG: "is_directory (" + path + ")" + m = re.match(_os8_file_re, path) + if m != None: + if DEBUG: print "OS/8 Match: DEV: " + m.group(1) + ", File: " + str(m.group(2)) + if m.group(2) == None or m.group(2) == "": + return True # Just a device so yes it's a directory. + else: return False + if has_os8_wildcards(path): + if DEBUG: print "Has wildcards." + return False + return os.path.isdir(path) + + +#### has_os8_wildcards ################################################# +# Returns True if the passed file name has OS/8 style wildcards. + +def has_os8_wildcards (filename): + os8_wild_cards = "*?" + for char in os8_wild_cards: + if char in filename: return True + return False + + +#### file_list_from_expect ############################################# +# Given a block of text output by the Python Expect module used by class +# simh, parse it with the assumption that it contains OS/8 DIR output. + +def file_list_from_expect(before): + file_list = [] + lines = before.split("\r") + for line in lines[1:]: # First line is our command. Skip it. + line = line.strip() + if line == "": continue + m = re.match("(\S+)\s*\.(\S+)", line) + if m == None: continue + # if DEBUG: print "file_list_from_expect: group 1: " + m.group(1) + ", group 2: " + m.group(2) + fname = m.group(1) + "." + m.group(2) + file_list.append(fname) + return file_list + + +#### append_copy ####################################################### +# Append a copy control array to the action_plan + +def append_copy(action_plan, mode, source, destination): + copy_type = "" + if ":" in source: + copy_type = "from" + if "/" in source: + abort_prog ("append_copy, out: Illegal OS/8 file spec containing a slash:" + source) + source = source.upper() + else: source = path_expand(source) + + if ":" in destination: + if copy_type == "from": + copy_type = "within" + else: + copy_type = "into" + if "/" in destination: + print "append_copy, into: Illegal OS/8 file spec containing a slash:" + destination + sys.exit(-1) + destination = destination.upper() + + if copy_type == "": + abort_prog ("append_copy: No OS/8 file spec found with source: " + source + ", destination: " + destination) + else: destination = path_expand(destination) + + copyspec = [mode, source, destination, copy_type] + action_plan["copy"].append (copyspec) + + +#### parse_args ######################################################## +# +# Builds the action plan from the command line arguments and executes it +# +# Note that if we specify the --action-file, the +# contents of that file REPLACE any command line arguments. +# +# An element of the action_plan["copy"] array is itself an array +# that names a file format, a source, a destination, and operation type. +# +# [, , , ] +# +# type is one of: +# "from" if we are transferring *from* OS/8 to POSIX +# "to" if we are transferring *to* OS/8 to POSIX +# "on" if we are transferring files within -- *on* OS/8 +# +# The source and destination file specifications are interpreted as in +# the USAGE message below. (Look for "colon".) + +TERSE_USAGE = 'usage: ' + os.path.basename (__file__) + \ + " [-dhvq] [-[s] image] ... [[-abiyz] ] ... \n" + \ + " " + os.path.basename (__file__) + " [-dhvq] [--action-file action-file]" + \ +""" + + is one of rk, td, dt, rx, corresponding to SIMH PDP-8 devices + must be a valid unit number for the device in both SIMH and + the booted OS/8 system. A unit number is required. Following + the unit with an "s" names the system device to boot. The + system image file must exist, and contain a working boot + image. Only one designated system device is allowed. + is a source file or wild card specification. + is a destination file if a single src file is specified; if + multiple files are given, it is either a POSIX + directory or an OS/8 device name. + + Specifying an action file overrides any device or file argument + previously specified on the command line. + + Example: + """ + \ + os.path.basename (__file__) + " -rk0s os8v3d-patched.rk05 -dt0 scratch.tu56 -a prog.pa DTA0:PROG.PA " + +USAGE = TERSE_USAGE + """ + To see more detailed documentation specify -v -h +""" + +VERBOSE_USAGE = TERSE_USAGE + """ + + This program boots an OS/8 environment in the SIMH PDP-8 simulator + then tries to behave like the POSIX program it is named after -- + copying files between the POSIX (host) side and the OS/8 + environment or under our control within the OS/8 environment. + + The copying direction is determined by which file name arguments + have a colon in them: + + * copy-within: The source and destination file arguments have + colons, so copy within the OS/8 environment from one volume to + another. + + * copy-into: Only the dest argument has a colon, so assume the + source file names are POSIX-side and copy those files into the + simulated OS/8 environment. + + * copy-from: The dest argument has no colon but the source file + names do, so copy the named OS/8 files out from the simulation. + + If none of the file arguments has a colon in it and you give exactly + two such arguments, we operate in a special case of copy-within + mode: the source and destination volumes are assumed to be DSK:, so + the file is simply copied within the OS/8 DSK: volume from one name + to the other. If you give greater than two file name arguments + without a colon in any of them, it is not possible to make sense of + the command since we do not intend to try and replace your perfectly + good POSIX cp implementation, so it errors out. + + If you give only one file name argument, the program always errors + out: it requires at least one source and one destination. + + The -a, -b, -i, -y, and -z flags correspond to the OS/8 PIP options: + + /A ASCII format. OS/8 and POSIX newlines are translated. + Such transfers are lossless if line endings are well-formed. + /B Binary OS/8 ABSLDR format with leader/trailer and other + specific formatting that is detected and enforced by PIP. + /I Image mode. Files are copied byte for byte verbatim. + /Z ZERO directory of destination OS/8 device. + /Y Yank system area from source to destination. + + If no format flag is set, the default transfer format is /I. + + (This priogram currently uses PIP as its primary handler for the + OS/8 side of the work.) They must be followed by at least one source + file name, and they affect all subsequent source file names until + another such option is found. For example: + + $ os8-cp -a foo bar -b qux sys: + + Files foo and bar are copied to SYS: in ASCII mode, overriding the + default binary mode, then binary mode is restored for the copy of + file qux to the SYS: volume. + + Beware that -i means something very different to this program than + it means to POSIX cp: destination files will be unceremoniously + overwritten! + + More about image file mounts: + + image files for non-boot dev specifications, if they do not + already exist, are created. Their directories are initalized + with the ZERO command. Multi-partition devices initialize all. + + A future version will include a default system device if + no dev system mount is made. + + Examples: + + $ os8-cp -td0s my.tu56 -rk0 my.rk05 foo DSK: + + ...will boot from my.tu56, which is presumed to be a bootable OS/8 + DECtape attached to SIMH device TD0. The RK05 disk image my.rk05 + will be attached to RK0, since the default boot disk is not attached + there in this example. It will copy POSIX-side file foo to DSK:FOO + which will probably be interpreted as DTA0:FOO by the typical BUILD + options for a bootable OS/8 TU56 DECtape. Beware therefore of using + the generic SYS: and DSK: device names! You would be better advised + to use DTA0:, RKA0: or RKB0: as the destination in this example. + + $ os8-cp -td0s my.tu56 -rx1 my.rx01 foo RXA1: + + This fixes the almost-certainly incorrect use of DSK: in the prior + example. + + The -dt and -td options are handled similarly to the -r* options, + differing only in whether we use the SIMH DT or TD PDP-8 devices, + which correspond to the TC08 or TD8E DECtape controllers. Which one + you give depends on the device support built into the OS/8 media + you've booted from. + + More about src and dest specifications: + + When only a destination device, directory, or volume name is given, + file names are normalized when coping between POSIX and OS/8 + systems. File names are uppercased and truncated to 6.2 limits when + copying into OS/8. File names are lowercased on copying from OS/8 + unless you give the *source* file name in all-uppercase. + Then file name case is preserved. This behavior is overridden + if you give a complete file name for the destination: + + $ os8-cp my-long-file-name.txt DSK:MLF.FD + + If you gave "DSK:" as the destination instead, you would have gotten + "MY-LON.TX" as the desintation file name instead. + + Give -d to run in debug mode. + + Give -h -v to print this message. + + Give -v to enable verbose status reporting. +""" + +def parse_args (): + global DEBUG + global VERBOSE + global QUIET + + action_plan = {} + action_plan["sys"] = None + action_plan["mount"] = [] + action_plan["copy"] = [] + + idx = 1 + numargs = len(sys.argv) + + filespec_seen = 0 + mode_opt = "i" # start of with default of binary. + first_mode = mode_opt + source = "" + destination = "" + # Keep file_list and mode_list in sync. + file_and_mode_list = [] + + while idx < numargs: + arg = sys.argv[idx] + + # First the simple bit set options + if arg == "-d": + DEBUG = True + elif arg == "-h": + if VERBOSE: + print VERBOSE_USAGE + else: + print USAGE + sys.exit(0) + elif arg == "-q": + QUIET = True + elif arg == "-v": + VERBOSE = 1 + + # look for option args. + elif arg in _arg_to_option: + new_opt = _arg_to_option[arg] + if mode_opt == new_opt: + print "Warning redundant reset of mode option to " + \ + _pip_option_info[new_opt] + mode_opt = new_opt + + # Not a simple bit set option. + + elif arg == "--action-file": + if idx + 1 == numargs: # Need filename, but no args left. + abort_prog ("No action file name.") + argfilename = sys.argv[idx + 1] + retval = parse_action_file(argfilename) + if retval == None: + abort_prog ("No action plan could be made from " + argfilename + ".") + else: return retval + + else: + # Parser for OS/8 attach spec. + m = re.match(_dev_arg_re, arg) + if m != None: + if idx + 1 == numargs: # Need filename, but no args left. + abort_prog ("No image file name.") + idx +=1 + parse_attach (action_plan, m, sys.argv[idx]) + + # Do file parser if we didn't get an OS/8 attach spec. + else: + + if DEBUG: print "File parsing of: " + arg + + # Need to know if arg is Linux. If so, we need to do globbing. + # If you want OS/8 globbing, specify a device to prevent globbing + # from being run. + m = re.match(_os8_file_re, arg) + if m == None: # Yup, it's POSIX. Glob it. + if DEBUG: print arg + " is POSIX." + more_files = glob.glob(arg) + if more_files == []: + if DEBUG: print "No more files in POSIX Glob. Our file is: " + arg + more_files.append(arg) # If file not found may be an OS/8 internal xfer. + for new_file in more_files: + if filespec_seen == 0: + source = new_file + first_mode = mode_opt + if DEBUG: print "Globber: Setting initial source: " + source + " and mode: " + first_mode + elif filespec_seen == 1: + if DEBUG: print "Globber: Setting initial destination: " + destination + destination = new_file + else: + if DEBUG: "Globber: Appending destination to list. New file is: " + new_file + file_and_mode_list.append([mode_opt,destination]) + destination = new_file + filespec_seen += 1 + else: + if filespec_seen == 0: + source = arg + first_mode = mode_opt + if DEBUG: print "Setting initial source: " + source + " and mode: " + first_mode + elif filespec_seen == 1: + destination = arg + if DEBUG: print "Setting initial destination: " + destination + else: + file_and_mode_list.append([mode_opt, destination]) + destination = arg + if DEBUG: "Appending destination to list. New file is: " + new_file + filespec_seen += 1 + + idx +=1 # Bottom of the while loop. Increment. + + if filespec_seen == 0: + abort_prog ("No file specs seen. Nothing to do.") + elif filespec_seen == 1: + abort_prog ("Only 1 file spec found. Nothing to do.") + # Now it gets a little complicated... + # If neither source nor destination is OS/8, pretend they both were OS/8 "DSK:" + # If source is OS/8, and has OS/8 wild cards, the destination must be a directory. + else: + # If more than 2 files, the destination must be either an OS/8 device or a Linux directory. + if DEBUG: print "Destination: " + destination + if filespec_seen > 2 and is_directory(destination) == False: + abort_prog ("Destination must be a Linux directory or OS/8 device for multiple source files.") + + m1 = re.match(_os8_file_re, source) + m2 = re.match(_os8_file_re, destination) + + # If source is OS/8 and it has wild cards, but destination is a file, not a device, + # it's a fatal error. + if m1 != None and has_os8_wildcards(source) and is_directory (destination) == False: + abort_prog ("Not going to concatinate multiple OS/8 files into: " + destination + ".") + + if m1 == None and m2 == None and filespec_seen == 2: # No OS/8 dev on two names, local DSK: copy. + source = "DSK:" + source + destination = "DSK:" + destination + + append_copy(action_plan, first_mode, source, destination) + + for mode_and_file in file_and_mode_list: + filename = mode_and_file[1] + m3 = re.match(_os8_file_re, filename) + if m3 != None and has_os8_wildcards(filename) and is_directory (destination) == False: + abort_prog ("Not going to concatinate multiple OS/8 files into: " + destination + ".") + append_copy(action_plan, mode_and_file[0], filename, destination) + + return action_plan + + +#### main ############################################################## + +def main (): + + action_plan = parse_args() + if action_plan == None: + abort_prog ("No action plan was parsed.") + if DEBUG: print str(action_plan) + + # Create the SIMH child instance and tell it where to send log output + try: + s = simh (dirs.build, True) + except (RuntimeError) as e: + print "Could not start simulator: " + e.message + '!' + exit (1) + # s.set_logfile (os.fdopen (sys.stdout.fileno (), 'w', 0)) + s.set_logfile (open ("logfile.txt", 'w')) + if VERBOSE: s.verbose = True + + # Perform sys attach + att_spec = action_plan["sys"] + if att_spec == None: att_spec = _default_att_spec + simh_boot_dev = att_spec[0] + att_spec[1] # Compose simh dev from name and unit. + imagename = att_spec[2] + if not os.path.exists (imagename): + abort_prog ("Requested boot image file: " + imagename + " not found.") + if VERBOSE or DEBUG: + print "Attaching " + simh_boot_dev + " to " + imagename + s.send_cmd ("att " + simh_boot_dev + " " + imagename) + + images_to_zero = [] + + # Attach other mounts + for att_spec in action_plan["mount"]: + simh_dev = att_spec[0] + att_spec[1] # Compose simh dev from name and unit. + imagename = att_spec[2] + if os.path.exists (imagename): + if VERBOSE or DEBUG: print "Modifying existing " + simh_dev + " image " + imagename + else: + if VERBOSE or DEBUG: print "Will create a new image file named: " + imagename + # Save this att_spec so we can zero it later. + images_to_zero.append (att_spec) + if VERBOSE or DEBUG: + print "Attaching " + simh_dev + " to " + imagename + s.send_cmd ("att " + simh_dev + " " + imagename) + + if VERBOSE or DEBUG: print "Booting " + simh_boot_dev + "..." + s.send_cmd ("boot " + simh_boot_dev) + + for att_spec in images_to_zero: + os8dev = _os8_from_simh_dev[att_spec[0]] + if os8dev in _os8_partitions: + for partition in _os8_partitions[os8dev]: + os8name = os8dev + partition + att_spec[1] + ":" + if VERBOSE or DEBUG: print "Initializing directory of " + os8name + " in " + imagename + s.os8_send_cmd ('\\.', "ZERO " + os8name) + else: + os8name = os8dev + att_spec[1] + ":" + if VERBOSE or DEBUG: print "Initializing directory of " + os8name + " in " + imagename + s.os8_send_cmd ('\\.', "ZERO " + os8name) + + # Perform copy operations + for do_copy in action_plan["copy"]: + mode_opt = do_copy[0] + source = do_copy[1] + destination = do_copy[2] + copy_type = do_copy[3] + if mode_opt in _option_to_pip: + pip_option = _option_to_pip[mode_opt] + else: + abort_prog ("Unrecognized mode option: " + mode_opt) + + if DEBUG: print "Source: " + source + ", Destination: " + destination + ", Mode: " + mode_opt + "." + # Is this "from" OS/8 to POSIX, "into" OS/8 from POSIX or "within" OS/8? + # "into" -- Attach source to simh ptr + # If we are operating "from" and source has wild cards, + # Use DIRECT to create list of files. + # "from" -- Attach destination to ptp. We've already done POSIX globing. + # "within" -- Use COPY. + + if copy_type == "into": + s.os8_pip_to(source, destination, pip_option) + elif copy_type == "from": + if has_os8_wildcards(source): + # Split off device from source: + os8dev = source[0:source.index(":")+1] + if DEBUG: print "Wild card dev: " + os8dev + # Use OS/8 Direct to enumerate our input files. + if DEBUG: print "Calling OS/8 DIRECT on wild card filespec: " + source + s.os8_send_cmd ('\\.', "DIR " + source + "/F=1") + # Now harvest direct output. One file per line. Ignore blank lines. + # Maybe parse the FREE BLOCKS Output. + # Done when we see a dot. + s._child.expect("\d+\s+FREE BLOCKS") + files = file_list_from_expect(s._child.before) + for filename in files: + if VERBOSE or DEBUG: + print "Wildcard call os8_pip_from: copy from: {" + os8dev + "}{" + filename + "}" + \ + " to: " + destination + ", mode: " + pip_option + s.os8_pip_from(os8dev + filename, destination, pip_option) + else: + if VERBOSE or DEBUG: + print "Call os8_pip_from: copy from: " + source + " to " + destination + \ + ", mode: " + pip_option + s.os8_pip_from(source, destination, pip_option) + elif copy_type == "within": + if VERBOSE or DEBUG: + print "Call COPY of: " + source + " to " + destination + s.os8_send_cmd ('\\.', "COPY " + destination + "< " + source) + else: + abort_prog ("Unrecognized copy type: " + copy_type) # Should never happen. + + # Detach all mounts and then sys. + s.back_to_cmd ('\\.') + for att_spec in action_plan["mount"]: + simh_dev = att_spec[0] + att_spec[1] # Compose simh dev from name and unit. + if VERBOSE or DEBUG: + print "Detaching " + simh_dev + s.send_cmd ("det " + simh_dev) + if VERBOSE or DEBUG: + print "Detaching " + simh_boot_dev + s.send_cmd ("det " + simh_boot_dev) + + # And shut down the simulator. + if VERBOSE or DEBUG: + print "Quitting simh." + s.send_cmd ('quit') + +if __name__ == "__main__": main() ADDED bin/os8-run Index: bin/os8-run ================================================================== --- /dev/null +++ bin/os8-run @@ -0,0 +1,222 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +######################################################################## +# Script runner for OS/8 under SIMH. +# The library module os8script.py does the heavy lifting. +# +# See USAGE message below for details. +# +# Copyright © 2018 by Bill Cattey and Warren Young +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the names of the authors above +# shall not be used in advertising or otherwise to promote the sale, +# use or other dealings in this Software without prior written +# authorization from those authors. +######################################################################## + +# Bring in just the basics so we can bring in our local modules +import os +import sys + +sys.path.insert (0, os.path.dirname (__file__) + '/../lib') +sys.path.insert (0, os.getcwd () + '/lib') + +# Remaining Python core modules +import subprocess +import string +import re +import shutil +import argparse +from itertools import chain + +# Our local modules +from pidp8i import * +from simh import * +from os8script import * + + +#### GLOBALS AND CONSTANTS ############################################# + +DEBUG = False +VERBOSE = False +VERY_VERBOSE = False +QUIET = False +SCRIPT_FILE = "" + +USAGE = "usage: " + os.path.basename (__file__) + \ + " [-h] [-d] [-v] [-vv] [--enable enable_option] ... \n\t[--disable disable_option] ... " + \ + "\n \tscript [script] ... " + +_en_dis_arg_re = re.compile("^(enable|disable)_(\S+)$") + + +def opt_set(en_dis, option, options_enabled, options_disabled): + if option == None or en_dis == None: return + # print en_dis + " " + option + if en_dis == "enable": + if option not in options_enabled: + options_enabled.append(option) + if option in options_disabled: + options_disabled.remove(option) + elif en_dis == "disable": + if option not in options_disabled: + options_disabled.append(option) + if option in options_enabled: + options_enabled.remove(option) + else: return + + +def add_bool (self, *args, **kwargs): + kwargs['action'] = 'store_true' + kwargs['default'] = False + self.add_argument (*args, **kwargs) + + +#### parse_args ######################################################## + +def parse_args (script_files, options_enabled, options_disabled): + global DEBUG + global VERBOSE + global VERY_VERBOSE + global USAGE + + enable_usage = "" + disable_usage = "" + + idx = 1 + numargs = len(sys.argv) + + if numargs < 2: + print USAGE + sys.exit(-1) + + # Add arguments corresponding to --*-os8-* configure script options + max_obn_len = 0 + for obn, vals in os8opts.opts.iteritems(): + max_obn_len = max(max_obn_len, len(obn)) + for obn, vals in os8opts.opts.iteritems(): + if vals[0]: + # Enable option + pad_str = (max_obn_len - len (obn)) * " " + new_usage = " " + obn + ": " + pad_str + vals[1] + "\n" + disable_usage += new_usage + else: + # Disable option + pad_str = (max_obn_len - len (obn)) * " " + new_usage = " " + obn + ": " + pad_str + vals[1] + "\n" + enable_usage += new_usage + + if enable_usage != "": + USAGE += "\n Known enable options: \n" + USAGE += enable_usage + + if disable_usage != "": + USAGE += "\n Known disable options: \n" + USAGE += disable_usage + + while idx < numargs: + arg = sys.argv[idx] + # print "idx: " + str(idx) + ", arg: " + arg + # print "Files: " + str(script_files) + # print "Options: " + str(options_enabled) + if arg == "-d" or arg == "--debug": + DEBUG = True + elif arg == "-h" or arg == "--help": + print USAGE + sys.exit(0) + elif arg == "-v" or arg == "--verbose": + VERBOSE = 1 + elif arg == "-vv" or arg == "--very-verbose": + VERY_VERBOSE = 1 + elif arg == "--enable": + idx +=1 + if idx == numargs: + print "expecting an option but got none." + else: + option = sys.argv[idx] + # Only add the option once + if option not in options_enabled: options_enabled.append(option) + elif arg == "--disable": + idx +=1 + if idx == numargs: + print "expecting an option but got none." + else: + option = sys.argv[idx] + # Only add the option once + if option not in options_disabled: options_disabled.append(option) + else: + script_files.append(arg) + idx += 1 + + + +#### main ############################################################## +# Program entry point. Parses the command line and drives the above. + +def main (): + script_files = [] + options_enabled = [] + options_disabled = [] + + parse_args (script_files, options_enabled, options_disabled) + if len(script_files) == 0: + print "Need a script file to run." + sys.exit(-1) + + if VERBOSE: + print "script_files: " + str(script_files) + if DEBUG: + print "options_enabled" + str(options_enabled) + print "options_disabled" + str(options_disabled) + + # Log SIMH and OS/8 output to a file by default, but send it to the + # console instead of the progress messages if -v was given using the + # trick from https://stackoverflow.com/questions/21239338 + s = simh (dirs.build, True) + if VERBOSE: s.verbose = True + s.set_logfile (open (dirs.log + 'os8-run' + '.log', 'w') \ + if not VERY_VERBOSE else os.fdopen (sys.stdout.fileno (), 'w', 0)) + + os8 = os8script (s, options_enabled, options_disabled, verbose=VERBOSE, debug=DEBUG) + + for script_file in script_files: + if VERBOSE: + print os.path.basename (__file__) + " -- Language Version: " + os8.lang_version + + print "Running script file: " + script_file + os8.run_script_file (script_file) + + # After all scripts are done, we remove any scratch files, + # detach any mounted devices, and shut down simh gracefully. + + for filename in os8.scratch_list: + if os8.verbose: print "Deleting scratch_copy: " + filename + os.remove(filename) + + s.send_cmd ("detach all") + + s.quit () + if VERBOSE: print "Done!" + + +if __name__ == "__main__": + main() Index: bin/teco-pi-demo ================================================================== --- bin/teco-pi-demo +++ bin/teco-pi-demo @@ -124,12 +124,17 @@ # execution rate while unthrottled on this host hardware. If # you don't run it long enough, the IPS value is untrustworthy. try: s.spin (10) except pexpect.TIMEOUT: - # Find out how many IPS was executing + # Explicitly shift back from OS/8 context to SIMH command context. + # We cannot rely on class simh to do this automatically because it + # expects to see a . prompt from the prior command, but we're + # still in TECO here, so we must be explicit. s.os8_send_ctrl ('e') + + # Ask the simulator what IPS rate we ran that benchmark at. s.send_cmd ('show clocks') line = s.read_tail ('Execution Rate:') curr_ips = int (line.strip().replace(',', '').split(' ')[0]) pf = open ('lib/pidp8i/ips.py', 'a') pf.write ('current = ' + str (curr_ips) + ' # ' + \ @@ -141,15 +146,15 @@ print "\nYour system is " + format (rpi_ratio, '.1f') + \ " times faster than a Raspberry Pi Model B+" print "or " + format (pdp_ratio, '.1f') + \ " times faster than a PDP-8/I.\n" else: - # Normal mode. Pop out to SIMH and throttle it down to a rate - # suitable for a blinkenlights demo. 1/17 means SIMH runs one - # instruction then waits for 17ms, yielding ~59 IPS. - time.sleep (0.02) # FIXME: simulator chokes on 'cont' without this - s.os8_send_ctrl ('e') + # Normal mode. Tell SIMH and throttle down to a rate suitable for a + # blinkenlights demo. 1/17 means SIMH runs one instruction then + # waits for 17ms, yielding ~59 IPS. + time.sleep (0.02) # FIXME: simulator chokes on 'cont' without this + s.os8_send_ctrl ('e') # same justification as above s.send_cmd ('set throttle 1/17') # You can't hit Ctrl-E while running this script in the foreground # since pexpect takes over stdio. Therefore, if you want to be able # to send commands to the simulator while the demo is running, Index: boot/3.script.in ================================================================== --- boot/3.script.in +++ boot/3.script.in @@ -1,12 +1,30 @@ ; Loads OS/8 from DECtape, as opposed to the RK05 based environment ; in 0.script. ; -echo Loading OS/8 from DECtape... +echo Loading OS/8 from DECtape type @OS8_TAPE_DEVICE@... 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 + +@if SIMH_TAPE_DEVICE == td +set dt disabled +set td enabled +; else, default is correct, per p.4 in SIMH's pdp8_doc.doc. +@endif + +@if SIMH_PASS_LOWERCASE +; The software was configured with either --lowercase=auto or =pass, +; so send all text input to the simulator as 7-bit ASCII, including +; lowercase. Lowercase output from the simulator will be sent to the +; console unchanged, as will non-printing chars. +set tti 7b +@else +; The software was configured with --lowercase=upper, meaning the user +; wants lowercase text to be forced to uppercase. This is bidirectional, +; affecting both input to the simulated PDP-8 and output from it. +set tti ksr +@endif + +attach @SIMH_TAPE_DEVICE@0 @MEDIADIR@/os8/@OS8_BOOT_TAPE@ +boot @SIMH_TAPE_DEVICE@0 ADDED boot/run-v3f.script.in Index: boot/run-v3f.script.in ================================================================== --- /dev/null +++ boot/run-v3f.script.in @@ -0,0 +1,34 @@ +; Loads OS/8 V3F from DECtape, as opposed to the RK05 based environment +; in 0.script. Based on 3.script.in, which differs by being conditional +; based on whether the user chooses at configure time whether to boot +; OS/8 V3F or V3D. This script also runs the tape image from the build +; directory, rather than from the installation directory, since it's +; normally run as "make run-v3f". +; +echo Loading OS/8 from DECtape type @OS8_TAPE_DEVICE@... +set df disabled +set cpu noidle +@SET_THROTTLE@ +deposit int-throttle THROT_DELAY 3 + +@if SIMH_TAPE_DEVICE == td +set dt disabled +set td enabled +; else, default is correct, per p.4 in SIMH's pdp8_doc.doc. +@endif + +@if SIMH_PASS_LOWERCASE +; The software was configured with either --lowercase=auto or =pass, +; so send all text input to the simulator as 7-bit ASCII, including +; lowercase. Lowercase output from the simulator will be sent to the +; console unchanged, as will non-printing chars. +set tti 7b +@else +; The software was configured with --lowercase=upper, meaning the user +; wants lowercase text to be forced to uppercase. This is bidirectional, +; affecting both input to the simulated PDP-8 and output from it. +set tti ksr +@endif + +attach @SIMH_TAPE_DEVICE@0 bin/v3f-@OS8_TAPE_DEVICE@.tu56 +boot @SIMH_TAPE_DEVICE@0 ADDED cpanfile Index: cpanfile ================================================================== --- /dev/null +++ cpanfile @@ -0,0 +1,3 @@ +requires 'inc::Module::Install'; +requires 'Math::Subsets::List'; +requires 'Parallel::Loops'; ADDED doc/os8-cp.md Index: doc/os8-cp.md ================================================================== --- /dev/null +++ doc/os8-cp.md @@ -0,0 +1,219 @@ +# os8-cp: A tool to copy files to and from OS/8 running in SIMH + +## Motivation + +There have been various tools for copying files between OS/8 +device image files and the platform hosting those image files. +For example, the graphical [OS8View by Ian Schofield][os8view-forum]. + +This tool was designed to be called from the command line in the POSIX +environment, and to interface between the POSIX filesystem and OS/8 +running underi SIMH. + +The semantics are like the POSIX `cp` program, except that a source or +destination containing a colon is interpreted to refer to files within +OS/8. + +Additional arguments allow for specifying how the simulation is configured +and booted. I.E. various POSIX files containing OS/8 device images are +attached to SIMH. + +The reason why direct copy into and out of image files was not pursued +was that is seemed easier to use OS/8 and SIMH tools to copy files around +rather than learning the low level directory formats and binary data +representations for the various OS/8 image files. Anything that can +be moved around by `PIP` under OS/8 is fair game for `os8-cp`. + +The tool is written in Python and utilizes the pexpect library for +starting up and communication with SIMH. + +## Capabilities + +* Copy files from and to OS/8 booted up under SIMH. +* In ASCII text mode, convert between OS/8 and POSIX newline codings. +* Allow attaching of arbitrary image files to arbitrary devices. +* Copy files within OS/8 if both source and destination specify OS/8 rather than POSIX names. +* For POSIX file specifications expand directories to lists of files, and use POSIX +style globbing to expand wild cards. +* For OS/8 file specificaitons, allow appropriate wild carding as well. + +## Limitations + +The architecture of OS/8 makes it impossible to operate with a write-locked +boot device. (Many would argue that this is a serious defect, and I agree. +However this is the reality we are dealing with.) + +This means that there really should only be one instance of `os8-cp` +manipulating a particular bootable image at a time. + +When this tool was used in a Makefile, care had to be taken with dependencies +so that a parallel make would never run more than a single instance of +`os8-cp`. + +There are a couple ways that this could be dealt with: + +* Create a scratch image for the running boot image: The problem with this +is that often os8-cp is used in the manipulation of a boot image to be used +later by others. A scratch image would carefully throw away any such work. + +* Manage a lock file that would be checked at run-time: This would actually work. +Indeed a lock file with a name based on the path to the boot image would allow +multiple instances of os8-cp acting on different boot images to run simultaneously. +The downside is complexity. Lock files in Python are platform specific, so +Linux, Windows, BSD, etc platforms might require specific code. Cleanup of +the lock file on any kind of abnormal exit would have to be managed. And +design of what to do when waiting for the lock. + +Ultimately the implementation decision was: Be careful not to run +multiple instances os8-cp in parallel against the same boot image file. + +## Usage + +> `os8-cp` [`-dhvq`] [-<_dev_><_unit_>[`s`] _image_] ... [[`-abiyz`] <_src_>] ... <_dest_> +> `os8-cp` [`-dhvq`] [`--action-file` _action-file_] + +| <_dev_> | **Device**: one of `rk`, `td`, `dt`, `rx`, corresponding to SIMH PDP-8 devices +| <_unit_> | **Unit**: must be a valid unit number for the device in both SIMH and +| | the booted OS/8 system. A unit number is required. Following +| | the unit with an "s" names the system device to boot. The +| | system image file must exist, and contain a working boot +| | image. Only one designated system device is allowed. +| <_src_> | **Source**: is a source file or wild card specification. +| <_dest_> | **Destination**: is a destination file if a single _src_ file is specified; +| | if multiple <_src_> files are given, it is either a POSIX +| | directory or an OS/8 device name. + + Specifying an action file overrides any device or file argument + previously specified on the command line. + + Example: + + os8-cp -rk0s os8v3d-patched.rk05 -dt0 scratch.tu56 -a prog.pa DTA0:PROG.PA + +### Further Details + +The copying direction is determined by which file name arguments have a colon in them: + +* copy-within: The source and destination file arguments have +colons, so copy within the OS/8 environment from one volume to +another. + +* copy-into: Only the dest argument has a colon, so assume the +source file names are POSIX-side and copy those files into the +SIMH OS/8 environment. + +* copy-from: The dest argument has no colon but the source file +names do, so copy the named OS/8 files out from the simulation. + +If none of the file arguments has a colon in it and you give exactly +two such arguments, we operate in a special case of copy-within +mode: the source and destination volumes are assumed to be DSK:, so +the file is simply copied within the OS/8 DSK: volume from one name +to the other. If you give greater than two file name arguments +without a colon in any of them, it is not possible to make sense of +the command since we do not intend to try and replace your perfectly +good POSIX cp implementation, so it errors out. + +If you give only one file name argument, the program always errors +out: it requires at least one source and one destination. + +The `-a`, `-b`, `-i`, `-y`, and `-z` flags correspond to the OS/8 PIP options: + +| flag | `PIP` | Description +|------|-------|---------------------------------------------------------- +| `-a` | `/A` | ASCII format. OS/8 and POSIX newlines are translated. +| | | Such transfers are lossless if line endings are well-formed. +| `-b` | `/B` | Binary OS/8 ABSLDR format with leader/trailer and other +| | | specific formatting that is detected and enforced by PIP. +| `-i` | `/I` | Image mode. Files are copied byte for byte verbatim. +| `-z` | `/Z` | ZERO directory of destination OS/8 device. +| `-y` | `/Y` | Yank system area from source to destination. + +If no format flag is set, the default transfer format is `/I`. + +(This priogram currently uses PIP as its primary handler for the +OS/8 side of the work.) They must be followed by at least one source +file name, and they affect all subsequent source file names until +another such option is found. For example: + + $ os8-cp -a foo bar -b qux sys: + +Files foo and bar are copied to SYS: in ASCII mode, overriding the +default binary mode, then binary mode is restored for the copy of +file qux to the SYS: volume. + +Beware that -i means something very different to this program than +it means to POSIX cp: destination files will be unceremoniously +overwritten! + +More about image file mounts: + +image files for non-boot dev specifications, if they do not +already exist, are created. Their directories are initalized +with the ZERO command. Multi-partition devices initialize all. + +A future version will include a default system device if +no dev system mount is made. + +Examples: + + $ os8-cp -td0s my.tu56 -rk0 my.rk05 foo DSK: + +...will boot from my.tu56, which is presumed to be a bootable OS/8 +DECtape attached to SIMH device TD0. The RK05 disk image my.rk05 +will be attached to RK0, since the default boot disk is not attached +there in this example. It will copy POSIX-side file foo to DSK:FOO +which will probably be interpreted as DTA0:FOO by the typical BUILD +options for a bootable OS/8 TU56 DECtape. Beware therefore of using +the generic SYS: and DSK: device names! You would be better advised +to use DTA0:, RKA0: or RKB0: as the destination in this example. + + $ os8-cp -td0s my.tu56 -rx1 my.rx01 foo RXA1: + +This fixes the almost-certainly incorrect use of DSK: in the prior +example. + +The -dt and -td options are handled similarly to the -r* options, +differing only in whether we use the SIMH DT or TD PDP-8 devices, +which correspond to the TC08 or TD8E DECtape controllers. Which one +you give depends on the device support built into the OS/8 media +you've booted from. + +### More about _src_ and _dest_ specifications: + +When only a destination device, directory, or volume name is given, +file names are normalized when coping between POSIX and OS/8 +systems. File names are uppercased and truncated to 6.2 limits when +copying into OS/8. File names are lowercased on copying from OS/8 +unless you give the *source* file name in all-uppercase. +Then file name case is preserved. This behavior is overridden +if you give a complete file name for the destination: + + $ os8-cp my-long-file-name.txt DSK:MLF.FD + +If you gave "DSK:" as the destination instead, you would have gotten +"MY-LON.TX" as the desintation file name instead. + +### Run-time Options: + +| `-d` | run in debug mode. +| `-v` | enable verbose status reporting. +| `-h -v` | print detailed usage message. + + +[os8view-forum]: http://groups.google.com/forum/#!topic/pidp-8/1hojqAATum4 + +## TODOs + +* No TODOs as of yet. + +## Notes + +* No notes as of yet. + +### License + +Copyright © 2018 by Bill Cattey and Warren Young. 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: @@ -43,16 +43,13 @@ You may want to examine this file to see if there are any decisions you would reverse. After modifying that file, say "`make`" to rebuild the OS/8 binary RK05 disk image file with your choice of patches. -You can disable all of these OS/8 patches by giving the -`--disable-os8-patches` option to the `configure` script. - [dsn]: http://bitsavers.org/pdf/dec/pdp8/softwarenews/ [dsn8010]: http://bitsavers.org/pdf/dec/pdp8/softwarenews/198010_PDP8swNews_AA-K629A-BA.pdf -[pl]: https://tangentsoft.com/pidp8i/doc/trunk/media/os8/patches/patch_list.txt +[pl]: https://tangentsoft.com/pidp8i/doc/trunk/media/os8/patches/patch-list.txt ## Review of Recommendations `BRTS 31.11.2O` is an optional patch which disables 8th bit parity. It @@ -145,11 +142,11 @@ ## Patch Application Order The `patch` routine in `mkos8` applies the patches in the order they -appear in [`patch_list.txt`][pl]. That list is currently in +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. @@ -399,7 +396,7 @@ Copyright © 2017 by Bill Cattey. Licensed under the terms of [the SIMH license][sl]. [sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md -[os8ext]: https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-v3d-device-extenaions.md +[os8ext]: https://tangentsoft.com/pidp8i/doc/trunk/doc/os8-v3d-device-extensions.md ADDED doc/os8-run.md Index: doc/os8-run.md ================================================================== --- /dev/null +++ doc/os8-run.md @@ -0,0 +1,1040 @@ +# os8-run: A Scripting Language for Driving OS/8 + +## History and Motivation + +In the beginning, the PiDP-8/I project shipped a hand-made and +hand-maintained OS/8 disk image. This image had multiple problems, and +sometimes the by-hand fixes to those problems caused other problems. + +For the 2017.12.22 release, [we][auth] created a tool called `mkos8` +which creates this disk programmatically based on the user's configuration +choices. This has many virtues: + +* The OS/8 system media is created from pristine distribution media, + rather than being built up in an undocumented *ad hoc* fashion. + +* The build sequence is documented in the source code of `mkos8`, + rather than being a mysterious product of history, most of which + happened before the OS/8 media came under version control. + +* The build products can be [tested][tm], because the process is + purposefully done in a way that it is [reproducible][rb]. + +* Because `mkos8` is written in Python, we have a full-strength + scripting language for making the build conditional. As of the + 2017.12.22 release, there are potentially 65536 different build + configurations, whereas the old manual process produced just one + OS/8 system disk image. + +That process worked fine for the limited scope of problem it was meant to +cover: creation of an OS/8 V3D RK05 image meant for use with SIMH's PDP-8 +simulator, which was configured in a very particular way. It doesn't solve +a number of related problems that should be simple extensions to the idea: + +* What if we want a different version of OS/8, such as V3F? + +* When running under SIMH, there is little practical difference between + its `DT` and `TD` devices for driving an emulated TU56 tape drive: + the default is almost certainly fine, since it's compatible with + the whole range of PDP-8 computers, and thus software for PDP-8 + computers. But, what if you've got a real PDP-8/e computer with a + real TU56 tape drive, which means you're using the TD8E interface, not + the TC08 that SIMH's PDP-8 simulator defaults to using? That `BUILD` + output will not work on your hardware. + +* The same basic problem has additional complications when what's + changed in the `BUILD` is the system device type, such as from an + RK05 to an RL01 or RX02. + +* How do we make it drive other tools not already hard-coded into + `mkos8` or its underlying helper library? + +Shortly after release 2017.12.22 came out, Bill Cattey began work on +`os8-run` to solve these problems. This new tool implements a scripting +language and a lot of new underlying functionality so that we can not +only implement all of what `mkos8` did, we can now write scripts to do +much more. + +The goals of the project are: + +* Entirely replace `mkos8`, in that `os8-run` plus a suitable script + should be able to do everything that `mkos8` currently does. + +* Provide a suite of scripts and documentation support for creating + one's own scripts to solve problems we haven't even anticipated. + +* Make it flexible enough to build media images suitable for arbitrary + real PDP-8 hardware. + + +[auth]: https://tangentsoft.com/pidp8i/doc/trunk/AUTHORS.md +[rb]: https://reproducible-builds.org/ +[tm]: https://tangentsoft.com/pidp8i/doc/trunk/tools/test-os8-run + + +## Capabilities + +`os8-run` is a general script running facility that can: + +* attach device image files with options that include but also go beyond what SIMH offers: + * Protect image by attaching it read-only. + * Recognize the use case of working with a pre-existing image, and abort the script if the image is not found, rather than creating a new, blank image. + * Protect a master boot image that will not boot read-only by creating a scratch copy and booting the copy instead. + * Recognize the use case of creating a new, blank image, but preserving any pre-existing image files of the same name. +* boot OS/8 on an arbitrary attached device image. +* create a duplicate of an existing file. This is the use case of building new image files from an existing baseline while preserving the baseline image file. +* copy files from the running OS/8 environment into the POSIX environment +* copy files to the running OS/8 from the POSIX environment running SIMH. +running SIMH. +* run any OS/8 command as long as it returns immediately to the OS/8 Keyboard +Monitor. This includes BATCH scripts. +* run `ABSLDR` and `FOTP`, cycling an arbitrary number of times through the OS/8 +Command Decoder. +* run `PAL8` with either a 3 argument form that produces a listing file, +or a 2 argument form that does not. +* run `BUILD` with arbitrarily complex configuration scripts, including +the `BUILD` of a system head that inputs `OS8.BN` and `CD.BN`. +* configure the `tti`, `rx`, `td`, and `dt` devices at run time to allow +shifting between otherwise incompatible configurations of SIMH and OS/8 +device drivers. +* run included script files so that common code blocks can be written once +in an external included script. +* run of patch scripts that will use `ODT` or `FUTIL` to patch files in +the booted system image. +* perform actions in a script conditionally on feature enablement matching +an arbitrary keyword. +* perform actions in a script unless a disablement keyword has been specified. +* set enable or disable keywords anywhere in the execution of a script. + + +## Key Implementation Detail: Pexpect + +Under the covers, `run-os8` is a Python script that uses the [Python +`pexpect` library][pex] to interact programmatically with SIMH and OS/8. +In principle, there is no limit to the complexity of the dialogs we can +script with this. + +In practice, the major difficulty in constructing correct Pexpect +scripts is that if you fall out of step with what is "expect"ed, the +expect engine can get into a state where it is blocked waiting for +input that either never will arrive or that already passed by and now +can no longer be matched. To avoid blocking forever in such situations, +`os8-run` configures Pexpect to time out eventually, resulting in a big +ugly Python backtrace. The `os8-run` scripts that ship with the +PiDP-8/I software distribution should never do this, but as you write +your own, you may find yourself having to debug such problems. + +Running `os8-run` with the `-v` option gives verbose output that +enables you to watch every step of the script as it runs. + +[pex]: https://pexpect.readthedocs.io/ + + +## Conventions + +In the documentation below, we use the term "POSIX" to refer to the +host side of the conversation or to resources available on that side. +We use this generic term because the PiDP-8/I software runs on [many +different platforms][osc], currently limited only to those that are either +POSIX-compliant (e.g. macOS) or those sufficiently close (e.g. Linux). +Thus, a "file from POSIX" refers to a file being copied from the host +system running `os8-run` and SIMH's PDP-8 simulator into the simulated +environment. + +Very few script language commands fail fatally. The design principle +was to ask, "Is the primary use case of this command a prerequisite +for other work that would make no sense if this command failed?" If +the answer is, "yes", then the failure of command kills the execution +of the whole script and aborts `os8-run`. Commands that have fatal +exits are mentioned specifically in the [command reference +section](#scripting) below. + +### Case Sensitivity -- a tricky issue + +It is expected that scripts will be written on the POSIX platform with +a case-sensitive text editor. `os8-run` should be considered +case-sensitive as well. Scripts should specify the `os8-run` commands +and options in lower case, and the OS/8 commands, options, and +filenames in upper case. + +POSIX is ostensibly a case-sensitive platform, filenames, commands +and command arguments are always case sensitive. This was a basic +design decision from the earliest days of ancestral Multics. + +The OS/8 platform began as an upper-case only environment. Only late in +the evolution of the PDP-8 as a word processing platform, did lower +case even exist on OS/8. SIMH addresses this reality with two different +console device setups, `KSR` and `7b`. + +In `KSR` mode, typed lower case characters are upcased automatically before +being sent to the running system. + +In `7b` mode, all characters are passed to OS/8 without case conversion. + +The current OS/8 default OS/8 system image run with this software distribution +is called `os8v3d-patched.rk05`. It is configured to be as modern as possible. +It contains patches to force lower case characters typed to the Keyboard Monitor +to upper case so they will be understood. A patch is made to OS/8 +BASIC to do the same thing. However many programs available for use under +OS/8 are upper case only, and get confused unless you set `CAPS LOCK` on +your keyboard. + +All of the example scripts specify OS/8 commands in upper case. Such +commands could have been specified in lower case, and would work just +fine if run in the default `os8v3d-patched.rk05` system image. +However, since a basic use case of `os8-run` is to be able to run +scripts against arbitrary system images (which probably will not have +patches to deal with lower case), use of lower case for OS/8 commands, +arguments, or filenames is discouraged. + +`os8-run` does not get involved with forcing OS/8 commands or filenames +to upper case if they appear as lower case in scripts. `os8-run` does +offer commands to toggle the SIMH console support between `KSR` and `7b`. +See the [`configure`](#configure-comm) command. + +Although the `os8-run` commands and options could have been made case +insensitive, and the OS/8 commands, options, and filenames could have +been forced to upper case, rendering them case insensitive, there would +still be that aspect where the script developer would have to deal with +establishing case-sensitive POSIX filename conventions that would fit +with OS/8's upper case only filenames. The decision was made to have +the scripting language require, mindfulness of case, where the developer +adopts a discipline to use lower case for scripting commands, and upper +case when dealing with OS/8. + +Apologies in advance for the inconvenience of having to do that. Time +will tell if it was or was not the right decision to have been made. + +[osc]: https://tangentsoft.com/pidp8i/wiki?name=OS+Compatibility + + +## An Illustrative First Example + +Here are some example `os8-run` scripts: + +Example 1: Begin work on a new RK05 image that gets an updated version +of the OS/8 `BUILD` utility from POSIX source. (Perhaps it was found +on the net.) + + mount rk0 $bin/os8v3d-patched.rk05 required + mount rk1 $bin/os8-v3f-build.rk05 preserve + + cpto $src/os8/v3f/BUILD.PA RKA1:BUILD.PA + + boot rk0 + + pal8 RKB1:BUILD.BNPOSIX Path Expansions + +Notice in the [above example](#first) the use of the variables `$bin/` +and `$src/` in the POSIX path specifications. These refer to particular +directories which vary depending on whether you run this script from +the PiDP-8/I source tree or from the installation tree. Not only does +using these variables allow the same script to work in both trees, it +allows your script to run regardless of where those source and +installation trees are on any given system. + +The scheme works much like predefined POSIX shell variables, but the +underlying mechanism is currently very limited. First, the substitution +can only occur at the very beginning of a POSIX file specification. +Second, the only values currently defined are: + +| $build/ | The absolute path to the root of the build. +| $src/ | The absolute path to the root of the source. +| $bin/ | The directory where executables and runable image files are installed at build time +| $media/ | The absolute path to OS/8 media files +| $os8mi/ | The absolute path to OS/8 media files used as input at build time +| $os8mo/ | The absolute path to OS/8 media files produced as output at build time + +To add new values, modify `.../lib/pidp8i/dirs.py.in` and rebuild the +PiDP-8/I software. Beware that changing this generates the `dirs.py` +file, which is a very deep dependency. Touching this file will cause +all the OS/8 bootable system image files to be rebuilt, which can take +quite some time even on a fast host computer. + + +## Execution Contexts + +It is important to be mindful of the different command contexts when +running scripts under `os8-run`: + +* __SIMH context:__ Commands are interpreted by SIMH command processor. +* __OS/8 context:__ Commands are interpreted by the OS/8 Keyboard Monitor. +* __`begin` / `end` blocks:__ These create special interpreter loops with their +own rules. + +Examples of `begin` / `end` blocks: + +* __Command Decoder:__ Programs like `ABSLDR` and `FOTP` call the OS/8 Command Decoder +to get file specifications and operate on them. `os8-run` uses a `begin` / `end` block to +define set of files to feed to the Command Decoder and to indicate the last file, and +a return to the OS/8 context. +* __OS/8 `BUILD`:__ Commands are passed to `BUILD` and output is interpreted. The `end` +of the block signifies the end of the `BUILD` program and a return to the OS/8 context. +* __Conditional Execution:__ Blocks of script code, delimited by a `begin` / `block` can be +either executed or ignored depending on the key word that is enabled when that block +is encountered. This context is very interesting and is more fully documented below. + +The commands that execute in the OS/8 environment require a system image +to be attached and booted. Attempts to run OS/8 commands without having +booted OS/8 kill the script. + +Scripting commands such as `mount`, `umount`, and `configure` execute +in the SIMH context. OS/8 is suspended for these commands. + +Ideally we would just resume OS/8 with a SIMH continue command when we are +finished running SIMH commands. Unfortunately this does not work under Python +expect. The expect engine needs a command prompt. + +Although hitting the erase character (`RUBOUT`) or the line kill character +(`CTRL/U`) to a terminal-connected SIMH OS/8 session gives a command prompt, +these actions don't work under Python expect. We don't know why. + +Booting OS/8 gives a fresh prompt. + +Restarting the OS/8 Monitor with a SIMH command line of \"`go 7600`\" +works. + +The least disruptive way we have found to resume OS/8 under Python expect +after having escaped to SIMH is to issue the SIMH `continue` command, then +pause for an keyboard delay, then send `CTRL/C` then pause again, then send +`\r\n`. That wakes OS/8 back up and produces a Keyboard Monitor prompt. + +The simh.py code that underlies all this keeps track of the switch +between the SIMH and OS/8 contexts. However it does not presume to +do this resumption because the `CTRL/C` will quit out of any program +being run under OS/8, and return to the keyboard monitor level. + +Because `os8-run` creates the `begin` / `end` blocks with their own +interpreter loops, around commands with complex command structures, +it guarantees that the switch into SIMH context will only happen +when OS/8 is quiescent in the Keyboard Monitor. + +Although `os8-run` provides a `resume` command that can appear in +scripts after the commands that escape out to SIMH, using it is optional. +`os8-run` checks the context and issues its own resume call if needed. + + +## Usage + +> `os8-run` [`-h`] [`-d`] [`-v`] [`-vv`] [`--enable` _enable_option_] ... [`--disable` _disable_option_] ... _script-file_ ... + +| | **Positional Arguments** +| _script-file_ | One or more script files to run +| | **Optional Arguments** +| `-h` | show this help message and exit +| `-d` | add extra debugging output, normally suppressed +| `-v` | verbose script status output instead of the usual few progress messages +| `-vv` | very verbose: Includes SIMH expect output with the verbose output. +| | **Known Enable Options** +| `focal69` | Add `FOCAL69` to the built OS/8 RK05 image +| `music` | Add *.MU files to the built OS/8 RK05 image +| `vtedit` | Add the TECO VTEDIT setup to the built OS/8 RK05 image +| | **Known Disable Options** +| `ba` | Leave *.BA `BASIC` games and demos off the built OS/8 RK05 image +| `uwfocal` | Leave U/W FOCAL (only) off the built OS/8 RK05 image +| `macrel` | Leave the `MACREL` assembler off the built OS/8 RK05 image +| `dcp` | Leave the `DCP` disassembler off the built OS/8 RK05 image +| `k12` | Leave 12-bit Kermit off the built OS/8 RK05 image +| `cc8` | Leave the native OS/8 CC8 compiler off the built OS/8 RK05 image +| `crt` | Leave CRT-style rubout processing off the built OS/8 RK05 image +| `advent` | Leave Adventure off the built OS/8 RK05 image +| `fortran-ii` | Leave FORTRAN II off the built OS/8 RK05 image +| `fortran-iv` | Leave FORTRAN IV off the built OS/8 RK05 image +| `init` | Leave the OS/8 INIT message off the built OS/8 RK05 image +| `chess` | Leave the CHECKMO-II game of chess off the built OS/8 RK05 image +| `lcmod` | Disable the OS/8 command upcasing patch. Used when SIMH has `tti ksr` set. + + +## Script Language Command Inventory + +Here is a list of the `os8-run` scripting language commands in alphabetical order. + + +| [`boot`](#boot-comm) | Boot the named SIMH device. | +| [`begin`](#begin-end-comm) | Begin complex conditional or sub-command block. | +| [`configure`](#configure-comm) | Perform specific SIMH configuration activities. | +| [`copy`](#copy-com) | Make a copy of a POSIX file. | +| [`cpfrom`](#copy-from-comm) | Copy *from* OS/8 into a file in POSIX environment. | +| [`cpto`](#copy-to-comm) | Copy POSIX file *to* OS/8 environment. | +| [`disable`](#en-dis-comm) | Set disablement of a feature by keyword. | +| [`enable`](#en-dis-comm) | Set enablement of a feature by keyword. | +| [`end`](#begin-end-comm) | End complex conditional or sub-command block. | +| [`exit`](#exit-comm) | Exit os8-run and send status | +| [`include`](#include-comm) | Execute a subordinate script file. | +| [`mount`](#mount-comm) | Mount an image file as a SIMH attached device. | +| [`os8`](#os8-comm) | Run arbitrary OS/8 command. | +| [`pal8`](#pal8-comm) | Run OS/8 `PAL8` assembler. | +| [`patch`](#patch-comm) | Run a patch file. | +| [`print`](#print-comm) | Print information from running script. | +| [`resume`](#resume-comm) | Resume OS/8 at Keyboard Monitor command level. | +| [`restart`](#restart-comm) | Restart OS/8. | +| [`umount`](#umount-comm) | Unmount a SIMH attached device image. | + +These commands are described in subsections of [Script Language +Command Reference](#scripting) below. That section presents commands +in an order appropriate to building up an understanding of making +first simple and then complex scripts with `os8-run`. + + +## Script Language Command Reference + +### `print` - Print information from a running script. + +`print` _output_ + +The simplest script command is `print` which allows display of status +information from the running script. _output_ is simply displayed. +If the `verbose` option to `os8-run` is set, the line number of the +print command is included in the output. + + +### `exit` - Exit `os8-run` and send status. + +`exit` [_status_] + +The `exit` command allows immediate termination of the `os8-run` script. + +The _status_ argument is optional. If the argument is an integer, `os8-run` +will return that status to the calling command shell. This enables rich +signalling of status when `os8-run` itself is run as a script. + +The _status_ argument can also be a string. If a string is specified, +the status returned by `os8-run` to the command shell is 1, and the +string is printed on exit. (This is the default behavior of the python +`sys.exit()` procedure.) + + +### `include` — Execute a subordinate script file. + +`include` _script-file-path_ + +The script file named in _script-file-path_ is executed. If no fatal +errors are encountered during that execution, then the main script +continues on. + +A fatal error in an included script kills the whole execution of +`os8-run`. + + +### `mount` — Mount an image file as a SIMH attached device. + +`mount` _simh-dev_ _image-file_ [_option_ ...] + +Because the primary expectation with `os8-run` scripts is that image +files are mounted, booted and operated on, the failure of a `mount` +command is fatal. + + +#### `mount` Options + +| `new` | If there is an existing file, rename it with a `.save` extension +| | because we want to create a new empty image file. If there is +| | already an existing `.save` version, the existing `.save` version is lost. +| `required` | _image-file_ is required to exist, otherwise abort the script. +| `preserve` | If _image-file_ already exists, create a copy with a version number suffix. +| | This is useful when you want to prevent overwrites of a good image file +| | with changes that might not work. `os8-run` preserves all versions seen +| | and creates new version that doesn't overwrite any of the previous ones. +| `readonly` | Passes the `-r` option to SIMH attach to mount the device in read only mode. +| `ro` | Abbreviation for `readonly`. +| `scratch` | Create a writeable scratch version of the named image file and mount it. +| | This is helpful when you are booting a distribution DECtape. +| | Booted DECtape images must be writeable. To protect a distribution DECtape, +| | use the `scratch` option. When all script runs are done, the scratch version +| | is deleted. + +Note that the `preserve` and `new` options approach preservation in fundamentally +different ways: `preserve` keeps all old copies, but `new` only keeps the most recent +copy. This is because it is expected that the `new` option is used with the expectation +that old content is not precious, but that we have a backstop. Whereas the expectation +of the use of `preserve` is that any and all old versions are precious and should be left +to a person explicitly to delete. + +When new image files are created, some sort of initialization is necessary before +files can be written to them. Although the `new` and `preserve` options could have +performed an OS/8 `ZERO` command to initialize the directories, it was decided +not to do so because in some cases, the OS/8 device drivers required to perform +such actions might not be active until farther down in a complex script. + + +#### `mount` Examples + +Mount the `os8v3d-patched.rk05` image, to be found in in the install directory +for runable image files, which must exist, on SIMH `rk0`. + + mount rk0 $bin/os8v3d-patched.rk05 required + +Mount the `advent.tu56` image, to be found in the media input directory, +which must exist, on SIMH `dt1` in read only mode, which will protect +it from inadvertent modification. + + mount dt1 $os8mi/subsys/advent.tu56 readonly required + +Create a new image file in the media output directory, and mount it on +the SIMH `dt0` device. + + mount dt0 $os8mo/test_copy.tu56 new + +Create a writeable copy of the distribution DECtape, +`al-4711c-ba-os8-v3d-1.1978.tu56`, to be found in the media input directory, +which must exist. Mount it on SIMH dt0 ready for for a read/write boot. +Delete the copy when the script is done. + + mount dt0 $os8mi/al-4711c-ba-os8-v3d-1.1978.tu56 required scratch + +Create a new image file `system.tu56` in the install director for +runable image files. If the file already exists, create a new +version. If the numbered version file exists, keep incrementing the +version number for the new file until a pre-existing file is not +found. + +For example, if `system.tu56` was not found, the new file would be +called `system.tu56`. If it was found the next version would be +called `system_1.tu56`. If `system_1.tu56` and `system_2.tu56` were +found the new file would be called `system_3.tu56`, and so on. + + mount dt0 $bin/system.tu56 preserve + +The `preserve` option is helpful when experimenting with scripts that may +not work the first time. + + +### umount — Unmount a SIMH attached device image. + +`umount` _simh-device_ + +This is just a wrapper for the SIMH `detach` command. + +Here is the rationale for having added a `umount` command instead of +just calling the `detach` command from SIMH by its own same name: + +Starting from the idea of providing some abstract but useful actions +to take around the SIMH `attach` command, the decision was made to +lean on the POSIX `mount` command because people familiar with +`mount` were used to hanging lots of different abstract but useful +actions off of it. + +`umount` was adopted as _what people familiar with `mount` would +expect as the command to undo what `mount` had done_. This seemed +preferable to inventing _`attach` with more features_ as a wholly +new syntactic/semantic design. + + +### `boot` — Boot the named SIMH device. + +`boot` _simh-device_ + +Boot OS/8 on the named _simh-device_ and enter the OS/8 run-time context. + +The `boot` command tests to see if something is attached to the +SIMH being booted. If nothing is attached, the command fails with a +fatal error. + +This test does not protect against trying to boot an image lacking a +system area and thus not bootable. This can't be tested in advance +because booting a non-bootable image simply hangs the virtual machine. +Heroic measures, like looking for magic system area bits in the image +file were deemed too much work. + +If an attempt is made to boot an image with no system area, `os8-run` +hangs for a while and then gives a timeout backtrace. + + +### `resume` — Resume OS/8 at Keyboard Monitor command level. + +`resume` + +As explained above in the [Execution contexts](#contexts) section, we +can't just issue a SIMH `continue` command because we need some output +from OS/8 running within SIMH to re-synchronize Python expect to. + +After trying several different things that did not work, the least +disruptive action is to send `CTRL/C` and a newline with some keyboard +delays. The `resume` command does this. + +However, because the context switches are well-defined, the `resume` +command is completely optional in scripts. Instead `os8-run`, when it +detects the need to return to OS/8 from SIMH command level, will issue +a `resume` command to force a context switch. + + +### `restart` — Restart OS/8. + +`restart` + +Equivalent to the SIMH command line of \"`go 7600`\". + +Before `resume` was developed, the next less disruptive way to get an +OS/8 Keyboard Monitor prompt was to restart SIMH at address 07600. +This is considered a soft-restart of OS/8. It is less disruptive than +a `boot` command, because the `boot` command loads OS/8 into main +memory from the boot device, whereas restarting at location 07600 is +just a resart without a reload. + +The restart does re-initilaize some state so it is more disruptive +than the `CTRL/C` resume documented above. + + +### `copy` — Make a copy of a POSIX file. + +`copy` _source-path_ _destination-path_ + +The most common activity for `os8-run` is to modify a system image. + +However, we often want to keep the original and modify a copy. +For example, `os8v3d-patched.rk05`, a commonly used system image comes +from modifying `os8v3d-bin.rk05`. We keep the latter around so we +don't have to keep rebuilding the baseline. + +Instead of requiring some external caller to carefully preserve the +old file, the "make a copy with arbitrary name" functionality was +added by way of this command. + +Adding an option to `mount` was considered, but in the interests +of allowing an arbitrary name for the modified image, a separate +command was created. + + +### `cpto` — Copy POSIX file *to* OS/8 environment. + +`cpto` _posix-path_ [_option_] + +`cpto` _posix-path_ _os8-filespec_ [_option_] + +The option is either empty or exactly one of + +| `/A` | OS/8 `PIP` ASCII format. POSIX newlines are converted to OS/8 newlines. +| `/B` | OS/8 `PIP` `BIN` format. Paper tape leader/trailer may be added. +| `/I` | OS/8 `PIP` image format. Bit for Bit copy. + +If no option is specified, `/A` is assumed. + +In the first form of the command, the OS/8 file specification is left +out, and one is synthesized from the file component of the _posix-path_. + +This is how you get files *to* OS/8 from the outside world. For +example, this enables source code management using modern tools. The +builder script would check out the latest source and use an `os8-run` +script beginning with a `cpto` command to send it to OS/8 for +assembly, linking, installation, etc. + +Example: + +Copy a POSIX file init.cm onto the default OS/8 device `DSK:` under the name `INIT.CM`: + + cpto ../media/os8/init.cm + + +### `cpfrom` — Copy *from* OS/8 to a file in POSIX environment. + +`cpfrom`_os8-filespec_ _posix-path_ [_option_] + +The option is either empty or exactly one of + +| `/A` | OS/8 `PIP` ASCII format. POSIX newlines are converted to OS/8 newlines. +| `/B` | OS/8 `PIP` `BIN` format. Paper tape leader/trailer may be added. +| `/I` | OS/8 `PIP` image format. Bit for Bit copy. + +If no option is specified, `/A` is assumed. + +Unlike `cpto` there is only one form of the command. Both the +_os8-filespec_ and the _posix-path_ must be specified. The options +are the same for both `cpfrom` and `cpto`. + +Copy files from the running OS/8 environment to the POSIX environment running SIMH. + +Example: + +Copy a listing file into the current working directory of the +executing `os8-run`: + + cpfrom DSK:OS8.LS ./os8.ls /A + + +### `os8` — Run arbitrary OS/8 command. + +`os8` _os8-command-line_ + +Everything on the script command line after \"os8 \" is passed, +uninterpreted, to the OS/8 keyboard monitor with the expectation that +the command will return to the monitor command level and the command +prompt, "`.`" will be produced. + +This command should be used ONLY for OS/8 commands that return +immediately to command level. `BATCH` scripts do this, and they can +be run from here. + + +### `pal8` — Run OS/8 `PAL8` assembler. + +Run `PAL8` with either a 3 argument form that produces a listing file, +or a 2 argument form that does not. + +Actually, the `PAL8` assembler can be called just fine +by using the `os8` command, for example: + + os8 PAL8 RKB1:RL0.BN`begin` / `end` — Complex conditionals and sub-command blocks. + +`begin` _keyword_ _argument_ + +`end` _keyword_ + +_keyword_ is either one of the following: + +| `cdprog` | Command loop through OS/8 Command Decoder with _argument_ specifying | +| | an OS/8 executable program by name and (optionally) device.| +| `build` | `BUILD` command interpreter with dialogs manged with Python expect. | +| `enabled` | Execution block only if _argument_ is enabled. (See the [`enable` \ `disable`](#en-dis-comm)) section below. | +| `default` | Execution block that runs by default but is ignored if _argument_ is disabled. | +| | (See the [`enable` \ `disable`](#en-dis-comm) section below.) | +| `version` | Execution block that runs if the current version of the `os8-run` | +| | scripting language is equal to or greater than the specified version string. | +| | (See [version test](#vers-test) below.)| + +For `cdprog`, and `build`, _argument_ is passed uninterpreted to the +OS/8 `RUN` command. It is expected that _argument_ will be the name +of an executable, optionally prefixed by a device specification. This +enables running the OS/8 command from specific devices. This is +necessary for running specific `BUILD` command for construction of +system images for specific versions of OS/8 that are __different__ +from the default run image. + +Example: + +Run `FOTP.SV` from device `RKA0` and cycle through the command decoder +to copy files onto a DECtape under construction from two different +places: the old system on `RKA0:` and the newly built components from +`RKB1:`. + + begin cdprog RKA0:FOTP.SV + DTA0:`enable` / `disable` — Set an enablement or disablement. + +`enable` _keyword_ + +`disable` _keyword_ + +The `enable` and `disable` commands are used within scripts to +dynamically set enablement and disablement. This expands the scope of +conditional execution beyond setting passed in from the `os8-run` +command line. + +As mentioned above, there are two lists of keywords, one for `enabled` +keywords and one for `disabled` keywords. + +The `enable` command not only adds the keyword to the `enabled` +list. It also looks for the keyword on the `disabled` list. If the +keyword is found on the `disabled` list, it is **removed**. + +Similarly, the `disable` command adds the keyword to the `disabled` +list, and searches the `enabled` list for the keyword. If it is found +on the `enabled` list, it is removed. + +A keyword, will appear only once, if present at all, and will be on +only one of the two lists. + +The rule is: Last action wins. + +Why all this complexity? Here is an example we tripped over and had to +implement: We normally apply patches to the version of `FUTIL` that +came on the OS/8 v3d distribution DECtapes. We also have an add-on +for the `MACREL` assembler. That add-on contains a version of `FUTIL` +with updates required to work with binaries assembled with `MACREL` v2. +The `os8v3d-patch.mkos8` script needed to either avoid trying to +patch an updated `FUTIL` if `MACREL` was present, or to perform the +patching action if `MACREL` was not present. + +A further complication is that we opt in to including the `MACREL` +add-on by default. We deal with this triple negative by setting +`disable futil_patch` by default, unless `macrel` gets disabled: + + # MACREL is enabled by default with no settings. + # We need to avoid patching FUTIL in that default case + # So we have to set a disablement of that action by + # default as well. + + begin default macrel + disable futil_patch + end default macrel + + begin default futil_patch + # The two FUTIL patches only get applied to FUTIL V7 which comes with + # OS/8 V3D to bring it up to V7D. MACREL V2 comes with FUTIL V8B, so + # these patches are skipped by mkos8 using an RE match on the file name + # when the user does not pass --disable os8-macrel to configure. + patch ../media/os8/patches/FUTIL-31.21.1M-v7B.patch8 + patch ../media/os8/patches/FUTIL-31.21.2M-v7D.patch8 + end default futil_patch + + +### version test + +The `os8-run` scripting language is expected to evolve over time. An internal +language version number is kept, and incremented when major or minor changes +are made to the language. + +This version numbering scheme can be detected and acted upon within a script +by specifying a `version` match string in a `begin` / `end` block. + +The language version string is sequence of numerical sub version numbers of arbitrary +depth separated by periods. Examples of valid language version strings: + + 3 + 3.1 + 3.10 + 3.10.1 + +Each sub version is an integer of arbitrary precision. + +Clarifying exmple: 3.10 is higher than 3.1.0. + +No whitespace is allowed within a version match string. + + +Therefore a conditional block requiring language version 2.0 and higher +would look like this: + + begin version 2.0 + # The symprini command exists only in version 2 and above. + symprini + end version 2.0 + + +### `patch` — Run a patch file. + +`patch` _patch-file-path_ + +Run _patch-file-path_ file as a script that uses `ODT` or `FUTIL` to +patch the booted system image. + + +### `configure` — Perform specific SIMH configuration activities. + +`configure` _device_ _setting_ + +The settings are device specific: + +| -------- | --------------------------------------------------------------- | +| **tape** | **DECtape device settings** | +| `dt` | Set TC08 operation by enabling `dt` as the SIMH DECtape device. | +| `td` | Set TD8e operation by enabling `td` as the SIMH DECTape device. | +| -------- | --------------------------------------------------------------- | +| **tti** | **Console terminal input device settings** | +| `KSR` | Upper case only operation. Typed lower case characters | +| | are upcased automatically before being sent to OS/8 | +| `7b` | SIMH 7bit mode. All characters are passed to OS/8 | +| | without case conversion. | +| -------- | --------------------------------------------------------------- | +| **rx** | **Floppy Disk device settings** | +| `RX8E` | Set the SIMH `rx` to `RX8E` mode compatible with RX01 | +| | Floppy Disk Drives. | +| `RX28` | Set the SIMH `rx` to `RX28` mode compatible with RX02 | +| | Floppy Disk Drives. | +| `rx01` | Synonym for the `RX8E` option. Compatible with RX01. | +| `rx02` | Synonym for the `RX28` option. Compatible with RX02. | +| -------- | --------------------------------------------------------------- | + +This command allows reconfiguration of the SIMH devices during the +execution of a `os8-run` script. This command makes it possible to +create system images for hardware configurations that are not what are +commony used for OS/8 operation under SIMH. + +The best example is the dichotomy between TD8e and TC08 DECTape. + +TC08 is a DMA device. It is trivial to emulate. The SIMH device driver +simply copies blocks around in the .tu56 DECtape image. + +TD8e is an inexpensive, DECtape interface on a single hex width card +for PDP8 hardware supporting the Omnibus&tm. The CPU does most of the +work. Although a SIMH emulation is available for TD8e, it runs +perceptably and often unacceptably more slowly than the simple TC08 +emulation. + +However, hardware in the field most often has the TD8e DECtape because +it was inexpensive. + +By allowing reconfiguration inside a script, we can use TC08 by +default, switch to TD8e to run `BUILD` and create .tu55 tape images +suitable for deployment on commonly found hardware out in the real +world. + + +## TODOs + +* Allow passing in of arguments to PAL8. +* Add sanity check parse of sub-commands to confirm command. **OR** Change the +begin command to treat _argument_ not as a full command, but merely +a device from which to fetch the command. Maybe make _argument_ optional. + + +## Notes + +* No notes as of yet. + +### License + +Copyright © 2018 by Bill Cattey and Warren Young. Licensed under the +terms of [the SIMH license][sl]. + +[sl]: https://tangentsoft.com/pidp8i/doc/trunk/SIMH-LICENSE.md DELETED lib/mkos8/__init__.py Index: lib/mkos8/__init__.py ================================================================== --- lib/mkos8/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -######################################################################## -# __init__.py - mkos8 module initialization -# -# Copyright © 2017 by Warren Young -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, -# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# -# Except as contained in this notice, the names of the authors above -# shall not be used in advertising or otherwise to promote the sale, -# use or other dealings in this Software without prior written -# authorization from those authors. -######################################################################## - -__all__ = [ 'argparser' ] DELETED lib/mkos8/argparser.py Index: lib/mkos8/argparser.py ================================================================== --- lib/mkos8/argparser.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -######################################################################## -# argparse.py - Extend ArgumentParser to add mkos8 bits. -# -# Copyright © 2017 by Warren Young -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, -# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT -# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# -# Except as contained in this notice, the names of the authors above -# shall not be used in advertising or otherwise to promote the sale, -# use or other dealings in this Software without prior written -# authorization from those authors. -######################################################################## - -import argparse -import opts - -class ArgParser (argparse.ArgumentParser): - def __init__ (self, allowed_acts): - # Call parent class ctor to initialize the arg parser - argparse.ArgumentParser.__init__ (self, - description = 'Build OS/8 RK05 disk images') - - # Add general-purpose args - self.add_bool ('-d', '--debug', - help = 'add extra debugging output, normally suppressed') - self.add_bool ('-v', '--verbose', - help = 'verbose SIMH output instead of progress messages') - - # Add arguments corresponding to --*-os8-* configure script options - for obn, vals in opts.opts.iteritems(): - od = 'dis' if vals[0] else 'en' - self.add_bool ('--' + od + 'able-' + obn, help = vals[1]) - - # Add options that do not exactly mirror configuration options - self.add_bool ('--disable-lcmod', - help = 'disable the OS/8 command upcasing patch; best set ' + - 'when SIMH is set to tti ksr mode') - - # Add trailing "what do do" argument - self.add_argument ( - 'what', - choices = allowed_acts, - help = 'select which RK05 media gets built; default is "all"', - nargs = argparse.REMAINDER) - - # Finish initializing - self.args = self.parse_args() - if len (self.args.what) == 0: self.args.what = [ 'all' ] - - - def add_bool (self, *args, **kwargs): - kwargs['action'] = 'store_true' - kwargs['default'] = False - self.add_argument (*args, **kwargs) ADDED lib/os8script.py Index: lib/os8script.py ================================================================== --- /dev/null +++ lib/os8script.py @@ -0,0 +1,1887 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +######################################################################## +# simh-os8-script.py Library for scripting OS/8 under SIMH +# Contains validators and callers for os8 and simh commands to make +# it easier to create scripts. +# +# Copyright © 2017 by Jonathan Trites, William Cattey, and Warren Young. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS LISTED ABOVE BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT +# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the names of the authors above +# shall not be used in advertising or otherwise to promote the sale, +# use or other dealings in this Software without prior written +# authorization from those authors. +######################################################################## + +# Bring in just the basics so we can bring in our local modules +import os +import sys +import tempfile +sys.path.insert (0, os.path.dirname (__file__) + '/../lib') +sys.path.insert (0, os.getcwd () + '/lib') + +# Python core modules we use +import re +from string import Template +import shutil +import subprocess + +# Our local modules +from pidp8i import * +from simh import * + +# Script Language Version +# Update this version number as the language evolves. +# Version 1.0 is the first public version. +LANG_VERSION = "1.0" + +# Error Class Definitions ############################################## +# Enables us to use exceptions from within this module. + +class Error(Exception): + """Base Class for exceptions in this module.""" + pass + +class InputError(Error): + """Exception raised for errors in the input. + + Attributes: + expr -- input expression in which the error occurred + msg -- explanation of the error + """ + + def __init__(self, msg): + self.msg = msg + + def __str__(self): + return self.msg + + +# Private globals ###################################################### +# Visible within this file, but not to the outside. + +# Identify a begin enabled/not_disabled command. group(1) contains either the enabled or +# disabled flag. Put the rest of the line in group(2) +_begin_en_dis_comm_re = re.compile ("^begin\s+(enabled|default|version)\s+(.+)$") + +# Identify an end enabled/not_disabled command. group(1) contains either the enabled or +# disabled flag. Put the rest of the line in group(2) +_end_en_dis_comm_re = re.compile ("^end\s+(enabled|default|version)\s+(.+)$") + +# Identify an end comm and put the rest of the line in group(1) +_end_comm_re = re.compile ("^end\s+(.+)?$") + +# Identify an end option command and put the rest of the line in group(1) +_end_option_comm_re = re.compile ("^end\s+option\s+(.+)$") + +# A valid version spec +_version_parse_re = re.compile ("^((\d+\.)*)?(\d+)?$") + +# Name of the DECtape image file we create +_new_sys_tape_prefix = "system" + +# Parser regexps used in patcher +_com_os8_parse_str = "^\.([a-zA-Z]+)\s*(.*)$" +_com_os8_parse = re.compile(_com_os8_parse_str) +_com_split_str = "^([a-zA-Z]+)\s*(.*)$" +_com_split_parse = re.compile(_com_split_str) +_odt_parse_str = "^([0-7]+)\s*/\s*(\S+)\s+([0-7;]+)" +_odt_parse = re.compile(_odt_parse_str) + +# Put command keyword in group(1) and the rest is in group(3) +_comm_re_str = "^(\S+)(\s+(.+))?$" +_comm_re = re.compile(_comm_re_str) + +# Identify an end comm and put the rest of the line in group(1) +_end_comm_re = re.compile ("^end\s+(.+)?$") + +# Identify an end option command and put the rest of the line in group(1) +_end_option_comm_re = re.compile ("^end\s+option\s+(.+)$") + +# Identify a begin command and put the rest of the line in group(1) +_begin_option_comm_re = re.compile ("^begin\s+option\s+(.+)$") + +# Parse an argument string into a sys device with +# device name in group(1), unit number in group(2) +# We put all bootable devices into this string so that when +# we add more devices, for example rl for RL01, we change one +# string not many. +_simh_boot_dev_str = "(rk|td|dt|rx)(\d*)" +_simh_boot_re = re.compile("^" + _simh_boot_dev_str + "$") + +# Parse an argument string for mount into SIMH device +# device name in group(1), unit number in group(2) +# And the rest in group (3) +_mount_regex_str = "^" + _simh_boot_dev_str + "\s+(.+)$" +_mount_re = re.compile(_mount_regex_str) + +# Map of SIMH device names to OS/8 device name prefixes. +_os8_from_simh_dev = {"rk" : "RK", "td" : "DTA", "dt" : "DTA", "rx" : "RX"} + +_os8_partitions = {"RK": ["A", "B"]} + +# OS/8 file name matching regex +_os8_file_re = re.compile("(\S+):(\S+)?") + +# Regular expression for syntax checking inside FOTP +# Destination is in group(1), Source is in group(3) +_fotp_re = re.compile ("^((\S+:)?\S+)<((\S+:)?\S+)$") + +# Regular expression for detecting the 2 arg and 3 arg forms +# of the "pal8" script command. + +# OS/8 name regex template: +# Optional device spec, i.e. DTA0: +# File spec with a specific extension or no extension. + +_os8_fspec = Template ("((\S+:)?([A-Z0-9]{1,6}|[A-Z0-9]{1,6}\.$ext))") +_os8_BN_fspec = _os8_fspec.substitute(ext="BN") +_os8_PA_fspec = _os8_fspec.substitute(ext="PA") +_os8_LS_fspec = _os8_fspec.substitute(ext="LS") + +# For the two arg form: +# The full destination spec is in group(1), The full source spec is in group(4). +# The device components, if any, are in group(2) for destination, and +# group(5) for source. +# The file components are in group(3) for destination, and group (6) for source. +# The destination file must either end in ".BN" or have no extension. +# The source must file either end in ".PA" or have no extension. +_two_arg_pal_re = re.compile ("^" + _os8_BN_fspec + "\s*<\s*" + _os8_PA_fspec + "$") + +# For the 3 arg form: +# The full destination spec is in group(1), The full source spec is in group(7). +# The full listing spec is in group(4) +# The device components, if any, are in group(2) for destination, group(5) +# for listing, and group(8) for source. +# The file components are in group(3) for destination, and group(9) for source, +# and group(6) for listing. +# The destination file must either end in ".BN" or have no extension. +# The source must file either end in ".PA" or have no extension. +# The listing must either end in "LS" or have no extension. + +_three_arg_pal_re = re.compile ("^" + _os8_BN_fspec + "\s*,\s*" + _os8_LS_fspec + "\s*<\s*" + _os8_PA_fspec + "$") + +# Regular expression for syntax checking inside ABSLDR +# One or more OS/8 binary files and optional args beginning with a slash. + +_absldr_re = re.compile ("^" + _os8_BN_fspec + "(," + _os8_BN_fspec + ")*(/\S)*$") + +# Regular expressions for syntax checking for cpto and cpfrom. +# May be where destination and default option /A is implied. +# Or