Home Blog About Contact

Tim Paterson's DOS Source Listings

To download the source code, you can go straight to the downloads section.

Background

In early January 2024, just a couple of days after 86-DOS 0.11 and 0.34 were dumped and shared by f15sim, I started a project to reconstruct the source code of 86-DOS 0.11. To help me with my project, I reached out to a few folks, and started a group called the "DOS Disassembly Group", dedicated to the disassembly of 86-DOS 0.11.

Whenever I got stuck on something, a core group member and friend of mine, Rich Cini, would jump in to help. One day in March 2024, I got stuck deciphering pass 2 of the SCP 8086 assembler, because it had been completely rewritten between 86-DOS 0.11 and MS-DOS 1.25, and I was using the sources from the 1.25 release as a reference. Something popped into my head - Tim Paterson told Len Shustek in his email regarding the MS-DOS 1.25 source code that:

I also have a 6" stack of printouts of assembly listings for some of these and probably other related programs.

I wondered whether there were earlier versions of the assembler in those listings to help me with my disassembly. I asked that question in the chat, and Rich immediately shot Tim an email, asking if he still had them, and got a response back!

You must be using disassembly to recreate the source code, so you should be able to disassemble ASM and get the translated source. I don't have pre-translation source for it (written in Z80 assembly language).

My stack of listings (on 15"-wide green/white fanfold) is about 5" high. I looked at some of the ones near the top and you might find them interesting. For example, one is 86DOS.A86 v1.00 4/28/81, which means it used SCP ASM mnemonics instead of MASM that you see in the v1.25 source. There are also listings for ASM (post translation) dated late 1981 and early 1982. I wouldn't scan these listing myself; how would you make it happen?

I was shocked. Wow, 86-DOS 1.00 source code? It is the general consensus that the earliest surviving DOS source code is 1.25, as mentioned by Tim himself several times. Now, this was news. Tim's response sounded like he wanted to help! But stuff happened and life went on, and we put this aside for more than a year. Bill Gates' release of Altair BASIC source listing scans put a thought in my head - what if we released the 86-DOS 1.00 source code on DOS' 45th birthday? With that thought in mind, I shot Tim an email on 2025/04/24, and he replied:

The listing stack is actually only 3.5" (I found a bunch of blank paper at the bottom). Here is the inventory:

The last item was my area of responsibility at MS at the time, and takes up half the stack. Most of the listings are from an MS line printer, and print quality is not great.

Getting around to packing and shipping is the hard part.

Tim Paterson

Ok, wow, there's a lot of stuff here, including multiple snapshots of DOS! Now, we had to jump through a few hoops to actually get the listings. Like Tim said, indeed, shipping was hard. Clearly, getting them shipped to me was out of the question, because Tim's in the US while I'm in Australia. So, getting them shipped to and scanned by Rich is the way to go, but then Tim lives on the West Coast and Rich lives on the East Coast. In the end, Rich's friend, Frank, who lives 10 minutes away from Tim, volunteered to visit Tim in person to grab the listings... but then that was delayed when other matters arose.

Scan 'n Transcribe

Finally, Frank visited Tim on 2025/09/26 and picked up the listings from him.

Tim Paterson in his workshop with the stack of DOS listings. Credits: Frank Ancona.

They were then shipped to Rich, and arrived 6 days later.

A banner page from the listings. Credits: Rich Cini.

The listings came in 10 bundles of connected greenbar paper, in the following order:

Realising that transcribing them would be a non-trivial amount of work, we came up with a plan for how we were going to transcribe the listings and created a GitHub organisation dedicated to the transcription, on the same day the listings arrived. Our approach mirrored that of the UNIX V1 kernel transcription project - doing it page by page and merging the pages together once all pages were done. Rich has a great writeup with some OCR correction tricks as well as info about printers.

Due to the amount of effort required to transcribe the listings, we limited the scope of the project to just the first 8 bundles. While the BASIC-86 Compiler runtime library stuff would be nice to have, we just don't have the bandwidth to transcribe almost 480 pages. After the scope of the project was defined, two things quickly appeared on our radar:

  1. Up to this stage, our group had 3-4 people, of which only Rich and I were active and could devote time to the project.
  2. There's no good solution for OCRing printer listings, because source code is not English text and almost all OCR tools mess up spacing.

While we were searching for solutions, Rich worked on scanning the listings and I worked on organising and processing the raw scans, as well as transcribing bundle 2 to test the waters. At the same time, I roped Joshua Scarsbrook, a friend of mine and a research officer at my university, into the project, to work on a segmenter and OCR program for greenbar printer listings. While the OCR program didn't end up being used for this project, it provided some good research into the segmentation of printer listings, to be used in future projects!

After all the scanning was done, Rich and I worked together to transcribe all of bundles 1-8. (Actually... I cheated for bundle 1 - it's identical to the MS-DOS 1.25 source code released by Microsoft, so I just used the 1.25 kernel listing for it.) Once that was done, our focus shifted to solving mysteries around the listings.

Inventory Breakdown

So, let's take a deep dive into what's in Tim's printouts.

Bundle 1

This is the first bundle, at the very top. It is an assembler listing of the MS-DOS 1.25 kernel. There's nothing special here, it's the exact output you get if you assemble the released MS-DOS 1.25 kernel source code. The listing was generated on 1983/01/26, well after Tim had left Microsoft in March 1982.

Bundle 2

This bundle is a lot more interesting - it contains 86DOS.A86 dated 1981/07/07, printed by user BASLIB-86 using the TOPS-10 spooler on the next day. It is a snapshot of the PC-DOS 1.00 kernel from July, making it a pre-release or "beta" of PC-DOS 1.00. The code is largely identical to that of the final PC-DOS 1.00, with the exception of the INT 13H hook being missing.

The front page has "DATE/TIME FUNCTIONS ADDED FOR IBM CLOCK" written in pencil, and there are several other annotations throughout the listing.

Bundle 3

This bundle contains 2 files, EDLIN.DIF and CHKDSK.A86, both printed by user ROS BASIC on 1981/07/28, using the TOPS-10 spooler.

EDLIN.DIF is a diff of the PC-DOS EDLIN source code from 1981/06/01 against the 86-DOS 1.00 EDLIN source code, generated by the TOPS-10 FILCOM utility on the same day it was printed.

CHKDSK.A86 is a snapshot of the PC-DOS CHKDSK source code from 1981/07/15.

Bundle 4

Bundle 4 contains 86DOS.ASM, the vanilla 86-DOS 1.00 kernel source code. It was printed on 1981/06/16, by user ROS BASIC using the TOPS-10 spooler, with the file itself created a day prior. This is the true original 86-DOS 1.00 kernel, which contains the bug in STORE that Pat Opalka found - a typo when dealing with file sizes greater than 64K. All existing copies of "86-DOS 1.00" are in fact functionally equivalent to 86-DOS 1.01, as they have this bug fixed through patching the kernel.

The front page has "PC-DOS 1.0 NO DATE/TIME FUNCTIONS" written in pencil. While labelled as "PC-DOS 1.0", it's 86-DOS with no modifications for the IBM PC. The terms "QDOS", "86-DOS", "PC-DOS" and "MS-DOS" were all used interchangeably in 1981.

At the end of this bundle, there is a header page for COMAND.DIF, created and printed on the same day as 86DOS.ASM. It's presumably a diff of the PC-DOS 1.00 command interpreter at that point of development, against the 86-DOS 1.00 command interpreter, but nobody knows for sure. Note that it's COMAND.DIF with only one "M", this is due to the TOPS-10 6.3 filename limit.

Bundle 5

Bundle 5 is the assembly listing of a work-in-progress version of the SCP 8086 assembler, version 2.40, printed from 86-DOS with a daisy-wheel printer. It identifies itself as version 2.31, however it has an incomplete 2.40 entry in the changelog - "Add 8087 mnemonics", dated 1981/08/23. The 8087 support is incomplete - several entries in the 8086 mnemonic table are placeholders.

The assembler itself also has some serious bugs, one severe enough to render it completely unusable - with a fix for it written on page 21 in pencil. There are a few other minor annotations distributed throughout the pages.

Bundle 6

Bundle 6 is the assembly listing of SCP 8086 assembler version 2.43, printed from MS-DOS with a dot-matrix printer. The changelog says it's from 1983/01/05, with 1983 mistyped as 1982 (fixed in the next version).

Bundle 7

This bundle contains another copy of CHKDSK.A86, dated 1981/06/15 and printed the next day, by user ROS BASIC using the TOPS-10 spooler. Like the CHKDSK.A86 from bundle 3, this is also the source code for the PC-DOS CHKDSK utility, but a month older.

Bundle 8

This bundle has 86DOS.DIF, a diff generated by FILCOM dated 1981/06/16 and printed the same day, by user ROS BASIC using the TOPS-10 spooler. The diff is between a snapshot of the PC-DOS 1.00 kernel from 1981/06/16 and the 86-DOS 1.00 kernel, and can be applied to the 86-DOS 1.00 kernel from bundle 4 to restore the 1981/06/16 kernel source code.

Bundle 9

This bundle has the build logs and listings of the Microsoft BASIC-86 Compiler runtime library, LIBLST.LOG and BASLIB.PRT. They were both created on 1981/11/13 and printed the same day by user BASLIB-86. As Tim said, this BASIC library was his area of responsibility at Microsoft during that time. The runtime was written in pure 8086 assembly, and assembled using MACRO-86 running under TOPS-10.

Bundle 10

This bundle has 2 files, PAINT.ASM and CIRCLE.ASM, dated 1982/01/06 and 1982/02/04 respectively. Both were printed on 1982/02/06 by user MULTI-PLAN. Not sure what they're for, an educated guess would be that they're part of the BASIC-86 Compiler's runtime, perhaps the graphics package. Of course, they could've also been for Microsoft Multiplan, given the user who printed them.

Download

Special thanks to Tim Paterson for providing the listings and to Scott Hanselman for obtaining approval to release these sources.

Other Sources

Based on the 86-DOS and PC-DOS kernel sources from these listings, I have reconstructed the source code of several other kernels from 1981:

The original kernel sources from the listings are provided here for reference:

You can compare these sources to figure out exactly what changed between different 86-DOS and PC-DOS versions, as well as the difference between 86-DOS and PC-DOS in general.

Compiling/Assembling

Most of the sources here target SCP' ASM assembler, so you will need a copy. It can be obtained from any release of 86-DOS or MS-DOS by SCP. You will also need the HEX2BIN utility from SCP to convert Intel HEX objects produced by the assembler into binaries.

The simplest way to assemble a source file is to run ASM <FILENAME-NO-EXTENSION>, followed by HEX2BIN <FILENAME-NO-EXTENSION>. For example, to assemble 86DOS.ASM into the binary 86DOS.COM, run:

A:ASM 86DOS

Seattle Computer Products 8086 Assembler Version 2.24
Copyright 1979,80,81 by Seattle Computer Products, Inc.



Error Count =    0

A:HEX2BIN 86DOS

A:

Discoveries

86-DOS vs PC-DOS

These listings give us some insight into how PC-DOS 1.00 was developed. The first thing they show is that 86-DOS and PC-DOS were developed separately, unlike later versions where you build MS-DOS or PC-DOS from the same source files using switches. There's no MSVER or IBMVER, nor the hypothetical SCPVER.

File Storage

Next, we see that all DOS source files were stored on Microsoft's DECSYSTEM-20 running TOPS-10, on the DSKC volume. PC-DOS sources used the extension .A86, while 86-DOS sources used the extension .ASM - presumably to allow them to coexist in the same place for easy diffing.

Line Numbers

One thing you may have found odd is that all the DOS source files printed by the TOPS-10 spooler have a 5-digit line number at the beginning of each line. Sometimes the numbers increment linearly, sometimes not.

Because some increments are non-linear, these line numbers must've been in the source files themselves rather than being added by the spooler. So, wouldn't these line numbers induce syntax errors? Well, no, because these line numbers are special - they are called SOS line numbers, produced by the SOS editor Microsoft used on TOPS-10.

ASCII on 8-/16-/32-/64-bit systems is quite straightforward - each 8-bit byte holds a 7-bit ASCII character, starting from bit 0 which is the LSB (least-significant bit). The remaining bit, bit 7 - the MSB (most-significant bit) is unused and set to 0.

MSB           LSB
 7 6 5 4 3 2 1 0
 0 \___________/
         |
     7-bit char

TOPS-10 on the other hand is a 36-bit operating system, which ran on the 36-bit PDP-10 family of computers (including the DECSYSTEM-20 that Microsoft used). ASCII on the 36-bit PDP-10 is a bit more complicated - these systems address things in terms of 36-bit words, and each 36-bit word stores five 7-bit ASCII characters. This scheme gives one unused bit per 5 characters, which has been exploited by programs to store metadata.

MSB                                                                  LSB
00 ....... 06 07 ....... 13 14 ....... 20 21 ....... 27 28 ....... 34 35
\___________/ \___________/ \___________/ \___________/ \___________/ |
      |             |             |             |             |       |____ unused
    char 1        char 2        char 3        char 4        char 5      

(Yes, bit 0 is the MSB which is the left-most bit on the PDP-10. Crazy, isn't it?)

The SOS editor inserts a word at the beginning of each line, which I call the "line number word", holding a 5-digit line number. These "line number words" have the unused bit set to 1, to separate them from regular text words which have the unused bit set to 0. This way, while the line numbers are stored within the text file, they are not a part of the actual text, and hence can easily be ignored or stripped.

For example, a text file with content Hello,\r\nWorld!\r\n and SOS line numbers may look like this when printed:

02670   Hello,
02680   World!

And it's stored in memory and on disk as the following array of 36-bit words:

                      |                        |
MSB               LSB | MSB                LSB | MSB                  LSB
0   <----------->  35 | 0   <----------->   35 | 0   <-------------->  35
'0' '2' '6' '7' '0' 1 | '\t' 'H' 'e' 'l' 'l' 0 | 'o' ',' '\r' '\n' '\0' 0
                      |                        |
----------------------+-----------------------+--------------------------
                      |                        |
MSB               LSB | MSB                LSB | MSB                  LSB
0   <----------->  35 | 0   <------------>  35 | 0   <-------------->  35
'0' '2' '6' '8' '0' 1 | '\t' 'W' 'o' 'r' 'l' 0 | 'd' '!' '\r' '\n' '\0' 0
                      |                        |

As you can see, the line numbers must be 5 characters each, so they cannot exceed 99999 and are 0-padded on the left if they do not have 5 digits. Technically any valid ASCII character may appear in a line number, but in practice only numerical digits are used.

Microsoft's TOPS-10 Setup

From the spooler headers and trailers, we can gather that Microsoft had the following accounts on their DECSYSTEM-20:

The tuple enclosed in square brackets is the PPN (Project-Programmer Number). The first value is the project ID, and the second value is the programmer ID. You can sort of think of them as group ID and user ID in Unix-based systems.

From the account names, we can gather some insight into what was developed or stored on the DECSYSTEM-20. "ROS BASIC" most likely referred to the IBM PC ROM BASIC, which Microsoft developed for IBM to use in the IBM PC 5150. "BASLIB-86" was what Tim worked on during that time - BASIC-86 Compiler runtime library. "MS-DOS" is self-explanatory, and "MULTI-PLAN" was Microsoft Multiplan, either the CP/M version or the DOS version.

File Corruption

If you examine DSKC:86DOS.ASM - the 86-DOS 1.00 kernel source code listed in bundle 4 and diff'd against in the bundle 8 diff, you'll see some weird things.

For example:

00930   ; word right four bits; 5) mask to 12 bits (AND with 0FFF hex). Entry number
00940   ; zero is used as an end-of-file trap in the OS and as a flag for directory
00950   ; entry size (if SMALLDIR selected). Entry 1 is reserved for future use. The
00960   ; first available allocation unit is assigned entry number two, and for Drive Parameter Block
00970   
00980           ORG     0
00990   DRVNUM: DS      1               ;Drive number

Line 00960 is quite a bit longer than the other lines, and the ending doesn't make much sense. Comparing against the same comment in the MS-DOS 1.25 kernel source, we can see that the last part of the FAT comment is truncated, and the second half of the block comment for DPB fields is merged with the truncated FAT comment. The original should've been:

00930   ; word right four bits; 5) mask to 12 bits (AND with 0FFF hex). Entry number
00940   ; zero is used as an end-of-file trap in the OS and as a flag for directory
00950   ; entry size (if SMALLDIR selected). Entry 1 is reserved for future use. The
00960   ; first available allocation unit is assigned entry number two, and even
+++++   ; though it is the first, is called cluster 2. Entries greater than 0FF8H are
+++++   ; end of file marks; entries of zero are unallocated. Otherwise, the contents
+++++   ; of a FAT entry is the number of the next cluster in the file.
+++++   
+++++   
+++++   ; Field definition for Drive Parameter Block
00970   
00980           ORG     0
00990   DRVNUM: DS      1               ;Drive number

The next spot is much more obvious - lines 02790 and 02810 are simply not syntactically correct:

02760           SEG     CS
02770           MOV     AX,[NSP]
02780           SEG     CS
02790           MOV     [SPSA   ENDIF
02800   
02810           PBX
02820           POP     CX
02830           POP     DX
02840           POP     SI

The original code is in bundle 2, as well as in the bundle 8 diff:

02760           SEG     CS
02770           MOV     AX,[NSP]
02780           SEG     CS
02790           MOV     [SPSAVE],AX
+++++           SEG     CS
+++++           MOV     AX,[NSS]
+++++           SEG     CS
+++++           MOV     [SSSAVE],AX
+++++           ENDIF
02800   
02810           POP     AX
+++++           POP     BX
02820           POP     CX
02830           POP     DX
02840           POP     SI

The final spot manifests itself as a syntax error on line 30270:

30240           CALL    GETLET
30250           SUB     AL,"@"          ;Convert drive letter to binary drive number
30260           JZ      NODRV           ;Valid drive numbers are 1-15
30270           INCcifier--back up pointer
30280   DEFAULT:
30290           XOR     AL,AL
30300   HAVDRV:

Again, characters went missing. This time the code actually changed between 86-DOS 1.00 and later versions of DOS, so the later sources aren't of any help for restoring these missing characters. I've figured out the missing instructions using the 1.00 kernel binary, but no guarantees that it's a perfect character-for-character match with the original source code.

30240           CALL    GETLET
30250           SUB     AL,"@"          ;Convert drive letter to binary drive number
30260           JZ      NODRV           ;Valid drive numbers are 1-15
30270           INC     SI
+++++           CMP     AL,15
+++++           JBE     HAVDRV
+++++           DEC     SI
+++++   NODRV:
+++++           DEC     SI              ;Invalid drive specifier--back up pointer
30280   DEFAULT:
30290           XOR     AL,AL
30300   HAVDRV:

So how did these characters go missing? There are a few possible theories, but let's take a look at the facts first.

Given that the PC-DOS 1.00 source code does not have the FAT comment at all, yet MS-DOS 1.25 source code has it, the corruption must've come from outside of SCP. This is because MS-DOS 1.25 is based on 86-DOS 1.13 or 1.14 from SCP, and if the comment in MS-DOS 1.25 is intact, it must've been intact at SCP.

Also worth noting is PC-DOS 1.00 source code does not have the FAT comment, yet has the 86-DOS 1.00 header comment as well as some of the instructions that are missing. This combined with the fact that development of PC-DOS started before 86-DOS 1.00 suggests that:

Clearly, the FAT comment was added after 86-DOS 0.34, because it mentions directory sizes - 0.34 used only 16-byte directory entries. Microsoft started working on porting 86-DOS to the IBM PC in late 1980 (possibly December), and first boot happened on or shortly before 1981/01/25. Whichever version they worked on, it's either 0.34 or earlier, hence could not have had that FAT comment. So, if they were to rebase their changes onto the corrupted 86-DOS 1.00 source code, they would've used their older sources to repair the missing instructions, but since their older sources didn't have the FAT comment, that was not repairable and hence taking it out would make sense. This would result in the PC-DOS 1.00 source code being based off of 86-DOS 1.00, have the missing instructions but missing that FAT comment - like what we see in bundles 2 and 8.

Ok, cool, so where exactly did the corruption happen? I'd say during the transfer from 8" 86-DOS-formatted floppies to the DECSYSTEM-20. Bob O'Rear, who led the PC-DOS project in 1981 wrote in his notes:

Seattle Computers delivers source of QDOS and utilities on 8" QDOS formatted diskette & an absolute assembler.

This was from early 1981, when the project had just started and source files were mostly stored on 8" DOS disks, while the DECSYSTEM-20 was used only for assembling the DOS BIOS. As they progressed, they moved the files to be stored on their DECSYSTEM-20, but I don't think how SCP delivered the source code changed. I imagine they would've used an SCP Gazelle to read the 8" floppies, then transfer source code to the DECSYSTEM-20 via some serial communication protocol like RS-232.

My suspicion is something happened during the serial transfer of the source file to the DECSYSTEM-20 and caused bytes to be dropped silently. A quick example would be a loose wire or bad contact when the receiving end uses RS-232 with pull-down resistors - if the connection is broken, the receiver would simply see no data and no errors, for the duration of the disconnection. It may not be what actually happened, but things like this would explain why missing bytes are all clustered together in chunks rather than spread out throughout the file.

Could it have been disk corruption - like bad sectors? Highly unlikely, because:

The only other plausible explanation for the corruptions would be someone messed up editing the file and accidentally deleted a bunch of characters. This is also unlikely because you have to try quite hard to mess up 3 different sections of the file by purely deleting characters, and have it go unnoticed until after you save the file.

86-DOS 1.00 vs 1.01

Perhaps the most mind-blowing fact we learned from this is that all known copies of 86-DOS 1.00 are functionally 86-DOS 1.01. If you read the MS-DOS 1.25 change log, you'll see this entry:

1.01 05/12/81 Fix bug in `STORE'

It bugged me for quite a while what that bug was. I had to inspect the "1.00" kernel binary in order to fix the corruptions mentioned, and after the fix, the binary from assembling 1.00 source did not match the original "1.00" binary.

A quick diff revealed two differences:

*** 86DOS.ASM
--- 86DOS_101.ASM
***************
*** 1526,1532 ****
        MOV     BL,[SECCLUSPOS]
        CALL    FIGREC
        OR      AL,AL
!       JNZ     SETBUF
        CMP     DX,[BUFSECNO]
        JNZ     GETSEC
        MOV     AL,[BUFDRVNO]
--- 1526,1533 ----
        MOV     BL,[SECCLUSPOS]
        CALL    FIGREC
        OR      AL,AL
!       NOP
!       NOP
        CMP     DX,[BUFSECNO]
        JNZ     GETSEC
        MOV     AL,[BUFDRVNO]
***************
*** 1920,1926 ****
        SEG     ES
        MOV     AX,[DI+FILSIZ]
        SEG     ES
!       MOV     DX,[DI+FILSIZ]
        DIV     AX,[BP+SECSIZ]
        OR      DX,DX
        JZ      NORNDUP
--- 1921,1927 ----
        SEG     ES
        MOV     AX,[DI+FILSIZ]
        SEG     ES
!       MOV     DX,[DI+FILSIZ+2]
        DIV     AX,[BP+SECSIZ]
        OR      DX,DX
        JZ      NORNDUP
  1. The flag for skipping pre-read for BUFSEC is disabled, so that it always does a pre-read before doing a write. I haven't looked into exactly what this fixes.
  2. This is the bug in STORE, where DX:AX is set to the 32-bit FILSIZ field. Original 1.00 stores the lower 16 bits of FILSIZ to both AX and DX, which is clearly wrong. 1.01 fixed it to store the lower 16 bits to AX, and the upper 16 bits to DX.

We can tell it was a binary patch, because of the NOPping out of the conditional jump rather than deleting it as well as the instruction above which sets the condition. Patching of the binary suggests that these were very last minute changes, otherwise they would've just modified the source code and re-assembled the kernel instead of patching.

Given the released "1.00" kernel has the STORE bug fixed, it's functionally 1.01. Apparently this bug was found by Pat Opalka and fixed upon a customer's request, and then the change was sent to Microsoft as Tim Paterson had already left SCP.

Files on TOPS-10

Given that a bunch of the files were originally stored on TOPS-10, can we reconstruct them and put them back on TOPS-10? Absolutely, but it's not that easy.

The first thing to do is to convert the printed files in plaintext to PDP-10 core dump format. Given that 36-bit words don't divide cleanly into 8-bit bytes, storing them on a modern byte-oriented computer is a challenge. Core dump format is the native format that PDP-10 uses for storing 36-bit words using 8-bit bytes on 9-track tapes. One can convert a plain ASCII text file to core dump format using my txt2core utility - it can also turn line numbers in the plaintext into SOS line number words with the -l option.

Once you have the plaintext files converted to core dump format, they can then be added to a backup tape using my fork of Johnny Eriksson's back10 program.

For example, if I want to create a TOPS-10 backup tape with the 86DOS.A86 from bundle 2, I would run:

$ txt2core -l plaintext/86DOS.A86 86DOS.A86
$ back10 -U MT:500,500 -S 86DOS -C -f 86dos.tap -c 86DOS.A86

This would create a TOPS-10 backup tape 86dos.tap in SIMH format, with the file 86DOS.A86<055> owned by [500,500]. You can now use this with the SIMH simulator or write it to a real 9-track tape.

To use it to import 86DOS.A86, at the user console, run:

.MOUNT TAPE: /REELID:86DOS /NOWAIT

Then at the operator console, attach the tape. In SIMH, to attach the tape, run:

OPR>^E
sim> att tu0 86dos.tap
sim> set tu0 lock
sim> c

If prompted about label error, run RESPOND <number> PROCEED at OPR> prompt.

OPR>SHOW QUEUE
OPR>IDENTIFY MTA0: REQUEST-ID <number>

Now that the tape is attached and mounted, at user console, run:

.R BACKUP
/TAPE MT:
/REWIND
/RESTORE DSK:86DOS.A86=MT:86DOS.A86[*,*]
/EXIT

Now 86DOS.A86 is restored from the backup tape - with the same owner and file mode as what Microsoft had.

To unmount the tape, run .DISMOUNT MT: at user console and then OPR>DISMOUNT TAPE-DRIVE MTA0: at operator console.

Assembler Bug - Bitwise Brain-Teaser

As I mentioned earlier - there is a fatal bug in the assembler from bundle 5 that made it completely unusable. What the code wants is to shift bits 6 and 7 of the register CH into bits 0 and 1 of the global variable [RELOC], such that:

The buggy code is:

        MOV     CL,[RELOC]
        ROL     CX
        ROL     CX
        MOV     [RELOC],CL

Do you see what's wrong :)? Let's trace it line by line:

        MOV     CL,[RELOC]  ->  CH:CL = IJKLMNOP:ABCDEFGH, CARRY = ?
        ROL     CX          ->  CH:CL = JKLMNOPA:BCDEFGH0, CARRY = I
        ROL     CX          ->  CH:CL = KLMNOPAB:CDEFGH0I, CARRY = J
        MOV     [RELOC],CL  ->  [RELOC] = CDEFGH0I

As you can see, [RELOC] becomes CDEFGH0I in the end. Clearly, this is not the CDEFGHIJ that the program wants.

So, here's the code written on the listing in pencil:

        MOV     AL,[RELOC]
        RCL     CH
        RCL     AL
        RCL     CH
        RCL     AL
        MOV     [RELOC],AL

Does it fix the bug? Let's trace through it and see!

        MOV     AL,[RELOC]  ->  AL = ABCDEFGH, CH = IJKLMNOP, CARRY = ?
        RCL     CH          ->  AL = ABCDEFGH, CH = JKLMNOP?, CARRY = I
        RCL     AL          ->  AL = BCDEFGHI, CH = JKLMNOP?, CARRY = A
        RCL     CH          ->  AL = BCDEFGHI, CH = KLMNOP?A, CARRY = J
        RCL     AL          ->  AL = CDEFGHIJ, CH = KLMNOP?A, CARRY = B
        MOV     [RELOC],CL  ->  [RELOC] = CDEFGHIJ

Yup, this indeed puts the desired CDEFGHIJ into [RELOC]. A bit of poking around revealed that all versions of the assembler both before and after this snapshot use the "fixed" code, which means Tim likely accidentally introduced this bug while trying to optimise the assembler, and quickly realised the optimisation was incorrect and undid it.

There are other bugs in the assembler as well, all can be fixed by applying this patch:

*** ASM.ASM
--- ASM_FIXED.ASM
***************
*** 1350,1359 ****
  ;Save byte of code in AL, given intermediate code bits in bits 7&8 of CH.
        CALL    PUTINC          ;Save it and bump code pointer
  GEN1:
!       MOV     CL,[RELOC]
!       ROL     CX
!       ROL     CX
!       MOV     [RELOC],CL
        MOV     BX,BCOUNT
        DEC     B,[BX]
        JNZ     RET
--- 1350,1361 ----
  ;Save byte of code in AL, given intermediate code bits in bits 7&8 of CH.
        CALL    PUTINC          ;Save it and bump code pointer
  GEN1:
!       MOV     AL,[RELOC]
!       RCL     CH
!       RCL     AL
!       RCL     CH
!       RCL     AL
!       MOV     [RELOC],AL
        MOV     BX,BCOUNT
        DEC     B,[BX]
        JNZ     RET
***************
*** 2489,2494 ****
--- 2491,2497 ----
        JZ      EXIT
        MOV     AL,1AH
        CALL    WRTBUF          ;Write end-of-file mark
+       MOV     DI,[LSTPNT]
        CALL    FLUSHBUF
        MOV     AX,[FCB+20]     ;Get date of source file
        MOV     [LSTFCB+20],AX
***************
*** 2699,2705 ****
        RET

  FLUSHBUF:
!       MOV     CX,[LSTPNT]
        MOV     DX,LSTBUF
        MOV     DI,DX
        SUB     CX,DX
--- 2702,2708 ----
        RET

  FLUSHBUF:
!       MOV     CX,DI
        MOV     DX,LSTBUF
        MOV     DI,DX
        SUB     CX,DX

Page written by Yufeng Gao. Last updated: 2026/04/12.