;       PC-DOS Bootstrap Loader
;
;       This code is found in sector 0 of every PC-DOS diskette.  It loads
;       the file IBMBIO.COM into memory and transfers control to it (on a
;       bootable diskette) or displays relevant error messages (on a non-
;       bootable diskette)
;
;       Although this code (in object form) appears in sector 0 of every
;       diskette formatted by DOS, copyrights are no doubt in effect, by
;       Microsoft, or IBM, or Both.
;       It has been hand dis-assembled from object code, and appropriately
;       commented.  Beware the computer police!
;
;
;       Notes - This code taken from a high-density (1.2Meg) diskette
;             - Boot code is loaded into memory, at absolute addr 0000:7C00
;             - Several locations within the code are used for data after
;               they are no longer useful.  These locations have been combined
;               into a structure, the elements of which are names xNN, where
;               NN is a two digit number corresponding to sequence.  They are
;               accessed via equates, of the form aXXXXXX, where XXXXXX is
;               a symbolic name ("a" stands for "alias")
;
;*****************************************************************************


LoadAdr equ     7C00h                   ; Load address of this code
Vec_1E  equ     0078h                   ; Address of Interrupt 1E Vector
FNSize  equ       11                    ; Size of a Filename + Extension

DirLoad equ     0500h                   ; Where the directory block is loaded
BIOLoad equ     0700h                   ; Where IBMBI.COM is loaded

ConOut  equ     10h                     ; BIOS Interrupt: Console Output
DiskInt equ     13h                     ; BIOS Interrupt: Diskette Control
KbdInt  equ     16h                     ; BIOS Interrupt: Keyboard Input
Reboot  equ     19h                     ; BIOS Interrupt: Reboot This Machine



        ; The following structure a directory entry.
        ; It's here to make the code look better.

DirNtry struc
        Filename    db  8 dup(?)
        Extension   db  3 dup(?)
        Attrib      db  ?
        Reserved    db  10 dup(?)
        FileTime    dw  ?
        FileDate    dw  ?
        FirstClstr  dw  ?
        FileSize    dd  ?
DirNtry ends


        page

        ; The following structure corresponds to the section of code space
        ; which is reused.  All aliases are defined as byte offsets.
        ; In addition, the diskette-description header symbols are defined
        ; here, as absolute addresses, because that is how they are
        ; accessed within the program (Offsets from DS, which is 0000)

ReUsed  struc
                    db      LoadAdr dup(?)  ; The beginning of memory
                    db      3       dup(?)  ; The jump to executable code
        OEM_ID      db      8       dup(?)  ; OEM Identification
        Byt_Sec     dw      ?               ; Bytes per Sector (512)
        Sct_AlU     db      ?               ; Sectors per Allocation Unit
        RsvdSct     dw      ?               ; Reserved Sectors (strt at 0)
        NumFATs     db      ?               ; Number of FAT's
        RootSiz     dw      ?               ; # of Root Dir Entries (112)
        TotSect     dw      ?               ; Total Sectors in Device (720)
        MedDesc     db      ?               ; Media Descriptor Byte (DS/9s)
        FATSect     dw      ?               ; # Sectors used by each FAT
        Sct_Trk     dw      ?               ; Sectors per Track
        NumHead     dw      ?               ; Number of Heads
        NumHSct     dw      ?               ; Nubmer of Hidden Sectors
        aDrivNm     db      ?               ; Drive Number (always 0, = A:)
        aHeadNm     db      ?               ; Head Number
                    db      0Bh     dup(?)  ; Disk Parameters Table
                    db      ?               ; - Not Resused -- It's START:
        a1stDat     dw      ?               ; First sector above directory
        aTrakNm     dw      ?               ; Track Number
        aSectNm     db      ?               ; Sector Number (1..n)
        aFilSize    db      ?               ; Sectors in current boot file
        aCurSect    dw      ?               ; Secotr to read from
        aDirLoc     dw      ?               ; First sector in directory
ReUsed  ends


        page
        org     0000


Entry:  0000    JMP     Start                   ; Skip identification code
        0002    NOP


        0003    db      'IBM  3.1'              ; OEM Identification
        000B    dw      0200h                   ; Bytes per Sector (512)
        000D    db         2                    ; Sectors per Allocation Unit
        000E    dw         1                    ; Reserved Sectors (strt at 0)
        0010    db         2                    ; Number of FAT's
        0011    dw        70h                   ; # of Root Dir Entries (112)
        0013    dw      02D0h                   ; Total Sectors in Device (720)
        0015    db       0FDh                   ; Media Descriptor Byte (DS/9s)
        0016    dw         2                    ; # Sectors used by each FAT
        0018    dw         9                    ; Sectors per Track
        001A    dw         2                    ; Number of Heads
        001C    dw         0                    ; Nubmer of Hidden Sectors

                dw         0                    ; - gets filled in...


DskParm db      00,  00,  00,  00               ; Disk parameter block - gets
        db      0Fh, 00,  00,  00               ;  contents of *IntVect_1E
        db      00,  01h, 00                    ;  (except those already set)


Start:  002B    CLI                             ; The stack will be placed just
        002C    XOR     AX,AX                   ;  below executable code
        002E    MOV     SS,AX
        0030    MOV     SP, LoadAdr
        0033    PUSH    SS                      ; ES will do 0-page addressing
        0034    POP     ES

        0035    MOV     BX, Vec_1E              ; Diskette parms at *IntVect_1E
        0038    LDS     SI, SS:[BX]             ;   DS:SI => Diskette Params
        003B    PUSH    DS                      ; - These addresses are being
        003C    PUSH    SI                      ;   saved so that the locations
        003D    PUSH    SS                      ;   may be restored if a boot
        003E    PUSH    BX                      ;   error occurs
        003F    MOV     DI, DskParm             ; Params will go into our CS
        0042    MOV     CX, 000Bh               ; Note - If any of the bytes
        0045    CLD                             ;        are already set in our
DParmLp:0046    LODSB                           ;        table, our values are
        0047    CMP     Byte Ptr ES:[DI], 00    ;        retained.  This allows
        004B    JZ      SetParm                 ;        simple changes to the
        004D    MOV     AL, ES:[DI]             ;        loaded code for any
SetParm:0050    STOSB                           ;        diskette in use.
        0051    MOV     AL, AH
        0053    LOOP    DParmLp

ReParm: 0055    PUSH    ES                      ; Re-point the Diskette params
        0056    POP     DS                      ;  into the block just built
        0057    MOV     [BX+02],AX
        005A    MOV     Word Ptr [BX],7C20
        005E    STI

        005F    INT     DiskInt                 ; Reset the disk subsystem
        0061    JB      DskErr2                 ;  (fatal error abort)
        0063    MOV     AL, [NumFATs]           ; Find the starting sector for
        0066    CBW                             ;  the Root Directory
        0067    MUL     Word Ptr [FATSect]
        006B    ADD     AX, [NumHSct]
        006F    ADD     AX, [RsvdSct]
        0073    MOV     [aDirLoc], AX
        0076    MOV     [a1stDat], AX
        0079    MOV     AX, TYPE DirNtry
        007C    MUL     Word Ptr [RootSiz]      ; How big is the directory?
        0080    MOV     BX, [Byt_Sec]
        0084    ADD     AX, BX                  ;  (this will round up)
        0086    DEC     AX
        0087    DIV     BX
        0089    ADD     [a1stDat], AX           ; Store upper bounds of Root
        008D    MOV     BX, DirLoad
        0090    MOV     AX, [aDirLoc]           ; And point to the first block
        0093    CALL    CvtSct
        0096    MOV     AX, 0201h
        0099    CALL    ReadSct                 ; Read it In!
        009C    JB      DskErr1                 ;  (Can't do it! Abort!)
        009E    MOV     DI, BX                  ; Index into the directory blk
        00A0    MOV     CX, FNSize
        00A3    MOV     SI, BIO_Nam             ; Looking for "IBMBIO.COM"
        00A6    REPZ
        00A7    CMPSB
        00A8    JNZ     DskErr1                 ; It wasn't the first file!

        00AA    LEA     DI, [BX+20]             ; Looking for "IBMDOS.COM"
        00AD    MOV     SI, DOS_Nam
        00B0    MOV     CX, FNSize
        00B3    REPZ
        00B4    CMPSB
        00B5    JZ      BootIt                  ; Hey! It's Bootable!

DskErr1:00B7    MOV     SI, OFFSET DskMsg       ; Yell at the user
DE_Sub: 00BA    CALL    DspErr
        00BD    XOR     AH, AH                  ; And wait for a keystroke
        00BF    INT     KbdInt
        00C1    POP     SI                      ; Pop the Int_1E Vector Addr
        00C2    POP     DS
        00C3    POP     [SI]                    ; Diskette Params pointer goes
        00C5    POP     [SI+02]                 ;  back into this vector
        00C8    INT     Reboot                  ; And we do it all again.

DskErr2:00CA    MOV     SI, OFFSET BootMsg      ; An indeterminate, real bad
        00CD    JMP     DE_Sub                  ;  error has happened.


BootIt: 00CF    MOV     AX, [DirLoad.FileSize]  ; Get the IBMBIO.COM's size
        00D2    XOR     DX, DX                  ; We hope it's under 64k!
        00D4    DIV     Word Ptr [Byt_Sec]
        00D8    INC     AL
        00DA    MOV     [aFilSize], AL          ; Store it away
        00DD    MOV     AX, [a1stDat]           ; It must start right after
        00E0    MOV     [aCurSect], AX          ;  the root directory ends
        00E3    MOV     BX, BIOLoad
RdLoop: 00E6    MOV     AX, [a1stDat]           ; So set up the location
        00E9    CALL    CvtSct
        00EC    MOV     AX, [Sct_Trk]           ; And read that track, from
        00EF    SUB     AL, [aSectNm]           ;  that sector to the end
        00F3    INC     AX
        00F4    PUSH    AX
        00F5    CALL    ReadSct
        00F8    POP     AX
        00F9    JB      DskErr2
        00FB    SUB     [aFilSiz], AL           ; Is there any more?
        00FF    JBE     CallBIO
        0101    ADD     [a1stDat], AX           ; Yes, position the buffer ptr
        0105    MUL     Word Ptr [Byt_Sec]      ;  after what we've read in,
        0109    ADD     BX, AX                  ;  and read the next track
        010B    JMP     RdLoop
CallBIO:010D    MOV     CH, [MedDesc]           ; Ready to call IBMBIO.COM
        0111    MOV     DL, [aDrivNm]
        0115    MOV     BX, [aCurSct]
        0119    JMP     0070:0000


        ;****************************************************************
        ;
        ; DspErr
        ;
        ;   Display error message on console
        ;
        ;   Input:
        ;       DS:SI = Pointer to NUL-terminated string
        ;

DspErr: 011E    LODSB
        011F    OR      AL, AL                  ; Check for End-Of-String
        0121    JZ      Return                  ; Save a byte!
        0123    MOV     AH, 0E                  ; Using TTYOUT mode
        0125    MOV     BX, 0007
        0128    INT     Video
        012A    JMP     DspErr


        ;****************************************************************
        ;
        ; CvtSct
        ;
        ;   Convert logical sector number to absolute Head / Track / Sector
        ;   position.
        ;
        ;   Input:
        ;       AX = Logical Sector Number
        ;
        ;   Output:
        ;       -- = Absolute position is stored in aliased variables
        ;       AX = Track Number
        ;       DL = Head Number

CvtSct: 012C    XOR     DX,DX
        012E    DIV     Word Ptr [SctTrk]       ; Sectors go from 1 .. N
        0132    INC     DL
        0134    MOV     [aSectNm], DL
        0138    XOR     DX, DX
        013A    DIV     Word Ptr [NumHead]
        013E    MOV     [aHeadNm], DL
        0142    MOV     [aTrakNm], AX
Return: 0145    RET


        ;****************************************************************
        ;
        ; ReadSct
        ;
        ;   Reads a sector into memory
        ;   position.
        ;
        ;   Input:
        ;       AL = Number of sectors to read
        ;       BX = Buffer Address
        ;
        ;   Output:
        ;       -- = Output is identical to BIOS Interrupt 13 (AH=2)

ReadSct:0146    MOV     AH, 02                  ; We will be doing a Read
        0148    MOV     DX, [aTrakNm]           ; Track and sector get mixed -
        014C    MOV     CL, 06                  ;  high 2 bits of track number
        014E    SHL     DH, CL                  ;  are combined with sector #
        0150    OR      DH, [aSectNm]           ;  (allows 1024 tracks/cyl)
        0154    MOV     CX, DX
        0156    XCHG    CH, CL
        0158    MOV     DX, [aDrivNum]          ; Drive and Head are here
        015C    INT     DiskInt                 ; Perform the operation
        015E    RET



;******************************************************************************
;
;       Messages, And Whatever else is Needed
;
;******************************************************************************


DskMsg  015F    db      13, 10, 'Non-System disk or disk error'
                db      13, 10, 'Replace and strike any key when ready'
                db      13, 10, 0
BootMsg 01A8    db      13, 10, 'Disk Boot failure'
                db      13, 10, 0

BIO_Nam 01BE    db      'IBMBIO  COM'
DOS_Nam 01CA    db      'IBMDOS  COM'

Resrvd          db      42 dup( 0 )
Boot_ID         db      55h, 0AAh
