TRS-80 DOS - Percom DoubleDOS 4.24 for the Model I - Disassembled
Page Index
Overview
DoubleDOS (DBLDOS in Percom's manual) is the double density disk operating system Percom Data Company supplied in 1980 with the Percom Doubler, the adapter that lets the TRS-80 Model I Expansion Interface read and write double density diskettes. Its system files carry the text MODIFIED FOR DOUBLE DENSITY OPERATION BY JAMES W STUTSMAN, and Percom's manual gives the Radio Shack TRSDOS and Disk BASIC Reference Manual as its users manual. These pages disassemble the 4.24 system diskette: the boot sector, the resident system SYS0/SYS, the system overlays SYS1/SYS to SYS7/SYS, and the three /CMD files of the diskette: Disk BASIC (BASIC/CMD), COPY/CMD and DOUBLE/CMD. Each page is written from the bytes of the file itself. DIR/SYS, the directory, has no page.
The Versions
The pages are for version 4.24. The 4.20 and 4.23 system diskettes were compared with it file by file. The boot sector, SYS1/SYS to SYS7/SYS and BASIC/CMD are the same, byte for byte, in all three versions. SYS0/SYS differs only in the version number it displays (the digit at 4F15H: 0, 3 or 4) and, in 4.20, in its name record (Percom's patch memo DPM-0003, which raised the system to 4.23 together with DPM-0001 and DPM-0002). COPY/CMD differs between 4.20 and 4.23 (DPM-0002) and DOUBLE/CMD between 4.23 and 4.24; their pages name each difference. DIR/SYS and PATCH/BAS also differ and have no page.
Boot Sector
Purpose:
The boot sector is cylinder 0, sector 0 of the system diskette, the only sector of the single density cylinder 0 that holds data; the directory has no BOOT/SYS entry. The ROM reads it into 4200H and runs it. It switches the Percom Doubler to double density (command code FFH, then Force Interrupt D0H), clears the screen, and reads SYS0/SYS from cylinder 1, sector 0 on, a sector at a time into 4D00H-4DFFH with two tries per sector, loading its load blocks as it reads them and passing over its other records, until the transfer address record sends it to 4E00H. A sector that cannot be read shows DISK ERROR and stops the computer. It does not read the directory: SYS0/SYS always begins at the first sector of cylinder 1. The sector is the same on the 4.20, 4.23 and 4.24 diskettes.
Disassembly:
A disassembly of the Boot Sector can be found here.
SYS0/SYS
Purpose:
SYS0/SYS is the resident system, loaded by the boot sector from cylinder 1, sector 0 on and started at 4E00H. It holds the vector table at 4400H, the overlay dispatcher reached by RST 28H (a request code whose low four bits name SYS1/SYS to SYS7/SYS, loaded through the system FCB at 44A0H), the interrupt routine with a task table of twelve slots and the clock, a keyboard driver with a check against key bounce, and the double density disk driver: each drive keeps its own head position, sectors are numbered in blocks of ten from cylinder 1 and converted to cylinder and sector at 43A5H, and one routine with inline parameters reads, writes and reads back sectors with retries. The file routines @READ, @WRITE, @VERF, @POSN, @BKSP, @REW and @PEOF, the search of a file's extents, the allocation of granules of five sectors from the GAT, the directory and GAT routines, @LOAD, @RUN and a loader that reads back every byte it stores complete it. The start-up finds the top of memory, installs the interrupts and the clock task, shows the banner and runs the AUTO command from the GAT. The loaded bytes of 4.20, 4.23 and 4.24 differ only in the version digit at 4F15H; the name record changed with Percom's memo DPM-0003, whose 11H is the directory block that programs read from byte 2 of the diskette's first sector.
Disassembly:
A disassembly of SYS0/SYS can be found here.
SYS1/SYS
Purpose:
SYS1/SYS is the command level, overlay 3, loaded at 4E00H for the request codes 93H to E3H. For 93H (DOS READY at 4400H, the end of every program) and A3H (the abnormal exit at 4030H) it shows DOS READY and takes a line of up to 63 characters at 4318H; for B3H (4405H) it runs a given line. The line is made capitals and its first word parsed into 4480H; the name is looked up in a table of its own commands (BASIC2, which starts the ROM's BASIC, DEBUG and TRACE) and in a table of nineteen library commands, which load SYS6/SYS with request code 88H and the command's number in Register C. Any other name is run as a program through @RUN, with /CMD added and 4480H as its FCB. The entries copy and double of the library table are in small letters and never match, so COPY and DOUBLE run COPY/CMD and DOUBLE/CMD; DEVICE is not in the table, as the manual says.
It also serves the other parts of DoubleDOS: C3H (441CH) parses a file specification (name, /extension, .password, :drive, or an asterisk and a device name, passing over the words TO, ON and OVER), D3H (4473H) adds a default extension, and E3H (4476H) parses a parameter list in parentheses with values YES, NO, ON, OFF, decimal and X'hex'. DEBUG turns 4030H into CALL 400FH and points BREAK at DEBUG; DEBUG (OFF) puts them back. SYS1/SYS is the same in versions 4.20, 4.23 and 4.24.
Disassembly:
A disassembly of SYS1/SYS can be found here.
SYS2/SYS
Purpose:
SYS2/SYS opens and makes files, overlay 4, loaded at 4E00H for the request codes 94H, A4H and B4H. For 94H (4424H, which @LOAD uses) it makes the file specification in the FCB capitals, parses it into name, extension, password and drive, works out the hash code of the name and looks for it in the hash index table, sector 1 of the directory, of the drive named or of drives 0-3 in turn, each of which must show index pulses. The entry found is compared by name and the FCB filled in with the first five extents. For A4H (4420H) a file that no drive has is made in a free place of the hash index table, chosen from a starting point taken from the tick count so that files are spread over the directory sectors; the C FLAG tells the caller the file is new.
For B4H, issued by SYS0/SYS when a file needs more extents than its directory entry holds, it makes an extra entry (attribute 90H) and links it from the last extent place of the entry before it with FEH. Passwords are hashed and stored but never tested: a jump passes over the tests, so every file is opened with every access. When no drive has room for a new file it returns error 1AH with the Z FLAG set unless a drive was named. SYS2/SYS is the same in versions 4.20, 4.23 and 4.24.
Disassembly:
A disassembly of SYS2/SYS can be found here.
SYS3/SYS
Purpose:
SYS3/SYS closes and kills files, overlay 5, loaded at 4E00H for the request codes 95H (close, 4428H) and A5H (kill, 442CH). Close writes a changed buffer, puts the end of file offset and the number of sectors into the directory entry when they differ, counts the granules of the file's entries and gives back, through the GAT, every granule beyond the number of sectors divided by 5 and rounded up; an extra directory entry left with no extents is freed with its place in the hash index table, and the entry before it ends its extents again. The FCB is then written over with NAME/EXT:D, which leaves it closed and ready to be opened again.
Kill needs an access level of 0 or 1 (which every file opened by SYS2/SYS has), marks the granules of every extent free in the GAT, frees each of the file's directory entries and its place in the hash index table, which SYS3/SYS reads into 5100H since the GAT is at 4D00H, and clears the FCB. The result of the buffer write at the start of a close is not tested, and for a file whose extra entry is in another directory sector the name put back in the FCB is taken from the wrong sector. SYS3/SYS is the same in versions 4.20, 4.23 and 4.24.
Disassembly:
A disassembly of SYS3/SYS can be found here.
SYS4/SYS
Purpose:
SYS4/SYS shows the message of an error code, overlay 6, loaded at 4E00H for request code 86H when a program calls @ERROR (4409H) with the code in Register A. The low six bits of the code choose one of 64 entries of a table, each pointing to a list of word numbers; the 53 words (NO, ERROR, PARITY, DURING ... OPEN) are stored once and each message is put together from them, for example DIRECTORY SPACE FULL or ATTEMPTED TO READ LOCKED/DELETED DATA RECORD. The codes 27H-3FH all give UNKNOWN ERROR CODE. The message is shown at the cursor and the line ended; with bit 7 of the code set SYS4/SYS returns to the caller, otherwise the program ends through the abnormal exit at 4030H. SYS4/SYS is the one overlay without which the system stops: the overlay dispatcher HALTs when it is missing.
The file also holds two parts that never run: an unconditional jump passes over the display of three star characters and ERRCOD=nn before the message, and three NOP stand where the call of a routine was that would show the device or file of the last file call and REFERENCED AT X'nnnn', the address of that call. The page gives the message of every code, which led to one correction on the SYS0/SYS page: a drive that is not ready during a write gives code 10H, ILLEGAL LOGICAL FILE NUMBER, where a read gives 08H, DEVICE NOT AVAILABLE. SYS4/SYS is the same in versions 4.20, 4.23 and 4.24.
Disassembly:
A disassembly of SYS4/SYS can be found here.
SYS5/SYS
Purpose:
SYS5/SYS is the DEBUG monitor, overlay 7, loaded at 4E00H for request code 87H. It is entered through the DEBUG vector 440DH, through RST 30H (a breakpoint), through the BREAK key once BREAK is armed, and, while the DEBUG command of SYS1/SYS has DEBUG on, through 4030H whenever an error ends a program. It saves all the program's registers, takes out its breakpoints and runs a command loop on the program's own stack. The X display shows the twelve registers, each with its value and the sixteen bytes it points to (the flags for AF and AF'), and four lines of memory; the S display shows sixteen lines of memory, in hexadecimal or, after A, as characters. Both are laid out so that the last character falls at 3FFEH, since writing 3FFFH would make the ROM scroll the screen.
The commands are one key each: D (the display address), the semicolon and the minus sign (forward and back), U (show the display until a key is pressed), M (change memory, with the byte being changed framed by two graphics blocks), R (change a register by its name), G (go, with an optional address and up to two breakpoints, arming BREAK on the way), and I and C, the single step, which find the length and the target of the instruction from three tables of masks and place one or two breakpoints; C runs a CALL whole. There is no command to leave DEBUG; G 402D returns to DOS READY. SYS5/SYS is the same in versions 4.20, 4.23 and 4.24. Four bugs were found (B23-B26): among them, a single step of JP (IX) takes its target from IY, and the breakpoints share their bytes with the display settings, which are therefore reset at each entry.
Disassembly:
A disassembly of SYS5/SYS can be found here.
SYS6/SYS
Purpose:
SYS6/SYS holds the library commands, overlay 8, loaded at 5200H for request code 88H when a command line starts with one of the 19 names of SYS1/SYS's library table: APPEND, ATTRIB, AUTO, CLOCK, DATE, DIR, DUMP, FREE, KILL, LIB, LIST, LOAD, PRINT, PROT, RENAME, TIME and VERIFY, and the reserved entries copy and double, which SYS1/SYS never matches, so that COPY and DOUBLE run COPY/CMD and DOUBLE/CMD. SYS1/SYS stays in memory at 4E00H and lends its parameter list parser; PROT, FREE, ATTRIB and RENAME open a file first so that SYS2/SYS is loaded and its hash, drive test and hash index table routines can be called.
DIR shows three files a line with S, I and P marks (A adds the record length, the records and the granules); FREE shows each drive's free directory places and granules; ATTRIB sets the protection level, the invisible bit and the password hashes of a file; PROT checks the diskette's master password and can change it, lock every visible file with it or unlock them; DUMP writes memory as a program file; VERIFY switches @WRITE to the write that reads each sector back. Ten bugs were found (B27-B36), among them a PROT password of eight characters that overwrites the start of APPEND, a LIST that hands its parameter table to the parser in the wrong register, and a DIR (A) whose record count is too large for most files. SYS6/SYS is the same in all the versions.
Disassembly:
A disassembly of SYS6/SYS can be found here.
SYS7/SYS
Purpose:
SYS7/SYS holds the error messages of Disk BASIC in words, overlay 9, loaded at 4D00H for request code 89H. BASIC/CMD puts LD A,89H and RST 28H into the error vector 41A6H, which the ROM's error routine calls with the error code in Register E; SYS7/SYS counts its way through 44 messages ended by 00H and hands the one wanted to the ROM at 1A01H, so that Syntax error is shown where Level II BASIC shows ?SN ERROR. Error numbers 1-24 and 51-70 have messages (three of them only a question mark); every other number shows Unprintable error. The file has no name or copyright record, 33 bytes of code and 716 of text.
No bug was found in SYS7/SYS itself. Its page explains what SYS0/SYS's bug B2 does to it: when a growing file makes SYS0/SYS read the GAT into 4D00H, SYS7/SYS is still taken to be in memory, and the next BASIC error runs the GAT. SYS7/SYS is the same in all the versions.
Disassembly:
A disassembly of SYS7/SYS can be found here.
BASIC/CMD
Purpose:
BASIC/CMD is Disk BASIC: TRSDOS 2.1's BASIC/CMD (Disk BASIC 1.1) at the same addresses, with DBLDOS's code at 5200H-5548H, a new start-up and some changes. The start-up takes BASIC F, M, command as the manual (3.4) says (F the highest file number 0-15, M the memory size, then a command run at the first READY) and also BASIC *, which keeps the program in memory; it hooks RST 28H so that the BREAK key reaches BASIC, copies the 49 Disk BASIC vectors from 4D00H to 4152H, and puts a 290 byte area for each file from 6431H. The long error messages are in SYS7/SYS, called through the vector 41A6H. New in DBLDOS: OPEN "E" (add to the end of a file, not in the manual), LOAD and SAVE of a program in memory form a sector at a time with the file read back after SAVE, FIELD records that may fill all 256 bytes, PUT with read back (@VERF), a working LOC, and CMD with one character only (CMD"X" refused).
581 bytes of DBLDOS's own code are never run: line recall by arrow keys, the abbreviations L, E and D, REF and RENUM, and CMD with a DOS command, which alone would set the error hook of SYS0/SYS at 4358H. BASIC/CMD is the same in 4.20, 4.23 and 4.24. Bugs B40-B42: LOAD's room test is one byte short; an SCF in FN (TRSDOS 2.1's code) leaves a branch never taken; BASIC * copies 294 bytes from a command with no carriage return, over start-up code that has run.
Disassembly:
A disassembly of BASIC/CMD can be found here.
COPY/CMD
Purpose:
COPY/CMD is DBLDOS's COPY command. COPY :d filespec1 TO filespec2 copies a file with one drive, asking for the source and the destination diskette in turn on drive d; without :d the two files may be on two drives. A dollar sign just before a file name marks a single density file: before each pass COPY switches the Doubler (FEH or FFH) and patches SYS0/SYS so that a block is a ten sector track (LD (37EEH),DE in place of CALL 43A5H at 4651H) and the GAT is searched only to 60H. So that no overlay is read while the diskettes are changed or in the other density, COPY loads SYS2/SYS and SYS3/SYS once, by an open and a close that fail, keeps copies of them at 54D8H and 58D8H and puts them back before each open and close. The copy goes a buffer at a time (5D00H to the top of memory, 162 sectors with 48K), every sector written with read back, and the destination gets the source's end of file.
4.20's COPY/CMD lacks Percom's DPM-0002, which added 5CD8H-5CE1H and moved the buffer from 5CD8H: in 4.20 the last sector of each full buffer was not written. Bug B43: in the last write pass of a long copy SYS2/SYS is no longer in memory, so a destination that needs another directory entry reads SYS2/SYS from drive 0.
Disassembly:
A disassembly of COPY/CMD can be found here.
DOUBLE/CMD
Purpose:
DOUBLE/CMD is the DBLDOS FORMAT/BACKUP UTILITY, version 1.21, which takes the place of FORMAT and BACKUP. DOUBLE FORMAT :n asks for the diskette name, the date (when DoubleDOS has none) and 18-80 cylinders, formats cylinder 0 in single density with ten sectors and the others in double density with eighteen, verifies every sector, locks out the granule of each flawed sector and records it in the five entry flaw table at GAT bytes C0H-C9H, and writes a NO SYSTEM boot sector, the GAT, the HIT and a directory holding DIR/SYS. DOUBLE BACKUP :s TO :d makes a sector for sector copy with one or two drives, formatting the destination first; Tnn gives the copy more cylinders, up to 80, which 4.24 verifies for flaws. DOUBLE/CMD drives the controller and the Doubler itself, with the interrupts off; from SYS0/SYS it takes only the display, date, multiply and divide routines.
4.24 (1.21) differs from 4.20 and 4.23 (1.20): the passes end at the source's size (in 4.23 a one drive backup with a larger Tnn could read past the source's last cylinder and never find the end), the extra cylinders are verified, formatted sectors hold 6DH B6H in place of E5H, two double density gaps changed, and the buffer moved from 5C65H to 5D00H. Bugs B37-B39: after a source sector that cannot be read, the copy goes on with the rest of the pass one sector early on the destination; a flaw on cylinder 0 is booked against GAT byte CBH; three error tests can never be taken.
Disassembly:
A disassembly of DOUBLE/CMD can be found here.