{"id":184,"date":"2009-05-12T00:01:13","date_gmt":"2009-05-12T08:01:13","guid":{"rendered":"http:\/\/www.pagetable.com\/?p=184"},"modified":"2009-05-12T00:01:13","modified_gmt":"2009-05-12T08:01:13","slug":"reverse-engineering-dos-10-ibmbiocom","status":"publish","type":"post","link":"https:\/\/www.pagetable.com\/?p=184","title":{"rendered":"Reverse-Engineering DOS 1.0 &#8211; Part 2: IBMBIO.COM"},"content":{"rendered":"<p><i>Update<\/i>: The source is available at <a href=\"https:\/\/github.com\/mist64\/msdos1\">github.com\/mist64\/msdos1<\/a><\/p>\n<p>My last post was about the <a href=\"http:\/\/www.pagetable.com\/?p=165\">internals of the DOS 1.0 bootsector<\/a>. This time, let&#8217;s look at the next stage of the DOS 1.0 boot process, the hardware abstraction library IBMBIO.COM.<\/p>\n<h3>CP\/M and DOS History<\/h3>\n<p>Let us first look at the historical background: CP\/M was an 8 bit operating system that existed for virtually every computer with an 8080\/Z80 CPU. It consisted of the three core components: <a href=\"http:\/\/www.seasip.demon.co.uk\/Cpm\/bios.html\">BIOS<\/a>, BDOS and CCP. BIOS was the machine abstraction layer that allowed CP\/M work on different platforms. BDOS was the platform agnostic core library code, and CCP the command line interpreter.<\/p>\n<p><a href=\"http:\/\/www.86dos.org\/\">86-DOS<\/a> by Seattle Computer Products was a clone of CP\/M intended for 8086 computers. It shared the architecture of CP\/M, having a separate machine abstraction layer (&#8220;DOSIO&#8221;). When Microsoft bought 86-DOS and ported it to the upcoming IBM PC (model 5150), they kept this architecture, although there was no need to implement custom drivers, since the IBM PC had all its drivers in its &#8220;BIOS&#8221; firmware. But IBM&#8217;s BIOS did not have the same interface as 86-DOS DOSIO, so PC-DOS 1.0 included a very small DOSIO which would just sit on top of BIOS and using its driver library (and work around some bugs).<\/p>\n<p>So DOS 1.0 for the IBM PC consisted of the three parts IBMBIO.COM (machine abstraction), IBMDOS.COM (DOS library) and COMMAND.COM (command line).<\/p>\n<p>Microsoft soon started licensing MS-DOS to other computer manufacturers that wanted to make IBM PC compatibles. Back then, IBM PC compatible meant being able to run DOS applications and not necessarily sharing the whole system design with the IBM PC, so for MS-DOS to run on other 8086-based systems, it was enough to adapt the hardware abstraction layer to these machines, and therefore Microsoft provided the source code of this part of MS-DOS to hardware vendors. In MS-DOS, the system files are called IO.SYS and MSDOS.SYS.<\/p>\n<p>But since MS-DOS only provided a rather narrow API that did not include, for example, access to bitmap graphics, soon many DOS programs accesses hardware directly, forcing clone makers to make their PC compatibles more and more similar to the IBM PC, eventually using the same support chips and a binary compatible firmware interface. The separation of the kernel in two parts was less and less necessary, so that starting with MS-DOS 5.0, Microsoft only provided a single version of IO.SYS, and in MS-DOS 7.0 (Windows 95), IO.SYS and MSDOS.SYS were merged into IO.SYS.<\/p>\n<h3>IBMBIO<\/h3>\n<p>On DOS 1.0, IBMBIO.COM provides the following library functions to DOS (names taken from 86-DOS DOSIO source):<\/p>\n<table border=\"1\">\n<tr>\n<td>STATUS<\/td>\n<td>check for keypress<\/td>\n<\/tr>\n<tr>\n<td>INP<\/td>\n<td>get key from keyboard<\/td>\n<\/tr>\n<tr>\n<td>OUTP<\/td>\n<td>send character to screen<\/td>\n<\/tr>\n<tr>\n<td>PRINT<\/td>\n<td>send character to printer<\/td>\n<\/tr>\n<tr>\n<td>AUXIN<\/td>\n<td>get character from serial<\/td>\n<\/tr>\n<tr>\n<td>AUXOUT<\/td>\n<td>send character to serial<\/td>\n<\/tr>\n<tr>\n<td>READ<\/td>\n<td>read sector(s) from disk<\/td>\n<\/tr>\n<tr>\n<td>WRITE<\/td>\n<td>write sector(s) to disk<\/td>\n<\/tr>\n<tr>\n<td>DSKCHG<\/td>\n<td>check for disk change<\/td>\n<\/tr>\n<\/table>\n<p>(READ and WRITE will be directly hooked up by DOS into the INT 0x25\/0x26 direct disk I\/O API later.)<\/p>\n<p>In addition to this, IBMBIO is the next step in the boot process after the bootloader and responsible for<\/p>\n<ul>\n<li>initializing the serial and printer ports<\/li>\n<li>building a list of floppy drives and its capabilities<\/li>\n<li>setting up exception vectors (division by zero etc.)<\/li>\n<li>call the IBMDOS init code (already in memory)<\/li>\n<li>load and run COMMAND.COM<\/li>\n<\/ul>\n<p>Let us now look at the library calls it provides:<\/p>\n<h3>Serial and Printer<\/h3>\n<p>The code to talk to the serial and printer ports is rather straightforward. There is support for a single serial port and a single printer port. IBMBIO sets up the port to 2400 8N1 and has no function to change this setting. I\/O will just be passed to the respective BIOS functions, but errors will be evaluated and error messages will be printed in the error case.<\/p>\n<h3>Keyboard and Screen<\/h3>\n<p>While printing a character just passes the character to BIOS, character input is quite interesting: When reading a character, BIOS returns the ASCII code as well as the raw keyboard scancode. For keys that have no ASCII equivalent like the function or cursor keys, this returns zero as the ASCII code. IBMBIO always returns the ASCII code, but for special keys, it returns two bytes: A zero, indicating that it is a special key, and the BIOS scancode. Therefore in case of a special key, it caches the scancode, returns the zero, and will return the scancode the next time a character is read.<\/p>\n<p>The Control+C\/Control+Break handler uses this infrastructure to inject the code &#8220;3&#8221; into the input stream.<\/p>\n<h3>Disk I\/O: Virtual Disks<\/h3>\n<p>The library code for Disk I\/O is the most interesting part, since it can simulate a virtual disk drive, and it works around two issues of the BIOS function.<\/p>\n<p>DOS supports up to four disk drives, A:, B:, C: and D:, but in case there is only a single drive, it will present two drives to DOS. Since all disk access goes through the READ and WRITE functions of IBMBIO, it can compare the requested disk drive with the disk drive last used, and if it&#8217;s the other drive, it will print:<\/p>\n<pre>Insert diskette for drive\u00a0A: and strike\nany key when ready<\/pre>\n<p>After pressing a key, the actual I\/O access is performed. This way, DOS can be completely agnostic about whether there are two physical drives, or a physical and a virtual drive, and &#8220;COPY A:FOO B:BAR&#8221; will just work. Note that without this feature, it would be impossible to get data from one disk onto another with standard DOS tools.<\/p>\n<h3>Disk I\/O: Multiple Tracks<\/h3>\n<p>The first IBM PC BIOS issue IBMBIO works around is the fact that the original version of BIOS did not support a read or write of sectors across multiple heads. A track always had 8 sectors, and if your read starting at sector 1 of a track, it is possible to read up to 8 sectors, but starting at sector 8, only one sector can be read &#8211; if you want to continue reading sectors from the next track, you have to call BIOS again explicitly. The IBMBIO driver therefore breaks up longer reads if they span tracks.<\/p>\n<h3>Disk I\/O: 8237 DMA Controller Bug<\/h3>\n<p>The second problem is actually a design issue with the Intel 8237 DMA controller in the PC. Although the Intel 8088 CPU was internally 16 bits, had 16 bit registers and could support up to 1 MB of RAM, it was the low cost version that was meant to interface to 8 bit support chips. The 8237 is such a support chip intended for 8 bit systems, and therefore only supports 64 KB of memory. Since this would have meant that data from the disk drive can only be read into the lower 64 KB of the PC, IBM extended the DMA controller by adding an external latch per channel to it: You set up the lower 16 bits of the DMA address in the DMA controller, and the upper 4 bits (20 bits correspond to 1 MB) in an external latch, and the upper four address lines will be provided by the latch when the DMA controller accesses memory.<\/p>\n<p>Unfortunately, the 8237 had no &#8220;carry out&#8221;, so if you set up a DMA to 0x0FFFF (latch: 0x0, DMA controller: 0xFFFF), the address inside the DMA controller will wrap around to 0x0000, but it will not update the upper four bits of the address in the latch. So DMA that spans a 64 KB boundary will end up at the wrong location.<\/p>\n<p>The idea of a device driver is to abstract away details of a device and work around device bugs, but the BIOS in the first PC failed to work around this quirk in the DMA hardware. Therefore IBMBIO works around it by detecting I\/O that spans a 64 KB boundary and performing it in a temporary buffer inside IBMBIO.<\/p>\n<p>What is interesting about these workarounds is that DOS 1.0 was the default operating system for the disk-equipped version of the IBM PC, and IBM shipped it with its first machines, so it should have been possible to include these workarounds in BIOS already. In fact, later versions of BIOS did not have these issues any more, but DOS kept supporting the workarounds for a long time.<\/p>\n<h3>Source<\/h3>\n<p>Here is the assembly source of IBMBIO 1.0. It can be compiled with NASM and produces a binary which is not 100% identical, because of variations in the instruction encoding of different assemblers. The original assembler wasted a few bytes in the encoding, so NOPs have been added to keep the layout identical. The binary is only 1920 bytes. IBMDOS.COM is 6400 bytes, and I might be looking into that one in the future as well.<\/p>\n<pre>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; DOS 1.0 IBMBIO.COM (disk image MD5 73c919cecadf002a7124b7e8bfe3b5ba)<\/font>\n<font color=\"#0e0eff\"><font color=\"#007400\">;   <a href=\"http:\/\/www.pagetable.com\/\"><font color=\"#0e0eff\">http:\/\/www.pagetable.com\/<\/font><\/a><\/font><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">SECTOR_SIZE     equ     <font color=\"#1c00cf\">0x0200<\/font>          <font color=\"#007400\">; size of a sector<\/font><\/font>\n<font color=\"#000000\">DOS_SIZE        equ     <font color=\"#1c00cf\">10000<\/font>           <font color=\"#007400\">; max size of IBMDOS.COM in bytes<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">PAUSE_KEY       equ     <\/font><font color=\"#1c00cf\">0x7200<\/font><font color=\"#000000\">          <\/font>; scancode + charcode of PAUSE key<\/font>\n<font color=\"#007400\"><font color=\"#000000\">KEYBUF_NEXT     equ     <\/font><font color=\"#1c00cf\">0x041A<\/font><font color=\"#000000\">          <\/font>; next character in keyboard buffer<\/font>\n<font color=\"#007400\"><font color=\"#000000\">KEYBUF_FREE     equ     <\/font><font color=\"#1c00cf\">0x041C<\/font><font color=\"#000000\">          <\/font>; next free slot in keyboard buffer<\/font>\n<font color=\"#000000\">KEYBUF          equ     <font color=\"#1c00cf\">0x041E<\/font>          <font color=\"#007400\">; keyboard buffer data<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">LOGICAL_DRIVE   equ     <\/font><font color=\"#1c00cf\">0x0504<\/font><font color=\"#000000\">          <\/font>; linear address of logical drive byte<\/font>\n<font color=\"#000000\">SEG_DOS_TEMP    equ     <font color=\"#1c00cf\">0xE0<\/font>            <font color=\"#007400\">; segment in which DOS was loaded<\/font><\/font>\n<font color=\"#000000\">SEG_DOS         equ     <font color=\"#1c00cf\">0xB1<\/font>            <font color=\"#007400\">; segment in which DOS will run<\/font><\/font>\n<font color=\"#000000\">SEG_BIO         equ     <font color=\"#1c00cf\">0x60<\/font>            <font color=\"#007400\">; segment in which BIO is running<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">                org <font color=\"#1c00cf\">0x0000<\/font>              <font color=\"#007400\">; segment 0x0060<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">                jmp     INIT            <font color=\"#007400\">; 0x0060:0x0000 entry point<\/font><\/font>\n<font color=\"#000000\">                jmp     STATUS          <font color=\"#007400\">; 0x0060:0x0003 check for keypress<\/font><\/font>\n<font color=\"#000000\">                jmp     INP             <font color=\"#007400\">; 0x0060:0x0006 get key from keyboard<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">                jmp     OUTP            <\/font>; 0x0060:0x0009 send character to screen<\/font>\n<font color=\"#007400\"><font color=\"#000000\">                jmp     PRINT           <\/font>; 0x0060:0x000C send character to printer<\/font>\n<font color=\"#007400\"><font color=\"#000000\">                jmp     AUXIN           <\/font>; 0x0060:0x000F get character from serial<\/font>\n<font color=\"#007400\"><font color=\"#000000\">                jmp     AUXOUT          <\/font>; 0x0060:0x0012 send character to serial<\/font>\n<font color=\"#007400\"><font color=\"#000000\">                jmp     READ            <\/font>; 0x0060:0x0015 read sector(s) from disk (INT 0x25)<\/font>\n<font color=\"#007400\"><font color=\"#000000\">                jmp     WRITE           <\/font>; 0x0060:0x0018 write sector(s) to disk  (INT 0x26)<\/font>\n<font color=\"#000000\">                jmp     DSKCHG          <font color=\"#007400\">; 0x0060:0x001B check for disk change<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">                dw SEG_DOS              <font color=\"#007400\">; ???<\/font><\/font>\n<font color=\"#000000\">                dw TXT_VERSION          <font color=\"#007400\">; ???<\/font><\/font>\n<font color=\"#000000\">TXT_VERSION     db <font color=\"#1c00cf\">'BIOS Version 1.00'<\/font><\/font>\n<font color=\"#000000\">                db <font color=\"#1c00cf\">' '<\/font>+<font color=\"#1c00cf\">0x80<\/font><\/font>\n<font color=\"#000000\">                db <font color=\"#1c00cf\">'22-Jul-81'<\/font>,<font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#1c00cf\"><font color=\"#000000\">ERR_PAPER       db <\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>'Out of pape'<font color=\"#000000\">,<\/font>'r'<font color=\"#000000\">+<\/font>0x80<font color=\"#000000\">,<\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>0<\/font>\n<font color=\"#1c00cf\"><font color=\"#000000\">ERR_PRINTER     db <\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>'Printer faul'<font color=\"#000000\">,<\/font>'t'<font color=\"#000000\">+<\/font>0x80<font color=\"#000000\">,<\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>0<\/font>\n<font color=\"#1c00cf\"><font color=\"#000000\">ERR_AUX         db <\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>'Aux I\/O erro'<font color=\"#000000\">,<\/font>'r'<font color=\"#000000\">+<\/font>0x80<font color=\"#000000\">,<\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>0<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; check for keypress<\/font>\n<font color=\"#007400\">;  AL = character<\/font>\n<font color=\"#007400\">;  Z  = set if no character<\/font>\n<font color=\"#007400\">;  all other registers preserved<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">STATUS          mov     al, [cs:next_char]<font color=\"#007400\">; check for waiting character<\/font><\/font>\n<font color=\"#000000\">                or      al, al<\/font>\n<font color=\"#000000\">                jnz     char_avail      <font color=\"#007400\">; yes, return it<\/font><\/font>\n<font color=\"#000000\">                push    dx<\/font>\n<font color=\"#000000\">                xchg    ax, dx<\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x16<\/font>            <font color=\"#007400\">; otherwise get key (don't clear)<\/font><\/font>\n<font color=\"#000000\">                jz      status_exit     <font color=\"#007400\">; no key<\/font><\/font>\n<font color=\"#000000\">                cmp     ax, PAUSE_KEY   <font color=\"#007400\">; PAUSE key?<\/font><\/font>\n<font color=\"#000000\">                jnz     status_exit<\/font>\n<font color=\"#000000\">                mov     al, <font color=\"#1c00cf\">0x10<\/font>        <font color=\"#007400\">; convert into Ctrl+P<\/font><\/font>\n<font color=\"#000000\">                or      al, al<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">status_exit     mov     ah, dh          <font color=\"#007400\">; restore original AH<\/font><\/font>\n<font color=\"#000000\">                pop     dx<\/font>\n<font color=\"#000000\">char_avail      retf<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; Interrupt 0x1B handler: Control+Break handler<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">int_1B          mov     byte [cs:next_char], <font color=\"#1c00cf\">3<\/font><font color=\"#007400\">; put code for Ctrl+C<\/font><\/font>\n<font color=\"#000000\">                iret                    <font color=\"#007400\">; into keyboard queue<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; Interrupt 0x00 handler: Division by Zero<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">int_00          sti<\/font>\n<font color=\"#000000\">                push    ax<\/font>\n<font color=\"#000000\">                push    dx<\/font>\n<font color=\"#000000\">                mov     dx, ERR_DIVIDE<\/font>\n<font color=\"#000000\">                call    print_string<\/font>\n<font color=\"#000000\">                pop     dx<\/font>\n<font color=\"#000000\">                pop     ax<\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x23<\/font>            <font color=\"#007400\">; exit program through Ctrl+C path<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; Interrupt 0x00 handler: Single Step<\/font>\n<font color=\"#007400\">; Interrupt 0x03 handler: Breakpoint<\/font>\n<font color=\"#007400\">; Interrupt 0x04 handler: Overflow<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">iret1           iret                    <font color=\"#007400\">; empty interrupt handler<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#1c00cf\"><font color=\"#000000\">ERR_DIVIDE      db <\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>'Divide overflo'<font color=\"#000000\">,<\/font>'w'<font color=\"#000000\">+<\/font>0x80<font color=\"#000000\">,<\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>0<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; get key from keyboard<\/font>\n<font color=\"#007400\">;  AL = character<\/font>\n<font color=\"#007400\">;  all other registers preserved<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">again           xchg    ax, dx<\/font>\n<font color=\"#000000\">                pop     dx<\/font>\n<font color=\"#000000\">INP             mov     al, <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                xchg    al, [cs:next_char]<font color=\"#007400\">; get and clear waiting character<\/font><\/font>\n<font color=\"#000000\">                or      al, al<\/font>\n<font color=\"#000000\">                jnz     inp_exit        <font color=\"#007400\">; there is no character waiting<\/font><\/font>\n<font color=\"#000000\">                push    dx<\/font>\n<font color=\"#000000\">                xchg    ax, dx<\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x16<\/font>            <font color=\"#007400\">; then read character from keyboard<\/font><\/font>\n<font color=\"#000000\">                or      ax, ax<\/font>\n<font color=\"#000000\">                jz      again<\/font>\n<font color=\"#000000\">                cmp     ax, PAUSE_KEY<\/font>\n<font color=\"#000000\">                jnz     not_pause2<\/font>\n<font color=\"#000000\">                mov     al, <font color=\"#1c00cf\">0x10<\/font>        <font color=\"#007400\">; Ctrl+P<\/font><\/font>\n<font color=\"#000000\">not_pause2      cmp     al, <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                jnz     skip1           <font color=\"#007400\">; key with ASCII representation<\/font><\/font>\n<font color=\"#000000\">                mov     [cs:next_char], ah<font color=\"#007400\">; return scancode next time<\/font><\/font>\n<font color=\"#000000\">skip1           mov     ah, dh          <font color=\"#007400\">; restore AH<\/font><\/font>\n<font color=\"#000000\">                pop     dx<\/font>\n<font color=\"#000000\">inp_exit        retf<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; send character to screen<\/font>\n<font color=\"#007400\">;  AL = character<\/font>\n<font color=\"#007400\">;  all registers preserved<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">OUTP            push    bp<\/font>\n<font color=\"#000000\">                push    ax<\/font>\n<font color=\"#000000\">                push    bx<\/font>\n<font color=\"#000000\">                push    si<\/font>\n<font color=\"#000000\">                push    di<\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0x0E<\/font><\/font>\n<font color=\"#000000\">                cs                      <font color=\"#007400\">; XXX makes no sense<\/font><\/font>\n<font color=\"#000000\">                mov     bx, <font color=\"#1c00cf\">7<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x10<\/font>            <font color=\"#007400\">; print character<\/font><\/font>\n<font color=\"#000000\">                pop     di<\/font>\n<font color=\"#000000\">                pop     si<\/font>\n<font color=\"#000000\">                pop     bx<\/font>\n<font color=\"#000000\">                pop     ax<\/font>\n<font color=\"#000000\">                pop     bp<\/font>\n<font color=\"#000000\">                retf<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; send character to printer<\/font>\n<font color=\"#007400\">;  AL = character<\/font>\n<font color=\"#007400\">;  all registers preserved<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">PRINT           push    ax<\/font>\n<font color=\"#000000\">                push    dx<\/font>\n<font color=\"#000000\">                mov     byte [cs:printer_retry], <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">printer_again   mov     dx, <font color=\"#1c00cf\">0<\/font>           <font color=\"#007400\">; printer port #0<\/font><\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x17<\/font>            <font color=\"#007400\">; send character to printer<\/font><\/font>\n<font color=\"#000000\">                mov     dx, ERR_PAPER<\/font>\n<font color=\"#000000\">                test    ah, <font color=\"#1c00cf\">0x20<\/font><\/font>\n<font color=\"#000000\">                jnz     printer_error   <font color=\"#007400\">; out of paper error<\/font><\/font>\n<font color=\"#000000\">                mov     dx, ERR_PRINTER<\/font>\n<font color=\"#000000\">                test    ah, <font color=\"#1c00cf\">5<\/font><\/font>\n<font color=\"#000000\">                jz      pop_dx_ax_retf  <font color=\"#007400\">; no timeout error, return<\/font><\/font>\n<font color=\"#000000\">                xor     byte [cs:printer_retry], <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                jnz     printer_again   <font color=\"#007400\">; on a timeout, try twice<\/font><\/font>\n<font color=\"#000000\">printer_error   call    print_string<\/font>\n<font color=\"#000000\">pop_dx_ax_retf  pop     dx<\/font>\n<font color=\"#000000\">                pop     ax<\/font>\n<font color=\"#000000\">                retf<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; print zero-terminated string at DS:DX<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">print_string    xchg    si, dx<\/font>\n<font color=\"#000000\">prints1         cs lodsb<\/font>\n<font color=\"#000000\">                and     al, <font color=\"#1c00cf\">0x7F<\/font>        <font color=\"#007400\">; clear bit 7 (XXX why?)<\/font><\/font>\n<font color=\"#000000\">                jz      prints2         <font color=\"#007400\">; zero-terminated<\/font><\/font>\n<font color=\"#000000\">                call    SEG_BIO:OUTP    <font color=\"#007400\">; print character<\/font><\/font>\n<font color=\"#000000\">                jmp     prints1         <font color=\"#007400\">; loop<\/font><\/font>\n<font color=\"#000000\">prints2         xchg    si, dx<\/font>\n<font color=\"#000000\">                retn<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; get character from serial<\/font>\n<font color=\"#007400\">;  AL = character<\/font>\n<font color=\"#007400\">;  all other registers preserved<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">AUXIN           push    dx<\/font>\n<font color=\"#000000\">                push    ax<\/font>\n<font color=\"#000000\">                mov     dx, <font color=\"#1c00cf\">0<\/font>           <font color=\"#007400\">; serial port #0<\/font><\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">2<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x14<\/font>            <font color=\"#007400\">; get character from serial port<\/font><\/font>\n<font color=\"#000000\">                mov     dx, ERR_AUX<\/font>\n<font color=\"#000000\">                test    ah, <font color=\"#1c00cf\">0x0E<\/font>        <font color=\"#007400\">; framing, parity or overrun?<\/font><\/font>\n<font color=\"#000000\">                jz      aux_noerr       <font color=\"#007400\">; no error<\/font><\/font>\n<font color=\"#000000\">                call    print_string<\/font>\n<font color=\"#000000\">aux_noerr       pop     dx<\/font>\n<font color=\"#000000\">                mov     ah, dh          <font color=\"#007400\">; restore AH<\/font><\/font>\n<font color=\"#000000\">                pop     dx<\/font>\n<font color=\"#000000\">                retf<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; send character to serial<\/font>\n<font color=\"#007400\">;  AL = character<\/font>\n<font color=\"#007400\">;  all registers preserved<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">AUXOUT          push    ax<\/font>\n<font color=\"#000000\">                push    dx<\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                mov     dx, <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x14<\/font>            <font color=\"#007400\">; send character to serial port<\/font><\/font>\n<font color=\"#000000\">                test    ah, <font color=\"#1c00cf\">0x80<\/font>        <font color=\"#007400\">; timeout error?<\/font><\/font>\n<font color=\"#000000\">                jz      pop_dx_ax_retf  <font color=\"#007400\">; no all fine<\/font><\/font>\n<font color=\"#000000\">                mov     dx, ERR_AUX<\/font>\n<font color=\"#000000\">                jmp     printer_error<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; check for disk change<\/font>\n<font color=\"#007400\">;  AH = flag (1=changed)<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">DSKCHG          mov     ah, <font color=\"#1c00cf\">0<\/font>           <font color=\"#007400\">; the IBM PC can't detect disk change<\/font><\/font>\n<font color=\"#000000\">                retf<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">temp_sector:<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; entry point from boot sector<\/font>\n<font color=\"#007400\">;  assumes DX = 0<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">INIT            cli<\/font>\n<font color=\"#000000\">                mov     ax, cs<\/font>\n<font color=\"#000000\">                mov     ds, ax<\/font>\n<font color=\"#000000\">                mov     ss, ax<\/font>\n<font color=\"#000000\">                mov     sp, temp_sector_end<font color=\"#007400\">; set stack used during init<\/font><\/font>\n<font color=\"#000000\">                sti<\/font>\n<font color=\"#000000\">                xor     ah, ah<\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x13<\/font>            <font color=\"#007400\">; reset disk 0 (DX = 0)<\/font><\/font>\n<font color=\"#000000\">                mov     al, <font color=\"#1c00cf\">0xA3<\/font>        <font color=\"#007400\">; 2400 8N1<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x14<\/font>            <font color=\"#007400\">; initialize serial port<\/font><\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x17<\/font>            <font color=\"#007400\">; initialize printer<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x11<\/font>            <font color=\"#007400\">; get system info<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">                and     ax, <\/font><font color=\"#1c00cf\">0xC0<\/font><font color=\"#000000\">        <\/font>; number of floppies in bits 6 and 7<\/font>\n<font color=\"#000000\">                mov     cx, <font color=\"#1c00cf\">5<\/font><\/font>\n<font color=\"#000000\">                shr     ax, cl          <font color=\"#007400\">; (floppies-1) * 2<\/font><\/font>\n<font color=\"#000000\">                add     ax, <font color=\"#1c00cf\">2<\/font>           <font color=\"#007400\">; floppies * 2<\/font><\/font>\n<font color=\"#000000\">                and     ax, <font color=\"#1c00cf\">6<\/font>           <font color=\"#007400\">; will become 0 for 4 floppies<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">                jz      four_floppies   <\/font>; 4 floppies (num_floppies pre-assigned with 4)<\/font>\n<font color=\"#000000\">                cmp     al, <font color=\"#1c00cf\">2<\/font>           <font color=\"#007400\">; one floppy?<\/font><\/font>\n<font color=\"#000000\">                jnz     multi_floppy    <font color=\"#007400\">; no<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">                shl     ax, <\/font><font color=\"#1c00cf\">1<\/font><font color=\"#000000\">           <\/font>; pretend we have two, we'll emulate one<\/font>\n<font color=\"#000000\">                mov     byte [single_floppy], <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">multi_floppy    mov     bx, floppy_list<\/font>\n<font color=\"#000000\">                add     bx, ax          <font color=\"#007400\">; + floppies * 2<\/font><\/font>\n<font color=\"#000000\">                mov     word [bx], <font color=\"#1c00cf\">0<\/font>    <font color=\"#007400\">; terminate list with 2 zero words<\/font><\/font>\n<font color=\"#000000\">                mov     word [bx+<font color=\"#1c00cf\">2<\/font>], <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                nop                     <font color=\"#007400\">; XXX original assembler wasted a byte<\/font><\/font>\n<font color=\"#000000\">                shr     ax, <font color=\"#1c00cf\">1<\/font>           <font color=\"#007400\">; =floppies<\/font><\/font>\n<font color=\"#000000\">                mov     [num_floppies], al<\/font>\n<font color=\"#000000\">four_floppies   push    ds<\/font>\n<font color=\"#000000\">                mov     ax, <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                mov     ds, ax          <font color=\"#007400\">; DS := 0x0000<\/font><\/font>\n<font color=\"#000000\">                mov     ax, SEG_BIO     <font color=\"#007400\">; target segment for interrupt vectors<\/font><\/font>\n<font color=\"#000000\">                mov     [<font color=\"#1c00cf\">0x6E<\/font>], ax      <font color=\"#007400\">; set INT 1Bh segment<\/font><\/font>\n<font color=\"#000000\">                mov     word [<font color=\"#1c00cf\">0x6C<\/font>], int_1B<font color=\"#007400\">; set INT 1Bh offset<\/font><\/font>\n<font color=\"#000000\">                mov     word [<font color=\"#1c00cf\">0x00<\/font>], int_00<font color=\"#007400\">; set INT 00h offset<\/font><\/font>\n<font color=\"#000000\">                mov     [<font color=\"#1c00cf\">0x02<\/font>], ax      <font color=\"#007400\">; set INT 00h segment<\/font><\/font>\n<font color=\"#000000\">                mov     bx, iret1       <font color=\"#007400\">; set INT 00h offset<\/font><\/font>\n<font color=\"#000000\">                mov     [<font color=\"#1c00cf\">0x04<\/font>], bx      <font color=\"#007400\">; set INT 01h offset (empty)<\/font><\/font>\n<font color=\"#000000\">                mov     [<font color=\"#1c00cf\">0x06<\/font>], ax      <font color=\"#007400\">; set INT 01h segment<\/font><\/font>\n<font color=\"#000000\">                mov     [<font color=\"#1c00cf\">0x0C<\/font>], bx      <font color=\"#007400\">; set INT 03h offset (empty)<\/font><\/font>\n<font color=\"#000000\">                mov     [<font color=\"#1c00cf\">0x0E<\/font>], ax      <font color=\"#007400\">; set INT 03h segment<\/font><\/font>\n<font color=\"#000000\">                mov     [<font color=\"#1c00cf\">0x10<\/font>], bx      <font color=\"#007400\">; set INT 04h offset (empty)<\/font><\/font>\n<font color=\"#000000\">                mov     [<font color=\"#1c00cf\">0x12<\/font>], ax      <font color=\"#007400\">; set INT 04h segment<\/font><\/font>\n<font color=\"#000000\">                mov     ax, <font color=\"#1c00cf\">0x50<\/font><\/font>\n<font color=\"#000000\">                mov     ds, ax          <font color=\"#007400\">; DS := 0x0050<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">                mov     word [<\/font><font color=\"#1c00cf\">0x0<\/font><font color=\"#000000\">], <\/font><font color=\"#1c00cf\">0<\/font><font color=\"#000000\">   <\/font>; clear 0x0500 in DOS Comm. Area (???)<\/font>\n<font color=\"#000000\">                push    es<\/font>\n<font color=\"#000000\">                mov     ax, SEG_DOS     <font color=\"#007400\">; target segment for IBMDOS.COM<\/font><\/font>\n<font color=\"#000000\">                mov     es, ax<\/font>\n<font color=\"#000000\">                mov     cx, DOS_SIZE\/<font color=\"#1c00cf\">2<\/font>  <font color=\"#007400\">; size\/2 of IBMDOS.COM<\/font><\/font>\n<font color=\"#000000\">                cld<\/font>\n<font color=\"#000000\">                mov     ax, SEG_DOS_TEMP<font color=\"#007400\">; source segment of IBMDOS.COM<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">                mov     ds, ax          <\/font>; the booloader read whole sectors and puts<\/font>\n<font color=\"#007400\"><font color=\"#000000\">                xor     di, di          <\/font>; the IBMDOS.COM image right after this;<\/font>\n<font color=\"#000000\">                mov     si, di          <font color=\"#007400\">; so move it down a little<\/font><\/font>\n<font color=\"#000000\">                rep movsw               <font color=\"#007400\">; copy 10 000 bytes from 0xE00 to 0xB10<\/font><\/font>\n<font color=\"#000000\">                pop     es<\/font>\n<font color=\"#000000\">                pop     ds<\/font>\n<font color=\"#000000\">                mov     si, num_floppies<font color=\"#007400\">; pass in pointer to structure<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">                call    SEG_DOS:<\/font><font color=\"#1c00cf\">0<\/font><font color=\"#000000\">       <\/font>; init DOS (returns DS = memory for COMMAND.COM)<\/font>\n<font color=\"#000000\">                sti<\/font>\n<font color=\"#000000\">                mov     dx, <font color=\"#1c00cf\">0x0100<\/font>      <font color=\"#007400\">; 0x0100 in COMMAND.COM segment<\/font><\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0x1A<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x21<\/font>            <font color=\"#007400\">; set disk transfer area address<\/font><\/font>\n<font color=\"#000000\">                mov     cx, [<font color=\"#1c00cf\">0x06<\/font>]      <font color=\"#007400\">; remaining memory size<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">                sub     cx, <\/font><font color=\"#1c00cf\">0x0100<\/font><font color=\"#000000\">      <\/font>; - Program Segment Prefix = bytes to read<\/font>\n<font color=\"#000000\">                mov     bx, ds<\/font>\n<font color=\"#000000\">                mov     ax, cs<\/font>\n<font color=\"#000000\">                mov     ds, ax<\/font>\n<font color=\"#000000\">                mov     dx, FCB_command_com<font color=\"#007400\">; File Control Block<\/font><\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0x0F<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x21<\/font>            <font color=\"#007400\">; DOS: open COMMAND.COM<\/font><\/font>\n<font color=\"#000000\">                or      al, al<\/font>\n<font color=\"#000000\">                jnz     error_command   <font color=\"#007400\">; error opening COMMAND.COM<\/font><\/font>\n<font color=\"#000000\">                mov     word [FCB_command_com+<font color=\"#1c00cf\">0x21<\/font>], <font color=\"#1c00cf\">0<\/font><font color=\"#007400\">; random record field<\/font><\/font>\n<font color=\"#000000\">                mov     word [FCB_command_com+<font color=\"#1c00cf\">0x23<\/font>], <font color=\"#1c00cf\">0<\/font><font color=\"#007400\">;  := 0x00000000<\/font><\/font>\n<font color=\"#000000\">                mov     word [FCB_command_com+<font color=\"#1c00cf\">0x0E<\/font>], <font color=\"#1c00cf\">1<\/font><font color=\"#007400\">; record length = 1 byte<\/font><\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0x27<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x21<\/font>            <font color=\"#007400\">; DOS: read<\/font><\/font>\n<font color=\"#000000\">                jcxz    error_command   <font color=\"#007400\">; read 0 bytes -&gt; error<\/font><\/font>\n<font color=\"#000000\">                cmp     al, <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                jnz     error_command   <font color=\"#007400\">; end of file not reached -&gt; error<\/font><\/font>\n<font color=\"#000000\">                mov     ds, bx<\/font>\n<font color=\"#000000\">                mov     es, bx          <font color=\"#007400\">; DS := ES := SS := COMMAND.COM<\/font><\/font>\n<font color=\"#000000\">                mov     ss, bx<\/font>\n<font color=\"#007400\"><font color=\"#000000\">                mov     sp, <\/font><font color=\"#1c00cf\">0x40<\/font><font color=\"#000000\">        <\/font>; 64 byte stack in PSP (XXX interrupts are on!)<\/font>\n<font color=\"#000000\">                xor     ax, ax<\/font>\n<font color=\"#000000\">                push    ax              <font color=\"#007400\">; push return address 0x0000 (int 0x20)<\/font><\/font>\n<font color=\"#000000\">                mov     dx, [<font color=\"#1c00cf\">0x80<\/font>]      <font color=\"#007400\">; get new DTA address<\/font><\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0x1A<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x21<\/font>            <font color=\"#007400\">; set disk transfer area address<\/font><\/font>\n<font color=\"#000000\">                push    bx              <font color=\"#007400\">; segment of COMMAND.COM<\/font><\/font>\n<font color=\"#000000\">                mov     ax, <font color=\"#1c00cf\">0x0100<\/font>      <font color=\"#007400\">; offset of COMMAND.COM entry<\/font><\/font>\n<font color=\"#000000\">                push    ax<\/font>\n<font color=\"#000000\">                retf                    <font color=\"#007400\">; run COMMAND.COM<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">error_command:  mov     dx, ERR_COMMANDCOM <font color=\"#007400\">; \"rnBad or missing Command Interprete\"<\/font><\/font>\n<font color=\"#000000\">                call    print_string<\/font>\n<font color=\"#000000\">halt            jp      halt    <font color=\"#007400\">; XXX jp instead of jmp<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">FCB_command_com db <font color=\"#1c00cf\">1<\/font>, <font color=\"#1c00cf\">'COMMAND CO'<\/font>,<font color=\"#1c00cf\">'M'<\/font>+<font color=\"#1c00cf\">0x80<\/font><\/font>\n<font color=\"#000000\">                times <font color=\"#1c00cf\">19<\/font>h db <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#1c00cf\"><font color=\"#000000\">ERR_COMMANDCOM  db <\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>'Bad or missing Command Interprete'<font color=\"#000000\">,<\/font>'r'<font color=\"#000000\">+<\/font>0x80<font color=\"#000000\">,<\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>0<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">; this is passed to IBMDOS.COM<\/font>\n<font color=\"#007400\"><font color=\"#000000\">num_floppies    db <\/font><font color=\"#1c00cf\">4<\/font><font color=\"#000000\">                    <\/font>; if there's 1 physical drive, this says 2<\/font>\n<font color=\"#007400\"><font color=\"#000000\">floppy_list     dw parameters           <\/font>; point to params for every floppy installed; 0-terminated<\/font>\n<font color=\"#000000\">                dw parameters<\/font>\n<font color=\"#000000\">                dw parameters<\/font>\n<font color=\"#000000\">                dw parameters<\/font>\n<font color=\"#000000\">                dw <font color=\"#1c00cf\">0<\/font>,<font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">parameters      dw SECTOR_SIZE<\/font>\n<font color=\"#000000\">                db <font color=\"#1c00cf\">1<\/font>                    <font color=\"#007400\">; will be decremented by 1, then used<\/font><\/font>\n<font color=\"#000000\">                dw <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                db <font color=\"#1c00cf\">2<\/font><\/font>\n<font color=\"#000000\">                dw <font color=\"#1c00cf\">0x0040<\/font><\/font>\n<font color=\"#000000\">                dw <font color=\"#1c00cf\">320<\/font>                  <font color=\"#007400\">; number of total sectors<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">                times <font color=\"#1c00cf\">512<\/font>-($-temp_sector) db <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">temp_sector_end:<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">printer_retry   db <font color=\"#1c00cf\">0<\/font>                    <font color=\"#007400\">; count for printer retries<\/font><\/font>\n<font color=\"#000000\">next_char       db <font color=\"#1c00cf\">0<\/font>                    <font color=\"#007400\">; extra character in keyboard queue<\/font><\/font>\n<font color=\"#000000\">                db <font color=\"#1c00cf\">0<\/font>                    <font color=\"#007400\">; XXX unused<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">single_floppy   db <\/font><font color=\"#1c00cf\">0<\/font><font color=\"#000000\">                    <\/font>; true if we emulate a second logical floppy<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; READ  - read sector(s) from disk<\/font>\n<font color=\"#007400\">; WRITE - write sector(s) to disk<\/font>\n<font color=\"#007400\">;  al     drive number (0-3)<\/font>\n<font color=\"#007400\">;  ds:bx  buffer<\/font>\n<font color=\"#007400\">;  cx     count<\/font>\n<font color=\"#007400\">;  dx     logical block number<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">READ            mov     ah, <font color=\"#1c00cf\">2<\/font>           <font color=\"#007400\">; BIOS code \"read\"<\/font><\/font>\n<font color=\"#000000\">                jmp     short read_write<\/font>\n<font color=\"#000000\">WRITE           mov     ah, <font color=\"#1c00cf\">3<\/font>           <font color=\"#007400\">; BIOS code \"write\"<\/font><\/font>\n<font color=\"#000000\">read_write      push    es<\/font>\n<font color=\"#000000\">                push    ds<\/font>\n<font color=\"#000000\">                push    ds<\/font>\n<font color=\"#000000\">                pop     es              <font color=\"#007400\">; ES := DS<\/font><\/font>\n<font color=\"#000000\">                push    cs<\/font>\n<font color=\"#000000\">                pop     ds              <font color=\"#007400\">; DS := CS<\/font><\/font>\n<font color=\"#000000\">                mov     [temp_sp], sp   <font color=\"#007400\">; save sp for function abort<\/font><\/font>\n<font color=\"#000000\">                mov     [int_13_cmd], ah<font color=\"#007400\">; save whether it was read or write<\/font><\/font>\n<font color=\"#007400\">; logic to emulate a \"logical\" drive B: by prompting the user to change disk<\/font>\n<font color=\"#007400\">; when the currently used drive is changed<\/font>\n<font color=\"#000000\">                cmp     byte [single_floppy], <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                jnz     multi_drive     <font color=\"#007400\">; more than one drive<\/font><\/font>\n<font color=\"#000000\">                push    ds<\/font>\n<font color=\"#000000\">                xor     si, si<\/font>\n<font color=\"#000000\">                mov     ds, si          <font color=\"#007400\">; DS := 0x0000<\/font><\/font>\n<font color=\"#000000\">                mov     ah, al<\/font>\n<font color=\"#000000\">                xchg    ah, [LOGICAL_DRIVE]<font color=\"#007400\">; current logical drive<\/font><\/font>\n<font color=\"#000000\">                pop     ds<\/font>\n<font color=\"#000000\">                cmp     al, ah<\/font>\n<font color=\"#000000\">                jz      drive_unchanged<\/font>\n<font color=\"#000000\">                push    dx              <font color=\"#007400\">; save block number<\/font><\/font>\n<font color=\"#000000\">                add     al, <font color=\"#1c00cf\">'A'<\/font><\/font>\n<font color=\"#000000\">                mov     [TXT_DRIVE], al<\/font>\n<font color=\"#000000\">                mov     dx, TXT_INSERTDISK<\/font>\n<font color=\"#000000\">                call    print_string    <font color=\"#007400\">; prompt for disk change<\/font><\/font>\n<font color=\"#000000\">                push    ds<\/font>\n<font color=\"#000000\">                xor     bp, bp<\/font>\n<font color=\"#000000\">                mov     ds, bp<\/font>\n<font color=\"#000000\">                mov     byte [KEYBUF_NEXT], KEYBUF &amp; <font color=\"#1c00cf\">0xFF<\/font><\/font>\n<font color=\"#000000\">                mov     byte [KEYBUF_FREE], KEYBUF &amp; <font color=\"#1c00cf\">0xFF<\/font><font color=\"#007400\">; clear keyboard buffer<\/font><\/font>\n<font color=\"#000000\">                pop     ds<\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x16<\/font>            <font color=\"#007400\">; wait for any key<\/font><\/font>\n<font color=\"#000000\">                pop     dx              <font color=\"#007400\">; block number<\/font><\/font>\n<font color=\"#007400\"><font color=\"#000000\">drive_unchanged mov     al, <\/font><font color=\"#1c00cf\">0<\/font><font color=\"#000000\">           <\/font>; for both logical A: or B: use drive A:<\/font>\n<font color=\"#000000\">multi_drive     xchg    ax, dx<\/font>\n<font color=\"#000000\">                mov     dh, <font color=\"#1c00cf\">8<\/font>           <font color=\"#007400\">; convert LBA to CHS<\/font><\/font>\n<font color=\"#000000\">                div     dh              <font color=\"#007400\">; al = track (starts at 0)<\/font><\/font>\n<font color=\"#000000\">                inc     ah              <font color=\"#007400\">; ah = sector (starts at 1)<\/font><\/font>\n<font color=\"#000000\">                xchg    al, ah          <font color=\"#007400\">; track and sector<\/font><\/font>\n<font color=\"#000000\">                xchg    ax, cx          <font color=\"#007400\">; cx = t\/s, ax = count<\/font><\/font>\n<font color=\"#000000\">                mov     [num_sectors], ax<font color=\"#007400\">; count<\/font><\/font>\n<font color=\"#000000\">                mov     dh, <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#007400\">; work around DMA hardware bug in case I\/O spans a 64 KB boundary<\/font>\n<font color=\"#007400\">; by using a temporary buffer<\/font>\n<font color=\"#000000\">                mov     di, es          <font color=\"#007400\">; destination segment<\/font><\/font>\n<font color=\"#000000\">                shl     di, <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                shl     di, <font color=\"#1c00cf\">1<\/font>           <font color=\"#007400\">; make es:bx a linear address<\/font><\/font>\n<font color=\"#000000\">                shl     di, <font color=\"#1c00cf\">1<\/font>           <font color=\"#007400\">; (discard upper bits)<\/font><\/font>\n<font color=\"#000000\">                shl     di, <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                add     di, bx<\/font>\n<font color=\"#000000\">                add     di, SECTOR_SIZE-<font color=\"#1c00cf\">1<\/font><font color=\"#007400\">; last byte of sector (linear)<\/font><\/font>\n<font color=\"#000000\">                jc      across_64k      <font color=\"#007400\">; sector overflows it<\/font><\/font>\n<font color=\"#000000\">                xchg    bx, di          <font color=\"#007400\">; bx = last byte, di = buffer<\/font><\/font>\n<font color=\"#000000\">                shr     bh, <font color=\"#1c00cf\">1<\/font>           <font color=\"#007400\">; sector index in memory<\/font><\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0x80<\/font>        <font color=\"#007400\">; 0x80 sectors fit into 64 KB<\/font><\/font>\n<font color=\"#000000\">                sub     ah, bh          <font color=\"#007400\">; sectors until 64 KB boundary<\/font><\/font>\n<font color=\"#000000\">                mov     bx, di          <font color=\"#007400\">; bx = buffer<\/font><\/font>\n<font color=\"#000000\">                cmp     ah, al          <font color=\"#007400\">; compare to number of sectors<\/font><\/font>\n<font color=\"#000000\">                jbe     skip2           <font color=\"#007400\">; they fit into 64 KB, cap num<\/font><\/font>\n<font color=\"#000000\">                mov     ah, al          <font color=\"#007400\">; don't cap number of sectors<\/font><\/font>\n<font color=\"#000000\">skip2           push    ax<\/font>\n<font color=\"#000000\">                mov     al, ah          <font color=\"#007400\">; al = count<\/font><\/font>\n<font color=\"#000000\">                call    rw_tracks<\/font>\n<font color=\"#000000\">                pop     ax<\/font>\n<font color=\"#000000\">                sub     al, ah          <font color=\"#007400\">; requested = done?<\/font><\/font>\n<font color=\"#000000\">                jz      rw_done         <font color=\"#007400\">; yes, exit<\/font><\/font>\n<font color=\"#000000\">across_64k      dec     al              <font color=\"#007400\">; one sector less<\/font><\/font>\n<font color=\"#000000\">                push    ax<\/font>\n<font color=\"#000000\">                cld<\/font>\n<font color=\"#000000\">                push    bx<\/font>\n<font color=\"#000000\">                push    es              <font color=\"#007400\">; save data pointer<\/font><\/font>\n<font color=\"#000000\">                cmp     byte [int_13_cmd], <font color=\"#1c00cf\">2<\/font><\/font>\n<font color=\"#000000\">                jz      across_64k_read <font color=\"#007400\">; write case follows<\/font><\/font>\n<font color=\"#000000\">                mov     si, bx<\/font>\n<font color=\"#000000\">                push    cx<\/font>\n<font color=\"#000000\">                mov     cx, SECTOR_SIZE\/<font color=\"#1c00cf\">2<\/font><font color=\"#007400\">; copy first sector<\/font><\/font>\n<font color=\"#000000\">                push    es<\/font>\n<font color=\"#000000\">                pop     ds<\/font>\n<font color=\"#000000\">                push    cs<\/font>\n<font color=\"#000000\">                pop     es<\/font>\n<font color=\"#000000\">                mov     di, temp_sector<\/font>\n<font color=\"#000000\">                mov     bx, di<\/font>\n<font color=\"#000000\">                rep movsw               <font color=\"#007400\">; copy into IBMBIO local data<\/font><\/font>\n<font color=\"#000000\">                pop     cx<\/font>\n<font color=\"#000000\">                push    cs<\/font>\n<font color=\"#000000\">                pop     ds<\/font>\n<font color=\"#000000\">                call    rw_one_sector   <font color=\"#007400\">; write last sector<\/font><\/font>\n<font color=\"#000000\">                pop     es<\/font>\n<font color=\"#000000\">                pop     bx<\/font>\n<font color=\"#000000\">                jmp     short across_64k_end<\/font>\n<font color=\"#000000\">across_64k_read mov     bx, temp_sector<\/font>\n<font color=\"#000000\">                push    cs<\/font>\n<font color=\"#000000\">                pop     es<\/font>\n<font color=\"#000000\">                call    rw_one_sector   <font color=\"#007400\">; read last sector into temp buffer<\/font><\/font>\n<font color=\"#000000\">                mov     si, bx<\/font>\n<font color=\"#000000\">                pop     es<\/font>\n<font color=\"#000000\">                pop     bx<\/font>\n<font color=\"#000000\">                mov     di, bx<\/font>\n<font color=\"#000000\">                push    cx<\/font>\n<font color=\"#000000\">                mov     cx, SECTOR_SIZE\/<font color=\"#1c00cf\">2<\/font><\/font>\n<font color=\"#000000\">                rep movsw               <font color=\"#007400\">; copy out<\/font><\/font>\n<font color=\"#000000\">                pop     cx<\/font>\n<font color=\"#000000\">across_64k_end  add     bh, <font color=\"#1c00cf\">2<\/font>           <font color=\"#007400\">; continue 0x0200 after that<\/font><\/font>\n<font color=\"#000000\">                pop     ax<\/font>\n<font color=\"#000000\">                call    rw_tracks<\/font>\n<font color=\"#000000\">rw_done         pop     ds<\/font>\n<font color=\"#000000\">                pop     es<\/font>\n<font color=\"#000000\">                clc                     <font color=\"#007400\">; success<\/font><\/font>\n<font color=\"#000000\">                retf<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#007400\">; read\/write an arbirtary number of sectors<\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\">rw_tracks       or      al, al<\/font>\n<font color=\"#000000\">                jz      ret2            <font color=\"#007400\">; nothing to read<\/font><\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">9<\/font><\/font>\n<font color=\"#000000\">                sub     ah, cl<\/font>\n<font color=\"#000000\">                cmp     ah, al          <font color=\"#007400\">; more sectors than left in track?<\/font><\/font>\n<font color=\"#000000\">                jbe     skip3           <font color=\"#007400\">; no<\/font><\/font>\n<font color=\"#000000\">                mov     ah, al          <font color=\"#007400\">; otherwise, read up to end of track<\/font><\/font>\n<font color=\"#000000\">skip3           push    ax<\/font>\n<font color=\"#000000\">                mov     al, ah<\/font>\n<font color=\"#000000\">                call    near rw_sectors <font color=\"#007400\">; reads\/writes up to 8 sectors<\/font><\/font>\n<font color=\"#000000\">                pop     ax<\/font>\n<font color=\"#000000\">                sub     al, ah          <font color=\"#007400\">; decrease sectors to read<\/font><\/font>\n<font color=\"#000000\">                shl     ah, <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\">                add     bh, ah          <font color=\"#007400\">; advance pointer by sectors * 0x0200<\/font><\/font>\n<font color=\"#000000\">                jmp     rw_tracks       <font color=\"#007400\">; continue<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">int_13_err      xchg    ax, di<\/font>\n<font color=\"#000000\">                mov     ah, <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x13<\/font>            <font color=\"#007400\">; disk reset<\/font><\/font>\n<font color=\"#000000\">                dec     si<\/font>\n<font color=\"#000000\">                jz      translate       <font color=\"#007400\">; retries exhausted<\/font><\/font>\n<font color=\"#000000\">                mov     ax, di<\/font>\n<font color=\"#007400\"><font color=\"#000000\">                cmp     ah, <\/font><font color=\"#1c00cf\">0x80<\/font><font color=\"#000000\">        <\/font>; in the \"timeout (not ready)\" case,<\/font>\n<font color=\"#007400\"><font color=\"#000000\">                jz      translate       <\/font>; we don't retry (this would take forever)<\/font>\n<font color=\"#000000\">                pop     ax<\/font>\n<font color=\"#000000\">                jmp     short retry<\/font>\n<font color=\"#000000\">translate       push    cs<\/font>\n<font color=\"#000000\">                pop     es<\/font>\n<font color=\"#000000\">                mov     ax, di<\/font>\n<font color=\"#000000\">                mov     al, ah          <font color=\"#007400\">; status<\/font><\/font>\n<font color=\"#000000\">                mov     cx, <font color=\"#1c00cf\">0x0A<\/font><\/font>\n<font color=\"#000000\">                mov     di, conv_status<\/font>\n<font color=\"#000000\">                repne scasb<\/font>\n<font color=\"#000000\">                mov     al, [di+<font color=\"#1c00cf\">9<\/font>]<\/font>\n<font color=\"#000000\">                nop                     <font color=\"#007400\">; XXX original assembler wasted a byte<\/font><\/font>\n<font color=\"#000000\">                mov     cx, [num_sectors]<\/font>\n<font color=\"#000000\">                mov     sp, [temp_sp]   <font color=\"#007400\">; clean up stack<\/font><\/font>\n<font color=\"#000000\">                pop     ds<\/font>\n<font color=\"#000000\">                pop     es<\/font>\n<font color=\"#000000\">                stc                     <font color=\"#007400\">; error<\/font><\/font>\n<font color=\"#000000\">                retf<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">rw_one_sector   mov     al, <font color=\"#1c00cf\">1<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">; reads\/writes one or more sectors that are on the same track<\/font>\n<font color=\"#000000\">rw_sectors      mov     si, <font color=\"#1c00cf\">5<\/font>           <font color=\"#007400\">; number of retries<\/font><\/font>\n<font color=\"#000000\">                mov     ah, [int_13_cmd]<\/font>\n<font color=\"#000000\">retry           push    ax<\/font>\n<font color=\"#000000\">                int     <font color=\"#1c00cf\">0x13<\/font>            <font color=\"#007400\">; perform the read\/write<\/font><\/font>\n<font color=\"#000000\">                jc      int_13_err<\/font>\n<font color=\"#000000\">                pop     ax<\/font>\n<font color=\"#000000\">                sub     [num_sectors], al<\/font>\n<font color=\"#000000\">                add     cl, al          <font color=\"#007400\">; calculate next sector number<\/font><\/font>\n<font color=\"#000000\">                cmp     cl, <font color=\"#1c00cf\">8<\/font>           <font color=\"#007400\">; exceeds track?<\/font><\/font>\n<font color=\"#000000\">                jbe     ret2            <font color=\"#007400\">; no<\/font><\/font>\n<font color=\"#000000\">                inc     ch              <font color=\"#007400\">; next track<\/font><\/font>\n<font color=\"#000000\">                mov     cl, <font color=\"#1c00cf\">1<\/font>           <font color=\"#007400\">; sector 1<\/font><\/font>\n<font color=\"#000000\">ret2            retn<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#1c00cf\"><font color=\"#000000\">TXT_INSERTDISK  db <\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>'Insert diskette for drive'<font color=\"#000000\">,<\/font>' '<font color=\"#000000\">+<\/font>0x80<\/font>\n<font color=\"#1c00cf\"><font color=\"#000000\">TXT_DRIVE       db <\/font>'A: and strik'<font color=\"#000000\">,<\/font>'e'<font color=\"#000000\">+<\/font>0x80<font color=\"#000000\">,<\/font>13<font color=\"#000000\">,<\/font>10<\/font>\n<font color=\"#1c00cf\"><font color=\"#000000\">                db <\/font>'any key when read'<font color=\"#000000\">,<\/font>'y'<font color=\"#000000\">+<\/font>0x80<font color=\"#000000\">,<\/font>13<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>10<font color=\"#000000\">,<\/font>0<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">conv_status     db <font color=\"#1c00cf\">0x80<\/font>,<font color=\"#1c00cf\">0x40<\/font>,<font color=\"#1c00cf\">0x20<\/font>,<font color=\"#1c00cf\">0x10<\/font>,<font color=\"#1c00cf\">9<\/font>,<font color=\"#1c00cf\">8<\/font>,<font color=\"#1c00cf\">4<\/font>,<font color=\"#1c00cf\">3<\/font>,<font color=\"#1c00cf\">2<\/font><font color=\"#007400\">; BIOS error codes<\/font><\/font>\n<font color=\"#000000\">                db <font color=\"#1c00cf\">1<\/font>,<font color=\"#1c00cf\">2<\/font>,<font color=\"#1c00cf\">6<\/font>,<font color=\"#1c00cf\">0x0C<\/font>,<font color=\"#1c00cf\">4<\/font>,<font color=\"#1c00cf\">0x0C<\/font>,<font color=\"#1c00cf\">4<\/font>,<font color=\"#1c00cf\">8<\/font>,<font color=\"#1c00cf\">0<\/font>,<font color=\"#1c00cf\">0x0C<\/font>,<font color=\"#1c00cf\">0x0C<\/font><font color=\"#007400\">; IBMBIO error codes<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">int_13_cmd      db <font color=\"#1c00cf\">2<\/font>     <\/font>\n<font color=\"#000000\">temp_sp         dw <font color=\"#1c00cf\">0<\/font>     <\/font>\n<font color=\"#000000\">num_sectors     db <font color=\"#1c00cf\">0<\/font>     <\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#000000\">                times <font color=\"#1c00cf\">513<\/font> db <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\">                db <font color=\"#1c00cf\">0xC9<\/font><\/font>\n<font color=\"#000000\">                times <font color=\"#1c00cf\">126<\/font> db <font color=\"#1c00cf\">0<\/font><\/font>\n<font color=\"#000000\"><br \/><\/font>\n<font color=\"#007400\">;-----------------------------------------------------------------------------<\/font>\n<font color=\"#000000\"><br \/><\/font>\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Update: The source is available at github.com\/mist64\/msdos1 My last post was about the internals of the DOS 1.0 bootsector. This time, let&#8217;s look at the next stage of the DOS 1.0 boot process, the hardware abstraction library IBMBIO.COM. CP\/M and DOS History Let us first look at the historical background: CP\/M was an 8 bit &#8230; <a title=\"Reverse-Engineering DOS 1.0 &#8211; Part 2: IBMBIO.COM\" class=\"read-more\" href=\"https:\/\/www.pagetable.com\/?p=184\" aria-label=\"Read more about Reverse-Engineering DOS 1.0 &#8211; Part 2: IBMBIO.COM\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,11,16,38],"tags":[],"class_list":["post-184","post","type-post","status-publish","format-standard","hentry","category-archeology","category-dos","category-github","category-x86"],"_links":{"self":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/posts\/184","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=184"}],"version-history":[{"count":0,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=\/wp\/v2\/posts\/184\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=184"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=184"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pagetable.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=184"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}