diff --git a/.gitignore b/.gitignore index b3ee1c6..2fc03c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.o dl +*.exe +tmp/ diff --git a/AUTHORS b/AUTHORS index 52910e6..70024b7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,13 +1,19 @@ dlplus: -Stephen Hurd Initial version? -John R. Hogerhuis Enhancements including WP-2 support -Brian K. White Bootstrap function and loaders library +Stephen Hurd Initial versions 1.0-1.3 +John R. Hogerhuis Enhancements including WP-2 support +Brian K. White Bootstrap function and loader collection + main cmd scanner, serial tty handling + magic files for UR-II ts-dos & sardine + pdd1 & pdd2 disk image sector access +Gabriele Gorla TS-DOS subdirectories TPDD Clients & loaders: -Kurt McCullum TS-DOS loaders -Ron Weisen TEENY loaders -J.K. Heilman DSKMGR -Phil Wheeler DSKMGR loaders -A. Ryan Port of DSKMGR & loader for Olivetti M10 -ACROATIX TINY -Tracy Allen TINY loader +Kurt McCullum TS-DOS loaders +Ron Weisen TEENY, WEENY, & D loaders +J.K. Heilman DSKMGR +Phil Wheeler DSKMGR loaders +A. Ryan Port of DSKMGR & loader for Olivetti M10 +ACROATIX TINY +Tracy Allen TINY loader +Hugo Ferreyra Disk Power +Brian K. White Disk Power loader & disk image diff --git a/BSDmakefile b/BSDmakefile new file mode 100644 index 0000000..6fdf128 --- /dev/null +++ b/BSDmakefile @@ -0,0 +1,3 @@ +$(.TARGETS) all: + @which gmake 2>&- >&- || { echo "Please install gmake." ;false ; } + @gmake $(.TARGETS) diff --git a/Brother_FB-100.rom b/Brother_FB-100.rom new file mode 100644 index 0000000..366f855 Binary files /dev/null and b/Brother_FB-100.rom differ diff --git a/CHANGES b/CHANGES index 51f391c..2423551 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,13 @@ +20220712 + Add sector access commands and disk image support for pdd1 & pdd2 + Magic ts-dos & sardine files for UR-II + +20220530 + Replace main cmd parser. Improve tty efficiency. + +20220516 + (GGLabs) Added support for TS-DOS subdirectories + 20210222 Increased default sleep between each byte in bootstrap to 6 ms Added -z=# to specify ms to sleep after sending each byte in bootstrap diff --git a/Makefile b/Makefile index 82aff4a..435b7d8 100644 --- a/Makefile +++ b/Makefile @@ -1,66 +1,190 @@ -# Makefile for DeskLink+ +# Makefile for DeskLink2 OS ?= $(shell uname) CC ?= gcc CFLAGS += -O2 -Wall +#CFLAGS += -std=c99 -D_DEFAULT_SOURCE # prove the code is still plain c PREFIX ?= /usr/local -APP_NAME = dl -APP_LIB_DIR = $(PREFIX)/lib/$(APP_NAME) -APP_DOC_DIR = $(PREFIX)/share/doc/$(APP_NAME) - -DEFAULT_CLIENT_TTY = ttyUSB0 -DEFAULT_CLIENT_MODEL = 100 -DEFAULT_CLIENT_APP = TEENY - -TEENY_INSTALLERS = clients/teeny/TEENY.100 clients/teeny/TEENY.200 clients/teeny/TEENY.NEC clients/teeny/TEENY.M10 -TEENY_DOCS = clients/teeny/teenydoc.txt clients/teeny/hownec.do clients/teeny/TNYO10.TXT -DSKMGR_INSTALLERS = clients/dskmgr/DSKMGR.100 clients/dskmgr/DSKMGR.200 clients/dskmgr/DSKMGR.K85 clients/dskmgr/DSKMGR.M10 -DSKMGR_DOCS = clients/dskmgr/DSKMGR.DOC -TS-DOS_INSTALLERS = clients/ts-dos/TS-DOS.100 clients/ts-dos/TS-DOS.200 clients/ts-dos/TS-DOS.NEC -TS-DOS_DOCS = clients/ts-dos/tsdos.pdf -TINY_INSTALLERS = clients/tiny/TINY.100 -TINY_DOCS = clients/tiny/tindoc.do -#POWR-D_INSTALLERS = clients/power-dos/POWR-D.100 -#POWR-D_DOCS = clients/power-dos/powr-d.txt - -CLIENT_APP_INSTALLERS = $(TEENY_INSTALLERS) $(TINY_INSTALLERS) $(TS-DOS_INSTALLERS) $(DSKMGR_INSTALLERS) -CLIENT_APP_DOCS = $(TEENY_DOCS) $(TINY_DOCS) $(TS-DOS_DOCS) $(DSKMGR_DOCS) - -DOCS = dl.do README.txt README.md LICENSE $(CLIENT_APP_DOCS) -SOURCES = dl.c dir_list.c - -DEFINES = \ - -DAPP_LIB_DIR=$(APP_LIB_DIR) \ - -DDEFAULT_CLIENT_TTY=$(DEFAULT_CLIENT_TTY) \ - -DDEFAULT_CLIENT_APP=$(DEFAULT_CLIENT_APP) \ - -DDEFAULT_CLIENT_MODEL=$(DEFAULT_CLIENT_MODEL) +NAME := dl +APP_NAME := DeskLink2 +APP_LIB_DIR := $(PREFIX)/lib/$(NAME) +APP_DOC_DIR := $(PREFIX)/share/doc/$(NAME) +APP_VERSION := $(shell git describe --long 2>&-) -ifdef DEBUG - CFLAGS += -g -else - CFLAGS += -s -endif +# optional configurables +#FB100_ROM := Brother_FB-100.rom # exists but not used +#TPDD2_ROM := TANDY_26-3814.rom # exists and is used +#DEFAULT_BASIC_BYTE_MS := 8 # ms per byte in bootstrap +#DEFAULT_MODEL := 1 # 1=tpdd1 2=tpdd2 (TS-DOS directory support requires tpdd1) +#DEFAULT_OPERATION_MODE := 1 # 0=FDC-mode 1=Operation-mode +#DEFAULT_BAUD := 19200 +#DEFAULT_RTSCTS := false +#DEFAULT_UPCASE := false +#DEFAULT_PROFILE := "k85" # k85 = Floppy/TS-DOS/etc - 6.2, padded, F, dme, magic files +#RAW_ATTR := 0x20 # attr for "raw" mode, drive firmware fills unused fields with 0x20 +#DEFAULT_TILDES := true +#XATTR_NAME := pdd.attr +#TSDOS_ROOT_LABEL := "0: " +#TSDOS_PARENT_LABEL := "^ " + +CLIENT_LOADERS := \ + clients/teeny/TINY.100 \ + clients/teeny/D.100 \ + clients/teeny/TEENY.100 \ + clients/teeny/TEENY.200 \ + clients/teeny/TEENY.NEC \ + clients/teeny/TEENY.M10 \ + clients/dskmgr/DSKMGR.100 \ + clients/dskmgr/DSKMGR.200 \ + clients/dskmgr/DSKMGR.K85 \ + clients/dskmgr/DSKMGR.M10 \ + clients/ts-dos/TSLOAD.100 \ + clients/ts-dos/TSLOAD.200 \ + clients/ts-dos/TS-DOS.100 \ + clients/ts-dos/TS-DOS.200 \ + clients/ts-dos/TS-DOS.NEC \ + clients/pakdos/PAKDOS.100 \ + clients/pakdos/PAKDOS.200 \ + clients/disk_power/Disk_Power.K85 \ +# clients/power-dos/POWR-D.100 + +LIB_OTHER := \ + $(TPDD2_ROM) \ + clients/ts-dos/DOS100.CO \ + clients/ts-dos/DOS200.CO \ + clients/ts-dos/DOSNEC.CO \ + clients/ts-dos/SAR100.CO \ + clients/ts-dos/SAR200.CO \ + clients/ts-dos/Sardine_American_English.pdd1 \ + clients/disk_power/Disk_Power.K85.pdd1 \ + TPDD1_26-3808_Utility_Disk.pdd1 \ + TPDD2_26-3814_Utility_Disk.pdd2 + +CLIENT_DOCS := \ + clients/teeny/teenydoc.txt \ + clients/teeny/hownec.do \ + clients/teeny/TNYO10.TXT \ + clients/teeny/tindoc.do \ + clients/teeny/ddoc.do \ + clients/dskmgr/DSKMGR.DOC \ + clients/ts-dos/tsdos.pdf \ + clients/pakdos/PAKDOS.DOC \ + clients/disk_power/Disk_Power.txt \ +# clients/power-dos/powr-d.txt + +DOCS := dl.do README.txt README.md LICENSE $(CLIENT_DOCS) +SOURCES := main.c dir_list.c xattr.c +HEADERS := constants.h dir_list.h xattr.h ifeq ($(OS),Darwin) + TTY_PREFIX := cu.usbserial else + ifneq (,$(findstring BSD,$(OS))) + TTY_PREFIX := ttyU + else ifeq ($(OS),Linux) + TTY_PREFIX := ttyUSB + else + TTY_PREFIX := ttyS + endif LDLIBS += -lutil endif +INSTALLOWNER = -o root +ifeq ($(OS),Windows_NT) + INSTALLOWNER = + CFLAGS += -D_WIN +endif + +DEFS = \ + -DAPP_NAME=\"$(APP_NAME)\" \ + -DAPP_VERSION=\"$(APP_VERSION)\" \ + -DAPP_LIB_DIR=\"$(APP_LIB_DIR)\" \ + -DTTY_PREFIX=\"$(TTY_PREFIX)\" \ + -DUSE_XATTR \ +# -DPRINT_8BIT \ +# -DNADSBOX_EXTENSIONS \ + +#ifdef TPDD1_ROM +# DEFS += -DTPDD1_ROM=\"$(TPDD1_ROM)\" +#endif +ifdef TPDD2_ROM + DEFS += -DTPDD2_ROM=\"$(TPDD2_ROM)\" +endif +ifdef TSDOS_ROOT_LABEL + DEFS += -DTSDOS_ROOT_LABEL=\"$(TSDOS_ROOT_LABEL)\" +endif +ifdef TSDOS_PARENT_LABEL + DEFS += -DTSDOS_PARENT_LABEL=\"$(TSDOS_PARENT_LABEL)\" +endif +ifdef DEFAULT_BASIC_BYTE_MS + DEFS += -DDEFAULT_BASIC_BYTE_MS=$(DEFAULT_BASIC_BYTE_MS) +endif +ifdef DEFAULT_MODEL + DEFS += -DDEFAULT_MODEL=$(DEFAULT_MODEL) +endif +ifdef DEFAULT_OPERATION_MODE + DEFS += -DDEFAULT_OPERATION_MODE=$(DEFAULT_OPERATION_MODE) +endif +ifdef DEFAULT_BAUD + DEFS += -DDEFAULT_BAUD=$(DEFAULT_BAUD) +endif +ifdef DEFAULT_RTSCTS + DEFS += -DDEFAULT_RTSCTS=$(DEFAULT_RTSCTS) +endif +ifdef DEFAULT_PROFILE + DEFS += -DDEFAULT_PROFILE=$(DEFAULT_PROFILE) +endif +ifdef DEFAULT_UPCASE + DEFS += -DDEFAULT_UPCASE=$(DEFAULT_UPCASE) +endif +ifdef DEFAULT_ATTR + DEFS += -DDEFAULT_ATTR=$(DEFAULT_ATTR) +endif +ifdef RAW_ATTR + DEFS += -DRAW_ATTR=$(RAW_ATTR) +endif +ifdef DEFAULT_TILDES + DEFS += -DDEFAULT_TILDES=$(DEFAULT_TILDES) +endif +ifdef XATTR_NAME + DEFS += -DXATTR_NAME=\"$(XATTR_NAME)\" +endif + +DEFINES := $(DEFS) + +ifdef DEBUG + CFLAGS += -g +endif + .PHONY: all -all: $(APP_NAME) +all: $(NAME) -$(APP_NAME): $(SOURCES) - $(CC) $(CFLAGS) $(DEFINES) $(SOURCES) $(LDLIBS) -o $(@) +$(NAME): Makefile $(SOURCES) $(HEADERS) + $(CC) $(CFLAGS) $(CXXFLAGS) $(DEFINES) $(SOURCES) $(LDLIBS) -o $(@) -install: $(APP_NAME) $(CLIENT_APP_INSTALLERS) $(DOCS) - install -o root -m 0755 -d $(APP_LIB_DIR) $(APP_DOC_DIR) - install -o root -m 0644 -t $(APP_LIB_DIR) $(CLIENT_APP_INSTALLERS) - for i in $(CLIENT_APP_INSTALLERS) ;do install -o root -m 0644 -t $(APP_LIB_DIR) $${i} $${i}.pre-install.txt $${i}.post-install.txt ;done - install -o root -m 0644 -t $(APP_DOC_DIR) $(DOCS) - install -o root -m 0755 $(APP_NAME) $(PREFIX)/bin/$(APP_NAME) +install: $(NAME) $(CLIENT_LOADERS) $(LIB_OTHER) $(DOCS) + mkdir -p $(APP_LIB_DIR) + for s in $(CLIENT_LOADERS) ;do \ + d=$(APP_LIB_DIR)/$${s##*/} ; \ + install $(INSTALLOWNER) -m 0644 $${s} $${d} ; \ + [ ! -f $${s}.pre-install.txt ] && continue ;install $(INSTALLOWNER) -m 0644 $${s}.pre-install.txt $${d}.pre-install.txt ; \ + [ ! -f $${s}.post-install.txt ] && continue ;install $(INSTALLOWNER) -m 0644 $${s}.post-install.txt $${d}.post-install.txt ; \ + done + for s in $(LIB_OTHER) ;do \ + d=$(APP_LIB_DIR)/$${s##*/} ; \ + install $(INSTALLOWNER) -m 0644 $${s} $${d} ; \ + done + for s in $(DOCS) ;do \ + d=$(APP_DOC_DIR)/$${s} ; \ + mkdir -p $${d%/*} ; \ + install $(INSTALLOWNER) -m 0644 $${s} $${d} ; \ + done + mkdir -p $(PREFIX)/bin + install $(INSTALLOWNER) -m 0755 $(NAME) $(PREFIX)/bin/$(NAME) + install $(INSTALLOWNER) -m 0755 co2ba.sh $(PREFIX)/bin/co2ba uninstall: - rm -rf $(APP_LIB_DIR) $(APP_DOC_DIR) $(PREFIX)/bin/$(APP_NAME) + rm -rf $(APP_LIB_DIR) $(APP_DOC_DIR) $(PREFIX)/bin/$(NAME) $(PREFIX)/bin/co2ba clean: - rm -f $(APP_NAME) + rm -f $(NAME) diff --git a/README.md b/README.md index c787223..4bca671 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,268 @@ -# dlplus -DeskLink+ is a Tandy Portable Disk Drive emulator or "TPDD Server" implimented in C. +# dl2 +DeskLink2 is a [Tandy Portable Disk Drive](http://tandy.wiki/TPDD) emulator or "[TPDD server](http://tandy.wiki/TPDD_server)" written in C. -[Original README](README.txt) +## Install +``` +$ make clean all && sudo make install +``` + +## Uninstall +``` +$ sudo make uninstall +``` + +## Manual +``` +$ dl -h +DeskLink2 v2.2.001-7-g1cd1b38 -[Documentation for the original DeskLink](dl.do) (No longer exactly matches this program) +Usage: dl [options] [tty_device] [share_path] -[Original source](http://bitchin100.com/files/linux/dlplus.zip) +Options Description... (default setting) + -a attr Attribute - default attr byte used when no xattr (F) + -b file Bootstrap - send loader file to client - empty for help + -c profile Client compatibility profile (k85) - empty for help + -d tty Serial device connected to the client (ttyUSB*) + -e bool TS-DOS Subdirectories (on) - TPDD1-only + -f Start in FDC mode - TPDD1-only + -g Getty mode - run as daemon + -h Print this help + -i file Disk image filename for raw sector access - empty for help + -m 1|2 Model - 1 = FB-100/TPDD1, 2 = TPDD2 (1) + -p dir Path - /path/to/dir with files to be served (./) + -r bool RTS/CTS hardware flow control (off) + -s # Speed - serial port baud rate (19200) + -u Uppercase all filenames (off) + -~ bool Truncated filenames end in '~' (on) + -v Verbosity - more v's = more verbose, both activity & help + -z # Sleep # ms per byte in bootstrap (8) + -^ Dump config and exit -[Serial Cable](http://tandy.wiki/Model_T_Serial_Cable) +The 1st non-option argument is another way to specify the tty device. +The 2nd non-option argument is another way to specify the share path. +TPDD2 mode accepts a 2nd share path for bank 1. +"bool" accepts case-insensitive: on off 0 1 y n t f yes no true false -## install -``` -make clean all && sudo make install +Examples: + $ dl + $ dl ttyUSB1 + $ dl -v -p ~/Downloads/REX + $ dl -c wp2 /dev/cu.usbserial-AB0MQNN1 "~/Documents/WP-2 Files" + +$ ``` -## uninstall ``` -sudo make uninstall +$ dl -b +DeskLink2 v2.2.001-7-g1cd1b38 +"-b" requires a value + +Help for Bootstrap + +Usage: + -b filename send file out over the serial port, slowly + -v -b more help about bootstrap + +If filename is not found, then /usr/local/lib/dl is searched. + +Available built-in bootstrap/loader files (in /usr/local/lib/dl): + +TRS-80 Model 100/102 : DSKMGR.100 D.100 TSLOAD.100 TS-DOS.100 PAKDOS.100 TEENY.100 TINY.100 +TANDY Model 200 : DSKMGR.200 TSLOAD.200 TS-DOS.200 PAKDOS.200 TEENY.200 +NEC PC-8201/PC-8300 : TEENY.NEC TS-DOS.NEC +Kyotronic KC-85 : Disk_Power.K85 DSKMGR.K85 +Olivetti M-10 : TEENY.M10 DSKMGR.M10 + +Examples: + + dl -b TS-DOS.100 + dl -b ~/Documents/LivingM100SIG/Lib-03-TELCOM/XMDPW5.100 + dl -vb rxcini.DO && dl -v + +$ ``` -## manual ``` -dl -h +$ dl -c +DeskLink2 v2.2.001-9-g8b17554 +"-c" requires a value + +Help for Client Compatibility Profiles + +Usage: + -c name use profile - (default: "k85") + -c #.# "raw", truncated but not padded to #.#, attr='F' + -c #.#p "raw", truncated and padded to #.#, attr='F' + -v -c more help about profiles + +Available profiles: + +NAME BASE EXT PAD ATTR DME TSLOAD UPCASE +------------------------------------------------------------- +raw 0 0 off ' ' off off off +k85 6 2 on 'F' on on on +wp2 8 2 on 'F' off off off +cpm 8 3 off 'F' off off on +rexcpm 6 2 on 'F' off off on +z88 12 3 off 'F' off off off +st 6 2 on 'F' off off on + +$ ``` -## run the TPDD server, serving files from the current directory ``` -dl +$ dl -i +DeskLink2 v2.2.001-7-g1cd1b38 +"-i" requires a value + +Help for Disk Images + +Usage: + -i filename use disk image file + -v -i more help about disk images + +Available built-in (bundled) disk image files (in /usr/local/lib/dl): + +TPDD1: + TPDD1_26-3808_Utility_Disk.pdd1 + Sardine_American_English.pdd1 + Disk_Power.K85.pdd1 +TPDD2: + TPDD2_26-3814_Utility_Disk.pdd2 + +Examples: + dl -v -i Sardine_American_English.pdd1 + dl -v -i ./my_new_disk.pdd2 + +$ ``` -## list all available TPDD client installers, and then bootstrap one of them (TS-DOS for Model 100) +There are also several [environment variables](ref/advanced_options.txt) + +Docs from the past versions of this program. They don't exactly match this version any more. +[README.txt](README.txt) from [dlplus](http://bitchin100.com/files/linux/dlplus.zip) by John R. Hogerhuis +[dl.do](dl.do) from [dl 1.0-1.3](http://m100.bbsdev.net/) the original "DeskLink for \*nix" by Steven Hurd + +## Hardware +[KC-85 to PC Serial Cable](http://tandy.wiki/Model_T_Serial_Cable) + +## Examples: + +### Run the TPDD server, verbose, serving files from the current directory +`$ dl -v` + +### List all available TPDD client installers, and then bootstrap one of them ``` -dl -h -dl -b=TS-DOS.100 +$ dl -b +$ dl -vb TS-DOS.100 ``` -## bootstrap a [REXCPM](http://bitchin100.com/wiki/index.php?title=REXCPM) +### Bootstrap a [REXCPM](http://bitchin100.com/wiki/index.php?title=REXCPM) +`$ dl -vb rxcini.DO && dl -vu` +([Full directions for REXCPM](ref/REXCPM.md)) + +### Update a [REX#](http://bitchin100.com/wiki/index.php?title=REXsharp) +`$ dl -vb 'rx#u1.do' && dl -v` + +## "Magic Files" / Ultimate ROM II / TSLOAD +There is a short list of filenames that are specially recognized: +DOS100.CO, DOS200.CO, DOSNEC.CO, DOSM10.CO, DOSK85.CO +SAR100.CO, SAR200.CO, SARNEC.CO, SARM10.CO, SARK85.CO + +When a client requests any of these filenames, dl2 first looks in the current directory (the current directory that the client is CD'd into within the share path). If a file matching the requested filename is there, it is used, the same as for any other file. + +Failing that, it looks in the root of the share path + +Failing that, it looks in /usr/local/lib/dl + +And some of those files are bundled with dl2 and installed in /usr/local/lib/dl: +DOS100.CO, DOS200.CO, DOSNEC.CO +SAR100.CO, SAR200.CO + +SARNEC.CO is known to have existed, but is currently lost. +The others probably never existed, but dl2 will recognize and serve them up if available just for completeness. + +This allows TSLOAD and the TS-DOS and SARDIN features in Ultimate ROM 2 to work "by magic" at all times without you having to actually place copies of these files in every directory and subdirectory in the share path. + +You can override the bundled versions of these files without touching the /usr/local/lib/dl files by placing say a different version of DOS100.CO in the root of a share path, and it will be in effect for all directories in that share path. + +[More details](ref/ur2.txt) + +## Sector Access / Disk Images +`$ dl -i disk_image.pdd1` +or +`$ dl -i disk_image.pdd2` + +This is support for disk image files that allow use of raw sector access commands on a virtual disk image file. + +Limitations: Only supports using the disk image for sector access. It doesn't provide access to the files in a disk image as files, just as raw sectors. + +If the file exists, it's size is used to set the emulation mode to tpdd1 vs tpdd2. +If the file doesn't exist or is zero bytes, then the last 5 characters in the filename are used, ".pdd1" or ".pdd2", case insensitive. + +One example usage is the [Sardine](ref/Sardine.md) spell checker. +Another is [installing Disk Power for Kyotronic KC-85](clients/disk_power/Disk_Power.txt) + +There are 2 ways to create disk image files so far: +* One way is to use [pdd.sh](https://github.com/bkw777/pdd.sh) to read a real disk from a real drive, and output a disk image file. +* Another way is to run `dl -i filename`, where the file either doesn't exist or is zero bytes, and then use a client (like TS-DOS or pdd.sh) to format the "disk". When dl2 gets the format command, it will create the disk image. + +More details about the disk image format [disk_image_files.txt](ref/disk_image_files.txt) + +## ROOT & PARENT labels +The `ROOT ` and `PARENT` labels are not hard coded in TS-DOS. You can set them to other things. +In both cases the length is limited to 6 characters. + +The ROOT label is `ROOT ` in the original Travelling Software Desk-Link. +This is what is shown for the current directory name in the top-right corner of TS-DOS when the current working directory is at the top level directory of the share path, like the root directory of a disk. +Almost anything may be used for the `ROOT ` label. + +The PARENT label is `PARENT` in the original Travelling Software Desk-Link. +This is shown as a virtual filename in the top-left filename slot when not in the root directory, and you press Enter on it in order to move up out of the current subdirectory to it's parent directory. +This is is limited to things that TS-DOS thinks is a valid filename. +Sadly, `..` can not be used, but here are a few examples that do work. + +`$ ROOT_LABEL=/ PARENT_LABEL=^ dl` +`$ ROOT_LABEL='-root-' PARENT_LABEL='-back-' dl` +`$ ROOT_LABEL='-top-' PARENT_LABEL='-up-' dl` +`$ ROOT_LABEL='0:' PARENT_LABEL='^:' dl` +or you can confuse someone... +`$ ROOT_LABEL='C:\' PARENT_LABEL='UP:' dl` + +## co2ba.sh +Also included is a bash script to read a binary .CO file and output an ascii BASIC loader .DO file, +which may then be used with the **-b** bootstrap function to re-create the original binary .CO file on the portable. +All KC-85 platform machines are supported including TRS-80 Model 100, TANDY 102 & 200, Kyotronic KC-85, Olivetti M10, NEC PC-8201 & PC-8300. +It's simple and doesn't handle all situations or do anything fancy like relocating, but it handles the common case and serves as a reference and starting point for making a custom loader. +See [co2ba](co2ba.md) + +Example using co2ba as part of bootstrapping a REX Classic: ``` -unzip REXCPMV21_b18.ZIP -dl -b=./rxcini.DO ;dl -u +$ wget https://www.bitchin100.com/wiki/images/3/38/R49_M100T102_260_rebuild.zip +$ unzip R49_M100T102_260_rebuild.zip +$ co2ba rf149.co call >rf149.do +$ dl -vb rf149.do && dl -v ``` + +## OS Compatibility +Tested on Linux, [Mac](ref/mac.md), [FreeBSD](ref/freebsd.md), and [Windows](ref/windows.md). + +## TODO - not all necessarily serious +* File/filesystem access on disk images - Currently can only use disk images for sector access. +* Verify if the code works on a big-endian platform - There are a lot of 2-byte values and a lot of direct byte manipulations because the protocol & drive uses MSB-first everywhere while most platforms today do not. +* Figure out and emulate more of the special memory addresses accessible in tpdd2 mode. We already do some. +* Fake sector 0 based on the files in the current share path so that if a client tries to read the FCB table directly it works. +* Fake entire disk image in ram based on current share path files. Option to save the image as long as we're there. +* -j 1111 to emulate the jumper settings + +## Latest Changes +* [real attr handling using xattr](ref/xattr.md) - enabled by default now + +* client compatibility profiles + +## History / Credits +[DeskLink for ms-dos](https://ftp.whtech.com/club100/com/dl-arc.exe.gz) 1987 Travelling Software +1.0-1.3 [DeskLink for *nix](http://m100.bbsdev.net/) 2004 Stephen Hurd +1.4 [DeskLink+](https://www.bitchin100.com/files/linux/dlplus.zip) 2005 John R. Hogerhuis +1.5 2019 Brian K. White +2.0 DeskLink2 2023 Brian K. White diff --git a/TANDY_26-3814.rom b/TANDY_26-3814.rom new file mode 100644 index 0000000..c2a891a Binary files /dev/null and b/TANDY_26-3814.rom differ diff --git a/TPDD1_26-3808_Utility_Disk.pdd1 b/TPDD1_26-3808_Utility_Disk.pdd1 new file mode 100644 index 0000000..a4b5257 Binary files /dev/null and b/TPDD1_26-3808_Utility_Disk.pdd1 differ diff --git a/TPDD2_26-3814_Utility_Disk.pdd2 b/TPDD2_26-3814_Utility_Disk.pdd2 new file mode 100644 index 0000000..af88f7d Binary files /dev/null and b/TPDD2_26-3814_Utility_Disk.pdd2 differ diff --git a/clients/disk_power/Disk_Power.K85 b/clients/disk_power/Disk_Power.K85 new file mode 100644 index 0000000..df75604 --- /dev/null +++ b/clients/disk_power/Disk_Power.K85 @@ -0,0 +1 @@ +0'Disk Power KC-85 loader - 2022 b.kenyon.w@gmail.com 0CLEAR0,59072:A=59072:S=0:N$="Disk Power KC-85":CLS:?"Loading "N$" installer..."; 1D$="":READD$:FORI=1TOLEN(D$)STEP2:B=(ASC(MID$(D$,I,1))-97)*16+ASC(MID$(D$,I+1,1))-97:POKEA,B:A=A+1:S=S+B:NEXT:?".";:IFA<62954THEN1 2IFS<>472115THEN?"Bad Checksum":END 3CLS:?"Connect TPDD drive and insert the":?N$" install disk.":LINE INPUT "Press [ENTER] when ready ";A$:CLS:CALL59343 4DATAamblhacafegpgpcagngbgohjcagggjgmgfhdcablhbaaibaaaamdbligmnebokbbaappnihhnpmkfpojcdmdnmogbbdlolcbdjokmngnojmngdfholmdkificbcjohabahaalamkahohajnhdcoaohnhdccboidccdoidccfoinhdcgcoinhdchpoinhdcmloinhdcdjolnhdcmjojmjdncdmbmfkpkpcddmclobofkhkhcl 5DATAofmnlncanbebmdecdeahanakgggphccahegigfcagjgohdhegbgmgmgbhegjgpgocahahcgpgdgfhdhdanakhagmgfgbhdgfcaglgjgmgmcahdgpgngfcagggjgmgfhdcagbgogecahehchjcagbghgbgjgoaaffffahappihomnabokcdafmcimohmjamblhacaffgogfhihagfgdhegfgeaacbgcpgbbaappmnjedbcbgh 6DATAojmddlojcbojoimdfgokamblhacaedgpgngnhfgogjgdgbhegjgpgoaaecdoacabcnaacbahokofmngdfhdcjppjdmmmpmogaihhcbiholbbkfpmmnbicfmnfkcackmeplmedaolabahamajbbaaaaoldjnpnkleohcbfdokccfjpgcbklpjofholhmediohobdgpobbmmpjabalaaajnpnkakoimcnmohaihhaihhaihham 7DATAhjofcbpbojamcdcdibnkcnoiccfjpgpfmnkhojbbiaojmbobmnecdeofckmeplagaomnecdemnidbcmniacamekkbpmnbgcbfmgfglklihdnihbnehelaiabfbalmnhfgikpfhfpolcfcfmkgnoiofmnaocbobeemnbkcfnfabdhaeaiolnjcbjholabfdaknbmnodgiabagaaajccpepfcbbligbbobpenjnbobmnjedbnf 8DATAcbngogbbaciamnbicfabalaacbbaplbbbcojdoaepfbdpfmngnfhpbifgppbmngnfhofonodciaiobnjmglaobaihhmnbgcbbbacojcbgbpidobamngnfhghgmccjnpjmnnacamnnlhlmdgnojamblhacaeogphecagfgogphfghgicagngfgngphchjcablhbaafegfhcgnbiedebememdddddbdddiancaicpiicalilck 9DATAicmgiehjigieagaamfmnffojmnhfgkmbmcnmogafmccaojmddcojcbecojmdgcojcbppponhnedademdmibhamblhacaeegjhdglcaeogphecafcgfgbgehjaaabaadaalhjlamimdfiojcblkohbbgpokmdpcogdjdieodbeeofnfcbmmohagadmnimohmnffojmndiojnbobmjlapnppebeeeefcfdfdcaaalapoppfded 10DATAeiefeeemcaaaaaaaiaabpoabaamdhdfiabpoabaamdhkfimnkfohcbihohagafmnimohmnffojcbaapoofcbdkokagahmnimohofmnffojabacaaobaidfofcbaippmnboojdkaopppodemcfpojdoacmnabokmnffojobdodiloobpfmnboojpbofmcljojmdodepcbjgohmdgcojcbaaialamefmokcbmngcokmddkglee 11DATAfaeldidfcofdfjcacaejgohdhegbgmgmgbhegjgpgocagjgocahahcgpghhcgfhdhdblfjcecjfagmgfgbhdgfcafhebejfecoaafndpdgcddpdmacmngcokmnhfgkdhmimniggknkomogmcfpojmjcbmaogbbebohmdpfogmnpjojkjepagepnlllogcamcdcojdoapkjepmjcagfhchcgphccablhbanakahedgigfgdgl 12DATAcahjgphfhccagdgbgcgmgfhdcagbgogecagehcgjhggfcoanakejggcahahcgpgcgmgfgncahagfhchdgjhdhehdcmcahjgphfhccagegjhdhehcgjgchfhegjgpgoanakgegjhdglcagngbhjcagcgfcagegbgngbghgfgecmcagphccahjgphfcagbhcgfcahehchjgjgoghanakhegpcagjgohdhegbgmgmcagghcgpgn 13DATAcagbcagogpgocngphcgjghgjgogbgmcagegjhdglanakblhacafagmgfgbhdgfcagdgpgohegbgdhecagngbgohfgggbgdhehfhcgfhccablhbaaofbdbdonciagnfmbobaimjahamfahcgpgdgfhdhdcagjgohegfhchchfhahegfgecacncaedgbgochhecagdgpgohegjgohfgfanakfehfhcgocaepegegcagbgogeca 14DATAepeocahjgphfhccagehcgjhggfcagbgogecahchfgoanakejfeemeldidfcoedepcagbghgbgjgoaaaafailabaaljmdbligdgdidkcbnfpmkpagahmnfeiemdihhamnbiidabpppjadajmdfiecgjgoghcagggjgmgfhdcmcahahcgpgdgfgfgecaciebcpfjcpeocjdpcccdmnoofhbdnfmnefianhmnfjigobnfmfmnlb 15DATAbbcbimpgmbnbmdgaigobmnbjiecbmlpmmkgjibdfpcgkibdoahdeohmdbiidlhmabnmjobmnanecdclepkmdjdfbmnoeiddcmkpmmnmjigaoabmngfieaockmkediimnanifaoacmdihibpfmnpkifpbdcmipmmnpkifdkmkpmlhmkbiiddocaagfacbmipomnlkeocbaecbmnfiecmnfpiimfcbajidodnimneliedkklpm 16DATApofanamnfpiioldnpkdoibmkfgigmnbfcfabppbccbkncfbbnepomngaigcbahalmnfieccknepkofmnpnibmbckngpkajmnpnibcknipkkpmdeniecbbkihmnjjikmnfpiinklaiddnpkoibpmkjabpmdkkbpmfmniacabdpoiammmoicmnbgcbmnahicmbkamjmnjhiimnihgjkpghgpccfjpgmdfhgjcbhmibofckmepm 17DATAbbkfpmmnjedbmnbfigcbdhiemnefigmdnkihnacaneihhbifhmibabicejiddkccdbiehldmlknikpmjcbbkihmnjjikmngaiemklaidmdpkiihlngafnanbmjofnfcbjopjholhpkddbfdfmefmihckklpmbbeeepolccklpmnpmkjpichlpocamcpiijmnbfigmngaienbobofnffhabagabafajkphhdcjppjhlapldoo 18DATAacogadfpdndnhkcbglihofpkocicmkgoiblhmkpcijmjmnplafolbdckmcpmmdaecbmnpkifmndpiemdlciklhminfmnpkiimngaienbmjojfafdfecccbjppjmdmbbhngdamabncdmnddempodkmadddddoajlhmjdoppagakcbgfplmnlkeocoagmnlkefdkmipmcbabafcljecmncbpidiedmihihihghmdfieccbjjig 19DATAmnikchcbcfibmneligababaganmdiaiemnnpijehmnnpijepmjlhmicbmmihmnefigcbjopjbbabpjdkmkpmlhmcgcidbgajdgabcdkpmnmiemmncoeonklgijbbghidcbjhijnfofpoanmipoakmipocanapgeaehdofomnjhijhimjhniccoikjliebpihpcicofmnhkblmnpcgomkjdidobmdpaapdjdieodbeefceben 20DATAcaeeejfcccaoalcbaoaibbaoajnbcbnocgkpehmdalikbbkopmcbnnpmdoagmngnfhdgcocdmnglfhagapdocamdlkeodkbepncoabcnghbamneiiemdhpijmngfihmnanecmnblecdobedcmlpmkpdcmjpmdcmipmdclepkckhjplcchppgobmnaidpofnbblblblnfojbcbcahbcbcbcbhbcdebhdjdhdkmipmcgabcfgp 21DATAcjcjcjabahpoajhoabpjppajoollpocamjmnpaiimdkefeblfjcecafdgbhgcccbmipmdedkmjpmlomjdodabbdocapfmnefiapbagagpfmnefecmnkjblpbohmdekecmnloidkpepgbcoegccpfpmabbkabafmnlbifmnimiimkikiekhmjdcnnpmabababmnlbifofmnimiioblhmicbpkijofabaepakaapapapapjblj 22DATAnaepmjpbpfabpfbemfpobkmimneebhnghpmaaoadaihhajaoiaagaemnleifmdidiekpdamnnpijcplkobmcojijelmjmnjhiimnpoebcbnmieccfjpgcbckflmnihfhkpdcfhpgmeafecmnflfkcbbnflmnikchmnpcefnhbbhcibnfmimnnocbnbofkpghgpccoopgdmolmdhjfmfjibgcicadilhhicfiiigcicagagmn 23DATAkjblmnmneanmdadehopgcaohdocaohcdhopfcbmjpmhodemnpiikpbhhcddkbdpnhhcddkbcpnhhmjdoanpocpmndkglnlllogcamimdojijdkmgpmlhmiofcboniamnjofeobmnjcidpoepmiodpofcmiodnbpofdminfmnafecmdfdifcacncnjncncncacalhminfdkmipmpfmnfkibckmipmolmngcicmkikifcbhiif 24DATAofpfmnpkifpbmkikifdcmipmmnpkifmdbiidmnhfginkomijmfofkphhcdalhilbmckcifccmcpmobmbmjcbnnpmmfabfkfkmnddiambmnddiahiibfhamhkanmkdkifigfhhocdmndlifmdmdifdkmgpmlhmicbanihmnlpijmnpcefnkopijnhmimnnccbofcbpiijccfjpgobmnmbelcklepmccklpmmjmnbhibmnbjie 25DATAagahmepkfgbbkfpmmnbicfmnopapbcbdmnpfikhobccbkopmmdbjccmnfmihmnoeidcpdcmkpmdcmhpkmnbgcbcbmepjabaiabafajhocdcdcddmaobemkediiofpmanifobmdcoigmnlpijcbclibmncaijmdpoebemgpgbgeccaobkbbkpepagfabbmipohocdljmipoanmcgligdoippocanchcigdocabcbdafmimdga 26DATAigpfnfofehdkfppglhhimmpkekcbgmpphocdkgngadmcjfigclhhmngofcobnbpbmjahamejgohdgfhchecagcgmgbgoglcagegjhdglakakedebfffeejepeoanakeggphcgngbhecaglgjgmgmhdcaeefdelahccmnomignlllogcaaoahmcpkijababahanmnlbifmnojigmnimiimiabdbenmnddiamndhifcgcoclhm 27DATAlfmcooigmjejepfhfaemfdeoejeifeedepebeceofceoegegefeeegeceoblfjcfcagggjgmgfgogbgngfccelgjgmgmccofdiaionbbghbinpmcdaihcbciiadiainjobolcbjppjmnipbiolmnfebhoghpmcelihmnlciikpehajdgbkaikpepajhopobkdhdpmcdoeodcjppjdpmddoeocbgcpgbbmnpmmnjedbcbjpid 28DATAmdpdiihlmnhkienbobhlpoaimcojbeboacmdojbecboaiidkmkpmlhmegbikccmapmmnjpibmnihhamnhkblmngdijmnpcgomkipihckmapmagabafeppoaibbimihdkmjpmnkbdfnlhmihjpoanmkdmijnfbbabifngblpoagncafecepcbjhibofckmipmolmdbdfnamblfhemgjhdhecccbdliemnjjikmnceccmnncif 29DATAmnceccmngaiemkpfihcbnkihmnegifmnpkiimngaiedoabmnhkiemnfpiinklaidolhjlamkldidkpfphjngiaephijlehpfmfhjnkbgiikpmgiaepmnlbiembpbnkcfiilbmcagiiababacanmdiaiekpehmfmnpaiimbcbjnpjhohalhmcbhiccdhohacdhalhmjdkmjpmjbcpepanmkhmihcbgjifagaimnkjblmdejii 30DATAhlmgaflkninbmjmniacaofolbhlhbhpfahfppbhlippfofbbhjiinfnchfcbnbobofnbpbpfagabafepajclclhnjdephmjkehpbobmjcbpjpmmnabijhipobchomjmnaneccbccilagcgdookohmnfeiekpgpaomamnhnfgmnpoebmdfmhlababadanmnleifmnipiimapodamcikiekpepmjdkmhpmdcmjpmdnmnblidmn 31DATAalefagcicbemplmnljeodcmipmmdlfefhmibfbiahbifboiggiicejidcniddbiecbmmpmnhnedademdmibhababafanmdiaieofmnakhdmneaidfjhiibamfhanmkljiemnnpijhhcdicmdamijojegfcefcaccmnikchmnjcidngebdcmgpmmipobimipoannbobmiofnfmnafecmdcdijdkmkpmdmdcmgpmmkepijmnfh 32DATAialhmacbdjicodmnfpiinkafecdnmccofgcdcdcdccmepmolmdpeiedkmkpmlhmadkmjpmlhmimnbiiakphhmnbjiebbnfpmmnjedbcbalibofmnbhibdoaiohmnaniankngidmnpfikmnoofhmneliemdhpijepdkmgpmlhhjmngbelmmalelmnpcgomipoblmklfijmnpcgomkkmijpoblmaobmnpeeocbkjidmdcmfmmn 33DATAikchdoekmnemecbbkfpmmnmbidcbnnpmdookohagajmnfeiecbopijccfjpgmjmndoifmniggknkopijmiaoafcbaoacbbaoagckaoaidkaoakcbaoalmncmiicbanieajfomcgeaebbboigcbkfidnfofmfmnpeeocbahabmnfiecmnalefmbcbpeigajajnhohnhohcbpbadmngdfhobmdcmfmdkjopjlhmkecikmndfbh 34DATAmelbieababacanmniaiemncmiimdapenckmcplmnjkifccmcplciacodabagabafainjajoddokamdldiacbcbilmnjofedoiackilpjmndliacbonicmnikchdomackjgpjmndliacbaiiamnikchckpepfmnebiacbbkijmnikchmnfmhldocaohcbfcicmjnblhminfofmnlnikcbafabmnfiecobmnikchcbccibmnca 35DATAijdnobmiofbbnjicnfofmdpkifdkmlpmpobemmfkibcbppppccmhpmcbmhpmdehomnbmieolmndpiemkmfiimnbjiemcneikmnjedbdocablbcmnpfikfodkmhpmmnpiikhdmdmlikdkmipmepihibepkpehcbfnpnajmjhldnpahkdnpadmmjddddofnfpfbbaeabbfbjhopoajmcnffabbiiidmdodfacaeeejfdelcafa 36DATAepfheffccaeldidfcadbcodacakldbdjdidhcaeihfghgpcaeggfhchcgfhjhcgbblfjchcaecebfdaaaaaa \ No newline at end of file diff --git a/clients/disk_power/Disk_Power.K85.pdd1 b/clients/disk_power/Disk_Power.K85.pdd1 new file mode 100644 index 0000000..1e73e25 Binary files /dev/null and b/clients/disk_power/Disk_Power.K85.pdd1 differ diff --git a/clients/disk_power/Disk_Power.K85.post-install.txt b/clients/disk_power/Disk_Power.K85.post-install.txt new file mode 100644 index 0000000..dcba6c5 --- /dev/null +++ b/clients/disk_power/Disk_Power.K85.post-install.txt @@ -0,0 +1,8 @@ +Next, if you haven't already, run this command: + + dl -vue -m 1 -i Disk_Power.K85.pdd1 + +This loads an image of the install disk, and dlplus will act as a +virtual drive with that disk inserted, which the installer requires. + +When the installer prompts to insert the disk, just hit [Enter]. diff --git a/clients/disk_power/Disk_Power.K85.pre-install.txt b/clients/disk_power/Disk_Power.K85.pre-install.txt new file mode 100644 index 0000000..905ca6d --- /dev/null +++ b/clients/disk_power/Disk_Power.K85.pre-install.txt @@ -0,0 +1,3 @@ +Type the following into BASIC on the Kyotronic KC-85: + + RUN "COM:98N1EN" diff --git a/clients/disk_power/Disk_Power.txt b/clients/disk_power/Disk_Power.txt new file mode 100644 index 0000000..9a17e95 --- /dev/null +++ b/clients/disk_power/Disk_Power.txt @@ -0,0 +1,118 @@ +Disk Power for KC-85 + +Excerpt, more info here: +http://tandy.wiki/TPDD_client:Disk_Power:KC-85 + +Disk Power requires both INSTAL.CO and the distribution disk to install. + +Here, INSTAL.CO has been repackaged into a BASIC loader which can be +bootstrapped over a serial cable, and the disk has been imaged with +pdd.sh so that dl2 can act as a virtual drive for sector access to it. + +--------------------------------------------------------------------------- + +Installation: Just run the following command: + + $ dl -vb Disk_Power.K85 && dl -vun -i Disk_Power.K85.pdd1 + +(both files are bundled with dl2 and will be loaded from /usr/local/lib/dl) + +When the installer on the KC-85 prompts to insert the disk, just hit [Enter]. + +--------------------------------------------------------------------------- + +Usage: + ++------+--------+--------+--------+--------+--------+--------+--------+--------+ +| DISK | NewDsk | Load | ALL | RAMDIR | Kill | List | Format | Menu | ++------+--------+--------+--------+--------+--------+--------+--------+--------+ +| RAM | ClrBAS | Save | ALL | DSKDIR | Kill | List | ClrPST | Menu | ++------+--------+--------+--------+--------+--------+--------+--------+--------+ + | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | + +--------+--------+--------+--------+--------+--------+--------+--------+ + +The top row are DISK operations which apply while in DISK view mode. +The bottom row are RAM operations which apply while in RAM view mode. + +DISK Operations: + + F1 - NewDsk - (re)-Read the (changed) disk directory. + + F2 - Load - Load a file from DISK to RAM. + + F3 - ALL - TODO/FIXME: I don't remember now, select all probably? + + F4 - RAMDIR - Switch to RAM View Mode + + F5 - Kill - Delete a file from the DISK. + + F6 - List - TODO/FIXME: I don't remember. + + F7 - Format - Format the disk. (note1) + + F8 - Menu - Exit out of Disk-Power back to the KC-85 main menu. + +RAM Operations: + + F1 - ClrBAS - Clear the BASIC program area to recover ram for loading files. + + F2 - Save - Save a file from RAM to DISK. + + F3 - ALL - TODO/FIXME: I don't remember now, select all probably? + + F4 - DSKDIR - Switch to DISK View Mode. (note1) + + F5 - Kill - Delete a file from RAM. + + F6 - List - TODO/FIXME: I don't remember. + + F7 - ClrPST - Clear the PASTE buffer area to recover ram for loading files. + + F8 - Menu - Exit out of Disk-Power back to the KC-85 main menu. + + +(note1) F4 Disk / F7 Format quirk: + + F4 will not enter DISK view unless there is a valid formatted disk + inserted to read. + + The F7 Format Disk function... + needed to create a formatted disk... + is in the DISK menu... + which requires a formatted disk to access... + + This means that it's not possible to format a new blank disk unless you + already have at least one other already-formatted disk available, just + to put in the drive first, just so that you can access the DISK + functions at all, where the Format function is. + + Once in DISK view mode, then you can swap in another disk and press + F7 to format it. + + Presumably the user was expected to always have the distribution disk + available if nothing else, because the software required the original + distribution disk to install or re-install after a crash. There was no + way to back up either the installed program or the whole original disk. + So, since crashes can happen at any time, you needed to carry the + actual distribution disk around with the computer at all times. + And so you could always use that to access the DISK menu if nothing else. + + In reality, today you can just use another TPDD client like DSKMGR on the + KC-85, or pdd.sh on a PC. This is only a theoretical limitation if it were + 1985, you were away from home, and had only your not-crashed KC-85 with + Disk-Power already installed, and a new disk. + +--------------------------------------------------------------------------- + +Printable reproductions of the keyboard template and disk label: +http://tandy.wiki/TPDD_client:Disk_Power:KC-85 + +Disk_Power_F-Keys_Template.odg and Disk_Power_F-Keys_Template.pdf contain +a printable reproduction of the template to label the F1-F8 buttons. + +Disk_Power_KC-85_disk.glabels is a disk label for the install disk using +https://github.com/jimevins/glabels-qt +and https://www.onlinelabels.com/ol775.htm (aka Avery 6490). + +Disk_Power_KC-85_disk_labels.pdf is a pdf print-to-file version of the +disk label that can be printed without the glabels program. diff --git a/clients/dskmgr/DSKMGR.100.pre-install.txt b/clients/dskmgr/DSKMGR.100.pre-install.txt index 90b9d9d..0bd2289 100644 --- a/clients/dskmgr/DSKMGR.100.pre-install.txt +++ b/clients/dskmgr/DSKMGR.100.pre-install.txt @@ -1,4 +1,3 @@ -Type the following in BASIC on the portable: +Type the following into BASIC on the TRS-80 Model 100/102: RUN "COM:98N1E" - diff --git a/clients/dskmgr/DSKMGR.200.pre-install.txt b/clients/dskmgr/DSKMGR.200.pre-install.txt index cd84cb7..cfb1d30 100644 --- a/clients/dskmgr/DSKMGR.200.pre-install.txt +++ b/clients/dskmgr/DSKMGR.200.pre-install.txt @@ -1,4 +1,3 @@ -Type the following in BASIC on the portable: +Type the following into BASIC on the TANDY Model 200: RUN "COM:98N1ENN" - diff --git a/clients/dskmgr/DSKMGR.K85.pre-install.txt b/clients/dskmgr/DSKMGR.K85.pre-install.txt index 16ab31b..905ca6d 100644 --- a/clients/dskmgr/DSKMGR.K85.pre-install.txt +++ b/clients/dskmgr/DSKMGR.K85.pre-install.txt @@ -1,4 +1,3 @@ -Type the following in BASIC on the portable: +Type the following into BASIC on the Kyotronic KC-85: RUN "COM:98N1EN" - diff --git a/clients/dskmgr/DSKMGR.M10.pre-install.txt b/clients/dskmgr/DSKMGR.M10.pre-install.txt index 25ed219..deeddfa 100644 --- a/clients/dskmgr/DSKMGR.M10.pre-install.txt +++ b/clients/dskmgr/DSKMGR.M10.pre-install.txt @@ -1,5 +1,4 @@ -Type the following in BASIC on the M10: +Type the following into BASIC on the Olivetti M10: CLEAR 0,60700 RUN "COM:98N1EN" - diff --git a/clients/pakdos/PAKDOS.100 b/clients/pakdos/PAKDOS.100 new file mode 100644 index 0000000..8fa7ab0 --- /dev/null +++ b/clients/pakdos/PAKDOS.100 @@ -0,0 +1,67 @@ +5 'PakDOS v1.2 [1-7-91] for M100 +6 '(C)1990 by James Yi [73327,1653] +7 'Disk file manager/compressor +10 CLEAR256:PRINT"Loading.." +20 FORM=64711TO65004:IFS$=""THENREADS$ +22 D=(ASC(MID$(S$,1))-97)*16+ASC(MID$(S$,2))-97:POKEM,D:S$=MID$(S$,3):C=C+D:NEXT:IFC<>42415THENGOSUB85:END +30 RESTORE100:CALL64711,,HIMEM:GOSUB80 +70 FORW=1TO500:NEXT:MENU +80 IFPEEK(64704)=0THENPRINT" Loaded":RETURN +85 PRINT" Cksm Error!":BEEP:RETURN +90 DATAofcbjdpmagaimnhhpnohhhcdafmcmnpmmnomcaofcbmapmagahmnakepcbmopkaoagmnmnpncknapkofciagecelcklcplmngnglnkccdpofbblaplonainj +91 DATAobbbmopkagagofmngjdenbmbmfmnmnpnoboddokamndjccmbobaimnilpnlhmecopncbmapmeomnilpnjbhhmjofonodnjbdbdmbaieeenonbdbdofonajnj +92 DATAobbjofbdbdnfmfabapaagagiciabmdfipnmnnjpnncfhpnbjbimnnjpnnkfapnblbjaimkgipnajnmfhdembnbbjolobninpniofonajnjmdecpnofcklipl +93 DATAhocdcclipllhcdcdcdcdcdmkhlpnobmjofnfcbmbpmboaihocddfpmklpnbpclhhhkbpfhbnmcjcpncligilahhhhknbobmjdgafofcbmepmhodnpcmdpnmn +94 DATAhhpnpockmcmhpnmnhhpnngdjhhdkmdpmclhhngdlobmjmnilpnhhcdalhilbmcmnpnmjofcbmfpmhocddfpcoipndgahmnilpnclbphhobmj +100 DATAPAKDOSCOKlxuL;@r>JGs>FGskk=Gk;>Gkc^i;*z*z*z*z*[^RXlpRYChNls`RHIGoYoDJYp@RZt\gPr>^Or<^rkiNlsPRWx`BmUXJGMXBwkiNLn;phEklHZZ +101 DATAor@RWl@azKneqxtxTyBHfUrbZFZZogeXv>Cbf]WW>MnUrAfNxtXYwbvNFDrGKXrGcT?=EtrGyXZPnsrxazlgjDaCmlXCi_ZnsrxPyhmEBWkn`X`rg;YrWIjt^QHF^VHF^Vu;LrmqgGixoY\RykgsxNy +103 DATAHMnmsQ[XCZEYzMnmsQs>n>X`rGRqpgZhXpNHvRVZXokJtxX^?sNHvRV@CZNYNkFtqGJo@ikOM\YQFYv[IHnlOuSszBxodCB=_Wrg;YrgZhXrC@CGiYJGJo@MNlC=_WV[;HNhryokkmsQCKmXn=shZ^ogGi +105 DATAP[kUvxkwKbILoxdCB=\zrgyYB]`JogyYB=`JC]_WBM;;C=_WB<H^btyKOnHXIqIK;=HnVrL;BrAKkkoUY?NmZCKmhmryKPnH +107 DATAXiJ;FxcZfNEXVhKJwk>A>cEdmHN@@YnLRiqQxXn;phIpoxdo^lt\ONryTyZyYBq^KNnxTImsrwMb>xfwCNHXbwHH>OrHbvrSR^Va;KnsiOn=YYnLYICA`AV` +108 DATAHZmH>PruONrb]ZnlErFaBKCpohhr\wHDBMHXvbx;YRnHxXxWbkrHbnrlbWn;pX?PoXv^_xAVoyX^_xAUk^?kV<QreWLr?pO?PoXvC +109 DATAW>mDGMqVgLvVcLJmv@sPvn;Hbarv@XJMG\Z^_xdDrGKX^RgCj[ZH^OrHR;sZUg;m;DrL?K;H +110 DATAz=sbxaCPoXmH>Or[`aCRohJ>Gga[f^?XZElg;iJ=GWUYVzH`^^;fF=SWBioR?Y?MTZv`BmZ]bXnIYI>K@qU?xoO[SRGiC>CV@NFroMIloXnIYi=K@qU?xoOu +111 DATAqWKAXhmS>Ytyj>EzTkZGbAHGFU?G^vM\B;w[RW?@UZpPldz_b_>KnIYIOy?qSf[E[lxEfgmEtjrhCrg^bR_WmxaW[gXCNmZnwVYnLYYRu;UCvBiZbYpuEFstfJmiEfttyHmdm>fs>JesHfftH +116 DATAZQuUngsHNutSrWVdJwj]rgzbrg[brW_b>]KtLRTn<]TmPQtDrgiarWNZfHx]rGdb^XnQxxkbFiEfmhD;?<[;oWDrog[bv>GbJgo]>PKFLoNNHoN\[ksHj^tH +117 DATA>QuOBOu\g^vH^XI\_yyAOK?SlXn=PMCQlX?@UZpHJWT\JWy;pgtYrwTd^XtPRgJBosRssHrorSRZnAfYsSFgg^RYntwXnd=iEqiZnoZtsGxqX`DBCC`ON@p?;Rt>QH +119 DATArotudxvcrgv\vJcEFysHzIuHzEC>BMQLjYv?p_EoRzEC?mi^FX_R@mcRPyuH>@Hs@FmPjYvNGlsfJwqfpgtYrWNZ^XnYKYnLxxkHKYnCXIqHR;s\cLrpoWIZ +120 DATAvN<>barHZQuSrWKdrWVdJWF_rWNZ^XntwXnd=Yn]COnu;YnNLio?LYnLxxkuKio\LiJKpGYbjBpkLlHDrGLFXPCHckrCf`JKpWNbfBpkLlk_YYnb`IqfrW\_ +121 DATArWVdJgY_rgC=\RgF[gZ^WYn_YioCMYmZLPLH<@CKLOnavXnNiYCfohCyoxdHret\SDt]kNrHnMtHf>sUVStyv\krMYndUYnWgYnjzHpLRiqZNYnjyXnu;izZ +122 DATACGp_Jwh_v^BH^Lt\KArA?\Z^sJ?EfJtyv\muNizZCw;`RjJKoGA`Bjl;pGA`R[kdNYnu;YnSOizZCg=`Jwh_rGKXB^NtHFQtOR=t\ +123 DATA;;;]cNrHbPtUZ@OHbEsecNrfrwN`f]hvF=IXbgYn]wAHf>s>zOtesNrHvet>R;sekNr>nPteOLr]cNrHFQtHZDseSvy`NONe[NrCGYkHRAs>VFsH^QtHFQtU +124 DATAjWtuSNrbeWD_rwm`B]`_F]JXBmEXF=JXfN?XZemwQYnTPiEwoxuHjmrL_L>>narHnMtHf>sO^LtMzVtH^QtHbPt>R=troGKXBlCXrggav;=CoWZafzsN;=_GI=we?DJm?Hcgy`bbz=CGFaJwt`rGKXjYCN +126 DATAmjmy>]kPUYD +127 DATArwsarWZafgNK`Wn^Vixr?mAXvnHLGDzA[[q=PmkdLNnxTIwyc]GNmjGTmzkcUiZbY>m^sZCSZUmHbgtCFxoarWma^gqUVyEDrGKXZOC@UZnUVYOr@rgKbRJpsi_IuSZJ?v<;HnjtlBIqHFstu;BrazptL;;;SJwuCoGKXrGqt?=uvJgcLoGiFHYnUYysDrGiFtbtztvCbfgabbWnVJPr`f=SWBkRzbKkC_gEqiZlH +131 DATAJzt<>XC;;Km`rgzb^KsDrGKXRxuA>ApnR[ok[iJUCwEcrGOoGmCDsbj^CwEcjMkC[>Or`RGPX`KtH>Du\NwqfuHrGufJWicVyHPB<;yRGpVjquWjxUfBWoDB;K<[;oDKON\;;;s@FkG[H;gYnV]ykA@MCpohZq>sX\cVy] +135 DATAsCrjE>WWrwvZfxoZB\^WrY;F;[ZyzjmNzX=bEjZdVy>=vRuT^[Z^spCQHMnM[YnLRiqh`ykjyXnuUYnhfIqHf>sURVuNszB=bUufrgKbNh[brGdbNHzeJGed +136 DATArgtYrwTdfhWeFNTWRYp@RW?alxHHFft\F]VWB\;;wEJ=h?=UFZuD[\zHCGtdr^z[G_lxfmKBrbxaCPoXn]dYnwqhJY +137 DATAoGA[B=TWBK?;ogTf>rCk@Zk\nxSfRJpaiYweOBrxpWXfnRnphiJZoGA[NXDhrgC=pGMbNhWeBIp<`YndUYnh`ykFbYCrohZr=GHHJtuu^@<>BDs\WNrjuvX? +138 DATAVFsbaWnhfIpAfIq=JfubxaCplhEqiZt@f>TWvJGGRou?nNvHZpuZpwPfZ`EplxrDpk>Jfu@rgfarGSdBgq`fiz[ +139 DATAEgrev>QHrotMRiuHRvt;rWKdrw=cfhCzwUmHJtuM^@sUJxryv\k@gYnWgYnLRiqwfykjyhEPoXt\KArOnS;YwDvN;HjdtsBLawf\z[W>k\;@LTbwRHfvuOhbGslXntlYnhji>AGwngF=]WF=[WF]ZW>QU]cCr\C;;tC=^WvnGkkakiEylxu\jRjENzuUsvC=V=vNclAyEgDgJLt> +143 DATAF?vUsfk\kyCN_ri=r>vsdfkxkysfC][WJms`f=\WJ]C?mXsHzHv>CXJgBjE;mHLHd=\WjiE=mHD`cWggj;=dk]IrlxNHzGvfBL;;weKF\[qMmIAwGw\gbkNu +144 DATAoBr>NEvrwQrecCr?pO?AmXvCW>mD +145 DATAGMqxTyZGRMvL[CrhvrCTRYxeOBrgYaBC_]oNoIZi>lTxtYCjp\kBoiCplXw\bWnphiJ;YuwDrGiFd]\WrGpts^abfM@XrGptsN>bJZmHfvuH^]vG[E;mxETgiEylhArpGBiJlCF[Rg=zPv>vSvtXUCslhkwpYCvlhJCWRmugBrJpgthrbEylxX^oGBiF]ZWjiE;mxE?pouCgiEtlxrKRYCr +147 DATAlhIslhj=RVvesBr<^kC];CrDNNsdCMYWJgbhfnXWzOnarYt\`AEZWZ_IxlXmoBrsD>ZWbWn;pXnynY?AmXv^_HLHd]]W^;kekCrtbWnRhiP^MUrwY +149 DATAftPU`MdkqlErE`qKtCNYmaE@?RQTic]FhNGZLnuPyo]nkcOQq_xMFztqRumt`bWQP;;[WPfeFRycWE@K[VCWdWb[]]OU;kf;=KC@;\]rUChM\Z@Ydgrf=hiOanuptN_X]dnh_U@hfWEjDEPuPvQdcMtNj`UPEPQbE`ZhJXiW`VjUQ]WXEhyx`JjdPe?OTv=CE<[N +151 DATAm`n@pmSEj__dCkdcGThLM_]NEfWA`MNEV`mtE^\XqmmQQanGndWqIV`__`Xm`HVTIFbNQ?v^Rf^m`r@=V`pGIDNY@LtKYcXFD@DDD\qQtrz diff --git a/clients/pakdos/PAKDOS.200 b/clients/pakdos/PAKDOS.200 new file mode 100644 index 0000000..a9c16c4 --- /dev/null +++ b/clients/pakdos/PAKDOS.200 @@ -0,0 +1,68 @@ +5 'PakDOS v1.2 [1-7-91] for T200 +6 '(C)1990 by James Yi [73327,1653] +7 'Disk file compression utility +10 CLEAR256:PRINT"Loading.." +20 FORM=63415TO63708:IFS$=""THENREADS$ +22 D=(ASC(MID$(S$,1))-97)*16+ASC(MID$(S$,2))-97:POKEM,D:S$=MID$(S$,3):C=C+D:NEXT:IFC<>41889THENGOSUB85:END +30 RESTORE100:CALL63415,,HIMEM:GOSUB80 +70 FORW=1TO500:NEXT:MENU +80 IFPEEK(63408)=0THENPRINT" Loaded":RETURN +85 PRINT" Cksm Error!":BEEP:RETURN +90 DATAofcbegphagaimnghpiohhhcdafmclnphmnajcmofcblaphagahmnmbfncbplpeaoagmnlnpickpnpeofciagecelckgfpgmnkiicnkdnemofbbgdpgonainj +91 DATAobbbplpeagagofmnlkebnbmbmfmnlnpioboddokamnkmcnmbobaimnhlpilhmebopicblapheomnhlpijbhhmjofonodnjbdbdmbaieeenonbdbdofonajnj +92 DATAobbjofbdbdnfmfabapaagagiciabmdeipimnmjpincehpibjbimnmjpinkeapiblbjaimkfipiajnmkiebmbnbbjolobninpniofonajnjmddcpiofckglpg +93 DATAhocdccglpglhcdcdcdcdcdmkglpiobmjofnfcblbphboaihocddfpmjlpibpclhhhkbpfhbnmcicpicligilahhhhknbobmjdgafofcblephhodnpcldpimn +94 DATAghpipockmclhpimnghpingdjhhdkldphclhhngdlobmjmnhlpihhcdalhilbmclnpimjofcblfphhocddfpcnipidgahmnhlpiclbphhobmj +100 DATAPAKDOSCOKlxvL;@r>ZGs>VGskk=Gk;>Gkc^i;*z*z*z*z*[^RXlpRYC?Sls`RHIGoYoDJYp@RZt\gPr>^Or<^rk@SlsPRWx`BmUXJGMXBwk@SLn;phEklHZZ +101 DATAor@RWl@azKneqxtxTyBHfUrbZFZZogeXv>Cbf]WW>MnUrAfNxtXYwbvNFDrGKXrw;V?m@mrGyXZPnsrxazlgjDaCmlXCYCZnsrxPH^ZrBWkTSn>TcIKohgHJ_\JhRs`JwEYz_Z^ +103 DATAowKAASBm^]rjowKAAykapStH>u^HbL\RxOnTOsRBHgNYZrC^CzwB]NDWNnYVSnNvXneuXnCvX?;WOnbuXYm?Lrb=W?;WKnFsh>uogA]dNxwZoNb]WCKmXn=sXnTOsX^O[kwuxkapSCH@^CKmh<=oGqYvN>>J_\\;DrHF[ry +105 DATAHMn>TS<=vhr>VQ^u?Lrb]WC?yZnyvXCQyKnyvXCOy[CMmXC<;[CKmX???nkftXn;pXngwhLHBptSv;^ftLSox\;DrASKnuA_Hi +106 DATAGmk?sqfH>Prbt]L][ErLc<;HjarytZ;DzeGUohJzFNAXrwwZfxpZfMAXzUjgDGVZzaJPHBpty;gobyhIdohp=xk>JSG[e_ErGPOsHRXtUfprH>OruONrbMJBbBMHXvbtztvXSJ=HHBctC^ZnJXIojm^GpoXmroGKXrWjZ^XHiT=;HZbQHVstHRtt[ +108 DATASOUgPqwDrGOXfNHXZEyHjr]jSY;\ONrqYBmr_WCpohZq>sXHBprpnX>ZrWJZBZmH>OrLOLrhJMqVgLvrHMqVcoGUYjRoxCl?yHMqbaWn;pHYl]w=ypGB[JmkgsrHmkv_W +110 DATABL;QU]WLryGkkY=iIMoxd?>>syCkkcthxrOII=giC[lXsMjLsLKnvhRYC>EYqHFttG;PSfLoNN<]Xls]G[gPGhWNFt?NrHFttE;PSf +111 DATALoNNdVnnNppEnHs`vzIcwbYlL;rvPBkZzpwdm[KnPlNsHvhtHNHBWRwoWeaBW?X@YvNG\ANWfmKAiy<@Ii=FOs`rysd +112 DATArwYYv^;>betJphLxEyMiKiOvLYOhBiKOGyzvtHL`FINSIIPvKyOzGYLFKyzP@YPE +113 DATAK_JdDBCNtNSqPACGtnV\LAC@t^W\HRT[K`JtrLABoOThxaWt<]Tp +114 DATAlAvMKOUnhACDxmnQsoV[wAW`DRSiLIHIxAX[SqVptAtrCOS_<]TdlQT[sQShPYIM@QV[KQUm<]TplAvKX`WdLRT[;bWjLRT^LRT_>ALdHrU[KQUm<]TplAv\ +115 DATALOUnhACaPBVg>BLdHrU[wQXoBBLdHrU[GAU\tqT`LI;e;ArfgmEf_zYr]WnMYYLmDrVmktHRttXd>c\wZsyXuPffg\bjyCvegbbvrCbEJh]Jwd] +116 DATArgjarwUdfHo]rGeb^Xnl`yk>FYn=[YnAYYnPYIC=`BX`HBCaDRT`bWnyUYnLxhqsFYncYIqHfpr>jisegEra;K?;=KntLOnAYiJkWykQFIO^ +117 DATArwJarGTdNXLdBmJirw>B@=bqVKMHBptU +118 DATAnrscSkuFgk=Fgk=HbAsHfFsHnot\NXLdrWfarWWdJWL^BWkBsHZ@tM^?tHRpr>v>tMRAty;PnwXyYO>T@r_Wn@cpP\o[=Z^gQy;PnLXiYO>T@>^utHjQuS +121 DATAjYnDMYnl`ykyLYnyw?Yli;myHmtH^utMnCtDz@P@p?O[;@LHfirHZtu\gMr]sNrbqWgaB]a_F=JXrGG`rgJ[fh]`vjB=jFtHretH>muHJzrORXtUJItHJvrH +122 DATAv>syz\kSNykvMiJYoGB`B=TWVK?yH]zKcgz_vjBEvJtyz\kBOYxy;LnWOYwA>OnWOY<=rItHv>sHnLtyz\kIOykvMYn;pX?soXvrHMqHNztKrGV`JGJ`rgT` +123 DATANXE_B=;;C]IXrWR`fh>ZpWf[f]IXjYnNPiEdTjCsoXmuHFmVogJ[JwO`f]JXrggaJW=[f=JXJGS`fM@XF]IXrgT`rwa[fMUqRILHd=IX^ksWtHrPtekNrfrwFZBLAHGwUYrGG`rgJ[NHB`Fxk`rGV`rWR`JWE_ZNn;pX?^oXnqUi>C[Kn=jYtHzotUj]tuONrbezEa^>m;Lr>F_tH>Ory;mk=rPnKpXCAXZ;CW[Z^sjECvzJCvJC=bdt@[Z>= +127 DATAfctG_WnbVYnfVKHBctkv^EmSoxmkox>netyXuHCJ]z\[xdDrWna^goRVYnHVImUvhtf_Wn;pxL\;rvHvht@czJa +128 DATArwpavZFERjt>Bjt]lys=n@cP<=JktDrGKXRWnAXYxOZbQuWkvxKgB;oG;bBZsSrg\bf>WWVyB?v<;?zQrDv>QLs>NLsnKLsNNL +129 DATAsNMH>PrR@=Wjrwpa^gzhCGQbJ]ZHNPrHNptSrwJargca>zHDrGPbB<;;[xkQ_Kn;pXnJWOCVUzkGBLnrdpsHvtt^bWnrd`Zazbj^WimYYYmH +130 DATAN;@CrwxMowxMWemjYiz[_Wn[pXnrdPnWWYnMYiO`uyc\kj[Yn\AoC^_[ZyI]kj[yE<^;I?_XmH>Or@>PQHNDufgYAfNWC>AwCffmTjzhqi\Y=]WqvPf=SW +132 DATAjYvNGLloY=TJl?xCg]cbGLHdMUqRI=?pOsDrGKXRYlOpP=`rG`cBIlQGCSd@G=?pOs> +133 DATAr?uH>Or@JYn]^ysH>HufrwIcrWocjykx]itrOX?kcZlOVl[fnh@>iYkLbW;;E;=Kb;LH@=;;[Rg;BWkDRZty>em\_Y=y>gmb_Y?PEjp;eWFdj=EAOWn\GsCk +134 DATAH=W^gYsTv[cpRiE`Tzkq_Y?[Ez=yclX^Wr;>oyCTjmu\rWicrW=g?Zngqmk]lMn;pxEQh]RfsRnQ[iz[ewQdFhQdJk;q;kuHzDu>FAF\ONryTJ[r +135 DATA@MhmF]^WzeGklXnnzhqRzX?ImXvJvrHvftHBjuSrgJ[fXidJ\zZCWfdjYnAXIpAYYncYIp; +136 DATAgykgaYnevXnf`iqqdiG`lXtPRWlLSArroghaBiCilX?=;kfwCoMCchxdb;Ayr[kcbYJyB[n;OjJNpGZbfX=eBzYyBKxlRjJlsH;mKArjE>XWZqV]OLrHVauH +137 DATAnVrysLnW;YC_lX;L;KnehIW^;RwzWu\WNryXFkoow`ffz[?GW`[BmHX +138 DATAzejrLgi[ZUmHBjuOfguSFwheZqV]OBre_ax`RgI_lhzkkWMfNGKhrwQfzPnVhyPeOBrZBWs=rfuuOArHjwsH>ku>NcuHFtt=@AT[G_O>JwheRWnmUYn_`YkU +139 DATAbiuy>ek]fiJSpWObFXuerWib>Wn@`YnJ[iqxkofDrw`fFx[?oW=[fh>ZpWf[rWfarwwZfxpZrgJ[fxpZvjB=bkuH>muHRXtU>ku>JvreOLr`B=TWBK?;oWMf +140 DATAf>XWzOCkcZnDhYkeOLrCGmuesAr?pOC`lhZ^_Wn;pHYl]w=HjpurHMn=oykGhYny;izK;Wny;ip;`Wn;pHYl]w=ypWYfrg;hJmkehYxy>\mDiizYcW]fvzB= +141 DATAFtupv>?HnmH;B=O?`XmZpgjfNpYm[BrHrBvHBzuISkkNniCCmhC;mhCylHSdD]]WB];;_^CGmhJBCnYWv>[moBrjE>ZW +142 DATAZqV]SCrwCnXWv];]_Brg`Wn^nIq>V;vulfkjkYlHfAv\jMnMqiEylxuhV=;]CCr`kaC?mhE;mhKFpwsgfhUgJk;>f=vesBrfByYxeGyff\j^CgEgJ\AVsfke +143 DATAky?`JgLgf\j=R=v^GLYvEWKg^bi=B?v^j]C=mxC^RiE?mxC]KCr\rwsgJ[omvDvrymVHvmoBrDrGSXf]]WNONLSCrh +145 DATA^kHCbkCTrbtzlWEhB<]WriZ^_XtpfMXWnuTY[KEMZLvwtI?bpbv\wQB=jKv]OBrlBYmHbruy;sdnbWnrd`EAmXnFW_JUYiEPoXnFW_JHWywDrgjfrGFiB[Lh +146 DATARWnGsio=qYn=riYnewWhJk;e;Crf_luesBrUWRn[sy?^g;YlEwShJg_hbrd\[Br=>Sv\gBry[kXDfnYWzOnerYZesBrrHMn[siCylxue;CrfKONf^kue_BrZ +147 DATA>Xt\WBru[BryEWihf]ZWBG=^C=[WbGH\b]Ctlxk]qiIrlxNHfYv`BQUc?KpHn[vMJYvHn[vMnXvT[lsdGykJrYoV_ZWZ_IxlXOrHFKvLSCrhJMq?p_EEmH=;f=^WbZmHjpuAWkwDR=RP@ZWotWEjFjJjwY`wEzdq`FPvMZsY +149 DATAOMvarVHjMfOE@qIQtQdoAEBTmNqOd[smEn@PWdX]tFUQjmbQWAA`mmagRUtJryMRIjw_ne=;;_fUPXtmB`Nd;=W>\F`Fk`MHJ[]^I?Og@`[f?NRua[CNKO;f +150 DATAIB^Ju[;A[TYV`MSs?OO_M>;Y@K;=d;COwQ>dPEwfkF`xYPcp`mWQjIZ]gNGXi`OfcX`NtHLdMjUrE@PEZuhOfMdmuOdwfxdn`fWvPnM_fNdpjjwtHhM`[EVR +151 DATA;LD;WMiWicQANt`_G@hVDdCDDDoqE +152 DATAnr diff --git a/clients/pakdos/PAKDOS.DOC b/clients/pakdos/PAKDOS.DOC new file mode 100644 index 0000000..9087f1f --- /dev/null +++ b/clients/pakdos/PAKDOS.DOC @@ -0,0 +1,258 @@ +PAKDOS.DOC -- Documentation for the disk file manager/archiver, + PAKDOS.100 and PAKDOS.200 v1.2 [1-7-91] +By James Yi [73327,1653] + + +___ What is it? ___ + + PAKDOS allows you to compress and combine Ram files to disk, in addition + to other routine disk file management tasks, such as Save, Load, Erase, etc. + Since files can be combined, more than 40 files can be stored per disk. + DO files get reduced by about %40-%60, BA and CO files %10-%30. + It runs on Model 100 or 200 with either PDD1 or PDD2. + + +___ How to load and run it ___ + +1. Download the program in the form of text file. There are two versions, + PAKDOS.100 for Model 100 and PAKDOS.200 for Tandy 200. + +2. Convert the text file to BA program file by going to BASIC and LOADing it. + If you get ?OM error, you can either save the text file to cassette or disk + and LOAD from there, or use DO2BA.100 or DO2BA.200 to convert it directly to + a BA file. + +3. Run the converted BA loader to create PAKDOS.CO. While being created, its + loading address is set according to the value of HIMEM; the address of its + last byte is set at HIMEM-1. If you are not worried about it conflicting + with other machine language programs in HIMEM, you can just load it at the + highest possible address by setting HIMEM to MAXRAM before running the + loader. HIMEM can be set by typing CLEAR 0,xxxxx + where xxxxx is the new HIMEM value. After it is created, you may delete + the loader. + +4. Run PAKDOS.CO. If it just beeps at you or gives an ?OM error, you need to + make space for it in HIMEM by setting HIMEM to at or below the load address + of PAKDOS.CO. The load address is found out by going to BASIC and + LOADMing the program by typing + LOADM "PAKDOS" + There will be displayed "Top", "End", "Exe" values; set HIMEM equal to "Top", + by typing + CLEAR 0,xxxxx + where xxxxx is the value of "Top". If you get ?OM error again, it means there + is not enough free Ram. + + +___ How to use it ___ + +At the command prompt "PakDOS:", enter a command in the following format: + + CMD FNM1 FNM2 ... -OPT1 -OPT2 ... + (Each item is seperated by blanks.) + +CMD is the command string. It is one character long. +FNM is the name of the file for the command to process. Enter as many FNMs + as are needed. +-OPT (option) is optional subcommand. For example, -L option shows, in + addition to the name of the file being processed, its size and date/time. + + +___ List of commands ___ + +Note: + 1. Although the command words shown below are spelled out, you need to + specify only the first letter. + + 2. Abbreviations for file name specifications: DFL = file on disk, + RFL = file in RAM, FNM = just any name. + + 4. Wildcards(*,?) are allowed for file name specifications when W is attached + to it. For example, RFLW means that wildcard substitution is allowed for + RFL. More about wildcards later. + + 3. Use of brackets([]) means that the item in it is optional. If a file name + specification is in [], it means that by omitting the file name, you can + pause before processing each file and press Y to process it, N to skip, or a + control character(^C, ESC, ENTER, etc.) to stop. + + 5. The following subcommands have common use in all of the commands that + allow them: + -I (Interact) gives a pause at each file to be processed, so you can + press Y to process it, N to skip, or a control character(^C, ESC, ENTER, etc.) + to stop. For example, to select only some DO files, specify *.DO and use -I. + It will then pause at every file with DO extension. + -L (Long format) displays in addition to the name of the file being + processed, its size, and where applicable, date/time and compressed + size. + -M (Move) deletes the file after it is processed. + -D (Date/time stamp) stamps the + date/time when creating a disk file. As a side effect, putting the date/time + on the file makes it unreadable by other disk related programs that do not + expect the date/time stamp. + -X (execute) executes DO, BA, or CO file after it is created in Ram, by + Load or Unpack command. DO and BA files load as files, but CO file does + not, i.e., it won't be on the Menu when you exit the CO file. If the load + address of CO file conflicts with that of PakDOS, you can still execute it, + but PakDOS will be overwritten. + + + Pack DFL [RFLW] [-S] [-M] [-I] [-L] [-D] + +compresses RFLW and stores them as DFL. Packed files are simply combined with + other packed files in DFL if DFL exists already. The default extension for + DFL is "PD". + -S (Skip compression) skips compression, when you want to just + combine files together without size reduction. + + + Unpack DFL [RFLW] [-I] [-L] [-X] + +decompresses RFLW from DFL and loads them to Ram. + + + View DFL [FNMW] [-L] + +lists packed files in DFL. + + + Omit DFL [FNMW] [-I] [-L] [-D] + +deletes packed files from DFL. Leave enough free Ram to buffer the DFL. + If there isn't enough free Ram, the DFL will be read from disk more than + once, in order to process it in parts. The more parts it has to be divided up + into, the longer it will take. + + + Save [RFLW] [-M] [-ADFL] [-I] [-H] [-D] [-L] + +saves RAM files to disk. + -ADFL option lets you append RFLW to DFL. + -H (remove Header) lets you remove the 6 bytes header of a CO type file to + save only the data part of it. This might come handy when for example, you + have Loaded a PD file from disk into Ram, and want to save it back onto + disk; -H removes the header that was attached to the PD file when it was + Loaded. + + + Load [DFLW] [-I] [-L] [-X] + +loads disk files to Ram. If the file extension's first letter is not one of + B for Basic, C for CO(binary) type, D for text type, it will load the file as + a CO type file, attaching to it a 6 bytes header needed to store a CO type + file in Ram. + + + DiskDirectory [FNMW] [-L] + +lists disk files. + + + Erase [DFLW] [-I] [-L] + +erases disk files. + + + Type [DFLW] [-Pnn] [-I] [-L] + +prints the content of disk files on screen. + -Pnn (print) outputs the file to printer, sending the printer code nn at + the end of each file, e.g., -P12 sends Form Feed, and just -P sends nothing. + + + Kill [RFLW] [-I] [-L] + +deletes RAM files. + + + Files [RFLW] [-L] + +lists RAM files. + + + Read [RFLW] [-L] + +prints content of Ram files on screen. + + + Quit + +exits the program. + + + Bye + +is a permanent exit, returning the HIMEM space occupied by PAKDOS to free + memory, and deleting PAKDOS.CO if it's in Ram. + + + HELP or any other nonexistent command shows the Help menu. + + +--- Other details --- + + ';' for command repeats the previous command. + + During files listing, press any key to pause, and then press a control + chr(^C, ESC, ENTER, etc.) to stop, or any other key to resume. + + Holding down SHIFT stops batch processing of files, letting the + current file finish, then stopping before processing the next file. + +Just press ENTER at the command prompt to clear the screen. + + If you use it with PDD2, you can add the prefix 0: or 1: to the file name to + specify which bank the file is in. Then the bank selection is set to that + bank for subsequent commands until you switch it back by using another prefix. + For example, + D 1: + lists directory of bank 1 and lets all subsequent operations take place in + bank 1. Or, + S 0:*.* + saves all Ram files to bank 0. + + Making a Ram file invisible makes it inaccessable by PakDOS. + + You can make CALLs to PakDOS to execute a single command and return. + The following example demonstrates this. It lists the disk files + directory in long format. + +10 LOADM"PakDOS" +20 PD=XXXXX:'"Top" address of PakDOS +30 C$="d -l"+CHR$(0):'Command string +40 C=VARPTR(C$):C=PEEK(C+1)+PEEK(C+2)*256 +50 CALL PD+3,,C +60 END + +Replace XXXXX in line 20 with the address of PakDOS's Top. When you CALL + PakDOS at Top, to run the program, or at Top+3, to execute a single command, + like the example above, all Basic variables are retained and you will be + returned to resume running the Basic program. The only case where a + variable might not be retained is when a string variable is assigned a single + constant literal string, like this: + +10 A$="hello" + +However this can be corrected by changing it to: + +10 A$=""+"hello" + or +10 A$="h"+"ello" + +The + forces the data of A$ to be located in a seperate variable storage + area, instead of being imbedded in the program line. + + If you up/download files using the built-in 300 baud modem and run out of +memory to hold all the files, you can use PakDOS without having to logoff + and relogon, by exiting the comm program, use PakDOS to save/load files + from disk, and resume the comm program. + +--- Wildcards --- + + * - Replaces a field...; *.* - any file; *.DO - any file with DO + extension; GR*.* - any file beginning with GR + + ? - Replaces a single letter...; ?ILE.* - file name with any first letter, + followed by "ILE" and any extension. ??????.?? is the same as *.* + + + END diff --git a/clients/teeny/D.100 b/clients/teeny/D.100 new file mode 100644 index 0000000..d689034 --- /dev/null +++ b/clients/teeny/D.100 @@ -0,0 +1 @@ +0'D.100 & WEENY.100 Ron Wiesen 0CLEAR99:GOSUB3 1?"0 for D.CO - DOS w/batch capability"TAB(40)"1 for WEENY.CO - World's teeniest DOS"TAB(40);:F$=INPUT$(1):F=ASC(F$)-48:IFF<0ORF>1THEN1ELSEF$="D.CO":IFFTHENF$="WEENY.CO" 2GOSUB6:END 3?"12 seconds..":FORN=64710TO64994:P=P+2:IFP>LEN(D$)THENREADD$:P=1 4D=(ASC(MID$(D$,P,1))-65)*16+ASC(MID$(D$,P+1,1))-65:POKEN,D:C=C+D:NEXT:IFC=41383THENRETURN 5GOSUB8:?"loader. Can't continue.":END 6?"End address for ";F$;:A=-1:INPUT" (Press ENTER for just below HIMEM)";A:CALL64710,F,A:F=F+1:E=PEEK(64704):IFETHEN?"Warning - ";:GOSUB8ELSE?"Loaded "; 7CALL4514,,64659:?".CO":RETURN 8?"Bad checksum on ";:BEEP:RETURN 9DATAMGDADCMAPMDCMBPMOFCKHMPGOLONCLCCMEPMCBJDPMFHMNDMPNCBMOPKMNDMPNOBNFAIMKBFPNCDAIMCPDPMCKPEPFOFCKNAPKEEENOBAIOFBBMOPKONEEEN 10DATAOBNJAICCMCPMOLCKNCPKHMLFMKBFPNBJCCNCPKMNEGCBMNIJCAMENJBPMNEGCBMNLJPNMNIJCAOLMNFADECDCDNBMNDPPNDCJJPMMNGBPNJCDCMAPMMJABAG 11DATAAAMFOFMNGBPNOBHHEHHKNKFFPNCLEOOLCKMCPMAJNJOLCDIAAPFHCDMBALHILBMCDPPNMJNFFEMNHOPNPOCKNCHJPNNGCFFHAPAPOGMAFPMNHOPNNGDALDNG 12DATAGLBINBMJCKMEPMHOLHMKJBPNHOPOCJCDCCMEPMMADODKMJABAFAAAJHONGIDMCKNPNCDLOMKJEPNABMAPMAKLHMKKNPNDKMBPMJGACCDHOMKICPNLHMCKNPN 13DATAMDJBPNMNOMCAOFCKNAPKABAGAAAJEEENCKLCPLNEGNGLNKCCDPOLCKLAPLAICCLAPLDOKAOBMNDJCCCBMOPKMDEACF 14DATA0#_#;#;#;#;#;g]_mg]##>#=kh#<$5$5#=yh#Euhd!J#Mwh#<"o&NP#<;&P8!=|8#_#a#<$H&N#=#ma#<$aa#Q#=8z#g8#a#<#U$`ai#l5!K&Pi#n5#c&P`8#) 15DATA &O5$6&N\A#f5x&O#X5$S&O#9kl#9ml#9ql#9sl#9{l#9}l#9"ol#9#1#Uwh!R5$^&N@#Eqh#_#h#Eoh8!)$F<##Qk4F!VQ#;3#9o.$J&N#<{h$Y#>$a$2$Y$)#b#> 16DATA !R$U52&N$a#>#8-,&N#J$R#Yp$[#b#p-;&N#<{h8$7$Q8i&N8i&N#jw8i&Nx-X&N!J#K#<|h#i#>$Y!R48!B&N8$Y$HE$<&N-$9&N$R#>4#Yp8!)&N8!L&N3Q[#)#)#)$).$J&N86$I8!1&N# 17DATA |$baqt8$D#Oqz#Q#;#>p-#]&O#Q#a#>$K8!L&N#b#U{hi}5#1&O$S!R4#9l#Yl8!)&N$R#>$N.}&O<#Yo8!)&N$L#>#\8$D#O.}&O8!J#;-$?&N#<$E&N#=#ma8O#;P8#Z&P#E#Fh#`#g$D$;#=shv$)#Wn#X-!3&O$2# 18DATA $9#>$1@#>$9#>$A$F$9#>$1$;$D#=qhV#=ohP8$H$FE$B&N#w.F&O#9n8$9&OP@#Yn8!)&N8#h&O!J#b#j#M{h 19DATA $R#EqhV#E!If#4#=!If4#Eoh#=!Kf4\#EohV#6L.#T#=#U!4gA#^[#Ym48#)&O-$?&N#U!4gi#]E$H&Ni#`=$H&N8!J#;5$6&N8#Z&P#j#Ym$l0@V8!8#<<$X$n$J$W!5$B,w#>#Fx-$S&P$X!O3@P8$7&O&N\A#f5#0&O#X5$[ 22DATA &O#9kl#9ml#9ql#9sl#9{l#9}l#9"ol#9#1#Ush!R5$f&N@#Emh#_#h#Ekh8!)$F<##Qk4F!VQ#;3#9o.$R&N#$a$2$Y$)#b#>!R$U5)&N$a#>#8-4&N#J$R#Yp$[ 23DATA #b#p-C&N#$Y!R48!J&N8$Y$HE$D&N-$A&N$R#>4#Yp8!B&N8!T&N3Q[#)#)#)$).$R&N86$I8!9&N#|$baqt8$D#Oqz#Q#;#>p-#e&O#Q#a#> 24DATA $K8!T&N#b#Uwhi}5#9&O$S!R4#9l#Yl8!B&N$R#>$N.#5&O<#Yo8!B&N$L#>#\8$D#O.#5&O8!J#;-$G&N#<$M&N#=#ma8O#;P8#b&P#E#Bh#`#g$D$;#=ohv$)#Wn#X-!;&O$2#<"o&P#4#4#4#4#4$Y`|#X&P@#>$9#>$1@#>$9#>$A$F$9#> 25DATA $1$;$D#=mhV#=khP8$H$FE$J&N#w.N&O#9n8$A&OP@#Yn8!B&N8$0&O!J#b#j#Mwh&P!If$[#A&P!5d#Q#54!J$R#> 26DATA $R#EmhV#E!If#4#=!If4#Ekh#=!Kf4\#EkhV#6L.#T#=#U!4gA#^[#Ym48#B&O-$G&N#U!4gi#]E$P&Ni#`=$P&N8!J#;5$>&N8#b&P#j#Ym$l0@V8!8#<<$X$n$J$W!5$B,w#>#Fx-$[&P$X!O3@P8$?&OLEN(D$)THENREADD$:P=1 -24 D=(ASC(MID$(D$,P,1))-65)*16+ASC(MID$(D$,P+1,1))-65:POKEN,D:C=C+D:NEXT:IFC=41383THENRETURN -28 GOSUB45:PRINT"loader. Can't continue.":END -30 PRINT"End address for ";F$;:A=-1:INPUT" (Press ENTER for just below HIMEM)";A -40 CALL64710,F,A:F=F+1:E=PEEK(64704):IFETHENPRINT"Warning - ";:GOSUB45ELSEPRINT"Loaded "; -44 CALL4514,,64659:PRINT".CO":RETURN -45 PRINT"Bad checksum on ";:BEEP:RETURN -50 DATA MGDADCMAPMDCMBPMOFCKHMPGOLONCLCCMEPMCBJDPMFHMNDMPNCBMOPKMNDMPNOBNFAIMKBFPNCDAIMCPDPMCKPEPFOFCKNAPKEEENOBAIOFBBMOPKONEEEN -55 DATA OBNJAICCMCPMOLCKNCPKHMLFMKBFPNBJCCNCPKMNEGCBMNIJCAMENJBPMNEGCBMNLJPNMNIJCAOLMNFADECDCDNBMNDPPNDCJJPMMNGBPNJCDCMAPMMJABAG -60 DATA AAMFOFMNGBPNOBHHEHHKNKFFPNCLEOOLCKMCPMAJNJOLCDIAAPFHCDMBALHILBMCDPPNMJNFFEMNHOPNPOCKNCHJPNNGCFFHAPAPOGMAFPMNHOPNNGDALDNG -65 DATA GLBINBMJCKMEPMHOLHMKJBPNHOPOCJCDCCMEPMMADODKMJABAFAAAJHONGIDMCKNPNCDLOMKJEPNABMAPMAKLHMKKNPNDKMBPMJGACCDHOMKICPNLHMCKNPN -70 DATA MDJBPNMNOMCAOFCKNAPKABAGAAAJEEENCKLCPLNEGNGLNKCCDPOLCKLAPLAICCLAPLDOKAOBMNDJCCCBMOPKMDEACF -100 DATA0#o#`#`#i$4#;p^Vmp^#&N4#T#S#i#L#_# -102 DATA #Qk4F!VQ#;3#9o.$K&N#$a$2$Y$)#b#>!R$U5r&O$a#>#8-l&O#J$R#Yp$[#b#p-{&O#$Y!R48R&N8$Y$HE$=&N-$)&N$R#>4#Yp8J&N8\&N3Q[#)#)#)$).$K&N86$I8<&N -103 DATA #|$baqt8$D#Oqz#Q#;#>p-$]&O#Q#a#>$K8\&N#b#Uwhi}5$1&O$S!R4#9l#Yl8J&N$R#>$N.#m&O<#Yo8J&N$L#>#\8$D#O.#m&O8!J#;-$@&N#<$F&N#=#ma8O#;P8$Z&P#E#Bh#`#g$D$;#=ohv$)#Wn#X-C&O$2#<#g&P#4#4#4#4#4$Y`|$P&P@ -104 DATA #>$9#>$1@#>$9#>$A$F$9#>$1$;$D#=mhV#=khP8$H$FE$C&N#w.#6&P#9n8!9&OP@#Yn8J&N8$h&O!J#b#j#Mwh$R#EmhV#E!If#4#=!If4#Ekh#=!Kf4\#EkhV -105 DATA #6L.#T#=#U!4gA#^[#Ym48$)&O-$@&N#U!4gi#]E$I&Ni#`=$I&N8!J#;5$7&N8$Z&P#j#Ym$l0@V8!8#<<$X$n$J$W!5$B,w#>#Fx-!S&P$X!O3@P8!7&OLEN(D$)THENREADD$:P=1 2D=(ASC(MID$(D$,P,1))-65)*16+ASC(MID$(D$,P+1,1))-65:POKEN,D:C=C+D:NEXT:IFC=41383THENRETURN 3GOSUB7:?"loader. Can't continue.":END 4?"(ENTER for just below HIMEM)":?"End address for ";F$;:A=-1:INPUTA 5CALL64710,F,A:F=F+1:E=PEEK(64704):IFETHEN?"Warning - ";:GOSUB7ELSE?"Loaded "; 6CALL4514,,64659:?".CO":RETURN 7?"Bad checksum on ";:BEEP:RETURN 8DATAMGDADCMAPMDCMBPMOFCKHMPGOLONCLCCMEPMCBJDPMFHMNDMPNCBMOPKMNDMPNOBNFAIMKBFPNCDAIMCPDPMCKPEPFOFCKNAPKEEENOBAIOFBBMOPKONEEEN 9DATAOBNJAICCMCPMOLCKNCPKHMLFMKBFPNBJCCNCPKMNEGCBMNIJCAMENJBPMNEGCBMNLJPNMNIJCAOLMNFADECDCDNBMNDPPNDCJJPMMNGBPNJCDCMAPMMJABAG 10DATAAAMFOFMNGBPNOBHHEHHKNKFFPNCLEOOLCKMCPMAJNJOLCDIAAPFHCDMBALHILBMCDPPNMJNFFEMNHOPNPOCKNCHJPNNGCFFHAPAPOGMAFPMNHOPNNGDALDNG 11DATAGLBINBMJCKMEPMHOLHMKJBPNHOPOCJCDCCMEPMMADODKMJABAFAAAJHONGIDMCKNPNCDLOMKJEPNABMAPMAKLHMKKNPNDKMBPMJGACCDHOMKICPNLHMCKNPN 12DATAMDJBPNMNOMCAOFCKNAPKABAGAAAJEEENCKLCPLNEGNGLNKCCDPOLCKLAPLAICCLAPLDOKAOBMNDJCCCBMOPKMDEACF 13DATA0#o#`#`#i$4#;p^Vmp^#&N4#T#S#i#L#_# 15DATA #Qk4F!VQ#;3#9o.$K&N#$a$2$Y$)#b#>!R$U5r&O$a#>#8-l&O#J$R#Yp$[#b#p-{&O#$Y!R48R&N8$Y$HE$=&N-$)&N$R#>4#Yp8J&N8\&N3Q[#)#)#)$).$K&N86$I8<&N 16DATA #|$baqt8$D#Oqz#Q#;#>p-$]&O#Q#a#>$K8\&N#b#Uwhi}5$1&O$S!R4#9l#Yl8J&N$R#>$N.#m&O<#Yo8J&N$L#>#\8$D#O.#m&O8!J#;-$@&N#<$F&N#=#ma8O#;P8$Z&P#E#Bh#`#g$D$;#=ohv$)#Wn#X-C&O$2#<#g&P#4#4#4#4#4$Y`|$P&P@ 17DATA #>$9#>$1@#>$9#>$A$F$9#>$1$;$D#=mhV#=khP8$H$FE$C&N#w.#6&P#9n8!9&OP@#Yn8J&N8$h&O!J#b#j#Mwh$R#EmhV#E!If#4#=!If4#Ekh#=!Kf4\#EkhV 18DATA #6L.#T#=#U!4gA#^[#Ym48$)&O-$@&N#U!4gi#]E$I&Ni#`=$I&N8!J#;5$7&N8$Z&P#j#Ym$l0@V8!8#<<$X$n$J$W!5$B,w#>#Fx-!S&P$X!O3@P8!7&OLEN(D$)THENREADD$:P=1 -24 D=(ASC(MID$(D$,P,1))-65)*16+ASC(MID$(D$,P+1,1))-65:POKEN,D:C=C+D:NEXT:IFC=40895THENRETURN -28 GOSUB45:PRINT"loader. Can't continue.":END -30 PRINT"End address for ";F$;:A=-1:INPUT" (Press ENTER for just below HIMEM)";A -40 CALL-2122,F,A:F=F+1:E=PEEK(-2128):IFETHENPRINT"Warning - ";:GOSUB45ELSEPRINT"Loaded "; -44 CALL4556,,-2234:PRINT".CO":RETURN -45 PRINT"Bad checksum on ";:BEEP:RETURN -50 DATA MGDADCLAPHDCLBPHOFCKGHOPOLONCLCCLEPHCBEGPHFHMNCMPICBPLPEMNCMPIOBNFAIMKAFPICDAIMCODPHCKLEOOOFCKPNPEEEENOBAIOFBBPLPEONEEEN -55 DATA OBNJAICCLCPHOLCKPPPEHMLFMKAFPIBJCCPPPEMNGCCMMNKGCLMEMOCKMNGCCMMNKJPIMNKGCLOLMNKBEBCDCDNBMNCPPIDCEMPHMNFBPIJCDCLAPHMJABAG -60 DATA AAMFOFMNFBPIOBHHEHHKNKEFPICLEOOLCKLCPHAJNJOLCDIAAPFHCDMBALHILBMCCPPIMJNFFEMNGOPIPOCKNCGJPINGCFFHAPAPOGMAFPMNGOPINGDALDNG -65 DATA GLBINBMJCKLEPHHOLHMKIBPIHOPOCJCDCCLEPHMADODKMJABAFAAAJHONGIDMCJNPICDLOMKIEPIABLAPHAKLHMKJNPIDKLBPHJGACCDHOMKHCPILHMCJNPI -70 DATA MDIBPIMNAJCMOFCKPNPEABAGAAAJEEENCKGFPGNEKIICNKDNEMOLCKGDPGAICCGDPGDOKAOBMNKMCNCBPLPEMDKFDC -100 DATA0#o#`#`#i$4#;0VVm0V#$a$2$Y$)#b#>!R$U5A&G$a#>#8-;&G#J$R#Yp$[#b#p-J&G#$Y!R4#Yp8_&H8+&G3Q[#)#)#)$).#K&G8!P$b8$l&G#|$MZqt8!U#\qz#Q#;#>p-#]&H#Q#a#>$K8+&G#b#Ugbi}5#1&H$S!R4#9l#Yl8_&H$R#>$N.}&H<#Yo8_&H$L#>#\8!U#\.}&H87#F-#@&G#<#F&G#=#OZ8l#GP8$c&G#E#2c#`#g$D$;#=_bv$)#Wn#X-!3&H$2#<#0&I#4#4#4#4#4$Y`|#Y&I@#>$9#>$1@ -104 DATA #>$9#>$A$F$9#>$1$;$D#=]bV#=[bP8!C$]E#C&G#w.F&H##Qk4F4Q$[+#9o.#K&G8g&H8#4$`E#=&G-#)&G$R#>4!;#R&I$>a+#?&I$$R#E]bV#E$a4\ -105 DATA #E[bV#6L.!G#H8#)&H-#@&G#U#gbi#]E#I&Gi#`=#I&G87#F5#7&G8$c&G#j#Ym$l0@V8;#G<$X$n$J$W!5$B,w#>#Fx-$S&I$X!O3@P8$7&HLEN(D$)THENREADD$:P=1 2D=(ASC(MID$(D$,P,1))-65)*16+ASC(MID$(D$,P+1,1))-65:POKEN,D:C=C+D:NEXT:IFC=40895THENRETURN 3GOSUB7:?"loader. Can't continue.":END 4?"End address for ";F$:?"(Press ENTER for just below HIMEM)":A=-1:INPUTA 5CALL-2122,F,A:F=F+1:E=PEEK(-2128):IFETHEN?"Warning - ";:GOSUB7ELSE?"Loaded "; 6CALL4556,,-2234:PRINT".CO":RETURN 7?"Bad checksum on ";:BEEP:RETURN 8DATAMGDADCLAPHDCLBPHOFCKGHOPOLONCLCCLEPHCBEGPHFHMNCMPICBPLPEMNCMPIOBNFAIMKAFPICDAIMCODPHCKLEOOOFCKPNPEEEENOBAIOFBBPLPEONEEENOBNJAICCLCPHOLCKPPPEHMLFMKAFPIBJCCPPPEMNGCCMMNKGCLMEMOCKMNGCCMMNKJPIMNKG 9DATACLOLMNKBEBCDCDNBMNCPPIDCEMPHMNFBPIJCDCLAPHMJABAGAAMFOFMNFBPIOBHHEHHKNKEFPICLEOOLCKLCPHAJNJOLCDIAAPFHCDMBALHILBMCCPPIMJNFFEMNGOPIPOCKNCGJPINGCFFHAPAPOGMAFPMNGOPINGDALDNGGLBINBMJCKLEPHHOLHMKIBPI 10DATAHOPOCJCDCCLEPHMADODKMJABAFAAAJHONGIDMCJNPICDLOMKIEPIABLAPHAKLHMKJNPIDKLBPHJGACCDHOMKHCPILHMCJNPIMDIBPIMNAJCMOFCKPNPEABAGAAAJEEENCKGFPGNEKIICNKDNEMOLCKGDPGAICCGDPGDOKAOBMNKMCNCBPLPEMDKFDC 11DATA0#o#`#`#i$4#;0VVm0V#$a$2$Y$)#b#>!R$U5A&G$a#>#8-;&G#J$R#Yp$[#b#p-J&G#$Y!R4#Yp8_&H8+&G3Q[#)#)#)$).#K&G8!P$b8$l&G#|$MZqt8!U#\qz#Q#;#>p-#]&H#Q#a#>$K8+&G#b#Ugbi}5#1&H$S!R4#9l#Yl8_&H$R#>$N.}&H<#Yo8_&H$L#>#\8!U#\.}&H87#F-#@&G#<#F&G#=#OZ8l#GP8$c&G#E#2c#`#g$D$;#=_bv$)#Wn#X-!3&H$2#<#0&I#4#4#4#4#4$Y`|#Y&I@#>$9#>$1@ 15DATA #>$9#>$A$F$9#>$1$;$D#=]bV#=[bP8!C$]E#C&G#w.F&H##Qk4F4Q$[+#9o.#K&G8g&H8#4$`E#=&G-#)&G$R#>4!;#R&I$>a+#?&I$$R#E]bV#E$a4\ 16DATA #E[bV#6L.!G#H8#)&H-#@&G#U#gbi#]E#I&Gi#`=#I&G87#F5#7&G8$c&G#j#Ym$l0@V8;#G<$X$n$J$W!5$B,w#>#Fx-$S&I$X!O3@P8$7&HT%THENCLEAR0,T%:RUN -3READD$:IFD$="END"THEN7 -4FORP%=1TOLEN(D$)STEP2:U$=MID$(D$,P%,1):L$=MID$(D$,P%+1,1) -5B%=16*INSTR(H$,U$)-17+INSTR(H$,L$) -6POKEI%,B%:?".";:S!=S!+B%:I%=I%+1:NEXT:GOTO3 -7READD$:IFS!-VAL(D$)THEN?"Checksum Error":END -8IFFRE(0)<7+E%-T%THENEND -9SAVEM"TEENY",T%,E%,T%:END -10DATA-3323,-2577 -11DATA210000392206FD215A5A220AFD2A06FDF9AF3208FD2112F3E521AFF3CDA511326DFFCD3946216EF3 -12DATA2255F62189F63622CD1E4CCD5A213A88F6FE51CAA257FE53CA88F5F5CD5FF4CA5CF3F1D64BCA4DF4 -13DATA3DCAB8F41E00011E02011E06011E08011E10011E12011E14011E163A08FDB7CA84F3D52A02FD444D -14DATA2A00FDCDA46BD12197F31600197EE7D7E721CAF3CDA511C312F3534E46464E52434D494F57504446 -15DATA4E4441454F4D464C4E4D3E2043204646464646462E58582028433D4B4C5351290D0A3E2000204572 -16DATA720D0A00162E1B7AB3C2D3F3C939384E3144210CFD77233600C9DBBBE620C91E04C370F3210CFD7E -17DATA2386577E5F4723B77ACA07F486231DC201F42F773E058047210AFD7ECD396EDA62F3CDE7F32305C2 -18DATA10F4210CFDCD9F76CD3EF4CD3EF44F0CCD3EF40DC22DF4AF30210DFD4E237EB7C9CDE7F3CD856DDA -19DATA62F3C25FF37723C93E05CDDFF3CDF1F3C8E6F01F1F1F5FC370F3CD636FCDD1F321DAF337CDF0173E -20DATA07CD4FF4AFCDDFF3361A23118AF60609CD8334060F36202305C282F436462370CDF1F3473A0CFDFE -21DATA12CA56F478B7C91E013E01CDDFF3772373C352F4D13E04CDDFF3712341CD8334C352F4CDC120C265 -22DATAF3216BF32255F6CDF620E5CD7FF52A27FD454C69602204FD0B5F3C033DC2D8F457214CF519191919 -23DATA197EF51175F5D5235E2356D5235E23666B5E235660692202FDEB2200FDE5CD726BDA68F32108FD34 -24DATACD34F52A04FDEBE13A0CFD4F06007AB379320CFDC8B7FC39F51BE5210EFD097EE177230CC31BF51E -25DATA03CD9EF4E5D53E03CDDFF3CD8DF4AF474F320CFDD1E1C9A06EF5B6FBC05BF5B4FB805EF59DF9361A -26DATAC9AF7723772A02FDEB2AB4FB1922B4FBC92A00FD22B6FBC9F12A00FDEB1BE1C352223A9FFCD643F0 -27DATA3E02C9CD5FF4C265F33A9FFCFE42DA6EF3FE45D26EF3CDC120CA5CF3CD7FF54F3E0291C5D5EBCDB6 -28DATA21D17D936F7C9A67C10C232B0DC2B8F57DB4C8D5E5CD9CF4D1E1CDCFF53E02C34FF4E5E5218000DF -29DATAD2EBF54D44E109E3EB0180FF09E5CDAAF4D1E1C3CFF54BE1C3A9F4 -30DATAEND,85504 +0'TEENY installer for Olivetti M10, USA variant (c) 2015 Ron Wiesen Technical Products, based on TINY by Acroatix 0CLS:?"Installing TEENY...";:CLEAR99:S!=0:H$="0123456789ABCDEF":FORB%=0TO7:S!=S!+PEEK(B%+32496):NEXT:IFS!-624THEN?"Not a North American Olivetti M-10":END 1READT%,E%:I%=T%:S!=T%+E%+T%:IFHIMEM-2^16>T%THENCLEAR0,T%:RUN 2?".";:READD$:IFD$="END"THEN6 3FORP%=1TOLEN(D$)STEP2:U$=MID$(D$,P%,1):L$=MID$(D$,P%+1,1) 4B%=16*INSTR(H$,U$)-17+INSTR(H$,L$) 5POKEI%,B%:S!=S!+B%:I%=I%+1:NEXT:GOTO2 6READD$:IFS!-VAL(D$)THEN?"Checksum Error":END 7IFFRE(0)<7+E%-T%THENEND 8SAVEM"TEENY",T%,E%,T%:CLEAR:END 9DATA-3323,-2577 10DATA210000392206FD215A5A220AFD2A06FDF9AF3208FD2112F3E521AFF3CDA511326DFFCD3946216EF32255F62189F63622CD1E4CCD5A213A88F6FE51CAA257FE53CA88F5F5CD5FF4CA5CF3F1D64BCA4DF43DCAB8F41E00011E02011E06011E08011E10011E12011E14011E163A08FDB7CA84F3D52A02FD444D 11DATA2A00FDCDA46BD12197F31600197EE7D7E721CAF3CDA511C312F3534E46464E52434D494F575044464E4441454F4D464C4E4D3E2043204646464646462E58582028433D4B4C5351290D0A3E2000204572720D0A00162E1B7AB3C2D3F3C939384E3144210CFD77233600C9DBBBE620C91E04C370F3210CFD7E 12DATA2386577E5F4723B77ACA07F486231DC201F42F773E058047210AFD7ECD396EDA62F3CDE7F32305C210F4210CFDCD9F76CD3EF4CD3EF44F0CCD3EF40DC22DF4AF30210DFD4E237EB7C9CDE7F3CD856DDA62F3C25FF37723C93E05CDDFF3CDF1F3C8E6F01F1F1F5FC370F3CD636FCDD1F321DAF337CDF0173E 13DATA07CD4FF4AFCDDFF3361A23118AF60609CD8334060F36202305C282F436462370CDF1F3473A0CFDFE12CA56F478B7C91E013E01CDDFF3772373C352F4D13E04CDDFF3712341CD8334C352F4CDC120C265F3216BF32255F6CDF620E5CD7FF52A27FD454C69602204FD0B5F3C033DC2D8F457214CF519191919 14DATA197EF51175F5D5235E2356D5235E23666B5E235660692202FDEB2200FDE5CD726BDA68F32108FD34CD34F52A04FDEBE13A0CFD4F06007AB379320CFDC8B7FC39F51BE5210EFD097EE177230CC31BF51E03CD9EF4E5D53E03CDDFF3CD8DF4AF474F320CFDD1E1C9A06EF5B6FBC05BF5B4FB805EF59DF9361A 15DATAC9AF7723772A02FDEB2AB4FB1922B4FBC92A00FD22B6FBC9F12A00FDEB1BE1C352223A9FFCD643F03E02C9CD5FF4C265F33A9FFCFE42DA6EF3FE45D26EF3CDC120CA5CF3CD7FF54F3E0291C5D5EBCDB621D17D936F7C9A67C10C232B0DC2B8F57DB4C8D5E5CD9CF4D1E1CDCFF53E02C34FF4E5E5218000DF 16DATAD2EBF54D44E109E3EB0180FF09E5CDAAF4D1E1C3CFF54BE1C3A9F4 17DATAEND,85504 \ No newline at end of file diff --git a/clients/teeny/TEENY.M10.pre-install.txt b/clients/teeny/TEENY.M10.pre-install.txt index 16ab31b..821611e 100644 --- a/clients/teeny/TEENY.M10.pre-install.txt +++ b/clients/teeny/TEENY.M10.pre-install.txt @@ -1,4 +1,3 @@ -Type the following in BASIC on the portable: +Type the following into BASIC on the Olivetti M10: RUN "COM:98N1EN" - diff --git a/clients/teeny/TEENY.NEC.pre-install.txt b/clients/teeny/TEENY.NEC.pre-install.txt index bafc1b3..62d9881 100644 --- a/clients/teeny/TEENY.NEC.pre-install.txt +++ b/clients/teeny/TEENY.NEC.pre-install.txt @@ -1,3 +1,3 @@ -Type the following in BASIC on the portable: +Type the following into BASIC on the NEC PC-8201/PC-8300: RUN "COM:9N81XN" diff --git a/clients/tiny/TINY.100 b/clients/teeny/TINY.100 similarity index 100% rename from clients/tiny/TINY.100 rename to clients/teeny/TINY.100 diff --git a/clients/tiny/TINY.100.post-install.txt b/clients/teeny/TINY.100.post-install.txt similarity index 100% rename from clients/tiny/TINY.100.post-install.txt rename to clients/teeny/TINY.100.post-install.txt diff --git a/clients/teeny/TINY.100.pre-install.txt b/clients/teeny/TINY.100.pre-install.txt new file mode 100644 index 0000000..26ea8be --- /dev/null +++ b/clients/teeny/TINY.100.pre-install.txt @@ -0,0 +1,3 @@ +Type the following into BASIC on the TRS-80 Model 100/102 + + RUN "COM:98N1E" diff --git a/clients/teeny/ddoc.do b/clients/teeny/ddoc.do new file mode 100644 index 0000000..d2f0d33 --- /dev/null +++ b/clients/teeny/ddoc.do @@ -0,0 +1,114 @@ +Documentation for D.CO and WEENY.CO by Ron Wiesen + + + General + +D.BA is a relocating loader which contains two disk file transfer programs: D.CO and WEENY.CO. The D.CO and WEENY.CO programs are descendents from a family of small size disk file transfer utilities. The family progeny follows. + +"In the beginning, God (or somebody) said 'Let there be TINY.' TINY beget TEENY. TEENY beget WEENY. WEENY beget D." A yellow polka-dot bikini played a role in all the begettin' but it was a minor role. + +This documentation covers WEENY and D in depth, particularly with respect to their interface with other programs rather than direct command use by an operator. For operator usage, refer to file TEENYD.DO and note that there is a minor difference in the prompt. + + + Attributes + +The distinguishing attributes of the family are listed in the table below. + ++-----+----+-----------+----+---------+ +|Name |Size|HIMEM-Range|Bufr|Terminate| ++-----+----+-----------+----+---------+ +|TINY |760 |62200-62959|No |MENU | +|TEENY|747 |Relocatable|No |MENU | +|WEENY|737 |Relocatable|Yes |MENU | +|D. |756 |Relocatable|Yes |Return | ++-----+----+-----------+----+---------+ + + + HIMEM Range + +TINY is a fixed allocation program. In a COmmand file format, the 6-byte allocation header of TINY.CO defines a TOP address of 62200, an object module length of 760 bytes (END of 62959), and an EXEcution entry address of 62200. + +TEENY and its progeny are relocatable and are necessarily enclosed in relocating loaders. TEENY.BA encloses TEENY.CO; D.BA encloses D.CO and WEENY.CO. The relocating loaders prompt you for an END address and then create a COmmand file with machine language code and allocation header that correspond to the END address that you specify. When you LOADM or RUNM the COmmand file, an image of the object module is loaded (copied) into HIMEM protected memory. + + + Buffer + +WEENY and D are command buffered via the Typeahead buffer. So other programs (BASIC or machine language) can drive WEENY and D by loading "simulated" keystrokes into the Typeahead buffer and then calling the HIMEM image of WEENY or D. + +The capacity of the Typeahead buffer is 32 keystrokes. All WEENY and D commands must terminate with a carriage return (CR). For an 11-character command, 12 simulated keystrokes must be loaded into the Typeahead buffer as shown below. + +00000000011C11111112222222222333 +12345678901R34567890123456789012 +++++++++++++ +L MYFILE.DO + +Because WEENY and D remember file names of prior commands, there is more than enough capacity in the Typeahead buffer to hold a 4-command Load/Kill/Save/Quit sequence which consists of 20 simulated keystrokes as shown below. Note that the Quit command includes a 1-character file name (Q) that guarantees termination regardless of the outcome (e.g., file name error) of the commands that precede it. + +00000000011C1C1C111C222222222333 +12345678901R3R5R789R123456789012 +++++++++++++++++++++ +L MYFILE.DO + K + S + Q Q + + + Terminate + +WEENY and D differ in how they terminate. In fact, this is the only functional difference between them. For the Quit command: WEENY terminates to the main MENU, D terminates by Returning control to the program that Called it. + + + WEENY + +Once a program Calls WEENY, the calling program can not regain control. Considering the capacity of the Typeahead buffer and the way WEENY terminates, a program that Calls WEENY can govern only a few memory/disk file transfers and then one of two things happens. + +1. Where a Quit command is present in the Typeahead buffer, TEENY terminates to the main MENU. + +2. Where no Quit command is present in the Typeahead buffer, control remains in WEENY. This allows actual keystrokes by the operator to govern additional file transfers. Eventually the operator issues a Quit command and WEENY terminates to the main MENU. + + + D + +A program that Calls D always regains control. Control returns to D in one of two ways. + +1. Where a Quit command is present in the Typeahead buffer, D returns immediately and the operator has no opportunity to intervene. + +2. Where no Quit command is present in the Typeahead buffer, control remains in D and the operator governs additional file transfers. Eventually the operator issues a Quit command and D returns control to the program that called it. + + + Using D as a Called Subroutine + +A program (BASIC or machine language) can Call D and be assured of regaining control as long as the program appends a guaranteed Quit command (Q_Q) to the other command or commands that it places in the Typeahead buffer. The capacity limit of the Typeahead buffer must be considered. + +Another consideration is that D "remembers" file names of prior commands only back to the time it was called. In other words, D "forgets" file names from prior invocations. + +A program that takes these two considerations into account can use D to govern an unlimited amount of memory/disk file transfers. The BASIC program DBATCH.BA is a batch file driver for D. It supplies a Q_Q command within the capacity limit of the Typeahead buffer but it relies on the operator to construct 1-command per line batch files (.DO files) with explicit file names. Of course you can modify DBATCH.BA to include its own "file name memory" so that an explicit file name is needed only in the lead command line of a multi-command sequence. DBATCH.BA is extensively documented in the DBDOC.DO file; the DBDEMO.DO batch file provides a simple demonstration. + + + Advantage of WEENY Over D + +Among all members of the family of small disk file transfer utilities, WEENY is the smallest. It requires 737 bytes of HIMEM memory, so it claims the title of the "World's teeniest DOS." Because it can interface with other programs, WEENY qualifies (barely) as a Disk Operating System (DOS) but D is a better DOS. + +Where an image of the highest possible memory allocation object module of WEENY (62223-62959) is in HIMEM memory, WEENY can't be beat! The maximum size file that WEENY can transfer is 28974 bytes. The chart below shows the configuration for the maximum size file transfer. + +=====:======:=====:========:=====:===== + : : : :Paste:Nonam +LoMem:MAXRAM:HIMEM:MAXFILES:bufr :BASIC +=====:======:=====:========:=====:===== +32768:62960 :62223:0 :Empty:Empty +.....:......:.....:........:.....:..... + +The amount of free memory reported at the main MENU under this configuration is: + +29168 Bytes - prior to file Load, +00194 Bytes - for file Save. + +To maintain this configuration and Load the maximum size file from disk, invoke BASIC and issue the following command which invokes WEENY. + +CLEAR0,HIMEM:CALLHIMEM + +The CLEAR0,HIMEM phrase is needed to release the 256 bytes of string space which is reserved by BASIC each time it's invoked. + +Where the maximum size file is present in memory, less than 256 bytes of memory are free before BASIC is invoked. In this case BASIC reserves no string space when it's invoked, so no CLEAR phrase is needed. To Save the maximum size file to disk, invoke BASIC and issue the following command which invokes WEENY. + +CALLHIMEM diff --git a/clients/tiny/tindoc.do b/clients/teeny/tindoc.do similarity index 100% rename from clients/tiny/tindoc.do rename to clients/teeny/tindoc.do diff --git a/clients/tiny/TINY.100.pre-install.txt b/clients/tiny/TINY.100.pre-install.txt deleted file mode 100644 index 90b9d9d..0000000 --- a/clients/tiny/TINY.100.pre-install.txt +++ /dev/null @@ -1,4 +0,0 @@ -Type the following in BASIC on the portable: - - RUN "COM:98N1E" - diff --git a/clients/ts-dos/DOS100.CO b/clients/ts-dos/DOS100.CO new file mode 100644 index 0000000..0f9b363 Binary files /dev/null and b/clients/ts-dos/DOS100.CO differ diff --git a/clients/ts-dos/DOS200.CO b/clients/ts-dos/DOS200.CO new file mode 100644 index 0000000..7deaca7 Binary files /dev/null and b/clients/ts-dos/DOS200.CO differ diff --git a/clients/ts-dos/DOSNEC.CO b/clients/ts-dos/DOSNEC.CO new file mode 100644 index 0000000..649aa5e Binary files /dev/null and b/clients/ts-dos/DOSNEC.CO differ diff --git a/clients/ts-dos/SAR100.CO b/clients/ts-dos/SAR100.CO new file mode 100644 index 0000000..e94499a Binary files /dev/null and b/clients/ts-dos/SAR100.CO differ diff --git a/clients/ts-dos/SAR200.CO b/clients/ts-dos/SAR200.CO new file mode 100644 index 0000000..4984c11 Binary files /dev/null and b/clients/ts-dos/SAR200.CO differ diff --git a/clients/ts-dos/Sardine_American_English.pdd1 b/clients/ts-dos/Sardine_American_English.pdd1 new file mode 100644 index 0000000..e69037f Binary files /dev/null and b/clients/ts-dos/Sardine_American_English.pdd1 differ diff --git a/clients/ts-dos/TS-DOS.100 b/clients/ts-dos/TS-DOS.100 index 49999e2..42bfb1e 100644 --- a/clients/ts-dos/TS-DOS.100 +++ b/clients/ts-dos/TS-DOS.100 @@ -1,76 +1 @@ -1'-=CO Loader=- -2'-=V1.01 9/21/2017 Kurt McCullum=- -3 ?"Installing TS-DOS" -4 CLEAR256,57089:Y=57089 -5 TL$="":READTL$:IFTL$="END"THEN10 -6 FORX=1TOLEN(TL$)STEP2 -7 TV=((ASC(MID$(TL$,X,1))-65)*16)+(ASC(MID$(TL$,X+1,1))-65) -8 POKEY,TV:Y=Y+1:NEXT -9 ?".";:GOTO5 -10 ?"Done" -11 OPEN"TMP.DO"FOR OUTPUT AS #1 -12 IFPEEK(100)=245THEN14 -13 ?#1,"0 CALL57089:MENU":GOTO15 -14 ?#1,"0 EXEC57089:MENU" -15 CLOSE#1:CLS:?"Type SAVE "+ CHR$(34) + "TS-DOS" + CHR$(34) -16 ?"Then type MENU" -17 LOAD"TMP.DO" -100 DATA MDAMNPDCONPEKPDCOJPEMJMMNLOJCBAAAADJCCNPPEDOABMNJBPDKPDCOHPEDCONPEDCOMPEDCOEPEDMDCIAOJCBDFNPCCOBPEMNDBECMNEEECCBKHPBCCFCPGMNOMNPDKOIPEDCIAOJMNAKOACBNFPBOFPFPOACNCJANPDKNOPEKHMKJANPDKOJPEEPAMAGAAMFDK -110 DATA IAOJDCOIPEMNFHOEMBAEANMKNFPBHIDCOIPEDCIAOJMFMNEFOECKPHPECLHOPODOMCGDNPMBPBCBGDNPPFMFOFPFMNDPECDKOMPELHMKLJNPPBKHMKMBOEDNMKBCOHDNMKDKOHDNDNMKPCOGDNDNMKJPOGDNOBMKNFNPMDDFNPPBKHMKOKOFDNMKLLOFDNMKILOEDNDN -120 DATA MKODOHDNDNDNOBMKNFNPMDDFNPMNMHPDMNMLGOMNDBECMNEEECCBAAAACCFCPGCKNPPEPJMJCBABABCCDJPGCBOPOHMNKCBBKPDCNOPEDOABDCOIPECBACACCCDJPGMDPDOBDKIAOJDCOIPEMNEFOEDKOIPEDNDHDPAHFPBGAACBAAPHBJOLCBAHCDCCDJPGMNFNECON -130 DATA MNGJECMNNEDJMNGOECMNJJOHMNECHCMKDFOANKMEOBPOANMKPIOAPOBHMKGCOBPOBEMKFAOBPOFNMKFAOBPODPMKCPOBPOACMKCPOBPOBOMKMEOAPOBPMKNCOAPOBNMKLDOAPOBMMKKAOAPOCAMKKAOAOGNPPOFEMKKGOBPOEBMKOLOAPOEHMKOLOAPOFFMKGGOBPOEE -140 DATA MKHBODPOEMMKHKODPOFAMKHKODMDDFOADOABCBOIPEIGCDLONKOAOAMKOAOADOABMDOAOADKOIPEDNMCOAOADKOJPEKHMKDFOAMDOAOADKOIPENGAENKDFOAMKDFOAMDOAOADOAECBOIPEIGEPCDHOLJNKDFOAHJPFMNFHOEPBDCIAOJMDAKOADODOMNICOBDOACDCNO -150 DATA PEMDAKOADKODPEPOACMCDFOACKPHPEABAHAAAJHOPODMMCDFOAMNEIPDCKPHPEMNDAOHMNCBOKCBACAAMNDCOKCKPHPEBBNEPEABAJAAMNNLGLKPMDECOBDKOMPEKHMKDFOADKOJPEPOBENKDFOADKOHPEDMDCOHPEDOABDCIAOJMNOMNPMDAKOADKOMPEKHMKDFOADK -160 DATA OHPEKHMKDFOADNMDECOBKPMDECOBDOCAMNICOBKPDCNOPEMDAKOADKODPEDNMCDFOADKOEPEOOABDCOEPEMJDCNOPEDKOJPEKHMIKPDMDCOIPEMNGAOECKPHPECLDKNOPEOHDKOIPECBOJPELONKILOBKHMJMNGAOEMNGJECCKPHPECLDODOLOMCLIOBDOCAOHMNGOEC -170 DATA CBNOPEDGACMDKAOAPOAINCDFOAPOAFMKOLOBPOADMADKOMPEOOABDCOMPEKPDCOHPEDOABDCIAOJMNDBECMNOMNPMDAKOADKOMPEKHMIMDGCOBCBAAPHCCOOPEKPHHCDHHDKOMPEKHMKBFODKPDCONPEMNHBPAMCMHOCDKOHPEKHMKCJOCMNAENPMNHOPADKONPEDNMC -180 DATA BFOCKPDCONPEDCOJPEMNHOPADKOJPEPOBENMGCODDKODPEKHMKFHOCDNMKEFOCCBNEPEMDFNOCCBABCECCDJPGMNGJECDOCDOHDKOEPEMGDAOHMNGOECMDHAOCOFCBABCBCCDJPGMNGJECOBAOAGMNAFPCMNGOECCBBNOIABOOOCDKOMPEKHMCIDOCCBFPOIABNKOCMF -190 DATA OFDKIAOJCBOJPELONKJFOCHOKHMCJFOCDMDCOIPECBAHALCCDJPGOBMNKCBBDKOMPEKHMKLNOCDKODPEKHMKLNOCCBGPOJDNMKLKOCCBFOOJMNKCBBMNPIOCCBAHBFCCDJPGMJKPMNGCODCBBNOIABNEOCMDIDOCCBMCOIMDKCBBCKLCPLOLCKHIPGHNJDGPHMJKGHAB -200 DATA PCPPAJMDNEDJMNCFPDMNNEDJDODAOHMJDKOMPEKHMADKCMPLBBHIOJPONLMKALODBBHMOJCBAIBJCCDJPGOLMDKCBBCBLKPJKPDCOJPEDOBEDNPFHOOOIAOGJPMCEOODOFCDFOCDFGODOFMNHCOGOLMNMNPAOBODCDAOAGMNAFPCDOCOOHAOACMNAFPCDOCAOHCBOJPE -210 DATA DEOBBBALAABJPBDNMCBPODDKOJPEPOBENMGCODMDDEOCPFCBFDOJMNKCBBPBDMPOBEMCGCODMJMNDLOEMNFOBOMDAKOADCPCPEBBAHAACKPHPEBJHOPOEEMCAKOAMNNMODDKPCPEPOEMMMDBECMEDLOEMNLHODPFMNPOODPBMKJIODDKPCPEPOEMMCLBODDOAHOHMNJD -220 DATA OHMNDBECMDNFPBDKOMPEKHMCENOKCKPAPEABIAAAHJAINCMNODAJHNGMEPCCPAPECKPDPEOFAJCCPDPEOBPOIAMJMNEIPDCKPHPEDKOMPEKHMCPIODMNFIPDNFMNHCOGCCPAPEOBCCPDPEMJMNDAOHMDCBOKOLLHMIEPDKPCPEPOEMMCBFOEMNCCOEBKOHBDANMCAKOE -230 DATA MJMNCCOEBKMNFFELBDANMCBFOEMJMNNLBDMIMNMLBCPOADMKLBODPOBLMKLBODPOCAMCCCOEMDMLBCNLLLOGAGOOACMCJLPBMJMNGAOECCPHPEMNGJECCLAOAKMNAFPCMDGOECMNGOECMNGAOEMDEOOEDKOIPEDNGPCGAABBAKAAMNCFDHBBCIPOBJCDOFABAAPOAIBB -240 DATA CIAAOLMNHODHHNDMDCDJPGOLBAHNDCDKPGOBMJMNFIPDCCPDPECBNAOIMNLFOHMIMNCCPCCKPDPEABAJAAAJBBJJPMHOBCBDCDHOBCMNFOPDMCJOPBCKPDPECDCDCDBBJDPMABAGAAOLMDNLGLMNEIPDCKPHPEMNDAOHDKNOPEKHMCOJOECBAAOJMNLFOHMKOJOEMNCC -250 DATA PCABACAACBACPFBBJJPMMNNLGLMNFOPDMKPMOECBDGOJMNBEPCMNIJOHMAMNMEOFCKDAPFHMGFGPCCDAPFDKACPFPOEEMCBFOFCKKOPLDOMAMDDGOFPOEDMCCIOFCKLAPLCCOIPECKLCPLDOKAMDDGOFCKDAPFCCOIPECKJKPJCCPDPEDOIADCPCPECCPFPECKDAPFCC -260 DATA PAPEOFMBDKPCPEPOKAMKFDOFADPOIAMCFDOFADMFMNOECAMBOFMFCKPFPEMNGNGLNKKKPBCKPFPEMBDGAACDALHJLAMCGHOFCLNBDKPCPEPOMAMCIDOFDGBKCKPFPECLMDKLOFPOKAMCJEOFCKOIPECCLAPLCKPFPEMDKLOFNFCKKOPLCLCCJKPJCDOLCKOIPECDCDBJ -270 DATA CCKOPLNBCKPDPEOLMNDJCCMNLOOKMNEGCBCBACAAMDDCOKMNNHOFCKPHPEMNCCPCMNFOPDPOMAMKLPBPPOKAMKNJBPPOIAMKBHCAMJDKNOPEDNMIMNHMOHDKNOPEKHMIDOABDCNOPEMJMNEIPDMNFIPDNFOFCDCDCDBBPLPEABAGAAMNNLGLDOCOBCBDABACAAMNNLGL -280 DATA DKNOPEKHMCCHOGCBPHOIMNLFOHMKCHOGMNCCPCABAGAABBPLPECBJDPMMNNLGLOBNBNFMNHCOGHMLFMKKEPBOFDOABDCOJPEMNGDPCMNHPPDMKGNOGCBBBOJMNBEPCMNJDOHOGNPOHPOFCMCFLOGMNJMOMMNHPPDMDGNOGOBNBPOEBMADKACPFPOEEMADOACDCOJPENF -290 DATA OFOBNBMDFPOKHOPOMAMKIFOGPOKAMKJEOGNFMNPEAFMBAICLMJOLBBAAAAHOCDBDPOBKMCIJOGBLOLMJOLCDCDEOCDEGCBAGAAAJMJDKODPEKHMIDNMKHCOBMNEIPDCBOHOIMNLFOHMIMNCCPCCBJDPMBBPLPEABAGAAMNNLGLCBDMDOCCACPFDOCODCABPFMNGDPCDK -300 DATA ODPEPOACMAMNHPPDMCOMOGDOABDCPLPECBABABMNDCOKCBACAAMDDCOKMNCBOKMDOGOGDKODPEPOACMICBKBOIMNBEPCMNIJOHMAMNGDPCMNADPDCBAGAAMNDCOKDOAHOHMJMNNHOFMNEIPDMNBOOHMDJMOMABAJAACKPHPEBBPLPEMNNLGLMNGDPCMDHPPDMNCCPCCB -310 DATA JDPMMNGEPDMJMNEIPDMNBOOHCBPLPEBBJMPMABAJAAMNNLGLCKDAPFEMEFCBMAHMAICBNAOIMCHCOHABAJAAMNLIOHMIOFPFMNEIPDPBOBBBPLPEEPAGAAMDEHOLMNLFOHMIMNCCPCMDDOOLCBAJOJMNBEPCMNIJOHMCNFPBMJMNJDOHOHPOFJMIPOHJMJMNJJOHMDML -320 DATA BCKPDNDCFGPGMNNLBDMKJJOHKPDCFGPGCBDCPJLGMKLBBLMNLFBDMDJJOHABAGAAMNMKOHMIOFPFMNOIAPHHCDKHMCLOOHPBOBMJMFMNBEPCMNEEEGNKNFPBCDHIDNNBMILLNIOFBJDGAAOBHLLHMJCKNKPKHOPOEJMCNLOJMDLAOJBLHACAFEFDCNEEEPFDCACIDBDA -330 DATA DACAHGDECODBDACJCADCDADBDECAFEFDEJCPELFACACACACACACACACACABLHBCAAAEEEJFDELCAEGHCGFGFDKCACACACACACACACACAEGGJGMGFDKANAKEMGPGBGECAELGJGMGMCAEOGBGNGFCAFCGBGNCACAEGHCGNHECAEMGPGHCACACACACACACAENGFGOHFAACA -340 DATA FCEBENCAEGHCGFGFDKCACACACACACACACACAEGGJGMGFDKANAKFDGBHGGFCAELGJGMGMCAEOGBGNGFCAEEGJHDGLCAEEEPFDCNCACACACACACACACACACACAENGFGOHFAAEJGOHDGFHCHECAEEGJHDGLCMCAFAHCGFHDHDCACCFJCCCAHEGPCAGCGFGHGJGOCAAAEOEP -350 DATA FECAEGEPFCENEBFEFEEFEEAAEOGFHHCAEOGBGNGFDKAAFDHJHDHEGFGNCAGOGBGNGFDKAAEEGJHCGFGDHEGPHCHJCAGOGBGNGFDKAAFDGBHGGFCAGBHDDKAAEMGPGBGECAGBHDDKAAFDHFHCGFCADPCAAAEGGJGMGFCAGFHIGJHDHEHDCMCAEBCJHAHAGFGOGECAFCCJ -360 DATA GFHAGMGBGDGFCAFBCJHFGJHEDKAAEGGJGMGFCAGFHIGJHDHEHDCMCAFCGFHAGMGBGDGFDPCACIFJCPEOCJDKAACNCOCNCACACACACACACAAABLFJDHDECACACACABLFJDHDOENGLEEHCAABLFJDHDOECGBGOGLAAEPEGEGAAEPEOCAAAABEJMDILOJMDNLOJMDLAOJOD -370 DATA AGCGBBNFPNMNECCFCDCDOLDGICCDDGOJCDDGMDCDHDCDHCOBMDNFPNOBCDCDCDOFCBIBOJMJCBPDHPCCNKPKCCNMPKCBPDHPCCAIPLCCAKPLCCPAPKCCPEPKCBNLAICCCMPLCCDCPLCCDEPLCCDGPLCCDIPLMJMNLAOJCBIBOJCCNKPKCBKHOJCCNMPKCBMCONCCPEPK -380 DATA CBDCOOCCPAPKCBDFONCCDIPLCBIFONCCDGPLCBKGOMCCAIPLCBFKOOCCAKPLCBLPOMCCCMPLCBONOMCCDCPLCBALONCCDEPLMJCBABABDOADDCPLPEMDDCOKGHCOAEMDDOOKMNECPBMNBAPDMNNJPAMDAFPBCCBFPFCBBFPFMNEIPBMNNJPAMDAFPBCBADAAMNECPBMN -390 DATA NJPADKBGPFCBBHPFPOIAMJNFOFMNJIOKOBCCOIPEOBBBBHPFABIAAAMNNLGLOFCKOIPEABIAAAAINKILOKMKILOKCCOIPEDOIAMNCMOKMDGIOKOBDKOIPEMNCMOKCBACAAMDDCOKBBAAAFOLMNHODHDKDCPFJFNKKBPBMKKBPBDKOJPEDCPLPECBABABMDDCOKCKPFPE -400 DATA MNGNGLNKKKPBCKPFPEOFMNCBOKMNENOKEPAGAANBMNNLGLNFDKBGPFCKPAPEEPAGAAAICCPAPENKOFOKHNLEMCMFOKNBMJDKPAPEPOABMIMNLIPCDOFHDCOGPEMNFAOMMNLIPCMNANOLCKPHPEMNPJPBCCPHPEMNHAOMMDOHOKDKBMPFMNCIOLPODFMKCAOLCBPBPEDE -410 DATA HOPOBEMACBPAPEDECDDGABMJABAAAFPODFMIABEAAAMJCBAIAAMNECPBMNPDPCMDLIPCCBJDPMBBPLPEABAGAAMNNLGLDKODPEKHMCAEOMMNHPPDMCJOPBBBAAAFMNIKOMCBAAABCCPAPEMNDCOLMNBFOMCKLGPLOFAGCIBBJMPMMNOPOLNKJLPBHIKHMKJLPBOFBBPL -420 DATA PEOLABAJAAMNNLGLOBOFBBDDPFABBPAAMNNLGLNBNFCBBPAABJOFCKLGPLABAAAFAJMBMFAIODMBMNNLGLCKLGPLAGCHBBDDPFMNOPOLOFCKLGPLABPPAEAJOFABBPAAAINBMBMFOFAIOFMBADOBMNOGGLCBDDPFNBABBPAAMNNLGLCBABBECCPBPEOBOBCCPHPECBAA -430 DATA ABCCPAPEMNOHOKMNONPCMJAOAJMNDBPDMINIHOLHMIMFABBPAAAJMBAFMIMDOPOLCBANBJMNECPBMNNJPADKBHPFKHMCJOPBMJCKPFPEMNCDOMDKPAPEDNMIMDBIOMOFDOFCDCOGPEMNFAOMMNANOLDOANMNHPPBMNFMHGOBMNDFPBHHCDALHJLAMCDIOMMJAGCPNGAK -440 DATA AENCEGOMMGDKEPMJDKPAPEMNEEOMDKOGPEMNHPPBMNPLPCDKPBPEMNEEOMDOCMMNHPPBMNPLPCMNPDPCCBBHPFOFAOAIMNCPPBANMCHGOMOBHOPODAMIPOECMKINPBMDIHPBCKLGPLOFCCPFPECBMAPPDJMBAINPNKKKPBMJKPDCBEPFCBAFAAMDDCOKPODANIPODCNA -450 DATA NGDADCOEPECDBNHOPODKMACDBNODOBDOAJLHMJNFOFPFKPMNJBPDDIACONHOKHMKOCOMPODKMKOCOMMNBDBBHLPOACNCOIOMDCOEPEDIACNJMNDGPAMNMHPDPBOBNBMBMJPOAJMCNLAINFOFPFKPMNJBPDCBJDPMMNGEPDMKJLPBMNJMOMMNMHPDMDOIOMPOAJMCNLAI -460 DATA NFOFPFKPMNJBPDCBJMPMMNGEPDMKJLPBCBPLPEBBJMPMABAJAAMNNLGLMNDOOLMNMHPDMDOIOMPOAJMAMBOFABEDEPMNAHOOMKJLPBMNCBOKCKDAPFHNGMGHCCPAPEMNENOKMNDNCFOFMNKECFCKMOPKOFOFCKPEPFODMBAINKKKPBCKNAPKAJABICOJAINCNLAINBOB -470 DATA DKBGPFNGAGEPAGAAMNMMOKMNMHPDMDBKCFPOAJMAMNEGCDABEDEPMNAHOOMCJOPBDOABDCOJPECKNAPKABAGAAMFAJOFMNJIOKOBCCOIPEMBCBMOPKBBBHPFMNNLGLCKBHPFABHKAAMNGPOKMNMHPDMDABAFPOAJMAMBPBPFMFMIJPDCJCPMABECEBMNAHOOMKJLPBCK -480 DATA DAPFEMEFGAGJCCPAPEMNPPCACDCCPFPECKPAPEOFOFMBMNLFOKNBCKKOPLBJCCKOPLCKNIPKBJCCNIPKMNMHPDKPMDPFCDPFOFNFMFCKJJPMBBCACANPMKCBOONBNFNPMKCBOOMBNBOBPBMBMJOBCCJJPMNBOBPBKPMNJBPDCBJDPMMDGEPDPOAJMAABECEBMNAHOOMC -490 DATA JOPBMNPAAFCKHMPGOLNPMKACAFNFMBAIDOABDCOJPEMNFPOKMNMHPDMDACAFPFKPDCOFPEPBCCNPPENFOFPFBBAEAABJHOPOAJMKHEOOPBOBNBMJPBPFPOAAMKJIOOPOACMKCCOPPOAEMKOEOPPOAGMKDPOPPOAIMCHAOODIAGCBBLBGNJMDHAOOKPMNJBPDCBJJPMHO -500 DATA POCAMKKKOOPOEEMCKNPBDGEECDDGEPCBJDPMMNGEPDEPMNIPOPDGAAOBOBNBNFOFFBHLOGAHCBONPDEPAGAAAJHODNMKAHOPDNMCBKOPDKCPPFKHMCNNOOCDCDHODCPLPECBABABMNDCOKCKNPPEBBAHAABJKPHHCDFEFNCDCDNJOBNBHLPOAIMCAAOPBOACHDODCBNO -510 DATA BEODMJHKLHMKNNOOOFMNJMOMCBJDPMMNGEPDOBMDNNOOHKLHMKJLPBMDNNOOPBOBNBOFHODGAAPOACMMAPPACBACAAMNDCOKMNMHPDOBODOBCBFJENOFMJMNIPOPHODGAAKHMCFPOPCKNPPEABAHAAAJHOCDOFKHMMJHOPNBONHOCDNJBLOLDFPOBKDHDPMCGLOPMNIP -520 DATA OPHHDHPFDIBAONABJKBIAIMCIFOPMNIPOPPBHHEPJPMNAKDEDIBCOLPJMJPBOBOBNBODCBIKEOODMJCKKCPKBBJBPKBJMJCBADAAMNECPBMNNJPADKBFPFPOBAMCNCOPDKBGPFCKNPPEBBAIAABJOFFEFNCDCDNJBBBHPFOLEPAGAAMNNLGLDKBGPFPOIAMKMOOPOLDG -530 DATA BKDMOBCLHHMJCKNPPEBBAHAABJDGABCDFEFNCDCDNJDGBKMJCKNPPEBBAHAABJHOPOIAMMAPPAPBOBNBOBPBPFPOBKMKAKPACKNPPEBBAHAABJDECDOLONHHCDNJCBOKBEOFMJCKNPPEABAHAAAJHOOFCDCDCDBBBHPFEPAGAAPFMNNLGLPBKHMECMOKOBDGAACDFEFN -540 DATA CDCDNJMJPBMJCBAAPHCCOOPEMNHBPAMCIKPBKPDCONPEDCOHPEDMDCDKPGMNHOPADKOJPEPOBEMMNOPBCBOHPEDEMNHOPAMNCCECMNCFPDMNNEDJCBNEPDMNKCBBMDCCECKPDCOJPEMNGDPCCBEGABMDKLPADOBEDCOKPEDKODPEKHMCJDPACBPGACMNKLPAMCAFPBDK -550 DATA BHPFKHMIDKONPEKHMMLNPACBOJPEDEDKOKPELOMCIKPAMJCCBDPFCBAABKMNECPBMNNJPADKBFPFPOBBMJAOAJCBBHPFMNAFPCDOCAOHCKDAPFFMFFCKOOPEOLNJOLCDCDCCOOPEMJCBBFPFOFMNCPPBMNCPPBEPMNCPPBEBHJKHMKPEPAMNCPPBANMCONPAOBAEAEKP -560 DATA DAIGCDAFMCPJPACPLOMCIKPBMJDKBFPFPOBCMCIKPBDKBHPFKHMIPOBAMKJOPBPOFAMKINPBPOGAMKKBPBNKIKPBMKJDPBPOIANKJDPBMDJAPBMNDFPBHHCDMJMNICPBMNHOGNNKKNPBMCIKPBMJCCPJPECBPJPEOFDKOEPEKHMKFHPBOGABBPBPBPLGHHHOCDIGEHHO -570 DATA PFCDKHMKGLPBEPHIIGCDANMCGEPBEHHICPFHABFKFKMNPLPCMBEIAGAAADADOBMNPJPBHKMNDCGONLLLOGCAMIDOABCBDOACCBDOADCBDOAECBDOAFCBOMPEDGAACBDOAGCBDOAHCBDOAICBDOAJCBDOAKCBDOALCBDOAMCBDOANCBALPEEPANMKMEPBHOCDPOAAMCLK -580 DATA PBMDLGPBDKOFPEKHMKPAPBDOAHOHCDMNBEPCMNOHPBCKNPPEPJCKOBPEOFMJMNCCECMNOHPBMDCCECCBMEPEMNKCBBMDMLBCFOCKNPPEDGAAMDFNAEHOMNHPPBCDALHJLAMCPJPBMJHOPOCANCANPCDOCAOHCDANMCAFPCMJOFCBAIABCCDJPGMNFNECOBMDKCBBOFDO -590 DATA CACBJDPMAGAIMNEBPDOBBBJDPMAGAGHOPOEBNKLAPBHOPOCOMKELPCLHMIBCCDBDAFMCDJPCMDFDPCDOCABCBDAFMCENPCAGACCDHOLHMIBCBDAFMCFFPCDOAABCMJMNKBPDMNMCPCMNNJPAMNAFPBDKODPEKHMICBAIAAMNECPBMNNJPADKBGPFPOABMKJHPCDNEPDO -600 DATA ACDCODPEAGAACBBIPFBBNEPEMDNLGLCBCDAAMNECPBMNNJPACBPAPDMNKPPCCBPJPDMNKPPCCBACPEABAJAAMNPJPBMDNJPAMNGNGNMIMNDFPBMDLIPCMNICPBMNONPCMNLIPCMNDCOLMNPDPCMNGNGNDOAAMCNKPCDMDCODPEMNLIPCMNONPCMNLIPCCBAHAAMNECPB -610 DATA MJABDBENMNPLPCDOANMNHPPBMDADPDHIMNHPPBHJMDHPPBCGBECOPPCNMCAHPDCFMCAFPDMJABFAPPMNADPDMNGNGNMAAFMCBDPDANMCBDPDMDIHPBDKDCPFGPCGAABBIAAAMDCFDHNFOFBKJGMCDOPDCDBDANMCDDPDOBNBMJHHCDAFMCEBPDMJDOCAAGBICBPLPEMN -620 DATA EBPDDOEGDCBDPFMJCKPHPEMNCCPCMNEGCBMDKPCAOFMNEIPDOBBBPLPEABAGAAMNNLGLDOCOBCBDABACAAMNNLGLMNGDPCKPDCBEPFCBAABKMNECPBMNNJPADKCPPFKHMJDCOFPEBBOHPDCBFLPGABAFAAMNNLGLMJMNMLGOCBOBPDDGDJDHMNOGBHMNLIPCMNMCPCMN -630 DATA ADPDMNGNGNMCLIPCMNMLGOCBOBPDDFDHMDOGBHCBOHPDHONGDKNKOGBHCDMDOGBHDACAECHJHEGFHDCAGGHCGFGFAADJDIEODBEEAAENDHEJDBEFAAACADABFKFKDBAEABAAIEPPEGFKFKDBAEABAAJGAPCEFKFKDBAEABAAJEAPCGBCEEHCGJHGGFCAEOGPHECAFCGF -640 DATA GBGEHJAABCEDGPGNGNHFGOGJGDGBHEGJGPGOCAEFHCHCGPHCAABCFHHCGJHEGFCAFAHCGPHEGFGDHEAABCEEHCGJHGGFCAFEHCGPHFGCGMGFAABCEEGJHDGLCAGOGPHECAGJGOCAGEHCGJHGGFAADEFAHCGJGOHEGFHCCAGOGPHECAHCGFGBGEHJAAAFEGGJGMGFCAEF -650 DATA HIGJHDHEHDAADJEEGJHDGLCAEGHFGMGMAABKEGGJGMGFCAEFGNHAHEHJAADJEEGJHCGFGDHEGPHCHJCAEGHFGMGMAAAHFCGBGNCAEGHFGMGMAADGAADHECGBGECAEGGJGMGFCAEOGBGNGFAACAFAHCGFHDHDCAGBGOHJCAGLGFHJCOAAAAAAAAAAAAAAAAAAAAAAAALM -660 DATA NEDFNPAAAAABAAAAABABAAAAAAAAACPHAAAAAAAAAAAAAACJPOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -670 DATA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -680 DATA END \ No newline at end of file +0'TS-DOS Loader V1.01 9/21/2017 Kurt McCullum - 2022 b.kenyon.w@gmail.com 0CLEAR256,57089:S=57089:Y=S:C$="TS-DOS":D$="TMP.DO":?"Installing "+C$ 1TL$="":READTL$:IFTL$="END"THEN3 2FORX=1TOLEN(TL$)STEP2:TV=((ASC(MID$(TL$,X,1))-65)*16)+(ASC(MID$(TL$,X+1,1))-65):POKEY,TV:Y=Y+1:NEXT:?".";:GOTO1 3?"Done":OPEND$FOROUTPUTAS#1:?#1,"0 CALL";S;":MENU":CLOSE#1:CLS:?"Please Type:":?"KILL "+CHR$(34)+D$+CHR$(34):?"SAVE "+CHR$(34)+C$+CHR$(34):?"MENU":LOADD$ 4DATAMDAMNPDCONPEKPDCOJPEMJMMNLOJCBAAAADJCCNPPEDOABMNJBPDKPDCOHPEDCONPEDCOMPEDCOEPEDMDCIAOJCBDFNPCCOBPEMNDBECMNEEECCBKHPBCCFCPGMNOMNPDKOIPEDCIAOJMNAKOACBNFPBOFPFPOACNCJANPDKNOPEKHMKJANPDKOJPEEPAMAGAAMFDK 5DATAIAOJDCOIPEMNFHOEMBAEANMKNFPBHIDCOIPEDCIAOJMFMNEFOECKPHPECLHOPODOMCGDNPMBPBCBGDNPPFMFOFPFMNDPECDKOMPELHMKLJNPPBKHMKMBOEDNMKBCOHDNMKDKOHDNDNMKPCOGDNDNMKJPOGDNOBMKNFNPMDDFNPPBKHMKOKOFDNMKLLOFDNMKILOEDNDN 6DATAMKODOHDNDNDNOBMKNFNPMDDFNPMNMHPDMNMLGOMNDBECMNEEECCBAAAACCFCPGCKNPPEPJMJCBABABCCDJPGCBOPOHMNKCBBKPDCNOPEDOABDCOIPECBACACCCDJPGMDPDOBDKIAOJDCOIPEMNEFOEDKOIPEDNDHDPAHFPBGAACBAAPHBJOLCBAHCDCCDJPGMNFNECON 7DATAMNGJECMNNEDJMNGOECMNJJOHMNECHCMKDFOANKMEOBPOANMKPIOAPOBHMKGCOBPOBEMKFAOBPOFNMKFAOBPODPMKCPOBPOACMKCPOBPOBOMKMEOAPOBPMKNCOAPOBNMKLDOAPOBMMKKAOAPOCAMKKAOAOGNPPOFEMKKGOBPOEBMKOLOAPOEHMKOLOAPOFFMKGGOBPOEE 8DATAMKHBODPOEMMKHKODPOFAMKHKODMDDFOADOABCBOIPEIGCDLONKOAOAMKOAOADOABMDOAOADKOIPEDNMCOAOADKOJPEKHMKDFOAMDOAOADKOIPENGAENKDFOAMKDFOAMDOAOADOAECBOIPEIGEPCDHOLJNKDFOAHJPFMNFHOEPBDCIAOJMDAKOADODOMNICOBDOACDCNO 9DATAPEMDAKOADKODPEPOACMCDFOACKPHPEABAHAAAJHOPODMMCDFOAMNEIPDCKPHPEMNDAOHMNCBOKCBACAAMNDCOKCKPHPEBBNEPEABAJAAMNNLGLKPMDECOBDKOMPEKHMKDFOADKOJPEPOBENKDFOADKOHPEDMDCOHPEDOABDCIAOJMNOMNPMDAKOADKOMPEKHMKDFOADK 10DATAOHPEKHMKDFOADNMDECOBKPMDECOBDOCAMNICOBKPDCNOPEMDAKOADKODPEDNMCDFOADKOEPEOOABDCOEPEMJDCNOPEDKOJPEKHMIKPDMDCOIPEMNGAOECKPHPECLDKNOPEOHDKOIPECBOJPELONKILOBKHMJMNGAOEMNGJECCKPHPECLDODOLOMCLIOBDOCAOHMNGOEC 11DATACBNOPEDGACMDKAOAPOAINCDFOAPOAFMKOLOBPOADMADKOMPEOOABDCOMPEKPDCOHPEDOABDCIAOJMNDBECMNOMNPMDAKOADKOMPEKHMIMDGCOBCBAAPHCCOOPEKPHHCDHHDKOMPEKHMKBFODKPDCONPEMNHBPAMCMHOCDKOHPEKHMKCJOCMNAENPMNHOPADKONPEDNMC 12DATABFOCKPDCONPEDCOJPEMNHOPADKOJPEPOBENMGCODDKODPEKHMKFHOCDNMKEFOCCBNEPEMDFNOCCBABCECCDJPGMNGJECDOCDOHDKOEPEMGDAOHMNGOECMDHAOCOFCBABCBCCDJPGMNGJECOBAOAGMNAFPCMNGOECCBBNOIABOOOCDKOMPEKHMCIDOCCBFPOIABNKOCMF 13DATAOFDKIAOJCBOJPELONKJFOCHOKHMCJFOCDMDCOIPECBAHALCCDJPGOBMNKCBBDKOMPEKHMKLNOCDKODPEKHMKLNOCCBGPOJDNMKLKOCCBFOOJMNKCBBMNPIOCCBAHBFCCDJPGMJKPMNGCODCBBNOIABNEOCMDIDOCCBMCOIMDKCBBCKLCPLOLCKHIPGHNJDGPHMJKGHAB 14DATAPCPPAJMDNEDJMNCFPDMNNEDJDODAOHMJDKOMPEKHMADKCMPLBBHIOJPONLMKALODBBHMOJCBAIBJCCDJPGOLMDKCBBCBLKPJKPDCOJPEDOBEDNPFHOOOIAOGJPMCEOODOFCDFOCDFGODOFMNHCOGOLMNMNPAOBODCDAOAGMNAFPCDOCOOHAOACMNAFPCDOCAOHCBOJPE 15DATADEOBBBALAABJPBDNMCBPODDKOJPEPOBENMGCODMDDEOCPFCBFDOJMNKCBBPBDMPOBEMCGCODMJMNDLOEMNFOBOMDAKOADCPCPEBBAHAACKPHPEBJHOPOEEMCAKOAMNNMODDKPCPEPOEMMMDBECMEDLOEMNLHODPFMNPOODPBMKJIODDKPCPEPOEMMCLBODDOAHOHMNJD 16DATAOHMNDBECMDNFPBDKOMPEKHMCENOKCKPAPEABIAAAHJAINCMNODAJHNGMEPCCPAPECKPDPEOFAJCCPDPEOBPOIAMJMNEIPDCKPHPEDKOMPEKHMCPIODMNFIPDNFMNHCOGCCPAPEOBCCPDPEMJMNDAOHMDCBOKOLLHMIEPDKPCPEPOEMMCBFOEMNCCOEBKOHBDANMCAKOE 17DATAMJMNCCOEBKMNFFELBDANMCBFOEMJMNNLBDMIMNMLBCPOADMKLBODPOBLMKLBODPOCAMCCCOEMDMLBCNLLLOGAGOOACMCJLPBMJMNGAOECCPHPEMNGJECCLAOAKMNAFPCMDGOECMNGOECMNGAOEMDEOOEDKOIPEDNGPCGAABBAKAAMNCFDHBBCIPOBJCDOFABAAPOAIBB 18DATACIAAOLMNHODHHNDMDCDJPGOLBAHNDCDKPGOBMJMNFIPDCCPDPECBNAOIMNLFOHMIMNCCPCCKPDPEABAJAAAJBBJJPMHOBCBDCDHOBCMNFOPDMCJOPBCKPDPECDCDCDBBJDPMABAGAAOLMDNLGLMNEIPDCKPHPEMNDAOHDKNOPEKHMCOJOECBAAOJMNLFOHMKOJOEMNCC 19DATAPCABACAACBACPFBBJJPMMNNLGLMNFOPDMKPMOECBDGOJMNBEPCMNIJOHMAMNMEOFCKDAPFHMGFGPCCDAPFDKACPFPOEEMCBFOFCKKOPLDOMAMDDGOFPOEDMCCIOFCKLAPLCCOIPECKLCPLDOKAMDDGOFCKDAPFCCOIPECKJKPJCCPDPEDOIADCPCPECCPFPECKDAPFCC 20DATAPAPEOFMBDKPCPEPOKAMKFDOFADPOIAMCFDOFADMFMNOECAMBOFMFCKPFPEMNGNGLNKKKPBCKPFPEMBDGAACDALHJLAMCGHOFCLNBDKPCPEPOMAMCIDOFDGBKCKPFPECLMDKLOFPOKAMCJEOFCKOIPECCLAPLCKPFPEMDKLOFNFCKKOPLCLCCJKPJCDOLCKOIPECDCDBJ 21DATACCKOPLNBCKPDPEOLMNDJCCMNLOOKMNEGCBCBACAAMDDCOKMNNHOFCKPHPEMNCCPCMNFOPDPOMAMKLPBPPOKAMKNJBPPOIAMKBHCAMJDKNOPEDNMIMNHMOHDKNOPEKHMIDOABDCNOPEMJMNEIPDMNFIPDNFOFCDCDCDBBPLPEABAGAAMNNLGLDOCOBCBDABACAAMNNLGL 22DATADKNOPEKHMCCHOGCBPHOIMNLFOHMKCHOGMNCCPCABAGAABBPLPECBJDPMMNNLGLOBNBNFMNHCOGHMLFMKKEPBOFDOABDCOJPEMNGDPCMNHPPDMKGNOGCBBBOJMNBEPCMNJDOHOGNPOHPOFCMCFLOGMNJMOMMNHPPDMDGNOGOBNBPOEBMADKACPFPOEEMADOACDCOJPENF 23DATAOFOBNBMDFPOKHOPOMAMKIFOGPOKAMKJEOGNFMNPEAFMBAICLMJOLBBAAAAHOCDBDPOBKMCIJOGBLOLMJOLCDCDEOCDEGCBAGAAAJMJDKODPEKHMIDNMKHCOBMNEIPDCBOHOIMNLFOHMIMNCCPCCBJDPMBBPLPEABAGAAMNNLGLCBDMDOCCACPFDOCODCABPFMNGDPCDK 24DATAODPEPOACMAMNHPPDMCOMOGDOABDCPLPECBABABMNDCOKCBACAAMDDCOKMNCBOKMDOGOGDKODPEPOACMICBKBOIMNBEPCMNIJOHMAMNGDPCMNADPDCBAGAAMNDCOKDOAHOHMJMNNHOFMNEIPDMNBOOHMDJMOMABAJAACKPHPEBBPLPEMNNLGLMNGDPCMDHPPDMNCCPCCB 25DATAJDPMMNGEPDMJMNEIPDMNBOOHCBPLPEBBJMPMABAJAAMNNLGLCKDAPFEMEFCBMAHMAICBNAOIMCHCOHABAJAAMNLIOHMIOFPFMNEIPDPBOBBBPLPEEPAGAAMDEHOLMNLFOHMIMNCCPCMDDOOLCBAJOJMNBEPCMNIJOHMCNFPBMJMNJDOHOHPOFJMIPOHJMJMNJJOHMDML 26DATABCKPDNDCFGPGMNNLBDMKJJOHKPDCFGPGCBDCPJLGMKLBBLMNLFBDMDJJOHABAGAAMNMKOHMIOFPFMNOIAPHHCDKHMCLOOHPBOBMJMFMNBEPCMNEEEGNKNFPBCDHIDNNBMILLNIOFBJDGAAOBHLLHMJCKNKPKHOPOEJMCNLOJMDLAOJBLHACAFEFDCNEEEPFDCACIDBDA 27DATADACAHGDECODBDACJCADCDADBDECAFEFDEJCPELFACACACACACACACACACABLHBCAAAEEEJFDELCAEGHCGFGFDKCACACACACACACACACAEGGJGMGFDKANAKEMGPGBGECAELGJGMGMCAEOGBGNGFCAFCGBGNCACAEGHCGNHECAEMGPGHCACACACACACACAENGFGOHFAACA 28DATAFCEBENCAEGHCGFGFDKCACACACACACACACACAEGGJGMGFDKANAKFDGBHGGFCAELGJGMGMCAEOGBGNGFCAEEGJHDGLCAEEEPFDCNCACACACACACACACACACACAENGFGOHFAAEJGOHDGFHCHECAEEGJHDGLCMCAFAHCGFHDHDCACCFJCCCAHEGPCAGCGFGHGJGOCAAAEOEP 29DATAFECAEGEPFCENEBFEFEEFEEAAEOGFHHCAEOGBGNGFDKAAFDHJHDHEGFGNCAGOGBGNGFDKAAEEGJHCGFGDHEGPHCHJCAGOGBGNGFDKAAFDGBHGGFCAGBHDDKAAEMGPGBGECAGBHDDKAAFDHFHCGFCADPCAAAEGGJGMGFCAGFHIGJHDHEHDCMCAEBCJHAHAGFGOGECAFCCJ 30DATAGFHAGMGBGDGFCAFBCJHFGJHEDKAAEGGJGMGFCAGFHIGJHDHEHDCMCAFCGFHAGMGBGDGFDPCACIFJCPEOCJDKAACNCOCNCACACACACACACAAABLFJDHDECACACACABLFJDHDOENGLEEHCAABLFJDHDOECGBGOGLAAEPEGEGAAEPEOCAAAABEJMDILOJMDNLOJMDLAOJOD 31DATAAGCGBBNFPNMNECCFCDCDOLDGICCDDGOJCDDGMDCDHDCDHCOBMDNFPNOBCDCDCDOFCBIBOJMJCBPDHPCCNKPKCCNMPKCBPDHPCCAIPLCCAKPLCCPAPKCCPEPKCBNLAICCCMPLCCDCPLCCDEPLCCDGPLCCDIPLMJMNLAOJCBIBOJCCNKPKCBKHOJCCNMPKCBMCONCCPEPK 32DATACBDCOOCCPAPKCBDFONCCDIPLCBIFONCCDGPLCBKGOMCCAIPLCBFKOOCCAKPLCBLPOMCCCMPLCBONOMCCDCPLCBALONCCDEPLMJCBABABDOADDCPLPEMDDCOKGHCOAEMDDOOKMNECPBMNBAPDMNNJPAMDAFPBCCBFPFCBBFPFMNEIPBMNNJPAMDAFPBCBADAAMNECPBMN 33DATANJPADKBGPFCBBHPFPOIAMJNFOFMNJIOKOBCCOIPEOBBBBHPFABIAAAMNNLGLOFCKOIPEABIAAAAINKILOKMKILOKCCOIPEDOIAMNCMOKMDGIOKOBDKOIPEMNCMOKCBACAAMDDCOKBBAAAFOLMNHODHDKDCPFJFNKKBPBMKKBPBDKOJPEDCPLPECBABABMDDCOKCKPFPE 34DATAMNGNGLNKKKPBCKPFPEOFMNCBOKMNENOKEPAGAANBMNNLGLNFDKBGPFCKPAPEEPAGAAAICCPAPENKOFOKHNLEMCMFOKNBMJDKPAPEPOABMIMNLIPCDOFHDCOGPEMNFAOMMNLIPCMNANOLCKPHPEMNPJPBCCPHPEMNHAOMMDOHOKDKBMPFMNCIOLPODFMKCAOLCBPBPEDE 35DATAHOPOBEMACBPAPEDECDDGABMJABAAAFPODFMIABEAAAMJCBAIAAMNECPBMNPDPCMDLIPCCBJDPMBBPLPEABAGAAMNNLGLDKODPEKHMCAEOMMNHPPDMCJOPBBBAAAFMNIKOMCBAAABCCPAPEMNDCOLMNBFOMCKLGPLOFAGCIBBJMPMMNOPOLNKJLPBHIKHMKJLPBOFBBPL 36DATAPEOLABAJAAMNNLGLOBOFBBDDPFABBPAAMNNLGLNBNFCBBPAABJOFCKLGPLABAAAFAJMBMFAIODMBMNNLGLCKLGPLAGCHBBDDPFMNOPOLOFCKLGPLABPPAEAJOFABBPAAAINBMBMFOFAIOFMBADOBMNOGGLCBDDPFNBABBPAAMNNLGLCBABBECCPBPEOBOBCCPHPECBAA 37DATAABCCPAPEMNOHOKMNONPCMJAOAJMNDBPDMINIHOLHMIMFABBPAAAJMBAFMIMDOPOLCBANBJMNECPBMNNJPADKBHPFKHMCJOPBMJCKPFPEMNCDOMDKPAPEDNMIMDBIOMOFDOFCDCOGPEMNFAOMMNANOLDOANMNHPPBMNFMHGOBMNDFPBHHCDALHJLAMCDIOMMJAGCPNGAK 38DATAAENCEGOMMGDKEPMJDKPAPEMNEEOMDKOGPEMNHPPBMNPLPCDKPBPEMNEEOMDOCMMNHPPBMNPLPCMNPDPCCBBHPFOFAOAIMNCPPBANMCHGOMOBHOPODAMIPOECMKINPBMDIHPBCKLGPLOFCCPFPECBMAPPDJMBAINPNKKKPBMJKPDCBEPFCBAFAAMDDCOKPODANIPODCNA 39DATANGDADCOEPECDBNHOPODKMACDBNODOBDOAJLHMJNFOFPFKPMNJBPDDIACONHOKHMKOCOMPODKMKOCOMMNBDBBHLPOACNCOIOMDCOEPEDIACNJMNDGPAMNMHPDPBOBNBMBMJPOAJMCNLAINFOFPFKPMNJBPDCBJDPMMNGEPDMKJLPBMNJMOMMNMHPDMDOIOMPOAJMCNLAI 40DATANFOFPFKPMNJBPDCBJMPMMNGEPDMKJLPBCBPLPEBBJMPMABAJAAMNNLGLMNDOOLMNMHPDMDOIOMPOAJMAMBOFABEDEPMNAHOOMKJLPBMNCBOKCKDAPFHNGMGHCCPAPEMNENOKMNDNCFOFMNKECFCKMOPKOFOFCKPEPFODMBAINKKKPBCKNAPKAJABICOJAINCNLAINBOB 41DATADKBGPFNGAGEPAGAAMNMMOKMNMHPDMDBKCFPOAJMAMNEGCDABEDEPMNAHOOMCJOPBDOABDCOJPECKNAPKABAGAAMFAJOFMNJIOKOBCCOIPEMBCBMOPKBBBHPFMNNLGLCKBHPFABHKAAMNGPOKMNMHPDMDABAFPOAJMAMBPBPFMFMIJPDCJCPMABECEBMNAHOOMKJLPBCK 42DATADAPFEMEFGAGJCCPAPEMNPPCACDCCPFPECKPAPEOFOFMBMNLFOKNBCKKOPLBJCCKOPLCKNIPKBJCCNIPKMNMHPDKPMDPFCDPFOFNFMFCKJJPMBBCACANPMKCBOONBNFNPMKCBOOMBNBOBPBMBMJOBCCJJPMNBOBPBKPMNJBPDCBJDPMMDGEPDPOAJMAABECEBMNAHOOMC 43DATAJOPBMNPAAFCKHMPGOLNPMKACAFNFMBAIDOABDCOJPEMNFPOKMNMHPDMDACAFPFKPDCOFPEPBCCNPPENFOFPFBBAEAABJHOPOAJMKHEOOPBOBNBMJPBPFPOAAMKJIOOPOACMKCCOPPOAEMKOEOPPOAGMKDPOPPOAIMCHAOODIAGCBBLBGNJMDHAOOKPMNJBPDCBJJPMHO 44DATAPOCAMKKKOOPOEEMCKNPBDGEECDDGEPCBJDPMMNGEPDEPMNIPOPDGAAOBOBNBNFOFFBHLOGAHCBONPDEPAGAAAJHODNMKAHOPDNMCBKOPDKCPPFKHMCNNOOCDCDHODCPLPECBABABMNDCOKCKNPPEBBAHAABJKPHHCDFEFNCDCDNJOBNBHLPOAIMCAAOPBOACHDODCBNO 45DATABEODMJHKLHMKNNOOOFMNJMOMCBJDPMMNGEPDOBMDNNOOHKLHMKJLPBMDNNOOPBOBNBOFHODGAAPOACMMAPPACBACAAMNDCOKMNMHPDOBODOBCBFJENOFMJMNIPOPHODGAAKHMCFPOPCKNPPEABAHAAAJHOCDOFKHMMJHOPNBONHOCDNJBLOLDFPOBKDHDPMCGLOPMNIP 46DATAOPHHDHPFDIBAONABJKBIAIMCIFOPMNIPOPPBHHEPJPMNAKDEDIBCOLPJMJPBOBOBNBODCBIKEOODMJCKKCPKBBJBPKBJMJCBADAAMNECPBMNNJPADKBFPFPOBAMCNCOPDKBGPFCKNPPEBBAIAABJOFFEFNCDCDNJBBBHPFOLEPAGAAMNNLGLDKBGPFPOIAMKMOOPOLDG 47DATABKDMOBCLHHMJCKNPPEBBAHAABJDGABCDFEFNCDCDNJDGBKMJCKNPPEBBAHAABJHOPOIAMMAPPAPBOBNBOBPBPFPOBKMKAKPACKNPPEBBAHAABJDECDOLONHHCDNJCBOKBEOFMJCKNPPEABAHAAAJHOOFCDCDCDBBBHPFEPAGAAPFMNNLGLPBKHMECMOKOBDGAACDFEFN 48DATACDCDNJMJPBMJCBAAPHCCOOPEMNHBPAMCIKPBKPDCONPEDCOHPEDMDCDKPGMNHOPADKOJPEPOBEMMNOPBCBOHPEDEMNHOPAMNCCECMNCFPDMNNEDJCBNEPDMNKCBBMDCCECKPDCOJPEMNGDPCCBEGABMDKLPADOBEDCOKPEDKODPEKHMCJDPACBPGACMNKLPAMCAFPBDK 49DATABHPFKHMIDKONPEKHMMLNPACBOJPEDEDKOKPELOMCIKPAMJCCBDPFCBAABKMNECPBMNNJPADKBFPFPOBBMJAOAJCBBHPFMNAFPCDOCAOHCKDAPFFMFFCKOOPEOLNJOLCDCDCCOOPEMJCBBFPFOFMNCPPBMNCPPBEPMNCPPBEBHJKHMKPEPAMNCPPBANMCONPAOBAEAEKP 50DATADAIGCDAFMCPJPACPLOMCIKPBMJDKBFPFPOBCMCIKPBDKBHPFKHMIPOBAMKJOPBPOFAMKINPBPOGAMKKBPBNKIKPBMKJDPBPOIANKJDPBMDJAPBMNDFPBHHCDMJMNICPBMNHOGNNKKNPBMCIKPBMJCCPJPECBPJPEOFDKOEPEKHMKFHPBOGABBPBPBPLGHHHOCDIGEHHO 51DATAPFCDKHMKGLPBEPHIIGCDANMCGEPBEHHICPFHABFKFKMNPLPCMBEIAGAAADADOBMNPJPBHKMNDCGONLLLOGCAMIDOABCBDOACCBDOADCBDOAECBDOAFCBOMPEDGAACBDOAGCBDOAHCBDOAICBDOAJCBDOAKCBDOALCBDOAMCBDOANCBALPEEPANMKMEPBHOCDPOAAMCLK 52DATAPBMDLGPBDKOFPEKHMKPAPBDOAHOHCDMNBEPCMNOHPBCKNPPEPJCKOBPEOFMJMNCCECMNOHPBMDCCECCBMEPEMNKCBBMDMLBCFOCKNPPEDGAAMDFNAEHOMNHPPBCDALHJLAMCPJPBMJHOPOCANCANPCDOCAOHCDANMCAFPCMJOFCBAIABCCDJPGMNFNECOBMDKCBBOFDO 53DATACACBJDPMAGAIMNEBPDOBBBJDPMAGAGHOPOEBNKLAPBHOPOCOMKELPCLHMIBCCDBDAFMCDJPCMDFDPCDOCABCBDAFMCENPCAGACCDHOLHMIBCBDAFMCFFPCDOAABCMJMNKBPDMNMCPCMNNJPAMNAFPBDKODPEKHMICBAIAAMNECPBMNNJPADKBGPFPOABMKJHPCDNEPDO 54DATAACDCODPEAGAACBBIPFBBNEPEMDNLGLCBCDAAMNECPBMNNJPACBPAPDMNKPPCCBPJPDMNKPPCCBACPEABAJAAMNPJPBMDNJPAMNGNGNMIMNDFPBMDLIPCMNICPBMNONPCMNLIPCMNDCOLMNPDPCMNGNGNDOAAMCNKPCDMDCODPEMNLIPCMNONPCMNLIPCCBAHAAMNECPB 55DATAMJABDBENMNPLPCDOANMNHPPBMDADPDHIMNHPPBHJMDHPPBCGBECOPPCNMCAHPDCFMCAFPDMJABFAPPMNADPDMNGNGNMAAFMCBDPDANMCBDPDMDIHPBDKDCPFGPCGAABBIAAAMDCFDHNFOFBKJGMCDOPDCDBDANMCDDPDOBNBMJHHCDAFMCEBPDMJDOCAAGBICBPLPEMN 56DATAEBPDDOEGDCBDPFMJCKPHPEMNCCPCMNEGCBMDKPCAOFMNEIPDOBBBPLPEABAGAAMNNLGLDOCOBCBDABACAAMNNLGLMNGDPCKPDCBEPFCBAABKMNECPBMNNJPADKCPPFKHMJDCOFPEBBOHPDCBFLPGABAFAAMNNLGLMJMNMLGOCBOBPDDGDJDHMNOGBHMNLIPCMNMCPCMN 57DATAADPDMNGNGNMCLIPCMNMLGOCBOBPDDFDHMDOGBHCBOHPDHONGDKNKOGBHCDMDOGBHDACAECHJHEGFHDCAGGHCGFGFAADJDIEODBEEAAENDHEJDBEFAAACADABFKFKDBAEABAAIEPPEGFKFKDBAEABAAJGAPCEFKFKDBAEABAAJEAPCGBCEEHCGJHGGFCAEOGPHECAFCGF 58DATAGBGEHJAABCEDGPGNGNHFGOGJGDGBHEGJGPGOCAEFHCHCGPHCAABCFHHCGJHEGFCAFAHCGPHEGFGDHEAABCEEHCGJHGGFCAFEHCGPHFGCGMGFAABCEEGJHDGLCAGOGPHECAGJGOCAGEHCGJHGGFAADEFAHCGJGOHEGFHCCAGOGPHECAHCGFGBGEHJAAAFEGGJGMGFCAEF 59DATAHIGJHDHEHDAADJEEGJHDGLCAEGHFGMGMAABKEGGJGMGFCAEFGNHAHEHJAADJEEGJHCGFGDHEGPHCHJCAEGHFGMGMAAAHFCGBGNCAEGHFGMGMAADGAADHECGBGECAEGGJGMGFCAEOGBGNGFAACAFAHCGFHDHDCAGBGOHJCAGLGFHJCOAAAAAAAAAAAAAAAAAAAAAAAALM 60DATANEDFNPAAAAABAAAAABABAAAAAAAAACPHAAAAAAAAAAAAAACJPOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 61DATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 62DATAEND \ No newline at end of file diff --git a/clients/ts-dos/TS-DOS.100.pre-install.txt b/clients/ts-dos/TS-DOS.100.pre-install.txt index 90b9d9d..26ea8be 100644 --- a/clients/ts-dos/TS-DOS.100.pre-install.txt +++ b/clients/ts-dos/TS-DOS.100.pre-install.txt @@ -1,4 +1,3 @@ -Type the following in BASIC on the portable: +Type the following into BASIC on the TRS-80 Model 100/102 RUN "COM:98N1E" - diff --git a/clients/ts-dos/TS-DOS.200 b/clients/ts-dos/TS-DOS.200 index 1400b1e..f198a8d 100644 --- a/clients/ts-dos/TS-DOS.200 +++ b/clients/ts-dos/TS-DOS.200 @@ -1,74 +1 @@ -1'-=CO Loader=- -2'-=V1.01 9/21/2017 Kurt McCullum=- -3 ?"Installing TS-DOS" -4 CLEAR256,55189:Y=55189 -5 TL$="":READTL$:IFTL$="END"THEN10 -6 FORX=1TOLEN(TL$)STEP2 -7 TV=((ASC(MID$(TL$,X,1))-65)*16)+(ASC(MID$(TL$,X+1,1))-65) -8 POKEY,TV:Y=Y+1:NEXT -9 ?".";:GOTO5 -10 ?"Done" -11 OPEN"TMP.DO"FOR OUTPUT AS #1 -12 IFPEEK(100)=245THEN14 -13 ?#1,"0 CALL55189:MENU":GOTO15 -14 ?#1,"0 EXEC55189:MENU" -15 CLOSE#1:CLS:?"Type SAVE "+ CHR$(34) + "TS-DOS" + CHR$(34) -16 ?"Then type MENU" -17 LOAD"TMP.DO" -100 DATA MDKANHDCHMONKPDCHIONMJMMGGOCCBAAAADJCCGOONDOABMNBMOMKPDCHGONDCHMONDCHLONDCHDONDMDCALOCCBMJNHCCHAONMNENEPMNGDEPCBDCOKCCDEOPMNIANIDKHHONDCALOCMNJONICBGAOKOFPFPOACNCCENIDKGNONKHMKCENIDKHIONEPAMAGAAMFDK -110 DATA ALOCDCHHONMNOLNMMBAEANMKGAOKHIDCHHONDCALOCMFMNNJNMCKIGONCLHOPODOMCPHNHMBPBCBPHNHPFMFOFPFMNFOEPDKHLONLHMKENNIPBKHMKEMNNDNMKJNNPDNMKMFNPDNDNMKHNNPDNDNMKCKNPDNOBMKGJNIMDMJNHPBKHMKHFNODNMKEGNODNMKBGNNDNDN -120 DATA MKGOOADNDNDNOBMKGJNIMDMJNHMNFCOMMNLFIHMNENEPMNGDEPCBAAAACCDEOPCKGOONPJMJCBABABCCAGOPCBHKOAMNMMBBKPDCGNONDOABDCHHONCBACACCCAGOPMDIHNKDKALOCDCHHONMNNJNMDKHHONDNDHDPAHFPBGAACBHAOPBJOLCBAPCDCCAGOPMNHMEPON -130 DATA MNIIEPMNALEHMNINEPMNCEOAMNADILMKMJNINKFINKPOANMKIMNJPOBHMKPGNJPOBEMKOENJPOFNMKOENJPODPMKMDNJPOACMKMDNJPOBOMKFINJPOBPMKGGNJPOBNMKEHNJPOBMMKDENJPOCAMKDENJOGNPPOFEMKDKNKPOEBMKHPNJPOEHMKHPNJPOFFMKPKNJPOEE -140 DATA MKAFNMPOEMMKAONMPOFAMKAONMMDMJNIDOABCBHHONIGCDLONKHENJMKHENJDOABMDHENJDKHHONDNMCHENJDKHIONKHMKMJNIMDHENJDKHHONNGAENKMJNIMKMJNIMDHENJDOAECBHHONIGEPCDHOLJNKMJNIHJPFMNOLNMPBDCALOCMDJONIDODOMNBGNKDOACDCGN -150 DATA ONMDJONIDKHCONPOACMCMJNICKIGONABAHAAAJHOPODMMCMJNIMNNDOLCKIGONMNLLNPMNKMOCCBACAAMNLNOCCKIGONBBGDONABAJAAMNBGIDKPMDNGNJDKHLONKHMKMJNIDKHIONPOCONKMJNIDKHGONDMDCHGONDOABDCALOCMNIANIMDJONIDKHLONKHMKMJNIDK -160 DATA HGONKHMKMJNIDNMDNGNJKPMDNGNJDOCAMNBGNKKPDCGNONMDJONIDKHCONDNMCMJNIDKHDONOOABDCHDONMJDCGNONDKHIONKHMIKPDMDCHHONMNPENMCKIGONCLDKGNONOHDKHHONCBHIONLONKBPNKKHMJMNPENMMNIIEPCKIGONCLDODOLOMCEMNKDOCAOHMNINEP -170 DATA CBGNONDGACMDDENJPOAINCMJNIPOAFMKHPNKPOADMADKHLONOOABDCHLONKPDCHGONDOABDCALOCMNENEPMNIANIMDJONIDKHLONKHMIMDPGNJCBHAOPCCHNONKPHHCDHHDKHLONKHMKKJNLKPDCHMONMNPMOIMCFLNLDKHGONKHMKLNNKMNJINHMNAJOJDKHMONDNMC -180 DATA KJNKKPDCHMONDCHIONMNAJOJDKHIONPOCONMPGNLDKHCONKHMKOLNKDNMKNJNKCBGDONMDPBNKCBABCECCAGOPMNIIEPDOCDOHDKHDONMGDAOHMNINEPMDAENLOFCBABCBCCAGOPMNIIEPOBAOAGMNJAOKMNINEPCBKIOAABICNLDKHLONKHMCBHNLCBOKOAABGONLMF -190 DATA OFDKALOCCBHIONLONKCJNLHOKHMCCJNLDMDCHHONCBAPALCCAGOPOBMNMMBBDKHLONKHMKFBNLDKHCONKHMKFBNLCBPKOBDNMKEONLCBOJOBMNMMBBMNIMNLCBAPBFCCAGOPMJKPMNPGNLCBKIOAABGINLMDBHNLCBENOBMDMMBBCKGFPGOLCKGDOPHNJDGPHMJKGHAB -200 DATA PCPPAJMDALEHMNLAOLMNALEHDODAOHMJDKHLONKHMADKFNPFBBADOCPOAGMKJPNLBBAHOCCBBABJCCAGOPOLMDMMBBCBLFPCKPDCHIONDOCODNPFHOOOIAOGJPMCOCNLOFCDFOCDFGODOFMNPNNOOLMNFIOJOBODCDAOAGMNJAOKDOCOOHAOACMNJAOKDOCAOHCBHION -210 DATA DEOBBBALAABJPBDNMCLDNLDKHIONPOCONMPGNLMDMINKPFCBNOOBMNMMBBPBDMPOCOMCPGNLMJMNMPNMMNEGCJMDJONIDCIBONBBAHAACKIGONBJHOPOEEMCJONIMNHANMDKIBONPOEMMMENEPMEMPNMMNELNMPFMNJCNMPBMKCMNMDKIBONPOEMMCEFNMDOAHOHMNBO -220 DATA OAMNENEPMDGAOKDKHLONKHMCNIOCCKHPONABIAAAHJAINCGBNMAJHNGMEPCCHPONCKICONOFAJCCICONOBPOIAMJMNNDOLCKIGONDKHLONKHMCIMNMMNODOLNFMNPNNOCCHPONOBCCICONMJMNLLNPMDKMOCOLLHMIEPDKIBONPOEMMCKJNMMNLGNMBKOHBDANMCJONM -230 DATA MJMNLGNMBKMNBEFKBDANMCKJNMMJMNAEBEMIMNPHBCPOADMKEFNMPOBLMKEFNMPOCAMCLGNMMDPHBCNLLLOGAGOOACMCCGOKMJMNPENMCCIGONMNIIEPCLAOAKMNJAOKMDINEPMNINEPMNPENMMDOCNMDKHHONDNFPBGAACBAEAAMNNCEECDCDOFCBAFAAMNHJEENBFF -240 DATA OLCECCAGOPMNIKFDCDMJMNODOLCCICONCBFLOBMNEAOAMIMNKNOKCKICONABAJAAAJBBEMPHHOBCBDCDHOBCMNOJOLMCCJOKCKICONCDCDCDBBEGPHABAGAAOLMDBGIDMNNDOLCKIGONMNLLNPDKGNONKHMCHENNCBILOBMNEAOAMKHENNMNKNOKABACAACBJBONBBEM -250 DATA PHMNBGIDMNOJOLMKIHNNCBMBOBMNJPOKMNBEOAMAMNEPNOCKLPONHMGFGPCCLPONDKJBONPOEEMCKANNCKGBPGDOMAMDMBNNPOEDMCLDNNCKGDPGCCHHONCKGFPGDOKAMDMBNNCKLPONCCHHONCKJFPCCCICONDOIADCIBONCCIEONCKLPONCCHPONOFMBDKIBONPOKA -260 DATA MKNONNADPOIAMCNONNADMFMNABCMMBOFMFCKIEONMNKIICNKDFOKCKIEONMBDGAACDALHJLAMCPCNNCLNBDKIBONPOMAMCAONODGBKCKIEONCLMDDGNOPOKAMCBPNOCKHHONCCGDPGCKIEONMDDGNONFCKGBPGCLCCJFPCCDOLCKHHONCDCDBJCCGBPGNBCKICONOLMN -270 DATA KMCNMNEJODMNGCCMCBACAAMDLNOCMNGCNOCKIGONMNKNOKMNOJOLPOMAMKLFCKPOKAMKMOCKPOIAMKAMCLMJDKGNONDNMIMNAHOADKGNONKHMIDOABDCGNONMJMNNDOLMNODOLNFOFCDCDCDBBIKONABAGAAMNBGIDDOCOBCBDABACAAMNBGIDDKGNONKHMCLCNOCBIC -280 DATA OBMNEAOAMKLCNOMNKNOKABAGAABBIKONCBEGPHMNBGIDOBNBNFMNPNNOHMLFMKCPOKOFDOABDCHIONMNOOOKMNAKOMMKPINOCBJMOBMNJPOKMNBOOAOGNPOHPOFCMCOGNOMNCHOFMNAKOMMDPINOOBNBPOEBMADKJBONPOEEMADOACDCHIONNFOFOBNBMDOKOCHOPOMA -290 DATA MKBANPPOKAMKBPNPNFMNBPAGMBAICLMJOLBBAAAAHOCDBDPOBKMCBENPBLOLMJOLCDCDEOCDEGCBAGAAAJMJDKHCONKHMIDNMKAGNKMNNDOLCBHCOBMNEAOAMIMNKNOKCBEGPHBBIKONABAGAAMNBGIDCBDMDOCCJBONDOCODCJAONMNOOOKDKHCONPOACMAMNAKOMMC -300 DATA HHNPDOABDCIKONCBABABMNLNOCCBACAAMDLNOCMNKMOCMDHBNPDKHCONPOACMICBCMOBMNJPOKMNBEOAMAMNOOOKMNIOOLCBAGAAMNLNOCDOAHOHMJMNGCNOMNNDOLMNKJNPMDCHOFABAJAACKIGONBBIKONMNBGIDMNOOOKMDAKOMMNKNOKCBEGPHMNOPOLMJMNNDOL -310 DATA MNKJNPCBIKONBBEPPHABAJAAMNBGIDCKLPONEMEFCBMAHHAICBFLOBMCPNNPABAJAAMNEDOAMIOFPFMNNDOLPBOBBBIKONEPAGAAMDNCODMNEAOAMIMNKNOKMDMJODCBJEOBMNJPOKMNBEOAMCGAOKMJMNBOOAOHPOFJMIPOHJMJMNCEOAMDPHBCKPDNDCDHOPMNAEBE -320 DATA MKCEOAKPDCDHOPCBCCPCLGMKJHCGMNNOBDMDCEOAABAGAAMNFFOAMIOFPFMNBDBAHHCDKHMCEJOAPBOBMJMFMNJPOKMNPGFENKGAOKCDHIDNNBMILLNIOFBJDGAAOBHLLHMJCKAHPFHOPOEJMCGGOCMDDLOCBLHACAFEFDCNEEEPFDCACIDCDADACAHGDECODBDACJCA -330 DATA DCDADBDECAFEFDEJCPELFACACACACACACACACACABLHBCAAAEEEJFDELCAEGHCGFGFDKCACACACACACACACACAEGGJGMGFDKANAKEMGPGBGECAELGJGMGMCAEOGBGNGFCAFCGBGNCACAEGHCGNHECAEMGPGHCACACACACACACAENGFGOHFAACAFCEBENCAEGHCGFGFDK -340 DATA CACACACACACACACACAEGGJGMGFDKANAKFDGBHGGFCAELGJGMGMCAEOGBGNGFCAEEGJHDGLCAEEEPFDCNCACACACACACACACACACACAENGFGOHFAAEJGOHDGFHCHECAEEGJHDGLCMCAFAHCGFHDHDCACCFJCCCAHEGPCAGCGFGHGJGOCAAAEOEPFECAEGEPFCENEBFEFE -350 DATA EFEEAAEOGFHHCAEOGBGNGFDKAAFDHJHDHEGFGNCAGOGBGNGFDKAAEEGJHCGFGDHEGPHCHJCAGOGBGNGFDKAAFDGBHGGFCAGBHDDKAAEMGPGBGECAGBHDDKAAFDHFHCGFCADPCAAAEGGJGMGFCAGFHIGJHDHEHDCMCAEBCJHAHAGFGOGECAFCCJGFHAGMGBGDGFCAFBCJ -360 DATA HFGJHEDKAAEGGJGMGFCAGFHIGJHDHEHDCMCAFCGFHAGMGBGDGFDPCACIFJCPEOCJDKAACNCOCNCACACACACACACAAABLFJDPDECACACACABLFJDPDOENGLEEHCAABLFJDPDOECGBGOGLAAEPEGEGAAEPEOCAAAABEJMDBGOCMDGGOCMDDLOCODAGCGBBNFPIMNKHDCCD -370 DATA CDOLDGANCDDGOCCDDGMDCDHDCDHCOBMDNFPIOBCDCDCDOFCBAMOCMJCBKIJMCCAHPFCCAJPFCBKIJMCCDHPFCCDJPFCCBPPFCCCDPFCBAGAJCCFNPFCCGDPFCCGFPFCCGHPFCCGJPFMJMNDLOCCBAMOCCCAHPFCBDCOCCCAJPFCBENOGCCCDPFCBLNOGCCBPPFCBMAOF -380 DATA CCGJPFCBBAOGCCGHPFCBDBOFCCDHPFCBOFOGCCDJPFCBEKOFCCFNPFCBHIOFCCGDPFCBJGOFCCGFPFMJCBABABDOADDCIKONMDLNOCGHCOAEMDMJOCMNMNOJMNJLOLMNGEOJMDJAOJCCKEONCBKEONMNNDOJMNGEOJMDJAOJCBADAAMNMNOJMNGEOJDKKFONCBKGONPO -390 DATA IAMJNFOFMNCDODOBCCHHONOBBBKGONABIAAAMNBGIDOFCKHHONABIAAAAINKBGODMKBGODCCHHONDOIAMNLHOCMDPDOCOBDKHHONMNLHOCCBACAAMDLNOCBBAAAFOLMNNCEEDKMBONJFNKCMOKMKCMOKDKHIONDCIKONCBABABMDLNOCCKIEONMNKIICNKDFOKCKIEON -400 DATA OFMNKMOCMNNIOCEPAGAANBMNBGIDNFDKKFONCKHPONEPAGAAAICCHPONNKHAODHNLEMCFAODNBMJDKHPONPOABMIMNEDOLDOFHDCHFONMNNLOEMNEDOLMNJIODCKIGONMNIEOKCCIGONMNPLOEMDHCODDKKLONMNLDODPODFMKKLODCBIAONDEHOPOBEMACBHPONDECD -410 DATA DGABMJABAAAFPODFMIABEAAAMJCBAIAAMNMNOJMNHOOLMDEDOLCBEGPHBBIKONABAGAAMNBGIDDKHCONKHMCIPOEMNAKOMMCCJOKBBAAAFMNBFOFCBAAABCCHPONMNLNODMNKAOECKGJPGOFAGCIBBEPPHMNHKOENKCGOKHIKHMKCGOKOFBBIKONOLABAJAAMNBGIDOB -420 DATA OFBBMCONABBPAAMNBGIDNBNFCBBPAABJOFCKGJPGABAAAFAJMBMFAIODMBMNBGIDCKGJPGAGCHBBMCONMNHKOEOFCKGJPGABPPAEAJOFABBPAAAINBMBMFOFAIOFMBADOBMNCBIDCBMCONNBABBPAAMNBGIDCBABBECCIAONOBOBCCIGONCBAAABCCHPONMNHCODMNHI -430 DATA OLMJAOAJMNLMOLMINIHOLHMIMFABBPAAAJMBAFMIMDHKOECBANBJMNMNOJMNGEOJDKKGONKHMCCJOKMJCKIEONMNKOOEDKHPONDNMIMDKDOEOFDOFCDCHFONMNNLOEMNJIODDOANMNAKOKMNKAIPOBMNMAOJHHCDALHJLAMCMDOEMJAGCPNGAKAENCNBOEMGDKEPMJDK -440 DATA HPONMNMPOEDKHFONMNAKOKMNIGOLDKIAONMNMPOEDOCMMNAKOKMNIGOLMNHOOLCBKGONOFAOAIMNLKOJANMCABOFOBHOPODAMIPOECMKBIOKMDBCOKCKGJPGOFCCIEONCBMAPPDJMBAINPNKDFOKMJKPDCKDONCBAFAAMDLNOCPODANIPODCNANGDADCHDONCDBNHOPO -450 DATA DKMACDBNODOBDOAJLHMJNFOFPFKPMNBMOMDIACONHOKHMKGNOFPODKMKGNOFMNDNBBHLPOACNCHDOFDCHDONDIACNJMNMBOIMNFCOMPBOBNBMBMJPOAJMCAGAJNFOFPFKPMNBMOMCBEGPHMNOPOLMKCGOKMNCHOFMNFCOMMDHDOFPOAJMCAGAJNFOFPFKPMNBMOMCBEP -460 DATA PHMNOPOLMKCGOKCBIKONBBEPPHABAJAAMNBGIDMNMJODMNFCOMMDHDOFPOAJMAMBOFABEDEPMNJCOGMKCGOKMNKMOCCKLPONHNGMGHCCHPONMNNIOCMNKCDCOFMNAJDDCKPLPEOFOFCKLEOOODMBAINKDFOKCKPNPEAJABANOCAINCAGAJNBOBDKKFONNGAGEPAGAAMN -470 DATA FHODMNFCOMMDIKDCPOAJMAMNLDDAABEDEPMNJCOGMCCJOKDOABDCHIONCKPNPEABAGAAMFAJOFMNCDODOBCCHHONMBCBPLPEBBKGONMNBGIDCKKGONABHKAAMNPKOCMNFCOMMDCMAFPOAJMAMBPBPFMFMIJPDCEFPHABECEBMNJCOGMKCGOKCKLPONEMEFGAGJCCHPON -480 DATA MNBMCMCDCCIEONCKHPONOFOFMBMNEAODNBCKGBPGBJCCGBPGCKAFPFBJCCAFPFMNFCOMKPMDGFDBPFOFNFMFCKEMPHBBCACANPMKKMOGNBNFNPMKKMOGMBNBOBPBMBMJOBCCEMPHNBOBPBKPMNBMOMCBEGPHMDOPOLPOAJMAABECEBMNJCOGMCCJOKMNBLAGCKGHOPOL -490 DATA NPMKCNAFNFMBAIDOABDCHIONMNOKOCMNFCOMMDCNAFPFKPDCHEONPBCCGOONNFOFPFBBAEAABJHOPOAJMKPPOGPBOBNBMJPBPFPOAAMKCDOHPOACMKKNOHPOAEMKGPOIPOAGMKMKOHPOAIMCPLOGDIAGCBEJBHNJMDPLOGKPMNBMOMCBEMPHHOPOCAMKDFOHPOEEMCDI -500 DATA OKDGEECDDGEPCBEGPHMNOPOLEPMNBKOIDGAAOBOBNBNFOFFBHLOGAHCBHMOMEPAGAAAJHODNMKJCOHDNMCKFOHDKLOONKHMCGIOHCDCDHODCIKONCBABABMNLNOCCKGOONBBAHAABJKPHHCDFEFNCDCDNJOBNBHLPOAIMCILOHBOACHDODCBPFBFODMJHKLHMKGIOHOF -510 DATA MNCHOFCBEGPHMNOPOLOBMDGIOHHKLHMKCGOKMDGIOHPBOBNBOFHODGAAPOACMMJKOICBACAAMNLNOCMNFCOMOBODOBCBBIFMOFMJMNBKOIHODGAAKHMCOKOHCKGOONABAHAAAJHOCDOFKHMMCCOINBONHOCDNJBLOLDFPOBKDHDPMCPGOHMNBKOIHHDHPFDIBAONABAJ -520 DATA BKAIMCBAOIMNBKOIPBHHEPJPMNFLEBDIBCOLPJMJPBOBOBNBODCBEGFNODMJCKNBPEBBMAPEBJMJCBADAAMNMNOJMNGEOJDKKEONPOBAMCFNOIDKKFONCKGOONBBAIAABJOFFEFNCDCDNJBBKGONOLEPAGAAMNBGIDDKKFONPOIAMKFJOIOLDGBKDMOBCLHHMJCKGOON -530 DATA BBAHAABJDGABCDFEFNCDCDNJDGBKMJCKGOONBBAHAABJHOPOIAMMJKOIPBOBNBOBPBPFPOBKMKJFOICKGOONBBAHAABJDECDOLONHHCDNJCBABBGOFMJCKGOONABAHAAAJHOOFCDCDCDBBKGONEPAGAAPFMNBGIDPBKHMELHOCOBDGAACDFEFNCDCDNJMJPBMJCBHAOP -540 DATA CCHNONMNPMOIMCBFOKKPDCHMONDCHGONDMDCAHOPMNAJOJDKHIONPOCOMMGJOKCBHGONDEMNAJOJMNDOEPMNLAOLMNALEHCBFPOMMNMMBBMDDOEPKPDCHIONMNOOOKCBEGABMDDGOJDOCODCHJONDKHCONKHMCBOOJCBPGACMNDGOJMCJAOJDKKGONKHMIDKHMONKHMM -550 DATA EIOJCBHIONDEDKHJONLOMCBFOJMJCCKCONCBAABKMNMNOJMNGEOJDKKEONPOBBMJAOAJCBKGONMNJAOKDOCAOHCKLPONFMFFCKHNONOLNJOLCDCDCCHNONMJCBKEONOFMNLKOJMNLKOJEPMNLKOJEBHJKHMKHPOJMNLKOJANMCHIOJOBAEAEKPDAIGCDAFMCIEOJCPLO -560 DATA MCBFOKMJDKKEONPOBCMCBFOKDKKGONKHMIPOBAMKCJOKPOFAMKBIOKPOGAMKCMOKNKBFOKMKBOOKPOIANKBOOKMDBLOKMNMAOJHHCDMJMNANOKMNBJIFNKDIOKMCBFOKMJCCIIONCBIIONOFDKHDONKHMKOCOJOGABBPBPBPLGHHHOCDIGEHHOPFCDKHMKPGOJEPHIIG -570 DATA CDANMCOPOJEHHICPFHABFKFKMNIGOLMBEIAGAAADADOBMNIEOKHKMNEDIGNLMPOGIAMADOABCBDOACCBDOADCBDOAECBDOAFCBHLONDGAACBDOAGCBDOAHCBDOAICBDOAJCBDOAKCBDOALCBDOAMCBDOANCBJKOMEPANMKEPOKHOCDPOAAMCEFOKMDEBOKDKHEONKHMK -580 DATA HLOKDOAHOHCDMNJPOKMNHCOKCKGOONPJCKHAONOFMJMNDOEPMNHCOKMDDOEPCBFDONMNMMBBMDPHBCFOCKGOONDGAAMDIIAEHOMNAKOKCDALHJLAMCIEOKMJHOPOCANCJIOKDOCAOHCDANMCJAOKMJOFCBBAABCCAGOPMNHMEPOBMDMMBBOFDOCACBEGPHAGAIMNMMOL -590 DATA OBBBEGPHAGAGHOPOEBNKDLOKHOPOCOMKNGOKLHMIBCCDBDAFMCMEOKMDNOOKDOCABCBDAFMCNIOKAGACCDHOLHMIBCBDAFMCOAOKDOAABCMJMNCMOMMNENOLMNGEOJMNJAOJDKHCONKHMICBAIAAMNMNOJMNGEOJDKKFONPOABMKCCOLDNEPDOACDCHCONAGAACBKHON -600 DATA BBGDONMDBGIDCBCDAAMNMNOJMNGEOJCBHPOMMNDKOLCBIIOMMNDKOLCBJBOMABAJAAMNIEOKMDGEOJMNAIIFMIMNMAOJMDEDOLMNANOKMNHIOLMNEDOLMNLNODMNHOOLMNAIIFDOAAMCGFOLDMDCHCONMNEDOLMNHIOLMNEDOLCBAHAAMNMNOJMJABDBENMNIGOLDOAN -610 DATA MNAKOKMDIOOLHIMNAKOKHJMDAKOKCGBECOPPCNMCJCOLCFMCJAOLMJABFAPPMNIOOLMNAIIFMAAFMCJOOLANMCJOOLMDBCOKDKMBONGPCGAABBIAAAMDHJEENFOFBKJGMCMJOLCDBDANMCLOOLOBNBMJHHCDAFMCMMOLMJDOCAAGBICBIKONMNMMOLDOEGDCKCONMJCK -620 DATA IGONMNKNOKMNGCCMMDMMCLOFMNNDOLOBBBIKONABAGAAMNBGIDDOCOBCBDABACAAMNBGIDMNOOOKKPDCKDONCBAABKMNMNOJMNGEOJDKLOONKHMJDCHEONBBHEOMCBDMOPABAHAAMNBGIDMJMNLFIHCBGMOMDGDJDHMNBNBJMNEDOLMNENOLMNIOOLMNAIIFMCEDOLMN -630 DATA LFIHCBGMOMDFDHMDBNBJCBHEOMHONGDKNKBNBJCDMDBNBJDACAECHJHEGFHDCAGGHCGFGFAADJDIEODBEEEOEOAAENDHEJDBEFEOEOAAACADABFKFKDBAEABAAIEPPEGFKFKDBAEABAAJGAPCEFKFKDBAEABAAJEAPCGBCEEHCGJHGGFCAEOGPHECAFCGFGBGEHJAABC -640 DATA EDGPGNGNHFGOGJGDGBHEGJGPGOCAEFHCHCGPHCAABCFHHCGJHEGFCAFAHCGPHEGFGDHEAABCEEHCGJHGGFCAFEHCGPHFGCGMGFAABCEEGJHDGLCAGOGPHECAGJGOCAGEHCGJHGGFAADEFAHCGJGOHEGFHCCAGOGPHECAHCGFGBGEHJAAAFEGGJGMGFCAEFHIGJHDHEHD -650 DATA AADJEEGJHDGLCAEGHFGMGMAABKEGGJGMGFCAEFGNHAHEHJAADJEEGJHCGFGDHEGPHCHJCAEGHFGMGMAAAHFCGBGNCAEGHFGMGMAADGAADHECGBGECAEGGJGMGFCAEOGBGNGFAACAFAHCGFHDHDCAGBGOHJCAGLGFHJCOAA -660 DATA END \ No newline at end of file +0'TS-DOS Loader V1.01 9/21/2017 Kurt McCullum - 2022 b.kenyon.w@gmail.com 0CLEAR256,55189:S=55189:Y=S:C$="TS-DOS":D$="TMP.DO":?"Installing "+C$ 1TL$="":READTL$:IFTL$="END"THEN3 2FORX=1TOLEN(TL$)STEP2:TV=((ASC(MID$(TL$,X,1))-65)*16)+(ASC(MID$(TL$,X+1,1))-65):POKEY,TV:Y=Y+1:NEXT:?".";:GOTO1 3?"Done":OPEND$FOROUTPUTAS#1:?#1,"0 CALL";S;":MENU":CLOSE#1:CLS:?"Please Type:":?"KILL "+CHR$(34)+D$+CHR$(34):?"SAVE "+CHR$(34)+C$+CHR$(34):?"MENU":LOADD$ 4DATAMDKANHDCHMONKPDCHIONMJMMGGOCCBAAAADJCCGOONDOABMNBMOMKPDCHGONDCHMONDCHLONDCHDONDMDCALOCCBMJNHCCHAONMNENEPMNGDEPCBDCOKCCDEOPMNIANIDKHHONDCALOCMNJONICBGAOKOFPFPOACNCCENIDKGNONKHMKCENIDKHIONEPAMAGAAMFDK 5DATAALOCDCHHONMNOLNMMBAEANMKGAOKHIDCHHONDCALOCMFMNNJNMCKIGONCLHOPODOMCPHNHMBPBCBPHNHPFMFOFPFMNFOEPDKHLONLHMKENNIPBKHMKEMNNDNMKJNNPDNMKMFNPDNDNMKHNNPDNDNMKCKNPDNOBMKGJNIMDMJNHPBKHMKHFNODNMKEGNODNMKBGNNDNDN 6DATAMKGOOADNDNDNOBMKGJNIMDMJNHMNFCOMMNLFIHMNENEPMNGDEPCBAAAACCDEOPCKGOONPJMJCBABABCCAGOPCBHKOAMNMMBBKPDCGNONDOABDCHHONCBACACCCAGOPMDIHNKDKALOCDCHHONMNNJNMDKHHONDNDHDPAHFPBGAACBHAOPBJOLCBAPCDCCAGOPMNHMEPON 7DATAMNIIEPMNALEHMNINEPMNCEOAMNADILMKMJNINKFINKPOANMKIMNJPOBHMKPGNJPOBEMKOENJPOFNMKOENJPODPMKMDNJPOACMKMDNJPOBOMKFINJPOBPMKGGNJPOBNMKEHNJPOBMMKDENJPOCAMKDENJOGNPPOFEMKDKNKPOEBMKHPNJPOEHMKHPNJPOFFMKPKNJPOEE 8DATAMKAFNMPOEMMKAONMPOFAMKAONMMDMJNIDOABCBHHONIGCDLONKHENJMKHENJDOABMDHENJDKHHONDNMCHENJDKHIONKHMKMJNIMDHENJDKHHONNGAENKMJNIMKMJNIMDHENJDOAECBHHONIGEPCDHOLJNKMJNIHJPFMNOLNMPBDCALOCMDJONIDODOMNBGNKDOACDCGN 9DATAONMDJONIDKHCONPOACMCMJNICKIGONABAHAAAJHOPODMMCMJNIMNNDOLCKIGONMNLLNPMNKMOCCBACAAMNLNOCCKIGONBBGDONABAJAAMNBGIDKPMDNGNJDKHLONKHMKMJNIDKHIONPOCONKMJNIDKHGONDMDCHGONDOABDCALOCMNIANIMDJONIDKHLONKHMKMJNIDK 10DATAHGONKHMKMJNIDNMDNGNJKPMDNGNJDOCAMNBGNKKPDCGNONMDJONIDKHCONDNMCMJNIDKHDONOOABDCHDONMJDCGNONDKHIONKHMIKPDMDCHHONMNPENMCKIGONCLDKGNONOHDKHHONCBHIONLONKBPNKKHMJMNPENMMNIIEPCKIGONCLDODOLOMCEMNKDOCAOHMNINEP 11DATACBGNONDGACMDDENJPOAINCMJNIPOAFMKHPNKPOADMADKHLONOOABDCHLONKPDCHGONDOABDCALOCMNENEPMNIANIMDJONIDKHLONKHMIMDPGNJCBHAOPCCHNONKPHHCDHHDKHLONKHMKKJNLKPDCHMONMNPMOIMCFLNLDKHGONKHMKLNNKMNJINHMNAJOJDKHMONDNMC 12DATAKJNKKPDCHMONDCHIONMNAJOJDKHIONPOCONMPGNLDKHCONKHMKOLNKDNMKNJNKCBGDONMDPBNKCBABCECCAGOPMNIIEPDOCDOHDKHDONMGDAOHMNINEPMDAENLOFCBABCBCCAGOPMNIIEPOBAOAGMNJAOKMNINEPCBKIOAABICNLDKHLONKHMCBHNLCBOKOAABGONLMF 13DATAOFDKALOCCBHIONLONKCJNLHOKHMCCJNLDMDCHHONCBAPALCCAGOPOBMNMMBBDKHLONKHMKFBNLDKHCONKHMKFBNLCBPKOBDNMKEONLCBOJOBMNMMBBMNIMNLCBAPBFCCAGOPMJKPMNPGNLCBKIOAABGINLMDBHNLCBENOBMDMMBBCKGFPGOLCKGDOPHNJDGPHMJKGHAB 14DATAPCPPAJMDALEHMNLAOLMNALEHDODAOHMJDKHLONKHMADKFNPFBBADOCPOAGMKJPNLBBAHOCCBBABJCCAGOPOLMDMMBBCBLFPCKPDCHIONDOCODNPFHOOOIAOGJPMCOCNLOFCDFOCDFGODOFMNPNNOOLMNFIOJOBODCDAOAGMNJAOKDOCOOHAOACMNJAOKDOCAOHCBHION 15DATADEOBBBALAABJPBDNMCLDNLDKHIONPOCONMPGNLMDMINKPFCBNOOBMNMMBBPBDMPOCOMCPGNLMJMNMPNMMNEGCJMDJONIDCIBONBBAHAACKIGONBJHOPOEEMCJONIMNHANMDKIBONPOEMMMENEPMEMPNMMNELNMPFMNJCNMPBMKCMNMDKIBONPOEMMCEFNMDOAHOHMNBO 16DATAOAMNENEPMDGAOKDKHLONKHMCNIOCCKHPONABIAAAHJAINCGBNMAJHNGMEPCCHPONCKICONOFAJCCICONOBPOIAMJMNNDOLCKIGONDKHLONKHMCIMNMMNODOLNFMNPNNOCCHPONOBCCICONMJMNLLNPMDKMOCOLLHMIEPDKIBONPOEMMCKJNMMNLGNMBKOHBDANMCJONM 17DATAMJMNLGNMBKMNBEFKBDANMCKJNMMJMNAEBEMIMNPHBCPOADMKEFNMPOBLMKEFNMPOCAMCLGNMMDPHBCNLLLOGAGOOACMCCGOKMJMNPENMCCIGONMNIIEPCLAOAKMNJAOKMDINEPMNINEPMNPENMMDOCNMDKHHONDNFPBGAACBAEAAMNNCEECDCDOFCBAFAAMNHJEENBFF 18DATAOLCECCAGOPMNIKFDCDMJMNODOLCCICONCBFLOBMNEAOAMIMNKNOKCKICONABAJAAAJBBEMPHHOBCBDCDHOBCMNOJOLMCCJOKCKICONCDCDCDBBEGPHABAGAAOLMDBGIDMNNDOLCKIGONMNLLNPDKGNONKHMCHENNCBILOBMNEAOAMKHENNMNKNOKABACAACBJBONBBEM 19DATAPHMNBGIDMNOJOLMKIHNNCBMBOBMNJPOKMNBEOAMAMNEPNOCKLPONHMGFGPCCLPONDKJBONPOEEMCKANNCKGBPGDOMAMDMBNNPOEDMCLDNNCKGDPGCCHHONCKGFPGDOKAMDMBNNCKLPONCCHHONCKJFPCCCICONDOIADCIBONCCIEONCKLPONCCHPONOFMBDKIBONPOKA 20DATAMKNONNADPOIAMCNONNADMFMNABCMMBOFMFCKIEONMNKIICNKDFOKCKIEONMBDGAACDALHJLAMCPCNNCLNBDKIBONPOMAMCAONODGBKCKIEONCLMDDGNOPOKAMCBPNOCKHHONCCGDPGCKIEONMDDGNONFCKGBPGCLCCJFPCCDOLCKHHONCDCDBJCCGBPGNBCKICONOLMN 21DATAKMCNMNEJODMNGCCMCBACAAMDLNOCMNGCNOCKIGONMNKNOKMNOJOLPOMAMKLFCKPOKAMKMOCKPOIAMKAMCLMJDKGNONDNMIMNAHOADKGNONKHMIDOABDCGNONMJMNNDOLMNODOLNFOFCDCDCDBBIKONABAGAAMNBGIDDOCOBCBDABACAAMNBGIDDKGNONKHMCLCNOCBIC 22DATAOBMNEAOAMKLCNOMNKNOKABAGAABBIKONCBEGPHMNBGIDOBNBNFMNPNNOHMLFMKCPOKOFDOABDCHIONMNOOOKMNAKOMMKPINOCBJMOBMNJPOKMNBOOAOGNPOHPOFCMCOGNOMNCHOFMNAKOMMDPINOOBNBPOEBMADKJBONPOEEMADOACDCHIONNFOFOBNBMDOKOCHOPOMA 23DATAMKBANPPOKAMKBPNPNFMNBPAGMBAICLMJOLBBAAAAHOCDBDPOBKMCBENPBLOLMJOLCDCDEOCDEGCBAGAAAJMJDKHCONKHMIDNMKAGNKMNNDOLCBHCOBMNEAOAMIMNKNOKCBEGPHBBIKONABAGAAMNBGIDCBDMDOCCJBONDOCODCJAONMNOOOKDKHCONPOACMAMNAKOMMC 24DATAHHNPDOABDCIKONCBABABMNLNOCCBACAAMDLNOCMNKMOCMDHBNPDKHCONPOACMICBCMOBMNJPOKMNBEOAMAMNOOOKMNIOOLCBAGAAMNLNOCDOAHOHMJMNGCNOMNNDOLMNKJNPMDCHOFABAJAACKIGONBBIKONMNBGIDMNOOOKMDAKOMMNKNOKCBEGPHMNOPOLMJMNNDOL 25DATAMNKJNPCBIKONBBEPPHABAJAAMNBGIDCKLPONEMEFCBMAHHAICBFLOBMCPNNPABAJAAMNEDOAMIOFPFMNNDOLPBOBBBIKONEPAGAAMDNCODMNEAOAMIMNKNOKMDMJODCBJEOBMNJPOKMNBEOAMCGAOKMJMNBOOAOHPOFJMIPOHJMJMNCEOAMDPHBCKPDNDCDHOPMNAEBE 26DATAMKCEOAKPDCDHOPCBCCPCLGMKJHCGMNNOBDMDCEOAABAGAAMNFFOAMIOFPFMNBDBAHHCDKHMCEJOAPBOBMJMFMNJPOKMNPGFENKGAOKCDHIDNNBMILLNIOFBJDGAAOBHLLHMJCKAHPFHOPOEJMCGGOCMDDLOCBLHACAFEFDCNEEEPFDCACIDCDADACAHGDECODBDACJCA 27DATADCDADBDECAFEFDEJCPELFACACACACACACACACACABLHBCAAAEEEJFDELCAEGHCGFGFDKCACACACACACACACACAEGGJGMGFDKANAKEMGPGBGECAELGJGMGMCAEOGBGNGFCAFCGBGNCACAEGHCGNHECAEMGPGHCACACACACACACAENGFGOHFAACAFCEBENCAEGHCGFGFDK 28DATACACACACACACACACACAEGGJGMGFDKANAKFDGBHGGFCAELGJGMGMCAEOGBGNGFCAEEGJHDGLCAEEEPFDCNCACACACACACACACACACACAENGFGOHFAAEJGOHDGFHCHECAEEGJHDGLCMCAFAHCGFHDHDCACCFJCCCAHEGPCAGCGFGHGJGOCAAAEOEPFECAEGEPFCENEBFEFE 29DATAEFEEAAEOGFHHCAEOGBGNGFDKAAFDHJHDHEGFGNCAGOGBGNGFDKAAEEGJHCGFGDHEGPHCHJCAGOGBGNGFDKAAFDGBHGGFCAGBHDDKAAEMGPGBGECAGBHDDKAAFDHFHCGFCADPCAAAEGGJGMGFCAGFHIGJHDHEHDCMCAEBCJHAHAGFGOGECAFCCJGFHAGMGBGDGFCAFBCJ 30DATAHFGJHEDKAAEGGJGMGFCAGFHIGJHDHEHDCMCAFCGFHAGMGBGDGFDPCACIFJCPEOCJDKAACNCOCNCACACACACACACAAABLFJDPDECACACACABLFJDPDOENGLEEHCAABLFJDPDOECGBGOGLAAEPEGEGAAEPEOCAAAABEJMDBGOCMDGGOCMDDLOCODAGCGBBNFPIMNKHDCCD 31DATACDOLDGANCDDGOCCDDGMDCDHDCDHCOBMDNFPIOBCDCDCDOFCBAMOCMJCBKIJMCCAHPFCCAJPFCBKIJMCCDHPFCCDJPFCCBPPFCCCDPFCBAGAJCCFNPFCCGDPFCCGFPFCCGHPFCCGJPFMJMNDLOCCBAMOCCCAHPFCBDCOCCCAJPFCBENOGCCCDPFCBLNOGCCBPPFCBMAOF 32DATACCGJPFCBBAOGCCGHPFCBDBOFCCDHPFCBOFOGCCDJPFCBEKOFCCFNPFCBHIOFCCGDPFCBJGOFCCGFPFMJCBABABDOADDCIKONMDLNOCGHCOAEMDMJOCMNMNOJMNJLOLMNGEOJMDJAOJCCKEONCBKEONMNNDOJMNGEOJMDJAOJCBADAAMNMNOJMNGEOJDKKFONCBKGONPO 33DATAIAMJNFOFMNCDODOBCCHHONOBBBKGONABIAAAMNBGIDOFCKHHONABIAAAAINKBGODMKBGODCCHHONDOIAMNLHOCMDPDOCOBDKHHONMNLHOCCBACAAMDLNOCBBAAAFOLMNNCEEDKMBONJFNKCMOKMKCMOKDKHIONDCIKONCBABABMDLNOCCKIEONMNKIICNKDFOKCKIEON 34DATAOFMNKMOCMNNIOCEPAGAANBMNBGIDNFDKKFONCKHPONEPAGAAAICCHPONNKHAODHNLEMCFAODNBMJDKHPONPOABMIMNEDOLDOFHDCHFONMNNLOEMNEDOLMNJIODCKIGONMNIEOKCCIGONMNPLOEMDHCODDKKLONMNLDODPODFMKKLODCBIAONDEHOPOBEMACBHPONDECD 35DATADGABMJABAAAFPODFMIABEAAAMJCBAIAAMNMNOJMNHOOLMDEDOLCBEGPHBBIKONABAGAAMNBGIDDKHCONKHMCIPOEMNAKOMMCCJOKBBAAAFMNBFOFCBAAABCCHPONMNLNODMNKAOECKGJPGOFAGCIBBEPPHMNHKOENKCGOKHIKHMKCGOKOFBBIKONOLABAJAAMNBGIDOB 36DATAOFBBMCONABBPAAMNBGIDNBNFCBBPAABJOFCKGJPGABAAAFAJMBMFAIODMBMNBGIDCKGJPGAGCHBBMCONMNHKOEOFCKGJPGABPPAEAJOFABBPAAAINBMBMFOFAIOFMBADOBMNCBIDCBMCONNBABBPAAMNBGIDCBABBECCIAONOBOBCCIGONCBAAABCCHPONMNHCODMNHI 37DATAOLMJAOAJMNLMOLMINIHOLHMIMFABBPAAAJMBAFMIMDHKOECBANBJMNMNOJMNGEOJDKKGONKHMCCJOKMJCKIEONMNKOOEDKHPONDNMIMDKDOEOFDOFCDCHFONMNNLOEMNJIODDOANMNAKOKMNKAIPOBMNMAOJHHCDALHJLAMCMDOEMJAGCPNGAKAENCNBOEMGDKEPMJDK 38DATAHPONMNMPOEDKHFONMNAKOKMNIGOLDKIAONMNMPOEDOCMMNAKOKMNIGOLMNHOOLCBKGONOFAOAIMNLKOJANMCABOFOBHOPODAMIPOECMKBIOKMDBCOKCKGJPGOFCCIEONCBMAPPDJMBAINPNKDFOKMJKPDCKDONCBAFAAMDLNOCPODANIPODCNANGDADCHDONCDBNHOPO 39DATADKMACDBNODOBDOAJLHMJNFOFPFKPMNBMOMDIACONHOKHMKGNOFPODKMKGNOFMNDNBBHLPOACNCHDOFDCHDONDIACNJMNMBOIMNFCOMPBOBNBMBMJPOAJMCAGAJNFOFPFKPMNBMOMCBEGPHMNOPOLMKCGOKMNCHOFMNFCOMMDHDOFPOAJMCAGAJNFOFPFKPMNBMOMCBEP 40DATAPHMNOPOLMKCGOKCBIKONBBEPPHABAJAAMNBGIDMNMJODMNFCOMMDHDOFPOAJMAMBOFABEDEPMNJCOGMKCGOKMNKMOCCKLPONHNGMGHCCHPONMNNIOCMNKCDCOFMNAJDDCKPLPEOFOFCKLEOOODMBAINKDFOKCKPNPEAJABANOCAINCAGAJNBOBDKKFONNGAGEPAGAAMN 41DATAFHODMNFCOMMDIKDCPOAJMAMNLDDAABEDEPMNJCOGMCCJOKDOABDCHIONCKPNPEABAGAAMFAJOFMNCDODOBCCHHONMBCBPLPEBBKGONMNBGIDCKKGONABHKAAMNPKOCMNFCOMMDCMAFPOAJMAMBPBPFMFMIJPDCEFPHABECEBMNJCOGMKCGOKCKLPONEMEFGAGJCCHPON 42DATAMNBMCMCDCCIEONCKHPONOFOFMBMNEAODNBCKGBPGBJCCGBPGCKAFPFBJCCAFPFMNFCOMKPMDGFDBPFOFNFMFCKEMPHBBCACANPMKKMOGNBNFNPMKKMOGMBNBOBPBMBMJOBCCEMPHNBOBPBKPMNBMOMCBEGPHMDOPOLPOAJMAABECEBMNJCOGMCCJOKMNBLAGCKGHOPOL 43DATANPMKCNAFNFMBAIDOABDCHIONMNOKOCMNFCOMMDCNAFPFKPDCHEONPBCCGOONNFOFPFBBAEAABJHOPOAJMKPPOGPBOBNBMJPBPFPOAAMKCDOHPOACMKKNOHPOAEMKGPOIPOAGMKMKOHPOAIMCPLOGDIAGCBEJBHNJMDPLOGKPMNBMOMCBEMPHHOPOCAMKDFOHPOEEMCDI 44DATAOKDGEECDDGEPCBEGPHMNOPOLEPMNBKOIDGAAOBOBNBNFOFFBHLOGAHCBHMOMEPAGAAAJHODNMKJCOHDNMCKFOHDKLOONKHMCGIOHCDCDHODCIKONCBABABMNLNOCCKGOONBBAHAABJKPHHCDFEFNCDCDNJOBNBHLPOAIMCILOHBOACHDODCBPFBFODMJHKLHMKGIOHOF 45DATAMNCHOFCBEGPHMNOPOLOBMDGIOHHKLHMKCGOKMDGIOHPBOBNBOFHODGAAPOACMMJKOICBACAAMNLNOCMNFCOMOBODOBCBBIFMOFMJMNBKOIHODGAAKHMCOKOHCKGOONABAHAAAJHOCDOFKHMMCCOINBONHOCDNJBLOLDFPOBKDHDPMCPGOHMNBKOIHHDHPFDIBAONABAJ 46DATABKAIMCBAOIMNBKOIPBHHEPJPMNFLEBDIBCOLPJMJPBOBOBNBODCBEGFNODMJCKNBPEBBMAPEBJMJCBADAAMNMNOJMNGEOJDKKEONPOBAMCFNOIDKKFONCKGOONBBAIAABJOFFEFNCDCDNJBBKGONOLEPAGAAMNBGIDDKKFONPOIAMKFJOIOLDGBKDMOBCLHHMJCKGOON 47DATABBAHAABJDGABCDFEFNCDCDNJDGBKMJCKGOONBBAHAABJHOPOIAMMJKOIPBOBNBOBPBPFPOBKMKJFOICKGOONBBAHAABJDECDOLONHHCDNJCBABBGOFMJCKGOONABAHAAAJHOOFCDCDCDBBKGONEPAGAAPFMNBGIDPBKHMELHOCOBDGAACDFEFNCDCDNJMJPBMJCBHAOP 48DATACCHNONMNPMOIMCBFOKKPDCHMONDCHGONDMDCAHOPMNAJOJDKHIONPOCOMMGJOKCBHGONDEMNAJOJMNDOEPMNLAOLMNALEHCBFPOMMNMMBBMDDOEPKPDCHIONMNOOOKCBEGABMDDGOJDOCODCHJONDKHCONKHMCBOOJCBPGACMNDGOJMCJAOJDKKGONKHMIDKHMONKHMM 49DATAEIOJCBHIONDEDKHJONLOMCBFOJMJCCKCONCBAABKMNMNOJMNGEOJDKKEONPOBBMJAOAJCBKGONMNJAOKDOCAOHCKLPONFMFFCKHNONOLNJOLCDCDCCHNONMJCBKEONOFMNLKOJMNLKOJEPMNLKOJEBHJKHMKHPOJMNLKOJANMCHIOJOBAEAEKPDAIGCDAFMCIEOJCPLO 50DATAMCBFOKMJDKKEONPOBCMCBFOKDKKGONKHMIPOBAMKCJOKPOFAMKBIOKPOGAMKCMOKNKBFOKMKBOOKPOIANKBOOKMDBLOKMNMAOJHHCDMJMNANOKMNBJIFNKDIOKMCBFOKMJCCIIONCBIIONOFDKHDONKHMKOCOJOGABBPBPBPLGHHHOCDIGEHHOPFCDKHMKPGOJEPHIIG 51DATACDANMCOPOJEHHICPFHABFKFKMNIGOLMBEIAGAAADADOBMNIEOKHKMNEDIGNLMPOGIAMADOABCBDOACCBDOADCBDOAECBDOAFCBHLONDGAACBDOAGCBDOAHCBDOAICBDOAJCBDOAKCBDOALCBDOAMCBDOANCBJKOMEPANMKEPOKHOCDPOAAMCEFOKMDEBOKDKHEONKHMK 52DATAHLOKDOAHOHCDMNJPOKMNHCOKCKGOONPJCKHAONOFMJMNDOEPMNHCOKMDDOEPCBFDONMNMMBBMDPHBCFOCKGOONDGAAMDIIAEHOMNAKOKCDALHJLAMCIEOKMJHOPOCANCJIOKDOCAOHCDANMCJAOKMJOFCBBAABCCAGOPMNHMEPOBMDMMBBOFDOCACBEGPHAGAIMNMMOL 53DATAOBBBEGPHAGAGHOPOEBNKDLOKHOPOCOMKNGOKLHMIBCCDBDAFMCMEOKMDNOOKDOCABCBDAFMCNIOKAGACCDHOLHMIBCBDAFMCOAOKDOAABCMJMNCMOMMNENOLMNGEOJMNJAOJDKHCONKHMICBAIAAMNMNOJMNGEOJDKKFONPOABMKCCOLDNEPDOACDCHCONAGAACBKHON 54DATABBGDONMDBGIDCBCDAAMNMNOJMNGEOJCBHPOMMNDKOLCBIIOMMNDKOLCBJBOMABAJAAMNIEOKMDGEOJMNAIIFMIMNMAOJMDEDOLMNANOKMNHIOLMNEDOLMNLNODMNHOOLMNAIIFDOAAMCGFOLDMDCHCONMNEDOLMNHIOLMNEDOLCBAHAAMNMNOJMJABDBENMNIGOLDOAN 55DATAMNAKOKMDIOOLHIMNAKOKHJMDAKOKCGBECOPPCNMCJCOLCFMCJAOLMJABFAPPMNIOOLMNAIIFMAAFMCJOOLANMCJOOLMDBCOKDKMBONGPCGAABBIAAAMDHJEENFOFBKJGMCMJOLCDBDANMCLOOLOBNBMJHHCDAFMCMMOLMJDOCAAGBICBIKONMNMMOLDOEGDCKCONMJCK 56DATAIGONMNKNOKMNGCCMMDMMCLOFMNNDOLOBBBIKONABAGAAMNBGIDDOCOBCBDABACAAMNBGIDMNOOOKKPDCKDONCBAABKMNMNOJMNGEOJDKLOONKHMJDCHEONBBHEOMCBDMOPABAHAAMNBGIDMJMNLFIHCBGMOMDGDJDHMNBNBJMNEDOLMNENOLMNIOOLMNAIIFMCEDOLMN 57DATALFIHCBGMOMDFDHMDBNBJCBHEOMHONGDKNKBNBJCDMDBNBJDACAECHJHEGFHDCAGGHCGFGFAADJDIEODBEEEOEOAAENDHEJDBEFEOEOAAACADABFKFKDBAEABAAIEPPEGFKFKDBAEABAAJGAPCEFKFKDBAEABAAJEAPCGBCEEHCGJHGGFCAEOGPHECAFCGFGBGEHJAABC 58DATAEDGPGNGNHFGOGJGDGBHEGJGPGOCAEFHCHCGPHCAABCFHHCGJHEGFCAFAHCGPHEGFGDHEAABCEEHCGJHGGFCAFEHCGPHFGCGMGFAABCEEGJHDGLCAGOGPHECAGJGOCAGEHCGJHGGFAADEFAHCGJGOHEGFHCCAGOGPHECAHCGFGBGEHJAAAFEGGJGMGFCAEFHIGJHDHEHD 59DATAAADJEEGJHDGLCAEGHFGMGMAABKEGGJGMGFCAEFGNHAHEHJAADJEEGJHCGFGDHEGPHCHJCAEGHFGMGMAAAHFCGBGNCAEGHFGMGMAADGAADHECGBGECAEGGJGMGFCAEOGBGNGFAACAFAHCGFHDHDCAGBGOHJCAGLGFHJCOAA 60DATAEND \ No newline at end of file diff --git a/clients/ts-dos/TS-DOS.200.pre-install.txt b/clients/ts-dos/TS-DOS.200.pre-install.txt index cd84cb7..8c4fcfd 100644 --- a/clients/ts-dos/TS-DOS.200.pre-install.txt +++ b/clients/ts-dos/TS-DOS.200.pre-install.txt @@ -1,4 +1,4 @@ -Type the following in BASIC on the portable: +Type the following into BASIC on the TANDY Model 200 RUN "COM:98N1ENN" diff --git a/clients/ts-dos/TS-DOS.NEC b/clients/ts-dos/TS-DOS.NEC index 56f190a..3fd559f 100644 --- a/clients/ts-dos/TS-DOS.NEC +++ b/clients/ts-dos/TS-DOS.NEC @@ -1,77 +1 @@ -1'-=CO Loader=- -2'-=V1.01 9/21/2017 Kurt McCullum=- -3 ?"Installing TS-DOS" -4 CLEAR256,56489:Y=56489 -5 TL$="":READTL$:IFTL$="END"THEN10 -6 FORX=1TOLEN(TL$)STEP2 -7 TV=((ASC(MID$(TL$,X,1))-65)*16)+(ASC(MID$(TL$,X+1,1))-65) -8 POKEY,TV:Y=Y+1:NEXT -9 ?".";:GOTO5 -10 ?"Done" -11 OPEN"TMP.DO"FOR OUTPUT AS #1 -12 IFPEEK(100)=245THEN14 -13 ?#1,"0 CALL56489:MENU":GOTO15 -14 ?#1,"0 EXEC56489:MENU" -15 CLOSE#1:CLS:?"Type SAVE "+ CHR$(34) + "TS-DOS" + CHR$(34) -16 ?"Then type MENU" -17 LOAD"TMP.DO" -100 DATA MDLENMDCLEPCKPDCLAPCMJMMLAOHCBAAAADJCCKGPCDOABMNFGPBKPDCKOPCDCLEPCDCLDPCDCKLPCDMDCHJOHCBNNNMCCKIPCMNGAECMNHDECCBGJOPCCPOPDMNJINNDKKPPCDCHJOHMNLGNNCBJHOPOFPFPOACNCDINNDKKFPCKHMKDINNDKLAPCEPAMAGAAMFDK -110 DATA HJOHDCKPPCMNFMOCMBAEANMKJHOPHIDCKPPCDCHJOHMFMNEKOCCKLOPCCLHOPODOMCALNNMBPBCBALNNPFMFOFPFMNGOECDKLDPCLHMKGDNNPBKHMKMGOCDNMKBHOFDNMKDPOFDNDNMKPHOEDNDNMKKEOEDNDNDNOBMKIBNNMDNNNMPBKHMKOPODDNMKMAODDNMKJAOC -120 DATA DNDNMKNMOFDNDNDNDNDNOBMKIBNNMDNNNMMNIMPBMNKIGPMNGAECMNHDECCBAAAACCPOPDCKKGPCPJMJCBABABCCOFPDCBOIOFMNGCBEKPDCKFPCDOABDCKPPCCBACACCCOFPDMDJPNPDKHJOHDCKPPCMNEKOCDKKPPCDNDHDPAHFPBGAACBKBPFBJOLCBAHCDCCOFPD -130 DATA MNIMECONMNKCECMNJIDIMNKHECMNJOOFMNHJHCMKOBNNNKHANPPOANMKKENOPOBHMKAONPPOBEMKPMNOPOFNMKPMNOPODPMKNLNOPOACMKNLNOPOBOMKHANOPOBPMKHONOPOBNMKFPNOPOBMMKEMNOPOCAMKEMNOOGNPPOFEMKFCNPPOEBMKJHNOPOEHMKJHNOPOFFMK -140 DATA BCNPPOEEMKFAOBPOEMMKHJOBPOFAMKHJOBMDOBNNDOABCBKPPCIGCDLONKIMNOMKIMNODOABMDIMNODKKPPCDNMCIMNODKLAPCKHMKOBNNMDIMNODKKPPCNGAENKOBNNMKOBNNMDIMNODOAECBKPPCIGEPCDHOLJNKOBNNHJPFMNFMOCPBDCHJOHMDLGNNDODOMNCONP -150 DATA DOACDCKFPCMDLGNNDKKKPCPOACMCOBNNCKLOPCABAHAAAJHOPODMMCOBNNMNANPBCKLOPCMNDFOFMNPAOHCBACAAMNABOICKLOPCBBJLPCABAJAAMNHIGMKPMDOONODKLDPCKHMKOBNNDKLAPCPOBFNKOBNNDKKOPCDMDCKOPCDOABDCHJOHMNJINNMDLGNNDKLDPCKH -160 DATA MKOBNNDKKOPCKHMKOBNNDNMDOONOKPMDOONODOCAMNCONPKPDCKFPCMDLGNNDKKKPCDNMCOBNNDKKLPCOOABDCKLPCMJDCKFPCDKLAPCKHMIKPDMDCKPPCMNGFOCCKLOPCCLDKKFPCOHDKKPPCCBLAPCLONKDHNPKHMJMNGFOCMNKCECCKLOPCCLDODOLOMCGENPDOCA -170 DATA OHMNKHECCBKFPCDGACMDEMNOPOAKNCOBNNPOAFMKJHNPPOADMADKLDPCOOABDCLDPCKPDCKOPCDOABDCHJOHMNGAECMNJINNMDLGNNDKLDPCKHMIMDAONPCBKBPFCCLFPCKPHHCDHHDKLDPCKHMKPEOAKPDCLEPCMNDDOOMCIBOADKKOPCKHMKNFNPMNKMNMMNEAOODK -180 DATA LEPCDNMCMBNPKPDCLEPCDCLAPCMNEAOODKLAPCPOBFNMEBOBDKKKPCKHMKADOADNMKPBNPCBJLPCMDAJOACBABCECCOFPDMNKCECDOCDOHDKKLPCMGDAOHMNKHECMDBMOAOFCBABCBCCOFPDMNKCECOBAOAGMNMHOPMNKHECCBBGOGABKIOADKLDPCKHMCCPOACBGAOG -190 DATA ABJEOAMFOFDKHJOHCBLAPCLONKEBOAHOKHMCEBOADMDCKPPCOBBBKFPGNFAOKAKPBCBDANMCEMOANBAGAKOLAOAFBKHHBDCDANMCFIOAMFABALAAAJMBAFMCFGOACBAHAMCCOFPDOLMNGCBEMNLCOAMNOEECCBAHBGCCOFPDMJKPMNEBOBCBBGOGABIOOAMDCPOACBML -200 DATA OGMDGCBECKOFPKOLCKFJPEHNJDGPHMJKGHABPCPPAJMDJIDIMNOKPAMNJIDIDODAOHMJDKLDPCKHMCNDOACBOJPGDKEEPKBBHBOHPOIHMKMKOABBHFOHBKHHCDBDKHMCMKOAMJDKKKPCKHMICBAFPHBBGMOHDNMKMKOACBOFPGKPAGBAMNAGPBCBAFPHBBGHOHMDMKOA -210 DATA CBJBPIKPDCLAPCDOBFDNPFHOOOIAOGJPMCCNOBOFCDFOCDFGODOFMNHHOEOLMNIPOOOBODCDAOAGMNMHOPDOCOOHAOACMNMHOPDOCAOHCBLAPCDEOBBBALAABJPBDNMCPOOADKLAPCPOBFNMEBOBMDOANPPFCBFMOHMNGCBEPBDMPOBFMCEBOBMJMNDKOCMNFJOBMDLG -220 DATA NNOFMNDKOCMNEEENCBAAPNBOAIBGCIHOMNALENCDBFMCGHOBMNEEENBNMCGFOBOBMJDCLJPCBBAHAACKLOPCBJHOPOEEMCLGNNMNNLOBDKLJPCPOEMMMGAECMEDKOCMNLGOBPFMNPNOBPBMKJHOBDKLJPCPOEMMCLAOBDOAHOHMNJIOFMNGAECMDJHOPDKLDPCKHMCBM -230 DATA OICKLHPCABIAAAHJAINCMMOBAJHNGMEPCCLHPCCKLKPCOFAJCCLKPCOBPOIAMJMNANPBCKLOPCDKLDPCKHMCPHOBMNBNPBNFMNHHOECCLHPCOBCCLKPCMJMNDFOFMDPAOHOLLHMIEPDKLJPCPOEMMCBEOCMNCBOCBKOHBDANMCAJOCMJMNCBOCBKMNALENBDANMCBEOC -240 DATA MJMNDNBIMIMNENBHPOADMKLAOBPOBLMKLAOBPOCAMCCBOCMDENBHNLLLOGAGOOACMCFNOPCBGPOPCCPOPDMJMNGFOCCCLOPCMNKCECCLAOAKMNMHOPMDKHECMNKHECMNGFOCMDFDOCDKKPPCDNGPCGAABBAKAAMNCDDEBBCIPNBJCDOFABAAPNAIBBCIAAOLMNHKDEHN -250 DATA DMDCOFPDOLBAHNDCOGPDOBMJMNBNPBCCLKPCCBNJOGMNLKOFMIMNOHOPCKLKPCABAJAAAJBBHOPLHOBCBDCDHOBCMNCDPBMCGAOPCKLKPCCDCDCDBBHIPLABAGAAOLMDHIGMMNANPBCKLOPCMNDFOFDKKFPCKHMCOOOCCBAJOHMNLKOFMKOOOCMNOHOPABACAACBMJPC -260 DATA BBHOPLMNHIGMMNCDPBMKABODCBDPOHMNNGOPMNIOOFMAMNMJODCKPHPCHMGFGPCCPHPCDKMJPCPOEEMCBKODCKOBPKDOMAMDDLODPOEDMCCNODCKODPKCCKPPCCKOFPKDOKAMDDLODCKPHPCCCKPPCCKHBPICCLKPCDOIADCLJPCCCLMPCCKPHPCCCLHPCOFMBDKLJPC -270 DATA POKAMKFIODADPOIAMCFIODADMFMNNDCCMBOFMFCKLMPCMNAKGMNKGMOPCKLMPCMBDGAACDALHJLAMCGMODCLNBDKLJPCPOMAMCIIODDGBKCKLMPCCLMDLAODPOKAMCJJODCKKPPCCCODPKCKLMPCMDLAODNFCKOBPKCLCCHBPICDOLCKKPPCCDCDBJCCOBPKNBCKLKPC -280 DATA OLMNDFCEMNINOIMNDKCDCBACAAMDABOIMNNMODCKLOPCMNOHOPMNCDPBPOMAMKKICBPOKAMKMCCBPOIAMKAACCMJDKKFPCDNMIMNIBOFDKKFPCKHMIDOABDCKFPCMJMNANPBMNBNPBNFOFCDCDCDBBMCPCABAGAAMNHIGMDOCOBCBDABACAAMNHIGMDKKFPCKHMCCMOE -290 DATA CBAAOHMNLKOFMKCMOEMNOHOPABAGAABBMCPCCBHIPLMNHIGMOBNBNFMNHHOEHMLFMKGGOPOFDOABDCLAPCMNCIPAMNEEPBMKHCOECBBKOHMNNGOPMNJIOFOGNPOHPOFCMCGAOEMNGLOKMNEEPBMDHCOEOBNBPOEBMADKMJPCPOEEMADOACDCLAPCNFOFOBNBMDCOOIHO -300 DATA POMAMKIKOEPOKAMKJJOENFMNBIAHMBAICLMJOLBBAAAAHOCDBDPOBKMCIOOEBLOLMJOLCDCDEOCDEGCBAGAAAJMJDKKKPCKHMIDNMKBONPMNANPBCBPAOGMNLKOFMIMNOHOPCBHIPLBBMCPCABAGAAMNHIGMCBDMDOCCMJPCDOCODCMIPCMNCIPADKKKPCPOACMAMNEE -310 DATA PBMCPBOEDOABDCMCPCCBABABMNABOICBACAAMDABOIMNPAOHMDOLOEDKKKPCPOACMICBKKOGMNNGOPMNIOOFMAMNCIPAMNMIPACBAGAAMNABOIDOAHOHMJMNNMODMNANPBMNCDOFMDGLOKABAJAACKLOPCBBMCPCMNHIGMMNCIPAMDEEPBMNOHOPCBHIPLMNCJPBMJMN -320 DATA ANPBMNCDOFCBMCPCBBIBPLABAJAAMNHIGMCKPHPCEMEFCBMAHLAICBNJOGMCHHOFABAJAAMNLMOFMIOFPFMNANPBPBOBBBMCPCEPAGAAMDBGOJMNLKOFMIMNOHOPMDANOJCBBCOHMNNGOPMNIOOFMCJHOPMJMNJIOFOHPOFJMIPOHJMJMNJOOFMDENBHKPDNDCADPEMN -330 DATA DNBIMKJOOFKPDCADPECBEBPILGMKPEBOMNBHBIMDJOOFAOAGMFMNNGOPMNKKEHNKJHOPCDMBAGAAOFHOKHMKNGOFCDAEANMCMLOFDGAAHIOBKHMJCKMMPJHOPOEJMCLAOHMDIIOHBLHACAFEFDCNEEEPFDCACIEOEFEDCAHGDECODBDACJCADCDADBDECAFEFDEJCPEL -340 DATA ENCACACACACACACACACABLHBCAAAEMGPGBGECAELGJGMGMCAEOGBGNGFCAFCGBGNCACAEGHCGNHECAEMGPGHCACACACACACACACACACACACACACACACACAENGFGOHFCAEEEJFDELCAEGHCGFGFDKCACACACACACACACAEGGJGMGFDKAAFDGBHGGFCAELGJGMGMCAEOGB -350 DATA GNGFCAEEGJHDGLCAEEEPFDCNCACACACACACACACACACACACACACACACACACACACACAENGFGOHFCACAFCEBENCAEGHCGFGFDKCACACACACACACACAEGGJGMGFDKAAEJGOHDGFHCHECAEEGJHDGLCMCAFAHCGFHDHDCACCFJCCCAHEGPCAGCGFGHGJGOCAAAEOEPFECAEG -360 DATA EPFCENEBFEFEEFEEAAEOGFHHCAEOGBGNGFDKAAFDHJHDHEGFGNCAGOGBGNGFDKAAEEGJHCGFGDHEGPHCHJCAGOGBGNGFDKAAFDGBHGGFCAGBHDDKAAEMGPGBGECAGBHDDKAAFDHFHCGFCADPCAAAEGGJGMGFCAGFHIGJHDHEHDCMCAEBCJHAHAGFGOGECAFCCJGFHAGM -370 DATA GBGDGFCAFBCJHFGJHEDKAAEGGJGMGFCAGFHIGJHDHEHDCMCAFCGFHAGMGBGDGFDPCACIFJCPEOCJDKAACNCOCNCACACACACACACAAAENGLEEHCAAECGBGOGLAAEPEGEGAAEPEOCAAAAAEJMDIEOHMDLAOHMDIIOHBBHLOHMJCBNLHPCCMMPJCBNLHPCCPMPJCCPOPJCC -380 DATA OEPJCCOIPJCBIHALCCEEPKCCEKPKCCEMPKCCEOPKCCFAPKMJMNIIOHCBHKOHCCMMPJCBIOOLCCOIPJCBPOOLCCOEPJCBAEOLCCFAPKCBFBOLCCEOPKCBHFOKCCPMPJCBCGOMCCPOPJCBIOOKCCEEPKCBLMOKCCEKPKCBNKOKCCEMPKMJCBABABDOADDCMCPCMDABOIGH -390 DATA COAEMDANOIMNAEOPMNNFPAMNJLOOMDMHOOCCNMPCCBNMPCMNAKOPMNJLOOMDMHOOCBADAAMNAEOPMNJLOODKNNPCCBNOPCPOIAMJNFOFMNGHOIOBCCKPPCOBBBNOPCABIAAAMNHIGMOFCKKPPCABIAAAAINKFKOIMKFKOICCKPPCDOIAMNPLOHMDDHOIOBDKKPPCMNPL -400 DATA OHCBACAAMDABOIBBAAAFOLMNHKDEDKPJPCJFNKGDOPMKGDOPDKLAPCDCMCPCCBABABMDABOICKLMPCMNAKGMNKGMOPCKLMPCOFMNPAOHMNBMOIEPAGAANBMNHIGMNFDKNNPCCKLHPCEPAGAAAICCLHPCNKLEOIHNLEMCJEOINBMJDKLHPCPOABMIMNHNPADOFHDCKNPC -410 DATA MNBPOKMNHNPAMNNMOICKLOPCMNLLOPCCLOPCMNDPOKMDLGOIDKODPCMNPHOIPODFMKOPOICBLIPCDEHOPOBEMACBLHPCDECDDGABMJABAAAFPODFMIABEAAAMJCBAIAAMNAEOPMNLIPAMDHNPACBHIPLBBMCPCABAGAAMNHIGMDKKKPCKHMCNDOJMNEEPBMCGAOPBBAA -420 DATA AFMNFJOKCBAAABCCLHPCMNABOJMNOEOJCKOJPKOFAGCIBBIBPLMNLOOJNKFNOPHIKHMKFNOPOFBBMCPCOLABAJAAMNHIGMOBOFBBPKPCABBPAAMNHIGMNBNFCBBPAABJOFCKOJPKABAAAFAJMBMFAIODMBMNHIGMCKOJPKAGCHBBPKPCMNLOOJOFCKOJPKABPPAEAJOF -430 DATA ABBPAAAINBMBMFOFAIOFMBADOBMNIDGMCBPKPCNBABBPAAMNHIGMCBABBECCLIPCOBOBCCLOPCCBAAABCCLHPCMNLGOIMNLCPAMJAOAJMNPGPAMINIHOLHMIMFABBPAAAJMBAFMIMDLOOJCBANBJMNAEOPMNJLOODKNOPCKHMCGAOPMJCKLMPCMNPCOJDKLHPCDNMIMD -440 DATA OHOJOFDOFCDCKNPCMNBPOKMNNMOIDOANMNEBOPMNOFHGOBMNPHOOHHCDALHJLAMCAHOKMJAGCPNGAKAENCBFOKMGDKEPMJDKLHPCMNBDOKDKKNPCMNEBOPMNMAPADKLIPCMNBDOKDOCMMNEBOPMNMAPAMNLIPACBNOPCOFAOAIMNPBOOANMCEFOKOBHOPODAMIPOECMK -450 DATA EPOPMDEJOPCKOJPKOFCCLMPCCBMAPPDJMBAINPNKGMOPMJKPDCNLPCCBAFAAMDABOIPODANIPODCNANGDADCKLPCCDBNHOPODKMACDBNODOBDOAJLHMJNFOFPFKPMNFGPBDIACONHOKHMKLBOKPODKMKLBOKMNMIBDHLPOACNCLHOKDCKLPCDIACNJMNACOOMNIMPBPB -460 DATA OBNBMBMJPOAJMCIHALNFOFPFKPMNFGPBCBHIPLMNCJPBMKFNOPMNGLOKMNIMPBMDLHOKPOAJMCIHALNFOFPFKPMNFGPBCBIBPLMNCJPBMKFNOPCBMCPCBBIBPLABAJAAMNHIGMMNANOJMNIMPBMDLHOKPOAJMAMBOFABEDEPMNNDOLMKFNOPMNPAOHCKPHPCHNGMGHCC -470 DATA LHPCMNBMOIMNPICIOFCKMAPJOFOFCKIEPDODMBAINKGMOPCKMCPJAJABHLOHAINCIHALNBOBDKNNPCNGAGEPAGAAMNJLOIMNIMPBMDNNCIPOAJMAMNNCCGABEDEPMNNDOLMCGAOPDOABDCLAPCCKMCPJABAGAAMFAJOFMNGHOIOBCCKPPCMBCBMAPJBBNOPCMNHIGMCK -480 DATA NOPCABHKAAMNDOOIMNIMPBMDCMAGPOAJMAMBPBPFMFMIJPDCHCPLABECEBMNNDOLMKFNOPCKPHPCEMEFGAGJCCLHPCMNOOCCCDCCLMPCCKLHPCOFOFMBMNIEOINBCKOBPKBJCCOBPKCKMKPJBJCCMKPJMNIMPBKPMDLICHPFOFNFMFCKHOPLBBCACANPMKONOLNBNFNP -490 DATA MKONOLMBNBOBPBMBMJOBCCHOPLNBOBPBKPMNFGPBCBHIPLMDCJPBPOAJMAABECEBMNNDOLMCGAOPMNBEAHCKFNPEOLNPMKCNAGNFMBAIDOABDCLAPCMNCOOIMNIMPBMDCNAGPFKPDCKMPCPBCCKGPCNFOFPFBBAEAABJHOPOAJMKEAOMPBOBNBMJPBPFPOAAMKGEOMPO -500 DATA ACMKOOOMPOAEMKLAONPOAGMKALONPOAIMCDMOMDIAGCBJDBKNJMDDMOMKPMNFGPBCBHOPLHOPOCAMKHGOMPOEEMCGPOPDGEECDDGEPCBHIPLMNCJPBEPMNFLONDGAAOBOBNBNFOFFBHLOGAHCBLEPBEPAGAAAJHODNMKNDOMDNMCOGOMDKPGPCKHMCKJOMCDCDHODCMC -510 DATA PCCBABABMNABOICKKGPCBBAHAABJKPHHCDFEFNCDCDNJOBNBHLPOAIMCMMOMBOACHDODCBEOBJODMJHKLHMKKJOMOFMNGLOKCBHIPLMNCJPBOBMDKJOMHKLHMKFNOPMDKJOMPBOBNBOFHODGAAPOACMMNLONCBACAAMNABOIMNIMPBOBODOBCBAFEPOFMJMNFLONHODG -520 DATA AAKHMCCLONCKKGPCABAHAAAJHOCDOFKHMMGDONNBONHOCDNJBLOLDFPOBKDHDPMCDHONMNFLONHHDHPFDIBAONABAPBNAIMCFBONMNFLONPBHHEPJPMNKLDBDIBCOLPJMJPBOBOBNBODCBCFFAODMJCKIPPJBBHOPJBJMJCBADAAMNAEOPMNJLOODKNMPCPOBAMCJOON -530 DATA DKNNPCCKKGPCBBAIAABJOFFEFNCDCDNJBBNOPCOLEPAGAAMNHIGMDKNNPCPOIAMKJKONOLDGBKDMOBCLHHMJCKKGPCBBAHAABJDGABCDFEFNCDCDNJDGBKMJCKKGPCBBAHAABJHOPOIAMMNLONPBOBNBOBPBPFPOBKMKNGONCKKGPCBBAHAABJDECDOLONHHCDNJCBFK -540 DATA BJOFMJCKKGPCABAHAAAJHOOFCDCDCDBBNOPCEPAGAAPFMNHIGMPBKHMEPLOHOBDGAACDFEFNCDCDNJMJPBMJCBKBPFCCLFPCMNDDOOMCEMOPKPDCLEPCDCKOPCDNDCLBPCDOABDCOGPDMNEFOOMDFBECMNOKPAMNJIDICBJJPBMNGCBEMDFBECKPDCLAPCMNCIPACBEG -550 DATA ABMDGNOODOBFDCLBPCDKKKPCKHMCFFOOCBPGACMNGNOOMCMHOODKNOPCKHMIDKLEPCKHMMHPOOCBLAPCDEDKLBPCLOMCEMOOMJCCNKPCCBAABKMNAEOPMNJLOODKNMPCPOBBMJAOAJCBNOPCMNMHOPDOCAOHCKPHPCFMFFCKLFPCOLNJOLCDCDCCLFPCMJCBNMPCOFMN -560 DATA PBOOMNPBOOEPMNPBOOEBHJKHMKLGOOMNPBOOANMCKPOOOBAEAEKPDAIGCDAFMCLLOOCPLOMCEMOPMJDKNMPCPOBCMCEMOPDKNOPCKHMIPOBAMKGAOPPOFAMKEPOPPOGAMKGDOPNKEMOPMKFFOPPOIANKFFOPMDFCOPMNPHOOHHCDMJMNEEOPMNNDGNNKGPOPMCEMOPMJ -570 DATA CCMAPCCBMAPCOFDKKLPCKHMKBJOPOGABBPBPBPLGHHHOCDIGEHHOPFCDKHMKCNOPEPHIIGCDANMCCGOPEHHICPFHABFKFKMNMAPAMBEIAGAAADADOBMNLLOPHKMNLOGONLLLOGCAMIDOABCBDOACCBDOADCBDOAECBDOAFCBLDPCDGAACBDOAGCBDOAHCBDOAICBDOAJ -580 DATA CBDOAKCBDOALCBDOAMCBDOANCBNCPBEPANMKIGOPHOCDPOAAMCHMOPMDHIOPDKKMPCKHMKLCOPDOAHOHCDMNNGOPMNKJOPCKKGPCPJCKKIPCOFMJMNFBECMNKJOPMDFBECCBILPCMNGCBEMDENBHFOCKKGPCDGAAMDIFAFHOMNEBOPCDALHJLAMCLLOPMJHOPOCANCMP -590 DATA OPDOCAOHCDANMCMHOPMJOFMNMDECCBAIABCCOFPDMNIMECOBMDGCBEOFDOCACBHIPLAGAIMNAGPBOBBBHIPLAGAGHOPOEBNKHCOPHOPOCOMKBAPALHMIBCCDBDAFMCPOOPMDBIPADOCABCBDAFMCBCPAAGACCDHOLHMIBCBDAFMCBKPADOAABCMJMNGGPBMNIHPAMNJL -600 DATA OOMNMHOODKKKPCKHMICBAIAAMNAEOPMNJLOODKNNPCPOABMKFMPADNEPDOACDCKKPCAGAACBNPPCBBJLPCMDHIGMCBCDAAMNAEOPMNJLOOCBLHPBMNHEPACBMAPBMNHEPACBMJPBABAJAAMNLLOPMDJLOOMNMCGNMIMNPHOOMDHNPAMNEEOPMNLCPAMNHNPAMNABOJMN -610 DATA LIPAMNMCGNDOAAMCJPPADMDCKKPCMNHNPAMNLCPAMNHNPACBAHAAMNAEOPMJABDBENMNMAPADOANMNEBOPMDMIPAHIMNEBOPHJMDEBOPCGBECOPPCNMCMMPACFMCMKPAMJABFAPPMNMIPAMNMCGNMAAFMCNIPAANMCNIPAMDEJOPDKPJPCGPCGAABBIAAAMDCDDENFOF -620 DATA BKJGMCADPBCDBDANMCPIPAOBNBMJHHCDAFMCAGPBMJDOCAAGBICBMCPCMNAGPBDOEGDCNKPCMJCKLOPCMNOHOPMNDKCDMDJLCCOFMNANPBOBBBMCPCABAGAAMNHIGMDOCOBCBDABACAAMNHIGMMNCIPAKPDCNLPCCBAABKMNAEOPMNJLOODKPGPCKHMJDCKMPCBBKNPB -630 DATA CBAGPEABAGAAMNHIGMMJMNKIGPCBKGPBDGDJDHMNEOBMMNHNPAMNIHPAMNMIPAMNMCGNMCHNPAMNKIGPCBKGPBDFDHMDEOBMCBKNPBHONGDKNKEOBMCDMDEOBMDACAECHJHEGFHDCAGGHCGFGFAADJEODIDBEOEOAAAAAAAAAAAAAAAAACADABFKFKDBAEABAAIEPPEG -640 DATA FKFKDBAEABAAJGAPCEFKFKDBAEABAAJEAPCGBCEEHCGJHGGFCAEOGPHECAFCGFGBGEHJAABCEDGPGNGNHFGOGJGDGBHEGJGPGOCAEFHCHCGPHCAABCFHHCGJHEGFCAFAHCGPHEGFGDHEAABCEEHCGJHGGFCAFEHCGPHFGCGMGFAABCEEGJHDGLCAGOGPHECAGJGOCAGE -650 DATA HCGJHGGFAADEFAHCGJGOHEGFHCCAGOGPHECAHCGFGBGEHJAAAFEGGJGMGFCAEFHIGJHDHEHDAADJEEGJHDGLCAEGHFGMGMAABKEGGJGMGFCAEFGNHAHEHJAADJEEGJHCGFGDHEGPHCHJCAEGHFGMGMAAAHFCGBGNCAEGHFGMGMAADGAADHECGBGECAEGGJGMGFCAEOGB -660 DATA GNGFAACAFAHCGFHDHDCAGBGOHJCAGLGFHJCOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -670 DATA AAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -680 DATA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -690 DATA END \ No newline at end of file +0'TS-DOS CO Loader V1.01 9/21/2017 Kurt McCullum, 2022 b.kenyon.w@gmail.com 0CLEAR256,56489:S=56489:Y=S:C$="TS-DOS":D$="TMP.DO":?"Installing "+C$ 1TL$="":READTL$:IFTL$="END"THEN3 2FORX=1TOLEN(TL$)STEP2:TV=((ASC(MID$(TL$,X,1))-65)*16)+(ASC(MID$(TL$,X+1,1))-65):POKEY,TV:Y=Y+1:NEXT:?".";:GOTO1 3?"Done":OPEND$FOROUTPUTAS#1:?#1,"0EXEC";S;":MENU":CLOSE#1:CLS:?"Please type:":?"KILL "+CHR$(34)+D$+CHR$(34):?"SAVE "+CHR$(34)+C$+CHR$(34):?"MENU":LOADD$ 4DATAMDLENMDCLEPCKPDCLAPCMJMMLAOHCBAAAADJCCKGPCDOABMNFGPBKPDCKOPCDCLEPCDCLDPCDCKLPCDMDCHJOHCBNNNMCCKIPCMNGAECMNHDECCBGJOPCCPOPDMNJINNDKKPPCDCHJOHMNLGNNCBJHOPOFPFPOACNCDINNDKKFPCKHMKDINNDKLAPCEPAMAGAAMFDK 5DATAHJOHDCKPPCMNFMOCMBAEANMKJHOPHIDCKPPCDCHJOHMFMNEKOCCKLOPCCLHOPODOMCALNNMBPBCBALNNPFMFOFPFMNGOECDKLDPCLHMKGDNNPBKHMKMGOCDNMKBHOFDNMKDPOFDNDNMKPHOEDNDNMKKEOEDNDNDNOBMKIBNNMDNNNMPBKHMKOPODDNMKMAODDNMKJAOC 6DATADNDNMKNMOFDNDNDNDNDNOBMKIBNNMDNNNMMNIMPBMNKIGPMNGAECMNHDECCBAAAACCPOPDCKKGPCPJMJCBABABCCOFPDCBOIOFMNGCBEKPDCKFPCDOABDCKPPCCBACACCCOFPDMDJPNPDKHJOHDCKPPCMNEKOCDKKPPCDNDHDPAHFPBGAACBKBPFBJOLCBAHCDCCOFPD 7DATAMNIMECONMNKCECMNJIDIMNKHECMNJOOFMNHJHCMKOBNNNKHANPPOANMKKENOPOBHMKAONPPOBEMKPMNOPOFNMKPMNOPODPMKNLNOPOACMKNLNOPOBOMKHANOPOBPMKHONOPOBNMKFPNOPOBMMKEMNOPOCAMKEMNOOGNPPOFEMKFCNPPOEBMKJHNOPOEHMKJHNOPOFFMK 8DATABCNPPOEEMKFAOBPOEMMKHJOBPOFAMKHJOBMDOBNNDOABCBKPPCIGCDLONKIMNOMKIMNODOABMDIMNODKKPPCDNMCIMNODKLAPCKHMKOBNNMDIMNODKKPPCNGAENKOBNNMKOBNNMDIMNODOAECBKPPCIGEPCDHOLJNKOBNNHJPFMNFMOCPBDCHJOHMDLGNNDODOMNCONP 9DATADOACDCKFPCMDLGNNDKKKPCPOACMCOBNNCKLOPCABAHAAAJHOPODMMCOBNNMNANPBCKLOPCMNDFOFMNPAOHCBACAAMNABOICKLOPCBBJLPCABAJAAMNHIGMKPMDOONODKLDPCKHMKOBNNDKLAPCPOBFNKOBNNDKKOPCDMDCKOPCDOABDCHJOHMNJINNMDLGNNDKLDPCKH 10DATAMKOBNNDKKOPCKHMKOBNNDNMDOONOKPMDOONODOCAMNCONPKPDCKFPCMDLGNNDKKKPCDNMCOBNNDKKLPCOOABDCKLPCMJDCKFPCDKLAPCKHMIKPDMDCKPPCMNGFOCCKLOPCCLDKKFPCOHDKKPPCCBLAPCLONKDHNPKHMJMNGFOCMNKCECCKLOPCCLDODOLOMCGENPDOCA 11DATAOHMNKHECCBKFPCDGACMDEMNOPOAKNCOBNNPOAFMKJHNPPOADMADKLDPCOOABDCLDPCKPDCKOPCDOABDCHJOHMNGAECMNJINNMDLGNNDKLDPCKHMIMDAONPCBKBPFCCLFPCKPHHCDHHDKLDPCKHMKPEOAKPDCLEPCMNDDOOMCIBOADKKOPCKHMKNFNPMNKMNMMNEAOODK 12DATALEPCDNMCMBNPKPDCLEPCDCLAPCMNEAOODKLAPCPOBFNMEBOBDKKKPCKHMKADOADNMKPBNPCBJLPCMDAJOACBABCECCOFPDMNKCECDOCDOHDKKLPCMGDAOHMNKHECMDBMOAOFCBABCBCCOFPDMNKCECOBAOAGMNMHOPMNKHECCBBGOGABKIOADKLDPCKHMCCPOACBGAOG 13DATAABJEOAMFOFDKHJOHCBLAPCLONKEBOAHOKHMCEBOADMDCKPPCOBBBKFPGNFAOKAKPBCBDANMCEMOANBAGAKOLAOAFBKHHBDCDANMCFIOAMFABALAAAJMBAFMCFGOACBAHAMCCOFPDOLMNGCBEMNLCOAMNOEECCBAHBGCCOFPDMJKPMNEBOBCBBGOGABIOOAMDCPOACBML 14DATAOGMDGCBECKOFPKOLCKFJPEHNJDGPHMJKGHABPCPPAJMDJIDIMNOKPAMNJIDIDODAOHMJDKLDPCKHMCNDOACBOJPGDKEEPKBBHBOHPOIHMKMKOABBHFOHBKHHCDBDKHMCMKOAMJDKKKPCKHMICBAFPHBBGMOHDNMKMKOACBOFPGKPAGBAMNAGPBCBAFPHBBGHOHMDMKOA 15DATACBJBPIKPDCLAPCDOBFDNPFHOOOIAOGJPMCCNOBOFCDFOCDFGODOFMNHHOEOLMNIPOOOBODCDAOAGMNMHOPDOCOOHAOACMNMHOPDOCAOHCBLAPCDEOBBBALAABJPBDNMCPOOADKLAPCPOBFNMEBOBMDOANPPFCBFMOHMNGCBEPBDMPOBFMCEBOBMJMNDKOCMNFJOBMDLG 16DATANNOFMNDKOCMNEEENCBAAPNBOAIBGCIHOMNALENCDBFMCGHOBMNEEENBNMCGFOBOBMJDCLJPCBBAHAACKLOPCBJHOPOEEMCLGNNMNNLOBDKLJPCPOEMMMGAECMEDKOCMNLGOBPFMNPNOBPBMKJHOBDKLJPCPOEMMCLAOBDOAHOHMNJIOFMNGAECMDJHOPDKLDPCKHMCBM 17DATAOICKLHPCABIAAAHJAINCMMOBAJHNGMEPCCLHPCCKLKPCOFAJCCLKPCOBPOIAMJMNANPBCKLOPCDKLDPCKHMCPHOBMNBNPBNFMNHHOECCLHPCOBCCLKPCMJMNDFOFMDPAOHOLLHMIEPDKLJPCPOEMMCBEOCMNCBOCBKOHBDANMCAJOCMJMNCBOCBKMNALENBDANMCBEOC 18DATAMJMNDNBIMIMNENBHPOADMKLAOBPOBLMKLAOBPOCAMCCBOCMDENBHNLLLOGAGOOACMCFNOPCBGPOPCCPOPDMJMNGFOCCCLOPCMNKCECCLAOAKMNMHOPMDKHECMNKHECMNGFOCMDFDOCDKKPPCDNGPCGAABBAKAAMNCDDEBBCIPNBJCDOFABAAPNAIBBCIAAOLMNHKDEHN 19DATADMDCOFPDOLBAHNDCOGPDOBMJMNBNPBCCLKPCCBNJOGMNLKOFMIMNOHOPCKLKPCABAJAAAJBBHOPLHOBCBDCDHOBCMNCDPBMCGAOPCKLKPCCDCDCDBBHIPLABAGAAOLMDHIGMMNANPBCKLOPCMNDFOFDKKFPCKHMCOOOCCBAJOHMNLKOFMKOOOCMNOHOPABACAACBMJPC 20DATABBHOPLMNHIGMMNCDPBMKABODCBDPOHMNNGOPMNIOOFMAMNMJODCKPHPCHMGFGPCCPHPCDKMJPCPOEEMCBKODCKOBPKDOMAMDDLODPOEDMCCNODCKODPKCCKPPCCKOFPKDOKAMDDLODCKPHPCCCKPPCCKHBPICCLKPCDOIADCLJPCCCLMPCCKPHPCCCLHPCOFMBDKLJPC 21DATAPOKAMKFIODADPOIAMCFIODADMFMNNDCCMBOFMFCKLMPCMNAKGMNKGMOPCKLMPCMBDGAACDALHJLAMCGMODCLNBDKLJPCPOMAMCIIODDGBKCKLMPCCLMDLAODPOKAMCJJODCKKPPCCCODPKCKLMPCMDLAODNFCKOBPKCLCCHBPICDOLCKKPPCCDCDBJCCOBPKNBCKLKPC 22DATAOLMNDFCEMNINOIMNDKCDCBACAAMDABOIMNNMODCKLOPCMNOHOPMNCDPBPOMAMKKICBPOKAMKMCCBPOIAMKAACCMJDKKFPCDNMIMNIBOFDKKFPCKHMIDOABDCKFPCMJMNANPBMNBNPBNFOFCDCDCDBBMCPCABAGAAMNHIGMDOCOBCBDABACAAMNHIGMDKKFPCKHMCCMOE 23DATACBAAOHMNLKOFMKCMOEMNOHOPABAGAABBMCPCCBHIPLMNHIGMOBNBNFMNHHOEHMLFMKGGOPOFDOABDCLAPCMNCIPAMNEEPBMKHCOECBBKOHMNNGOPMNJIOFOGNPOHPOFCMCGAOEMNGLOKMNEEPBMDHCOEOBNBPOEBMADKMJPCPOEEMADOACDCLAPCNFOFOBNBMDCOOIHO 24DATAPOMAMKIKOEPOKAMKJJOENFMNBIAHMBAICLMJOLBBAAAAHOCDBDPOBKMCIOOEBLOLMJOLCDCDEOCDEGCBAGAAAJMJDKKKPCKHMIDNMKBONPMNANPBCBPAOGMNLKOFMIMNOHOPCBHIPLBBMCPCABAGAAMNHIGMCBDMDOCCMJPCDOCODCMIPCMNCIPADKKKPCPOACMAMNEE 25DATAPBMCPBOEDOABDCMCPCCBABABMNABOICBACAAMDABOIMNPAOHMDOLOEDKKKPCPOACMICBKKOGMNNGOPMNIOOFMAMNCIPAMNMIPACBAGAAMNABOIDOAHOHMJMNNMODMNANPBMNCDOFMDGLOKABAJAACKLOPCBBMCPCMNHIGMMNCIPAMDEEPBMNOHOPCBHIPLMNCJPBMJMN 26DATAANPBMNCDOFCBMCPCBBIBPLABAJAAMNHIGMCKPHPCEMEFCBMAHLAICBNJOGMCHHOFABAJAAMNLMOFMIOFPFMNANPBPBOBBBMCPCEPAGAAMDBGOJMNLKOFMIMNOHOPMDANOJCBBCOHMNNGOPMNIOOFMCJHOPMJMNJIOFOHPOFJMIPOHJMJMNJOOFMDENBHKPDNDCADPEMN 27DATADNBIMKJOOFKPDCADPECBEBPILGMKPEBOMNBHBIMDJOOFAOAGMFMNNGOPMNKKEHNKJHOPCDMBAGAAOFHOKHMKNGOFCDAEANMCMLOFDGAAHIOBKHMJCKMMPJHOPOEJMCLAOHMDIIOHBLHACAFEFDCNEEEPFDCACIEOEFEDCAHGDECODBDACJCADCDADBDECAFEFDEJCPEL 28DATAENCACACACACACACACACABLHBCAAAEMGPGBGECAELGJGMGMCAEOGBGNGFCAFCGBGNCACAEGHCGNHECAEMGPGHCACACACACACACACACACACACACACACACACAENGFGOHFCAEEEJFDELCAEGHCGFGFDKCACACACACACACACAEGGJGMGFDKAAFDGBHGGFCAELGJGMGMCAEOGB 29DATAGNGFCAEEGJHDGLCAEEEPFDCNCACACACACACACACACACACACACACACACACACACACACAENGFGOHFCACAFCEBENCAEGHCGFGFDKCACACACACACACACAEGGJGMGFDKAAEJGOHDGFHCHECAEEGJHDGLCMCAFAHCGFHDHDCACCFJCCCAHEGPCAGCGFGHGJGOCAAAEOEPFECAEG 30DATAEPFCENEBFEFEEFEEAAEOGFHHCAEOGBGNGFDKAAFDHJHDHEGFGNCAGOGBGNGFDKAAEEGJHCGFGDHEGPHCHJCAGOGBGNGFDKAAFDGBHGGFCAGBHDDKAAEMGPGBGECAGBHDDKAAFDHFHCGFCADPCAAAEGGJGMGFCAGFHIGJHDHEHDCMCAEBCJHAHAGFGOGECAFCCJGFHAGM 31DATAGBGDGFCAFBCJHFGJHEDKAAEGGJGMGFCAGFHIGJHDHEHDCMCAFCGFHAGMGBGDGFDPCACIFJCPEOCJDKAACNCOCNCACACACACACACAAAENGLEEHCAAECGBGOGLAAEPEGEGAAEPEOCAAAAAEJMDIEOHMDLAOHMDIIOHBBHLOHMJCBNLHPCCMMPJCBNLHPCCPMPJCCPOPJCC 32DATAOEPJCCOIPJCBIHALCCEEPKCCEKPKCCEMPKCCEOPKCCFAPKMJMNIIOHCBHKOHCCMMPJCBIOOLCCOIPJCBPOOLCCOEPJCBAEOLCCFAPKCBFBOLCCEOPKCBHFOKCCPMPJCBCGOMCCPOPJCBIOOKCCEEPKCBLMOKCCEKPKCBNKOKCCEMPKMJCBABABDOADDCMCPCMDABOIGH 33DATACOAEMDANOIMNAEOPMNNFPAMNJLOOMDMHOOCCNMPCCBNMPCMNAKOPMNJLOOMDMHOOCBADAAMNAEOPMNJLOODKNNPCCBNOPCPOIAMJNFOFMNGHOIOBCCKPPCOBBBNOPCABIAAAMNHIGMOFCKKPPCABIAAAAINKFKOIMKFKOICCKPPCDOIAMNPLOHMDDHOIOBDKKPPCMNPL 34DATAOHCBACAAMDABOIBBAAAFOLMNHKDEDKPJPCJFNKGDOPMKGDOPDKLAPCDCMCPCCBABABMDABOICKLMPCMNAKGMNKGMOPCKLMPCOFMNPAOHMNBMOIEPAGAANBMNHIGMNFDKNNPCCKLHPCEPAGAAAICCLHPCNKLEOIHNLEMCJEOINBMJDKLHPCPOABMIMNHNPADOFHDCKNPC 35DATAMNBPOKMNHNPAMNNMOICKLOPCMNLLOPCCLOPCMNDPOKMDLGOIDKODPCMNPHOIPODFMKOPOICBLIPCDEHOPOBEMACBLHPCDECDDGABMJABAAAFPODFMIABEAAAMJCBAIAAMNAEOPMNLIPAMDHNPACBHIPLBBMCPCABAGAAMNHIGMDKKKPCKHMCNDOJMNEEPBMCGAOPBBAA 36DATAAFMNFJOKCBAAABCCLHPCMNABOJMNOEOJCKOJPKOFAGCIBBIBPLMNLOOJNKFNOPHIKHMKFNOPOFBBMCPCOLABAJAAMNHIGMOBOFBBPKPCABBPAAMNHIGMNBNFCBBPAABJOFCKOJPKABAAAFAJMBMFAIODMBMNHIGMCKOJPKAGCHBBPKPCMNLOOJOFCKOJPKABPPAEAJOF 37DATAABBPAAAINBMBMFOFAIOFMBADOBMNIDGMCBPKPCNBABBPAAMNHIGMCBABBECCLIPCOBOBCCLOPCCBAAABCCLHPCMNLGOIMNLCPAMJAOAJMNPGPAMINIHOLHMIMFABBPAAAJMBAFMIMDLOOJCBANBJMNAEOPMNJLOODKNOPCKHMCGAOPMJCKLMPCMNPCOJDKLHPCDNMIMD 38DATAOHOJOFDOFCDCKNPCMNBPOKMNNMOIDOANMNEBOPMNOFHGOBMNPHOOHHCDALHJLAMCAHOKMJAGCPNGAKAENCBFOKMGDKEPMJDKLHPCMNBDOKDKKNPCMNEBOPMNMAPADKLIPCMNBDOKDOCMMNEBOPMNMAPAMNLIPACBNOPCOFAOAIMNPBOOANMCEFOKOBHOPODAMIPOECMK 39DATAEPOPMDEJOPCKOJPKOFCCLMPCCBMAPPDJMBAINPNKGMOPMJKPDCNLPCCBAFAAMDABOIPODANIPODCNANGDADCKLPCCDBNHOPODKMACDBNODOBDOAJLHMJNFOFPFKPMNFGPBDIACONHOKHMKLBOKPODKMKLBOKMNMIBDHLPOACNCLHOKDCKLPCDIACNJMNACOOMNIMPBPB 40DATAOBNBMBMJPOAJMCIHALNFOFPFKPMNFGPBCBHIPLMNCJPBMKFNOPMNGLOKMNIMPBMDLHOKPOAJMCIHALNFOFPFKPMNFGPBCBIBPLMNCJPBMKFNOPCBMCPCBBIBPLABAJAAMNHIGMMNANOJMNIMPBMDLHOKPOAJMAMBOFABEDEPMNNDOLMKFNOPMNPAOHCKPHPCHNGMGHCC 41DATALHPCMNBMOIMNPICIOFCKMAPJOFOFCKIEPDODMBAINKGMOPCKMCPJAJABHLOHAINCIHALNBOBDKNNPCNGAGEPAGAAMNJLOIMNIMPBMDNNCIPOAJMAMNNCCGABEDEPMNNDOLMCGAOPDOABDCLAPCCKMCPJABAGAAMFAJOFMNGHOIOBCCKPPCMBCBMAPJBBNOPCMNHIGMCK 42DATANOPCABHKAAMNDOOIMNIMPBMDCMAGPOAJMAMBPBPFMFMIJPDCHCPLABECEBMNNDOLMKFNOPCKPHPCEMEFGAGJCCLHPCMNOOCCCDCCLMPCCKLHPCOFOFMBMNIEOINBCKOBPKBJCCOBPKCKMKPJBJCCMKPJMNIMPBKPMDLICHPFOFNFMFCKHOPLBBCACANPMKONOLNBNFNP 43DATAMKONOLMBNBOBPBMBMJOBCCHOPLNBOBPBKPMNFGPBCBHIPLMDCJPBPOAJMAABECEBMNNDOLMCGAOPMNBEAHCKFNPEOLNPMKCNAGNFMBAIDOABDCLAPCMNCOOIMNIMPBMDCNAGPFKPDCKMPCPBCCKGPCNFOFPFBBAEAABJHOPOAJMKEAOMPBOBNBMJPBPFPOAAMKGEOMPO 44DATAACMKOOOMPOAEMKLAONPOAGMKALONPOAIMCDMOMDIAGCBJDBKNJMDDMOMKPMNFGPBCBHOPLHOPOCAMKHGOMPOEEMCGPOPDGEECDDGEPCBHIPLMNCJPBEPMNFLONDGAAOBOBNBNFOFFBHLOGAHCBLEPBEPAGAAAJHODNMKNDOMDNMCOGOMDKPGPCKHMCKJOMCDCDHODCMC 45DATAPCCBABABMNABOICKKGPCBBAHAABJKPHHCDFEFNCDCDNJOBNBHLPOAIMCMMOMBOACHDODCBEOBJODMJHKLHMKKJOMOFMNGLOKCBHIPLMNCJPBOBMDKJOMHKLHMKFNOPMDKJOMPBOBNBOFHODGAAPOACMMNLONCBACAAMNABOIMNIMPBOBODOBCBAFEPOFMJMNFLONHODG 46DATAAAKHMCCLONCKKGPCABAHAAAJHOCDOFKHMMGDONNBONHOCDNJBLOLDFPOBKDHDPMCDHONMNFLONHHDHPFDIBAONABAPBNAIMCFBONMNFLONPBHHEPJPMNKLDBDIBCOLPJMJPBOBOBNBODCBCFFAODMJCKIPPJBBHOPJBJMJCBADAAMNAEOPMNJLOODKNMPCPOBAMCJOON 47DATADKNNPCCKKGPCBBAIAABJOFFEFNCDCDNJBBNOPCOLEPAGAAMNHIGMDKNNPCPOIAMKJKONOLDGBKDMOBCLHHMJCKKGPCBBAHAABJDGABCDFEFNCDCDNJDGBKMJCKKGPCBBAHAABJHOPOIAMMNLONPBOBNBOBPBPFPOBKMKNGONCKKGPCBBAHAABJDECDOLONHHCDNJCBFK 48DATABJOFMJCKKGPCABAHAAAJHOOFCDCDCDBBNOPCEPAGAAPFMNHIGMPBKHMEPLOHOBDGAACDFEFNCDCDNJMJPBMJCBKBPFCCLFPCMNDDOOMCEMOPKPDCLEPCDCKOPCDNDCLBPCDOABDCOGPDMNEFOOMDFBECMNOKPAMNJIDICBJJPBMNGCBEMDFBECKPDCLAPCMNCIPACBEG 49DATAABMDGNOODOBFDCLBPCDKKKPCKHMCFFOOCBPGACMNGNOOMCMHOODKNOPCKHMIDKLEPCKHMMHPOOCBLAPCDEDKLBPCLOMCEMOOMJCCNKPCCBAABKMNAEOPMNJLOODKNMPCPOBBMJAOAJCBNOPCMNMHOPDOCAOHCKPHPCFMFFCKLFPCOLNJOLCDCDCCLFPCMJCBNMPCOFMN 50DATAPBOOMNPBOOEPMNPBOOEBHJKHMKLGOOMNPBOOANMCKPOOOBAEAEKPDAIGCDAFMCLLOOCPLOMCEMOPMJDKNMPCPOBCMCEMOPDKNOPCKHMIPOBAMKGAOPPOFAMKEPOPPOGAMKGDOPNKEMOPMKFFOPPOIANKFFOPMDFCOPMNPHOOHHCDMJMNEEOPMNNDGNNKGPOPMCEMOPMJ 51DATACCMAPCCBMAPCOFDKKLPCKHMKBJOPOGABBPBPBPLGHHHOCDIGEHHOPFCDKHMKCNOPEPHIIGCDANMCCGOPEHHICPFHABFKFKMNMAPAMBEIAGAAADADOBMNLLOPHKMNLOGONLLLOGCAMIDOABCBDOACCBDOADCBDOAECBDOAFCBLDPCDGAACBDOAGCBDOAHCBDOAICBDOAJ 52DATACBDOAKCBDOALCBDOAMCBDOANCBNCPBEPANMKIGOPHOCDPOAAMCHMOPMDHIOPDKKMPCKHMKLCOPDOAHOHCDMNNGOPMNKJOPCKKGPCPJCKKIPCOFMJMNFBECMNKJOPMDFBECCBILPCMNGCBEMDENBHFOCKKGPCDGAAMDIFAFHOMNEBOPCDALHJLAMCLLOPMJHOPOCANCMP 53DATAOPDOCAOHCDANMCMHOPMJOFMNMDECCBAIABCCOFPDMNIMECOBMDGCBEOFDOCACBHIPLAGAIMNAGPBOBBBHIPLAGAGHOPOEBNKHCOPHOPOCOMKBAPALHMIBCCDBDAFMCPOOPMDBIPADOCABCBDAFMCBCPAAGACCDHOLHMIBCBDAFMCBKPADOAABCMJMNGGPBMNIHPAMNJL 54DATAOOMNMHOODKKKPCKHMICBAIAAMNAEOPMNJLOODKNNPCPOABMKFMPADNEPDOACDCKKPCAGAACBNPPCBBJLPCMDHIGMCBCDAAMNAEOPMNJLOOCBLHPBMNHEPACBMAPBMNHEPACBMJPBABAJAAMNLLOPMDJLOOMNMCGNMIMNPHOOMDHNPAMNEEOPMNLCPAMNHNPAMNABOJMN 55DATALIPAMNMCGNDOAAMCJPPADMDCKKPCMNHNPAMNLCPAMNHNPACBAHAAMNAEOPMJABDBENMNMAPADOANMNEBOPMDMIPAHIMNEBOPHJMDEBOPCGBECOPPCNMCMMPACFMCMKPAMJABFAPPMNMIPAMNMCGNMAAFMCNIPAANMCNIPAMDEJOPDKPJPCGPCGAABBIAAAMDCDDENFOF 56DATABKJGMCADPBCDBDANMCPIPAOBNBMJHHCDAFMCAGPBMJDOCAAGBICBMCPCMNAGPBDOEGDCNKPCMJCKLOPCMNOHOPMNDKCDMDJLCCOFMNANPBOBBBMCPCABAGAAMNHIGMDOCOBCBDABACAAMNHIGMMNCIPAKPDCNLPCCBAABKMNAEOPMNJLOODKPGPCKHMJDCKMPCBBKNPB 57DATACBAGPEABAGAAMNHIGMMJMNKIGPCBKGPBDGDJDHMNEOBMMNHNPAMNIHPAMNMIPAMNMCGNMCHNPAMNKIGPCBKGPBDFDHMDEOBMCBKNPBHONGDKNKEOBMCDMDEOBMDACAECHJHEGFHDCAGGHCGFGFAADJEODIDBEOEOAAAAAAAAAAAAAAAAACADABFKFKDBAEABAAIEPPEG 58DATAFKFKDBAEABAAJGAPCEFKFKDBAEABAAJEAPCGBCEEHCGJHGGFCAEOGPHECAFCGFGBGEHJAABCEDGPGNGNHFGOGJGDGBHEGJGPGOCAEFHCHCGPHCAABCFHHCGJHEGFCAFAHCGPHEGFGDHEAABCEEHCGJHGGFCAFEHCGPHFGCGMGFAABCEEGJHDGLCAGOGPHECAGJGOCAGE 59DATAHCGJHGGFAADEFAHCGJGOHEGFHCCAGOGPHECAHCGFGBGEHJAAAFEGGJGMGFCAEFHIGJHDHEHDAADJEEGJHDGLCAEGHFGMGMAABKEGGJGMGFCAEFGNHAHEHJAADJEEGJHCGFGDHEGPHCHJCAEGHFGMGMAAAHFCGBGNCAEGHFGMGMAADGAADHECGBGECAEGGJGMGFCAEOGB 60DATAGNGFAACAFAHCGFHDHDCAGBGOHJCAGLGFHJCOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 61DATAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 62DATAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 63DATAEND \ No newline at end of file diff --git a/clients/ts-dos/TS-DOS.NEC.pre-install.txt b/clients/ts-dos/TS-DOS.NEC.pre-install.txt index bafc1b3..2b2795d 100644 --- a/clients/ts-dos/TS-DOS.NEC.pre-install.txt +++ b/clients/ts-dos/TS-DOS.NEC.pre-install.txt @@ -1,3 +1,3 @@ -Type the following in BASIC on the portable: +Type the following into BASIC on the NEC PC-8201/PC-8300 RUN "COM:9N81XN" diff --git a/clients/ts-dos/TSLOAD.100 b/clients/ts-dos/TSLOAD.100 new file mode 100644 index 0000000..04329ab --- /dev/null +++ b/clients/ts-dos/TSLOAD.100 @@ -0,0 +1 @@ +0'TSLOAD for TRS-80 Model 100 - Travelling Software - loader: co2ba.sh b.kenyon.w@gmail.com 2023-02-20 0CLEAR256,56400:T=56400:E=56926:K=65583:N$="TSLOAD":T$="TMP.DO":Q$=CHR$(34):A=T:S=0:CLS:?"Installing "N$" ..."; 1D$="":READD$:FORI=1TOLEN(D$)STEP2:B=(ASC(MID$(D$,I,1))-97)*16+ASC(MID$(D$,I+1,1))-97:POKEA,B:A=A+1:S=S+B:NEXT:?".";:IFA<=ETHEN1 2IFS<>KTHEN?"Bad Checksum":END 3OPENT$FOROUTPUTAS#1:?#1,"0CLEAR0,"T":RUNM"Q$N$Q$":CLEAR0,MAXRAM:MENU":CLOSE#1:CLS:?"Please type:":?"LOAD "Q$T$Q$:?"KILL "Q$T$Q$:?"SAVE "Q$"TS-DOS"Q$:SAVEMN$,T,E,T 4DATAcbaaaadjccfhnocbflpgbbbfnoabafaamnnlglcbapnodhmnogbhcbbonomnjhnncgbecoppcnmchenmcfmchcnmmngngnmcjdnmmnmlgocbapnodfdhmnogbhcbbonomnjhnnmneknndkifpgpobcmclonncbdhnomnjhnnmneknndkjppgkhmkmenncbdanomnjhnnmneknndkihpgkhmclonnmnddnnngagepmnhpnnfp 5DATAmnhpnnfhnfmfmnhpnngpmnhpnnghaiccblnomnhpnnmnhpnncknkpkhopoejmcpinmbbahaabjbbpinmnfofcbbnnodgabmjcknkpkabpdhpaimclinnmbnbnfmdannnnfmnddnnnbmnhpnnbcbdanmcannnmnhpnnckblnohnlemcainncbcenomnjhnnmneknnmnkcnndkbnnopoabmjcbcknomnjhnnmnhpnnmnhpnnag 6DATAaaepckblnoaiccblnomjcbifpgmnfmhgofmnhjnnmnhjnnepmnhjnnebhjkhmkginnmnhjnnanmcgbnnobaeaekpdaigcdafmcgnnncplomclonnmjmnhpnnhhcdmjmnipnnmnhognnklonnmclonnmjmndcgonlllogcamimdlonnegcdhomnimnnafmcjinnmjcbbfnohongdknkogbhcdmdogbhcbppnnmnkcbbmdmlbc 7DATAcbnknnmdmhnncbodnnmdmhnncboonnolckfhnopjolmndbecmnkcbbmnkpnnmnkcnnmjfcebencaeghfgmgmaaeegjhdglcaefhchcgphcaaeeepfddbdadacagogphecagggphfgogeaaahcafahcgfhdhdcagbgohjcaglgfhjaadjdieodbeeaaaaaaaaaaaaaaaaaaaaaffkfkahaapiaffkfkacaapnaffkfkadaapm 8DATAagfkfkababadpkbpfkfkaabkeeepfddbdadacoedepcacacacacacacacacacacacacacacaegaaiiaaaaaabenpdhabig \ No newline at end of file diff --git a/clients/ts-dos/TSLOAD.200 b/clients/ts-dos/TSLOAD.200 new file mode 100644 index 0000000..e7b0786 --- /dev/null +++ b/clients/ts-dos/TSLOAD.200 @@ -0,0 +1 @@ +0'TSLOAD for TANDY 200 - Travelling Software - loader: co2ba.sh b.kenyon.w@gmail.com 2023-02-20 0CLEAR256,54600:T=54600:E=55125:K=62866:N$="TSLOAD":T$="TMP.DO":Q$=CHR$(34):A=T:S=0:CLS:?"Installing "N$" ..."; 1D$="":READD$:FORI=1TOLEN(D$)STEP2:B=(ASC(MID$(D$,I,1))-97)*16+ASC(MID$(D$,I+1,1))-97:POKEA,B:A=A+1:S=S+B:NEXT:?".";:IFA<=ETHEN1 2IFS<>KTHEN?"Bad Checksum":END 3SAVEMN$,T,E,T:OPENT$FOROUTPUTAS#1:?#1,"0CLEAR0,"T":RUNM"Q$N$Q$":CLEAR0,MAXRAM:MENU":CLOSE#1:CLS:?"Please Type:":?"KILL "Q$T$Q$:?"SAVE "Q$"TS-DOS"Q$:?"CLEAR 0,MAXRAM":LOADT$ 4DATAcbaaaadjccfbnhcbdmopbbannhabahaamnbgidcbafnhdhmnbnbjcbbinhmninngcgbecoppcnmcgmnfcfmcgknfmnaiifmcilnfmnlfihcbafnhdfdhmnbnbjcbbinhmninngmnedngdkhaoppobcmclengcbdbnhmninngmnedngdkikopkhmklkngcbcknhmninngmnedngdkhcopkhmclengmncmngngagepmnhfngfp 5DATAmnhfngfhnfmfmnhfnggpmnhfngghaiccbfnhmnhfngmnhfngckahpfhopoejmcpbnfbbahaabjbbpbnfnfofcbbhnhdgabplmjckahpfabkijmaimckongmbnbnfmdagngnfmncmngnbmnhfngbcbdanmcagngmnhfngckbfnhhnlemcabngcbbonhmninngmnedngmnjingdkbhnhpoabmjcbcenhmninngmnhfngmnhfng 6DATAagaaepckbfnhaiccbfnhmjcbhaopofmngpngmngpngepmngpngebhjkhmkfongmngpnganmcfhngobaeaekpdaigcdafmcgdngcplomclengmjmnhfnghhcdmjmnifngmnbjifnklengmclengmjmnedignlmpogiamamdlengegcdhomnicngafmciongmjcbannhhongdknkbnbjcdmdbnbjcbpfngmnmmbbmdphbccbna 7DATAngmdlnngcbnjngmdlnngcboengolckfbnhpjolmnenepmnmmbbmnkfngmnjingmjfcebencaeghfgmgmaaeegjhdglcaefhchcgphcaaeeepfddcdadacagogphecagggphfgogeaaahcafahcgfhdhdcagbgohjcaglgfhjaadjdieodbeeeoeoaaaaaaaaaaaaaaaaaaaaaaaaaffkfkahaapiaffkfkacaapnaffkfkad 8DATAaapmagfkfkababadpkbpfkfkaabkeeepfddcdadacoedepcacacacacacacacacacacacacacacaegaaihaaaafopkaa \ No newline at end of file diff --git a/co2ba.md b/co2ba.md new file mode 100644 index 0000000..4a02131 --- /dev/null +++ b/co2ba.md @@ -0,0 +1,25 @@ +# co2ba.sh + +Reads a binary .CO file and generates an ascii BASIC loader .DO file + +The general usage is +```co2ba FILE.CO [action] [comment] > FILE.DO``` + +**FILE.CO** is the input binary .CO filename that you want to bootstrap onto the portable. + +**action** is what the loader should do with the .CO after it's done re-creating it on the portable: + call - Immediately execute - for TANDY, Kyotronic, Olivetti + exec - Immediately execute - for NEC + savem - Save FILE.CO - for TANDY, Kyotronic, Olivetti + bsave - Save FILE.CO - for NEC + Otherwise if the option is not given, or any other value than these, the loader will only print a message showing the Top, End, and Exec addresses of the loaded binary. + +**comment** is an optional custom replacement text for the first half of the line #0 comment. + By default a basic comment giving the name of the .CO file is generated. + The 2nd half of the line always has co2ba.sh itself and the date it was run to generate the loader. + You can use this to give more info about the payload than just the filename. + +**FILE.DO** is the output ascii BASIC .DO filename. + +For example, to generate TSLOAD.200 +```co2ba TSLOAD.CO savem "TSLOAD for TANDY 200 - Travelling Software" >TSLOAD.200``` diff --git a/co2ba.sh b/co2ba.sh new file mode 100755 index 0000000..46935aa --- /dev/null +++ b/co2ba.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Read a .CO file and generate a BASIC loader +# Brian K. White + +CO_IN="$1" ;shift +ACTION="${1^^}" ;shift +COMMENT="$@" + +CO="${CO_IN##*/}" ;CO="${CO:0:6}" ;CO="${CO%%.*}.CO" +BYTES_PER_DATA_LINE=120 +typeset -ra h=({a..p}) # hex data output alphabet + +typeset -i i t b c SUM TOP END EXE LEN LINE +typeset -a d=() + +abrt () { printf '%s: Usage\n%s IN.CO [call|exec|savem|bsave] > OUT.DO\n%s\n' "$0" "${0##*/}" "$@" >&2 ;exit 1 ; } + +# read a binary file into to global int array d[] +ftoi () { + [[ -r "$1" ]] || abrt "Can't read \"$1\"" + local -i i= ;local x= LANG=C ;d=() + while IFS= read -d '' -r -n 1 x ;do printf -v d[i++] '%u' "'$x" ;done < $1 +} + +############################################################################### + +# help +[[ "${CO_IN}" ]] || abrt + +# read the .CO file into d[] +ftoi "$CO_IN" + +# parse & discard the .CO header +((TOP=${d[0]}+${d[1]}*256)) +((LEN=${d[2]}+${d[3]}*256)) +((EXE=${d[4]}+${d[5]}*256)) +d=(${d[*]:6}) +((LEN==${#d[*]})) || abrt "Corrupt .CO file\nheader declares LEN=$LEN\nfile has ${#d[*]} bytes after header" +((END=TOP+LEN-1)) +SUM= ;for ((i=0;i%uTHEN?"Bad Checksum":END\r' $((LINE++)) $SUM + +# action after loading +case "$ACTION" in + CALL|EXEC) printf '%u%s%u\r' $((LINE++)) $ACTION $EXE ;; + SAVEM|BSAVE) printf '%u?:?"Done. Please type: NEW":%sN$,%u,%u,%u\r' $((LINE++)) $ACTION $TOP $END $EXE ;; + *) printf '%uCLS:?"Loaded:":?"top %u":?"end %u":?"exe %u":"?"Please type: NEW"\r' $((LINE++)) $TOP $END $EXE ;; +esac + +# DATA lines +c= ;for ((i=0;i + +// TPDD drive firmware/protocol constants + +// TPDD request block formats +#define REQ_DIRENT 0x00 // (add 0x40 for TPDD2 bank 1) +#define REQ_OPEN 0x01 // (add 0x40 for TPDD2 bank 1) +#define REQ_CLOSE 0x02 // (add 0x40 for TPDD2 bank 1) +#define REQ_READ 0x03 // (add 0x40 for TPDD2 bank 1) +#define REQ_WRITE 0x04 // (add 0x40 for TPDD2 bank 1) +#define REQ_DELETE 0x05 // (add 0x40 for TPDD2 bank 1) +#define REQ_FORMAT 0x06 +#define REQ_STATUS 0x07 // (add 0x40 for undocumented synonym on TPDD2) +#define REQ_FDC 0x08 // TPDD1 +#ifdef NADSBOX_EXTENSIONS + #define REQ_NADSBOX_SEEK 0x09 + #define REQ_NADSBOX_TELL 0x0A + #define REQ_NADSBOX_SET_EXT 0x0B +#endif +#define REQ_CONDITION 0x0C // TPDD2 +#define REQ_RENAME 0x0D // TPDD2 (add 0x40 for bank 1) +#ifdef NADSBOX_EXTENSIONS + #define REQ_NADSBOX_GET_EXT 0x0E + #define REQ_NADSBOX_COND_LIST 0x0F // NADSBox but TPDD2 also responds with RET_CACHE +#endif +#define REQ_VERSION 0x23 // TPDD2 Get Version Number +#define REQ_CACHE 0x30 // TPDD2 sector access +#define REQ_MEM_WRITE 0x31 // TPDD2 sector access +#define REQ_MEM_READ 0x32 // TPDD2 sector access +#define REQ_SYSINFO 0x33 // TPDD2 Get System Information +#define REQ_EXEC 0x34 // TPDD2 Execute Program + +// TPDD return block formats {fmt,len} +#define RET_READ 0x10 +static const uint8_t RET_DIRENT[2] = {0x11,0x1C}; +static const uint8_t RET_STD[2] = {0x12,0x01}; // shared return format for: error open close delete status write +static const uint8_t RET_VERSION[2] = {0x14,0x0F}; // TPDD2 +static const uint8_t RET_CONDITION[2] = {0x15,0x01}; // TPDD2 +static const uint8_t RET_CACHE[2] = {0x38,0x01}; // TPDD2 shared return format for: cache mem_write cond_list +#define RET_MEM_READ 0x39 // TPDD2 +static const uint8_t RET_SYSINFO[2] = {0x3A,0x06}; // TPDD2 +static const uint8_t RET_EXEC[2] = {0x3B,0x03}; // TPDD2 + +// directory entry request types +#define DIRENT_SET_NAME 0x00 +#define DIRENT_GET_FIRST 0x01 +#define DIRENT_GET_NEXT 0x02 +#define DIRENT_GET_PREV 0x03 // TPDD2 +#define DIRENT_CLOSE 0x04 // TPDD2 + +// file open access modes +#define F_OPEN_NONE 0x00 // used in here, not part of protocol +#define F_OPEN_WRITE 0x01 +#define F_OPEN_APPEND 0x02 +#define F_OPEN_READ 0x03 + +// TPDD Operation-mode error codes +// Normal +#define ERR_SUCCESS 0x00 // 'Operation Complete' +// File +#define ERR_NO_FILE 0x10 // 'File Not Found' +#define ERR_EXISTS 0x11 // 'File Exists' +// Sequence +#define ERR_NO_FNAME 0x30 // 'Missing Filename' +#define ERR_DIR_SEARCH 0x31 // 'Directory Search Error' +#define ERR_BANK 0x35 // 'Bank Error' +#define ERR_PARAM 0x36 // 'Parameter Error' +#define ERR_FMT_MISMATCH 0x37 // 'Open Format Mismatch' +#define ERR_EOF 0x3F // 'End of File' +// Disk I/O +#define ERR_NO_START 0x40 // 'No Start Mark' +#define ERR_ID_CRC 0x41 // 'ID CRC Check Error' +#define ERR_SECTOR_LEN 0x42 // 'Sector Length Error' +#define ERR_FMT_VERIFY 0x44 // 'Format Verify Error' +#define ERR_NOT_FORMATTED 0x45 // 'Disk Not Formatted' +#define ERR_FMT_INTERRUPT 0x46 // 'Format Interruption' +#define ERR_ERASE_OFFSET 0x47 // 'Erase Offset Error' +#define ERR_DATA_CRC 0x49 // 'DATA CRC Check Error' +#define ERR_SECTOR_NUM 0x4A // 'Sector Number Error' +#define ERR_READ_TIMEOUT 0x4B // 'Read Data Timeout' +#define ERR_SECTOR_NUM2 0x4D // 'Sector Number Error' +// Protect +#define ERR_WRITE_PROTECT 0x50 // 'Write-Protected Disk' +#define ERR_DISK_NOINIT 0x5E // 'Disk Not Formatted' +#define ERR_WP_TPDD1_DISK 0x5F // TPDD2 'Write Protect to 26-3808 Diskette' +// File Territory +#define ERR_DIR_FULL 0x60 // 'Disk Full or Max File Size Exceeded or Directory Full' / TPDD2 'Directory Full' +#define ERR_DISK_FULL 0x61 // 'Disk Full' +#define ERR_FILE_LEN 0x6E // 'File Too Long' (real drive limits to 65534, we exceed for REXCPM) +// Diskette Condition +#define ERR_NO_DISK 0x70 // 'Disk Not Inserted' +#define ERR_DISK_CHG 0x71 // 'Disk Change Error' +// Sensor +#define ERR_NO_INDEX_SIGNAL 0x80 +#define ERR_ABNORMAL_TRACK_ZERO 0x81 +#define ERR_ABNORMAL_INDEX_SIGNAL 0x82 +#define ERR_DEFECTIVE 0x83 // 'Defective Disk' - real drive needs a power-cycle to clear this error - not in the manual + +// TPDD1 FDC-mode commands +#define FDC_SET_MODE 'M' // set Operation-mode or FDC-mode +#define FDC_CONDITION 'D' // drive condition +#define FDC_FORMAT 'F' // format disk +#define FDC_FORMAT_NV 'G' // format disk without verify +#define FDC_READ_ID 'A' // read sector ID +#define FDC_READ_SECTOR 'R' // read sector data +#define FDC_SEARCH_ID 'S' // search sector ID +#define FDC_WRITE_ID 'B' // write sector ID +#define FDC_WRITE_ID_NV 'C' // write sector ID without verify +#define FDC_WRITE_SECTOR 'W' // write sector data +#define FDC_WRITE_SECTOR_NV 'X' // write sector data without verify +static const char FDC_CMDS[] = {FDC_SET_MODE,FDC_CONDITION,FDC_FORMAT,FDC_FORMAT_NV,FDC_READ_ID,FDC_READ_SECTOR,FDC_SEARCH_ID,FDC_WRITE_ID,FDC_WRITE_ID_NV,FDC_WRITE_SECTOR,FDC_WRITE_SECTOR_NV,0x00}; + +// TPDD1 FDC-mode error codes +// There is no documentation for FDC error codes. +// These are guesses from experimenting. +// These appear in the first hex pair of an 8-byte FDC-mode response. +#define ERR_FDC_SUCCESS 0x00 // 'OK' +#define ERR_FDC_LSN_LO 0x11 // 'Logical Sector Number Below Range' +#define ERR_FDC_LSN_HI 0x12 // 'Logical Sector Number Above Range' +#define ERR_FDC_PSN_HI 0x13 // 'Physical Sector Number Above Range' +#define ERR_FDC_PARAM 0x21 // 'Parameter Invalid, Wrong Type' +#define ERR_FDC_LSSC_LO 0x32 // 'Invalid Logical Sector Size Code' +#define ERR_FDC_LSSC_HI 0x33 // 'Logical Sector Size Code Above Range' +#define ERR_FDC_ID_NOT_FOUND 0x3C // 'ID Not Found' +#define ERR_FDC_S_BAD_PARAM 0x3D // 'Search ID Unexpected Parameter' +#define ERR_FDC_NOT_FORMATTED 0xA0 // 'Disk Not Formatted' +#define ERR_FDC_READ 0xA1 // 'Read Error' +#define ERR_FDC_WRITE_PROTECT 0xB0 // 'Write-Protected Disk' +#define ERR_FDC_COMMAND 0xC1 // 'Invalid Command' +#define ERR_FDC_NO_DISK 0xD1 // 'Disk Not Inserted' +#define ERR_FDC_INTERRUPTED 0xD8 // 'Operation Interrupted' + +// TPDD1 FDC Logical Sector Length Codes +static const uint16_t FDC_LOGICAL_SECTOR_SIZE[7] = {64,80,128,256,512,1024,1280}; + +// TPDD1 Condition bits +#define PDD1_COND_BIT_NOTINS 7 // disk not inserted +#define PDD1_COND_BIT_CHANGED 6 // disk changed +#define PDD1_COND_BIT_WPROT 5 // disk write-protected +#define PDD1_COND_BIT_4 4 +#define PDD1_COND_BIT_3 3 +#define PDD1_COND_BIT_2 2 +#define PDD1_COND_BIT_1 1 +#define PDD1_COND_BIT_0 0 +#define PDD1_COND_NONE 0x00 // no conditions + +// TPDD2 Condition bits +#define PDD2_COND_BIT_7 7 +#define PDD2_COND_BIT_6 6 +#define PDD2_COND_BIT_5 5 +#define PDD2_COND_BIT_4 4 +#define PDD2_COND_BIT_CHANGED 3 // disk changed +#define PDD2_COND_BIT_NOTINS 2 // disk not inserted +#define PDD2_COND_BIT_WPROT 1 // disk write protected +#define PDD2_COND_BIT_POWER 0 // low power +#define PDD2_COND_NONE 0x00 // no conditions + +// lengths & addresses +#define PDD1_TRACKS 40 +#define PDD1_SECTORS 2 +#define PDD2_TRACKS 80 +#define PDD2_SECTORS 2 +#define DIRENTS 40 +#define REQ_RW_DATA_MAX 128 // largest chunk size in req_read() req_write() +#define TPDD_FILENAME_LEN 24 +#define LOCAL_FILENAME_MAX 256 +#define SECTOR_ID_LEN 12 +#define SECTOR_HEADER_LEN (SECTOR_ID_LEN+1) // pdd1: lsc+id, pdd2: id+unknown +#define SECTOR_DATA_LEN 1280 +//#define OLD_PDD2_HEADER_LEN 4 // for old .pdd2 disk image files +#define SECTOR_LEN (SECTOR_HEADER_LEN+SECTOR_DATA_LEN) +#define PDD1_IMG_LEN (PDD1_TRACKS*PDD1_SECTORS*SECTOR_LEN) +#define PDD2_IMG_LEN (PDD2_TRACKS*PDD2_SECTORS*SECTOR_LEN) +#define SMT_OFFSET 1240 +#define PDD1_SMT 0x80 +#define PDD2_SMT 0xC0 +#define PDD2_MEM_READ_MAX 252 // real drive absolute limit +#define PDD2_MEM_WRITE_MAX 127 // real drive absolute limit +#define TPDD_MSG_MAX 256 // largest theoretical packet is 256+3, largest actual is 252+3 + +// cpu memory map +#define IOPORT_ADDR 0x00 +#define IOPORT_LEN 0x1F +#define CPURAM_ADDR 0x80 +#define CPURAM_LEN 0x7F +#define GA_ADDR 0x4000 +#define GA_LEN 0x03 +#define RAM_ADDR 0x8000 +#define RAM_LEN 0x0800 +#define ROM_ADDR 0xF000 +#define ROM_LEN 0x1000 + +// sector cache +#define PDD2_ID_REL 0x04 +#define PDD2_ID_ADDR (RAM_ADDR+PDD2_ID_REL) +#define PDD2_DATA_REL 0x13 +#define PDD2_DATA_ADDR (RAM_ADDR+PDD2_DATA_REL) +#define PDD2_CACHE_LEN (PDD2_DATA_REL+SECTOR_DATA_LEN) +#define PDD2_CACHE_LEN_MSB ((PDD2_CACHE_LEN>>0x08)&0xFF) // 0x05 +#define PDD2_CACHE_LEN_LSB (PDD2_CACHE_LEN&0xFF) // 0x13 + +// TPDD2 version data: 41 10 01 00 50 05 00 02 00 28 00 E1 00 00 00 +#define VERSION_MSB 0x41 +#define VERSION_LSB 0x10 +#define SIDES 0x01 +#define TRACKS_MSB ((PDD2_TRACKS>>0x08)&0xFF) +#define TRACKS_LSB (PDD2_TRACKS&0xFF) +#define SECTOR_SIZE_MSB ((SECTOR_DATA_LEN>>0x08)&0xFF) +#define SECTOR_SIZE_LSB (SECTOR_DATA_LEN&0xFF) +#define SECTORS_PER_TRACK 0x02 +#define DIRENTS_MSB ((DIRENTS>>0x08)&0xFF) +#define DIRENTS_LSB (DIRENTS&0xFF) +#define MAX_FD 0x00 // it's 0 but it means the highest fd# is 0, meaning max 1 open file +#define MODEL_CODE 0xE1 // E1 = TPDD2 +#define VERSION_R0 0x00 +#define VERSION_R1 0x00 +#define VERSION_R2 0x00 + +// TPDD2 sysinfo data: 80 13 05 00 10 E1 +#define SECTOR_CACHE_START_MSB ((PDD2_DATA_ADDR>>0x08)&0xFF) // 0x80 +#define SECTOR_CACHE_START_LSB (PDD2_DATA_ADDR&0xFF) // 0x13 +// sysinfo[2] = SECTOR_SIZE_MSB +// sysinfo[3] = SECTOR_SIZE_LSB +#define SYSINFO_CPU_CODE 0x10 // 0x10 = HD6301 +// sysinfo[5] = MODEL_CODE + +// flags +#define FE_FLAGS_NONE 0 +#define FE_FLAGS_DIR 1 +#define NO_RET 0 +#define ALLOW_RET 1 +#define CACHE_LOAD 0 +#define CACHE_COMMIT 1 +#define CACHE_COMMIT_VERIFY 2 +#define MEM_CACHE 0 +#define MEM_CPU 1 + +// KC-85-platform BASIC interpreter EOL & EOF bytes for bootstrap() +#define BASIC_EOL 0x0D +#define BASIC_EOF 0x1A +#define LOCAL_EOL 0x0A + +// drive command seperators +#define OPR_CMD_SYNC 0x5A +#define FDC_CMD_EOL 0x0D + +// drive operating modes +#define MODE_OPR 1 +#define MODE_FDC 0 + +#ifdef RAW_ATTR +#define ATTR_RAW RAW_ATTR +#else +#define ATTR_RAW 0x20 // space - drive firmware fills unused with 0x20 +#endif + +#ifdef DEFAULT_ATTR +#define ATTR_DEF DEFAULT_ATTR +#else +#define ATTR_DEF 0x46 // F - almost all clients on all platforms hardcode F +#endif + +#endif // PDD_CONSTANTS_H diff --git a/dir_list.c b/dir_list.c index a2e5eb0..a91d600 100644 --- a/dir_list.c +++ b/dir_list.c @@ -1,12 +1,13 @@ /* -DeskLink+ +DeskLink2 Extensions and enhancements Copyright (C) 2005 John R. Hogerhuis +Copyright (c) 2022 Gabriele Gorla -DeskLink+ is free software; you can redistribute it and/or modify it +DeskLink2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or any later as version as published by the Free Software Foundation. -DeskLink+ is distributed in the hope that it will be useful, but +DeskLink2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -17,243 +18,96 @@ Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ - +#include #include #include #include #include -#include "dir_list.h" - - -typedef struct -{ - Char name[12] ; // file name - UInt32 len ; // length - LocalID dbID ; // database ID -} FILE_ENTRY; -#define MAX(a,b) ((a)>(b)?(a):(b)) - -static UInt16 allocated; -static UInt16 ndx; -static Int16 cur; // current must be signed (get-prev) -static FILE_ENTRY *tblp = 0; +#include "dir_list.h" -static Err get_current_record (Char *namep, UInt32 *lenp, LocalID *dbIDp); +static uint16_t allocated; +static uint16_t ndx; +static uint16_t cur; +static FILE_ENTRY* tblp = 0; -int file_list_init () -{ - tblp = malloc (sizeof (FILE_ENTRY) * QUANTUM + sizeof (Char *) * QUANTUM); - if (!tblp) - return (-1); - allocated = QUANTUM; - ndx = 0; - cur = 0; +static FILE_ENTRY* current_record(void); - return (0); +int file_list_init() { + tblp = malloc(sizeof(FILE_ENTRY)*DIRENTS); + if (!tblp) return -1; + allocated = DIRENTS; + ndx = 0; + cur = 0; + return 0; } -int file_list_cleanup() -{ - - - allocated = 0; - ndx = 0; - cur = 0; - if (tblp) - free (tblp); - tblp = NULL; - - return (0); +int file_list_cleanup() { + allocated = 0; + ndx = 0; + cur = 0; + if (tblp) free(tblp); + tblp = NULL; + return 0; } -void file_list_clear_all () -{ - cur = ndx = 0; +void file_list_clear_all() { + cur = ndx = 0; } - -Err find_file (Char *find_namep, UInt32 *lenp, LocalID *dbIDp) -{ +int add_file(FILE_ENTRY* fe) { + /* allocate DIRENTS more records if out of space */ + if (ndx >= allocated) { + /* resize the array */ + tblp = realloc(tblp, (allocated+DIRENTS)*sizeof(FILE_ENTRY)); + if (!tblp) return -1; + allocated += DIRENTS; + } - for (cur = 0; cur < ndx; cur++) - if (strncasecmp ((char *) find_namep, (char *) tblp[cur].name, sizeof (tblp[cur].name) - 1) == 0) - { - if (lenp) *lenp = tblp[cur].len; - if (dbIDp) *dbIDp = tblp[cur].dbID; - return (0); - } + /* reference the entry */ + if (!tblp) return -1; - return (-1); -} - -Err addto_file_len (UInt32 len_delta) -{ - tblp[cur].len += len_delta; + memcpy(tblp+ndx, fe, sizeof(FILE_ENTRY)); + /* adjust cur to address this record, ndx to next avail */ + cur = ndx; + ndx++; - return (0); + return 0; } -Err delete_file (LocalID dbID) -{ - UInt16 i, j; - FILE_ENTRY *ep; - Char **cpp; - - /** find the entry */ - for (i = 0, ep = tblp; i < ndx; i++, ep++) - if (ep->dbID == dbID) break; - - /** no matching entry; return error */ - if (i >= ndx) return (-1); - - /** move up all entries to cover */ - memmove (ep, ep + 1, (ndx - i - 1) * sizeof (FILE_ENTRY)); - - /** adjust indices */ - ndx--; - if (cur > i) cur--; - - /** correct trailing pointers */ - cpp = (Char **) (tblp + allocated); - for (j = i; j < ndx; j++) - cpp[j] = tblp[j].name; - cpp[ndx] = NULL; - - return (0); - +FILE_ENTRY* find_file(char* client_fname, uint8_t attr) { + int i; + for (i=0;idbID == dbID) break; - - /** no matching entry; return error */ - if (i >= ndx) return (-1); - - /** update the name */ - strncpy ((char *) ep->name, (char *) namep, sizeof (ep->name) * sizeof (Char) - 1); - - return (0); +FILE_ENTRY* get_first_file(void) { + cur = 0; + return current_record(); } -int add_file (Char *namep, UInt32 len, LocalID dbID) -{ - FILE_ENTRY *ep; - Char **cpp; - int i; - - /** reallocate QUANTUM more records if out of space */ - if (ndx >= allocated) - { - - /** resize the array */ - tblp = realloc (tblp, (allocated + QUANTUM) * (sizeof (FILE_ENTRY) + sizeof (Char *))); - if (!tblp) return (-1); - allocated += QUANTUM; - - /** update the char * array */ - for (i = 0, ep = tblp, cpp = (Char **) (tblp + allocated); i < ndx; - i++, ep++, cpp++) - *cpp = ep->name; - } - - /** reference the entry */ - if (!tblp) return (-1); - ep = tblp + ndx; - - /** fill the entry */ - strncpy ((char *) ep->name, (char *)namep, sizeof (ep->name) * sizeof (Char) - 1); - ep->len = len; - ep->dbID = dbID; - - /** set the pointer for use in filling list controls */ - ((Char **) (tblp + allocated))[ndx] = ep->name; - - /** adjust cur to address this record, ndx to next avail */ - cur = ndx; - ndx += 1; - - return (0); +FILE_ENTRY* get_next_file(void) { + if (cur + 1 > ndx) return NULL; + cur++; + return current_record(); } - -Char **get_str_table(UInt16 *lenp) -{ - /** valchk */ - if (!tblp) - { - *lenp = 0; - return (NULL); - } - - /** return string table */ - if (lenp) - *lenp = ndx; - return ((Char **) (tblp + allocated)); +FILE_ENTRY* get_prev_file(void) { + if (cur==0) return NULL; + cur--; + return current_record(); } - -Err get_first_file (Char *namep, UInt32 *lenp, LocalID *dbIDp) -{ - Err err; - - /** reset cur, get, next */ - cur = 0; - if ((err = get_current_record (namep, lenp, dbIDp))) return (err); - - return (0); -} - -Err get_next_file (Char *namep, UInt32 *lenp, LocalID *dbIDp) -{ - Err err; - - /** return error if out-of-range */ - if (cur + 1 > ndx) - return (-1); - - /** get, next */ - cur++; - if ((err = get_current_record (namep, lenp, dbIDp))) return (err); - - return (0); -} - -Err get_prev_file (Char *namep, UInt32 *lenp, LocalID *dbIDp) -{ - Err err; - - /** move back by one, but floor at -1 */ - cur = MAX (cur -1, -1); - - /** ensure don't go off the shallow end */ - if (cur < 0) return (-1); - - /** get */ - if ((err = get_current_record (namep, lenp, dbIDp))) return (err); - - return (0); -} - -static Err get_current_record (Char *namep, UInt32 *lenp, LocalID *dbIDp) -{ - FILE_ENTRY *ep; - - /** return error if out-of-range */ - if (cur >= ndx) return (-1); - - /** reference the record, fill in caller's vars */ - if (!tblp) return (-1); - ep = tblp + cur; - if (namep) strcpy ((char *) namep, (char *) ep->name); - if (lenp) *lenp = ep->len; - if (dbIDp) *dbIDp = ep->dbID; - - return (0); +static FILE_ENTRY* current_record(void) { + FILE_ENTRY* ep; + if (cur >= ndx) return NULL; + if (!tblp) return NULL; + ep = tblp + cur; + return ep; } diff --git a/dir_list.h b/dir_list.h index 0411d47..99451ec 100644 --- a/dir_list.h +++ b/dir_list.h @@ -1,12 +1,13 @@ /* -DeskLink+ +DeskLink2 Extensions and enhancements Copyright (C) 2005 John R. Hogerhuis +Copyright (c) 2022 Gabriele Gorla -DeskLink+ is free software; you can redistribute it and/or modify it +DeskLink2 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or any later as version as published by the Free Software Foundation. -DeskLink+ is distributed in the hope that it will be useful, but +DeskLink2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. @@ -17,33 +18,29 @@ Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ - #ifndef DIR_LIST #define DIR_LIST -#define QUANTUM 10 +#include +#include "constants.h" -typedef unsigned LocalID; -typedef unsigned char Char; -typedef int Err; -typedef unsigned short UInt16; -typedef short int Int16; -typedef unsigned UInt32; +typedef struct { + char client_fname[TPDD_FILENAME_LEN+1]; + char local_fname[LOCAL_FILENAME_MAX+1]; + uint8_t attr; + uint16_t len; + uint8_t flags; +} FILE_ENTRY; int file_list_init (); -int file_list_cleanup(); - -int add_file (Char *namep, UInt32 len, LocalID dbID); +int file_list_cleanup (); + void file_list_clear_all (); -Char **get_str_table(UInt16 *lenp); - -Err get_first_file (Char *namep, UInt32 *lenp, LocalID *dbIDp); -Err get_next_file (Char *namep, UInt32 *lenp, LocalID *dbIDp); -Err get_prev_file (Char *namep, UInt32 *lenp, LocalID *dbIDp); - -Err find_file (Char *find_namep, UInt32 *lenp, LocalID *dbIDp); -Err addto_file_len (UInt32 len_delta); -Err delete_file (LocalID dbID); -Err rename_file (LocalID dbID, Char *namep); +int add_file (FILE_ENTRY* fe); + +FILE_ENTRY* find_file (char* client_fname, uint8_t attr); +FILE_ENTRY* get_first_file (void); +FILE_ENTRY* get_next_file (void); +FILE_ENTRY* get_prev_file (void); #endif diff --git a/dl.c b/dl.c deleted file mode 100644 index c2fed00..0000000 --- a/dl.c +++ /dev/null @@ -1,934 +0,0 @@ -/* - * DeskLink for *nix (dl) - * Copyright (C) 2004 - * Stephen Hurd - * - * Redistribution of modified and unmodified copies - * is premitted provided the copyright remains intact - */ - -/* -DeskLink+ -Extensions and enhancements Copyright (C) 2005 John R. Hogerhuis -20191226 Brian K. White - repackaging, reorganizing, bootstrap function - Kurt McCullum - TS-DOS loaders - -DeskLink+ is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License version 2 or any -later as version as published by the Free Software Foundation. - -DeskLink+ is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program (in the file "COPYING"); if not, write to the -Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, -MA 02111, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "dir_list.h" - -#if defined(__darwin__) -#include -#endif - -#if defined(__FreeBSD__) -#include -#endif - -#if defined(__NetBSD__) || defined(OpenBSD) -#include -#endif - -#if defined(__linux__) -#include -#include -#endif - -#ifndef APP_LIB_DIR -#define APP_LIB_DIR . -#endif - -#ifndef DEFAULT_CLIENT_TTY -#define DEFAULT_CLIENT_TTY ttyS0 -#endif - -#ifndef DEFAULT_CLIENT_BAUD -#define DEFAULT_CLIENT_BAUD B19200 -#endif - -#ifndef DEFAULT_CLIENT_MODEL -#define DEFAULT_CLIENT_MODEL 100 -#endif - -#ifndef DEFAULT_CLIENT_APP -#define DEFAULT_CLIENT_APP TEENY -#endif - -#define DEFAULT_BOOTSTRAP_BYTE_MSEC 6 - -#define BASIC_EOF 0x1A - -#define STRINGIFY2(X) #X -#define STRINGIFY(X) STRINGIFY2(X) - -unsigned prev_dir_type = 0; -unsigned fname_ndx = 0; -unsigned file_len; -LocalID cur_id; - -int file = -1; -int mode = 0; /* 0=unopened, 1=Write, 3=Read, 2=Append */ -int client_fd = -1; // client tty file handle -unsigned char filename[25]; -struct dirent *dire; -DIR *dir = NULL; -char *dirname; -char **args; -struct termios origt; -struct termios ti; -bool getty_mode = false; -bool bootstrap_mode = false; -int bootstrap_byte_msec = DEFAULT_BOOTSTRAP_BYTE_MSEC; -bool debug = false; -bool upcase = false; -bool rtscts = false; -unsigned dot_offset = 6; // 6 for 100/102/200/NEC/K85/M10 , 8 for WP-2 -unsigned char buf[131]; -int client_baud = DEFAULT_CLIENT_BAUD; - -int be_disk(void); - -void out_buf(unsigned char *bufp, unsigned len); - -int bootstrap(char *f); - -int send_installer(char *f); - -void print_usage() { - fprintf (stderr, "DeskLink+ usage:\n"); - fprintf (stderr, "\n"); - fprintf (stderr, "%s [tty_device] [options]\n",args[0]); - fprintf (stderr, "\n"); - fprintf (stderr, "tty_device:\n"); - fprintf (stderr, " Serial device the client is connected to\n"); - fprintf (stderr, " examples: ttyS0, ttyUSB0, /dev/pts/foo4, etc...\n"); - fprintf (stderr, " default = " STRINGIFY(DEFAULT_CLIENT_TTY) "\n"); - fprintf (stderr, " \"-\" = stdin/stdout (/dev/tty)\n"); // 20191227 bkw - could this be used with inetd to make a network tpdd server? - fprintf (stderr, "\n"); - fprintf (stderr, "options:\n"); - fprintf (stderr, " -h Print this help\n"); - fprintf (stderr, " -b=file Bootstrap: Install onto the portable\n"); - fprintf (stderr, " -v Verbose/debug mode\n"); - fprintf (stderr, " -g Getty mode. Run as daemon\n"); - fprintf (stderr, " -p=dir Path to files to be served, default is \".\"\n"); - fprintf (stderr, " -w WP-2 compatibility mode (8.2 filenames)\n"); - fprintf (stderr, " -u Uppercase all filenames\n"); - fprintf (stderr, " -c Hardware flow control (RTS/CTS)\n"); - fprintf (stderr, " -z=# Sleep # milliseconds between each byte while sending bootstrap file (default " STRINGIFY(DEFAULT_BOOTSTRAP_BYTE_MSEC) ")\n"); - fprintf (stderr, "\n"); - fprintf (stderr, "available bootstrap files:\n"); - // blargh ... - //(void)(system ("find " STRINGIFY(APP_LIB_DIR) " -regex \'.*/.+\\.\\(100\\|200\\|NEC\\|M10\\|K85\\)$\' -printf \'\%f\\n\' >&2")+1); - // more blargh... (using system("find...") just to get some filenames) - fprintf (stderr, " TRS-80 Model 100 / Tandy 102 : "); - (void)(system ("find " STRINGIFY(APP_LIB_DIR) " -regex \'.*/.+\\.100$\' -printf \'\%f \' >&2")+1); - fprintf (stderr, "\n Tandy 200 : "); - (void)(system ("find " STRINGIFY(APP_LIB_DIR) " -regex \'.*/.+\\.200$\' -printf \'\%f \' >&2")+1); - fprintf (stderr, "\n NEC PC-8201/PC-8201a/PC-8300 : "); - (void)(system ("find " STRINGIFY(APP_LIB_DIR) " -regex \'.*/.+\\.NEC$\' -printf \'\%f \' >&2")+1); - fprintf (stderr, "\n Kyotronic KC-85 : "); - (void)(system ("find " STRINGIFY(APP_LIB_DIR) " -regex \'.*/.+\\.K85$\' -printf \'\%f \' >&2")+1); - fprintf (stderr, "\n Olivetti M-10 : "); - (void)(system ("find " STRINGIFY(APP_LIB_DIR) " -regex \'.*/.+\\.M10$\' -printf \'\%f \' >&2")+1); - fprintf (stderr, "\n\n"); - fprintf (stderr, "Bootstrap Examples:\n"); - fprintf (stderr, " %s -b=TS-DOS.100\n",args[0]); - fprintf (stderr, " %s -b=~/Documents/TRS-80/M100SIG/Lib-03-TELCOM/XMDPW5.100\n",args[0]); - fprintf (stderr, " %s -b=./rxcini.DO\n",args[0]); - fprintf (stderr, "\n"); - fprintf (stderr, "TPDD Server Examples:\n"); - fprintf (stderr, " %s\n",args[0]); - fprintf (stderr, " %s ttyUSB1 -p=~/Documents/wp2files -w -v\n",args[0]); - fprintf (stderr, "\n"); - -} - -void cat(char *f) { - int h = -1; - char b[4097]; - - if((h=open(f,O_RDONLY))<0) - return; - while(read(h,&b,4096)>0) - printf("%s",b); - close(h); -} - -int bootstrap(char *f) { - int r = 0; - char installer_file[PATH_MAX]=""; - char pre_install_txt_file[PATH_MAX]=""; - char post_install_txt_file[PATH_MAX]=""; - - if (f[0]=='~'&&f[1]=='/') { - strcpy(installer_file,getenv("HOME")); - strcat(installer_file,f+1); - } - - if ((f[0]=='/')||(f[0]=='.'&&f[1]=='/')) - strcpy(installer_file,f); - - if(installer_file[0]==0) { - strcpy(installer_file,STRINGIFY(APP_LIB_DIR)); - strcat(installer_file,"/"); - strcat(installer_file,f); - } - - strcpy(pre_install_txt_file,installer_file); - strcat(pre_install_txt_file,".pre-install.txt"); - - strcpy(post_install_txt_file,installer_file); - strcat(post_install_txt_file,".post-install.txt"); - - printf("Bootstrap: Installing %s\n", installer_file); - - if(access(installer_file,F_OK)==-1) { - if(debug) - fprintf(stderr, "Not found.\n"); - return(1); - } - - cat(pre_install_txt_file); - - printf("Press [Enter] when ready..."); - getchar(); - - if ((r=send_installer(installer_file))!=0) - return(r); - - cat(post_install_txt_file); - - printf("\n\n\"%s -b\" will now exit.\n",args[0]); - printf("Re-run \"%s\" (without -b this time) to run the TPDD server.\n",args[0]); - printf("\n"); - - return(0); -} - -int my_write (int fh, void *srcp, size_t len) { - if (debug) { - fprintf (stderr, "Writing: "); - out_buf (srcp, len); - } - return (write (fh, srcp, len)); -} - -unsigned char calc_sum(unsigned char type, unsigned char length, unsigned char *data) { - unsigned short sum=0; - int i; - - sum+=type; - sum+=length; - for(i=0;ist_size); - memcpy(buf+27,&size,2); - memset(buf+2,' ',24); - - if (upcase) - for (i = 0; i < strlen((char *)dire->d_name); i++) { - buf[2+i] = toupper(dire->d_name[i]); - //if(debug) fprintf(stderr,"buf: |%s|\n",buf); - } - else - memcpy(buf+2,dire->d_name,strlen((char *)dire->d_name)); - - //if(debug) fprintf(stderr,"buf: |%s|\n",buf); - - dot = memchr(buf+2,'.',24); - - if(dot!=NULL) { - //if(debug) fprintf(stderr,"buf: |%s|\n",buf); - memmove (buf + dot_offset + 2, dot, 3); - //if(debug) fprintf(stderr,"buf: |%s|\n",buf); - for( p = dot; p < buf + dot_offset + 2; p++) { - *p=' '; - //if(debug) fprintf(stderr,"buf: |%s|\n",buf); - } - } - - } - - buf[30] = calc_sum (0x11, 0x1C, buf+2); - if(debug) fprintf(stderr,"buf: |%s|\n",buf); - return (my_write (client_fd, buf, 31) == 31); -} - -int read_next_dirent(struct stat *st) { - - if (dir == NULL) { - printf ("%s:%u\n", __FUNCTION__, __LINE__); - dire=NULL; - normal_return (0x70); - return(0); - } - while((dire=readdir(dir)) != NULL) { - if (debug) - fprintf (stderr, "%s (%u)", dire->d_name, (unsigned) st->st_size); - - if(stat(dire->d_name,st)) { - normal_return(0x31); - return 0; - } - - if (!S_ISREG (st->st_mode)) - continue; - - /* add file to list so we can traverse any order */ - add_file ((unsigned char *) dire->d_name, st->st_size, ++fname_ndx); - - if (debug) - fprintf (stderr, "added file %s len %ld\n", dire->d_name, st->st_size); - - break; - } - - if (dire == NULL) - return 0; - - return 1; -} - -int directory(unsigned char length, unsigned char *data) { - unsigned char search_form; - unsigned char *p,*dot; - struct stat st; - - bzero(buf,31); - buf[29]=40; - buf[0]=0x11; - buf[1]=0x1C; - search_form=data[length-1]; - switch (search_form) { - case 0x00: /* Pick file for open/delete */ - strncpy((char *)filename,(char *)data,24); - filename[24]=0; - //if (debug) fprintf (stderr, "Request: %s\n", filename); - /* Remove trailing spaces */ - for(p = (unsigned char *) strrchr((char *)filename,' '); p >= filename && *p == ' '; p--) - *p = 0; - /* Remove spaces between base and dot */ - dot = (unsigned char *) strchr((char *)filename,'.'); - if(dot!=NULL) { - for(p=dot-1;*p==' ';p--) ; - memmove(p+1,dot,strlen((char *)dot)+1); - } - if(dir!=NULL) - closedir(dir); - dir=opendir("."); - //if (debug) fprintf (stderr, " : %s\n", filename); - if (upcase) { - for(;read_next_dirent(&st) && dire!=NULL && strcasecmp((char *)dire->d_name, (char *)filename);); - } else { - for(;read_next_dirent(&st) && dire!=NULL && strcmp((char *)dire->d_name, (char *)filename);); - } - //if (debug) fprintf (stderr, "Found : %s\n", dire->d_name); - send_dirent(buf,&st); - break; - case 0x01: /* "first" directory block */ - if (debug) fprintf (stderr, "get first directory entry command\n"); - if(dir!=NULL) - closedir(dir); - dir=opendir("."); - /** rebuild the file list */ - file_list_clear_all(); - while (read_next_dirent (&st)); - /** send the file name */ - if (get_first_file (filename, &file_len, &cur_id) == 0) { - if (debug) fprintf (stderr, "get_first_file -> %s len = %d\n", filename, file_len); - out_dirent (filename, file_len); - } - else - out_dirent (NULL, 0); - break; - case 0x02: /* "next" directory block */ - if (get_next_file (filename, &file_len, &cur_id) == 0) - out_dirent (filename, file_len); - else - out_dirent (NULL, 0); - break; - case 0x03: /* "previous" directory block */ - if (get_prev_file (filename, &file_len, &cur_id) == 0) - out_dirent (filename, file_len); - else - out_dirent (NULL, 0); - break; - case 0x04: /* end directory reference */ - closedir(dir); - file_list_clear_all (); - fname_ndx = 0; - break; - } - return 0; -} - -int open_file(unsigned char omode) { - - //if(debug) fprintf (stderr, "open_file() mode:%d filename:%s dire->d_name:%s\n", omode,filename,dire->d_name); - - switch(omode) { - case 0x01: /* New file for my_write */ - if (file >= 0) { - close(file); - file=-1; - } - file = open ((char *) filename,O_CREAT|O_TRUNC|O_WRONLY|O_EXCL,0666); - if(file<0) - normal_return(0x37); - else { - mode=omode; - normal_return(0x00); - } - break; - case 0x02: /* existing file for append */ - if (file >= 0) { - close(file); - file=-1; - } - file = open ((char *) dire->d_name, O_WRONLY | O_APPEND); - if (file < 0) - normal_return (0x37); - else { - mode=omode; - normal_return (0x00); - } - break; - case 0x03: /* Existing file for read */ - if (file >= 0) { - close (file); - file=-1; - } - file = open ((char *)dire->d_name, O_RDONLY); - if(file<0) - normal_return(0x37); - else { - mode = omode; - normal_return (0x00); - } - break; - } - return (file); -} - -void read_file(void) { - int in; - - buf[0]=0x10; - if(file<0) { - normal_return(0x30); - return; - } - if(mode!=3) { - normal_return(0x37); - return; - } - in = read (file, buf+2, 128); - buf[1] = (unsigned char) in; - buf[2+in] = calc_sum(0x10, (unsigned char)in, buf+2); - my_write (client_fd, buf, 3+in); -} - -void respond_mystery() { - static unsigned char canned[] = {0x38, 0x01, 0x00}; - - memcpy (buf, canned, sizeof (canned)); - buf[sizeof(canned)] = calc_sum (canned[0], canned[1], canned + 2); - my_write (client_fd, buf, sizeof (canned) + 1); -} - -void respond_mystery2() { - static unsigned char canned[] = {0x14, 0x0F, 0x41, 0x10, 0x01, 0x00, 0x50, 0x05, 0x00, 0x02, 0x00, 0x28, 0x00, 0xE1, 0x00, 0x00, 0x00}; - - memcpy (buf, canned, sizeof (canned)); - buf[sizeof(canned)] = calc_sum (canned[0], canned[1], canned + 2); - my_write (client_fd, buf, sizeof (canned) + 1); -} - -// STUB -void respond_place_path() { - static unsigned char canned[] = {0x12, 0x0b, 0x00, 0x52, 0x4f, 0x4f, 0x54, 0x20, 0x20, 0x2e, 0x3c, 0x3e, 0x20, 0x96}; - - my_write (client_fd, canned, sizeof (canned)); -} - -void renamefile(unsigned char *name) { - unsigned char *p; - - name[24]=0; - for(p = (unsigned char *) strrchr((char *)name,' ');*p==' ';p--) - *p=0; - if(rename ((char *) filename, (char *)name)) - normal_return(0x4A); - else - normal_return(0x00); -} - -int readbytes(int handle, void *buf, int max) { - int r = 0; - int rval; - unsigned i; - - if (debug) - fprintf (stderr, "Read... "); - - while (r < max) { - rval = read (client_fd, buf + r, 1); - if (rval < 0) - continue; - r += rval; - } - - if (debug) { - for (i = 0; r >=0 && i < r; i++) - fprintf (stderr, "%02X ", ((unsigned char *)buf)[i]); - fprintf (stderr, "\n"); - } - - return (r); -} - -int send_installer(char *f) { - int w=0; - int i=0; - int fd; - int byte_usleep = bootstrap_byte_msec*1000; - unsigned char b; - - if((fd=open(f,O_RDONLY))<0) { - if(debug) - fprintf(stderr, "Failed to open %s for read.\n",f); - return(9); - } - - if(debug) { - fprintf(stderr, "Sending %s\n",f); - fflush(stdout); - } - - while(read(fd,&b,1)==1) { - while((i=my_write(client_fd,&b,1))!=1); - w+=i; - usleep(byte_usleep); - if(debug) - fprintf(stderr, "Sent: %d bytes\n",w); - else - fprintf(stderr, "."); - fflush(stdout); - } - fprintf(stderr, "\n"); - b = BASIC_EOF; - my_write(client_fd,&b,1); - close(fd); - close(client_fd); - - if(debug) { - fprintf(stderr, "Sent %s\n",f); - fflush(stdout); - } - else - fprintf(stderr, "\n"); - - return(0); -} - -void out_buf(unsigned char *bufp, unsigned len) { - unsigned i,j,k; - - if (!debug) - return; - - for (i = 0; i < len;) { - for (j = 0; j < 2; j++) { - for (k = 0; i < len && k < 8; k++, i++) - fprintf (stderr, "%02X ", bufp[i]); - fprintf (stderr, " "); - } - } - fprintf (stderr, "\n"); -} - -int be_disk(void) { - char preamble[3]; - unsigned char type; - unsigned char length; - unsigned char data[0x81]; - unsigned char checksum; - unsigned precnt = 0; - unsigned len; - char TPDD_P[3] = "ZZ"; - char TDME_P[3] = "M1"; - - preamble[0]=0; - preamble[1]=0; - preamble[2]=0; - - for (precnt = 0; precnt < 2; precnt++) { - len = readbytes(client_fd, preamble+precnt, 1); - if (len == 0) { - fprintf (stderr, "zero len - 1\n"); - return(-1); - } - if (preamble[precnt] == 0x0D) { - if (debug) fprintf (stderr, "CR\n"); - return(-1); - } - } - - if(strcmp(preamble,TPDD_P)==0) { // TPDD Command - if (debug) fprintf(stderr, "Got TPDD preamble.\n"); - } - else if(strcmp(preamble,TDME_P)==0) { // TS-DOS DME Request - if (debug) fprintf(stderr, "Got TS-DOS DME preamble.\n"); - return (-1); - } - else { - if (debug) fprintf(stderr, "Bad preamble: (%02X %02X)\n",preamble[0],preamble[1]); - return (-1); - } - - len = readbytes(client_fd,&type,1); - if (!len) { - fprintf (stderr, "zero len - 2\n"); - return (0); - } - - len = readbytes(client_fd,&length,1); - if (!len) { - fprintf (stderr, "zero len - 3\n"); - return (0); - } - - len = readbytes(client_fd,data,length); - if (length && !len) { - fprintf (stderr, "zero len - 4\n"); - return (0); - } - - data[length]=0; - len = readbytes(client_fd,&checksum,1); - if (!len) { - fprintf (stderr, "zero len - 5\n"); - return (0); - } - - if (debug) - fprintf (stderr, "\nProcessing command type = %02X length = %02X csum = %02X\n", type, length, checksum); - - if(calc_sum(type,length,data)!=checksum) { - if(debug) { - fprintf(stderr, "BAD CHECKSUM! Preamble: %s Type: %02X Length: %02X Data: %s\n",preamble,type,length,data); - fprintf(stderr, "Packet checksum: %02X My checksum: %02X\n",checksum,calc_sum(type,length,data)); - } - normal_return(0x36); - return(7); - } - - switch(type) { - case 0x00: /* Directory ref */ - directory(length,data); - break; - case 0x01: /* Open file */ - open_file(data[0]); - break; - case 0x02: /* Close file */ - if(file>=0) - close(file); - normal_return(0x00); - break; - case 0x03: /* Read */ - read_file(); - break; - case 0x04: /* Write */ - if(file<0) { - normal_return(0x30); - break; - } - if(mode!=1 && mode !=2) { - normal_return(0x37); - break; - } - if(my_write(file,data,length)!=length) - normal_return(0x4a); - else - normal_return(0x00); - break; - case 0x05: /* Delete */ - unlink ((char *) filename); - normal_return(0x00); - break; - case 0x06: /* Format disk */ - normal_return(0x00); - break; - case 0x07: /* Drive Status */ - normal_return(0x00); - break; - case 0x08: /* TS-DOS DME Request */ - respond_place_path(); - break; - case 0x0C: /* Condition */ - normal_return(0x00); - break; - case 0x0D: /* Rename File */ - renamefile(data); - break; - case 0x23: /* TS-DOS mystery command 2 */ - respond_mystery2(); - break; - case 0x31: /* TS-DOS mystery command 1 */ - respond_mystery(); - break; - default: - return(8); - break; - } - - return(0); -} diff --git a/main.c b/main.c new file mode 100644 index 0000000..e0df0e4 --- /dev/null +++ b/main.c @@ -0,0 +1,2934 @@ +/* + * DeskLink for *nix (dl) + * Copyright (C) 2004 + * Stephen Hurd + * + * Redistribution of modified and unmodified copies + * is premitted provided the copyright remains intact + */ + +/* +DeskLink+ +2005 John R. Hogerhuis Extensions and enhancements +2019 Brian K. White - repackaging, reorganizing, bootstrap function +2020 Kurt McCullum - TS-DOS loaders +2022 Gabriele Gorla - TS-DOS subdirectories + +DeskLink2 +2023 Brian K. White - disk image files, pdd1 FDC mode, pdd2 cache & memory + +DeskLink2 is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 or any +later version as published by the Free Software Foundation. + +DeskLink2 is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program (in the file "COPYING"); if not, write to the +Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +MA 02111, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) +#include +#elif defined(__APPLE__) || defined(__NetBSD__) || defined(OpenBSD) +#include +#elif defined(__FreeBSD__) +#include +#endif + +#include "constants.h" +#include "dir_list.h" +#include "xattr.h" + +/*** config **************************************************/ + +#ifndef APP_NAME +#define APP_NAME "DeskLink2" +#endif + +#ifndef APP_LIB_DIR +#define APP_LIB_DIR "." +#endif + +#ifndef TTY_PREFIX +#define TTY_PREFIX "ttyS" +#endif + +#ifndef DEFAULT_BAUD +#define DEFAULT_BAUD 19200 +#endif + +// default model emulation, 1=pdd1 2=pdd2 +// TS-DOS sub-directories requires tpdd1 +#ifndef DEFAULT_MODEL +#define DEFAULT_MODEL 1 +#endif + +// if a loader fails in bootstrap(), try increasing this +#ifndef DEFAULT_BASIC_BYTE_MS +#define DEFAULT_BASIC_BYTE_MS 8 +#endif + +#define DEFAULT_TPDD1_IMG_SUFFIX ".pdd1" +#define DEFAULT_TPDD2_IMG_SUFFIX ".pdd2" + +#ifndef DEFAULT_UPCASE +#define DEFAULT_UPCASE false +#endif + +#ifndef DEFAULT_RTSCTS +#define DEFAULT_RTSCTS false +#endif + +#ifndef DEFAULT_PROFILE +#define DEFAULT_PROFILE "k85" +#endif + +#ifndef DEFAULT_OPERATION_MODE +#define DEFAULT_OPERATION_MODE MODE_OPR +#endif + +#ifndef DEFAULT_TILDES +#define DEFAULT_TILDES true +#endif + + +// To mimic the original Desk-Link from Travelling Software: +#ifndef TSDOS_ROOT_LABEL +#define TSDOS_ROOT_LABEL "0: " +#endif +#ifndef TSDOS_PARENT_LABEL +#define TSDOS_PARENT_LABEL "^ " +#endif +// you can't change this unless you also hack ts-dos +#define TSDOS_DIR_LABEL "<>" + +/* + * "magic" files - See ref/ur2.txt + * + * Support for Ultimate ROM II, TSLOAD, & any other on-the-fly loaders. + * These filenames will always be loadable "by magic" in any cd path, even + * if no such filename exists anywhere in the share tree. + * + * Whenever a client tries to request any of these filenames, + * after searching cwd-within-share-path as normal, then search share root, finally app_lib_dir. + * They will always be found in app_lib_dir if nowhere else. + * TODO add $XDG_DATA_HOME (~/.local/share/myapp mac: ~/Library/myapp/) + * + * You may add any other files you want here if you find any other software + * that tries to load-use-discard a file from disk like UR2 uses DOS100.CO. + * + * Files must also be added to install target in Makefile. + * + * This list is checked for a match every time a requested filename is not found, + * so keep it short. + * + * TODO add run-time config list of filenames and search paths + */ +const char * magic_files[] = { + "DOS100.CO", + "DOS200.CO", + "DOSNEC.CO", + "SAR100.CO", + "SAR200.CO", + // The rest of these files don't exist, but we are ready to serve them up if they did exist. + // Some are known to have existed, but no known copies available currently. + // Some may not have ever existed. Most filenames are guesses. + "SARNEC.CO", // Sardine for NEC is known to have existed, with this filename. + "DOSM10.CO", // or DOSOLV.CO ? Jeff Birt found TS-DOS for Olivetti M-10 listed in a catalog. + "DOSK85.CO", // or DOSKYO.CO ? may have never existed + "SARM10.CO", // or SAROLV.CO ? Since TS-DOS for M-10 existed, probably Sardine existed too. + "SARK85.CO" // or SRAKYO.CO ? may have never existed +}; + +// client compatibility profiles +// +// KC-85 +// The platform can use lowercase filenames just fine, but at least both +// TS-DOS and TEENY convert to uppercase in places, so upcase to avoid the battle. +// +// CP/M +// https://www.shaels.net/index.php/cpm80-22-documents/using-cpm/3-file-names +// "The CPM CPP module converts commands into upper case before they are executed +// which leads many to believe that the CPM file system is not case sensitive, +// when in fact the CPM file system is case sensitive. If you use a CPM program +// such as Microsoft Basic you can create file names which contain lower case +// characters. The problem is files which contain lower case characters can not +// be specified as parameters at the CPP command prompt, as the characters will +// be converted to upper case by the CPP before the command is executed." +// So upcase to avoid the battle... +// +// REXCPM native is CP/M, but import & export are limited further to 6.2 upcase. +// +// Cambridge Z88 native is 12.3, not sure what DISCMNGR or DISC_RBL actually does. +// +// Atari ST native is CP/M, later MS-DOS, but PDDOS limits to 6.2 +// +// MS-DOS (Atari Portfolio) by rights would be this: +// { "msdos", 8, 3, false, ATTR_RAW, false, false, false }, +// except most of the pdd software was only made to work with Floppy/TS-DOS, +// disks so even with an ms-dos client you usually want to use k85 or cpm +// +// Probably no xenix client exists until I port one, but it would be this: +// { "xenix", 14, 0, false, ATTR_RAW, false, false, false } +// +// id, base, ext, pad, attr, dme, magic, upcase +#define CLIENT_PROFILES { \ + { "raw", 0, 0, false, ATTR_RAW, false, false, false }, \ + { "k85", 6, 2, true, ATTR_DEF, true, true, true }, \ + { "wp2", 8, 2, true, ATTR_DEF, false, false, false }, \ + { "cpm", 8, 3, false, ATTR_DEF, false, false, true }, \ + { "rexcpm", 6, 2, true, ATTR_DEF, false, false, true }, \ + { "z88", 12, 3, false, ATTR_DEF, false, false, false }, \ + { "st", 6, 2, true, ATTR_DEF, false, false, true } \ +} + +// terminal emulation +#define SSO "\033[7m" // set standout +#define RSO "\033[m" // reset standout +#define D8C "\033 F" // disable 8-bit vt control bytes (0x80-0x9F) + +// The TPDD1 rom is actually the FB-100 rom. +// The roms in Brother FB-100, knitking FDD19, Purple Computing D103, and +// TANDY 26-3808 (TPDD1) have all been dumped and compared, and are all identical. +// That means the rom came from Brother and is the FB-100 rom in all cases. +// We have this file but it's not used currently. +//#ifndef FB100_ROM +//#define FB100_ROM "Brother_FB-100.rom" +//#endif + +// The TPDD2 rom is used because the normal TPDD2 memory access functions +// can read the rom contents the same as any other memory address. +#ifndef TPDD2_ROM +#define TPDD2_ROM "TANDY_26-3814.rom" +#endif + +// termios VMIN & VTIME +#define C_CC_VMIN 1 +#define C_CC_VTIME 5 + +/*************************************************************/ + +int debug = 0; +int operation_mode = DEFAULT_OPERATION_MODE; +bool upcase = DEFAULT_UPCASE; +bool rtscts = DEFAULT_RTSCTS; +bool tildes = DEFAULT_TILDES; +uint8_t model = DEFAULT_MODEL; +uint16_t baud = DEFAULT_BAUD; +int BASIC_byte_us = DEFAULT_BASIC_BYTE_MS*1000; + +char client_tty_name[PATH_MAX+1] = {0x00}; +char disk_img_fname[PATH_MAX+1] = {0x00}; +char app_lib_dir[PATH_MAX+1] = APP_LIB_DIR; +char share_path[2][PATH_MAX+1] = {{0},{0}}; +char dme_root_label[7] = TSDOS_ROOT_LABEL; +char dme_parent_label[7] = TSDOS_PARENT_LABEL; +char dme_dir_label[3] = TSDOS_DIR_LABEL; +uint8_t cfnl = TPDD_FILENAME_LEN; + +#if !defined(_WIN) +bool getty_mode = false; +#endif + +char** args; + +int f_open_mode = F_OPEN_NONE; +int client_tty_fd = -1; +int disk_img_fd = -1; +struct termios client_termios; +int o_file_h = -1; +uint8_t gb[TPDD_MSG_MAX]; +char iwd[PATH_MAX+1] = {0x00}; +char cwd[PATH_MAX+1] = {0x00}; +char dme_cwd[7] = TSDOS_ROOT_LABEL; +char bootstrap_fname[PATH_MAX+1] = {0x00}; +uint8_t in_dme = 0; +uint8_t bank = 0; +uint8_t ch[2] = {0xFF}; // 0x00 is a valid Operation-mode command, so init to 0xFF +uint8_t rb[SECTOR_LEN] = {0x00}; // pdd1 disk image record buffer +FILE_ENTRY* cur_file; +int dir_depth=0; +uint8_t pdd1_condition = PDD1_COND_NONE; // pdd1 condition bit flags +uint8_t pdd2_condition = PDD2_COND_NONE; // pdd2 condition bit flags + +// drive cpu memory map +uint8_t ioport[IOPORT_LEN] = {0x00}; // i/o port +uint8_t cpuram[CPURAM_LEN] = {0x00}; // 128 bytes cpu internal ram +uint8_t ga[GA_LEN] = {0x00}; // gate array interface +uint8_t ram[RAM_LEN] = {0x00}; // 2k ram (pdd2 disk image record buffer) +uint8_t rom[ROM_LEN] = {0x00}; // 4k cpu internal mask rom + +// client compatibility settings +#define PROFILE_ID_LEN 8 +typedef struct { + char id[PROFILE_ID_LEN+1]; + uint8_t base; + uint8_t ext; + bool pad; + uint8_t attr; + bool dme; + bool magic; + bool upcase; +} CLIENT_PROFILE; +const CLIENT_PROFILE profiles [] = CLIENT_PROFILES ; +//const char* profile = profiles[0].id; +char profile[PROFILE_ID_LEN+1] = {0}; +uint8_t base_len = 0; +uint8_t ext_len = 0; +char default_attr = ATTR_RAW; +bool enable_magic_files = false; +bool pad_fn = false; +bool dme_en = false; + +/////////////////////////////////////////////////////////////////////////////// + +void show_main_help(); + +/* primitives and utilities */ + +// dbg(verbosity_threshold, printf_format, args...) +// dbg(3,"err %02X",err); // means only show this message if debug>=3 +void dbg( const int v, const char* format, ... ) { + if (debug t=",o" -> matches ",on," + t[3]=','; + return strstr(",on,1 ,t ,y ,tr,ye,en,",t); +} + +// int-to-rate - given int 9600 return macro B9600 +speed_t itobaud (uint32_t i) { + return + i==0?B0: + i==50?B50: + i==75?B75: + i==110?B110: + i==134?B134: + i==150?B150: + i==200?B200: + i==300?B300: + i==600?B600: + i==1200?B1200: + i==1800?B1800: + i==2400?B2400: + i==4800?B4800: + i==9600?B9600: + i==19200?B19200: + i==38400?B38400: +#ifdef B57600 + i==57600?B57600: +#endif +#ifdef B76800 + i==76800?B76800: +#endif +#ifdef B115200 + i==115200?B115200: +#endif +#ifdef B153600 + i==153600?B153600: +#endif +#ifdef B230400 + i==230400?B230400: +#endif +#ifdef B307200 + i==307200?B307200: +#endif +#ifdef B460800 + i==460800?B460800: +#endif +#ifdef B500000 + i==500000?B500000: +#endif +#ifdef B576000 + i==576000?B576000: +#endif +#ifdef B614400 + i==614400?B614400: +#endif +#ifdef B921600 + i==921600?B921600: +#endif +#ifdef B1000000 + i==1000000?B1000000: +#endif +#ifdef B1152000 + i==1152000?B1152000: +#endif +#ifdef B1500000 + i==1500000?B1500000: +#endif +#ifdef B2000000 + i==2000000?B2000000: +#endif +#ifdef B2500000 + i==2500000?B2500000: +#endif +#ifdef B3000000 + i==3000000?B3000000: +#endif +#ifdef B3500000 + i==3500000?B3500000: +#endif +#ifdef B4000000 + i==4000000?B4000000: +#endif + 0; +} + +// given int 19200 return 9 (the # in "COM:#8N1ENN") +uint8_t baud_to_stat_code (uint16_t r) { + return + r==75?1: + r==110?2: + r==300?3: + r==600?4: + r==1200?5: + r==2400?6: + r==4800?7: + r==9600?8: + r==19200?9: + 0; +} + +void lsx (char* path,char* match,char* fmt) { + struct dirent *files; + DIR *dir = opendir(path); + if (!dir){dbg(0,"Cannot open \"%s\"",path); return;} + int i; + while ((files = readdir(dir))) { + for (i=strlen(files->d_name);files->d_name[i]!='.';i--); + if (!strcmp(files->d_name+i+1,match)) dbg(0,fmt,files->d_name); + } + closedir(dir); +} + +void show_profiles_help (int e) { + const int n = sizeof(profiles)/sizeof(profiles[0]); + + dbg(0, + "\n" + "Help for Client Compatibility Profiles\n" + "\n" + "Usage:\n" + " -c name use profile - (default: \"%1$s\")\n" + " -c #.# \"raw\", truncated but not padded to #.#, attr='%2$c'\n" + " -c #.#p \"raw\", truncated and padded to #.#, attr='%2$c'\n" + " -v -c more help about profiles\n" + ,DEFAULT_PROFILE,ATTR_DEF + ); + + dbg(1, + "\n" + "Profiles taylor the translation between local filenames and TPDD filenames.\n" + "\n" + "A real TPDD doesn't care what's in the filename, and emulating a TPDD\n" + "doesn't require any translation other than truncating to 24 bytes.\n" + "\n" + "But most TPDD clients write filenames to TPDD drives in specific formats,\n" + "and we need to translate filenames between the local and client formats.\n" + "\n" + "Strictly speaking, \"raw\" always works for any and all clients,\n" + "from the clients point of view. It still emulates a real drive exactly.\n" + "\n" + "The only reason for any compatibility profile is for more convenient\n" + "local filenames. When TS-DOS saves a file like \"A.BA\", it actually\n" + "writes \"A .BA\" to a real drive. In \"raw\" mode this would create a\n" + "local file named verbatim: \"A .BA\", which is legal but inconvenient.\n" + "And TS-DOS does not recognize any disk files that don't conform\n" + "to the \"k85\" profile below. (fixed-length, space-padded, 6.2)\n" + "\n" + "\"raw\" still \"works\" because TS-DOS can both create any files it\n" + "wants, and access any files it created, identical to a real drive.\n" + "\n" + "Profiles just make it so that a local file named \"my_long_file_name.text\"\n" + "appears to TS-DOS as \"my_lo~.t~\", which may be ugly but TS-DOS can use it.\n" + "And when TS-DOS tries to read or write a file named \"FOO .CO\",\n" + "we use \"FOO.CO\" for the local filename.\n" + "\n" + "Most of the parameters in a profile also have individual commandline flags,\n" + "and all parameters have individual environment variables.\n" + "Example: \"dl -c k85\" is short for \"dl -c 6.2p -a F -e on\"\n" + "or: \"PROFILE=6.2p ATTR=F DME=on TSLOAD=on UPCASE=on dl\"\n" + "(except k85 is the default so you don't need to use any of those)\n" + "\n" + "The default \"k85\" matches all KC-85-clone platform clients. Examples:\n" + "Floppy, TS-DOS, DSKMGR, TEENY, etc, on TRS-80 Model 100, NEC PC-8201a, etc.\n" + "\n" + "NAME profile name\n" + "BASE basename length\n" + "EXT extension length\n" + "PAD fixed-length space-padded\n" + "ATTR default attribute byte if no xattr\n" + "DME enable TS-DOS directory mode extension\n" + "TSLOAD enable \"magic files\" (ex: DOS100.CO) for TSLOAD / Ultimate ROM II\n" + "UPCASE translate filenames to all uppercase\n" + ); + + dbg(0, + "\n" + "Available profiles:\n" + "\n" +// "PROFILE\tBASE\tEXT\tPAD\tATTR\tTS-DOS\tMAGIC\tUP\n" +// "NAME\tLEN\tLEN\tFNAMES\tBYTE\tDIRS\tFILES\tCASE\n" + "NAME\tBASE\tEXT\tPAD\tATTR\tDME\tTSLOAD\tUPCASE\n" + "-------------------------------------------------------------\n" + ); + + for (int i=0; i\n" + " -v -i more help about disk images\n" + "\n" + ); + dbg(1, + "If filename is not found, then %1$s is searched.\n" + "\n" + "If the filename ends in \".pdd1\", or the file is the correct exact\n" + "size of a TPDD1 disk image, then dl2 will automatically operate in\n" + "TPDD1 emulation mode, and the same for \".pdd2\" and TPDD2.\n" + "\n" + "If the drive model cannot be determined by either name or size\n" + "(such as a new empty file with an arbitrary name that you want created),\n" + "then use \"-m 1\" or \"-m 2\" to specify tpdd1 or tpdd2.\n" + "\n" + "If filename does not exist, or exists but is zero bytes, then the file\n" + "will be created and filled with a new blank formatted disk image,\n" + "if and when the client issues a format command.\n" + "\n" + "Disk images may be dumped from / restored to physical disks using\n" + "the appropriate model real drive and https://github.com/bkw777/pdd.sh\n" + "\n" + ,app_lib_dir + ); + dbg(0, + "Available built-in (bundled) disk image files (in %1$s):\n" + "\n" + ,app_lib_dir + ); + + dbg(0,"TPDD1:\n"); + lsx(app_lib_dir,"pdd1"," %s\n"); + dbg(0,"TPDD2:\n"); + lsx(app_lib_dir,"pdd2"," %s\n"); + + dbg(0, + "\n" + "Examples:\n" + " %1$s -v -i Sardine_American_English.pdd1\n" + " %1$s -v -i ./my_new_disk.pdd2\n" + "\n" + ,args[0]); + + exit(e); +} + +bool ckhelp (const char* s) { + return ( + !s[0] || + !strncasecmp(s,"list",PROFILE_ID_LEN) || + !strncasecmp(s,"help",PROFILE_ID_LEN) || + !strncasecmp(s,"?",PROFILE_ID_LEN) + ); +} + +// set base_len, ext_len, pad_fn from ##.##p +void set_fnames (const char* s) { + + if (ckhelp(s)) show_profiles_help(0); + + int i, p; + char t[4] = {0}; + + p = strchr(s,'.')-s; + if (p<1 || p>2) show_profiles_help(1); + + for (i=sizeof(s);i>p;i--) { + if (s[i]=='p'||s[i]=='P') pad_fn = true; + if (s[i]>='0' && s[i]<='9') break; + } + + memcpy(t,s,p); + i = atoi(t); + if (i>0 && i4) i = 4; + memcpy(t,s+p+1,i); + i = atoi(t); + if (i>-1 && i0) { + // if file exists and >0 bytes + dbg(1,"Loading disk image file \"%s\"\n",t); + + // use file size to automatically set model 1 vs 2 or reject file + if (info.st_size==PDD1_IMG_LEN) model = 1; + if (info.st_size==PDD2_IMG_LEN) model = 2; + + // user may have explicitly used -m 1 or -m 2 + if (model==1 && info.st_size != PDD1_IMG_LEN) { + dbg(0,"%d bytes, expected %u bytes for TPDD1\n",info.st_size,PDD1_IMG_LEN); + return 1; + } + if (model==2 && info.st_size != PDD2_IMG_LEN) { + dbg(0,"%d bytes, expected %u bytes for TPDD2\n",info.st_size,PDD2_IMG_LEN); + return 1; + } + + } else { + // if file doesn't exist or is 0 bytes, create it + + dbg(1,"Disk image file \"%s\" is empty or does not exist.\nIt will be created if the client issues a format command.\n",t); + + // use file name to automatically set model 1 vs 2 + // overrides -m if -i came after -m + // ext = dot pddN null + char ext[6] = {0}; + strcpy(ext,t+strlen(t)-5); + if (!strcasecmp(ext,DEFAULT_TPDD1_IMG_SUFFIX)) model = 1; + else if (!strcasecmp(ext,DEFAULT_TPDD2_IMG_SUFFIX)) model = 2; + } + + memset(disk_img_fname,0,PATH_MAX+1); + + // rewrite with leading path if not already + // because we we may cd all over the place + if (t[0]!='/') { + strcpy(disk_img_fname,iwd); + strcat(disk_img_fname,"/"); + } + strcat(disk_img_fname,t); + + return 0; +} + +// search for TTY(s) matching TTY_PREFIX +void find_ttys (char* f) { + dbg(3,"%s(%s)\n",__func__,f); + + // open /dev + char path[] = "/dev/"; + DIR* dir = opendir(path); + if (!dir){dbg(0,"Cannot open \"%s\"\n",path); return;} + + // read /dev, look for all files beginning with prefix + // add any matches to ttys[] + char** ttys = malloc(sizeof(char*)); + struct dirent *files; + uint16_t nttys = 0, l=strlen(f); +#if defined(__FreeBSD__) + char* p; +#endif + + dbg(2,"Searching for \"%s%s*\"\n",path,f); + while ((files = readdir(dir))) { + if (strncmp(files->d_name,f,l)) continue; +#if defined(__FreeBSD__) + p = strrchr(files->d_name,'.'); + if (p!=NULL) if (!strcmp(p,".init") || !strcmp(p,".lock")) continue; +#endif + nttys++; + ttys = realloc(ttys, (nttys+1) * sizeof(char(*))); + ttys[nttys] = files->d_name; + } + + closedir(dir); + + int i=0; + if (nttys==1) i=1; // if there is only one element in ttys[], use it + if (nttys>1) while (!i) { // if more than 1 then menu + dbg(0,"\n"); + for (i=1;i<=nttys;i++) dbg(0,"%d) %s\n",i,ttys[i]); + i=0; char a[6]={0}; + dbg(0,"Which serial port is the TPDD client on (1-%d or q) ? ",nttys); + if (fgets(a,sizeof(a),stdin)) i=atoi(a); + if (i<1 || i>nttys) i=0; + dbg(0,"\n"); + if (a[0]=='q'||a[0]=='Q') break; + } + + // set client_tty_name[] with the final result + client_tty_name[0]=0x00; + if (i) { + strcpy(client_tty_name,path); + strcat(client_tty_name,ttys[i]); + } + + free(ttys); +} + +// take the user-supplied tty arg and figure out the actual /dev/ttyfoo +void resolve_client_tty_name () { + dbg(3,"%s()\n",__func__); + switch (client_tty_name[0]) { + case 0x00: + // nothing supplied, scan for any ttys matching the default prefix + find_ttys(TTY_PREFIX); + break; + case '-': + // stdin/stdout mode, silence all messages - untested + debug = -1; + strcpy (client_tty_name,"/dev/tty"); + client_tty_fd=1; + break; + default: + // something given, try with and without prepending /dev/ + if (!access(client_tty_name,F_OK)) break; + char t[PATH_MAX+1]={0x00}; + int i = 0; + strcpy(t,client_tty_name); + strcpy(client_tty_name,"/dev/"); + if (!strncmp(client_tty_name,t,5)) i=5; + strcat(client_tty_name,t+i); + } +} + +// set termios VMIN & VTIME +void client_tty_vmt(int m,int t) { + if (m<-1 || t<-1) tcgetattr(client_tty_fd,&client_termios); + if (m<0) m = C_CC_VMIN; + if (t<0) t = C_CC_VTIME; + if (client_termios.c_cc[VMIN] == m && client_termios.c_cc[VTIME] == t) return; + client_termios.c_cc[VMIN] = m; + client_termios.c_cc[VTIME] = t; + tcsetattr(client_tty_fd,TCSANOW,&client_termios); +} + +int open_client_tty () { + dbg(3,"%s()\n",__func__); + + if (!client_tty_name[0]) { + show_main_help(); + dbg(0,"Error: No serial device specified\n(searched: /dev/%s*)\n",TTY_PREFIX); + return 1; + } + + dbg(0,"Opening \"%s\" ... ",client_tty_name); + // open with O_NONBLOCK to avoid hang if client not ready, then unset later. + if (client_tty_fd<0) client_tty_fd=open((char *)client_tty_name,O_RDWR|O_NOCTTY|O_NONBLOCK); + if (client_tty_fd<0) { dbg(0,"%s\n",strerror(errno)); return 1; } + dbg(0,"OK\n"); + +#ifdef TIOCEXCL + ioctl(client_tty_fd,TIOCEXCL); +#endif + +#if !defined(_WIN) + if (getty_mode) { + debug = -1; + if (!login_tty(client_tty_fd)) client_tty_fd = STDIN_FILENO; + else (void)!daemon(1,1); + } +#endif + + (void)!tcflush(client_tty_fd, TCIOFLUSH); + + // unset O_NONBLOCK + fcntl(client_tty_fd, F_SETFL, fcntl(client_tty_fd, F_GETFL, NULL) & ~O_NONBLOCK); + + if (tcgetattr(client_tty_fd,&client_termios)==-1) return 21; + + cfmakeraw(&client_termios); + client_termios.c_cflag |= CLOCAL|CS8; + + if (rtscts) client_termios.c_cflag |= CRTSCTS; + else client_termios.c_cflag &= ~CRTSCTS; + + if (cfsetspeed(&client_termios,itobaud(baud))==-1) return 22; + + if (tcsetattr(client_tty_fd,TCSANOW,&client_termios)==-1) return 23; + + client_tty_vmt(-2,-2); + + return 0; +} + +int write_client_tty(void* b, int n) { + dbg(4,"%s(%u)\n",__func__,n); + n = write(client_tty_fd,b,n); + dbg(3,"SENT: "); dbg_b(3,b,n); + return n; +} + +// It is correct that this blocks and waits forever. +// The one time we don't want to block, we don't use this. +int read_client_tty(void* b, const unsigned int n) { + dbg(4,"%s(%u)\n",__func__,n); + unsigned t = 0; + int i = 0; + while (t0) dbg(0,"%s",b); + close(h); +} + +/* + * The manual says: + * + * "The checksum is the one's complement of the least significant byte + * of the number of bytes from the block format through the data block." + * + * But the bytes are summed, not just counted! + * Replace "number of" with "sum of the". + * + * Sum all the bytes in the specified range. + * Take the least significant byte of that sum. + * Invert all the bits in that byte. + * + * b[0] = cmd (block format) + * b[1] = len + * b[2] to b[1+len] = 0 to 128 bytes of payload (data block) + * ignore everything after b[1+len] + */ +uint8_t checksum(unsigned char* b) { + uint16_t s=0; uint8_t i, l=2+b[1]; + for (i=0;i1;i--) if (fname[i-1]!=' ') break; + + if (fname[base_len+1]==dme_dir_label[0] && fname[base_len+2]==dme_dir_label[1]) { + fname[i]=0x00; + } else { + fname[i]=fname[base_len]; + fname[i+1]=fname[base_len+1]; + fname[i+2]=fname[base_len+2]; + fname[i+3]=0x00; + } + return fname; +} + +int check_magic_file(char* b) { + dbg(3,"%s(\"%s\")\n",__func__,b); + if (!enable_magic_files) return 1; + int l = sizeof(magic_files)/sizeof(magic_files[0]); + for (int i=0;i ascii hex pair +// s = status or data -> ascii hex pair +// l = length or address -> 2 ascii hex pairs +// TODO - don't assume endianness +void ret_fdc_std(uint8_t e, uint8_t s, uint16_t l) { + dbg(2,"%s()\n",__func__); + char b[9] = { 0x00 }; + snprintf(b,9,"%02X%02X%04X",e,s,l); + dbg(2,"FDC: response: \"%s\"\n",b); + write_client_tty(b,8); +} + +// p : physical sector to seek to +// m : mode read-only / write-only / read-write +int open_disk_image (int p, int m) { + dbg(2,"%s(%d,%d)\n",__func__,p,m); + int of; int e=ERR_FDC_SUCCESS; + + if (!*disk_img_fname) e=ERR_FDC_NO_DISK; + + if (!e) switch (m) { + case O_RDWR: of=O_RDWR; dbg(2,"edit rw\n"); + if (access(disk_img_fname,W_OK)) e=ERR_FDC_WRITE_PROTECT; + break; + case O_WRONLY: of=O_WRONLY; + if (access(disk_img_fname,F_OK)) { of|=O_CREAT; dbg(2,"create\n");} else { + dbg(2,"edit wo\n"); + if (access(disk_img_fname,W_OK)) e=ERR_FDC_WRITE_PROTECT; + } + break; + default: of=O_RDONLY; dbg(2,"read\n"); break; + } + + if (!e) { + disk_img_fd=open(disk_img_fname,of|O_EXCL,0666); + if (disk_img_fd<0) { dbg(0,"%s\n",strerror(errno)) ;e=ERR_FDC_READ;} + } + + if (!e) { + int s = (p*SECTOR_LEN); // initial seek position to start of physical sector + if (lseek(disk_img_fd,s,SEEK_SET)!=s) e=ERR_FDC_READ; + } + + if (operation_mode) switch (e) { + //case ERR_FDC_SUCCESS: e=ERR_SUCCESS; break; // same + case ERR_FDC_NO_DISK: e=ERR_NO_DISK; break; + case ERR_FDC_WRITE_PROTECT: e=ERR_WRITE_PROTECT; break; + case ERR_FDC_READ: e=ERR_READ_TIMEOUT; break; + } + + return e; +} + +void req_fdc_set_mode(int m) { + dbg(2,"%s(%d)\n",__func__,m); + operation_mode = m; // no response, just switch modes + if (m==MODE_OPR) dbg(2,"Switched to \"Operation\" mode\n"); +} + +// disk state +// ret_fdc_std(e,s,l) +// e = ERR_FDC_SUCCESS +// s = +// bit 7 = disk not inserted +// bit 6 = disk changed +// bit 5 = disk write-protected +// 0-4 not used +// l = 0 +void req_fdc_condition() { + dbg(2,"%s()\n",__func__); + ret_fdc_std(ERR_FDC_SUCCESS,pdd1_condition,0); +} + +// lc = logical sector size code +void req_fdc_format(uint8_t lc) { + dbg(2,"%s(%d)\n",__func__,lc); + uint16_t ll = FDC_LOGICAL_SECTOR_SIZE[lc]; + uint8_t rn = 0; // physical sector number + uint8_t rc = (PDD1_TRACKS*PDD1_SECTORS); // total record count + + dbg(0,"Format: Logical sector size: %d = %d\n",lc,ll); + + uint8_t e = open_disk_image(0,O_RDWR); + if (e) { ret_fdc_std(e,0,0); return; } + + memset(rb,0x00,SECTOR_LEN); + rb[0]=lc; // logical sector size code + for (rn=0;rnSECTOR_DATA_LEN) { + close(disk_img_fd); + ret_fdc_std(ERR_FDC_LSN_HI,tp,l); + return; + } + + // seek to target_physical*(id_len+physical_len) + id_len + (target_logical-1)*logical_len + int s = (tp*SECTOR_LEN)+SECTOR_HEADER_LEN+((tl-1)*l); + if (lseek(disk_img_fd,s,SEEK_SET)!=s) { + dbg(1,"failed seek %d : %s\n",s,strerror(errno)); + close(disk_img_fd); + ret_fdc_std(ERR_FDC_READ,tp,0); + return; + } + memset(rb,0x00,l); + if (read(disk_img_fd,rb,l)!=l) { // read one logical sector of DATA + dbg(1,"failed logical sector read\n"); + close(disk_img_fd); + ret_fdc_std(ERR_FDC_READ,tp,0); + return; + } + close(disk_img_fd); + ret_fdc_std(ERR_FDC_SUCCESS,tp,l); // 1st stage response + char t=0x00; + read_client_tty(&t,1); // read 1 byte from client + if (t==FDC_CMD_EOL) write_client_tty(rb,l); // if 0D send data else silently abort +} + +// ref/search_id_section.txt +void req_fdc_search_id() { + dbg(2,"%s()\n",__func__); + int rn = 0; // physical sector number + int rc = (PDD1_TRACKS*PDD1_SECTORS); // total record count + char sb[SECTOR_ID_LEN] = {0x00}; // search data + + uint8_t e = open_disk_image(0,O_RDONLY); + if (e) { ret_fdc_std(e,0,0); return; } + + ret_fdc_std(ERR_FDC_SUCCESS,0,0); // tell client to send data + read_client_tty(sb,SECTOR_ID_LEN); // read 12 bytes from client + + uint16_t l = 0; + bool found = false; + for (rn=0;rn + // where: + // P = physical sector number 0-79 (decimal integer as 0-2 ascii characters) + // L = logical sector number 1-20 (decimal integer as 0-2 ascii characters) + // (P & L sometimes have other meanings but the format & type rule still holds) + p=0; // real drive uses physical sector 0 when omitted + l=1; // real drive uses logical sector 1 when omitted + char* t; + if ((t=strtok((char*)gb,","))!=NULL) p=atoi(t); // target physical sector number + if ((t=strtok(NULL,","))!=NULL) l=atoi(t); // target logical sector number + // for physical sector out of range, real drive error response will have dat=last_valid_p if any + // if no command has ever supplied a valid physical sector number yet, then dat=FF + if (p<0) {ret_fdc_std(ERR_FDC_PARAM,0xFF,0); return;} + if (p>79) {ret_fdc_std(ERR_FDC_PSN_HI,0xFF,0); return;} + if (l<1) {ret_fdc_std(ERR_FDC_LSN_LO,p,0); return;} + if (l>20) {ret_fdc_std(ERR_FDC_LSN_HI,p,0); return;} + + // debug + dbg(3,"command:%c physical:%d logical:%d\n",c,p,l); + + // dispatch + switch (c) { + case FDC_SET_MODE: req_fdc_set_mode(p); break; + case FDC_CONDITION: req_fdc_condition(); break; + case FDC_FORMAT_NV: + case FDC_FORMAT: req_fdc_format(p); break; + case FDC_READ_ID: req_fdc_read_id(p); break; + case FDC_READ_SECTOR: req_fdc_read_sector(p,l); break; + case FDC_SEARCH_ID: req_fdc_search_id(); break; + case FDC_WRITE_ID_NV: + case FDC_WRITE_ID: req_fdc_write_id(p); break; + case FDC_WRITE_SECTOR_NV: + case FDC_WRITE_SECTOR: req_fdc_write_sector(p,l); break; + default: dbg(2,"FDC: invalid cmd \"%s\"\n",gb); + ret_fdc_std(ERR_FDC_COMMAND,0,0); // required for model detection + } +} + +//////////////////////////////////////////////////////////////////////// +// +// OPERATION MODE +// + +FILE_ENTRY* make_file_entry(char* namep, uint8_t attr, uint16_t len, char flags) { + dbg(3,"%s(\"%s\")\n",__func__,namep); + static FILE_ENTRY f; + strncpy(f.local_fname, namep, LOCAL_FILENAME_MAX); + memset(f.client_fname, 0x00, TPDD_FILENAME_LEN+1); + f.attr = attr; + f.len = len; + f.flags = flags; + + // input length + uint8_t il = strlen(namep); + + // find the last dot but not if it's a directory + uint8_t dp = 0; + if (!f.flags&FE_FLAGS_DIR && strrchr(namep,'.')) dp = strrchr(namep,'.')-namep; + + // output length + uint8_t ol = base_len?(base_len+(ext_len?(1+ext_len):0)):TPDD_FILENAME_LEN; + + if (!ext_len) { + // ignore dots + + snprintf(f.client_fname,TPDD_FILENAME_LEN+1,"%-*.*s",ol,ol,namep); + if (tildes && il>ol) f.client_fname[ol-1]='~'; + + } else { + // handle dots + + // base + char bn[TPDD_FILENAME_LEN+1] = {0}; + // might be shorter than base_len + uint8_t bl = (dp&&dpbl:il>ol || + (f.flags&FE_FLAGS_DIR && il > ol-ext_len-1) + ) bn[bl-1]='~'; + + // ext + char en[TPDD_FILENAME_LEN+1] = {0}; + uint8_t x = il-dp-1; + uint8_t el = dp? xel) en[el-1]='~'; + + // TS-DOS directories + if (dme_en && flags&FE_FLAGS_DIR) { + if (!strcmp(f.local_fname,"..")) memcpy(bn,dme_parent_label,base_len); + memcpy(en,dme_dir_label,ext_len+1); + el = ext_len; + f.len = 0; + } + + // output + // base + if (pad_fn) snprintf(f.client_fname,cfnl,"%-*.*s",base_len,base_len,bn); + else snprintf(f.client_fname,cfnl,"%s",bn); + // dot + if (dp||pad_fn) strncat(f.client_fname,".",1); + // ext + strncat(f.client_fname,en,el); + + // upcase + if (upcase) for(int i=0;id_name,&st)) { + if (m) ret_std(ERR_NO_FILE); + return 0; + } + + if (S_ISDIR(st.st_mode)) flags=FE_FLAGS_DIR; + else if (!S_ISREG (st.st_mode)) continue; + + if (flags==FE_FLAGS_DIR && in_dme<2) continue; + + if (base_len) { + if (dire->d_name[0]=='.') continue; // skip "." ".." and hidden files + if (strlen(dire->d_name)>LOCAL_FILENAME_MAX) continue; // skip long filenames + } + + // TODO - make this configurable + // If filesize is too large for the tpdd 16 bit size field, then say + // size=0 but allow the file to be accessed. + // A real drive does NOT do this, but REXCPM cpmupd.CO + // violates the tpdd protocol to load a large CP/M disk image. + if (st.st_size>UINT16_MAX) st.st_size=0; + + uint8_t attr = default_attr; + dl_getxattr(dire->d_name, &attr); + add_file(make_file_entry(dire->d_name, attr, st.st_size, flags)); + break; + } + + if (dire == NULL) return 0; + + return 1; +} + +// read the current share directory +void update_file_list(int m) { + dbg(3,"%s()\n",__func__); + DIR* dir; + + if (model==2) cd_share_path(); + dir = opendir("."); + file_list_clear_all(); + + //int w = base_len+1+ext_len; + //if (base_len<1||w>TPDD_FILENAME_LEN) w = TPDD_FILENAME_LEN; + dbg(1,"\nDirectory %s: %s\n",model==2?bank==1?"[Bank 1]":"[Bank 0]":"",cwd); + /* match format with end of make_file_entry() */ + dbg(1,"\"%-*s\" |a| local filename\n",cfnl,"tpdd view"); + dbg(1,"-------------------------------------------------------------------------------\n"); + if (dir_depth) add_file(make_file_entry("..", default_attr, 0, FE_FLAGS_DIR)); + while (read_next_dirent(dir,m)); + dbg(1,"-------------------------------------------------------------------------------\n"); + closedir(dir); +} + +// return for dirent +int ret_dirent(FILE_ENTRY* ep) { + // ep may be null + dbg(2,"%s()\n",__func__); + int i; + + memset(gb,0x00,TPDD_MSG_MAX); + gb[0] = RET_DIRENT[0]; + gb[1] = RET_DIRENT[1]; + + if (ep) { + // name + memset (gb + 2, ' ', TPDD_FILENAME_LEN); + if (base_len) for (i=0;iclient_fname[i])?ep->client_fname[i]:' '; + else memcpy (gb+2,ep->client_fname,TPDD_FILENAME_LEN); + + // attribute + gb[26] = ep->attr; + + // size + gb[27] = (uint8_t)(ep->len >> 0x08); // most significant byte + gb[28] = (uint8_t)(ep->len & 0xFF); // least significant byte + } + + dbg(3,"\"%*.*s\" (%c) 0x%02X%02X\n",TPDD_FILENAME_LEN,TPDD_FILENAME_LEN,gb+2,gb[26],gb[27],gb[28]); + + // free sectors + gb[29] = model==2?(PDD2_TRACKS*PDD2_SECTORS):(PDD1_TRACKS*PDD1_SECTORS); + + gb[30] = checksum (gb); + + return (write_client_tty(gb,31) == 31); +} + +void dirent_set_name() { + dbg(2,"%s()\n",__func__); + if (gb[2]) { + dbg(3,"filename: \"%-*.*s\"\n",TPDD_FILENAME_LEN,TPDD_FILENAME_LEN,gb+2); + dbg(3," attr: \"%c\" (%1$02X)\n",gb[26]); + } + char* p; + char filename[TPDD_FILENAME_LEN+1] = {0x00}; + uint8_t fileattr = 0x00; + int f = 0; + + // Update the local file list before every set-name. + // * clients may open files any time without ever listing first + // * local files may be changed at any time by other processes + // and we need to have the tpdd version of all filenames ready to compare + // against, to respond correctly if it exists/doesn't/writable/not etc. + // TODO - Do we really need to do it that way? + // What about examining each local file on the spot instead of + // pre/re-generating a stored list of converted filenames? + // Maybe one thing a pre-generated list might be good for is the eventual + // filesystem access to disk images where we would model the FCB and SMT + // tables and disk sectors and update them the same way a real drive + // does when files are added/removed/read/written. + update_file_list(ALLOW_RET); + + // copy the filename from the buffer + strncpy(filename,(char*)gb+2,TPDD_FILENAME_LEN); + filename[TPDD_FILENAME_LEN]=0x00; + fileattr = gb[26]; + + // Remove trailing spaces + for (p = strrchr(filename,' ');p >= filename && *p == ' ';p--) *p = 0x00; + + cur_file = find_file(filename, fileattr); + + if (cur_file) { + dbg(3,"Exists: \"%s\" %u\n", cur_file->local_fname, cur_file->len); + ret_dirent(cur_file); + } else if (!check_magic_file(filename)) { + // let UR2/TSLOAD load DOSxxx.CO from anywhere + cur_file = make_file_entry(filename, fileattr, 0, 0); + char t[LOCAL_FILENAME_MAX+1] = {0x00}; + // try share root + // TODO - save initial share_path[0] and use that instead of "../"*depth + // tpdd2 can't do dme, so share_path[1] is available + for (int i=dir_depth;i>0;i--) strcat(t,"../"); + strncat(t,cur_file->local_fname,LOCAL_FILENAME_MAX-dir_depth*3); + struct stat st; int e = stat(t, &st); + if (e) { // try app_lib_dir + strcpy(t,app_lib_dir); + strcat(t,"/"); + strcat(t,cur_file->local_fname); + e=stat(t,&st); + } + if (e) ret_dirent(NULL); // not found + else { // found in share root or in app_lib_dir + strcpy(cur_file->local_fname,t); + cur_file->len=st.st_size; + dbg(3,"Magic: \"%s\" <-- \"%s\"\n",cur_file->client_fname,cur_file->local_fname); + ret_dirent(cur_file); + } + } else { + if (!strncmp(filename+base_len+1,dme_dir_label,2)) f = FE_FLAGS_DIR; + cur_file = make_file_entry(collapse_padded_fname(filename), fileattr, 0, f); + dbg(3,"New %s: \"%s\"\n",f==FE_FLAGS_DIR?"Directory":"File",cur_file->local_fname); + ret_dirent(NULL); + } +} + +void dirent_get_first() { + dbg(2,"Directory Listing\n"); + // update every time before get-first, + // because set-name is not required before get-first + update_file_list(ALLOW_RET); + ret_dirent(get_first_file()); + in_dme = 0; // exit dme - see req_fdc() +} + +// b[0] = cmd +// b[1] = len +// b[2]-b[25] = filename +// b[26] = attr +// b[27] = action (search form) +// +// Ignore the name & attr until after determining the action. +// TS-DOS submits get-first & get-next requests with junk data +// in the filename & attribute fields left over from previous actions. +int req_dirent() { + if (debug>1) { + dbg(2,"%s(%s)\n",__func__, + gb[27]==DIRENT_SET_NAME?"set_name": + gb[27]==DIRENT_GET_FIRST?"get_first": + gb[27]==DIRENT_GET_NEXT?"get_next": + gb[27]==DIRENT_GET_PREV?"get_prev": + gb[27]==DIRENT_CLOSE?"close": + "UNKNOWN" + ); + dbg(5,"gb[]\n"); + dbg_b(5,gb,-1); + dbg_p(4,gb); + } + + switch (gb[27]) { + case DIRENT_SET_NAME: dirent_set_name(); break; + case DIRENT_GET_FIRST: dirent_get_first(); break; + case DIRENT_GET_NEXT: ret_dirent(get_next_file()); break; + case DIRENT_GET_PREV: ret_dirent(get_prev_file()); break; + case DIRENT_CLOSE: break; + } + return 0; +} + +// update dme_cwd with current dir, truncated & padded both required +// If you don't send all 6 bytes, TS-DOS doesn't clear the previous +// contents from the display +void update_dme_cwd() { + dbg(2,"%s()\n",__func__); + if (!dme_en) return; + + int i; + update_cwd(); + dbg(0,"Changed Dir: %s\n",cwd); + if (dir_depth) { + for (i=strlen(cwd); i>=0 ; i--) { + if (cwd[i]=='/') break; + if (upcase && cwd[i]>='a' && cwd[i]<='z') cwd[i]=cwd[i]-32; + } + snprintf(dme_cwd,base_len+1,"%-*.*s",6,6,cwd+1+i); + } else { + memcpy(dme_cwd,dme_root_label,6); + } +} + +// TS-DOS DME return +// Construct a DME packet around dme_cwd and send it to the client +void ret_dme_cwd() { + dbg(2,"%s(\"%s\")\n",__func__,dme_cwd); + if (!dme_en) return; + gb[0] = RET_STD[0]; + gb[1] = 0x0B; // not RET_STD[1] because TS-DOS DME violates the spec + gb[2] = 0x00; // don't know why this byte is 0 + memcpy(gb+3,dme_cwd,6); // 6 bytes 3-8 display in top-right corner + gb[9] = 0x00; // gb[9]='.'; // remaining contents don't matter but length does + gb[10] = 0x00; // gb[10]=dme_dir_label[0]; + gb[11] = 0x00; // gb[11]=dme_dir_label[1]; + gb[12] = 0x00; // gb[12]=0x20; + gb[13] = checksum(gb); + write_client_tty(gb,14); +} + +// The "switch to FDC-mode" command requires careful handling, because +// unlike the original Desk-Link, we actually support the FDC commands, +// and need the "switch-to-fdc-mode" command to work like a real drive. +// +// So here we always look for TS-DOS "DME" request and set a "we're doing dme" +// flag, but only for the duration of a single directory listing process. +// The first stage of a directory listing, dirent(get-first), clears the dme +// flag so that FDC commands immediatly go back to working like a real drive. +// +// Any FDC request might actually be a DME request +// See ref/dme.txt for the full explaination +void req_fdc() { + dbg(2,"%s()\n",__func__); + + // TPDD1 does not send back any response + // TPDD2 returns a standard 0x12 return packet with 0x36 payload + // + // You can't have both full TPDD2 emulation including banks, + // and TS-DOS directories support at the same time. + // + // If we recognize a DME request and respond with a DME return, then you get + // TS-DOS subdirecties, but then TS-DOS does not show a Bank button, + // even if we had otherwise responded as a TPDD2 ie with the tpdd2 version + // packet and working tpdd2-only features like dirent(get-prev). + // + // If we are in tpdd2 mode and reject the DME request like a real tpdd2, + // then TS-DOS does show the Bank button and you can switch banks 0 & 1. + if (model==2) { ret_std(ERR_PARAM); return; } + + // Some versions of TS-DOS send 2 FDC requests in a row, both with trailing + // 0x0D. Some versions also send a 3rd FDC request without the trailing 0x0D. + // Look for 2 consecutive FDC requests with trailing 0x0D. Once we see that, + // don't try to read a trailing 0x0D any more to avoid reading the command + // byte of a real FDC command, and respond to the 2nd and any other FDC + // requests with DME response instead of switching to FDC mode, as long as in_dme>1. + // in_dme is only set here, and only unset in dirent_get_first() + if (in_dme<2 && dme_en) { + // Try to read one more byte, and store it in ch[0] where get_fdc_req() + // can pick it up in case it was NOT the trailing 0x0D of a DME request + // but instead was the first byte of an actual FDC command. + // Timeout fast whether there is a byte or not. + //dbg(3,"looking for dme req %d of 2\n",in_dme+1); + ch[0] = 0x00; + client_tty_vmt(0,1); // allow this read to time out, and fast + (void)!read(client_tty_fd,ch,1); + client_tty_vmt(-1,-1); // restore normal VMIN/VTIME + if (ch[0]==FDC_CMD_EOL) dbg(3,"Got dme req %d of 2\n",++in_dme); + //if (ch[0]) dbg(3,"ate a byte: %02X\n",ch[0]); + } + if (in_dme>1) { + ret_dme_cwd(); + } else { + operation_mode = MODE_FDC; + dbg(2,"Switched to \"FDC\" mode\n"); // no response to client, just switch modes + } +} + +// b[0] = fmt 0x01 +// b[1] = len 0x01 +// b[2] = mode 0x01 write new +// 0x02 write append +// 0x03 read +// b[3] = chk +int req_open() { + if (debug>1) { + dbg(2,"%s(\"%s\",\"%c\")\n",__func__,cur_file->client_fname,cur_file->attr); + dbg(5,"gb[]\n"); + dbg_b(5,gb,-1); + dbg_p(4,gb); + } + + uint8_t omode = gb[2]; + + switch(omode) { + case F_OPEN_WRITE: + dbg(2,"mode: write\n"); + if (o_file_h >= 0) { + close(o_file_h); + o_file_h=-1; + } + if (cur_file->flags&FE_FLAGS_DIR) { + if (!mkdir(cur_file->local_fname,0777)) { + ret_std(ERR_SUCCESS); + } else { + ret_std(ERR_FMT_MISMATCH); + } + } else { + o_file_h = open(cur_file->local_fname,O_CREAT|O_TRUNC|O_WRONLY|O_EXCL,0666); + if (o_file_h<0) + ret_std(ERR_FMT_MISMATCH); + else { + f_open_mode=omode; + dl_fsetxattr(o_file_h, &cur_file->attr); + dbg(1,"Open for write: \"%s\" (%c)\n",cur_file->local_fname,cur_file->attr); + ret_std(ERR_SUCCESS); + } + } + break; + case F_OPEN_APPEND: + dbg(2,"mode: append\n"); + if (o_file_h >= 0) { + close(o_file_h); + o_file_h=-1; + } + if (cur_file==0) { + ret_std(ERR_FMT_MISMATCH); + return -1; + } + o_file_h = open(cur_file->local_fname, O_WRONLY | O_APPEND); + if (o_file_h < 0) + ret_std(ERR_FMT_MISMATCH); + else { + f_open_mode=omode; + dl_fsetxattr(o_file_h, &cur_file->attr); + dbg(1,"Open for append: \"%s\" (%c)\n",cur_file->local_fname,cur_file->attr); + ret_std(ERR_SUCCESS); + } + break; + case F_OPEN_READ: + dbg(2,"mode: read\n"); + if (o_file_h >= 0) { + close(o_file_h); + o_file_h=-1; + } + if (cur_file==0) { + ret_std(ERR_NO_FILE); + return -1; + } + + if (cur_file->flags&FE_FLAGS_DIR) { + int err=0; + // directory + if (cur_file->local_fname[0]=='.' && cur_file->local_fname[1]=='.') { + // parent dir + if (dir_depth>0) { + err=chdir(cur_file->local_fname); + if (!err) dir_depth--; + } + } else { + // enter dir + err=chdir(cur_file->local_fname); + if (!err) dir_depth++; + } + update_dme_cwd(); + if (err) ret_std(ERR_FMT_MISMATCH); + else ret_std(ERR_SUCCESS); + } else { + // regular file + o_file_h = open(cur_file->local_fname, O_RDONLY); + if (o_file_h<0) + ret_std(ERR_NO_FILE); + else { + f_open_mode = omode; + dl_fgetxattr(o_file_h, &cur_file->attr); + dbg(1,"Open for read: \"%s\" (%c)\n",cur_file->local_fname,cur_file->attr); + ret_std(ERR_SUCCESS); + } + } + break; + default: + dbg(2,"Unrecognized mode: \"0x%02X\"\n",omode); + ret_std(ERR_PARAM); + break; + } + return o_file_h; +} + +void req_read() { + dbg(2,"%s()\n",__func__); + int i; + + if (o_file_h<0) { + ret_std(ERR_NO_FNAME); + return; + } + if (f_open_mode!=F_OPEN_READ) { + ret_std(ERR_FMT_MISMATCH); + return; + } + + i = read(o_file_h, gb+2, REQ_RW_DATA_MAX); + + gb[0] = RET_READ; + gb[1] = (uint8_t)i; + gb[2+i] = checksum(gb); + + if (debug<2) { + dbg(1,"."); + if (i1) { + dbg(4,"...outgoing packet...\n"); + dbg(5,"gb[]\n"); + dbg_b(5,gb,-1); + dbg_p(4,gb); + dbg(4,".....................\n"); + } + + write_client_tty(gb, 3+i); +} + +// b[0] = 0x04 +// b[1] = 0x01 - 0x80 +// b[2] = b[1] bytes +// b[2+len] = chk +void req_write() { + if (debug>1) { + dbg(2,"%s()\n",__func__); + dbg(4,"...incoming packet...\n"); + dbg(5,"gb[]\n"); + dbg_b(5,gb,-1); + dbg_p(4,gb); + dbg(4,".....................\n"); + } + + if (o_file_h<0) {ret_std(ERR_NO_FNAME); return;} + + if (f_open_mode!=F_OPEN_WRITE && f_open_mode !=F_OPEN_APPEND) { + ret_std(ERR_FMT_MISMATCH); + return; + } + + if (debug<2) { + dbg(1,"."); + if (gb[1]flags&FE_FLAGS_DIR) rmdir(cur_file->local_fname); + else unlink (cur_file->local_fname); + dbg(1,"Deleted: %s\n",cur_file->local_fname); + ret_std (ERR_SUCCESS); +} + + +/* + * PDD2 cache load, cache commit, mem read, mem write + * + * Emulating access to the sector cache is straightforward. + * Emulating access to the cpu memory is less so. + * + * The command allows to read from anywhere in the cpus address space, + * but we wouldn't know what to return for much of that. + * + * We recognize a few special addresses and just return "success" + * for all other access to the cpu area without actually doing anything. + * + * cpu memory map: + * 0000-001F cpu i/o port + * 0080-00FF cpu internal ram 128 bytes + * 4000-4002 gate array (floppy controller) + * 8000-87FF ram 2k bytes + * F000-FFFF cpu internal rom 4k bytes + * + * Some cpu_memory writes observed from common clients, not including ZZ or checksum: + * + * fmt len area offset data + * BACKUP.BA + * 0x31, 0x04, 0x01, 0x00,0x83, 0x00, + * 0x31, 0x04, 0x01, 0x00,0x96, 0x00, + * 0x31, 0x07, 0x01, 0x80,0x04, 0x16,0x00,0x00,0x00 (data varies) this is the only one we actually do anything + * + * TS-DOS + * 0x31, 0x04, 0x01, 0x00,0x84, 0xFF, + * 0x31, 0x04, 0x01, 0x00,0x96, 0x0F, + * 0x31, 0x04, 0x01, 0x00,0x94, 0x0F, + * + * pdd2 service manual p102 says: + * Reset Drive Status + * write FF to 0084 + * write 0F to 0096 + * write 0F to 0094 + * + */ + +// also the return format for mem_write and undocumented 0x0F +void ret_cache(uint8_t e) { + dbg(3,"%s()\n",__func__); + gb[0] = RET_CACHE[0]; + gb[1] = RET_CACHE[1]; + gb[2] = e; + gb[3] = checksum(gb); + write_client_tty(gb,4); +} + +/* + * Load a sector from disk into ram[], + * or commit ram[] to a sector on the disk. + * + * Committing the cache to disk does NOT clear the cache in ram. + * + * Load/Commit Cache + * b[0] fmt 0x30 + * b[1] len 0x05 + * b[2] action 0=load (cachedisk) 2=commit+verify + * b[3] track msb - (always 00) + * b[4] track lsb - 00-4F + * b[5] side (always 00) + * b[6] sector 0-1 + */ +void req_cache() { + dbg(3,"%s(action=%u track=%u sector=%u)\n",__func__,gb[2],gb[4],gb[6]); + if (model==1) return; + uint8_t a=gb[2]; + //uint_16_t t=b[3]*256+b[4]; // b[3] is always 0 + uint8_t t=gb[4]; + //int d=gb[5]; // side#? - always 0 + uint8_t s=gb[6]; // sector + if (t>=PDD2_TRACKS || s>=PDD2_SECTORS) { ret_cache(ERR_PARAM); return; } + uint8_t rn = t*2 + s; // convert track#:sector# to linear record# + uint8_t e = ERR_SUCCESS; + + switch (a) { + case CACHE_LOAD: + dbg(2,"cache load: track:%u sector:%u\n",t,s); + + // open disk image file and seek to record number + if ((e = open_disk_image(rn,O_RDONLY))) break; + + // virtual 2k drive ram + memset(ram,0x00,RAM_LEN); // 2k ram at 0x8000 - 0x87FF + ram[0]=PDD2_CACHE_LEN_MSB; // len MSB - always 0x05 + ram[1]=PDD2_CACHE_LEN_LSB; // len LSB - always 0x13 + ram[2]=rn; // linear sector number (0-159) + //ram[0x03]=0x00; // side number? - always 0 + if (read(disk_img_fd,ram+PDD2_ID_REL,SECTOR_HEADER_LEN)!=SECTOR_HEADER_LEN) { e = ERR_DEFECTIVE; break; } + //ram[0x11]= // unknown but changes when other data changes, crc msb? + //ram[0x12]= // unknown but changes when other data changes, crc lsb? + if (read(disk_img_fd,ram+PDD2_DATA_REL,SECTOR_DATA_LEN)!=SECTOR_DATA_LEN) { e = ERR_DEFECTIVE; break; } + //ram[0x0513]= // unknown + //... // + //ram[0x07FF]= // end of 2k ram + break; + + case CACHE_COMMIT: // write cache to disk + case CACHE_COMMIT_VERIFY: // write cache to disk and verify + + // open disk image file and seek to record number + dbg(2,"cache commit: track:%u sector:%u\n",t,s); + if ((e = open_disk_image(rn,O_WRONLY))) break; + if (write(disk_img_fd,ram+PDD2_ID_REL,SECTOR_HEADER_LEN)!=SECTOR_HEADER_LEN) { e = ERR_DEFECTIVE; break; } + if (write(disk_img_fd,ram+PDD2_DATA_REL,SECTOR_DATA_LEN)!=SECTOR_DATA_LEN) { e = ERR_DEFECTIVE; break; } + break; + default: e = ERR_PARAM; + } + close(disk_img_fd); + dbg_b(3,ram,RAM_LEN); + if (e) dbg(2,"FAILED\n"); + ret_cache(e); +} + +/* + * req: + * b[0] fmt req_mem_read + * b[1] len 4 + * b[2] area 0=sector_cache 1=cpu_memory + * b[3] offset msb 0000-0500 0000-8FFF + * b[4] offset lsb + * b[5] dlen 00-FC + * b[6] chk + * + * ret: + * b[0] fmt ret_mem_read + * b[1] len (dlen+3) + * b[2] area 0=sector_cache 1=cpu_memory + * b[3] offset msb + * b[4] offset lsb + * b[5+] data dlen bytes + * b[#] chk + */ +void req_mem_read() { + dbg(3,"%s()\n",__func__); + if (model==1) return; + uint8_t a = gb[2]; + uint16_t o = gb[3]*256+gb[4]; + uint8_t l = gb[5]; + uint8_t e = ERR_SUCCESS; + uint8_t* src = ram; // source of virtual ram data, ram[], rom[], etc + switch (a) { + case MEM_CACHE: + dbg(2,"mem_read: cache offset:0x%04X len:0x%02X\n",o,l); + if (o+l>SECTOR_DATA_LEN || l>PDD2_MEM_READ_MAX) e=ERR_PARAM; + o+=PDD2_DATA_REL; + break; + case MEM_CPU: + dbg(2,"mem_read: cpu addr:0x%04X len:0x%02X\n",o,l); + if (o>=IOPORT_ADDR && o=CPURAM_ADDR && o=GA_ADDR && o=RAM_ADDR && o=ROM_ADDR && oSECTOR_DATA_LEN || l>PDD2_MEM_WRITE_MAX) e=ERR_PARAM; + o+=PDD2_DATA_REL; + break; + case MEM_CPU: + dbg(2,"mem_write: cpu addr:0x%04X len:0x%02X\n",o,l); + if (o>=IOPORT_ADDR && o=CPURAM_ADDR && o=GA_ADDR && o=RAM_ADDR && o=ROM_ADDR && olocal_fname,t)) + ret_std(ERR_SECTOR_NUM); + else { + dbg(1,"Renamed: %s -> %s\n",cur_file->local_fname,t); + ret_std(ERR_SUCCESS); + } +} + +void req_close() { + dbg(2,"%s()\n",__func__); + if (o_file_h>=0) close(o_file_h); + o_file_h = -1; + dbg(2,"Closed: \"%s\"\n",cur_file->local_fname); + ret_std(ERR_SUCCESS); +} + +void req_status() { + dbg(2,"%s()\n",__func__); + ret_std(ERR_SUCCESS); +} + +// TPDD2 only +// response is 8 bit flags +// 7 unused MSB +// 6 unused +// 5 unused +// 4 unused +// 3 disk changed +// 2 disk not inserted +// 1 write protected +// 0 low power +void ret_condition() { + dbg(3,"%s()\n",__func__); + gb[0] = RET_CONDITION[0]; + gb[1] = RET_CONDITION[1]; + gb[2] = pdd2_condition; + gb[3] = checksum(gb); + write_client_tty(gb,gb[1]+3); +} + +void req_condition() { + dbg(2,"%s()\n",__func__); + if (model!=2) return; + ret_condition(); +} + +// opr-format - this creates a disk that can load & save files +// the only difference from fdc-format is a single byte, the first byte of the SMT +// opr-format is just this: +// start with: fdc-format 0 (0=64-byte logical sector size) +// then: write 0x80 at sector 0 byte 1240 (aka physical:0 logical:20 byte:25 counting from 1) +void req_format() { + dbg(2,"%s()\n",__func__); + const int rc = model==1?(PDD1_TRACKS*PDD1_SECTORS):(PDD2_TRACKS*PDD2_SECTORS); // records count + int rn = 0; // record number + + dbg(0,"Operation-mode Format (make a filesystem)\n"); + + uint8_t e = open_disk_image(0,O_WRONLY); + if (e==ERR_READ_TIMEOUT) e=ERR_FMT_INTERRUPT; + if (e) { ret_std(e); return; } + + // write the image + // Real drive TPDD1 fresh Operation-mode format is strange. + // Any sector with any data gets LSC 0, and all others get LSC 1. + // Later, any sector that gets used by a file gets changed from LSC 1 to + // LSC 0, and never changed back even when files are deleted. + // A fresh format has one byte of data in sector 0 in the SMT, + // so a fresh format sector 0 has LSC 0 and all other sectors have LSC 1. + // We exactly mimick that here "just because", even though the LSC 1s + // don't seem to actually matter and we could just make all LSC 0. + for (rn=0;rn> 0x08); // msb + gb[4] = (uint8_t)(reg_X & 0xFF); // lsb + gb[5] = checksum(gb); + write_client_tty(gb,6); +} + +/* Load cpu registers A and X with supplied values, then jump to supplied address. + * + * examples: + * - jump to a rom routine + * - req_cache() to load a sector from disk first, then jump to the sector cache + * - req_mem_write() to write arbitrary code to cpu memory first, then jump to it + * + * b[0] fmt (0x34) + * b[1] len (0x05) + * b[2] addr msb - execute address 2 bytes + * b[3] addr lsb + * b[4] reg A - 1 byte + * b[5] reg X msb - 2 bytes + * b[6] reg X lsb + * b[7] chk + */ +void req_exec() { + dbg(3,"%s() ***STUB***\n",__func__); + if (model==1) return; + uint16_t addr = gb[2]*256+gb[3]; + uint8_t reg_A = gb[4]; + uint16_t reg_X = gb[5]*256+gb[6]; + dbg(2,"exec: addr:%u A:%u X:%u\n",addr,reg_A,reg_X); + /* + * ...6301 emulator here... + * executed code leaves new values in reg_A and reg_X + */ + dbg(2,"(stub, exec() not implimented)"); + ret_exec(reg_A,reg_X); +} + +void get_opr_cmd() { + dbg(3,"%s()\n",__func__); + uint16_t i = 0; + memset(gb,0x00,TPDD_MSG_MAX); + + while (read_client_tty(&gb,1) == 1) { + if (gb[0]==OPR_CMD_SYNC) i++; else { i=0; gb[0]=0x00; continue; } + if (i<2) { gb[0]=0x00; continue; } + if (read_client_tty(&gb,2) == 2) if (read_client_tty(&gb[2],gb[1]+1) == gb[1]+1) break; + i=0; memset(gb,0x00,TPDD_MSG_MAX); + } + + dbg_p(3,gb); + + if ((i=checksum(gb))!=gb[gb[1]+2]) { + dbg(0,"Failed checksum: received: 0x%02X calculated: 0x%02X\n",gb[gb[1]+2],i); + return; // real drive does not return anything + } + + // Preserve the original packet for reference "just because" even though + // we could actually get away with modifying gb[0] at this point. + uint8_t c = gb[0]; + + // decode bit 6 in the FMT byte b[0] for bank0 vs bank1 + if (model==2) { + //bank = 0; if (c&0x40) { bank = 1; c-=0x40; } // alternative + bank = (c >> 6) & 1; // read bit 6 to set bank 0 or 1 + c &= ~(1 << 6); // clear bit 6 so incoming 0x4# matches 0x0# case + } + + // translate the undocumented synonyms + // https://www.mail-archive.com/m100@lists.bitchin100.com/msg18555.html + if ( c>0x0D && c<0x13 ) c+=0x22; + + // TODO + // Test combinations of both of the above things on a real drive. + // Example, what does 0x51 do? Is it right that we test for 0x11 + // after subtracting 0x40, so that we end up doing 0x33? + // Does tpdd1 do the 0x22 thing? + + // dispatch + switch(c) { + case REQ_DIRENT: req_dirent(); break; + case REQ_OPEN: req_open(); break; + case REQ_CLOSE: req_close(); break; + case REQ_READ: req_read(); break; + case REQ_WRITE: req_write(); break; + case REQ_DELETE: req_delete(); break; + case REQ_FORMAT: req_format(); break; + case REQ_STATUS: req_status(); break; + case REQ_FDC: req_fdc(); break; + case REQ_CONDITION: req_condition(); break; + case REQ_RENAME: req_rename(); break; + case REQ_VERSION: ret_version(); break; + case REQ_CACHE: req_cache(); break; + case REQ_MEM_READ: req_mem_read(); break; + case REQ_MEM_WRITE: req_mem_write(); break; + case REQ_SYSINFO: ret_sysinfo(); break; + case REQ_EXEC: req_exec(); break; + default: dbg(1,"OPR: unknown cmd \"0x%02X\"\n",gb[0]); dbg_p(1,gb); + // local msg, nothing to client + } +} + +//////////////////////////////////////////////////////////////////////// +// +// BOOTSTRAP +// + +void slowbyte(uint8_t b) { + write_client_tty(&b,1); + tcdrain(client_tty_fd); + usleep(BASIC_byte_us); + + // line-endings - convert CR, LF, CRLF to local eol + if (ch[0]==BASIC_EOL) { + ch[0]=0x00; + dbg(0,"%c",LOCAL_EOL); + if (b==LOCAL_EOL) return; + } + if (b==BASIC_EOL) { ch[0]=BASIC_EOL; return; } + +#if defined(PRINT_8BIT) + // display <32 and 127 as inverse ctrl char without ^ + // print everything else, requires disable 8-bit vt codes + if (b<32) { dbg(0,SSO"%c"RSO,b+64); return; } + if (b==127) { dbg(0,SSO"?"RSO); return; } +#else + // display <32 >126 as inverse hex + if (b<32||b>126) { dbg(0,SSO"%02X"RSO,b); return; } +#endif + + dbg(0,"%c",b); +} + +int send_BASIC(char* f) { + int fd; + uint8_t b; + + if ((fd=open(f,O_RDONLY))<0) { + dbg(0,"Could not open \"%s\" : %s\n",f,errno); + return 9; + } + +#if defined(PRINT_8BIT) + dbg(1,D8C); // disable 8-bit vt codes (0x80-0x9F) so we can print them +#endif + dbg(0,"-- start --\n"); + ch[0]=0x00; + while(read(fd,&b,1)==1) slowbyte(b); + close(fd); + if (base_len) { // if not in raw mode supply missing trailing EOF & EOL + if (b!=LOCAL_EOL && b!=BASIC_EOL && b!=BASIC_EOF) slowbyte(BASIC_EOL); + if (b!=BASIC_EOF) slowbyte(BASIC_EOF); + } + close(client_tty_fd); + dbg(0,"\n-- end --\n\n"); + return 0; +} + +int bootstrap(char* f) { + dbg(0,"Bootstrap: Installing \"%s\"\n\n",f); + if (access(f,F_OK)==-1) { + dbg(0,"Not found.\n"); + return 1; + } + + char t[PATH_MAX+1]={0x00}; + uint8_t sc = baud_to_stat_code(baud); + if (!sc) { + dbg(0,"Prepare the client to receive data." + "\n" + "Note: The current baud setting, %d, is not supported\n" + "by the TRS-80 Model 100 or other KC-85-platform machines.\n" + "There is no way for BASIC or TELCOM to use this baud rate.\n",baud); + } else { + strcpy(t,f); + strcat(t,".pre-install.txt"); + if (!access(t,F_OK) && sc==9) dcat(t); // the text files all assume 19200 + else { + dbg(0,"Prepare BASIC to receive:\n" + "\n" + " RUN \"COM:%1$d8N1ENN\" [Enter] <-- TANDY/Olivetti/Kyotronic\n" + " RUN \"COM:%1$dN81XN\" [Enter] <-- NEC\n",sc); + } + } + + dbg(0,"\nPress [Enter] when ready..."); + getchar(); + + { int r; if ((r=send_BASIC(f))!=0) return r; } + + strcpy(t,f); + strcat(t,".post-install.txt"); + dcat(t); + + dbg(0,"\n\n\"%1$s -b\" will now exit.\n" + "Re-run \"%1$s\" (without -b this time) to run the TPDD server.\n\n",args[0]); + + return 0; +} + +//////////////////////////////////////////////////////////////////////// +// +// MAIN +// + +void show_config () { + dbg(0,"model : %d\n",model); + dbg(0,"operation_mode : %d\n",operation_mode); + dbg(0,"profile : %s\n",profile); + dbg(0,"base_len : %d\n",base_len); + dbg(0,"ext_len : %d\n",ext_len); + dbg(0,"pad_fn : %s\n",pad_fn?"true":"false"); + dbg(0,"attr : '%c' (0x%1$02X)\n",default_attr); +#if defined(USE_XATTR) + dbg(0,"xattr_name : \"%s\"\n",xattr_name); +#endif + dbg(0,"upcase : %s\n",upcase?"true":"false"); + dbg(0,"rtscts : %s\n",rtscts?"true":"false"); + dbg(0,"verbosity : %d\n",debug); + dbg(0,"dme_en : %s\n",dme_en?"true":"false"); + dbg(0,"magic_files : %s\n",enable_magic_files?"true":"false"); + dbg(0,"BASIC_byte_ms : %d\n",BASIC_byte_us/1000); + dbg(0,"bootstrap_fname : \"%s\"\n",bootstrap_fname); + dbg(0,"app_lib_dir : \"%s\"\n",app_lib_dir); + dbg(0,"client_tty_name : \"%s\"\n",client_tty_name); + dbg(0,"disk_img_fname : \"%s\"\n",disk_img_fname); + dbg(2,"iwd : \"%s\"\n",iwd); + dbg(2,"cwd : \"%s\"\n",cwd); + dbg(0,"share_path[0] : \"%s\"\n",share_path[0]); + dbg(0,"share_path[1] : \"%s\"\n",share_path[1]); + dbg(0,"baud : %d\n",baud); + dbg(0,"dme_root_label : \"%-*.*s\"\n",6,6,dme_root_label); + dbg(0,"dme_parent_label: \"%-*.*s\"\n",6,6,dme_parent_label); + dbg(0,"dme_dir_label : \"%-2.2s\"\n",dme_dir_label); + dbg(0,"tildes : %s\n",tildes?"true":"false"); +#if !defined(_WIN) + dbg(0,"getty_mode : %s\n",getty_mode?"true":"false"); +#endif +} + +void show_main_help() { + load_profile(DEFAULT_PROFILE); + dbg(0,"\nUsage: %1$s [options] [tty_device] [share_path]\n" + "\n" + "Options Description... (default setting)\n" +// " -0 Raw mode - no filename munging, attr = ' '\n" +#if defined(USE_XATTR) + " -a attr Attribute - default attr byte used when no xattr (%2$c)\n" +#else + " -a attr Attribute - attribute byte used for all files (%2$c)\n" +#endif + " -b file Bootstrap - send loader file to client - empty for help\n" + " -c profile Client compatibility profile (%9$s) - empty for help\n" + " -d tty Serial device connected to the client (%4$s*)\n" + " -e bool TS-DOS Subdirectories (%10$s) - TPDD1-only\n" + " -f Start in FDC mode - TPDD1-only\n" +#if !defined(_WIN) + " -g Getty mode - run as daemon\n" +#endif + " -h Print this help\n" + " -i file Disk image filename for raw sector access - empty for help\n" +// " -l List loader files and show bootstrap help\n" + " -m 1|2 Model - 1 = FB-100/TPDD1, 2 = TPDD2 (%5$u)\n" +// " -n Disable TS-DOS directories\n" +// " -n #.#[p] Names - Translate filenames to #.# format, optionally [p]added\n" + " -p dir Path - /path/to/dir with files to be served (./)\n" + " -r bool RTS/CTS hardware flow control (%7$s)\n" + " -s # Speed - serial port baud rate (%6$d)\n" + " -u Uppercase all filenames (%8$s)\n" + " -~ bool Truncated filenames end in '~' (%11$s)\n" + " -v Verbosity - more v's = more verbose, both activity & help\n" +// " -w WP-2 mode - 8.2 filenames for TANDY WP-2\n" + " -z # Sleep # ms per byte in bootstrap (%3$d)\n" + " -^ Dump config and exit\n" + "\n" + "The 1st non-option argument is another way to specify the tty device.\n" + "The 2nd non-option argument is another way to specify the share path.\n" + "TPDD2 mode accepts a 2nd share path for bank 1.\n" + //"TS-DOS directory support is only possible in TPDD1 mode.\n" + "\"bool\" accepts case-insensitive: on off 0 1 y n t f yes no true false\n" + "\n" + "Examples:\n" + " $ %1$s\n" + " $ %1$s ttyUSB1\n" + " $ %1$s -v -p ~/Downloads/REX\n" + " $ %1$s -c wp2 /dev/cu.usbserial-AB0MQNN1 \"~/Documents/WP-2 Files\"\n" + " $ %1$s -m2 -p /tmp/bank0 -p /tmp/bank1\n" + "\n" + ,args[0] + ,ATTR_DEF + ,DEFAULT_BASIC_BYTE_MS + ,TTY_PREFIX + ,DEFAULT_MODEL + ,DEFAULT_BAUD + ,DEFAULT_RTSCTS?"on":"off" + ,DEFAULT_UPCASE?"on":"off" + ,DEFAULT_PROFILE + ,dme_en?"on":"off" + ,tildes?"on":"off" + ); + +} + +void show_bootstrap_help(int e) { + + dbg(0, + "\n" + "Help for Bootstrap\n" + "\n" + "Usage:\n" + " -b filename send file out over the serial port, slowly\n" + " -v -b more help about bootstrap\n" + "\n" + "If filename is not found, then %1$s is searched.\n" + "\n" + ,app_lib_dir + ); + dbg(1, + "The bootstrap function is a convenient way to load software onto\n" + "KC-85 clone machines like TRS-80 Model 100 via the serial port,\n" + "when there is no proper file-transfer software installed yet.\n" + "\n" + "It just does the same thing you could do manually with TELCOM and any\n" + "kind of serial terminal program on the pc, but automates the process\n" + "to the fewest possible manual steps, and the few necessary manual steps\n" + "have on-screen prompts so you never have to remember the key details.\n" + "\n" + " should be a valid BASIC program file in ascii format,\n" + "meaning a plain text *.DO file not a tokenized *.BA file.\n" + "\n" + "Line-endings may be either CRLF or CR-only, but not LF-only.\n" + "Lines may be up to 255 bytes long, although the interactive editor\n" + "in the BASIC interpreter can not handle lines longer than 127 bytes.\n" + "\n" + "The file should have a CR or CRLF at the end of the last line,\n" + "and a ^Z (0x1A) after that as the last byte in the file.\n" + "If the final ^Z is missing then one will be sent after the data.\n" + "\n" + "Follow the on-screen prompts. First, dl2 will display a prompt showing\n" + "the RUN \"COM:...\" command to run on the receiving machine, and waits\n" + "for you to press Enter before proceeding.\n" + "\n" + "Open BASIC on the portable and type-in the displayed RUN command\n" + "and hit Enter there. BASIC will now look hung because there will be no\n" + "cursor or propmt or any other visible activity on the portable.\n" + "\n" + "Then press Enter here on the pc. The file will then start streaming\n" + "over to the portable, and will immediately start executing as soon as\n" + "the BASIC reads the ending ^Z." + "\n" + "Some installers have further instructions for that particular installer,\n" + "displayed either here on the pc or on the portable.\n" + "\n" + "If you want to keep the transferred BASIC instead of immediately\n" + "execute-and-discard, then where the prompt says RUN \"COM:98N1ENN\",\n" + "you can just type LOAD \"COM:98N1ENN\" instead, then SAVE \"NAME\" .\n" + "\n" + "This process is also handy for random ad-hoc transfers of any text or\n" + "basic files, not just program installers, simply because it removes all\n" + "of the variables of getting two comm programs configured correctly on\n" + "both ends of the serial link.\n" + "\n" + ); + dbg(0, + "Available built-in bootstrap/loader files (in %s):\n" + "\n" + ,app_lib_dir + ); + + dbg(0, "TRS-80 Model 100/102 :"); lsx(app_lib_dir,"100"," %s"); + dbg(0,"\nTANDY Model 200 :"); lsx(app_lib_dir,"200"," %s"); + dbg(0,"\nNEC PC-8201/PC-8300 :"); lsx(app_lib_dir,"NEC"," %s"); + dbg(0,"\nKyotronic KC-85 :"); lsx(app_lib_dir,"K85"," %s"); + dbg(0,"\nOlivetti M-10 :"); lsx(app_lib_dir,"M10"," %s"); + + dbg(0, + "\n" + "\n" + "Examples:\n" + "\n" + " %1$s -b TS-DOS.100\n" + " %1$s -b ~/Documents/LivingM100SIG/Lib-03-TELCOM/XMDPW5.100\n" + " %1$s -vb rxcini.DO && %1$s -v\n" + "\n" + ,args[0] + ); + + exit(e); +} + +int main(int argc, char** argv) { + dbg(0,APP_NAME " " APP_VERSION "\n"); + + int i; + bool x = false; + args = argv; + (void)!getcwd(iwd,PATH_MAX); // remember initial working directory + load_profile(DEFAULT_PROFILE); + + // environment + if (getenv("FDC_MODE")) operation_mode = !atobool(getenv("FDC_MODE")); + if (getenv("PROFILE")) load_profile(getenv("PROFILE")); + if (getenv("ATTR")) default_attr = *getenv("ATTR"); + if (getenv("DME")) dme_en = atobool(getenv("DME")); + if (getenv("TSLOAD")) enable_magic_files = atobool(getenv("TSLOAD")); + if (getenv("TILDES")) tildes = atobool(getenv("TILDES")); + if (getenv("CLIENT_TTY")) strcpy(client_tty_name,getenv("CLIENT_TTY")); + if (getenv("BAUD")) baud = atoi(getenv("BAUD")); + if (getenv("RTSCTS")) rtscts = atobool(getenv("RTSCTS")); + if (getenv("ROOT_LABEL")) snprintf(dme_root_label,6+1,"%-*.*s",6,6,getenv("ROOT_LABEL")); + if (getenv("PARENT_LABEL")) snprintf(dme_parent_label,6+1,"%-*.*s",6,6,getenv("PARENT_LABEL")); + if (getenv("DIR_LABEL")) snprintf(dme_dir_label,3,"%-2.2s",getenv("DIR_LABEL")); +#ifdef USE_XATTR + if (getenv("XATTR_NAME")) xattr_name = getenv("XATTR_NAME"); +#endif + + // commandline + while ((i = getopt (argc, argv, ":0a:b:c:d:e:fhi:lm:np:r:s:uvwz:~:^" +#if !defined(_WIN) + "g" +#endif + )) >=0) + switch (i) { + case '0': load_profile("raw"); break; // back compat, short for -c raw + case 'a': default_attr=*strndup(optarg,1); break; + case 'b': strcpy(bootstrap_fname,optarg); break; + case 'c': load_profile(optarg); break; + case 'd': strcpy(client_tty_name,optarg); break; + case 'e': dme_en = atobool(optarg); break; + //case 'f': set_fnames(optarg); break; + case 'f': operation_mode = MODE_FDC; break; +#if !defined(_WIN) + case 'g': getty_mode = true; debug = 0; break; +#endif + case 'h': show_main_help(); exit(0); break; + case 'i': set_disk_img_fname(optarg); break; + case 'l': show_bootstrap_help(0); break; // back compat, short for -b help / -i help + case 'm': model = atoi(optarg); break; + case 'n': dme_en = false; break; // back compat, short for -e false + //case 'n': set_fnames(optarg); break; + //case 'o': operation_mode = atobool(optarg); break; + case 'p': add_share_path(optarg); break; + case 'r': rtscts = atobool(optarg); break; + case 's': baud = atoi(optarg); break; + case 'u': upcase = true; break; + case 'v': debug++; break; + case 'w': load_profile("wp2"); break; // back compat, short for -c wp2 + case 'z': BASIC_byte_us=atoi(optarg)*1000; break; + case '~': tildes = atobool(optarg); break; + case '^': x = true; break; + case ':': dbg(0,"\"-%c\" requires a value\n",optopt); + if (optopt=='b') show_bootstrap_help(0); + if (optopt=='i') show_diskimage_help(0); + if (optopt=='c') show_profiles_help(0); + show_main_help(); return 1; + case '?': + if (isprint(optopt)) dbg(0,"Unknown option \"-%c\"\n",optopt); + else dbg(0,"Unknown option \"0x%02X\"\n",optopt); + default: show_main_help(); return 1; + } + + // commandline non-option arguments + for (i=0; optind < argc; optind++) { + if (x) dbg(1,"non-option arg %u: \"%s\"\n",i,argv[optind]); + switch (i++) { + case 0: strcpy (client_tty_name,argv[optind]); break; // tty device + case 1: + case 2: add_share_path(argv[optind]); break; // share path(s) + default: dbg(0,"Unknown argument: \"%s\"\n",argv[optind]); + } + } + + // base setup that's always needed, whether tpdd or bootstrap + if (model<1||model>2) {dbg(0,"Invalid model \"%u\"\n",model); return 1; } + if (share_path[0][0]) cd_share_path(); + if (!cwd[0]) update_cwd(); + if (!share_path[0][0]) strcpy(share_path[0],cwd); + resolve_client_tty_name(); + find_lib_file(bootstrap_fname); + + if (x) { show_config(); return 0; } + + dbg(0, "Serial Device: %s\n",client_tty_name); + + if ((i=open_client_tty())) return i; + + // send loader and exit + if (bootstrap_fname[0]) return (bootstrap(bootstrap_fname)); + + // further setup that's only needed for tpdd + if (model==2) { load_rom(TPDD2_ROM); dme_en=false; } + if (dme_en && base_len && base_len<=6) memcpy(dme_cwd,dme_root_label,base_len); + cfnl = base_len + 1 + ext_len; // client filename length + if (base_len<1||cfnl>TPDD_FILENAME_LEN) cfnl = TPDD_FILENAME_LEN; + + dbg(0,"\n"); + + dbg(2,"Emulating %s\n",(model==2)?"TANDY 26-3814 (TPDD2)":"Brother FB-100 (TPDD1)"); + dbg(2,"TPDD2 banks %s\n",(model==2)?"enabled":"disabled"); + if (strcmp(profile,DEFAULT_PROFILE)) dbg(2,"Client Compatibility Profile: \"%s\"\n",profile); + dbg(2,"TS-DOS directories %s\n",(dme_en)?"enabled":"disabled"); + dbg(2,"Magic files for UR-II/TSLOAD %s\n",(enable_magic_files)?"enabled":"disabled"); + if (model==2) dbg(0,"Bank 0 Dir: %s\nBank 1 Dir: %s\n",share_path[0],share_path[1]); + if (tildes) dbg(2,"Truncated filenames end in \"~\"\n"); +#ifdef USE_XATTR + dbg(2,"Attribute: Stored in xattr \"%s\", default \"%c\" when absent",xattr_name,default_attr); +#else + dbg(2,"Attribute: \"%c\"",default_attr); +#endif + dbg(2,"\n"); + + // initialize the file list + file_list_init(); + + // show the directory listing locally even before any directory list + // commands, so that a user with no client-side display like TEENY, REX + // rom image loading, REXCPM rxcini setup, etc can see what filenames are + // available to load, and their exact spelling from the tpdd client side. + if (debug) update_file_list(NO_RET); + + // process commands forever + while (1) switch (operation_mode) { + case MODE_FDC: get_fdc_cmd(); break; + default: get_opr_cmd(); break; + } + + // file_list_cleanup() + return 0; +} diff --git a/ref/REXCPM.md b/ref/REXCPM.md new file mode 100644 index 0000000..ec5ed7b --- /dev/null +++ b/ref/REXCPM.md @@ -0,0 +1,83 @@ +# Initializing a [REXCPM](http://bitchin100.com/wiki/index.php?title=REXCPM) +REXCPM is a pure RAM device. It has no flash or eeprom, and so has to be loaded with software when first installed, or any time the 100's internal memory battery dies all the way, or if the REXCPM is removed from the 100 for more than a minute or so. + +## 1: Download and unzip the following all into one directory: +* REXCPMV21_b19.ZIP (or whatever is the latest version at the time) +from http://bitchin100.com/wiki/index.php?title=REXCPM#Software + +* M100_OPTION_ROMS.zip (or T200_ or NEC_) +from http://bitchin100.com/wiki/index.php?title=REXCPM#Option_ROM_Images_for_Download + +* CPMUPD.CO and CPM210.BK (or CPM410.BK if you have a 4 Meg unit) +from http://bitchin100.com/wiki/index.php?title=M100_CP/M + +``` +$ mkdir rexcpm +$ cd rexcpm +$ wget http://bitchin100.com/wiki/images/0/03/REXCPMV21_b19.ZIP +$ wget http://bitchin100.com/wiki/images/6/63/M100_OPTION_ROMS.zip +$ wget http://bitchin100.com/wiki/images/8/8e/CPMUPD.CO +$ wget http://bitchin100.com/wiki/images/9/9d/Cpm210.bk +$ unzip REXCPMV21_b19.ZIP +$ unzip M100_OPTION_ROMS.zip +``` + +## 2: bootstrap rxcini.DO and then start the tpdd server with the upcase option. + +``` +$ dl -vb rxcini.DO && dl -vu +``` +Enter BASIC on the 100 and type `RUN "COM:98N1ENN"` \[Enter\] +Press Enter on the modern machine. +``` +Load REXCPM code? Yes +Init REXCPM dir? Yes +Execute choices.. sure? (y/n) Y +Name: RXC_12 +``` +"Name:" is the basename portion of `RXC_*.BR` from the REXCPM\*.ZIP file. +The \* part may change over time, and you don't type the .BR part. +Currently in REXCPMV21_b19.ZIP this is `RXC_12.BR`, so you enter `RXC_12` at that prompt. + +When rxcini finishes: +Turn the 100 off and back on (electrically triggers the REXCPM into a default state) +Hard-reset the 100: CTRL+BREAK+RESET (frees the ram used by rxcini) +Enter BASIC and type: `CALL 63012` \[Enter\] (installs RXCMGR from the REXCPM to the 100's main menu) + +### At this point: +* dl will be left running in tpdd server mode on the modern machine. Leave that running while performing all the following actions on the 100. +* the 100 is at the main menu +* there is a `RXCMGR` entry on the main menu + +The REXCPM now has it's basic firmware installed which provides the same REX functionality as [REX#](http://bitchin100.com/wiki/index.php?title=REXsharp) or [REX Classic](http://tandy.wiki/REX). You can use RXCMGR to load option rom images from TPDD, select & activate installed rom images, create and restore ram backup images. + +What is not done yet: +* TS-DOS option rom not installed yet +* CP/M not installed yet + +## 3: Install the TS-DOS option rom image +Run RXCMGR +Press TAB to get to the ROM screen +Press F2 (Load) +Loading from image filename: TSD100 +Press Enter again after the TS-DOS image finishes loading, to activate the TS-DOS rom. (Tis also launches TS-DOS as if you had selected it from the main menu, so at this point you are out of RXCMGR and in the TS-DOS program. It looks very similar to the 100's main menu, because it's the same RAM file list.) + +## 4: Use TS-DOS to copy the CP/M installer onto the 100 +The end of the previous step launched TS-DOS so you should already be sitting in TS-DOS right now. +Press F4 to switch from the RAM file list to the DISK file list. +Use the arrow keys to highlight CPMUPD.CO +Press F1, Enter (don't type anything after "Load as: ") +Press F8 to exit TS-DOS and return to the main menu + +## 5: run the CP/M installer +Run BASIC +Type: `CLEAR 0,60000` \[enter\] +Press F8 to return to the main menu +Run CPMUPD.CO +Enter file name: CPM210.BK (or CPM410.BK if you have a 4M unit) +Are you sure? (y/n) Y + +## Done +REXCPM is now fully installed with both the REX and CP/M parts. +Enter CP/M by pressing CTRL+C at the main menu. +Return from CP/M to the normal main menu by pressing F8. diff --git a/ref/Sardine.md b/ref/Sardine.md new file mode 100644 index 0000000..7019893 --- /dev/null +++ b/ref/Sardine.md @@ -0,0 +1,36 @@ +#Using Sardine with dl2 and a disk image of the dictionary disk + +Reference: [Manual for Ultimate ROM II](http://www.club100.org/library/librom.html): + +One way to use Sardine is to let Ultimate ROM II load & unload the program from disk into ram on the fly instead of installing permanently in ram. Sardine uses raw sector access commands to read a special dictionary data disk. +For this to work, UR2 has to be able to load `SAR100.CO` from a normal filesystem disk using normal file/filesystem access, and then `SAR100.CO` needs to be able to use TPDD1 FDC-mode commands to read raw sectors from the special dictionary data disk. + +dl2 supports this by both the **magic files** and **disk image file** features. +`SAR100.CO` is one of the bundled "magic files", so when UR2 tries to load a file by that name, it always works even if there is no such file in the share path. +An image of the dictionary disk is also bundled with dl2, and the **-i file.pdd1** option allows SAR100.CO to use raw sector-access commands on the disk image. + + +1: Run dl with the following commandline arguments, +``` +$ dl -i Sardine_American_English.pdd1 +``` + +This tells dl2 to emulate a TPDD1 and use the Sardine American English dictionary disk image file for any sector-access commands. +Both `SAR100.CO` and `Sardine_American_English.pdd1` are bundled with dl2, installed in /usr/local/lib/dl, so you don't have to do anything for SAR100.CO, and for the disk image you don't have to specify the full path. + +At present, [Sardine is uniquely picky about the usb-serial adapter](https://github.com/bkw777/dl2/issues/9). Even ensuring you have a real FTDI chip isn't good enough, but certain adapters work and others don't. +Any adapter works for ordinary file transfers, even big random binary files that fill the entire disk copy without a hitch. It's only Sardine that has a problem. +Currently the safe bet is Startech ICUSB232C +The fault is probably really a timing issue in dl2, that may eventually be fixable to work with more hardware. But at present, it may or may not work depending on your serial port hardware. + +2: Enter the UR-2 menu. +Notice the "SARDIN" entry with the word "OFF" under it. +Hit enter on SARDIN. +If you get a prompt about HIMEM, answer Y. +This loads SAR100.CO into ram. +Now notice the SARDIN entry changed from "OFF" to "ON" under it. + +3: Enter T-Word and start a new document and type some text. + +4: Press GRPH+F to invoke Sardine to spell-check the document. +This will invoke SAR100.CO, which will try to use TPDD1 FDC-mode sector access commands, wich dl2 will respond to with data from the .pdd1 file. diff --git a/ref/advanced_options.txt b/ref/advanced_options.txt new file mode 100644 index 0000000..69db393 --- /dev/null +++ b/ref/advanced_options.txt @@ -0,0 +1,367 @@ +Some special features & controls, and experiments. + +Aside from the options displayed by the help options -h, -c, -b, & -i, +there are also some debugging and special options and some environment +variables. + +Command-line stwitches override env variables, +and later switches override earlier switches. + +PROFILE is processed before other variables, so that the other +variables can override individual settings within a profile. + +Commandline switches: + + -^ Config dump. This just displays several config options and exits. + If used with -vv, then it shows even more info, + including the internal results of all environment variables. + + This is a way to verify what config options are actually in effect. + + Build a command line with both variables and switches, and put -^ + as the very last argument, and the dump will show the final result + of all the combined settings which may override each other, etc. + +Environment variables: + +variable data equivalent (default description if no +name type cli switch value) equivalent in -h help + +FDC_MODE bool true = -f (false) +PROFILE str -c str (k85) +ATTR chr -a chr (F) +DME bool -e bool (true for k85) +TSLOAD bool (true for k85) enable magic files +TILDES bool -~ bool (true) +CLIENT_TTY str -d str (/dev/ttyUSB* for linux) +BAUD # -s # (19200) +RTSCTS bool -r bool (false) +ROOT_LABEL str ("0: ") +PARENT_LABEL str ("^ ") +DIR_LABEL str ("<>") +XATTR_NAME str ("pdd.attr" w/ platform-specific prefix/suffix) + +str = a string +chr = a single character +# = an integer +bool = case-insensitive on/off true/false yes/no 0/1 t/f y/n + +To use these, just put them in the environment. The simplest is just type +them on the same command line before the executable name. + +ROOT & PARENT are padded or truncated as needed to exactly 6 bytes, +so you can give a short value without quotes + +Example: + + $ ROOT_LABEL=/ PARENT_LABEL=^ dl + + +ROOT_LABEL="0:" + examples: + 'ROOT' + '/' + '0:' + '-root-' + ' ROOT ' + '_ROOT_' + + The original Desk-Link, and so, most other emulators also, + puts the string "ROOT " in the top-right corner of the TS-DOS + display when the user is in the top level share directory. + + This variable lets you put whatever you want there. + + The field in TS-DOS is exactly 6 characters. + + Shorter than 6 bytes will be left-justified. + Longer than 6 bytes will be truncated. + + If you want a shorter string to be centered, + use quotes and include the spaces. + +PARENT_LABEL="^" + examples: + 'PARENT' + '^' + '^:' + ' up ' + '__up__' + '-back-' + 'UP:' + + Similar to ROOT_LABEL, Desk-Link used the word "PARENT" for the + the "go back up one level" directory entry in the top-left file + slot in the directory listing screen. + This was Desk-Links equivalent to ".." + + This variable lets you put whatever you want there instead of + "PARENT", with limitations. Unfortunately, since this appears + in the file list like any other file, and TS-DOS is picky about + what it considers a "bad" filename, and you can't put just + anything here. Most notably, you can't use "..", because normally + "." would be an illegal character in a Model 100 filename except + the one in the 7th byte. + + This is a TS-DOS limitation. You CAN actually use "..", in that dl2 + will send it to TS-DOS, and TS-DOS will even display it properly in + the file listing, but if you try to select it to move back up a + directory, TS-DOS just beeps and says "bad filename". The error is not + dl2 rejectimng the command, TS-DOS rejects the filename and never tries + to send any command. + + However a lot of other things do work fine. Generally, for something + to work, it has to be left-justified like a real filename would be. + It takes some trial & error to figure out what works. + +ATTR="F" + examples: 'F', ' ', any single byte + + A real TPDD drive has a one-byte field along with every filename. + + It's just a text field that the drive can use to return a group of + matching files all together before the rest during file listings. + It's not really used by any software I know of, but the field is + there, and when a client gives a dirent() command, it must include + the field with some value in it, and if trying to access an existing + file on disk, the value must match the value on disk. + + Floppy and TS-DOS and all other KC-85 platform tpdd client software + always writes an 'F' there, hard-coded for every file, reading or writing, + and does not display the field to the user or give any way to change it. + This essentially no-ops the field. + + When an emulator is serving files, usually there is no actual attribute + byte stored with the file, so all emulators just fabricate an F there for + every file when they give filenames to clients. + + But a real drive lets you put anything you want in that field, and actually + matches the value in a dirent() request from a client against the values + in the directory entries on the disk. + + All this setting does is, when the client requests a file or directory + listing, and we're sending a filename and filesize to the client, it uses + the specified value instead of the built-in default 'F' for the attribute byte. + + Who/what ever cares? Who knows. It's possible the Cambridge Z88 doesn't + write 'F' in the attr field the way Floppy does, and so for those machines + maybe you may need to specify ' ' or something else. + Possibly also the Brother knitting machines used the field for actual file + classification/grouping and wrote various values there. + + pdd.sh exposes the field and let's you see it and write whatever + you want in it and search whatever you want in it. + + PROFILE=k85 + PROFILE=8.3 + PROFILE=8.2p + Same as command line switch "-c" + + PROFILE=name + -c name use profile - (default: "k85") + + PROFILE=#.# + -c #.# "raw", truncated but not padded to #.#, attr=F + + profile=#.#p + -c #.#p "raw", truncated and padded to #.#, attr=F + + Profiles taylor the translation between local filenames and TPDD filenames. + + A real TPDD doesn't care what's in the filename, and emulating a TPDD + doesn't require any translation other than truncating to 24 bytes. + + But most TPDD clients write filenames to TPDD drives in specific formats, + and we need to translate filenames between the local and client formats. + + Strictly speaking, "raw" always works for any and all clients, + from the clients point of view. It still emulates a real drive exactly. + + The only reason for any compatibility profile is for more convenient + local filenames. When TS-DOS saves a file like "A.BA", it actually + writes "A .BA" to a real drive. In "raw" mode this would create a + local file named verbatim: "A .BA", which is legal but inconvenient. + And TS-DOS does not recognize any disk files that don't conform + to the "k85" profile below. (fixed-length, space-padded, 6.2) + + "raw" still "works" because TS-DOS can both create any files it + wants, and access any files it created, identical to a real drive. + + Profiles just make it so that a local file named "my_long_file_name.text" + appears to TS-DOS as "my_lo~.t~", which may be ugly but TS-DOS can use it. + And when TS-DOS tries to read or write a file named "FOO .CO", + we use "FOO.CO" for the local filename. + + Most of the parameters in a profile also have individual commandline flags. + Example: "-c k85" is short for "-c 6.2p -a F -e on" + (except k85 is the default so you don't need to use any of those) + + The #.# syntax is a way to specify any other filename pattern you might need + that isn't covered by one of the built-in profiles below. + + If the number after the dot is >0 then the translated filename will have only + one dot. The last dot in the original name will be taken as "the" dot. + Everything before the last dot will be truncated down to the basename length, + and any dots substituted with "_". Everything after the last dot will be + truncated down to the ext length. + + To specify a simple max length without limiting or modifying dots, use #.0 + + Examples: + -c cpm "abc.txt" -> "abc.txt" + -c 8.3 "my.long.file.name.shtml" -> "my_long~.sh~" + -c 8.2p "abc.txt" -> "abc .t~" + -c 14.0 "my.long.file.name.shtml" -> "my.long.file.~" + -c 6.2p -u "abc.co" -> "ABC .CO" + + or equivalent: + PROFILE=cpm "abc.txt" -> "abc.txt" + PROFILE=8.3 "my.long.file.name.shtml" -> "my_long~.sh~" + PROFILE=8.2p "abc.txt" -> "abc .t~" + PROFILE=14.0 "my.long.file.name.shtml" -> "my.long.file.~" + PROFILE=6.2p UPCASE=on "abc.co" -> "ABC .CO" + PROFILE=k85 "abc.co" -> "ABC .CO" + + The default is the same as: PROFILE=k85 + which is also the same as: PROFILE=6.2p UPCASE=on ATTR=F DME=on TSLOAD=on TILDES=on + + The default "k85" matches all KC-85-clone platform clients. Examples: + Floppy, TS-DOS, DSKMGR, TEENY, etc, on TRS-80 Model 100, NEC PC-8201a, etc. + + NAME profile name + BASE basename length + EXT extension length + PAD fixed-length space-padded + ATTR default attribute byte if no xattr + DME enable TS-DOS directory mode extension + TSLOAD enable "magic files" (ex: DOS100.CO) for TSLOAD / Ultimate ROM II + UPCASE translate filenames to all uppercase + + Available profiles: + + NAME BASE EXT PAD ATTR DME TSLOAD UPCASE + ------------------------------------------------------------- + raw 0 0 off ' ' off off off + k85 6 2 on 'F' on on on + wp2 8 2 on 'F' off off off + cpm 8 3 off 'F' off off off + rexcpm 6 2 on 'F' off off on + z88 12 3 off 'F' off off off + st 6 2 on 'F' off off off + +FDC_MODE=false + default false + commandline switch -f (takes no argument) is the same as FDC_MODE=true + + A TPDD1 / FB-100 drive has two main modes of operation called + Operation-mode and FDC-mode. + + These are basically two entire different sets of commands to + use the drive. + + A real drive has dip switches that let you set either mode to be the + default mode at power-on. + + This setting lets you emulate the dip switch settings for starting up + in FDC-mode instead of Operation-mode. + + Default is FDC_MODE=false, meaning dl2 starts up in Operation-mode. + This matches the TPDD1 default dip switch settings, and the hard-wired + solder-blob settings on Brother FB-100, KnitKing FDD19, Purple Computing D103, + and matches the TPDD2's only mode (tpdd2 has no FDC mode). + + Regardless which mode is the default at start-up, the drive (and dl2) still + responds to the commands to switch between modes, and at least some clients + like TS-DOS always send a little set/reset sequence to ensure the drive gets put + into Operation-mode before it tries to use any other Operation-mode commands. + +BAUD=19200 + valid values: + 75 model-t supports, drive does not + 110 model-t supports, drive does not + 150 drive supports, model-t does not + 300 + 600 + 1200 + 2400 + 4800 + 9600 default Brother FB-100, KnitKing FDD19, Purple Computing D103 + 19200 default TANDY 26-3808, TANDY 26-3814 + 38400 + 76800 drive supports, host platform usually does not + + Set the baud rate. Same as the "-s" commandline flag. + Default is 19200. + + The TPDD1 has dip switches to support several different baud rates from + 75 to 76800. The default setting from the factory and in the user manual + is 19200. + + The Brother FB-100 and re-badges like Purple Computing D103 have a set of + solder-jumper pads instead of dip switches. They support all the same baud + rates as TPDD1 but require soldering the jumper pads or installing a dip + switch to change the baud rate. From the factory they are all hard-wired + to run at 9600 baud with a solder blob on one of the dip switch positions. + + The TPDD2 has no dip switches or other means to set a different baud rate, + and only runs at 19200 baud. + + Most tpdd client software just tries to run at 19200. + + Although the drive has a dip switch setting for 76800 baud, that is a weird + rate that a typical linux/bsd machine today does not natively support. + But some platforms like linux on Sparc do (apparently) support this baud + rate. So the option is there in dl2 but isn't available unless you compiled + on a system that supports it. + +CLIENT_TTY=ttyUSB0 + examples: ttyUSB0, ttyS2, cu.usbserial-AL03RAXP + + The device name of the tty that the client is connected to, + with or without the leading "/dev/". + Same as the "-d" commandline flag. + + For osx/macos always use the "/dev/cu.*" interface and not the + "/dev/tty.*" interface to the same device. The cu.* interface + provides exclusive access to the port and avoids data corruption. + +DME=true + Enable/Disable TS-DOS directory support. + Default depends on the PROFILE. + Default profile is k85 which has DME enabled. + Use this to override whatever the profile has set. + + TS-DOS/Desk-Link directory mode extensions technically violate the tpdd spec, + which could theoretically break some legitimate tpdd client software that + is playing by the rules, and just happens to do the same thing as what TS-DOS + does for a "DME request". + + The only things likely to break would be things that switch between + Operation-mode and FDC-mode on TPDD1, and the only things found so far that + use FDC-mode at all are Sardine and the installer for Disk-Power, and both + of those are working on dl2 without needing this option. + +TSLOAD=true + Enable/Disable the "magic files" feature. + Default depends on PROFILE + Default profile is k85, which has TSLOAD enabled + Use this to override whatever the profile has set. + + In case you specifically do not want the special filenames like "DOS100.CO" + to be recognized and work by magic even if there is no file by that name. + +TILDES=true + Enable/Disable indicating truncated filenames with a trailing "~" + Default is true + +XATTR_NAME=pdd.attr + + If compiled with -DUSE_XATTR, then this environment variable can be used + to specify a different xattr name than the default "pdd.attr" for + storing and retrieving the ATTR byte for each file. + + Default depends on the platform. + Default is "pdd.attr", with platform-specific prefixes or suffixes added. + linux: "user.pdd.attr" + mac: "pdd.attr#S" + freebsd: "pdd.attr" in EXTATTR_NAMESPACE_USER diff --git a/ref/attribute.txt b/ref/attribute.txt new file mode 100644 index 0000000..0036a68 --- /dev/null +++ b/ref/attribute.txt @@ -0,0 +1,37 @@ + +File Attribute byte + +https://archive.org/details/tandy-service-manual-26-3808-s-software-manual-for-portable-disk-drive/page/23/mode/1up + +Always "F" for any KC-85 platform clients. + +This "F" is really just a convention of "Floppy", the original client for +Model 100 & 200 that shipped with the TPDD, and subsequently other KC-85 +platform clients that wanted to be fully compatible with Floppy. (TS-DOS, etc) + +The field is just a single letter simple text search field into which a client +may write any single letter, and later select files by that letter. + +The only effect it has is the order that filenames are returned by the drive. +Matching files, if any, are returned first. That's it. As a client you can ask +the drive "give me the E files" and stop doing get-next once you get a non-E. + +Floppy always writes "F" for every file. And since the other KC-85 platform +clients wanted to be as compatible as possible, some or all of those do too. + +Because of that, when a real drive reads a disk with files created by Floppy +or TS-DOS etc, every file on the disk will have an F. + +And so we basically lie and say that we read an F off the disk for every file. + +However there are other clients and other platforms that could possibly use +this field for real. + +The most technically correct, client-agnostic, faithful drive emulating way +to handle this field would be to save whatever a client supplies when a client +creates a file, and reproduce that later when a client asks for file listings, +(And perform the same directory list re-ordering that the drive firmware does. + Just make 2 passes through the directory table, once returning the matches, + then the rest.), and only fabricate data based on a configurable user setting, + and when there is no actual data previously saved from a client. + IE, a "tandy compat" config option to supply F (or other) by default. diff --git a/ref/baud_linux.c b/ref/baud_linux.c new file mode 100644 index 0000000..ca8651a --- /dev/null +++ b/ref/baud_linux.c @@ -0,0 +1,72 @@ +// The TPDD1 drive has a dip switch setting for 76800 baud. +// 76800 is a native rate on some odd or old platforms like sparc, +// but is weird and not directly/natively supported on others. +// On a typical linux on intel it requires termios2 and BOTHER +// It will probably need several #ifdefs to support different platforms. + +// https://stackoverflow.com/a/39924923/5754855 +/* + struct termios2 t; + ioctl(fd, TCGETS2, &t); // Read current settings + t.c_cflag &= ~CBAUD; // Remove current baud rate + t.c_cflag |= BOTHER; // Allow arbitrary int baud rate + t.c_ispeed = 76800; // Set the input baud rate (int) + t.c_ospeed = 76800; // Set the output baud rate (int) + ioctl(fd, TCSETS2, &t); // Apply new settings +*/ + +/* set weird baud rates on linux + * + * cc -o baud baud.c + * baud /dev/ttyUSB0 74880 + * picocom --noinit /dev/ttyUSB0 + * + * http://cholla.mmto.org/esp8266/weird_baud/ + */ + +#define DEFAULT_DEVICE "/dev/ttyUSB0" +#define DEFAULT_BAUD 76800 + +#include +#include +#include +#include +#include +#include +#include + +int ioctl (int,int,struct termios2 *); + +void set_bother (int fd,int baud) { + struct termios2 tio; + int x; + + x = ioctl(fd,TCGETS2,&tio); + // printf ("x = %d\n",x); + + tio.c_cflag &= ~CBAUD; + tio.c_cflag |= BOTHER; + tio.c_ispeed = baud; + tio.c_ospeed = baud; + x = ioctl(fd,TCSETS2,&tio); + // printf ("x = %d\n",x); +} + +int main (int argc,char **argv) +{ + char *device = DEFAULT_DEVICE; + int baud = DEFAULT_BAUD; + int fd; + + if (argc==2) device = argv[1]; + if (argc>2) baud = atoi(argv[2]); + + fd = open(device,O_RDWR); + if (fd<0) { + printf("Sorry, cannot open %s\n",device); + return 1; + } + + set_bother(fd,baud); + printf("Baud rate for %s set to %d\n",device,baud); +} diff --git a/bootstrap.txt b/ref/bootstrap.txt similarity index 73% rename from bootstrap.txt rename to ref/bootstrap.txt index fd08a36..9ec0828 100644 --- a/bootstrap.txt +++ b/ref/bootstrap.txt @@ -18,3 +18,7 @@ Olivetti M-10: Aditionally, 100 & 102 also accept "COM:98N1EN" All but NEC also accept "COM:98N1ENN" + +Thus, two commands cover all KC-85 platfom machines: +RUN "COM:98N1ENN" <-- TANDY, Olivetti, Kyotronic +RUN "COM:9N81XN" <-- NEC diff --git a/ref/disk_image_files.txt b/ref/disk_image_files.txt new file mode 100644 index 0000000..e238c7f --- /dev/null +++ b/ref/disk_image_files.txt @@ -0,0 +1,82 @@ +Disk Image Files + +The -i option takes a filename of a disk image file. + +If the file does not already exist, and the client issues a format command, +the file will be created and filled with nulls and sector headers. + +A *.pdd1 TPDD1 disk image file is composed of 80 records. +Each record is composed of 3 fields: Logical Size Code, ID, DATA + + +--------+----------+---------------------------------------+ + | LSC | ID | DATA | + +--------+----------+---------------------------------------+ + | 1 byte | 12 bytes | 1280 bytes | + +--------+----------+---------------------------------------+ + +A *.pdd2 TPDD2 disk image file is composed of 160 records. +Each record is composed of 3 fields: ID, unknown, DATA + + +----------+------------------------------------------------+ + | ID | ? | DATA | + +----------+--------+---------------------------------------+ + | 12 bytes | 1 byte | 1280 bytes | + +----------+--------+---------------------------------------+ + +for tpdd2 these correspond to these ram addresses: +ID 0x8004 to 0x800F +? 0x8010 +DATA 0x8013 to 0x8512 + +The "?" byte on tpdd2 is an extra byte that isn't described in the manual, +but exists in the drives ram after the ID section. It's normally just 0x00, +but you can write it in ram, commit to disk, and read it back from disk, +so it's included in the disk image format. + +There are no delimiters or other formatting or header. + +Currently, disk image files are only usable for sector access commands. +Meaning you can not use dl2 to access the files within a disk image, +only read and write the raw sectors. + +Two example uses so far are the dictionary disk for Sardine, +and the install disk for Disk Power KC-85. + +Sardine's dictionary disk is a pure data disk with no filesystem, +and so Sardine uses FDC-mode to access the sectors like a database. + +The install disk for Disk Power is designed to look like a normal disk with a +filesystem, but the installer uses fdc-mode sector access to read sectret data +from some sectors that aren't marked as in-use in the FCB ot SMT tables. + +Generally, you also need to use some extra commandline options to disable +TS-DOS directory extensions support (-e off), and to force stricter TPDD1-only +or TPDD2-only emulation (-m 1) or (-m 2). Example: + +$ dl -v -e off -m 1 -i Sardine_American_English.pdd1 + +That isn't required to use disk images in general, it's just that in both of +these real examples, the software involved happens to do things that trigger +responses from an emulator that a real drive wouldn't do, so the extra options +just turn off features and make the server act more strictly like a real drive. + +Disk image files can be created with https://github.co/bkw777/pdd.sh + +dl2 can also create new disk images from client-issued format and sector +access commands. Example: run "$ dl -vv -e off -m 1 -i ./newdisk.pdd1" +and then use a client (like TS-DOS) to "format" the "disk". + +For TPDD1, both the FDC-mode the Operation-mode format commands do what a real +drive does. The FDC-mode format just generates all nulls and the one logical +size code byte per record, which creates a raw data disk with no filesystem, +like the Sardine dictionary disk. The Operation-mode format command does the +same as FDC-mode format with logical size code 0 (64-byte logical sectors) and +then writes the single byte of non-null data at the right address to create a +valid Space Management Table, which makes the disk into a functioning filesystem +disk. If you took the generated .pdd1 or .pdd2 file and used pdd.sh to "restore" +it to a real disk, that disk would be a functioning filesystem disk. + +Real disks can only be dumped or restored using the matching type of drive. +Although a real TPDD2 drive can at least read a TPDD1 disk, you must use a +TPDD1 drive to dump a TPDD1 disk to a .pdd1 image file, or to restore a .pdd1 +image file to a real disk, and a TPDD2 drive for .pdd2 files. diff --git a/ref/dme.txt b/ref/dme.txt new file mode 100644 index 0000000..d1c470a --- /dev/null +++ b/ref/dme.txt @@ -0,0 +1,80 @@ +The DMEReq sequence works like this: + +DMEReq is a mutual-recognition scheme that both TS-DOS and Desk-Link +(the original commercial Desk-Link for MS-DOS by Travelling Software Inc, +the same who also made TS-DOS) use to recognize each other, +while using only "legal" TPDD commands and responses on both sides. + +* TS-DOS has to detect if it is talking to Desk-Link + without confusing a real drive or any other server. + +* Desk-Link has to detect if it is talking to TS-DOS + without confusing FLOPPY or any other client. + +The total sequence that TS-DOS sends is: + 4D 31 0D 5A 5A 08 00 F7 0D + aka + M 1 \r Z Z [] [] [] \r + +This breaks down as a sequence of 3 things: + +1) 3 bytes "M1\r" is the FDC-mode command to switch to Operation-mode. + + If a real drive drive was already in Operation-mode, then these bytes + have no effect, as a drive in Operation-mode is reading bytes and silently + ignoring everything until it gets two 0x5A (Z) in a row. + + If a real drive was in FDC-mode, then this is just the valid FDC-mode command + to switch to Operation-mode. This command does not send back any response. + + Either way, the command was safe to blindly send to the drive, and + either way, you know the drive is not stuck waiting for you to read + anything from it, and you know the drive should now be in Operation-mode + instead of the unknown initial state. + +2) 5 bytes "ZZ" 08 00 F7 is the Operation-mode command to switch to FDC-mode. + + The drive should definitely be in Operation-mode now, and this is just a + normal Operation-mode command, so this command should be processed normally. + + This command also does not send back any kind of response or result back + to the client. The drive is simply now no longer scanning for "ZZ" but is + instead reading command lines that end with "\r". + +3) 1 byte "\r" is the terminator for FDC-mode commands. + + The drive is in FDC-mode, and while in FDC-mode a lone "\r" is essentially + an empty command. + + A real drive sends back a standard FDC-mode response block containing the + result code that means "invalid command" + +TS-DOS sends the above sequence twice in a row. Some versions actually +send 3 times in a row with no trailing \r on the 3rd time. + +dl2 responds like a real drive the first time through, so as to +be compatible with FLOPPY and other standard clients. On the 2nd time through, +dl2 concludes the client is TS-DOS requesting directory extensions. + +In that case, on the 2nd FDC request, instead of actually switching to +FDC-mode and sending back an "invalid command" response, dl2 stays in +Operation-mode, and sends back a DME packet. A DME response packet is +technically an illegal or malformed Operation-mode return packet +with format code 0x12 but 11 bytes of payload data instead of 1. +Return format 0x12 has only one valid payload size which is 1 byte, +not 11 or any other value. A real drive can never send a return packet +like this, so this helps TS-DOS verify that it is receiving a DME packet +instead of an actual 0x12 packet. + +The 11 byte payload is: +0x00 0x## 0x## 0x## 0x## 0x## 0x## 0x2E 0x3C 0x3E 0x20 +NULL ABCDEF.<> SPACE + +The full packet is: +0x12 0x0B 11_bytes checksum + +dl2 then rembers that there is a dme request in progress until it gets a +dirent(get-first) command. The dme-in-progress flag tells dirent() to include +the directory names in the file list it generates. Then the flag is reset +at the end of the directory listing process, so that legit switch-to-FDC-mode +commands will still work. diff --git a/ref/fdc.txt b/ref/fdc.txt new file mode 100644 index 0000000..a0e90f7 --- /dev/null +++ b/ref/fdc.txt @@ -0,0 +1,38 @@ + FDC-mode transaction format + Commands & parameters are all printable ascii, terminated by 0x0D + + client sends: C [ ] [P[,P]...] CR + + C = command letter, ascii letter + [ ] = optional space between command letter and first parameter + P = zero or more parameters, integer decimal values in ascii, comma seperated + CR = carriage return (0x0D) + + Values are in plain text ascii. Example, a parameter value of 0 is written as 0, + ie 0x30, not a 0x00 byte like in Operation-mode. + + server sends: EEDDAABB + + 8 bytes: 4 ascii hex pairs representing 4 byte values + or in some cases 2 byte values and a 2-byte value + + the different pairs have different meanings depending on the command + being responded to, but generally: + + 1st pair EE is always the error status, an 8-bit integer error code + + 2nd pair DD is usually result data, an 8-bit integer result data + For some commands this byte is actually 8 individual bit flags. + + 3rd & 4th pairs AA BB are a single 16-bit integer length value. + + Some fdc commands have one or more send-and-receive after that. + For the multi-transaction commands, if the status response to the + first part was not error, then the second (3rd,...)part, depending on + the type of command: + + client sends: up to 128 bytes data for a sector write + server sends: another standard 8-byte response as above + or + client sends: single carriage-return + server sends: up to 128 bytes data from a sector read diff --git a/ref/freebsd.md b/ref/freebsd.md new file mode 100644 index 0000000..4f03bee --- /dev/null +++ b/ref/freebsd.md @@ -0,0 +1,33 @@ +# Notes for FreeBSD + +## Building + +* Use gmake instead of make + +## TTY Permissions + +* Create the file: +```/usr/local/etc/devd/usbserial.conf``` + +``` +notify 100 { + match "system" "DEVFS"; + match "subsystem" "CDEV"; + match "type" "CREATE"; + match "cdev" "ttyU[0-9]+"; + action "chgrp dialer /dev/$cdev ;chmod g+rw /dev/$cdev"; +}; +``` + +* Restart the devd service: +```# service devd restart``` + +* Add yourself to the dialer group (replace "bkw" with your login name): +```# pw group mod dialer -m bkw``` + +* Re-plug the usb-serial adapter. + +## Behavior Quirks + +Hangs at opening the client tty if the client machine is not connected yet. +Proceeds as soon as the client machine is connected. diff --git a/ref/mac.md b/ref/mac.md new file mode 100644 index 0000000..f8dd4e2 --- /dev/null +++ b/ref/mac.md @@ -0,0 +1,20 @@ +# Notes for Macos / OSX + +## TTY Device +There is no predictable likely default tty device name like `ttyUSB0` on Linux, so there can be no default built-in, so you must always supply the tty device name via the commandline or the environment variable CLIENT_TTY. + +Each serial tty device has two interfaces, a `/dev/tty.foo` and a `/dev/cu.foo` +Either one usually works, but you always want to use the cu.\* version for this simply because it guarantees the process has exclusive access to the device while it's open (No other process can corrupt the data). + +There is usually at least one bluetooth device that shows up as a serial device, so you need to ignore those. + +`$ ls /dev/cu.* |grep -v Bluetooth` + +Or just start writing the command and use tab-completion to show the cu.* devices and pick one: + +``` +$ dl -v -u /dev/cu. +cu.Bluetooth-Incoming-Port cu.usbserial-AL03RAXP +$ dl -v -u /dev/cu.usbserial-AL03RAXP +... +``` diff --git a/ref/pdd2_ram.txt b/ref/pdd2_ram.txt new file mode 100644 index 0000000..28cf90e --- /dev/null +++ b/ref/pdd2_ram.txt @@ -0,0 +1,40 @@ +TPDD2 2k ram map + +pick a random disk sctor, say 68 + +read sector 68 from disk into cache +sector 68 is track 34 sector 0 +PDD(pdd2[0]:6.2,F)> cache 34 0 0 + +read the entire 2k external ram +PDD(pdd2[0]:6.2,F)> mem_read 1 0x8000 0x800 + +2k ram includes both the 1280 byte DATA and the 12-byte ID section +ram starts at 0x8000 +ID starts at 0x8004 +DATA aka Sector Cache starts at 0x8013 + +8000: 05 length MSB always 0x0513 = 1299 +8001: 13 length LSB +8002: 44 sector number 0x44 = 68 = track 34 sector 0 +8003: 00 side# or logical size code? always 00 +8004: 96 start of ID +8005: FF +8006: 46 +8007: 75 end of 4-byte ID from BACKUP.BA +8008: 00 +8009: 00 +800A: 00 +800B: 00 +800C: 00 +800D: 00 +800E: 00 +800F: 00 end of full 12-byte ID +8010: 00 logical size code? always 00 but can be written and read back from disk. +8011: EC ??? maybe id-crc msb changes when id data changes & saved & reloaded from disk +8012: 8E ??? maybe id-crc lsb +8013: start of DATA +... +8512: end of DATA +... +87FF: end of 2k ram diff --git a/ref/search_id_section.txt b/ref/search_id_section.txt new file mode 100644 index 0000000..305762b --- /dev/null +++ b/ref/search_id_section.txt @@ -0,0 +1,88 @@ +TPDD1 FDC-mode command "S" - Search ID Section +Page 38 of the software manual +https://archive.org/details/tandy-service-manual-26-3808-s-software-manual-for-portable-disk-drive/page/38/ + +This is an odd command that isn't actually used by any of the main TPDD clients +or applications as far as I've seen yet. All the "DOS"s (TS-DOS, DSKMGR, TEENY, Floppy, etc) +use only "Operation-mode" (aka file-access) commands. +The only applications I know of so far that use FDC-mode (aka sector-access) commands +are Sardine and the installer for Disk-Power, and neither of those uses this command. + +So there is no client software I know of to test if we are emulating this function well enough. +What we do have is, pdd.sh was used to probe out what a real drive does, +and then both pdd.sh and dl2 do that. +So *maybe* we are emulating the real drives "S" command more or less? + +The software manual explains almost nothing about this command. It only says: + + "The procedure is similar to that for the Write Sector command, + except that you send a search string instead of data to be written." + +Which is not only insufficient but also not as correct as it could be. + +The process is actually more like the Write ID Section command. + +The process goes like this (as far as I can tell): + + 0 - client sends the Operation-mode command for "switch to FDC-mode" + 0x5A 0x5A 0x08 0x00 0xDC + (ZZ 0x08 NULL checksum) + + "Search ID Section" is an FDC-mode command, and a real drive always starts + in Operation-mode at power-on by default. So if not done already, + the drive must first be switched from Operation-mode to FDC-mode before + you can issue any FDC-mode commands. + + https://archive.org/details/tandy-service-manual-26-3808-s-software-manual-for-portable-disk-drive/page/21/ + + The drive does not send back any response to this command. + The drive is simply now in FDC-mode, waiting to accept FDC-mode commands instead of Operation-mode commands. + + 1 - client sends 0x53 0x0D + (S carriage-return) + + This command takes no parameters after the S. + dl2 silently ignores any extra characters, + but a real drive gives error 0x61 if there are any extra bytes after the S. + + 2 - drive sends a standard FDC-mode response block(a) + (8 bytes, all ascii "0"s if there was no hardware fault or other error) + + If the command was valid and no hardware problem: err:0 dat:0 len:0 + (0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 = "00000000" = err:0x00 = success) + + If the command was malformed: err:0x61 dat:0 len:0 + (0x36 0x31 0x30 0x30 0x30 0x30 0x30 0x30 = "61000000" = err:0x61 = error) + + client parses the response block + + if err>0, abort operation + drive returns to waiting for new commands + client should not send or expect anything more for this operation + otherwise, if err==0, proceed... + + 3 - client sends exactly 12 bytes of data + no less or more + right-pad with 0x00 or truncate to 12 bytes as necessary + to search for "abc" send 0x61 0x62 0x63 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + + drive is busy for up to 20 seconds while searching up to 80 IDs for a match + drive does not send anything during this time + client must not send anything during this time + wait at least 20 seconds and abort if no response by 30 seconds + + 4 - drive sends another FDC-mode response block + If a match was found: err:0x00 dat:sector-number len:logical-sector-size-bytes + If a match was not found: err:0x60 dat:0xFF len:logical-sector-size-bytes + +There seems to be no way to search for multiple sectors with the same ID. +(other than using READ ID and reading them all yourself instead of using SEARCH ID) +The search always only returns the 1st match, and there is no way to tell it to start at some other sector than 0. + +github/bkw777/pdd.sh has an implementation of the client side of this and is working with real TPDD drives. +dl2 has an implementation of the server side of this and is working with pdd.sh at least. + +(a) all FDC-mode commands use this 8-byte response format: + 2 bytes = 1 ascii hex pair representing a uint8_t for error/status code (err:#) 0=success, >0 = various errors & conditions + 2 bytes = 1 ascii hex pair representing a uint8_t for result data (dat:#), meaning depends on command + 4 bytes = 2 ascii hex pairs representing a uint16_t (msb-first) for length or address or offset (len:#), meaning depends on command diff --git a/ref/ur2.txt b/ref/ur2.txt new file mode 100644 index 0000000..f94fd50 --- /dev/null +++ b/ref/ur2.txt @@ -0,0 +1,118 @@ +Special support for the TS-DOS and SARDIN buttons in Ultimate ROM II + +A few "magic" filenames that have the following properties: + +* Names: DOS100.CO, DOS200.CO, DOSNEC.CO, SAR100.CO, SAR200.CO + (1) + +* If client tries to open(read), and file doesn't exist in cwd, silently + succeed anyway and supply contents from the same file from the share root + dir instead. Failing that, get from the app lib dir (/usr/local/lib/dl). + +* Do not include in directory listing unless it actually exists in cwd. + +* Do not supply substitute contents if file actually exists in cwd. + If client tries to load a file that actually exists in cwd, + it should work as normal, not sustitute the contents from some other file. + +X Do not apply the remapping/translation to open(write) or open(append). + If client tries to save a file, it should work as normal, + not overwite the copy in the share root! + (2) + +X DO allow overwriting the share root copy if the client is actually in + the share root dir. Not implemented yet but no problem to. + +* Don't even look if not in "floppy compat" mode (dot_offset=6). + There is no UR-II for CP/M or WP-2 or Z88 any other kind of client. + TS-LOAD, Sardine/SarDOS, and UR-II are the only users of this feature. + +Explaination + +Ultimate ROM II has a "TS-DOS" button that tries to load a file named +"DOS100.CO" on the fly. + +(or DOS200.CO for TANDY Model 200, or DOSNEC.CO for NEC PC-8201/PC-8300) + +UR-II does not know about directories, and does not do anything to try +to cd to the root dir before trying to open the file. + +On a real drive, this works fine. If a disk simply contains a copy of +the file, then the open() works and so the TS-DOS button works. + +On an emulated drive that can cd into sub-directories, it's possible for +the user to launch TS-DOS, cd into a sub-directory that does not contain +a copy of DOS100.CO, and then exit TS-DOS. + +Now the server is chdir'd into a directory that does not contain DOS100.CO, +and the user has exited TS-DOS, which unloaded their temporary copy of +DOS100.CO from ram. If the user tries to use the TS-DOS button again, +the server just gets a request to open "DOS100.CO", which does not exist, +and so the user has no way to load TS-DOS again. + +So this version of dlplus has special support so that whenever one of those +particular filenames are requested, if it isn't found in the current dir +naturally like any other file, then the server silently also looks in the +share root dir, and failing that finally gets it from /usr/local/lib/dl, so +the load always succeeds no matter if the user has CD'd into some subdirectory, +no matter if there is even no copy of DOS100.CO anywhere in the share tree. + +All the above also applies similarly to SAR100.CO/SAR200.CO, +which is loaded by the SARDIN button in UR-II. + +------------------------------------------------------------------------------- + +(1) There are actually more filenames in the list, but the rest don't actually + exist. The rest of the recognized filenames are DOSM10.CO, DOSK85.CO, + SARNEC.CO, SARM10.CO, and SARK85.CO. + + M10.CO & K85.CO would be TS-DOS & Sardine for Olivetti and Kyotronic, + which probably never existed. If they did exist, these are just my + guesses what the filenames would be. Maybe they will turn up some day. + +(2) *Not quite possible* + + The magic files don't get overwritten, so it's only an annoying cosmetic + problem, but there's basically no way around it unless we can detect + something unique about the way UR-II requests DOS100.CO vs any other client. + If we could do that, then we could answer UR-II always with "file exists" + and answer any other client honestly, which would allow a normal client to + create and write to the file. I don't think there is any such tell, but maybe. + + The problem is the tpdd protocol requires the server to say if a file + exists *before* the client says what they intend to do with it. + We don't know that they want to write until it's too late to give + the right response to allow writing. + + The sequence of events when a client accesses a file goes like this: + + Client: I want to do something with a file named "DOS100.CO". + Server: OK. DOS100.CO does not exist. + Client: open for writing. + Server: OK. + Client: Write this data ...... + Server: OK. + + Except in the special case of DOS100.CO, that first server response + always has to say "OK. DOS100.CO exists and is N bytes". + + If we answer set-name with null, then the magic LOAD doesn't work + because we told the client that the file doesn't exist. + If we answer set-name with the filename & size, then SAVE doesn't work + because we told the client that the file already exists. + + It's far more frequently needed, and more useful & important to load + DOS___.CO than to save it, so dl defaults to make load work. And if the + user does actually answer "Overwrite" when trying to save, it harmlessly + fails to overwrite the real file in /usr/local/lib/dl due to permissions. + + This could be made slightly better by recognizing the special filename + after the set-name step, and doing something different during open(write) + if that's what ends up happening. Like write to the current dir instead + of trying to overwite the magic file. The user would still get an annoying + "Overwrite? Append? Cancel?" message, but at least if they just ignore + that and choose Overwite, the file would appear in the current dir + otherwise as expected. This last little bit is not implemented currently. + +----------- + diff --git a/ref/windows.md b/ref/windows.md new file mode 100644 index 0000000..0c8bf09 --- /dev/null +++ b/ref/windows.md @@ -0,0 +1,479 @@ +# Notes for Windows + +## 1 - Install either Cygwin or MSYS2 +Cygwin and MSYS2 are pretty similar systems. Pick either one. +MSYS2 is more convenient. If you don't already have any opinion, use MSYS2. + +### for Cygwin +* Install [Cygwin](https://www.cygwin.com/) + When it gets to the **Select Packages** screen, + select these additional packages to install: + **cygwin-devel make gcc-g++ git** + * View->Full + * Scroll or search to find **cygwin-devel** + * Pull-down menu to the right in the "New" column + * Select the highest number that doesn't say "(Test)" + * Repeat for: **gcc-g++**, **make**, **git** +* Launch a Cygwin terminal window + +### for MSYS2 +* Install [MSYS2](https://www.msys2.org/) +* Close the URCT window that opens after install +* Launch an MSYS window + Start -> MSYS2 -> MSYS2 MSYS +* Update the installed packages: `$ pacman -Syu` +* If the window closed, launch new MSYS window +* Update again: `$ pacman -Syu` +* Install git, gcc, & make: `$ pacman -Sy git gcc make` + +## 2 - Download, build, & install dl2 +``` +$ git clone https://github.com/bkw777/dl2.git +$ cd dl2 +$ make clean all && make install +``` + +## Platform notes + +* Getty/daemon mode is #ifdef'd out at compile-time on Windows, so there is no -g getty option. + +* Serial tty devices are named like ttyS# +Use `ls /dev/tty*` to find the serial tty device after plugging in a usb-serial adapter. +Then use `ttyS4` (for example) as the last argument on the dl command line. +(current versions of dl2 should automatically find the tty device in most cases, so you should not need to put any ttyS# on the command line unless you have multiple serial devices and want to specify the right one instead of having to select it from a list interactively) + +* The Windows user might need to be in the Administrator group, I haven't done much testing of dl2 on Windows. + +--- + +## Example usage session - initialize a REXCPM + +Consult the [REXCPM docs](http://bitchin100.com/wiki/index.php?title=REXCPM) to understand all the commands and actions below. +This is just an example to show using dl2 on Windows, not a full explaination of REXCPM installation or usage. +See also: https://github.com/bkw777/dl2/blob/master/ref/REXCPM.md + +In addition to the packages above where you installed git and gcc etc, also install the "unzip" package, or download and unzip the the files from Windows and skip the download & unzip steps shown here. +You want to get the latest versions of any files from the REXCPM docs anyway, not paste the exact versions shown below. + +Start with a cold-reset of the Model 100: SHIFT+CTRL+BREAK+RESET +(this erases all RAM, including all files) + +* Download & unzip the REXCPM setup files for the Model 100 +``` +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ curl -O http://www.bitchin100.com/wiki/images/0/03/REXCPMV21_b19.ZIP + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 19859 100 19859 0 0 197k 0 --:--:-- --:--:-- --:--:-- 199k + +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ curl -O http://www.bitchin100.com/wiki/images/6/63/M100_OPTION_ROMS.zip + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 172k 100 172k 0 0 220k 0 --:--:-- --:--:-- --:--:-- 220k + +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ curl -O http://www.bitchin100.com/wiki/images/8/8e/CPMUPD.CO + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 1206 100 1206 0 0 1088 0 0:00:01 0:00:01 --:--:-- 1089 + +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ curl -O http://www.bitchin100.com/wiki/images/a/a7/Cpm410.bk + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 198k 100 198k 0 0 899k 0 --:--:-- --:--:-- --:--:-- 904k + +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ unzip REXCPMV21_b19.ZIP +Archive: REXCPMV21_b19.ZIP + inflating: rxctst.DO + inflating: rxcini.DO + inflating: rxcupg.DO + inflating: rxcutl.do + inflating: RXC_12.BR + +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ unzip M100_OPTION_ROMS.zip +Archive: M100_OPTION_ROMS.zip + inflating: checksums.txt + inflating: IS100.BX + inflating: MFORTH.BX + inflating: MP100.bx + inflating: notes.txt + inflating: R2C1D.BX + inflating: R2C100.BX + inflating: SAR100.BX + inflating: SUP100.BX + inflating: TSD100.BX + inflating: TSR100.BX + inflating: UR2100.BX + inflating: ANLYST.BX + +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ +``` + +'''(Current versions of dl2 should automatically figure out the tty device as long as there is only one, and interactively ask you to select from a list if it detects more than one possible. You can probably skip the parts about the tty device here and just omit the ttyS# from the dl command lines.)''' + +* Identify the serial port tty device +``` +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ ls /dev/ttyS* +/dev/ttyS4 +``` +In my case, my usb-serial device is ttyS4 at the moment. You may get something else. +You may even see multiple matches, even if you only have one usb-serial device connected. +One of my laptops has some internal component that shows up as COM4 and ttyS4, and when I connect a usb-serial adapter or arduino programmer etc, it shows up as COM5 or higher -> ttyS5 or higher. + +* Run dl, specifying the tty device determined above + +Overview of the sequence of events about to follow: + +The initial command below, `dl -vb rxcini.DO ttyS4 && dl -vu ttyS4` is two consecutive commands. +First `dl -vb rxcini.DO ttyS4` uses the bootstrap function `-b` to send `rxcini.DO` to the 100 and start executing it, +As soon as rxcini is sent, the next command `dl -vu ttyS4` starts providing TPDD file access to the current directory, with uppercase filename conversion `-u`. + +rxcini itself and all the later steps all use TPDD to load files from the pc. + +While `rxcini.DO` is running it will use TPDD to load the REXCPM firmware image `RXC_12.BR`, +and then RXCMGR uses TPDD to load the TS-DOS option rom image `TSD100.BX`, +and then you manually use TS-DOS (which uses TPDD) to copy `CPMUPD.CO` to the 100, +and then `CPMUPD.CO` uses TPDD to load the CP/M disk image `Cpm210.bk` or `Cpm410.bk`. + +``` +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ dl -vb rxcini.DO ttyS4 && dl -vu ttyS4 +DeskLink+ v2.0.000-16-g9d2a488 +Loading: "rxcini.DO" +Serial Device: /dev/ttyS4 +Working Dir : /cygdrive/c/Users/bkw/Documents/REX +Opening "/dev/ttyS4" ... OK +Bootstrap: Installing "rxcini.DO" + +Prepare BASIC to receive: + + RUN "COM:98N1ENN" [Enter] <-- TANDY/Olivetti/Kyotronic + RUN "COM:9N81XN" [Enter] <-- NEC + +Press [Enter] when ready... +Sending "rxcini.DO" ... +10 clear1000,62000:SCREEN0:GOSUB300:GOSUB75 +15 RESTORE 90:FORI=0TO1:READR(I),Q$(I),C$(I,0),C$(I,1):CH(I)=0:NEXTI +20 I=0 +25 K=0:GOSUB80 +30 GOSUB85 +35 Z$=INKEY$:IFZ$=""THEN 35 +40 IF ASC(Z$)=32 THEN K=(K+1)MOD2:GOTO 30 +45 IF ASC(Z$)=13 THEN CH(I)=K:I=I+1 +50 IF I<2 THEN GOTO 25 +55 PRINT@280,"Execute choices.. sure? (y/n) "; +60 Z$=INKEY$:IFZ$=""THEN 60 +65 IF Z$="y" OR Z$="Y" THEN 198 ELSE 10 +75 PRINT@280,"selection: choice:"; +76 print@40,"This program is able to load REXCPM" +77 print@80,"software, and able to wipe clean" +78 print@120,"the directory." +80 PRINT@R(I)*40,Q$(I):RETURN +85 PRINT@R(I)*40+19,C$(I,K):RETURN +90 DATA 5,"Load REXCPM code? ", "No ","Yes" +91 DATA 6,"Init REXCPM dir? ", "No ","Yes" + +198 if CH(0)=0 and CH(1)=0 then end +199 a=CH(0): hl= CH(1) +200 gosub300 +201 n=1:restore400:gosub280:q0=q1 +213 gosub240 +215 FORX=1TOpeek(m)step2:k=16*peek(z)+peek(z+1)-1105:POKEQ,k:Q=Q+1:cs=cs+k:z=z+2:NEXT:GOTO213 +216 gosub270 + +220 n=2:restore500:gosub280 +223 gosub240 +224 pokeq0+2,m-256*INT(m/256):pokeq0+3,INT(m/256):callq0+4,0,q:cs=cs+256*peek(q0+1)+peek(q0):q=q+peek(m)/2:GOTO223 +226 gosub270 + +230 rem a$=inkey$:ifa$=""then 207 +232 callq1,a,hl:END + +240 READP$:m=65536+varptr(p$):print".";:z=256*peek(m+2)+peek(m+1) +241 on (1+((-n)*(peek(z)=47))) goto 242,216,226 +242 return + +270 print">";q-1:READP:ifcs=Pthenreturn +272 beep:print"Checksum Error! Check file!":end + +280 cs=0:print"Stage ";n;": ";:readq1:q=q1:printq;"<";:ifq>=himemthenreturn +281 beep:print"Himem conflict!":end + +400 DATA 64704 +401 DATA AAAAAAAAOFKPDCMAPMDCMBPMCKMCPMEGCDFOCDFGOBOLHONG +402 DATA EBAHAHAHAHEPCDHONGEBIBOFBCBDNFBGAAFPCKMAPMBJCCMA +403 DATA PMNBOBCDAFAFMCNGPMMJ +404 DATA / +405 DATA 6667 +300 CLS:print"REXCPM INITIALIZE v3" +304 return +500 DATA 62000 +501 DATA DCFOPDHNDCFPPDDIAAOLCCFMPDCBDCPEDHMNOGBHCBAGAAMN +502 DATA CNPFPOAAMCBGPECBAHAAMNCNPFPOABMCBGPECBAFAAMNCNPF +503 DATA POACMCBGPECBPPPFHOPOMJMCBGPEMNFPPFDKFOPDLHMKBOPD +504 DATA CBGMPDMNKCBBCBHPPDMNKCBBMNEEEGAFMKAOPFDOAGLIMCHO +505 DATA PCCBILPGDGCOCDDGECCDDGFCCDCIAODGCANPCDMCJPPCBBEG +506 DATA AAOLNJDOABDCGHPDCBAHAAMNDIPEMCBAPECBAABKMNDIPEMK +507 DATA HOPCDOADDCIFPGCBABABMNDIPEMCBAPEKPMNCEPFDOACMNCK +508 DATA PFDOABDCGHPDKPDCGGPDDOBLOHDOEBOHCBAAIACCGKPDCKGK +509 DATA PDDKGHPDKEPOKAMKBOPDOLCBADAAMNDIPEHINGBCMKBAPEDO +510 DATA ANOHDKGHPDOGEAEHCKGKPDHMOGHPLAGHMNNEDJMDOOPCDKFP +511 DATA PDLHMKEMPDCBIIPDMNKCBBDOADMNCKPFKPMNCEPFCBAAKADG +512 DATA PPCDHMPOKDMCDHPDCBAAKBDGAACDHMPOKCMCEDPDKPMNCKPF +513 DATA DOABMNCEPFCBKFPDMDKCBBAAAAAAAAAAAAAAAAAAAAAAAAAA +514 DATA AAAAAAAAANAKAJEGGPHCGNGBHEDKFIFIFIFIFIFIANAKAAAJ +515 DATA EOGBGNGFCACADKAAANAKAJEJGOGJHEGJGBGMGJHKGJGOGHCA +516 DATA GEGJHCGFGDHEGPHCHJCOCOCOAAAHANAKAJEDGPGNHAGMGFHE +517 DATA GFCBCBAAAHAHANAKEDGBGOCHHECAHCHFGOCBCBANAKFCGFGN +518 DATA GPHGGFCAFCEFFICAGIGPGPGLCAEDEOFEEMCNFIANAKFAGPHH +519 DATA GFHCCAGDHJGDGMGFCOAAAHAHANAKEDGIGFGDGLCAFEFAEEEE +520 DATA CBCBAAAHAHANAKEDGIGFGDGLHDHFGNCAEFHCHCGPHCCBCBAA +521 DATA CBOKPDMDBPPECBLEPDMDBPPECBPLPDMNJBFHCKFMPDPJKPMN +522 DATA CKPFDOABMNCEPFMDKAFLDJDIEODBEEAAHNDCGAPDOFCBIFPG +523 DATA CCGIPDCBLHPGCCGKPDDKGAPDPOAEMCFIPEOLCCGIPDMDGEPE +524 DATA DKGAPDPOADMCGEPEOLCCGKPDOBKPEPDOFKMNBIPFMNBIPFHN +525 DATA DCGAPDMNBIPFIBEPHMMNBIPFIBEPOLCKGIPDOLCECFMKJCPE +526 DATA BKMNBIPFIBEPBDMDIEPEOLCCGIPDCKGKPDHJCPMNBIPFPDMN +527 DATA APPFFPDCGBPDMNAPPFDCGCPDOFFHIDFPDKGHPDLHMCLKPEMD +528 DATA NPPEHMPOMAMCNIPECBAAIADKGHPDPOPPMKLHPEDOPPDCGHPD +529 DATA DOADMNCKPFCBAAIAMNAPPFHHCDIDFPBFMCLAPEMNAPPFDCGD +530 DATA PDFHHLCPDCGEPDLKMCBMPEDOIAEHDKGCPDLIMKABPFKPDCGC +531 DATA PDDKGBPDEHCCGKPDOBKPIGDCGFPDMJCAOGCAMKAPPFNLMIMJ +532 DATA PFNLNIOGBAMKBJPFPBNDMIMJCBAAAAMDCNPFCBACAAPFHNOG +533 DATA AHAHAHAHAHPGIAGHGPPBPFOGPAAPAPAPAPLEGHPBOGAPLFGP +534 DATA PDDKAALIDKAAPCDKAAMEDKAAONDKAAKBDKAAJIHOGFHOMJCB +535 DATA ILPFMNKCBBCBJGPFMNKCBBCBJNPFDKFOPDLHMCHIPFCBKDPF +536 DATA MNKCBBCBKMPFDKFPPDLHMCIIPFCBLBPFMDKCBBANAKAJEBGD +537 DATA HEGJGPGODNAADBDJDCDADACMAAEMGPGBGECMAAEOGPCAGMGP +538 DATA GBGECMAAEJGOGJHEAAEOGPCAGJGOGJHEAA +539 DATA / +540 DATA 104506 + +DONE + +"dl -b" will now exit. +Re-run "dl" (without -b this time) to run the TPDD server. + +DeskLink+ v2.0.000-16-g9d2a488 +Serial Device: /dev/ttyS4 +Working Dir : /cygdrive/c/Users/bkw/Documents/REX +Opening "/dev/ttyS4" ... OK +------------------------------------------------------------------------------- +"ANLYST.BX" ANLYST.BX +"CHECKS.TX" checksums.txt +"CPM410.BK" Cpm410.bk +"CPMUPD.CO" CPMUPD.CO +"IS100 .BX" IS100.BX +"M100_O.ZI" M100_OPTION_ROMS.zip +"MFORTH.BX" MFORTH.BX +"MP100 .BX" MP100.bx +"NOTES .TX" notes.txt +"R2C100.BX" R2C100.BX +"R2C1D .BX" R2C1D.BX +"REXCPM.ZI" REXCPMV21_b19.ZIP +"RXCINI.DO" rxcini.DO +"RXCTST.DO" rxctst.DO +"RXCUPG.DO" rxcupg.DO +"RXCUTL.DO" rxcutl.do +"RXC_12.BR" RXC_12.BR +"SAR100.BX" SAR100.BX +"SUP100.BX" SUP100.BX +"TSD100.BX" TSD100.BX +"TSR100.BX" TSR100.BX +"UR2100.BX" UR2100.BX +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +"ANLYST.BX" ANLYST.BX +"CHECKS.TX" checksums.txt +"CPM410.BK" Cpm410.bk +"CPMUPD.CO" CPMUPD.CO +"IS100 .BX" IS100.BX +"M100_O.ZI" M100_OPTION_ROMS.zip +"MFORTH.BX" MFORTH.BX +"MP100 .BX" MP100.bx +"NOTES .TX" notes.txt +"R2C100.BX" R2C100.BX +"R2C1D .BX" R2C1D.BX +"REXCPM.ZI" REXCPMV21_b19.ZIP +"RXCINI.DO" rxcini.DO +"RXCTST.DO" rxctst.DO +"RXCUPG.DO" rxcupg.DO +"RXCUTL.DO" rxcutl.do +"RXC_12.BR" RXC_12.BR +"SAR100.BX" SAR100.BX +"SUP100.BX" SUP100.BX +"TSD100.BX" TSD100.BX +"TSR100.BX" TSR100.BX +"UR2100.BX" UR2100.BX +------------------------------------------------------------------------------- +``` + +Here I typed `RXC_12` at the filename prompt in rxcini + +``` +Open for read: "RXC_12.BR" +................................................................................................................................................................................................------------------------------------------------------------------------------- +"ANLYST.BX" ANLYST.BX +"CHECKS.TX" checksums.txt +"CPM410.BK" Cpm410.bk +"CPMUPD.CO" CPMUPD.CO +"IS100 .BX" IS100.BX +"M100_O.ZI" M100_OPTION_ROMS.zip +"MFORTH.BX" MFORTH.BX +"MP100 .BX" MP100.bx +"NOTES .TX" notes.txt +"R2C100.BX" R2C100.BX +"R2C1D .BX" R2C1D.BX +"REXCPM.ZI" REXCPMV21_b19.ZIP +"RXCINI.DO" rxcini.DO +"RXCTST.DO" rxctst.DO +"RXCUPG.DO" rxcupg.DO +"RXCUTL.DO" rxcutl.do +"RXC_12.BR" RXC_12.BR +"SAR100.BX" SAR100.BX +"SUP100.BX" SUP100.BX +"TSD100.BX" TSD100.BX +"TSR100.BX" TSR100.BX +"UR2100.BX" UR2100.BX +------------------------------------------------------------------------------- +``` + +After rxcini completed, I: +- Typed `CALL 63012` in BASIC to install RXCMGR from the REXCPM +- Exited BASIC and launched RXCMGR from the main menu +- Pressed TAB to switch to the ROM screen in RXCMGR +- Pressed F2 for Load +- Typed `TSD100` +- Pressed Enter on the new TS-DOS entry to install the TS-DOS option rom (which also launches it) + +``` +Open for read: "TSD100.BX" +................................................................................................................................................................................................................................................................------------------------------------------------------------------------------- +"ANLYST.BX" ANLYST.BX +"CHECKS.TX" checksums.txt +"CPM410.BK" Cpm410.bk +"CPMUPD.CO" CPMUPD.CO +"IS100 .BX" IS100.BX +"M100_O.ZI" M100_OPTION_ROMS.zip +"MFORTH.BX" MFORTH.BX +"MP100 .BX" MP100.bx +"NOTES .TX" notes.txt +"R2C100.BX" R2C100.BX +"R2C1D .BX" R2C1D.BX +"REXCPM.ZI" REXCPMV21_b19.ZIP +"RXCINI.DO" rxcini.DO +"RXCTST.DO" rxctst.DO +"RXCUPG.DO" rxcupg.DO +"RXCUTL.DO" rxcutl.do +"RXC_12.BR" RXC_12.BR +"SAR100.BX" SAR100.BX +"SUP100.BX" SUP100.BX +"TSD100.BX" TSD100.BX +"TSR100.BX" TSR100.BX +"UR2100.BX" UR2100.BX +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +"ANLYST.BX" ANLYST.BX +"CHECKS.TX" checksums.txt +"CPM410.BK" Cpm410.bk +"CPMUPD.CO" CPMUPD.CO +"IS100 .BX" IS100.BX +"M100_O.ZI" M100_OPTION_ROMS.zip +"MFORTH.BX" MFORTH.BX +"MP100 .BX" MP100.bx +"NOTES .TX" notes.txt +"R2C100.BX" R2C100.BX +"R2C1D .BX" R2C1D.BX +"REXCPM.ZI" REXCPMV21_b19.ZIP +"RXCINI.DO" rxcini.DO +"RXCTST.DO" rxctst.DO +"RXCUPG.DO" rxcupg.DO +"RXCUTL.DO" rxcutl.do +"RXC_12.BR" RXC_12.BR +"SAR100.BX" SAR100.BX +"SUP100.BX" SUP100.BX +"TSD100.BX" TSD100.BX +"TSR100.BX" TSR100.BX +"UR2100.BX" UR2100.BX +------------------------------------------------------------------------------- +``` + +Now TS-DOS option rom is installed. +Next, use TS-DOS to copy `CPMUPD.CO` to the 100. + +``` +Open for read: "CPMUPD.CO" +.......... +------------------------------------------------------------------------------- +"ANLYST.BX" ANLYST.BX +"CHECKS.TX" checksums.txt +"CPM410.BK" Cpm410.bk +"CPMUPD.CO" CPMUPD.CO +"IS100 .BX" IS100.BX +"M100_O.ZI" M100_OPTION_ROMS.zip +"MFORTH.BX" MFORTH.BX +"MP100 .BX" MP100.bx +"NOTES .TX" notes.txt +"R2C100.BX" R2C100.BX +"R2C1D .BX" R2C1D.BX +"REXCPM.ZI" REXCPMV21_b19.ZIP +"RXCINI.DO" rxcini.DO +"RXCTST.DO" rxctst.DO +"RXCUPG.DO" rxcupg.DO +"RXCUTL.DO" rxcutl.do +"RXC_12.BR" RXC_12.BR +"SAR100.BX" SAR100.BX +"SUP100.BX" SUP100.BX +"TSD100.BX" TSD100.BX +"TSR100.BX" TSR100.BX +"UR2100.BX" UR2100.BX +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +"ANLYST.BX" ANLYST.BX +"CHECKS.TX" checksums.txt +"CPM410.BK" Cpm410.bk +"CPMUPD.CO" CPMUPD.CO +"IS100 .BX" IS100.BX +"M100_O.ZI" M100_OPTION_ROMS.zip +"MFORTH.BX" MFORTH.BX +"MP100 .BX" MP100.bx +"NOTES .TX" notes.txt +"R2C100.BX" R2C100.BX +"R2C1D .BX" R2C1D.BX +"REXCPM.ZI" REXCPMV21_b19.ZIP +"RXCINI.DO" rxcini.DO +"RXCTST.DO" rxctst.DO +"RXCUPG.DO" rxcupg.DO +"RXCUTL.DO" rxcutl.do +"RXC_12.BR" RXC_12.BR +"SAR100.BX" SAR100.BX +"SUP100.BX" SUP100.BX +"TSD100.BX" TSD100.BX +"TSR100.BX" TSR100.BX +"UR2100.BX" UR2100.BX +------------------------------------------------------------------------------- +``` + +- Exited TS-DOS +- Entered BASIC and typed `CLEAR0,60000` to make room for CPMUPD to run +- Launched CPMUPD from the main menu +- Entered `CPM410.BK` at the filename prompt in CPMUPD because my REXCPM has a 4MB chip. + +``` +Open for read: "Cpm410.bk" +................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................. + + +bkw@win10pro_bkw /cygdrive/c/Users/bkw/Documents/REX +$ +``` + +Press Ctrl+C on the pc to quit dl2. + +REXCPM is now fully installed. diff --git a/ref/xattr.md b/ref/xattr.md new file mode 100644 index 0000000..ee1d8b6 --- /dev/null +++ b/ref/xattr.md @@ -0,0 +1,123 @@ +## Storing the ATTR byte from the tpdd client in local xattr + +If compiled with -DUSE_XATTR +`$ make clean all CXXFLAGS=-DUSE_XATTR && sudo make install` +then the ATTR field of a TPDD directory entry is stored in and retrieved from an extended attribute named "pdd.attr", instead of just hard-coded with 'F' at all times. + +On Linux the xattr name is prefixed with "user." to become "user.pdd.attr" +On Mac the xattr name is suffixed with "#S" to become "pdd.attr#S" +On FreeBSD the name is unchanged and the namespace used is EXTATTR_NAMESPACE_USER +The xattr name is not something like "com.dl2.attr" because it is intended to be generic and not tied just to dl2, so other tpdd clients and servers might use the same name and the files would be compatible across different software. + +The attr field is a single byte, and may contain any value, 0x00 to 0xFF. +The field is normally never shown to users because TRS-80 Model 100 software doesn't use the field and just hard-codes 'F' in that field behind the scenes. +And because of that, most drive emulators also ignore the field except to just hard-code the same 'F' there at all times. +But a real drive is actually recording and checking that data with every file access, and other software could use it. + +So the idea is to more accurately emulate a real drive by actually recording the attr byte supplied by the client, and actually checking it later. +If the client is creating a new file, save the given attr value as part of the local file in the fom of an xattr field. +If looking for an existing file, read the attr for each local file from the xattr, and compare with the attr given by the client along with the filename. +Only use the default attr value when the xattr doesn't exist. + +Example using [pdd.sh](https://github.com/bkw777/pdd.sh) + +First the help reference for the load and save commands. +You don't normally use the attr argument but there is one. + +``` +PDD(opr:6.2,F)> help save + + save src_filename [dest_filename] [attr] + Copy a file from local to disk +PDD(opr:6.2,F)> help load + + load src_filename [dest_filename] [attr] + Copy a file from disk to local + +PDD(opr:6.2,F)> help delete + + rm | del | delete filename [attr] + Delete filename [attr] from disk + +PDD(opr:6.2,F)> + +``` + +The parameters are simple position dependant, so in order to use the 3rd argument you have to supply the 2nd. + +Normally to save a file without also renaming it along the way, you only need to say "save filename" + +But if you want to override the default attr and specify an arbitrary attr like X, +you need to say `save filename filename X`. +Or you can give "" or '' for the destination filename so: `save filename "" X` +and in that case it will use the source filename without having to type it out twice. + +So to show the attr actually being stored and retrieved: + + Create a small text local file `T0.DO` + Save `T0.DO` as `T1.DO` without specifying any attr + Save `T0.DO` as `T2.DO` with attr `X` + Save `T0.DO` as `T3.DO` with attr `d` + Save `T0.DO` as `T4.DO` with attr `' '` + List the directory + +``` +$ printf 'test\r\n' >T0.DO +$ pdd ttyUSB1 "save T0.DO T1.DO;save T0.DO T2.DO X;save T0.DO T3.DO d;save T0.DO T4.DO ' ';ls" +Saving TPDD:T1.DO (F) +[########################################] 100% (6/6 bytes) +Saving TPDD:T2.DO (X) +[########################################] 100% (6/6 bytes) +Saving TPDD:T3.DO (d) +[########################################] 100% (6/6 bytes) +Saving TPDD:T4.DO ( ) +[########################################] 100% (6/6 bytes) +-------- Directory Listing -------- +T1.DO | F | 6 +T2.DO | X | 6 +T3.DO | d | 6 +T4.DO | | 6 +------------------------------------- +102400 bytes free +$ +``` + +The directory listing shows that the attr values were written to "disk" and then read back out. + +As with a real drive, both the filename and attr must match in order to access a file. +filename "foo" with attr "a" +and +filename "foo" with attr "b" +are two different files, basically the same as if one were named "fooa" and one were named "foob" + +For instance to load or delete one of these files that has a non-default attr, you have to specify both the filename and the attr : +`PDD(opr:6.2,F)> rm T3.DO d` + +This is working on Linux, Macos, & FreeBSD. + +For any platform that isn't supported, or on any filesystem that doesn't have extended attributes, or any new local files that weren't created by a tpdd client, it will just transparently work the old way. Attr will be 'F' or whatever the "-a" commandline flag or the ATTR environment variable says. + +## notes + +To see xattrs on files: +`getfattr -d -- NAME.BA` +`getfattr -d -- *` + +cp, tar, rsync, etc don't preserve xattrs with files by default. + +They all have options for it, so you can preserve the xattrs in copies, it's just not the default behavior. + +Even text editors may need extra config so they don't unlink & create a new file on each save. + +In reality, you will never notice or care because everything that you ever want to use with a TPDD emulator, they all happen to always hard-code attr=F at all times, and that is the same thing dl2 will do any time a file has no xattr. +This is the same as all tpdd emulators which have all been hard-coding 'F' all along anyway since the beginning in 1984. + +## misc references +Wrapper to provide a single interface to the different platforms xattr interfaces. +Interesting but not used because it's c++ not plain c. +Maybe it can be ported to plain c. +https://github.com/edenzik/pxattr + +Macos info +https://eclecticlight.co/2019/10/01/how-to-preserve-metadata-stored-in-a-custom-extended-attribute/ + diff --git a/xattr.c b/xattr.c new file mode 100644 index 0000000..6bb417e --- /dev/null +++ b/xattr.c @@ -0,0 +1,64 @@ +// Instead of hard-coding 'F' for the ATTR field at all times, +// store the actual attr byte from the client in xattr. + +// dl.c always sets attr first (default or from client) then calls one of these. +// If !USE_XATTR, then these are all no-op and attr simply left unchanged. +// If USE_XATTR, then these may or may not copy xattr to attr, or attr to xattr, +// depending on get vs set and depending on if the xattr exists. + +#ifdef USE_XATTR + +#if defined(__FreeBSD__) +#include +#else +#include +#endif + +#include "xattr.h" + +#ifndef XATTR_NAME +#define XATTR_NAME "pdd.attr" +#endif + +const char* xattr_name = +#if defined(__linux__) + "user." XATTR_NAME +#elif defined(__APPLE__) + XATTR_NAME "#S" +#else + XATTR_NAME +#endif +; + +void dl_getxattr(const char* path, uint8_t* value) { +#if defined(__linux__) + getxattr(path, xattr_name, value, 1); +#elif defined(__APPLE__) + getxattr(path, xattr_name, value, 1, 0, 0); +#elif defined(__FreeBSD__) + extattr_get_file(path, EXTATTR_NAMESPACE_USER, xattr_name, value, 1); +#endif +} + +void dl_fgetxattr(int fd, uint8_t* value) { +#if defined(__linux__) + fgetxattr(fd, xattr_name, value, 1); +#elif defined(__APPLE__) + fgetxattr(fd, xattr_name, value, 1, 0, 0); +#elif defined(__FreeBSD__) + extattr_get_fd(fd, EXTATTR_NAMESPACE_USER, xattr_name, value, 1); +#endif +} + +void dl_fsetxattr(int fd, const uint8_t* value) { +#if defined(__linux__) + fsetxattr(fd, xattr_name, value, 1, 0); +#elif defined(__APPLE__) + fsetxattr(fd, xattr_name, value, 1, 0, 0); +#elif defined(__FreeBSD__) + extattr_set_fd(fd, EXTATTR_NAMESPACE_USER, xattr_name, value, 1); +#endif +} + +#endif // USE_XATTR + diff --git a/xattr.h b/xattr.h new file mode 100644 index 0000000..b313955 --- /dev/null +++ b/xattr.h @@ -0,0 +1,24 @@ +#ifndef PDD_XATTR_H +#define PDD_XATTR_H + +#include + +#ifdef USE_XATTR + +// default xattr_name is #defined in xattr.c +// and overridable at run-time from main.c +extern const char* xattr_name; + +void dl_getxattr (const char* path, uint8_t* value); +void dl_fgetxattr (int fd, uint8_t* value); +void dl_fsetxattr (int fd, const uint8_t* value); + +#else // USE_XATTR + +#define dl_getxattr(x,y) +#define dl_fgetxattr(x,y) +#define dl_fsetxattr(x,y) + +#endif // USE_XATTR + +#endif // PDD_XATTR_H