{"id":69,"date":"2022-12-28T11:56:00","date_gmt":"2022-12-28T11:56:00","guid":{"rendered":"https:\/\/thebrokenpipe.com\/blog\/?p=69"},"modified":"2026-04-26T09:54:16","modified_gmt":"2026-04-26T09:54:16","slug":"porting-86-dos-1-14-to-the-ibm-pc","status":"publish","type":"post","link":"https:\/\/thebrokenpipe.com\/blog\/porting-86-dos-1-14-to-the-ibm-pc\/","title":{"rendered":"Porting 86-DOS 1.14 to the IBM PC"},"content":{"rendered":"\n<div style=\"padding: 0.5rem 1rem; margin-bottom: 1rem; color: inherit; border-left: .25em solid #8250df;\" dir=\"auto\">\n  <p style=\"display: flex; font-weight: 500; align-items: center; line-height: 1; color: #8250df; font-size: 20px;\" dir=\"auto\"><svg class=\"octicon mr-2\" style=\"margin-right: 0.5rem !important; display: inline-block; overflow: visible !important; vertical-align: text-bottom; fill: currentColor;\" viewBox=\"0 0 20 20\" version=\"1.1\" width=\"20\" height=\"20\" aria-hidden=\"true\"><path d=\"M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z\"><\/path><\/svg>2024 Edit<\/p>\n  <p dir=\"auto\">Following the discovery of other versions of 86-DOS and newer efforts, this port is now <u>outdated<\/u>. The latest ports of 86-DOS to the IBM PC are on <a href=\"https:\/\/github.com\/TheBrokenPipe\/86-DOS_PCAdaptation\">GitHub<\/a> and binaries may be downloaded from there too.<br \/><br \/>Nonetheless, this post is still worth reading because it forms the basis of my newer ports and provides context for some of my later posts.<\/p>\n<\/div>\n\n\n\n<p>A friend challenged me to compile and boot MS-DOS 1.25 on a modern computer. But, I decided to do something slightly more challenging. Instead of MS-DOS, I decided to go with the old and proprietary 86-DOS, which never supported the IBM PC line of computers and does not have source code available. There are only two versions of 86-DOS out in the wild &#8211; 1.00 and 1.14, and I rolled with 1.14 for the task.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"766\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1-1024x766.png\" alt=\"\" class=\"wp-image-78\" style=\"width:500px\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1-1024x766.png 1024w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1-300x224.png 300w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1-768x575.png 768w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1.png 1259w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">86-DOS 1.14 booting on a modern laptop.<\/figcaption><\/figure>\n<\/div>\n\n\n<!--more-->\n\n\n\n<p>Why not 86-DOS 1.00? Well, it has already been done by somebody else (I&#8217;ll talk about it later).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PC-DOS 1.00 BIOS &amp; Boot Sector Disassembly<\/h2>\n\n\n\n<p>Before I began porting 86-DOS, I created <a href=\"\/blog\/reverse-engineering-pc-dos-1-00s-bios-and-boot-sector\">binary-exact disassemblies<\/a> of PC-DOS 1.00&#8217;s <a href=\"assets\/uploads\/2022\/11\/IBMBIO.ASM\">IBMBIO.COM<\/a> and <a href=\"assets\/uploads\/2022\/11\/BOOT.ASM\">boot sector<\/a> to learn about the real-life uses of the PC BIOS interrupts. Instead of rewriting the aforementioned components from scratch, I ended up recycling the disassemblies for this port.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Getting the Files<\/h2>\n\n\n\n<p>The <a href=\"http:\/\/www.bitsavers.org\/bits\/SeattleComputerProducts\/86DOS11T.IMD\">copy of 86-DOS 1.14<\/a> on <a href=\"http:\/\/www.bitsavers.org\">Bitsavers<\/a> is in the IBM 8-inch single-sided single-density 250.25 KiB format. Nothing can read FAT partitions with 128-byte sectors and the CP\/M-style reserved system area, so I extracted the files manually. Luckily, the FAT implementation is the same as the MS-DOS\/PC-DOS one and directory entries are 32 bytes in length, so it didn&#8217;t take me long to extract all 21 files.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Files &amp; Their Fate<\/h2>\n\n\n\n<p>Some files were modified\/patched to work on the PC, some remained unchanged while others were simply removed\/replaced.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>File<\/th><th>Description<\/th><th>Fate<\/th><\/tr><\/thead><tbody><tr><td><code>86DOS.SYS<\/code><\/td><td>86-DOS Kernel<\/td><td>Patched to work on the PC.<\/td><\/tr><tr><td><code>ASM.COM<\/code><\/td><td>8086 Assembler<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><tr><td><code>BOOT.ASM<\/code><\/td><td>Boot Sector Source Code<\/td><td>Replace with (a modified copy of) PC-DOS 1.00&#8217;s boot sector source code.<\/td><\/tr><tr><td><code>CHKDSK.COM<\/code><\/td><td>Disk Check Utility<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><tr><td><code>COMMAND.COM<\/code><\/td><td>Command Interpreter<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><tr><td><code>CPMTAB.ASM<\/code><\/td><td><code>RDCPM<\/code> Config File<\/td><td>Modified to suit the reading of 5.25&#8243; CP\/M-86 disks.<\/td><\/tr><tr><td><code>DATE.COM<\/code><\/td><td>DATE Utility<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><tr><td><code>DEBUG.COM<\/code><\/td><td>Debugger<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><tr><td><code>DOSIO.ASM<\/code><\/td><td>BIOS Source Code<\/td><td>Replaced with (a modified copy of) PC-DOS 1.00&#8217;s BIOS source code.<\/td><\/tr><tr><td><code>EDLIN.COM<\/code><\/td><td>Line Editor<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><tr><td><code>HEX2BIN.COM<\/code><\/td><td>Intel Hex to Binary Converter<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><tr><td><code>INIT.ASM<\/code><\/td><td>Disk Initialiser Source Code<\/td><td>Extensively modified (basically rewritten from scratch) to format 5.25&#8243; single-sided double-density disks on the PC.<\/td><\/tr><tr><td><code>INIT.COM<\/code><\/td><td>Disk Initialiser<\/td><td>Recompiled from the modified source code.<\/td><\/tr><tr><td><code>MAKRDCPM.COM<\/code><\/td><td>RDCPM Configuration Utility<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><tr><td><code>MON.ASM<\/code><\/td><td>SCP 8086 Monitor ROM Source Code<\/td><td>Removed, that ROM chip does not exist on the IBM PC motherboard.<\/td><\/tr><tr><td><code>NEWS.DOC<\/code><\/td><td><code>RDCPM<\/code> and <code>INIT<\/code> Notes<\/td><td>Updated to reflect the changes I made.<\/td><\/tr><tr><td><code>RDCPM.COM<\/code><\/td><td>CP\/M Disk Reader<\/td><td>Patched (recompiled) to read 5.25&#8243; CP\/M-86 disks instead of 8&#8243; CP\/M-80 disks.<\/td><\/tr><tr><td><code>READTHIS.DOC<\/code><\/td><td>Addendum for 86-DOS 1.1<\/td><td>Updated to reflect the changes I made to the operating system.<\/td><\/tr><tr><td><code>SYS.COM<\/code><\/td><td>System Transfer Utility<\/td><td>Extensively modified (basically rewritten) to be compatible with the ported OS.<\/td><\/tr><tr><td><code>TIME.COM<\/code><\/td><td>TIME Utility<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><tr><td><code>TRANS.COM<\/code><\/td><td>Z80 to 8086 Translator<\/td><td>Standard DOS program, unchanged.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Of these 21 files:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>10 remained unchanged<\/li>\n\n\n\n<li>2 were replaced<\/li>\n\n\n\n<li>4 (text files) were edited<\/li>\n\n\n\n<li>1 was patched<\/li>\n\n\n\n<li>3 were recompiled\/rewritten<\/li>\n\n\n\n<li>1 was deleted<\/li>\n<\/ul>\n\n\n\n<p>An extra <code>IO.SYS<\/code> was also added to store the DOS BIOS that would normally be located in the CP\/M-styled system area.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Boot Sector (<code>BOOT.ASM<\/code>)<\/h2>\n\n\n\n<p>Instead of starting from scratch, I opted to tweak the <a href=\"\/blog\/reverse-engineering-pc-dos-1-00s-bios-and-boot-sector#boot-sector\">PC-DOS 1.00 boot sector disassembly<\/a> that I had cooked up earlier. I changed the hard-coded system file names from <code>ibmbio.com<\/code> to <code>IO.SYS<\/code> and from <code>ibmdos.com<\/code> to <code>86DOS.SYS<\/code>. I also added a BIOS Parameter Block (BPB) to it and optimised the code a bit by removing some of the redundant stuff such as code for stripping the MSB from improperly defined strings and code for converting filenames to lowercase.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The DOS BIOS (<code>DOSIO.ASM<\/code>)<\/h2>\n\n\n\n<p>Just like the boot sector, I started off with my <a href=\"\/blog\/reverse-engineering-pc-dos-1-00s-bios-and-boot-sector#bios\">PC-DOS 1.00 BIOS disassembly<\/a>. Below are the modifications I made:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Copied the missing date and time BIOS functions over from the <a href=\"https:\/\/winworldpc.com\/product\/microsoft-ms-dos-oem-adaption-kit\/3x\">MS-DOS 3.30 OEM Adaptation Kit<\/a> and modified them to match the same functions in the PC-DOS 1.10 BIOS.<\/li>\n\n\n\n<li>Removed the divide-by-zero handler (86-DOS 1.14&#8217;s kernel is capable of handling division overflows).<\/li>\n\n\n\n<li>Properly defined the strings (<code>DB<\/code> instead of <code>DM<\/code>) and removed code for stripping the MSB.<\/li>\n\n\n\n<li>Fixed all of the <a href=\"\/blog\/reverse-engineering-pc-dos-1-00s-bios-and-boot-sector#bios-bugs\">bugs<\/a> I identified in Microsoft&#8217;s code.<\/li>\n\n\n\n<li>Replaced various constants with more meaningful expressions (<code>ASM<\/code> 2.40 can do multiplication and division of constants).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">The DOS Kernel (<code>86DOS.SYS<\/code>)<\/h2>\n\n\n\n<p>Although they claimed that the kernel is completely relocatable and should work on any real hardware, it does not work on the PC. The kernel assumes that the DOS BIOS is located at segment <code>40H<\/code>, but it&#8217;s actually located at segment <code>60H<\/code> on the IBM PC (segments <code>40H<\/code> and <code>50H<\/code> are reserved for communication with the ROM BIOS and BASIC). To solve this problem, I patched the kernel to change all instances of the hard coded BIOS segment number from <code>40H<\/code> to <code>60H<\/code>.<\/p>\n\n\n\n<p>After patching those hard-coded BIOS segments, I decided to give booting it a shot. And what do you know? It booted successfully! Yay!<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pc_boot.png\" alt=\"\" class=\"wp-image-81\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pc_boot.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pc_boot-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">86-DOS 1.14 booting up on the IBM PC.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>But, I celebrated too early &#8211; it locked up while listing the directory and stopped responding, even to things like <code>Ctrl + C<\/code> and <code>Ctrl + Alt + Del<\/code> :(.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_dir_hang.png\" alt=\"\" class=\"wp-image-99\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_dir_hang.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_dir_hang-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">86-DOS 1.14 hanging the machine while listing the directory.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>This was a rather significant problem. Pretty much everything, for example typing too fast, could hang the machine. It took me a whole day of debugging to arrive at the diagnosis that a stack overflow overwrote the saved <code>SS<\/code> and <code>SP<\/code>, causing the next return to take <code>CS:IP<\/code> to some random place in memory. But surely, Tim Paterson or somebody else tested 86-DOS 1.14 beyond just seeing it boot before releasing it to their customers, right?<\/p>\n\n\n\n<p>The next day, I tried it in DOSBox, and interestingly enough, it did not lock up or crash. How? Shouldn&#8217;t it stack overflow? I spent the next 3 hours tracing every single instruction on the (emulated) IBM PC, and guess what? The stack overflow happened inside <code>INT 13H<\/code>! Who would&#8217;ve guessed that?<\/p>\n\n\n\n<p>The way BIOS interrupts are handled in DOSBox is very different compared to how they are handled on an actual PC. Under DOSBox, BIOS interrupts are simulated by the DOSBox code, meaning they do not use or make changes to the stack. The real (or emulated) IBM PC, on the other hand, uses the BIOS ROM to interact with the hardware, and the ROM code uses the stack to save registers and temporary data. Since we call BIOS ROM code to perform inputs and outputs (instead of communicating directly with the hardware), a larger stack is required.<\/p>\n\n\n\n<p>To prevent stack overflows when running 86-DOS 1.14 on the IBM PC, I increased the I\/O stack (<code>IOSTACK<\/code>) size and the disk stack (<code>DSKSTACK<\/code>) size from <code>26H<\/code> and <code>3CH<\/code> both to <code>80H<\/code> (same as MS-DOS 1.25). It was a straightforward patch since the stack area was at the very end of the binary. I simply changed the values to be put into the <code>SP<\/code> register and modified a few adjustment factors. And as expected, the stack overflows and lockups disappeared!<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_dir_succeed.png\" alt=\"\" class=\"wp-image-98\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_dir_succeed.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_dir_succeed-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">86-DOS 1.14 successfully completing a directory listing.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>I also customised the special editing commands to align with those of PC-DOS 1.00, as OEMs and users were intended to tailor them to their terminals or needs. Since 86-DOS does not support insert mode toggling, I assigned the <code>F9<\/code> key as the enter insert mode key and the <code>F10<\/code> key as the exit insert mode key. In PC-DOS, the <code>INS<\/code> key is used to toggle insert mode on\/off. This is the only difference between the editing commands of my 86-DOS 1.14 port and PC-DOS 1.00. Here is a list of the special editing commands for my 86-DOS 1.14 port in case you are not familiar with the PC-DOS ones:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Key<\/th><th>Function<\/th><\/tr><\/thead><tbody><tr><td><code>F1<\/code><\/td><td>Copy one character from template.<\/td><\/tr><tr><td><code>F2<\/code><\/td><td>Copy up to specified character.<\/td><\/tr><tr><td><code>F3<\/code><\/td><td>Copy rest of template.<\/td><\/tr><tr><td><code>F4<\/code><\/td><td>Skip up to specified character.<\/td><\/tr><tr><td><code>F5<\/code><\/td><td>Cancel line and update template.<\/td><\/tr><tr><td><code>F6<\/code><\/td><td>Kill line (no change to template).<\/td><\/tr><tr><td><code>F7<\/code><\/td><td>Escape sequence to represent escape character.<\/td><\/tr><tr><td><code>F8<\/code><\/td><td>(Unused)<\/td><\/tr><tr><td><code>F9<\/code><\/td><td>Enter insert mode.<\/td><\/tr><tr><td><code>F10<\/code><\/td><td>Exit insert mode.<\/td><\/tr><tr><td><code>&lt;\u2014\u2014<\/code><\/td><td>Backspace (same as <code>Ctrl + H<\/code>).<\/td><\/tr><tr><td><code>\u2014\u2014&gt;<\/code><\/td><td>Copy one character from template (same as <code>F1<\/code>).<\/td><\/tr><tr><td><code>DEL<\/code><\/td><td>Skip over one character in template.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">The Disk Initialiser (<code>INIT.ASM<\/code>\/<code>INIT.COM<\/code>)<\/h2>\n\n\n\n<p>Unlike PC-DOS or MS-DOS, 86-DOS did not come with <code>FORMAT<\/code>. Instead, you need to use its low-level disk formatter\/initialiser called <code>INIT<\/code>. <code>INIT<\/code> is much simpler compared to <code>FORMAT<\/code>, it only performs a low-level format of the disk. You must use the internal <code>CLEAR<\/code> command to place the FAT and root directory onto the formatted disk, and the <code>SYS<\/code> command to copy the system files and boot sector. SCP&#8217;s <code>INIT<\/code> never supported the NEC \u00b5PD765 floppy disk controller used by the IBM PC, so I essentially rewrote it from scratch. The good thing is that the PC BIOS ROM takes care of all the hard work, so I managed to make it work by simply executing a few <code>INT 13H<\/code> calls. Here is a basic description of the two functions I wrote:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Function<\/th><th>Description<\/th><\/tr><\/thead><tbody><tr><td><code>FMTTRK<\/code><\/td><td>Formats a track. On entry, <code>AL<\/code> = track number, <code>AH<\/code> = head number, <code>CL<\/code> = sectors per track, <code>CH<\/code> = sector size (<code>0<\/code> = 128, <code>1<\/code> = 256, <code>2<\/code> = 512, <code>3<\/code> = 1024), <code>DL<\/code> = drive number, <code>DH<\/code> = number of retries and <code>ES:BX<\/code> points to a buffer with 4 \u00d7 <code>CL<\/code> bytes of free space.<\/td><\/tr><tr><td><code>FMTDSK<\/code><\/td><td>Formats a 5.25&#8243; SSDD floppy disk. 40 tracks starting from 0 is formatted to each contain eight 512-byte sectors using the <code>FMTTRK<\/code> function.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>I only tested it inside emulators, but it should be able to format physical floppy disks too.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The System Transfer Utility (<code>SYS.COM<\/code>)<\/h2>\n\n\n\n<p>This one took me quite some time. The original <code>SYS<\/code> tool copies the first track of the disk in drive A along with <code>86DOS.SYS<\/code> onto the destination disk. In my IBM PC port, <code>SYS<\/code> needs to copy solely the first sector of the first track (the boot sector) along with both <code>IO.SYS<\/code> and <code>86DOS.SYS<\/code>. I disassembled the original <code>SYS.COM<\/code> and essentially rewrote it, staying true to the original code. The new <code>SYS<\/code> now behaves differently depending on the state of the disks, with four possible states:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Is <code>IO.SYS<\/code> on Destination Disk<\/th><th>Is <code>86DOS.SYS<\/code> on Destination Disk<\/th><th>Outcome and Description<\/th><\/tr><\/thead><tbody><tr><td>No<\/td><td>No<\/td><td>Can proceed. Must check to see if there are other files on the disk. If no other files are found, the disk is considered blank. If other files are found, then it is unlikely for the first few sectors and the first two directory entries to be free. Proceed if the disk is blank, otherwise throw an error.<\/td><\/tr><tr><td>Yes<br>No<\/td><td>No<br>Yes<\/td><td>Cannot proceed. Either <code>IO.SYS<\/code> or <code>86DOS.SYS<\/code> is on the disk, but not both. If <code>IO.SYS<\/code> is the first entry and the first file on the disk, and we have enough space for DOS (the second directory entry is free, and there are enough free sectors following <code>IO.SYS<\/code>), then the disk can be turned into a system disk, but that is highly unlikely. Simply throw an error.<\/td><\/tr><tr><td>Yes<\/td><td>Yes<\/td><td>Can proceed. The disk will refuse to boot correctly if <code>IO.SYS<\/code> and <code>86DOS.SYS<\/code> are not the first two entries and files. The system files on the destination disk must not be smaller than the system files on the source disk.<br>This version of <code>SYS<\/code> does not check for the positions of the system files and their sizes, therefore the user should be aware of the limitations.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>While it may sound quite limited, in reality, it handles all the cases that the original <code>SYS<\/code> is capable of handling. By the way, just like the original <code>SYS<\/code>, it does not copy the date stamps of the system files. Adding features or fixing bugs isn&#8217;t really a part of porting something &#8211; I want ported apps to behave in the exact same way, just on a different computer\/platform.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">CP\/M Disk Reader (<code>RDCPM.COM<\/code> and <code>CPMTAB.ASM<\/code>)<\/h2>\n\n\n\n<p>This utility took even longer to port than <code>SYS<\/code>. It&#8217;s supposed to copy files from CP\/M-80 disks to 86-DOS disks, but the IBM PC does not support reading 8&#8243; CP\/M-80 disks, at least not without hacks. So, I decided to make it read CP\/M-86 disks instead. In theory, modifying <code>CPMTAB.ASM<\/code> alone should be enough to make <code>RDCPM<\/code> read CP\/M-86 disks (both CP\/M-80 2.2 and CP\/M-86 1.0 used the same filesystem), but in reality it&#8217;s not that simple. With that said, here is the standard definition for the 8 SPT 5.25&#8243; SSDD format used by CP\/M-86:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>;Below is the definition for standard double-density 5.25\" drives\n\nIBM:\n\tDW\t32\t;Sectors per track\n\tDB\t3\t;Block shift\n\tDB\t7\t;Block mask\n\tDB\t0\t;Extent mask\n\tDW\t155\t;Disk size - 1\n\tDW\t63\t;Directory entries - 1\n\tDS\t4\t;Not used\n\tDW\t1\t;Tracks to skip\n\tDW\tMOD32\t;Modulo-32 sector translate table\n\nMOD32:\n\tDB\t00,01,02,03,04,05,06,07\n\tDB\t08,09,10,11,12,13,14,15\n\tDB\t16,17,18,19,20,21,22,23\n\tDB\t24,25,26,27,28,29,30,31<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #357B42; font-style: italic\">;Below is the definition for standard double-density 5.25&quot; drives<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">IBM:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DW<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">32<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Sectors per track<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DB<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">3<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Block shift<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DB<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">7<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Block mask<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DB<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">0<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Extent mask<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DW<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">155<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Disk size - 1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DW<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">63<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Directory entries - 1<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DS<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">4<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Not used<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DW<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">1<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Tracks to skip<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DW<\/span><span style=\"color: #002339\">\tMOD32\t<\/span><span style=\"color: #357B42; font-style: italic\">;Modulo-32 sector translate table<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">MOD32:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DB<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">00<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">01<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">02<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">03<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">04<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">05<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">06<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">07<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DB<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">08<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">09<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">10<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">11<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">12<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">13<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">14<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">15<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DB<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">16<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">17<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">18<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">19<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">20<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">21<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">22<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">23<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #0991B6\">DB<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">24<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">25<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">26<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">27<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">28<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">29<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">30<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">31<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The reason why simply replacing the original table with this table wouldn&#8217;t work is that <code>RDCPM<\/code> treats the <code>SPT<\/code> field of the CP\/M Disk Parameter Block (DPB) as the number of physical sectors per track, not the number of 128-byte records that <a href=\"#cpm-dpb\">it&#8217;s supposed to be treating as<\/a>. Furthermore, the physical sector size must also be 128 bytes, as it uses <code>INT 25H<\/code> for reading.<\/p>\n\n\n\n<p>I was stuck on this issue for an entire weekend before I came up with the idea of adding buffered I\/O to <code>RDCPM<\/code>. One of the paragraphs from the <a href=\"http:\/\/bitsavers.org\/pdf\/seattleComputer\/86-DOS_0.3_Programmers_Manual_1980.pdf\">86-DOS 0.3 Programmer\u2019s Manual<\/a> says:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>[&#8230;] Thus if disk I\/O is being buffered in memory, as would be the case if physical sector size is greater than 128 bytes [&#8230;]. Version 1.0 of 86-DOS will automatically handle physical sector sizes larger than 128 bytes and buffering in the I\/O area will no longer be necessary.<\/p>\n<\/blockquote>\n\n\n\n<p>It&#8217;s not hard to deduce that 86-DOS 0.3 did not support physical sector sizes larger than 128 bytes, so I\/O buffering was necessary for disks with larger sectors. It&#8217;s almost identical to the current situation &#8211; if sector buffering tricked 86-DOS 0.3 into believing that it&#8217;s operating on a disk with 128-byte physical sectors, <code>RDCPM<\/code> can be tricked into believing the same thing too.<\/p>\n\n\n\n<p>How does I\/O buffering work? Let&#8217;s assume logical sectors are 128 bytes and physical sectors are 512 bytes. Say that the <code>RDCPM<\/code> program wants to read logical sector 98, I will have to read in physical sector 24 <em>(\u230a98 \u00f7 4\u230b)<\/em>, divide it into 4 virtual 128-byte sectors in memory and return virtual sector 2 <em>(98 &#8211; 24 \u00d7 4)<\/em>. If <code>RDCPM<\/code> is doing a sequential read and now wants logical sector 99, I can simply return virtual sector 3 of the physical sector currently in memory. When it asks for logical sector 100, I&#8217;ll have to read in physical sector 25 <em>(\u230a100 \u00f7 4\u230b)<\/em> and return virtual sector 0 of it&#8230; and so on.<\/p>\n\n\n\n<p>I spent some time on a binary-exact disassembly of <code>RDCPM<\/code> so that I could make changes at the source code level. Afterwards, I added two functions:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>;Reads a physical sector\nREADPHYS:\n\tPUSH\tBX\n\tMOV\tBX,PHYSBUF\n\tPUSH\tBP\n\tINT\t25H\n\tPOP\tAX\t\t;Throw saved flags away\n\tPOP\tBP\n\tPOP\tBX\n\tRET\n\n;Copy 128 bytes from physical buffer to DS:BX\n;AL = 0 1 2 3\nBUFFCPY:\n\tPUSH\tSI\n\tPUSH\tDI\n\tMOV\tDI,BX\n\tMOV\tSI,PHYSBUF\n\tOR\tAL,AL\n\tJZ\tDOBUFCPY\n\n\tPUSH\tAX\nINCPTR:\n\tDEC\tAL\n\tADD\tSI,128\n\tOR\tAL,AL\n\tJNZ\tINCPTR\n\tPOP\tAX\n\nDOBUFCPY:\n\tPUSH\tCX\n\tMOV\tCX,64\n\tREP\n\tMOVSW\n\tPOP\tCX\n\tPOP\tDI\n\tPOP\tSI\n\tRET<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #357B42; font-style: italic\">;Reads a physical sector<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">READPHYS:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">PUSH<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">BX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">BX<\/span><span style=\"color: #002339\">,PHYSBUF<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">PUSH<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">BP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">INT<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">25H<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AX<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Throw saved flags away<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">BP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">BX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">RET<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #357B42; font-style: italic\">;Copy 128 bytes from physical buffer to DS:BX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #357B42; font-style: italic\">;AL = 0 1 2 3<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">BUFFCPY:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">PUSH<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">SI<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">PUSH<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DI<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DI<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">BX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">SI<\/span><span style=\"color: #002339\">,PHYSBUF<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">OR<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">AL<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JZ<\/span><span style=\"color: #002339\">\tDOBUFCPY<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">PUSH<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">INCPTR:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">DEC<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">ADD<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">SI<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">128<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">OR<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">AL<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JNZ<\/span><span style=\"color: #002339\">\tINCPTR<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AX<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">DOBUFCPY:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">PUSH<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">CX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">CX<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">64<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">REP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOVSW<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">CX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DI<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">SI<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">RET<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The <code>READPHYS<\/code> function reads a physical sector into the 512-byte physical sector buffer. The <code>BUFFCPY<\/code> function copies a virtual 128-byte sector to <code>DS:BX<\/code>, taking <code>AL<\/code> as the index of the virtual sector.<\/p>\n\n\n\n<p>After adding these two functions, I modified the disk reading routine to work out whether a new physical sector should be read, and which virtual sector needs to be returned. I changed the following lines of the disk reading function:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\tPUSH\tBP\n\tINT\t25H\n\tPOP\tAX\t\t;Throw saved flags away\n\tPOP\tBP\n\tJNC\tREADDONE<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">PUSH<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">BP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">INT<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">25H<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AX<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Throw saved flags away<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">BP<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JNC<\/span><span style=\"color: #002339\">\tREADDONE<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>to:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\tJP\tFINDSECT\n\nUPDBUF:\n\tMOV\t&#91;LASTREAD&#93;,DX\n\tMOV\tB,AL,&#91;SRCDRIVEID&#93;\n\tCALL\tREADPHYS\n\tPOP\tDX\n\tJC\tREADERR\n\nFINDSECT:\n\tMOV\tAX,DX\n\tPUSH\tDX\n\tSHR\tDX\n\tSHR\tDX\n\tCMP\tDX,&#91;LASTREAD&#93;\n\tJNZ\tUPDBUF\n\tSHL\tDX\n\tSHL\tDX\n\tSUB\tAX,DX\n\tPOP\tDX\n\tCALL\tBUFFCPY\n\tJMP\tREADDONE\n\nREADERR:<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JP<\/span><span style=\"color: #002339\">\tFINDSECT<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">UPDBUF:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t&#91;LASTREAD&#93;,<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\tB,<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,&#91;SRCDRIVEID&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">CALL<\/span><span style=\"color: #002339\">\tREADPHYS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JC<\/span><span style=\"color: #002339\">\tREADERR<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">FINDSECT:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AX<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">PUSH<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">SHR<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">SHR<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">CMP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><span style=\"color: #002339\">,&#91;LASTREAD&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JNZ<\/span><span style=\"color: #002339\">\tUPDBUF<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">SHL<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">SHL<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">SUB<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AX<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">CALL<\/span><span style=\"color: #002339\">\tBUFFCPY<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JMP<\/span><span style=\"color: #002339\">\tREADDONE<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">READERR:<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Basically, instead of reading the 512-byte physical sector <code>DX<\/code> to the 128-byte buffer pointed to by <code>DS:BX<\/code>, I made it:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Take the floor of <code>DX<\/code> \u00f7 4 (which gives the index of the physical sector to read) and compare it against the <code>LASTREAD<\/code> variable (which contains the index of the current physical sector in memory).<\/li>\n\n\n\n<li>If the physical sector to read is the same as the current physical sector in memory, then work out the index of the virtual 128-byte sector, put it in <code>AL<\/code> and call <code>BUFFCPY<\/code> to copy it to <code>DS:BX<\/code>.<\/li>\n\n\n\n<li>If the physical sector to read is not the same as the current physical sector in memory, then read the desired physical sector before copying the desired virtual sector to <code>DS:BX<\/code>.<\/li>\n<\/ul>\n\n\n\n<p>After making the above changes, I recompiled the program and tested it with a CP\/M-86 disk. It worked perfectly.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_rdcpm_list.png\" alt=\"\" class=\"wp-image-97\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_rdcpm_list.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_rdcpm_list-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">RDCPM listing the directory of a CP\/M-86 disk.<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_rdcpm_copy.png\" alt=\"\" class=\"wp-image-96\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_rdcpm_copy.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_rdcpm_copy-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">RDCPM successfully copying a file from the CP\/M-86 disk to a DOS disk.<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Bashing the Last Bug<\/h2>\n\n\n\n<p>Two days after I thought I had finished the port, I realised I hadn&#8217;t actually tested third-party programs. Later that day, I gathered a few period-correct (released in 1981) programs and ran them under this IBM PC port of 86-DOS 1.14.<\/p>\n\n\n\n<p>A pre-release of Microsoft Pascal from late 1981 (<a href=\"http:\/\/www.bitsavers.org\/bits\/SeattleComputerProducts\/MSPASPR1.IMD\">disk 1<\/a>, <a href=\"http:\/\/www.bitsavers.org\/bits\/SeattleComputerProducts\/MSPASPR2.IMD\">disk 2<\/a>) is on <a href=\"http:\/\/www.bitsavers.org\/\">Bitsavers<\/a>. I copied the files onto four 160 KiB disks, fired it up, and it worked flawlessly.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pas_link.png\" alt=\"\" class=\"wp-image-95\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pas_link.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pas_link-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">Linking the object file produced by Microsoft Pascal 1.10.<\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pas_run.png\" alt=\"\" class=\"wp-image-94\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pas_run.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pas_run-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">The compiled executable running correctly under 86-DOS 1.14.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>That gave me some confidence, so I went ahead and tested all the other programs I managed to find.<\/p>\n\n\n\n<p>I tried both versions of <a href=\"http:\/\/www.bitsavers.org\/bits\/SeattleComputerProducts\/BASIC_5.21.IMD\">Microsoft BASIC 5.21<\/a> under my PC port of 86-DOS 1.14. The newer version from November caused the system to lock up, and according to the readme, it expects more than 64 KiB of RAM. So, I turned my attention to the older version from August, and it worked&#8230; well, not really.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_basic1.png\" alt=\"\" class=\"wp-image-93\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_basic1.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_basic1-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">Microsoft BASIC-86 before screwing up.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>It appeared to be working, but after typing in about three characters, the screen filled up with repeated patterns of random characters, and then it stopped responding.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_basic2.png\" alt=\"\" class=\"wp-image-92\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_basic2.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_basic2-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">Microsoft BASIC-86 after typing in 3 characters.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>I then increased the system RAM to 128 KiB and tested the November version, the same problem as the August version. Maybe it&#8217;s an emulator bug? Nope, inside the same emulator, both versions worked perfectly fine under PC-DOS 1.00 and PC-DOS 1.10.<\/p>\n\n\n\n<p>I didn&#8217;t know what to do, so I simply moved on and tested the next program, SCP Super Editor 1.00. It is a knock-off of Word-Master &#8211; the program itself literally says that. I booted up 86-DOS 1.14 and launched <code>WM.COM<\/code> (yes, the exact same name as Word-Master&#8217;s executable) to create a new document. At first glance, it was working correctly.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm1.png\" alt=\"\" class=\"wp-image-91\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm1.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm1-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">SCP Super Editor before exploding.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>But after pressing a key on the keyboard, it started typing random characters and exclamation marks on its own.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm2.png\" alt=\"\" class=\"wp-image-90\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm2.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm2-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">SCP Super Editor typing random characters on its own.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>And after a while, it started filling the screen up with this weird pattern:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm3.png\" alt=\"\" class=\"wp-image-89\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm3.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm3-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">SCP Super Editor filling the screen up with a weird pattern.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>I rebooted the machine and opened up an existing document. To my amazement, it&#8230; worked. But, of course, not in the intended way.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm4.png\" alt=\"\" class=\"wp-image-88\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm4.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm4-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">SCP Super Editor displaying an existing document in a weird way.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>It did actually display the document, but with an exclamation mark in between each character. Also, it did not stop printing characters to the console after reaching the end of the document. After about a minute, the screen got filled up with the same thing repeated over and over.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm5.png\" alt=\"\" class=\"wp-image-87\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm5.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm5-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">Screen filling up with the same lines of text.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>I then tested it under PC-DOS 1.00, and just like BASIC, it worked impeccably. Bruh. I spent the rest of that day debugging the problem, but unfortunately, I didn&#8217;t manage to find anything useful. The next day, I stopped debugging the programs and started comparing the PC-DOS 1.00 and 86-DOS 1.14 kernels in IDA. The kernels are actually quite similar, except for differences in the undocumented DPB structure and file I\/O routines, where the 86-DOS kernel checks the first byte of the FAT for <code>0xFF<\/code>, perhaps to support disks with 16-byte directory entries. I ignored those differences and kept comparing until I hit syscall 11 (internally called <code>CONSTAT<\/code>). It&#8217;s at offset <code>0x1118<\/code> in the PC-DOS 1.00 kernel and at offset <code>0x11FD<\/code> in the 86-DOS 1.14 kernel.<\/p>\n\n\n\n<p>86-DOS 1.14&#8217;s <code>CONSTAT<\/code> function:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>CODE:11FD CONSTAT:\nCODE:11FD                 call    far ptr 40h:3   ;STATUS\nCODE:1202                 jz      short RET19\nCODE:1204                 or      al, 0FFh\nCODE:1206                 retn<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #002339\">11FD <\/span><span style=\"color: #7EB233\">CONSTAT:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #002339\">11FD                 <\/span><span style=\"color: #7B30D0\">call<\/span><span style=\"color: #002339\">    <\/span><span style=\"color: #DA5221\">far<\/span><span style=\"color: #002339\"> ptr <\/span><span style=\"color: #174781\">40h<\/span><span style=\"color: #002339\">:<\/span><span style=\"color: #174781\">3<\/span><span style=\"color: #002339\">   <\/span><span style=\"color: #357B42; font-style: italic\">;STATUS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #174781\">1202<\/span><span style=\"color: #002339\">                 <\/span><span style=\"color: #7B30D0\">jz<\/span><span style=\"color: #002339\">      short RET19<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #174781\">1204<\/span><span style=\"color: #002339\">                 <\/span><span style=\"color: #7B30D0\">or<\/span><span style=\"color: #002339\">      <\/span><span style=\"color: #174781\">al<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #174781\">0FFh<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #174781\">1206<\/span><span style=\"color: #002339\">                 <\/span><span style=\"color: #7B30D0\">retn<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>PC-DOS 1.00&#8217;s <code>CONSTAT<\/code> function:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>CODE:1118 CONSTAT:\nCODE:1118                 call    far ptr 60h:3   ;STATUS\nCODE:111D                 mov     al, 0\nCODE:111F                 jz      short RET19\nCODE:1121                 or      al, 0FFh\nCODE:1123                 retn<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #174781\">1118<\/span><span style=\"color: #002339\"> <\/span><span style=\"color: #7EB233\">CONSTAT:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #174781\">1118<\/span><span style=\"color: #002339\">                 <\/span><span style=\"color: #7B30D0\">call<\/span><span style=\"color: #002339\">    <\/span><span style=\"color: #DA5221\">far<\/span><span style=\"color: #002339\"> ptr <\/span><span style=\"color: #174781\">60h<\/span><span style=\"color: #002339\">:<\/span><span style=\"color: #174781\">3<\/span><span style=\"color: #002339\">   <\/span><span style=\"color: #357B42; font-style: italic\">;STATUS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #174781\">111D<\/span><span style=\"color: #002339\">                 <\/span><span style=\"color: #7B30D0\">mov<\/span><span style=\"color: #002339\">     <\/span><span style=\"color: #174781\">al<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #174781\">0<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #002339\">111F                 <\/span><span style=\"color: #7B30D0\">jz<\/span><span style=\"color: #002339\">      short RET19<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #174781\">1121<\/span><span style=\"color: #002339\">                 <\/span><span style=\"color: #7B30D0\">or<\/span><span style=\"color: #002339\">      <\/span><span style=\"color: #174781\">al<\/span><span style=\"color: #002339\">, <\/span><span style=\"color: #174781\">0FFh<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">CODE:<\/span><span style=\"color: #174781\">1123<\/span><span style=\"color: #002339\">                 <\/span><span style=\"color: #7B30D0\">retn<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The difference is quite easy to spot &#8211; PC-DOS 1.00&#8217;s <code>CONSTAT<\/code> function moves <code>0<\/code> into the <code>AL<\/code> register after calling the <code>STATUS<\/code> function. The official documentation of the <code>CONSTAT<\/code> function says:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>11 &#8211; Check console status. If a character is waiting at the console, AL will be FF hex on return. Otherwise, AL will be 00.<\/p>\n<\/blockquote>\n\n\n\n<p>And the documentation of the <code>STATUS<\/code> function states:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>STATUS &#8211; Console input status<br><br>If a character is ready at the console, this routine returns a non-zero value in AL and the zero flag is clear. If no character is ready, AL returns zero and the zero flag is set. No registers other than AL may be changed.<\/p>\n<\/blockquote>\n\n\n\n<p>Hold on, if no character is ready, then <code>AL<\/code> should be <code>0<\/code> after returning from <code>STATUS<\/code>, right? So why move <code>0<\/code> to <code>AL<\/code> again? The extra <code>MOV AL,0<\/code> instruction in PC-DOS 1.00&#8217;s <code>CONSTAT<\/code> function seems redundant&#8230; but is it? Let&#8217;s take a look at PC-DOS 1.00&#8217;s <code>STATUS<\/code> function:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>;\n; STATUS - Console input status\n;\n; AL contains the returned character, all other registers must be\n; preserved. ZF set if character not ready, otherwise ZF is cleared.\n;\nSTATUS:\n\tSEG\tCS\n\tMOV\tAL,&#91;LASTCHAR&#93;\t;Fetch the last returned char\n\tOR\tAL,AL\t\t;Get flags for it\n\tJNZ\tSTATUSRET\t;Last char is not 0, return it\n\tPUSH\tDX\t\t;Save DX\n\tXCHG\tAX,DX\t\t;Save value of AX to DX\n\tMOV\tAH,1\t\t;Function = get keystroke status\n\tINT\t16H\t\t;Call keyboard BIOS service\n\tJZ\tSTATUSDONE\t;No key pressed\n\tCMP\tAX,7200H\t;Check if key is CTRL + PRTSCR\n\tJNZ\tSTATUSDONE\t;No, skip the next bit\n\tMOV\tAL,10H\t\t;Yes, set to CTRL + P\n\tOR\tAL,AL\t\t;Get ZF for the character\n\nSTATUSDONE:\t;Restore registers for STATUS\n\tMOV\tAH,DH\t\t;Restore AH from value saved in DX\n\tPOP\tDX\t\t;Restore saved DX\n\nSTATUSRET:\n\tRET\tL<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #357B42; font-style: italic\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #357B42; font-style: italic\">; STATUS - Console input status<\/span><\/span>\n<span class=\"line\"><span style=\"color: #357B42; font-style: italic\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #357B42; font-style: italic\">; AL contains the returned character, all other registers must be<\/span><\/span>\n<span class=\"line\"><span style=\"color: #357B42; font-style: italic\">; preserved. ZF set if character not ready, otherwise ZF is cleared.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #357B42; font-style: italic\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">STATUS:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\tSEG\t<\/span><span style=\"color: #174781\">CS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,&#91;LASTCHAR&#93;\t<\/span><span style=\"color: #357B42; font-style: italic\">;Fetch the last returned char<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">OR<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Get flags for it<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JNZ<\/span><span style=\"color: #002339\">\tSTATUSRET\t<\/span><span style=\"color: #357B42; font-style: italic\">;Last char is not 0, return it<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">PUSH<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Save DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">XCHG<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AX<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">DX<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Save value of AX to DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AH<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">1<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Function = get keystroke status<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">INT<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">16H<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Call keyboard BIOS service<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JZ<\/span><span style=\"color: #002339\">\tSTATUSDONE\t<\/span><span style=\"color: #357B42; font-style: italic\">;No key pressed<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">CMP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AX<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">7200H<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Check if key is CTRL + PRTSCR<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JNZ<\/span><span style=\"color: #002339\">\tSTATUSDONE\t<\/span><span style=\"color: #357B42; font-style: italic\">;No, skip the next bit<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">10H<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Yes, set to CTRL + P<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">OR<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Get ZF for the character<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">STATUSDONE:<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Restore registers for STATUS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AH<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">DH<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Restore AH from value saved in DX<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">POP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">DX<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Restore saved DX<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">STATUSRET:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">RET<\/span><span style=\"color: #002339\">\tL<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Wait a minute, after calling <code>INT 16H<\/code>, if <code>ZF<\/code> is set (no character is ready), it simply returns. What about the value of <code>AL<\/code>? The documentation of <code>INT 16H<\/code> doesn&#8217;t say anything about the content of the <code>AX<\/code> register if no character is ready, so shouldn&#8217;t <code>AL<\/code> be explicitly set to <code>0<\/code> before returning, as stated in the documentation of the <code>STATUS<\/code> function? Another PC-DOS BIOS bug, eh?<\/p>\n\n\n\n<p>Interestingly enough, Microsoft fixed it by making changes to the DOS kernel. Well, they had the source code for both the BIOS and the kernel, so for them, it really didn&#8217;t matter which one to change.<\/p>\n\n\n\n<p>I don&#8217;t have the source code for the kernel, so I modified the <code>STATUS<\/code> function to return <code>0<\/code> if no character is ready.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro padding-disabled\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:8;tab-size:var(--cbp-tab-width, 2)\"><span role=\"button\" tabindex=\"0\" style=\"color:#002339;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\t...\n\tINT\t16H\t\t;Call keyboard BIOS service\n\tJZ\tNOKEYPRES\t;No key pressed\n\tCMP\tAX,7200H\t;Check if key is CTRL + PRTSCR\n\tJNZ\tSTATUSDONE\t;No, skip the next bit\n\tMOV\tAL,10H\t\t;Yes, set to CTRL + P\n\tOR\tAL,AL\t\t;Get ZF for the character\n\tJP\tSTATUSDONE\t;Done\n\nNOKEYPRES:\t;Clear AL before returning\n\tMOV\tAL,0\n\nSTATUSDONE:\t;Restore registers for STATUS\n\t...<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki slack-ochin\" style=\"background-color: #FFF\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #002339\">\t...<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">INT<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">16H<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Call keyboard BIOS service<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JZ<\/span><span style=\"color: #002339\">\tNOKEYPRES\t<\/span><span style=\"color: #357B42; font-style: italic\">;No key pressed<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">CMP<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AX<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">7200H<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Check if key is CTRL + PRTSCR<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JNZ<\/span><span style=\"color: #002339\">\tSTATUSDONE\t<\/span><span style=\"color: #357B42; font-style: italic\">;No, skip the next bit<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">10H<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Yes, set to CTRL + P<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">OR<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">\t\t<\/span><span style=\"color: #357B42; font-style: italic\">;Get ZF for the character<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">JP<\/span><span style=\"color: #002339\">\tSTATUSDONE\t<\/span><span style=\"color: #357B42; font-style: italic\">;Done<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">NOKEYPRES:<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Clear AL before returning<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t<\/span><span style=\"color: #7B30D0\">MOV<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #174781\">AL<\/span><span style=\"color: #002339\">,<\/span><span style=\"color: #174781\">0<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #7EB233\">STATUSDONE:<\/span><span style=\"color: #002339\">\t<\/span><span style=\"color: #357B42; font-style: italic\">;Restore registers for STATUS<\/span><\/span>\n<span class=\"line\"><span style=\"color: #002339\">\t...<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Recompiled the BIOS and tested Microsoft BASIC again. I think this screenshot speaks for itself:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_basic3.png\" alt=\"\" class=\"wp-image-86\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_basic3.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_basic3-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">Microsoft BASIC-86 working correctly under 86-DOS 1.14.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>What about the SCP Super Editor?<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"350\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm6.png\" alt=\"\" class=\"wp-image-85\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm6.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_wm6-300x146.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">SCP Super Editor working and displaying the help screen.<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Mission Complete &#8211; 86-DOS 1.14 on Modern Computers<\/h2>\n\n\n\n<p>Some of you may have wondered why on Earth I would port this ancient OS to an ancient computer when the challenge is to boot it on a modern computer. Well, this may surprise you a bit, but most modern x86 computers we use today are descendants of the IBM PC line of computers and hence compatible with the IBM PC. So, if things didn&#8217;t totally screw up, this IBM PC port of 86-DOS 1.14 should work out of the box on all modern x86 PCs.<\/p>\n\n\n\n<p>You can actually copy 160 KiB floppy disk images to USB sticks, and funnily enough, Windows happily mounts it.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"474\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_usb-1024x474.png\" alt=\"\" class=\"wp-image-84\" style=\"width:600px\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_usb-1024x474.png 1024w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_usb-300x139.png 300w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_usb-768x355.png 768w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_usb-1536x710.png 1536w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_usb.png 1615w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n\n<p>Alright, moment of history:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"766\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1-1024x766.png\" alt=\"\" class=\"wp-image-78\" style=\"width:600px\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1-1024x766.png 1024w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1-300x224.png 300w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1-768x575.png 768w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop1.png 1259w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"767\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop2-1024x767.png\" alt=\"\" class=\"wp-image-80\" style=\"width:600px\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop2-1024x767.png 1024w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop2-300x225.png 300w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop2-768x575.png 768w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_modern_laptop2.png 1259w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">A 1.44MB Version<\/h2>\n\n\n\n<p>I&#8217;ve also created a 1.44 MB version of 86-DOS 1.14. Yes, you heard it right, 1.44 MB. No, I didn&#8217;t merely copy the 160 KiB disk to the first 8 sectors of the first 40 tracks of the first side of a 1.44 MB disk, I actually made it support 1.44 MB disks. While it does function, please keep in mind the following limitations:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It will not boot from or correctly read\/write disks in other formats (including 160 KiB).<\/li>\n\n\n\n<li>It does not have the <code>RDCPM<\/code> utility, because as far as I know, CP\/M never officially supported 1.44 MB disks.<\/li>\n\n\n\n<li>I didn&#8217;t test it much, so you may encounter bugs.<\/li>\n<\/ul>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"400\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_144mb.png\" alt=\"\" class=\"wp-image-83\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_144mb.png 720w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_144mb-300x167.png 300w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><figcaption class=\"wp-element-caption\">86-DOS 1.14 booting from a 1.44MB disk on a PC\/AT clone.<\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Download This Project<\/h2>\n\n\n\n<p>Download the <a href=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-DOS-1.14-for-IBM-PC.zip\">IBM PC version of 86-DOS 1.14 here<\/a>. Files included:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>86-DOS 1.14.img<\/code><\/strong>: A disk image of this IBM PC port of 86-DOS 1.14. Inside the disk image you can find the source code of the boot sector, DOS BIOS and the <code>INIT<\/code> program, just like the original distribution of 86-DOS 1.14.<\/li>\n\n\n\n<li><strong><code>[Disks With Misc. Files]<\/code><\/strong>\n<ul class=\"wp-block-list\">\n<li><strong><code>MiscCPM.img<\/code><\/strong>: A CP\/M-86 disk image with 4 text files (taken from the VisiCalc IV 2.0 demo). You may use this disk to test the <code>RDCPM<\/code> program.<\/li>\n\n\n\n<li><strong><code>MiscFiles.img<\/code><\/strong>: A disk image containing various documents, binaries, and source code that I used throughout the project.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>Download an archive of <a href=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-DOS-1.14-Software-for-IBM-PC.zip\">86-DOS 1.14 software here<\/a>. Software included:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><u>Microsoft BASIC-86 5.21<\/u><\/strong>: BASIC-86 for 86-DOS. Use <code>OLDBASIC.COM<\/code> if your machine has only 64 KiB of RAM.<\/li>\n\n\n\n<li><strong><u>Microsoft MASM 1.00<\/u><\/strong>: The first version of MASM for DOS. Also called MACRO-86. You need at least 128 KiB of RAM.<\/li>\n\n\n\n<li><strong><u>Microsoft Pascal 1.10<\/u><\/strong>: A pre-release version of Microsoft Pascal. You may want to copy <code>PASKEY<\/code> from the first disk to the disk with your code. You need at least 128 KiB of RAM.<\/li>\n\n\n\n<li><strong><u>SCP Super Editor 1.00<\/u><\/strong>: SCP&#8217;s Word-Master knock-off. It is a bit hard to use if you&#8217;re not familiar with Word-Master.<\/li>\n\n\n\n<li><strong><u>VisiCalc<\/u><\/strong>: The first-ever spreadsheet program for personal computers. This program targeted PC-DOS 1.00 but is 100% compatible with 86-DOS. To turn the disks into bootable 86-DOS disks, see <code>VCCPYSYS.DOC<\/code> on my Misc. Files disk. Versions include:\n<ul class=\"wp-block-list\">\n<li><strong><u>VisiCalc 156-IBM<\/u><\/strong>: The first version of VisiCalc for the IBM PC. This copy does not have copy protection (I removed it).<\/li>\n\n\n\n<li><strong><u>VisiCalc 176-IBM-TEST<\/u><\/strong>: An internal version of VisiCalc for the IBM PC from late 1981. This copy does not have copy protection.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>Download the <a href=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-DOS-1.14-for-IBM-PC-1.44MB.zip\">1.44MB version of 86-DOS 1.14 here<\/a>. Files included:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>86-DOS 1.14 - 1.44MB.img<\/code><\/strong>: A disk image of the 1.44 MB version of 86-DOS 1.14.<\/li>\n\n\n\n<li><strong><code>readme.txt<\/code><\/strong>: A text document describing this version of 86-DOS 1.14.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Similar\/Related Projects By Other People<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/www.os2museum.com\/wp\/pc-86-dos\">PC-86-DOS<\/a> by <a href=\"https:\/\/www.os2museum.com\/wp\/author\/michaln\">Michal Necasek<\/a><\/h3>\n\n\n\n<p>PC-86-DOS is 86-DOS 1.00 patched to boot on the IBM PC. It is very similar to my 86-DOS 1.14 port, except it has fewer modifications. Its BIOS and boot sector were taken directly from the PC-DOS 1.00 pre-release, no modifications were made.<\/p>\n\n\n\n<p>PC-86-DOS is revolutionary &#8211; it accomplished something previously thought to be impossible, and it holds the record for the oldest operating system to run on the IBM PC. It is also remarkably simple and well-documented. Anyone with basic programming knowledge should be able to follow Michal&#8217;s post and produce a working copy of PC-86-DOS.<\/p>\n\n\n\n<p>While PC-86-DOS is very impressive, it does have a few problems. Here are the issues I have noticed:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><u>Console Input Status<\/u><\/strong>: After returning from <code>STATUS<\/code>, <code>AL<\/code> may not be <code>0<\/code> if no character is ready, which could cause system calls 2, 6, and 11 to misbehave. This is a severe issue, more than half of the programs either hang or crash.<\/li>\n\n\n\n<li><strong><u>RDCPM<\/u><\/strong>: <code>RDCPM<\/code> does not work on the IBM PC hardware. <code>MAKRDCPM<\/code> is also missing.<\/li>\n\n\n\n<li><strong><u>INIT<\/u><\/strong>: <code>INIT<\/code> is missing. <code>FORMAT<\/code> from PC-DOS is available, but they are not quite the same thing.<\/li>\n\n\n\n<li><strong><u>Source Code<\/u><\/strong>: The source code of the BIOS, boot sector, <code>RDCPM<\/code> table and <code>INIT<\/code> are all missing, making it hard to customise. Not too big of an issue though.<\/li>\n<\/ul>\n\n\n\n<p>Overall, PC-86-DOS is truly amazing. It is hard to imagine that somebody could port an OS designed for a completely incompatible machine to the IBM PC with a mere 20-byte patch.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><a href=\"https:\/\/github.com\/LucasBrooks\/DOS-Disassembly\/blob\/main\/86-DOS\/1.14\/86DOS.ASM\">86-DOS.SYS Disassembly<\/a> by <a href=\"https:\/\/github.com\/LucasBrooks\">Lucas Brooks<\/a><\/h3>\n\n\n\n<p>Found this disassembly <a href=\"https:\/\/www.betaarchive.com\/forum\/viewtopic.php?t=35931\">here<\/a> two days after I finished my project. It is a complete disassembly of <code>86DOS.SYS<\/code>, something I desired but didn&#8217;t have when I was porting 86-DOS 1.14 to the PC.<\/p>\n\n\n\n<p>Lucas&#8217; disassembly is a full one (I also did one when I was comparing <code>86DOS.SYS<\/code> against <code>IBMDOS.COM<\/code>, but mine was an IDA database with a lot of undefined data), and it compiles back to the exact same binary &#8211; except he changed the BIOS segment from <code>40H<\/code> to <code>60H<\/code>. Clearly, that was an attempt at getting it to boot on the IBM PC, but he forgot to increase the I\/O and disk stack buffer sizes. It does have a few hard coded offsets and constants, but they are inevitable when it comes to the disassembly of large\/complex binaries. Overall, the disassembly is pretty cool.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Bonus: IBM PC + SCP 8086 ROM<\/h2>\n\n\n\n<p>What if, in a parallel universe, the designers at IBM had decided to put an S-100 style boot ROM (similar to the SCP 8086 Monitor ROM) on the IBM PC motherboard? Well, we find ourselves in that universe right now! There is nothing you can&#8217;t do with emulators.<\/p>\n\n\n\n<p><a href=\"https:\/\/jaybertsoftware.weebly.com\/8086-tiny-plus.html\">8086 Tiny Plus<\/a> by <a href=\"https:\/\/jaybertsoftware.weebly.com\/\">Julian Olds<\/a> (based on <a href=\"https:\/\/github.com\/adriancable\/8086tiny\">8086tiny<\/a> by <a href=\"https:\/\/github.com\/adriancable\">Adrian Cable<\/a>) is a very simple IBM PC\/XT emulator written in C++. To try SCP&#8217;s 8086 Monitor ROM on the PC, I modified the emulator to load the 2 KiB ROM to <code>FF80:0000<\/code> and changed the BIOS to jump to <code>FFFF:0000<\/code> instead of booting from floppy\/hard disks. Lo and behold, it&#8217;s working!<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"642\" height=\"447\" src=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pcmon.png\" alt=\"\" class=\"wp-image-82\" srcset=\"https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pcmon.png 642w, https:\/\/thebrokenpipe.com\/blog\/wp-content\/uploads\/2022\/12\/86-dos_114_pcmon-300x209.png 300w\" sizes=\"auto, (max-width: 642px) 100vw, 642px\" \/><figcaption class=\"wp-element-caption\">SCP 8086 Monitor ROM on the IBM PC.<\/figcaption><\/figure>\n<\/div>\n\n\n<p>No, the original ROM didn&#8217;t just magically work, I had to rewrite all of the console I\/O and disk boot routines.<\/p>\n\n\n\n<p>The SCP 8086 Monitor eventually morphed into the <code>DEBUG<\/code> utility, you can actually find a fair amount of its functions\/routines in the MS-DOS 2.11 <code>DEBUG<\/code> source code released by Microsoft. So yeah, if you want to try it on the IBM PC, just boot into DOS and run <code>DEBUG<\/code> :).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Notes<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li id=\"cpm-dpb\">According to <a href=\"https:\/\/www.seasip.info\/index.html\">John Elliott<\/a>&#8216;s <a href=\"https:\/\/www.seasip.info\/Cpm\/format22.html\">documentation<\/a> of CP\/M&#8217;s DPB, the <code>SPT<\/code> field is the number of 128-byte records per track, not the number of sectors. This is correct as the byte sequence <code>20 00 03 07 00 9B 00 3F 00 C0 00 10 00 01 00<\/code> can be found in CP\/M-86 1.00&#8217;s <code>CPM.SYS<\/code> at offset <code>0x2871<\/code>. This byte sequence is the default DPB and the first word is the <code>SPT<\/code> field. As you can see, <code>SPT<\/code> is <code>0x20<\/code> (<code>32<\/code>) and there are indeed 32 128-byte records in each track of an 8-SPT 5.25&#8243; SSDD floppy disk.<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>2024 Edit Following the discovery of other versions of 86-DOS and newer efforts, this port is now outdated. The latest ports of 86-DOS to the IBM PC are on GitHub and binaries may be downloaded from there too. Nonetheless, this post is still worth reading because it forms the basis of my newer ports and [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[],"class_list":["post-69","post","type-post","status-publish","format-standard","hentry","category-dos"],"_links":{"self":[{"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/posts\/69","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/comments?post=69"}],"version-history":[{"count":48,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/posts\/69\/revisions"}],"predecessor-version":[{"id":224,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/posts\/69\/revisions\/224"}],"wp:attachment":[{"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/media?parent=69"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/categories?post=69"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thebrokenpipe.com\/blog\/wp-json\/wp\/v2\/tags?post=69"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}