        TITLE   'rxdos bios interface drivers'
        PAGE 59, 132
        .LALL

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Real Time Dos                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This material  was created as a published version  of a DOS  ;
        ;  equivalent product.   This program  logically  functions in  ;
        ;  the same way as  MSDOS functions and it  is  internal  data  ;
        ;  structure compliant with MSDOS 6.0                           ;
        ;                                                               ;
        ;  This product is distributed  AS IS and contains no warranty  ;
        ;  whatsoever,   including  warranty  of   merchantability  or  ;
        ;  fitness for a particular purpose.                            ;
        ;                                                               ;
        ;                                                               ;
        ;  (c) Copyright 1990, 1997. Api Software and Mike Podanoffsky  ;
        ;      All Rights Reserved Worldwide.                           ;
        ;                                                               ;
        ;  This product is protected under copyright laws and  may not  ;
        ;  be reproduced  in whole  or in part, in any form  or media,  ;
        ;  included but not limited to source listing, facsimile, data  ;
        ;  transmission, cd-rom, or  floppy disk without the expressed  ;
        ;  written consent of the author.                               ;
        ;                                                               ;
        ;  License  for  distribution  for commercial  use  or  resale  ;
        ;  required from:                                               ;
        ;                                                               ;
        ;  Api Software                                                 ;
        ;  12 South Walker Street                                       ;
        ;  Lowell,  MA   01851                                          ;
        ;                                                               ;
        ;  internet: mikep@world.std.com                                ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  Compile with MASM 5.1                                        ;
        ;...............................................................;

        include rxdosmac.asm
        include rxdosdef.asm

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  RxDOSBIO.SYS                                                 ;
        ;...............................................................;

RxDOSBIOS               SEGMENT PARA PUBLIC 'CODE'
                        assume cs:RxDOSBIOS, ds:RxDOSBIOS, es:RxDOSBIOS, ss:RxDOSBIOS

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Load remainder of RxDOS                                      ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  When rxdosbio.sys is loaded by the disk boot loader, it      ;
        ;  loads automatically the first three sectors regardless of    ;
        ;  the cluster allocation.  For RxDOS, only the first sector in ;
        ;  a cluster needs to actually be loaded.  This loads this      ;
        ;  portion of the rxdosbio.sys code, which loads the remainder  ;
        ;  of the program using FAT allocations.                        ;
        ;                                                               ;
        ;  The boot program has also detected and read the first sector ;
        ;  of the root directory which is located at 0000:0500 (that    ;
        ;  is, 50:0).                                                   ;
        ;                                                               ;
        ;  Boot sequence is                                             ;
        ;                                                               ;
        ;  MBR (master boot record)                                     ;
        ;    loads at 0:7c00, relocated to 60:0                         ;
        ;    located at physical address cyl: 0  head: 0  sector: 1     ;
        ;                                                               ;
        ;  BOOT (boot record)                                           ;
        ;    loads at 0:7c00                                            ;
        ;    located at start of partition (logical sector 0)           ;
        ;                                                               ;
        ;  RXDOSBIO.SYS                                                 ;
        ;    loads initially at 70:0, relocated high                    ;
        ;    boot record loads first three sectors (regardless of       ;
        ;     cluster size)                                             ;
        ;    initial part of rxdosbio.sys loads remainder of module     ;
        ;     high in low memory, then loads rxdos.sys                  ;
        ;                                                               ;
        ;  RXDOS.SYS                                                    ;
        ;    loaded by rxdosbio.sys                                     ;
        ;                                                               ;
        ;  RXDOSCMD.EXE                                                 ;
        ;                                                               ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Startup Code                                                 ;
        ;...............................................................;

        org 0000h

        public RxDOSBIOS_Start


RxDOSBIOS_Start:        jmp RxDOSBIOS_Initialize

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Block Device Info                                            ;
        ;...............................................................;

                DISKBLOCK struc

_dskNextPointer                 dd ?                    ; pointer to next
_dskPhysDriveNumber             db ?                    ; 
_dskDOSLogicalDiskUnit          db ?                    ; 
_dskBPB                         db sizeBPB dup(?)       ; 
_dskFATSystemStatus             db ?                    ; 
_dskDevOpenCount                dw ?                    ; 
_dskDeviceType                  db ?                    ; 22
_dskStatusFlag                  dw ?                    ; 23
_dskCylinders                   dw ?                    ; 25
_dskDefaultBPB                  db sizeBPB dup(?)       ; 27

_dskPartitionBeginHead          db ?                    ; begin head address
_dskPartitionBeginSector        db ?                    ; begin sector address
_dskPartitionBeginCylinder      db ?                    ; begin cylinder address
_dskPartitionEndHead            db ?                    ; end head address
_dskPartitionEndSector          db ?                    ; end sector address
_dskPartitionEndCylinder        db ?                    ; end cylinder address

_dskCylinderAtLastOp            db ?                    ; 46
_dskClockAtLastOp               dd ?                    ; 47
_dskVolumeName                  db (sizeFILENAME + 1) dup(?)
_dskSerialNumber                dd ?                    ; 57
_dskFATSystemID                 db 8 dup(?)             ; 5B

_dskExtHiddenSectors            dd ?                    ; extended hidden sectors
                DISKBLOCK ends

sizeDISKBLOCK                   equ size DISKBLOCK

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
;  Type Codes
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

DSKFILESYSTYPE_12Bits           equ 00h
DSKFILESYSTYPE_16Bits           equ 40h
DSKFILESYSTYPE_Invalid          equ 80h
DSKFILESYSTYPE_IsValid          equ 40h                 ; only bits 7, 6 are assignable

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
;  Form Codes
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

DSKFORMTYPES_40_9               equ 00h                 ; 40 cyls, <= 9 sect/track
DSKFORMTYPES_80_15              equ 01h                 ; 80 cyls, 15 sect/track
DSKFORMTYPES_80_9               equ 02h                 ; 80 cyls,  9 sect/track
DSKFORMTYPES_HARDDISK           equ 03h                 ; hard disk
DSKFORMTYPES_FLOPPY_OTHER       equ 07h                 ; other floppy disk types
DSKFORMTYPES_80_36              equ 07h                 ; 80 cyls, 36 sect/track      1.44M

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
;  Status Codes
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

IsNonRemovable                  equ 0001h               ; non-removable media
ChgLineSupportedbyBIOS          equ 0002h               ; change line supported
BPBLocked                       equ 0004h               ; BPB Locked status
GoodTrackLayout                 equ 0008h               ; if continuous sectors on track
SharesPhysDevice                equ 0010h               ; if physical unit partitioned
ActiveUnit                      equ 0020h               ; if active unit
DiskChangeDetected              equ 0040h               ; disk change detected
DASD_SetRequired                equ 0080h               ; DASD_SetRequired
VerifyWrite                     equ 0100h               ; Verify Write
RW_Disabled                     equ 0200h               ; read/ write disabled

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Media Descriptor Table                                       ;
        ;...............................................................;

                MEDIADESCRIPT struc

_mdtSpecifyByte                 dw ?                    
_mdtTimerTillMotorOff           db ?
_mdtBytesPerSector              db ?                    ; 0 = 128, 1=256, 2=512, 3=1024
_mdtSectorsPerTrack             db ?
_mdtGapLengthinBytes            db ?
_mdtDataLengthinBytes           db ?
_mdtGapLengthForFormat          db ?
_mdtFillByteForFormat           db ?
_mdtHeadSettleTime              db ?                    ; in ms
_mdtMotorStartupTime            db ?                    ; in 1/8 seconds

                MEDIADESCRIPT ends

sizeMEDIADESCRIPT               equ size MEDIADESCRIPT     

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Data                                                         ;
        ;...............................................................;

BootDrive:                      db 0

BootDrive_BPB:                  dw sizeCCBData          ; __bsBytesPerSector
                                db 1                    ; __bsSectorsPerCluster
                                dw 1                    ; __bsResSectors
                                db 2                    ; __bsNumCopiesFAT
                                dw 00E0h                ; __bsMaxAllocRootDir
                                dw 0960h                ; __bsMaxSectors
                                db 0f9h                 ; __bsMediaDescriptor
                                dw 7                    ; __bsSectorsPerFat
                                dw 15                   ; __bsSectorsPerTrack
                                dw 2                    ; __bsHeads
                                dd 0                    ; __bsHiddenSectors
                                dd 0960h                ; __bsHugeSectors

RxDOSBIOS_INITBLOCK:            db sizeSYSINIT dup(0)
diskParameterTable:             dd 0                    ; disk parameter table

_FATbuffer_SectorNumber:        dd -1                   ; allocated FAT sector
_FATbuffer:                     dd 0

RootDirSector:                  dd 0                    ; root directory sector
ReservedBeforeDataSector:       dd 0                    ; first data sector

RXDOSBIO_ClusterAddr:           dw 0                    ; cluster address of RxDOSBIO.SYS
RXDOS_ClusterAddr:              dw 0                    ; cluster address of RxDOS.SYS

_FloppyDrives:                  dw 0                    ; # floppy drives
_FixedDrives:                   dw 0                    ; # fixed drives
RxDOS_LoadSegment:              dd 0                    ; where RxDOS loaded

ptr_StartBlockedDeviceTable:    dd 0                    ; ptr to Disk Parameter Blocks

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; location of system entries in root directory
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RXDOSBIO_SYS                    equ (0 * sizeDIRENTRY)
RXDOS_SYS                       equ (1 * sizeDIRENTRY)

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; messages
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RXDOSBIO_MSGLoadingRxDOS:       db "Starting RxDOS...", 0
RXDOSBIO_MSGErrorLoadingSystem: db 0Dh, 0Ah, "Error loading RxDOS. Please reboot. ", 0

     IFDEF RxDOS_DEBUG
RXDOSBIO_MSGDEBUG_CANTLOAD:     db "Can't Load DEBUG Image", 0

     ENDIF

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Load Program Using FAT                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   dx     starting cluster address                             ;
        ;   es:di  buffer                                               ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cy     error reading file                                   ;
        ;...............................................................;

_LoadProgram:

        Entry
        def  _sectorsToRead                             ; sectors to read
        def  _scanEndCluster, dx                        ; last cluster scanned
        def  _currentCluster, dx                        ; cluster address of pgm /start
        def  _relCluster                                ; rel cluster offset required
        ddef _buffer, es, di                            ; where to load

        xor dx, dx
        mov ax, word ptr [ _buffer. _pointer ][ bp ]
        div word ptr [ BootDrive_BPB. _bpbBytesPerSector ]
        div byte ptr [ BootDrive_BPB. _bpbSectorsPerCluster ]

        xor ah, ah
        storarg _relCluster, ax                         ; required rel cluster offset
        or ax, ax                                       ; starts at first file cluster ?
        jz _LoadProgram_18                              ; yes -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  at start, we must position at rel cluster
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_LoadProgram_14:
        mov ax, word ptr [ BootDrive ]
        mov dx, word ptr [ _currentCluster ][ bp ]
        call getNextCluster                             ; get next cluster
        jz _LoadProgram_Return                          ; exit if error -->

        mov word ptr [ _currentCluster ][ bp ], dx      ; save cluster number
        dec word ptr [ _relCluster ][ bp ]              ; scan more clusters ?
        jnz _LoadProgram_14                             ; scan more -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  optimize cluster read
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_LoadProgram_18:
        mov ax, word ptr [ BootDrive ]
        mov dx, word ptr [ _currentCluster ][ bp ]
        call scanClusterMap
        mov word ptr [ _sectorsToRead ][ bp ], cx       ; # sectors to sequentially read
        mov word ptr [ _scanEndCluster ][ bp ], dx      ; last cluster scanned

    ;   mov dl, byte ptr [ BootDrive_BPB. _bpbSectorsPerCluster ]
    ;   sub dl, al                                      ; sectors remaining
    ;   sub word ptr [ _sectorsToRead ][ bp ], dx       ; adjust sectors to read
    ;   jnz _LoadProgram_26                             ; keep reading if more required -->
    ;   mov word ptr [ _sectorsToRead ][ bp ], 0001     ; else default to one sector

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get sector offset to read
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_LoadProgram_26:
        xor dx, dx
        mov ax, word ptr [ _buffer. _pointer ][ bp ]
        div word ptr [ BootDrive_BPB. _bpbBytesPerSector ]
        div byte ptr [ BootDrive_BPB. _bpbSectorsPerCluster ]

        xor bh, bh
        mov bl, ah                                      ; sector adjust for pointer

        mov ax, word ptr [ _currentCluster ][ bp ]
        dec ax
        dec ax

        xor cx, cx
        mov cl, byte ptr [ BootDrive_BPB. _bpbSectorsPerCluster ]
        mul cx                                          ; compute sector
        add ax, word ptr [ ReservedBeforeDataSector. _low  ]
        adc dx, word ptr [ ReservedBeforeDataSector. _high ]

        add ax, bx                                      ; add sector adjust
        adc dx, 0000

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  read buffer
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov cx, dx
        mov dx, ax                                      ; sector address

        mov al, byte ptr [ BootDrive ]
        getdarg es, di, _buffer
        getarg bx, _sectorsToRead
        call ReadBuffer                                 ; do read
        jc _LoadProgram_Abrupt                          ; if error -->

        mov ax, word ptr [ _sectorsToRead ][ bp ]
        mul word ptr [ BootDrive_BPB. _bpbBytesPerSector ]
        add word ptr [ _buffer. _pointer ][ bp ], ax

        getarg dx, _scanEndCluster                      ; last cluster scanned
        storarg _currentCluster, dx                     ; cluster address of pgm /start
        cmp dx, -1                                      ; was last read to end of file ?
        jnz _LoadProgram_18                             ; no, keep reading -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_LoadProgram_Return:
        mov cx, word ptr [ _buffer. _pointer ][ bp ]
        or cx, cx                                       ; if ok return

_LoadProgram_Abrupt:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read FAT Buffer                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx:dx  sector to read                                       ;
        ;...............................................................;

readFATBuffer:
        les di, dword ptr [ _FATbuffer ]

        cmp word ptr [ _FATbuffer_SectorNumber. _low  ], dx
        jnz readFATBuffer_08
        cmp word ptr [ _FATbuffer_SectorNumber. _high ], cx
        jz readFATBuffer_12

readFATBuffer_08:
        mov bx, 0001
        mov al, byte ptr [ BootDrive ]
        mov word ptr [ _FATbuffer_SectorNumber. _high ], cx
        mov word ptr [ _FATbuffer_SectorNumber. _low  ], dx
        call readBuffer

readFATBuffer_12:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read Buffer                                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   bx     buffers to read                                      ;
        ;   cx:dx  logical sector to read                               ;
        ;   es:di  buffer address                                       ;
        ;...............................................................;

readBuffer:
        Entry
        ddef _bufferPtr, es, di
        ddef _sector, cx, dx
     ;  def _readSector
     ;  def _readHead
     ;  def _readTrack
        def _drive, ax
        def _buffers, bx
        def _retries   

        SaveRegisters bp, dx, ax, cx

readBuffer_08:
        mov word ptr [ _retries ][ bp ], 3

readBuffer_10:
        getdarg dx, ax, _sector
        add ax, word ptr [ BootDrive_BPB. _bpbHiddenSectors. _low ]
        adc dx, word ptr [ BootDrive_BPB. _bpbHiddenSectors. _high ]

        div word ptr [ BootDrive_BPB. _bpbSectorsPerTrack ]
        inc dl
        mov cl, dl                                      ; _readSector

        xor dx, dx
        div word ptr [ BootDrive_BPB. _bpbHeads ]
      ; mov word ptr [ _readTrack ][ bp ], ax           ; dont need to save track
      ; mov byte ptr [ _readHead  ][ bp ], dl           ; don't need to save heads
        mov dh, dl                                      ; head

        shl ah, 1                                       ; high order two bits of track
        shl ah, 1                                       ; ... moved to top end of reg
        shl ah, 1
        shl ah, 1
        shl ah, 1
        shl ah, 1
        or cl, ah                                       ; or with sector
        mov ch, al                                      ; remainder of track to ch

        mov al, byte ptr [ _buffers ][ bp ]             ; plus buffers to read 
        mov ah, al                                      ; buffers
        add ah, cl                                      ; starting sector
        sub ah, byte ptr [ BootDrive_BPB. _bpbSectorsPerTrack ]
        jle readBuffer_12                               ; no, ok to read -->
        sub al, ah                                      ; safe number of buffers to read
        inc ax

readBuffer_12:
        push ax                                         ; # buffers requested
        mov ah, 02h                                     ; read command type
        mov dl, byte ptr [ _drive ][ bp ]
        getdarg es, bx, _bufferPtr
      ;  NormalizeBuffer es, bx

        int 13h
        pop ax                                          ; # buffers requested
        jc readBuffer_26                                ; if error -->

        xor ah, ah
        xor cx, cx
        mov bx, ax
        mul word ptr [ BootDrive_BPB. _bpbBytesPerSector ]
        add word ptr [ _bufferPtr. _pointer ][ bp ], ax

        add word ptr [ _sector. _low  ][ bp ], bx
        adc word ptr [ _sector. _high ][ bp ], cx

        sub byte ptr [ _buffers ][ bp ], bl             ; buffers remaining after this read 
        jg  readBuffer_08                               ; if more to read
        jmp short readBuffer_28                         ; exit -->

readBuffer_26:
        xor ax, ax
        mov dl, byte ptr [ _drive ][ bp ]
        int 13h                                         ; if error, attempt reset

        dec word ptr [ _retries ][ bp ]
        jnz readBuffer_10

        stc                                             ; make sure exit with error flag

readBuffer_28:
        RestoreRegisters cx, ax, dx, bp
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Next Cluster                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   dx     current cluster                                      ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx     next cluster                                         ;
        ;   zr     if end of cluster chain.                             ;
        ;...............................................................;

getNextCluster:
        Entry
        def _drive, ax
        def _cluster, dx
        def _MaxClusters

        saveRegisters es, di, si, cx, bx, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  determine whether its 12 or 16 bit FAT entries
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor cx, cx
        mov cl, byte ptr [ BootDrive_BPB. _bpbSectorsPerCluster  ]
        mov dx, word ptr [ BootDrive_BPB. _bpbHugeSectors. _high ]
        mov ax, word ptr [ BootDrive_BPB. _bpbHugeSectors. _low  ]
        div cx
        storarg _MaxClusters, ax

        mov dx, word ptr [ BootDrive_BPB. _bpbHugeSectors. _high ]
        mov ax, word ptr [ BootDrive_BPB. _bpbHugeSectors. _low  ]

        mov dx, -1                                      ; presume end if error
        mov ax, word ptr [ _cluster ][ bp ]             ; get cluster #
        or ax, ax                                       ; invalid number 
        jz getNextCluster_Return                        ; exit -->
        cmp ax, word ptr [ _MaxClusters ][ bp ]
        jnc getNextCluster_Return                       ; if valid cluster # -->

getNextCluster_08:
        xor dx, dx
        test word ptr [ _MaxClusters ][ bp ], 0F000h
        jnz getNextCluster_16Bits                       ; if 16 -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  12 bit FAT entries
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getNextCluster_12Bits:
        mov ax, word ptr [ _drive   ][ bp ]             ; get drive
        mov dx, word ptr [ _cluster ][ bp ]             ;  and cluster
        call _get_12Bit_ClusterValue
        jmp short getNextCluster_Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  16 bit FAT entries
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getNextCluster_16Bits:
        mov cx, word ptr [ BootDrive_BPB. _bpbBytesPerSector ]
        shr cx, 1
        div cx                                          ; FAT sector/ Offset

    ; ax will contain FAT sector
    ; dx will contain byte offset into FAT sector

        add dx, dx                                      ; make word offset
        push dx

        xor cx, cx
        mov dx, word ptr [ BootDrive_BPB. _bpbResSectors ]  ; where is first FAT table ?
        add dx, ax                                      ; add offset required
        mov ax, word ptr [ _drive ][ bp ]               ; get drive
        call ReadFATBuffer                              ; read FAT Table

        pop bx                                          ; word offset into FAT table
        mov dx, word ptr es:[ bx + di ]                 ; get FAT word

        mov ax, dx
        and ax, 0FFF8h                                  ; FAT value, 12 bit entries.
        cmp ax, 0FFF8h                                  ; end of chain ?
        jnz getNextCluster_Return                       ; no -->
        mov dx, -1                                      ; if end, set end value

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getNextCluster_Return:
        restoreRegisters ax, bx, cx, si, di, es
        cmp dx, -1                                      ; set if end of chain
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Scan Ahead Cluster Map                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   dx     current cluster                                      ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     # sectors to sequentially read                       ;
        ;   dx     last cluster value                                   ;
        ;...............................................................;

scanClusterMap:

        Entry
        def  _drive, ax
        def  _cluster, dx
        def  _nextCluster
        def  _clusterCount, 0000

scanClusterMap_12:
        inc word ptr [ _clusterCount ][ bp ]            ; cluster count

        mov cx, dx
        call getNextCluster                             ; get next cluster
        cmp dx, -1                                      ; end ?
        jz scanClusterMap_22                            ; yes -->

        sub cx, dx                                      ; sequential clusters are ...
        cmp cx, -1                                      ;  ... identified by a -1 offset
        jz scanClusterMap_12                            ; if still sequential -->

scanClusterMap_22:
        mov ax, word ptr [ _clusterCount ][ bp ]        ; # sequential clusters
        mul byte ptr [ BootDrive_BPB. _bpbSectorsPerCluster ]
        mov cx, ax                                      ; # sectors to sequentially read
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get 12Bit FAT Table Value                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   dx     current cluster                                      ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx     value at cluster (next cluster)                      ;
        ;   zr     if end of cluster chain.                             ;
        ;...............................................................;

_get_12Bit_ClusterValue:
        Entry
        def  _drive, ax
        def  _cluster, dx
        def  _sectorsize
        ddef _sector

        mov ax, dx
        add ax, ax
        add ax, dx                                      ; nibble address

        xor dx, dx
        mov cx, word ptr [ BootDrive_BPB. _bpbBytesPerSector ]
        mov word ptr [ _sectorsize ][ bp ], cx
        dec word ptr [ _sectorsize ][ bp ]
        shl cx, 1                                       ; nibbles / sector
        div cx                                          ; sector to read

      ; ax will contain sector
      ; dx will contain nibble offset

        shr dx, 1                                       ; word offset
        push dx                                         ;

        xor cx, cx                                      ; 32 bit address
        mov dx, ax
        add dx, word ptr [ BootDrive_BPB. _bpbResSectors ]  ; where is first FAT table ?
        stordarg _sector, cx, dx                        ; 32 bit sector address

        mov ax, word ptr [ _drive ][ bp ]               ; get drive
        call ReadFATBuffer                              ; read FAT Table

        pop bx                                          ; word offset into FAT table
        mov dx, word ptr es:[ bx + di ]                 ; get FAT word

        cmp bx, word ptr [ _sectorsize ][ bp ]          ; at sector size -1 boundry ?
        jnz _get_12Bit_ClusterValue_12                  ; no, ok to return as is -->

        push dx                                         ; else save what we have
        getdarg cx, dx, _sector                         ; read next cluster sector
        add dx, 0001                                    ; incr by one
        adc cx, 0000

        mov ax, word ptr [ _drive ][ bp ]               ; get drive
        call ReadFATBuffer                              ; read FAT Table

        pop dx
        mov dh, byte ptr es:[ di ]                      ; get FAT word at start of buffer

_get_12Bit_ClusterValue_12:
        test word ptr [ _cluster ][ bp ], 1             ; is cluster Odd ?
        jz _get_12Bit_ClusterValue_14                   ; no, just take value -->

        shr dx, 1
        shr dx, 1
        shr dx, 1
        shr dx, 1

_get_12Bit_ClusterValue_14:
        and dx, 0FFFh                                   ; 12 bit mask
        mov ax, dx
        and ax, 00FF8h                                  ; FAT value, 12 bit entries.
        cmp ax, 00FF8h                                  ; end of chain ?
        jnz _get_12Bit_ClusterValue_16                  ; no -->
        mov dx, -1                                      ; if end, set end value

_get_12Bit_ClusterValue_16:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Error Display                                                ;
        ;...............................................................;

RxDOSBIOS_Error:
        mov si, offset RXDOSBIO_MSGErrorLoadingSystem
        call _DisplayMsg

RxDOSBIOS_Error_04:
        xor ax, ax
        int 16h
        jmp RxDOSBIOS_Error_04

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Display Message                                              ;
        ;...............................................................;

_DisplayMsg:
        push cs
        pop ds

_DisplayMsg_02:
        lodsb                                           ; get character (ds:si)
        or al, al                                       ; null terminator ?
        jnz _DisplayMsg_06                              ; not yet -->
        ret

_DisplayMsg_06:
        push si
        mov ah, 0Eh
        mov bx, 0007h
        int 10h
        pop si
        jmp _DisplayMsg_02

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Init Interrupt Vectors                                       ;
        ;...............................................................;

InitInterruptVectors:

        mov word ptr es:[ _segment ][ bx ], cs
        mov word ptr es:[ _pointer ][ bx ], offset IntReturn
        add bx, 4
        loop InitInterruptVectors

        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Initialize                                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  The boot loader  loader transfers control to here with the   ;
        ;  following arguments:                                         ;
        ;                                                               ;
        ;  CH     media descriptor byte                                 ;
        ;  DL     unit address of startup drive                         ;
        ;  AX:BX  sector address of root directory                      ;
        ;  DS:SI  BIOS parameter table                                  ;
        ;                                                               ;
        ;  int 1E address contains a pointer to the current drive DISK  ;
        ;  PARAMETER Table.                                             ;
        ;                                                               ;
        ;...............................................................;

RxDOSBIOS_Initialize:

        cli
        cld
        mov word ptr cs:[ diskParameterTable. _pointer ], si
        mov word ptr cs:[ diskParameterTable. _segment ], ds
        mov byte ptr cs:[ BootDrive ], dl               ; physical drive (80, ... )

        mov ax, cs
        mov ds, ax                                      ; DS:
        mov es, ax                                      ; ES:
        mov ss, ax                                      ; SS:
        mov sp, offset RxDOSBIOS_LastAddress + 1024     ; temporary stack
        sti

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; copy BPB from boot record (at 0:7C0Bh)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        push ds
        xor ax, ax
        mov ds, ax                                      ; disk boot table
        mov si, 7C0Bh                                   ; at 0000:7C0B
        mov di, offset BootDrive_BPB
        mov cx, sizeBPB
        rep movsb                                       ; copy BPB

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; get cluster address of RXDOSBIO.SYS and RXDOS.SYS
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        pop ds
        xor ax, ax
        mov es, ax                                      ; ES:
        mov si, 500h                                    ; where root directory is located
        mov ax, word ptr es:[ RXDOSBIO_SYS. deStartCluster ][ si ]
        mov dx, word ptr es:[ RXDOS_SYS. deStartCluster ][ si ]
        mov word ptr [ RXDOSBIO_ClusterAddr ], ax
        mov word ptr [ RXDOS_ClusterAddr ], dx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; relocate disk parameter table to safe address ( at, surprisingly, 0:522 )
;  we move it here to maintain some strange compatability with the other DOS.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cli
        mov di, 522h                                    ; destination address (0:522)
        mov word ptr es:[ 1Eh * 4 ][ _segment ], es
        mov word ptr es:[ 1Eh * 4 ][ _pointer ], di

        lds si, dword ptr cs:[ diskParameterTable ]     ; where to copy table
        mov cx, 11
        rep movsb
        sti

        mov bx, 522H + _mdtHeadSettleTime
        mov byte ptr es:[ bx ], 2

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; relocate this loader up to the high end of lower memory 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, cs
        mov ds, ax
        mov es, ax

        int 12h                                         ; get memory configuration
        mov word ptr [ RxDOSBIOS_INITBLOCK. initMemParagraphs ], ax

        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1
        shl ax, 1                                       ; shl ax, 6
        sub ax, 64 * (1024 / sizePARAGRAPH)             ; subtract 64k in segment form
        mov word ptr [ _FATbuffer. _segment ], ax       ; address of FAT buffer

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; switch stacks so it doesn't get clobbered during load
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cli
        mov ss, ax                                      ; new stack
        mov sp, 0FE00h
        nop
        sti

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; initialize and load
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor ax, ax
        mov al, byte ptr [ BootDrive_BPB. _bpbNumCopiesFAT ]
        mul word ptr [ BootDrive_BPB. _bpbSectorsPerFat ]
        add ax, word ptr [ BootDrive_BPB. _bpbResSectors ]
        adc dx, 0000
        mov word ptr [ RootDirSector. _low  ], ax
        mov word ptr [ RootDirSector. _high ], dx

    ; read first sector of Root Directory

        mov cx, word ptr [ BootDrive_BPB. _bpbMaxAllocRootDir ]
        shr cx, 1
        shr cx, 1
        shr cx, 1
        shr cx, 1
        add ax, cx
        adc dx, 0000
        mov word ptr [ ReservedBeforeDataSector. _low  ], ax
        mov word ptr [ ReservedBeforeDataSector. _high ], dx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Reset Interrupts
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov al, 20h
        out 20h, al

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Display startup banner
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, offset RXDOSBIO_MSGLoadingRxDOS
        call _DisplayMsg                                ; Loading RxDOS ...

     IFDEF RxDOS_DEBUG
        mov si, offset RXDOSBIO_MSGDEBUG_CANTLOAD
        call _DisplayMsg                                ; Can't Load Debug Image

     RxDOSBIOS_InitHalt:
        jmp RxDOSBIOS_InitHalt
     ENDIF

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; load the remainder of rxdosbio.sys and rxdos.sys
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov di, 600h                                    ; reload starts at 600h
        mov dx, word ptr [ RXDOSBIO_ClusterAddr ]
        call _LoadProgram                               ; Load RxDOSBIO
        ifc RxDOSBIOS_Error

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Load Protection                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Everything above this line must reside in the first 600 hex  ;
        ;  bytes of the file. This is the minimal portion loaded by the ;
        ;  bootstarp loader.                                            ;
        ;                                                               ;
        ;  The code below causes an error message in compile if the     ;
        ;  file size at this location will exceed 600 hex bytes.        ;
        ;...............................................................;

     IF1
       IF ($ GE 600h)
        error "RxDOSBIO minimal section exceeds 600h (3 sectors) bytes."

       ENDIF
     ENDIF

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Hardware Evaluation and Startup                              ;
        ;...............................................................;

RxDOSBIOS_EvaluateHardware:

        mov dl, byte ptr BootDrive                      ; physical drive
        mov byte ptr [ RxDOSBIOS_INITBLOCK. initBootDrive ], dl

        mov word ptr [ RxDOSBIOS_INITBLOCK. initDeviceChain. _segment ], cs
        mov word ptr [ RxDOSBIOS_INITBLOCK. initDeviceChain. _pointer ], offset CON

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Determine the amount of actual memory
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        int 12h                                         ; get memory size
        mov word ptr [ RxDOSBIOS_INITBLOCK. initMemParagraphs ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Determine number of floppy drives
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        int 11h                                         ; read hardware configuration
        mov word ptr [ _floppyDrives ], 0000            ; number of floppy disks

        test ax, 1                                      ; floppy disks listed ?
        jz RxDOSBIOS_EvalHardware_08                    ; if no -->

        shr ax, 1                                       ; floppy bit
        shr ax, 1                                       ; math coprocessor
        shr ax, 1                                       ; memory (xt only)
        shr ax, 1                                       ;
        shr ax, 1                                       ; video mode bits
        shr ax, 1                                       ;
        and ax, 0003h
        inc ax
        jmp short RxDOSBIOS_EvalHardware_12

RxDOSBIOS_EvalHardware_08:
        mov ah, 8                                       ; read parameters 
        mov dl, 0                                       ; unit 0
        int 13h

        mov al, dl
        mov ah, 0                                       ; number of drives (if any )
        jnc RxDOSBIOS_EvalHardware_12                   ; if drives reported -->
        xor ax, ax

RxDOSBIOS_EvalHardware_12:
        mov word ptr [ _floppyDrives ], ax              ; number of floppy disks

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Determine number of hard disk drives
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor bx, bx
        mov ax, 40h
        mov es, ax
        mov al, byte ptr es:[ _BIOS_NumFixedDrives ][ bx ]
        mov word ptr [ _fixedDrives ], ax               ; number of fixed drives

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  determine (manufacture) floppy drives
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, word ptr [ _fixedDrives ]               ; get number of fixed drives
        or ax, ax                                       ; if no hard disk, total is fine
        jnz RxDOSBIOS_EvalHardware_20                   ; make sure we add 2 floppy drives -->

        cmp word ptr [ _floppyDrives ], 0000            ; number of floppy disks
        jz RxDOSBIOS_EvalHardware_22                    ; if diskless system -->

RxDOSBIOS_EvalHardware_20:
        mov word ptr [ _floppyDrives ], 2               ; if hard disk or one floppy

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Setup remainder of INIT Block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

RxDOSBIOS_EvalHardware_22:
        mov word ptr [ RxDOSBIOS_INITBLOCK. initRelocHighBegAddress. _segment ], cs
        mov word ptr [ RxDOSBIOS_INITBLOCK. initRelocHighBegAddress. _pointer ], offset RxDOSBIOS_Start
        mov word ptr [ RxDOSBIOS_INITBLOCK. initRelocHighSize                 ], RxDOSBIOS_LastAddress - RxDOSBIOS_Start
        mov word ptr [ RxDOSBIOS_INITBLOCK. initRelocHighNotify               ], -1

        mov word ptr [ RxDOSBIOS_INITBLOCK. initRelocLowBegAddress. _segment  ], 0000
        mov word ptr [ RxDOSBIOS_INITBLOCK. initRelocLowBegAddress. _pointer  ], 0000
        mov word ptr [ RxDOSBIOS_INITBLOCK. initRelocLowSize                  ], 0000
        mov word ptr [ RxDOSBIOS_INITBLOCK. initRelocHighNotify               ], -1

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  fix-up device driver chain
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, offset con

RxDOSBIOS_InitDeviceChain:
        mov word ptr _segment [ si ], cs

        mov si, word ptr _pointer [ si ]
        cmp si, -1
        jnz RxDOSBIOS_InitDeviceChain

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Set Interrupt Vectors
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor ax, ax
        mov es, ax

        mov dx, word ptr es:[ 0h * 4 ][ _segment ]      ; divide by zero int
        mov ax, word ptr es:[ 0h * 4 ][ _pointer ]
        mov word ptr cs:[ DivideByZero. _segment ], dx
        mov word ptr cs:[ DivideByZero. _pointer ], ax 

        mov word ptr es:[ 0h * 4 ][ _segment ], cs
        mov word ptr es:[ 0h * 4 ][ _pointer ], offset Interrupt_0

        mov word ptr es:[ _segment ][ 29h * 4 ], cs
        mov word ptr es:[ _pointer ][ 29h * 4 ], offset Interrupt_29

        mov cx, 6
        mov bx, 0001 * 4                                ; int 01h - int 07h
        call InitInterruptVectors

        mov bx, 33h * 4                                 ; int 33h
        mov word ptr es:[ _segment ][ bx ], cs
        mov word ptr es:[ _pointer ][ bx ], offset IntReturn

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Initialize Disk Parameter Tables
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setDS cs
        mov cx, word ptr [ _fixedDrives  ]              ; number of fixed drives
        mov dx, word ptr [ _floppyDrives ]              ; number of floppy disks
        call initDiskParameterBlocks                    ; returns actual # devices
        mov word ptr [ RxDOSBIOS_INITBLOCK. initTotalDrives ], cx
        mov byte ptr [ block. devUnits ], cl            ; block device header

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Load remainder of RxDOS                                      ;
        ;...............................................................;

        mov ax, sizeDISKBLOCK                           ; number of block devices
        mul cx                                          ; size required by table

        add ax, offset RxDOSBIOS_LastAddress + (sizeParagraph - 1)
        shr ax, 1
        shr ax, 1
        shr ax, 1
        shr ax, 1                                       ; paragraphs size
        
        mov dx, cs                                      ; current segment
        add dx, ax
        mov es, dx                                      ; seg address to load next module.
        mov word ptr [ RxDOSBIOS_INITBLOCK. initLowMemSegment ], es
        mov word ptr [ RxDOS_LoadSegment. _segment ], es

        xor di, di
        mov dx, word ptr [ RXDOS_ClusterAddr ]
        call _LoadProgram                               ; Load RxDOS
        ifc RxDOSBIOS_Error

        add cx, (PARAGRAPH-1)                           ; (rounded up)
        shr cx, 1
        shr cx, 1
        shr cx, 1
        shr cx, 1                                       ; paragraphs read
        add word ptr [ RxDOSBIOS_INITBLOCK. initLowMemSegment ], cx

        push cs
        pop es
        mov dl, byte ptr BootDrive                      ; physical drive
        mov di, offset RxDOSBIOS_INITBLOCK
        JMP dword ptr cs:[ RxDOS_LoadSegment ]

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Strategy Argument                                            ;
        ;...............................................................;

Strategy                dd 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Con Definition                                               ;
        ;...............................................................;

con                     dd clock
                         dw ( DEV_CHAR + DEV_STDINPUT + DEV_STDOUTPUT + DEV_FASTCHARIO )
                          dw _DevStrategy
                           dw con_interrupt
                            db 'CON     '
                             dw con_servicetable, 0

con_servicetable        db (maxcon - con_servicetable - 1) / 2
                        dw _NoAction                    ; = 0, Init
                        dw _NoAction                    ; = 1, Media Check
                        dw _NoAction                    ; = 2, Build BPB
                        dw _NoAction                    ; = 3, IOCTL Read
                        dw ConRead                      ; = 4, Read
                        dw ConNonDestrRead              ; = 5, Nondestructive Read
                        dw ConStatus                    ; = 6, Input Status
                        dw ConInputFlush                ; = 7, Input Flush
                        dw ConWrite                     ; = 8, Write
                        dw ConWrite                     ; = 9, Write With Verify
                        dw _NoAction                    ; =10, Output Status
                    maxcon = $

Con_LookAhead           dw 0
Con_ScanCode            db 0
Con_TabPosition         dw 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Clock Device Definitions                                     ;
        ;...............................................................;

clock                   dd prn
                         dw ( DEV_CHAR + DEV_CLOCK )
                          dw _DevStrategy
                           dw clock_interrupt
                            db 'CLOCK$  '
                             dw clock_servicetable, 0

clock_servicetable      db (maxclock - clock_servicetable - 1) / 2
                        dw _NoAction                    ; = 0, Init
                        dw _NoAction                    ; = 1, Media Check
                        dw _NoAction                    ; = 2, Build BPB
                        dw _IllegalFunction             ; = 3, IOCTL Read
                        dw ClockRead                    ; = 4, Read
                        dw ClockRead                    ; = 5, Nondestructive Read
                        dw _NoAction                    ; = 6, Input Status
                        dw _NoAction                    ; = 7, Input Flush
                        dw ClockWrite                   ; = 8, Write
                        dw ClockWrite                   ; = 9, Write With Verify
                        dw _NoAction                    ; =10, Output Status
                    maxclock = $

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Print, Lpt1, ... Definitions                                 ;
        ;...............................................................;

prn                     dd lpt1
                         dw ( DEV_CHAR + DEV_OUTPUTTILLBUSY )
                          dw _DevStrategy
                           dw prn_interrupt
                            db 'PRN     '
                             dw prn_servicetable, 0

lpt1                    dd lpt2
                         dw ( DEV_CHAR + DEV_OUTPUTTILLBUSY )
                          dw _DevStrategy
                           dw lpt1_interrupt
                            db 'LPT1    '
                             dw prn_servicetable, 0

lpt2                    dd lpt3
                         dw ( DEV_CHAR + DEV_OUTPUTTILLBUSY )
                          dw _DevStrategy
                           dw lpt2_interrupt
                            db 'LPT2    '
                             dw prn_servicetable, 2

lpt3                    dd aux
                         dw ( DEV_CHAR + DEV_OUTPUTTILLBUSY )
                          dw _DevStrategy
                           dw lpt3_interrupt
                            db 'LPT3    '
                             dw prn_servicetable, 3

prn_servicetable        db (maxprn - prn_servicetable - 1) / 2
                        dw _NoAction                    ; = 0, Init
                        dw _NoAction                    ; = 1, Media Check
                        dw _NoAction                    ; = 2, Build BPB
                        dw _NoAction                    ; = 3, IOCTL Read
                        dw _NoAction                    ; = 4, Read
                        dw _NoAction                    ; = 5, Nondestructive Read
                        dw _NoAction                    ; = 6, Input Status
                        dw _NoAction                    ; = 7, Input Flush
                        dw PrnWrite                     ; = 8, Write
                        dw PrnWrite                     ; = 9, Write With Verify
                        dw PrnStatus                    ; =10, Output Status
                        dw _NoAction                    ; =11, Output Flush
                        dw _NoAction                    ; =12, IOCTL Write
                        dw _NoAction                    ; =13, Open Device
                        dw _NoAction                    ; =14, Close Device
                        dw _NoAction                    ; =15, Removable Media
                        dw _NoAction                    ; =16, Output Until Busy
                        dw _NoAction                    ; =17, unused
                        dw _NoAction                    ; =18, unused
                        dw _NoAction                    ; =19, Generic IOCTL
                        dw _NoAction                    ; =20, unused
                        dw _NoAction                    ; =21, unused
                        dw _NoAction                    ; =22, unused
                        dw _NoAction                    ; =23, Get Logical Device
                        dw _NoAction                    ; =24, Set Logical Device
                        dw _NoAction                    ; =25, IOCTL query
                    maxprn = $

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Aux, Com1, ... Definitions                                   ;
        ;...............................................................;

aux                     dd com1
                         dw ( DEV_CHAR )
                          dw _DevStrategy
                           dw aux_interrupt
                            db 'AUX     '
                             dw aux_servicetable, 0

com1                    dd com2
                         dw ( DEV_CHAR )
                          dw _DevStrategy
                           dw com1_interrupt
                            db 'COM1    '
                             dw aux_servicetable, 0

com2                    dd block
                         dw ( DEV_CHAR )
                          dw _DevStrategy
                           dw com2_interrupt
                            db 'COM2    '
                             dw aux_servicetable, 1

aux_servicetable        db (maxaux - aux_servicetable - 1) / 2
                        dw _NoAction                    ; = 0, Init
                        dw _NoAction                    ; = 1, Media Check
                        dw _NoAction                    ; = 2, Build BPB
                        dw _NoAction                    ; = 3, IOCTL Read
                        dw AuxRead                      ; = 4, Read
                        dw AuxNonDestrRead              ; = 5, Nondestructive Read
                        dw _NoAction                    ; = 6, Input Status
                        dw AuxInputFlush                ; = 7, Input Flush
                        dw AuxWrite                     ; = 8, Write
                        dw AuxWrite                     ; = 9, Write With Verify
                        dw AuxOutputStatus              ; =10, Output Status
                    maxaux = $

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Comm LookAhead Buffer
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Comm_LookAhead          db 4 dup(0)                     ; com1, com2, com3, ...

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Block Device                                                 ;
        ;...............................................................;

block                   dw -1, 0
                         dw ( DEV_REMOVABLEMEDIA )
                          dw _DevStrategy
                           dw block_interrupt
                            db 03, 7 dup (0)            ; pre-initialized to three devs
                             dw block_servicetable, 0

block_servicetable      db (maxblock - block_servicetable - 1) / 2

                        dw _hdInit                      ; = 0, Init
                        dw _hdMediaCheck                ; = 1, Media Check
                        dw _hdBuildBPB                  ; = 2, Build BPB
                        dw _hdIOCTLRead                 ; = 3, IOCTL Read
                        dw _hdRead                      ; = 4, Read
                        dw _NoAction                    ; = 5, Nondestructive Read
                        dw _NoAction                    ; = 6, Input Status
                        dw _NoAction                    ; = 7, Input Flush
                        dw _hdWrite                     ; = 8, Write
                        dw _hdWriteWithVerify           ; = 9, Write With Verify
                        dw _NoAction                    ; =10, Output Status
                        dw _NoAction                    ; =11, Output Flush
                        dw _hdIOCTLWrite                ; =12, IOCTL Write
                        dw _hdOpenDevice                ; =13, Open Device
                        dw _hdCloseDevice               ; =14, Close Device
                        dw _hdRemovableMedia            ; =15, Removable Media
                        dw _NoAction                    ; =16, Output Until Busy
                        dw _NoAction                    ; =17, unused
                        dw _NoAction                    ; =18, unused
                        dw _hdGenericIOCTL              ; =19, Generic IOCTL
                        dw _NoAction                    ; =20, unused
                        dw _NoAction                    ; =21, unused
                        dw _NoAction                    ; =22, unused
                        dw _hdGetLogicalDevice          ; =23, Get Logical Device
                        dw _hdSetLogicalDevice          ; =24, Set Logical Device
                        dw _hdIOCTLQuery                ; =25, IOCTL query
                    maxblock = $

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Device Strategy                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  ES:BX     request header                                     ;
        ;...............................................................;

_DevStrategy     proc far

     ;  cli
        mov word ptr cs:[ Strategy. _segment ], es
        mov word ptr cs:[ Strategy. _pointer ], bx
        ret

_DevStrategy     endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Device Interrupt                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  ES:BX     request header                                     ;
        ;                                                               ;
        ;  on stack  pointer to strategy table                          ;
        ;  on stack  logical unit                                       ;
        ;                                                               ;
        ;  This appears to be a function, but is some common code that  ;
        ;  *most* but not necessarily all functions jump to. IT MUST    ;
        ;  MAKE A FAR RETURN ON EXIT.                                   ;
        ;                                                               ;
        ;...............................................................;

_DevInterrupt:

        Entry
        rArg _LogicalUnit, 4
        rArg _StrategyTable, 2                          ; offset to items in stack
        ddef _RequestBlock
        def _ServiceAddr
        
        cld
        SaveAllRegisters
        lds bx, dword ptr cs:[ Strategy ]               ; compliant strategy call
        stordarg _RequestBlock, ds, bx                  ; see text for compliance issues
        sti

        mov al, byte ptr [ rhFunction ][ bx ]
        cbw 
        add ax, ax
        inc ax                                          ; offset past table entry count
        mov si, word ptr [ _StrategyTable ][ bp ]
        add si, ax
        mov ax, word ptr cs:[ si ]
        mov word ptr [ _ServiceAddr ][ bp ], ax

        push bp                                         ; device or BIOS might change bp
        mov ah, byte ptr [ rhMedia      ][ bx ]
        mov al, byte ptr [ rhUnit       ][ bx ]
        mov cx, word ptr [ rwrBytesReq  ][ bx ]
        mov dx, word ptr [ _LogicalUnit ][ bp ]         ; passed to function
        mov word ptr [ rhStatus ][ bx ], 0000

     ;- - - - - - - - - - - - - - - - - - - - - - -
     ; es: di   pointer to buffer, if any
     ; ds: bx   pointer to packet
     ;- - - - - - - - - - - - - - - - - - - - - - -

        les di, dword ptr [ rwrBuffer   ][ bx ]
        call word ptr [ _ServiceAddr ][ bp ]
        pop bp                                          ; device or BIOS may have changed bp

        lds bx, dword ptr [ _RequestBlock ][ bp ]
        or word ptr [ rhStatus ][ bx ], (OP_DONE)

        RestoreAllRegisters                             ; restore all other registers
        mov sp, bp
        pop bp
        add sp, 4                                       ; sum of arg references
        retf

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  No Action                                                    ;
        ;...............................................................;

_NoAction:
        mov word ptr [ rwrStatus ][ bx ], ( OP_DONE )
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Illegal Function                                             ;
        ;...............................................................;

_IllegalFunction:
        mov word ptr [ rwrStatus ][ bx ], ( OP_EXITERROR + devErrUnknownCommand )
        mov word ptr [ rwrBytesReq ][ bx ], 0000
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Strategies                                                   ;
        ;...............................................................;

clock_interrupt:        push word ptr cs:[ CLOCK. devLogicalUnit ]
                        push word ptr cs:[ CLOCK. devTablePtr ]
                        jmp _DevInterrupt

prn_interrupt:          push word ptr cs:[ PRN. devLogicalUnit ]
                        push word ptr cs:[ PRN. devTablePtr ]
                        jmp _DevInterrupt

lpt1_interrupt:         push word ptr cs:[ LPT1. devLogicalUnit ]
                        push word ptr cs:[ LPT1. devTablePtr ]
                        jmp _DevInterrupt

lpt2_interrupt:         push word ptr cs:[ LPT2. devLogicalUnit ]
                        push word ptr cs:[ LPT2. devTablePtr ]
                        jmp _DevInterrupt

lpt3_interrupt:         push word ptr cs:[ LPT3. devLogicalUnit ]
                        push word ptr cs:[ LPT3. devTablePtr ]
                        jmp _DevInterrupt

aux_interrupt:          push word ptr cs:[ AUX. devLogicalUnit ]
                        push word ptr cs:[ AUX. devTablePtr ]
                        jmp _DevInterrupt

com1_interrupt:         push word ptr cs:[ COM1. devLogicalUnit ]
                        push word ptr cs:[ COM1. devTablePtr ]
                        jmp _DevInterrupt

com2_interrupt:         push word ptr cs:[ COM2. devLogicalUnit ]
                        push word ptr cs:[ COM2. devTablePtr ]
                        jmp _DevInterrupt

con_interrupt:          push word ptr cs:[ CON. devLogicalUnit ]
                        push word ptr cs:[ CON. devTablePtr ]
                        jmp _DevInterrupt

block_interrupt:        push word ptr cs:[ BLOCK. devLogicalUnit ]
                        push word ptr cs:[ BLOCK. devTablePtr ]
                        jmp _DevInterrupt

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Clock Read                                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  On Input:                                                    ;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to CLOCKDATA buffer                            ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;                                                               ;
        ;...............................................................;

ClockRead:

        Entry
        ddef  _packet, ds, bx
        ddef  _buffer
        ddef  _ticks
        def   _seconds
        def   _minutes
        def   _hours

        def   _century
        def   _year
        def   _month
        def   _day

        mov ax, word ptr [ rwrBuffer. _pointer ][ bx ]  ; packet
        mov dx, word ptr [ rwrBuffer. _segment ][ bx ]
        stordarg _buffer, dx, ax

        xor ax, ax                                      ; read real time clock 
        Exint 1Ah                                       ; cx/dx contains time of day
        or al, al                                       ; over 24 hours ?
        jz clockRead_12                                 ; no -->
        inc word ptr [ _day ][ bp ]                     ; increment day.

clockRead_12:
        stordarg _ticks, cx, dx                         ; save clock ticks

        mov ax, 0400h                                   ; see if extended BIOS support
        mov cx, -1                                      ; these will change if extended support
        mov dx, -1                                      ; ch = BCD century  / cl = BCD year
        Exint 1Ah                                       ; dh = BCD month    / dl = BCD day
        cmp cx, -1                                      ; was date returned ?
        jz clockRead_14                                 ; no, we'll skip save -->

        push dx                                         ; month/ day
        push cx                                         ; century/ year
        mov al, ch                                      ; century
        call ConvBCDToBin
        mov word ptr [ _century ][ bp ], ax

        pop ax                                          ; year
        call ConvBCDToBin
        mov word ptr [ _year ][ bp ], ax

        pop dx
        push dx
        mov al, dh                                      ; month
        call ConvBCDToBin
        mov word ptr [ _month ][ bp ], ax

        pop ax                                          ; day 
        call ConvBCDToBin
        mov word ptr [ _day ][ bp ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute days since 1980
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, 100
        mul word ptr [ _century ][ bp ]                 ; century
        add ax, word ptr [ _year ][ bp ]
        mov cx, ax                                      ; 1994, ... 2004, ...

        mov dh, byte ptr [ _month ][ bp ]
        mov dl, byte ptr [ _day ][ bp ]

        getdarg es, di, _buffer
        mov byte ptr es:[ cl_day    ][ di ], dl
        mov byte ptr es:[ cl_month  ][ di ], dh
        mov word ptr es:[ cl_year   ][ di ], cx

        call getDaysSince1980

        getdarg es, di, _buffer
        mov word ptr es:[ cl_daysSince1980 ][ di ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute time from ticks
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

clockRead_14:
        mov ax, 0200h                                   ; see if extended BIOS support
        mov cx, -1                                      ; these will change if extended support
        mov dx, -1                                      ; ch = BCD hours    / cl = BCD minutes
        Exint 1Ah                                       ; dh = BCD seconds  / dl = daylight savings time

        getdarg es, di, _buffer
        getdarg dx, ax, _ticks                          ; get clock ticks back
        mov cx, 10
        call _mul32                                     ; ticks times 10

        mov cx, 182                                     ; clock ticks per second
        call _div32

        push ax
        push dx
        xor dx, dx
        mov ax, 549
        call _mul32                                     ; each tick is .0549 of a second
        
        mov cx, 100
        call _div32                                     ; hundreds
        mov byte ptr es:[ cl_hseconds ][ di ], cl

        pop dx
        pop ax
        mov cx, 60                                      ; seconds per minute
        call _div32
        storarg _seconds, cx
        mov byte ptr es:[ cl_seconds  ][ di ], cl

        mov cx, 60                                      ; minutes per hour
        call _div32
        storarg _minutes, cx
        storarg _hours, ax

        mov byte ptr es:[ cl_minutes  ][ di ], cl
        mov byte ptr es:[ cl_hours    ][ di ], al

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Convert to BCD form
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        call convToBCD                                  ; hours (already in AX) to BCD
        mov ch, al                                      ; hours

        getarg ax, _minutes
        call convToBCD                                  ; minutes
        mov cl, al                                      ; minutes

        getarg ax, _seconds
        call convToBCD                                  ; seconds
        mov dh, al                                      ; seconds
        mov dl, 0                                       ; daylight savings time (don't know)

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Day of Week (not computed)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov word ptr [ rwrBytesReq ][ bx ], size CLOCKDATA
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Clock Write                                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  On Input:                                                    ;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to CLOCKDATA buffer                            ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;                                                               ;
        ;...............................................................;

ClockWrite:

        Entry
        ddef  _packet, ds, bx
        ddef  _buffer, es, di

        def   _yearBCD
        def   _monthBCD
        def   _dayBCD
        ddef  _ticks

        mov di, word ptr [ rwrBuffer. _pointer ][ bx ]  ; packet
        mov es, word ptr [ rwrBuffer. _segment ][ bx ]
        stordarg _buffer, es, di

        mov al, byte ptr es:[ cl_day       ][ di ]
        call convToBCD                                  ; day
        storarg _dayBCD, ax

        mov al, byte ptr es:[ cl_month     ][ di ]
        call convToBCD                                  ; month
        storarg _monthBCD, ax

        mov ax, word ptr es:[ cl_year      ][ di ]
        mov cx, 100
        xor dx, dx
        div cx

        push dx                                         ; save remainder
        call convToBCD                                  ; year
        storarg _yearBCD, ax

        pop ax
        call convToBCD                                  ; century
        mov cl, al
        mov ch, byte ptr [ _yearBCD   ][ bp ]

        mov dh, byte ptr [ _monthBCD  ][ bp ]
        mov dl, byte ptr [ _dayBCD    ][ bp ]
        mov ax, 0500h
        Exint 1Ah                                       ; set date

        getdarg es, di, _buffer
        mov al, byte ptr es:[ cl_hours     ][ di ]
        xor ah, ah
        mov cx, 60
        mul cx                                          ; express hours in minutes

        add al, byte ptr es:[ cl_minutes   ][ di ]
        adc ah, 0
        mul cx                                          ; express time in seconds

        add al, byte ptr es:[ cl_seconds   ][ di ]
        adc ah, 0
        mov cx, 182
        call _mul32                                     ; express time in hseconds

        add al, byte ptr es:[ cl_hseconds  ][ di ]
        adc ah, 0
        adc dx, 0

        mov cx, 10
        call _div32                                     ; express time in hseconds

        mov cx, dx
        mov dx, ax
        mov ax, 0100h
        Exint 1Ah                                       ; set date

        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Days Since 1980                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     year   ( 1980 ... )                                  ;
        ;   dh     month  ( 1 - 12)                                     ;
        ;   dl     day    ( 1 - 31)                                     ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     days since 1980 format                               ;
        ;...............................................................;

getDaysSince1980:

        Entry
        def _year, cx

        def _month, 0000
        def _day, 0000
        def _m14_12, 0000
        ddef _daysSince

        push bx
        push cx
        push dx

        mov byte ptr [ _month ][ bp ], dh
        mov byte ptr [ _day   ][ bp ], dl

        dec dh
        jnz getDaysSince1980_06
        mov word ptr [ _m14_12 ][ bp ], -1

getDaysSince1980_06:
        xor dx, dx
        mov ax, word ptr [ _m14_12 ][ bp ]
        mov cx, 12
        mul cx                                  ; *12
        inc ax
        inc ax                                  ; 2
        sub ax, word ptr [ _month ][ bp ]
        neg ax                                  ; - m_14
        mov cx, 367
        mul cx
        
        xor dx, dx
        mov cx, 12
        div cx
        mov word ptr [ _daysSince. _Low ][ bp ], ax
        mov word ptr [ _daysSince. _High ][ bp ], 0000

        mov ax, word ptr [ _m14_12 ][ bp ]
        add ax, 4900
        add ax, word ptr [ _year ][ bp ]

;;;       jdn = (long)(d - 32076)
;;;             + 1461L * (y + 4800L + (m - 14) / 12) / 4
;;;             + 367 * (m - 2 - (m - 14) / 12 * 12) / 12
;;;             - 3 * ((y + 4900L + (m - 14) / 12) / 100) / 4
;;;             + 1;            /* correction by rdg */

        xor dx, dx
        mov cx, 100
        div cx

        mov cx, 3
        mul cx

        shr ax, 1
        shr ax, 1                               ; / 4
        dec ax
        sub word ptr [ _daysSince. _Low  ][ bp ], ax
        sbb word ptr [ _daysSince. _High ][ bp ], 0000

        mov ax, word ptr [ _m14_12 ][ bp ]
        add ax, 4800
        add ax, word ptr [ _year ][ bp ]

        mov cx, 1461
        mul cx

    ; do a long shift here
        shr dx, 1
        rcr ax, 1
        shr dx, 1                               ; / 4
        rcr ax, 1
        add word ptr [ _daysSince. _Low  ][ bp ], ax
        adc word ptr [ _daysSince. _High ][ bp ], dx  

        xor dx, dx
        mov ax, word ptr [ _day ][ bp ]
        sub ax, 32076
        sbb dx, 0000
        add ax, word ptr [ _daysSince. _Low  ][ bp ]
        adc dx, word ptr [ _daysSince. _High ][ bp ]

        sub ax, 4BD0h
        sbb dx, 25h


 IFDEF OLDDATE_LOGIC

        sub cx, 1980
        storarg _year, cx

        mov ax, 365
        mul cx                                  ; rough estimate of days

        shr cx, 1
        shr cx, 1                               ; # leap years 
        inc cx                                  ; adj for 1980
        add ax, cx                              ; days since
        storarg _daysSince, ax                  ; days since as of last year

        xor ax, ax
        getarg dx, _monthnDays
        mov bl, dh
        and bx, 255
        jz getDaysSince1980_08

getDaysSince1980_06:
        add al, byte ptr cs:[ DaysInMonthTable ][ bx ]
        adc ah, 0
        dec bx
        jnz getDaysSince1980_06

getDaysSince1980_08:
        mov dh, 0                               ; days only in dx
        add ax, dx                              ; days this year
        cmp ax, 28                              ; between Jan 1 and Feb 28 ?
        jle getDaysSince1980_12                 ; yes -->

        test word ptr [ _year ][ bp ], 11b      ; this year a leap year ?
        jnz getDaysSince1980_12                 ; no -->
        inc ax                                  ; we get one more day
        add ax, word ptr [ _daysSince ][ bp ]
 ENDIF

getDaysSince1980_12:
        pop dx
        pop cx
        pop bx
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Code Segment Data                                            ;
        ;...............................................................;

DaysInMonthTable:  db 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Convert AL to BCD form                                       ;
        ;...............................................................;

convToBCD:

        mov ah, 0
        aam                                             ; divide by ten
        shl ah, 1
        shl ah, 1
        shl ah, 1
        shl ah, 1                                       ; shift four bits
        or al, ah                                       ; BCD form
        mov ah, 0
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Convert BCD to Binary                                        ;
        ;...............................................................;

ConvBCDToBin:

        mov ah, al
        and al, 0Fh
        shr ah, 1
        shr ah, 1
        shr ah, 1
        shr ah, 1

        mov ch, ah                                      ; 1
        add ah, ah                                      ; 2
        add ah, ah                                      ; 4
        add ah, ch                                      ; 5
        add ah, ah                                      ; 10
        add al, ah
        mov ah, 0
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  32 Bit Multiply                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   dx:ax  numerator                                            ;
        ;   cx     multiplier                                           ;
        ;                                                               ;
        ;...............................................................;

_mul32: or dx, dx                                       ; simple multiply ?
        jnz _mul32_12                                   ; not really -->

        mul cx
        ret

_mul32_12:
        push bx
        mov bx, ax
        mov ax, dx
        mul cx
        xchg ax, bx

        mul cx
        add dx, bx
        pop bx                                          ; restore bx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  32 Bit Divide                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   dx:ax  numerator                                            ;
        ;   cx     divisor                                              ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx:ax  answer                                               ;
        ;   cx     remainder                                            ;
        ;                                                               ;
        ;...............................................................;

_div32: or cx, cx                                       ; protect from zero divisor
        stc                                             ; in case of error
        jz _div32_return                                ; if so, just return with carry

        push bx
        mov bx, dx
        xchg ax, bx
        xor dx, dx
        div cx                                          ; divide high order first
      
        xchg ax, bx
        div cx                                          ; REMAINDER WILL BE IN DX
        mov cx, dx                                      ; save remainder
        mov dx, bx                                      ; full 32-bit answer
        pop bx

_div32_return:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Con Read (Fct 4)                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;                                                               ;
        ;  for block devices:                                           ;
        ;                                                               ;
        ;  ah     device media (old )                                   ;
        ;  al     block device unit (a: = 0, ... )                      ;
        ;...............................................................;

ConRead:
        or cx, cx                                       ; any input requested ?
        jz ConRead_22                                   ; no, failsafe exit -->

ConRead_12:
        call getConData                                 ; read from con device
        or al, al                                       ; is data an extended char ?
        jnz ConRead_16                                  ; no -->
        cmp cx, 1                                       ; one character read ?
        jnz ConRead_12                                  ; we'll ignore -->
        stosw                                           ; else save entire 2 bytes
        xor cx, cx                                      ; all read
        jmp short ConRead_22                            ; then return -->

ConRead_16:
        stosb                                           ; fill data in buffer
        loop ConRead_12                                 ; if more data -->

ConRead_22:
        sub word ptr [ rwrBytesReq ][ bx ], cx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Con NonDestructive Read (Fct 5)                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;...............................................................;

ConNonDestrRead:
        mov ah, BIOS_ConStatus
        Int 16h                                         ; see if characters waiting ?
        jz ConNonDestrRead_NoData                       ; none waiting -->

        mov byte ptr [ ndrCharRead ][ bx ], al          ; return lookahead character.
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  If no data
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ConNonDestrRead_NoData:
        mov word ptr [ ndrStatus   ][ bx ], (OP_NODATA)
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Con Status (Fct 6)                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;                                                               ;
        ;  for block devices:                                           ;
        ;                                                               ;
        ;  ah     device media (old )                                   ;
        ;  al     block device unit (a: = 0, ... )                      ;
        ;...............................................................;

ConStatus:
        cmp word ptr cs:[ Con_LookAhead ], 0000         ; character in pending area ?
        jnz ConStatus_16                                ; yes -->

        mov ah, BIOS_ConStatus
        Int 16h                                         ; see if characters waiting ?
        jz ConStatus_NoData                             ; none waiting -->

ConStatus_16:
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  If no data
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ConStatus_NoData:
        mov word ptr [ rhStatus   ][ bx ], (OP_NODATA)
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Con Input Flush (Fct 7)                                      ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;...............................................................;

ConInputFlush:
        mov word ptr cs:[ Con_LookAhead ], 0000         ; flush input character

ConInputFlush_08:
        mov ah, BIOS_ConStatus
        Int 16h                                         ; see if characters waiting ?
        jz ConInputFlush_NoData                         ; no more -->

        mov ah, BIOS_ConRead
        Int 16h                                         ; read waiting character
        jmp ConInputFlush_08                            ; test for more -->

ConInputFlush_NoData:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Con Write (Fcts 8, 9)                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;...............................................................;

ConWrite:
        or cx, cx
        jz ConWrite_38

ConWrite_08:
        mov al, byte ptr es:[ di ]                      ; get character
        inc di

    ; check for raw versus cooked mode

        cmp al, ControlM                                ; carriage return ?
        jnz ConWrite_12                                 ; no -->
        mov word ptr cs:[ Con_TabPosition ], 0000       ; set tab position
        jmp short ConWrite_28

ConWrite_12:
        cmp al, ControlI                                ; tab character ?
        jnz ConWrite_22                                 ; no, display as is -->

ConWrite_14:
        mov al, ' '                                     ; display a space
        Int 29h                                         ; internal Video Driver

        inc byte ptr cs:[ Con_TabPosition ]             ; tab position
        test byte ptr cs:[ Con_TabPosition ], 7         ; tab position
        jnz ConWrite_14
        jmp short ConWrite_32

ConWrite_22:
        cmp al, ControlJ                                ; line feed ?
        jz ConWrite_28                                  ; yes -->
        inc byte ptr cs:[ Con_TabPosition ]             ; tab position

ConWrite_28:
        Int 29h                                         ; internal Video Driver

ConWrite_32:
        loop ConWrite_08                                ; go get additional characters -->

ConWrite_38:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Con Character                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  ds:bx  points to request header                              ;
        ;  dl     logical device                                        ;
        ;...............................................................;

getConData:
        xor ax, ax
        xchg ax, word ptr cs:[ Con_LookAhead ]          ; get char waiting
        or ax, ax                                       ; any character waiting ?
        jnz getConData_08                               ; no -->

        mov ah, BIOS_ConRead
        Int 16h                                         ; wait for character
        or ax, ax                                       ; anything returned ?
        jz getConData                                   ; continue looping -->

getConData_08:
        mov byte ptr cs:[ Con_ScanCode ], ah            ; save scan code
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Prn Output Characters (Fct 8, 9)                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;...............................................................;

PrnWrite:
        or cx, cx
        jz PrnWrite_16

PrnWrite_08:
        mov bx, Prn_RetryCount

PrnWrite_10:
        mov ah, BIOS_PrnWrite
        mov al, byte ptr es:[ di ]
        inc di
        Int 17h

        mov al, devErrDeviceNotReady
        test ax, Prn_IOError                            ; i/o error ?
        jnz PrnWrite_Retry                              ; yes -->

        test ax, Prn_TimeOut                            ; operation timed out ?
        jz PrnWrite_14                                  ; no -->

PrnWrite_Retry:
        dec di
        dec bx
        jnz PrnWrite_10
        jmp short PrnWrite_WriteFault

PrnWrite_14:
        loop PrnWrite_08

PrnWrite_16:
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  If no data
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PrnWrite_WriteFault:
        test ax, Prn_OutOfPaper
        jz PrnWrite_WriteFault_08
        mov al, devErrPrinterOutPaper

PrnWrite_WriteFault_08:
        or ax, OP_EXITERROR
        mov word ptr [ rwrStatus   ][ bx ], ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Prn Output Status (Fct 10)                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;...............................................................;

PrnStatus:
        mov ah, BIOS_PrnStatus
        Int 17h

    ;** this is wrong

        mov al, devErrDeviceNotReady
        test ax, Prn_OutOfPaper                         ; out of paper ?
        jz PrnStatus_08                                 ; no -->
        mov al, devErrPrinterOutPaper

PrnStatus_08:
        xor ah, ah
        or ax, OP_EXITERROR
        mov word ptr [ rwrStatus   ][ bx ], ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Com Read (Fct 4)                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;                                                               ;
        ;  for block devices:                                           ;
        ;                                                               ;
        ;  ah     device media (old )                                   ;
        ;  al     block device unit (a: = 0, ... )                      ;
        ;...............................................................;

AuxRead:
        or cx, cx
        jz AuxRead_16

AuxRead_12:
        call getComData                                 ; read from comm device
        test ax, (CommLS_FE + CommLS_PE + CommLS_OE)
        jnz AuxRead_Error                               ; if errors (no data or timeout) -->
        stosb                                           ; fill data in buffer
        loop AuxRead_12                                 ; if more data -->

AuxRead_16:
        mov word ptr [ rwrBytesReq ][ bx ], 0000
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AuxRead_Error:
        sub word ptr [ rwrBytesReq ][ bx ], cx
        mov word ptr [ rwrStatus   ][ bx ], (devErrReadFault + OP_EXITERROR)
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Com NonDestructive Read (Fct 5)                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;...............................................................;

AuxNonDestrRead:
        call getComStatus                               ; read from comm device
        test ax, CommLS_DataAvail                       ; data available ?
        jz AuxNonDestrRead_NoData                       ; if no data -->
        test ax, CommMS_DSR                             ; DSR ?
        jz AuxNonDestrRead_NoData                       ; if no dsr -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  If Data Available, read data (lookahead)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call getComData
        jnz AuxRead_Error                               ; if no data or timeout -->

        mov si, dx
        add si, offset Comm_LookAhead                   ; 
        mov byte ptr cs:[ si ], al                      ; save waiting data.

        mov byte ptr [ ndrCharRead ][ bx ], al          ; return lookahead character.
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  If no data
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AuxNonDestrRead_NoData:
        mov word ptr [ ndrStatus   ][ bx ], (OP_NODATA)
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Aux Input Flush (Fct 7)                                      ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;...............................................................;

AuxInputFlush:
        mov si, dx
        add si, offset Comm_LookAhead                   ; 
        mov byte ptr cs:[ si ], 00                      ; flush input character
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Aux Output Characters (Fct 8, 9)                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;...............................................................;

AuxWrite:
        or cx, cx
        jz AuxWrite_16

AuxWrite_12:
        mov ah, BIOS_ComWrite
        mov al, byte ptr es:[ di ]
        inc di
        Int 14h
        test ax, CommLS_TimeOut
        jnz AuxWrite_WriteFault

        loop AuxWrite_12

AuxWrite_16:
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  If no data
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AuxWrite_WriteFault:
        mov word ptr [ rwrStatus   ][ bx ], (devErrWriteFault + OP_EXITERROR)
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Aux Output Status (Fct 10)                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:bx  points to request header                              ;
        ;  es:di  points to request buffer                              ;
        ;  cx     characters to transfer                                ;
        ;  dl     logical device                                        ;
        ;...............................................................;

AuxOutputStatus:
        mov ah, BIOS_ComStatus
        Int 14h
        test ax, CommMS_DSR
        jz AuxOutputStatus_08
        test ax, CommLS_HoldRegEmpty
        jnz AuxOutputStatus_12

AuxOutputStatus_08:
        mov word ptr [ rwrStatus   ][ bx ], OP_NODATA        

AuxOutputStatus_12:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Com Character                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  ds:bx  points to request header                              ;
        ;  dl     logical device                                        ;
        ;...............................................................;

getComData:
        mov si, dx
        add si, offset Comm_LookAhead                   ; 
        cmp byte ptr cs:[ si ], 00                      ; data waiting ?
        jz getComData_08                                ; no -->

        xor ax, ax
        xchg al, byte ptr cs:[ si ]                     ; get char waiting
        ret

getComData_08:
        mov ah, BIOS_ComRead
        Int 14h
        test ax, (CommLS_FE + CommLS_PE + CommLS_OE)    ; errors ??
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Com Status                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  ds:bx  points to request header                              ;
        ;  dl     logical device                                        ;
        ;...............................................................;

getComStatus:
        mov si, dx
        add si, offset Comm_LookAhead                   ; 
        cmp byte ptr cs:[ si ], 00                      ; data waiting ?
        jz getComStatus_08                              ; no -->

        mov ax, CommLS_DataAvail
        mov al, byte ptr cs:[ si ]                      ; get char waiting
        ret

getComStatus_08:
        mov ah, BIOS_ComStatus
        Int 14h
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 29                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al     character to display                                  ;
        ;...............................................................;

Interrupt_29    proc far
        sti
        saveAllRegisters

        mov ah, 0Eh                                     ; write teletype
        mov bx, 0007h                                   ; default page 0, color 07
        Int 10h

        restoreAllRegisters
        iret

Interrupt_29    endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Divide by Zero                                               ;
        ;...............................................................;

Interrupt_0     proc far

        jmp dword ptr cs:[ DivideByZero ]

Interrupt_0     endp

DivideByZero:   dd 0                                    ; BIOS value

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  General Interrupt Trap                                       ;
        ;...............................................................;

IntReturn       proc far
        iret

IntReturn       endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Block Device Functions                                       ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Block Device Init                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  DS:BX    request header                                      ;
        ;  ES:DI    buffer                                              ;
        ;  CX       characters to transfer                              ;
        ;...............................................................;

_hdInit:
        Entry
        ddef  _packet, ds, bx

        mov al, byte ptr cs:[ block. devUnits ]
        mov byte ptr [ irUnits ][ bx ], al              ; set max units available

        mov word ptr [ irEndAddress. _pointer ][ bx ], offset RxDOSBIOS_LastAddress
        mov word ptr [ irEndAddress. _segment ][ bx ], cs

        mov word ptr [ irParamAddress. _pointer ][ bx ], NULL
        mov word ptr [ irParamAddress. _segment ][ bx ], NULL

     ; BPB address

        mov al, byte ptr [ irUnit ][ bx ]               ; find Disk block
        call BuildDiskParameterBlock                    ; es: di

        push word ptr [ _packet. _segment ][ bp ]
        push word ptr [ _packet. _pointer ][ bp ]
        call SetInvalidDriveError                       ; if error, set error code -->
        jc _hdInit_12                                   ; then exit 

        getdarg ds, bx, _packet
        lea di, offset _dskBPB [ di ]
        mov word ptr [ irParamAddress. _pointer ][ bx ], di
        mov word ptr [ irParamAddress. _segment ][ bx ], es

        call stampTimerCount

_hdInit_12:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Block Device Media Descriptor                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  DS:BX    request header                                      ;
        ;  ES:DI    buffer                                              ;
        ;  CX       characters to transfer                              ;
        ;...............................................................;

_hdMediaCheck:
        Entry
        ddef  _packet, ds, bx

        mov al, byte ptr [ mrUnit ][ bx ]               ; get unit requested
        call BuildDiskParameterBlock                    ; es: di

        push word ptr [ _packet. _segment ][ bp ]
        push word ptr [ _packet. _pointer ][ bp ]
        call SetInvalidDriveError                       ; if error, set error code -->
        jc _hdMediaCheck_12

        getdarg ds, bx, _packet
        mov cl, byte ptr es:[ _dskBPB. _bpbMediaDescriptor ][ di ]
        mov byte ptr [ mrMediaID ][ bx ], cl            ; media ID

        mov dx, word ptr es:[ _dskSerialNumber. _High ][ di ]
        mov ax, word ptr es:[ _dskSerialNumber. _Low  ][ di ]
        mov word ptr [ mrVolumeID. _High ][ bx ], dx
        mov word ptr [ mrVolumeID. _Low  ][ bx ], ax

        mov byte ptr [ mrReturn  ][ bx ], MEDIA_UNCHANGED
        test word ptr es:[ _dskStatusFlag ][ di ], DiskChangeDetected
        jz _hdMediaCheck_12                             ; not changed -->
        mov byte ptr [ mrReturn  ][ bx ], MEDIA_HASCHANGED

_hdMediaCheck_12:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Block Device Build BPB                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  DS:BX    request header                                      ;
        ;  ES:DI    buffer                                              ;
        ;  CX       characters to transfer                              ;
        ;...............................................................;

_hdBuildBPB:
        Entry
        ddef  _packet, ds, bx
        mov word ptr [ bbrBPBAddress. _pointer ][ bx ], NULL
        mov word ptr [ bbrBPBAddress. _segment ][ bx ], NULL

        mov al, byte ptr [ bbrUnit ][ bx ]              ; get unit requested
        call BuildDiskParameterBlock                    ; es: di

        push word ptr [ _packet. _segment ][ bp ]
        push word ptr [ _packet. _pointer ][ bp ]
        call SetInvalidDriveError                       ; if error, set error code -->
        jc _hdBuildBPB_12

        mov al, cl                                      ; Media Descriptor
        lea di, offset _dskBPB [ di ]                   ;

        getdarg ds, bx, _packet
        mov byte ptr [ bbrMediaID ][ bx ], al           ; media ID
        mov word ptr [ bbrBPBAddress. _pointer ][ bx ], di
        mov word ptr [ bbrBPBAddress. _segment ][ bx ], es

        call stampTimerCount

_hdBuildBPB_12:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Block Device Build BPB                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  DS:BX    request header                                      ;
        ;  ES:DI    buffer                                              ;
        ;  CX       sectors to transfer                                 ;
        ;...............................................................;

_hdRead:
        Entry
        ddef  _packet, ds, bx
        ddef  _buffer, es, di
        ddef  _diskparameters
        ddef  _logSector
        def   _numSectorsRemain, cx
        def   _numSectorsRead, cx
        def   _numSectorsTotal, 0000

        xor dx, dx
        mov ax, word ptr [ rwrStartSec ][ bx ]          ; get start sector address
        cmp ax, -1                                      ; use huge instead ?
        jnz _hdRead_08                                  ; if not -->

        mov ax, word ptr [ rwrHugeStartSec. _low  ][ bx ]   ; get huge sector
        mov dx, word ptr [ rwrHugeStartSec. _high ][ bx ]

_hdRead_08:
        stordarg _logSector, dx, ax

        mov al, byte ptr [ rwrUnit ][ bx ]              ; get unit requested
        call FindDiskParameterBlock                     ; make sure disk is fine
        stordarg _diskparameters, es, di

        push word ptr [ _packet. _segment ][ bp ]
        push word ptr [ _packet. _pointer ][ bp ]
        call SetInvalidDriveError                       ; if error, set error code -->
        jc _hdRead_20

        cmp word ptr [ _numSectorsRemain ][ bp ], 0000  ; read no sectors ?
        jz _hdRead_20                                   ; just a read test -->

_hdRead_12:
        getdarg es, di, _diskparameters
        getdarg dx, ax, _logSector
        call ConvertLogSectorToPhysical

        mov ax, word ptr es:[ _dskBPB. _bpbSectorsPerTrack ][ di ]
        mov bx, cx                                      ; sector
        and bx, 003Fh                                   ; drop high order cyl portion
        sub ax, bx                                      ; sectors remaining in track
        inc ax                                          ; real number is inclusive
        cmp ax, word ptr [ _numSectorsRemain ][ bp ]    ; more than sectors requested ?
        jc _hdRead_16                                   ; if carry, only allow max -->
        mov ax, word ptr [ _numSectorsRemain ][ bp ]    ; more than sectors requested ?

_hdRead_16:
        mov word ptr [ _numSectorsRead ][ bp ], ax      ; temporary
        mov ah, 02h                                     ; read function
        getdarg es, bx, _buffer
        call DiskFunctionsWithRetry

;;        pushf
;;        mov word ptr [ _numSectorsRead ][ bp ], cx
;;        add word ptr [ _numSectorsTotal ][ bp ], cx
;;        popf

        push word ptr [ _packet. _segment ][ bp ]
        push word ptr [ _packet. _pointer ][ bp ]
        call setPacketReqError                          ; if error 
        jc _hdRead_20

        mov ax, word ptr [ _numSectorsRead ][ bp ]
        add word ptr [ _numSectorsTotal ][ bp ], ax     ; total 
        add word ptr [ _logSector. _low  ][ bp ], ax
        adc word ptr [ _logSector. _high ][ bp ], 0000

        xchg ah, al
        shl ah, 1
        add word ptr [ _buffer. _pointer ][ bp ], ax

        mov ax, word ptr [ _numSectorsRead ][ bp ]
        sub word ptr [ _numSectorsRemain ][ bp ], ax    ; compute how many more to go 
        jg _hdRead_12                                   ; if more to go -->

        clc

_hdRead_20:
        pushf
        getdarg es, di, _diskparameters
        call stampTimerCount

        getdarg ds, bx, _packet
        mov ax, word ptr [ _numSectorsTotal ][ bp ]
        mov word ptr [ rwrBytesReq ][ bx ], ax          ; sectors read
        popf
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Block Device Build BPB                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  DS:BX    request header                                      ;
        ;  ES:DI    buffer                                              ;
        ;  CX       characters to transfer                              ;
        ;...............................................................;

_hdWrite:
_hdWriteWithVerify:
        Entry
        ddef  _packet, ds, bx
        ddef  _buffer, es, di
        ddef  _diskparameters
        ddef  _logSector
        def   _numSectors, cx
        def   _numSectorsTotal, cx
        def   _Verify, ax

        xor dx, dx
        mov ax, word ptr [ rwrStartSec ][ bx ]          ; get start sector address
        cmp ax, -1                                      ; use huge instead ?
        jnz _hdWrite_08                                 ; if not -->

        mov ax, word ptr [ rwrHugeStartSec. _low  ][ bx ]   ; get huge sector
        mov dx, word ptr [ rwrHugeStartSec. _high ][ bx ]

_hdWrite_08:
        stordarg _logSector, dx, ax

        getdarg ds, bx, _packet
        cmp word ptr [ _numSectors ][ bp ], 0000        ; sectors == zero ?
        ifz _hdWrite_20                                 ; if none -->

        mov al, byte ptr [ rwrUnit ][ bx ]              ; get unit requested
        call FindDiskParameterBlock                     ; make sure disk is fine
        stordarg _diskparameters, es, di

        push word ptr [ _packet. _segment ][ bp ]
        push word ptr [ _packet. _pointer ][ bp ]
        call SetInvalidDriveError                       ; if error, set error code -->
        jc _hdWrite_20                                  ; exit -->

_hdWrite_12:
        getdarg es, di, _diskparameters
        getdarg dx, ax, _logSector
        call ConvertLogSectorToPhysical

        getdarg es, bx, _buffer
        NormalizeBuffer es, bx
        mov ax, 0301h                                   ; write sector.
        call DiskFunctionsWithRetry

        push word ptr [ _packet. _segment ][ bp ]
        push word ptr [ _packet. _pointer ][ bp ]
        call setPacketReqError                          ; if error 
        jc _hdWrite_20

        getdarg ds, bx, _packet
        cmp byte ptr [ rhFunction ][ bx ], DEVICEWRITEVERIFY
        jnz _hdWrite_16                                 ; if not verify -->

        getdarg es, bx, _buffer
        NormalizeBuffer es, bx
        mov ax, 0401h                                   ; verify sector.
        call DiskFunctionsWithRetry
        
        push word ptr [ _packet. _segment ][ bp ]
        push word ptr [ _packet. _pointer ][ bp ]
        call setPacketReqError                          ; if error 
        jc _hdWrite_20
        
_hdWrite_16:
        add word ptr [ _buffer. _pointer ][ bp ], sizeSector
        add word ptr [ _logSector. _low  ][ bp ], 0001
        adc word ptr [ _logSector. _high ][ bp ], 0000
        dec word ptr [ _numSectors ][ bp ]
        jnz _hdWrite_12

        clc

_hdWrite_20:
        pushf
        getdarg es, di, _diskparameters
        call stampTimerCount

        getdarg ds, bx, _packet
        mov ax, word ptr [ _numSectorsTotal ][ bp ]
        sub ax, word ptr [ _numSectors      ][ bp ]
        mov word ptr [ rwrBytesReq ][ bx ], ax          ; sectors read
        popf
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Hard Disk Functions                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  ES:BX     request header                                     ;
        ;...............................................................;

_hdIOCTLRead:
_hdIOCTLWrite:
_hdOpenDevice:
_hdCloseDevice:
_hdRemovableMedia:
_hdGenericIOCTL:
_hdGetLogicalDevice:
_hdSetLogicalDevice:
_hdIOCTLQuery:
                jmp _NoAction

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Convert Log Sector Address To Physical                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  ES:DI     disk parameter block address                       ;
        ;  AX:DX     logical sector address                             ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  ES:DI     disk parameter block address                       ;
        ;  CX        CL = sector/ CH = track                            ;
        ;  DX        DL = unit/ DH = head                               ;
        ;...............................................................;

ConvertLogSectorToPhysical:

        add ax, word ptr es:[ _dskBPB. _bpbHiddenSectors. _low  ][ di ]
        adc dx, word ptr es:[ _dskBPB. _bpbHiddenSectors. _high ][ di ]

        mov cx, word ptr es:[ _dskBPB. _bpbSectorsPerTrack ][ di ]
        call _div32

        inc cx
        push cx                                 ; sector
        mov cx, word ptr es:[ _dskBPB. _bpbHeads ][ di ]
        call _div32

    ; track is at ax
    ; head is at cx

        mov dh, cl                                        ; head
        mov dl, byte ptr es:[ _dskPhysDriveNumber ][ di ] ; unit

        clc
        shl ah, 1
        shl ah, 1
        shl ah, 1
        shl ah, 1
        shl ah, 1
        shl ah, 1                                       ; move high order of cyl up 

        pop cx
        or cl, ah                                       ; top two bits of cyl or'd into phys sector
        mov ch, al                                      ; rest of cyl 
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Rebuild BIOS Parameter Block (if disk changed)               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  ES:DI     Disk Parameter Block Address (unitialized)         ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  ES:DI     Disk Parameter Block Address, if carry not set.    ;
        ;  ZR        if diskette not changed                            ;
        ;  NZ        if diskette changed                                ;
        ;...............................................................;

_ChangedLineSet:

        push es
        push di
        and word ptr es:[ _dskStatusFlag ][ di ], NOT DiskChangeDetected
        cmp byte ptr es:[ _dskDeviceType  ][ di ], -1   ; device entry not initialized ?
        jz _ChangedLineSet_ChangeLineSet                ; set as changed -->
        test word ptr es:[ _dskStatusFlag ][ di ], IsNonRemovable
        jnz _ChangedLineSet_notChanged

        call GetTimerCount                              ; timer count into dx:ax
        sub ax, word ptr es:[ _dskClockAtLastOp. _low ][ di ]
        sbb dx, word ptr es:[ _dskClockAtLastOp. _high ][ di ]
        cmp ax, 18 * 2                                  ; within 2 seconds ?
        jc _ChangedLineSet_notChanged                   ; line not changed -->

        mov ah, 16h
        mov dl, byte ptr es:[ _dskPhysDriveNumber ][ di ]
        call DiskFunctions                              ; see if change line set
      ; jc _ChangedLineSet_ChangeLineSet                ; assume changed -->
        cmp ah, 06h                                     ; diskette change line set ?
        jnz _ChangedLineSet_notChanged

_ChangedLineSet_ChangeLineSet:
        or word ptr es:[ _dskStatusFlag ][ di ], DiskChangeDetected

_ChangedLineSet_notChanged:
        pop di
        pop es
        test word ptr es:[ _dskStatusFlag ][ di ], DiskChangeDetected
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get BIOS Timer Count                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  dx:ax     BIOS Timer Value                                   ;
        ;...............................................................;

GetTimerCount:

        push es
        push di
        xor ax, ax
        mov es, ax
    ;   mov di, offset 46Ch
        mov ax, word ptr es:[ 46Ch ]
        mov dx, word ptr es:[ 46Eh ]

        pop di
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get BIOS Timer Count                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  dx:ax     BIOS Timer Value                                   ;
        ;...............................................................;

stampTimerCount:

        push ax
        push dx
        push es
        push di

        xor ax, ax
        mov es, ax
    ;   mov di, offset 46Ch
        mov ax, word ptr es:[ 46Ch ]
        mov dx, word ptr es:[ 46Eh ]

        pop di
        pop es
        mov word ptr es:[ _dskClockAtLastOp. _low ][ di ], ax
        mov word ptr es:[ _dskClockAtLastOp. _high ][ di ], dx

        pop dx
        pop ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Rebuild BIOS Parameter Block (if disk changed)               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  ES:DI     Disk Parameter Block Address, if carry not set.    ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  ES:DI     Disk Parameter Block Address, if carry not set.    ;
        ;  CY        if error.                                          ;
        ;...............................................................;

RebuildBPB:

        Entry
        ddef _dskParameterTable, es, di

        push ds
        setDS cs

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Rebuild BIOS Parameter Block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_RebuildBPB_12:
        mov dl, byte ptr es:[ _dskPhysDriveNumber       ][ di ]
        mov dh, byte ptr es:[ _dskPartitionBeginHead    ][ di ]
        mov cx, word ptr es:[ _dskPartitionBeginSector  ][ di ]
        mov ax, 0201h

        setES cs
        mov bx, offset RxDOSBIOS_DiskBuffer
        call DiskFunctionsWithRetry
        jc _RebuildBPB_38

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Validate Boot Sector
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

     ;  cmp byte ptr [ RxDOSBIOS_DiskBuffer + _bsJump ], 0EBh   ; must be jump
     ;  jnz ...

        getdarg es, di, _dskParameterTable
        lea si, RxDOSBIOS_DiskBuffer + _bsBytesPerSector
        lea di, offset _dskBPB [ di ]
        mov cx, sizeBPB
        rep movsb

        mov di, word ptr [ _dskParameterTable ][ bp ]
        mov ax, word ptr [ _dskExtHiddenSectors. _low  ][ di ]
        mov dx, word ptr [ _dskExtHiddenSectors. _high ][ di ]
        add word ptr [ _dskBPB. _bpbHiddenSectors. _low  ][ di ], ax
        adc word ptr [ _dskBPB. _bpbHiddenSectors. _high ][ di ], dx

     ; copy serial number

        mov di, word ptr [ _dskParameterTable ][ bp ]
        mov ax, word ptr [ RxDOSBIOS_DiskBuffer. _bsVolumeId. _Low  ]
        mov dx, word ptr [ RxDOSBIOS_DiskBuffer. _bsVolumeId. _High ]
        mov word ptr [ _dskSerialNumber. _Low  ][ di ], ax
        mov word ptr [ _dskSerialNumber. _High ][ di ], dx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Determine FAT Size
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getdarg es, di, _dskParameterTable
        mov byte ptr es:[ _dskDeviceType  ][ di ], DSKFILESYSTYPE_IsValid

_RebuildBPB_36:
        getdarg es, di, _dskParameterTable
        mov cl, byte ptr es:[ _dskBPB. _bpbMediaDescriptor ][ di ]
        clc

_RebuildBPB_38:
        pop ds
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Init Reset Drive                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  ES:DI     Disk Parameter Block Address, if carry not set.    ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  CY        if error.                                          ;
        ;...............................................................;

InitResetDrive:

        cmp byte ptr es:[ _dskDeviceType  ][ di ], -1   ; device entry not initialized ?
        clc                                             ; (set no error )
        jnz InitResetDrive_08                           ; if already initialized -->

        mov word ptr es:[ _dskStatusFlag ][ di ], 0000  ; no flags

        mov dl, byte ptr es:[ _dskPhysDriveNumber ][ di ]
        test dl, 80h                                    ; fixed drive ?
        jz InitResetDrive_06                            ; no -->
        or word ptr es:[ _dskStatusFlag ][ di ], IsNonRemovable

InitResetDrive_06:
        mov ah, 00h
        call DiskFunctions                              ; reset drive

        push es
        push di
        or word ptr es:[ _dskStatusFlag ][ di ], ActiveUnit

        mov ax, 0800h
        mov dl, byte ptr es:[ _dskPhysDriveNumber ][ di ]
        call DiskFunctionsWithRetry                     ; get disk parameters

        mov bx, es
        or bx, di                                       ; disk parameter table == 0 ?
        pop di
        pop es                                          ; restore pointr to 
        jnz InitResetDrive_08                           ; if valid drive -->
        and word ptr es:[ _dskStatusFlag ][ di ], NOT ActiveUnit
        stc                                             ; set carry (error ) flag

InitResetDrive_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ; Build Disk Parameter Block                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  al        DOS disk unit.                                     ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  es:di     Disk Parameter Block Address, if carry not set.    ;
        ;  cy        if error.                                          ;
        ;...............................................................;

BuildDiskParameterBlock:

        cmp word ptr cs:[ ptr_StartBlockedDeviceTable. _segment ], 0000
        stc                                             ; assume not initialized
        jz _buildDPB_20                                 ; if not initialized -->

        les di, dword ptr cs:[ ptr_StartBlockedDeviceTable ]

_buildDPB_08:
        cmp al, byte ptr es:[ _dskDOSLogicalDiskUnit ][ di ]
        jz _buildDPB_12                                 ; if logical unit ->

        cmp word ptr es:[ _dskNextPointer. _pointer ][ di ], -1
        stc                                             ; assume end of list
        jz _buildDPB_20                                 ; if end of list -->
          
        les di, dword ptr es:[ _dskNextPointer ][ di ]
        jmp _buildDPB_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; if never initialized or disk change suspected, rebuild BPB
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_buildDPB_12:
        call InitResetDrive                             ; is drive valid ?
        jc _buildDPB_18                                 ; no -->

        test word ptr es:[ _dskStatusFlag ][ di ], ActiveUnit
        jz _buildDPB_18                                 ; if unit is invalid -->
        call _ChangedLineSet                            ; see if removable
        jz _buildDPB_20                                 ; if line not changed -->

        call RebuildBPB                                 ; else rebuild BPB
        jnc _buildDPB_20                                ; exit if no error -->

_buildDPB_18:
        stc                                             ; error exit

_buildDPB_20:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Find Block Device Info                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  al        DOS disk unit.                                     ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  es:di     Disk Parameter Block Address, if carry not set.    ;
        ;  cy        if error.                                          ;
        ;...............................................................;

FindDiskParameterBlock:

        cmp word ptr cs:[ ptr_StartBlockedDeviceTable. _segment ], 0000
        stc                                             ; assume not initialized
        jz _findDPB_20                                  ; if not initialized -->

        les di, dword ptr cs:[ ptr_StartBlockedDeviceTable ]

_findDPB_08:
        cmp al, byte ptr es:[ _dskDOSLogicalDiskUnit ][ di ]
        jz _findDPB_12                                  ; if logical unit ->

        cmp word ptr es:[ _dskNextPointer. _pointer ][ di ], -1
        stc                                             ; assume end of list
        jz _findDPB_20                                  ; if end of list -->
          
        les di, dword ptr es:[ _dskNextPointer ][ di ]
        jmp _findDPB_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; if never initialized or disk change suspected, rebuild BPB
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_findDPB_12:
        cmp byte ptr es:[ _dskDeviceType  ][ di ], -1   ; device entry not initialized ?
        jnz _findDPB_14                                 ; if already init -->
        call InitResetDrive                             ; is drive valid ?
        jc _findDPB_18                                  ; if error -->

_findDPB_14:
        test word ptr es:[ _dskStatusFlag ][ di ], ActiveUnit
        jz _findDPB_18                                  ; if unit is invalid -->
        call _ChangedLineSet                            ; see if removable
        jz _findDPB_20                                  ; if line not changed -->
        call RebuildBPB                                 ; else rebuild BPB
        jnc _findDPB_20                                 ; exit if no error -->

_findDPB_18:
        stc                                             ; if error -->

_findDPB_20:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Initialize Block Device Info                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  cx        number of fixed disks to initialize                ;
        ;  dx        number of floppy disks to initialize               ;
        ;...............................................................;

initDiskParameterBlocks:

        Entry
        def  _fixeddevices, cx
        def  _floppydevices, dx
        def  _devices, 0002
        def  _physicalDrive, 80h

        mov ax, cs
        mov es, ax
        mov ds, ax

        add cx, dx                                      ; total # of devices
        mov ax, sizeDISKBLOCK                           ; multiply by size
        mul cx
        mov cx, ax                                      ; save size in cx
        or cx, cx                                       ; no devices ?
        ifz _initDiskParams_36                          ; then exit -->

        mov di, offset RxDOS_StartBlockedDeviceTable
        clearMemory                                     ; init clear

     ; make first two devices disk A: and B:

        mov di, offset RxDOS_StartBlockedDeviceTable
        mov byte ptr [ _dskPhysDriveNumber    ][ di ], 00h      ; A:
        mov byte ptr [ _dskDOSLogicalDiskUnit ][ di ], 00       ; A:
        mov byte ptr [ _dskDeviceType         ][ di ], -1       ; say device not init

        mov byte ptr [ _dskPartitionBeginHead    ][ di ], 0
        mov byte ptr [ _dskPartitionBeginCylinder][ di ], 0
        mov byte ptr [ _dskPartitionBeginSector  ][ di ], 1

        lea bx, sizeDISKBLOCK [ di ]
        mov word ptr [ _dskNextPointer. _segment    ][ di ], ds
        mov word ptr [ _dskNextPointer. _pointer    ][ di ], bx
        mov di, bx                                              ; go to next drive

        mov byte ptr [ _dskPhysDriveNumber    ][ di ], 01h      ; B:
        mov byte ptr [ _dskDOSLogicalDiskUnit ][ di ], 01       ; B:
        mov byte ptr [ _dskDeviceType         ][ di ], -1       ; say device not init

        mov byte ptr [ _dskPartitionBeginHead    ][ di ], 0
        mov byte ptr [ _dskPartitionBeginCylinder][ di ], 0
        mov byte ptr [ _dskPartitionBeginSector  ][ di ], 1

        lea bx, sizeDISKBLOCK [ di ]
        mov word ptr [ _dskNextPointer. _segment    ][ di ], ds
        mov word ptr [ _dskNextPointer. _pointer    ][ di ], bx
        mov di, bx                                              ; go to next drive

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; initialize all remaining devices to hard disks, link list all blocks
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        cmp word ptr _fixeddevices[ bp ], 0000          ; any fixed devices ?
        jz _initDiskParams_32                           ; no -->

_initDiskParams_12:
        mov dx, word ptr [ _physicalDrive ][ bp ]       ; physical disk unit
        mov ax, word ptr [ _devices       ][ bp ]       ; current devices counter
        call mapAllStandardDOSPartitions
        mov word ptr [ _devices ][ bp ], ax             ; additional devices

        inc word ptr [ _physicalDrive ][ bp ]           ; physical disk unit
        dec word ptr [ _fixeddevices ][ bp ]            ; more phys devices to go ?
        jnz _initDiskParams_12                          ; if yes -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; set end of list
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_initDiskParams_32:
        lea bx, ( -sizeDISKBLOCK) [ di ]
        mov word ptr [ _dskNextPointer. _pointer    ][ bx ], -1

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; end, return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_initDiskParams_36:
        mov word ptr [ ptr_StartBlockedDeviceTable. _segment ], cs
        mov word ptr [ ptr_StartBlockedDeviceTable. _pointer ], offset RxDOS_StartBlockedDeviceTable

        getarg cx, _devices                             ; total devices (floppy + fixed).
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Scan Standard DOS Partitions                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  ax        current devices                                    ;
        ;  cx:dx     partition table physical address                   ;
        ;  ds:di     drive table to create                              ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  ds:di     drive table to create (points to end of table )    ;
        ;  ax        current devices                                    ;
        ;                                                               ;
        ;...............................................................;

mapAllStandardDOSPartitions:

        Entry
        def  _devices, ax
        def  _entries, 4
        def  _physaddressDX, dx
        ddef _additionalSectorOffset, 0000, 0000
        ddef _PartitionTablePtr, ss, si
        ddef _DiskParameterTablePtr, es, di
        defbytes  _PhysPartitionTable, sizePARTITIONTABLE

        mov cx, 0001h                                   ; assume at boot sector
        or dh, dh                                       ; extended or sec part table ?
        jz _mapStandardPartitions_08                    ; no, arguments already in cx:dx

        mov ax, word ptr ss:[ _ptStartSector. _low ][ si ]
        mov dx, word ptr ss:[ _ptStartSector. _high ][ si ]
        mov word ptr [ _additionalSectorOffset. _low ][ bp ], ax
        mov word ptr [ _additionalSectorOffset. _high ][ bp ], dx

        mov dl, byte ptr [ _physaddressDX ][ bp ]       ; physical drive 
        mov dh, byte ptr ss:[ _ptBeginHead   ][ si ]    ; begin head address
        mov cx, word ptr ss:[ _ptBeginSector ][ si ]    ; begin sector address

_mapStandardPartitions_08:
        call ReadPartitionTable
        ifc _mapStandardPartitions_36

        setDS es                                        ; es: si --> ds: si
        setES ss                                        ; ss: di --> es: di
        lea di, word ptr _PhysPartitionTable [ bp ]
        mov cx, sizePARTITIONTABLE
        fastmove

        lea si, word ptr _PhysPartitionTable [ bp ]     ; restore pointer
        getdarg es, di, _DiskParameterTablePtr          ; disk parameter table to build

_mapStandardPartitions_12:
        mov al, byte ptr ss:[ _ptFileSystemName ][ si ] 
        cmp al, FILESYSID_12FAT                         ; 12-bit FAT (max 10 MBytes)
        jz _mapStandardPartitions_16 
        cmp al, FILESYSID_16FAT                         ; 16-bit FAT (max 32 MBytes)
        jz _mapStandardPartitions_16 
        cmp al, FILESYSID_LARGE16FAT                    ; 16-bit FAT (greater than 32 MBytes)
        jz _mapStandardPartitions_16 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; process extended partition
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp al, FILESYSID_EXTENDED                      ; Extended DOS partition
        ifnz _mapStandardPartitions_24                  ; if not DOS file system -->

        push si
        mov ax, word ptr [ _devices ][ bp ]             ; device assignment
        mov dl, byte ptr [ _physaddressDX ][ bp ]       ; physical drive 
        mov dh, -1                                      ; say read additional params from part table.
        call mapAllStandardDOSPartitions                ; recursive call; di preserved

        mov word ptr [ _devices ][ bp ], ax             ; device assignment
        pop si
        jmp _mapStandardPartitions_24
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; process standard partition
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_mapStandardPartitions_16:
        clearMemory sizeDISKBLOCK                       ; clear area for each item

        mov al, byte ptr [ _physaddressDX ][ bp ]
        mov ah, byte ptr [ _devices ][ bp ]
        mov byte ptr [ _dskPhysDriveNumber       ][ di ], al
        mov byte ptr [ _dskDOSLogicalDiskUnit    ][ di ], ah
        mov word ptr [ _dskStatusFlag            ][ di ], IsNonRemovable
        mov byte ptr [ _dskDeviceType            ][ di ], -1       ; say device not init

        mov al, byte ptr ss:[ _ptBeginHead       ][ si ]     ; begin head address
        mov dx, word ptr ss:[ _ptBeginSector     ][ si ]     ; begin sector address
        mov byte ptr [ _dskPartitionBeginHead    ][ di ], al
        mov word ptr [ _dskPartitionBeginSector  ][ di ], dx

        mov al, byte ptr ss:[ _ptEndHead         ][ si ]     ; end head address
        mov dx, word ptr ss:[ _ptEndSector       ][ si ]     ; end sector address
        mov byte ptr [ _dskPartitionEndHead      ][ di ], al
        mov word ptr [ _dskPartitionEndSector    ][ di ], dx

        mov ax, word ptr ss:[ _ptSectors. _low   ][ si ]
        mov dx, word ptr ss:[ _ptSectors. _high  ][ si ]
        mov word ptr [ _dskBPB. _bpbMaxSectors   ][ di ], ax
        or dx, dx                                       ; small volume ?
        jz _mapStandardPartitions_20                    ; yes -->

        mov word ptr [ _dskBPB. _bpbMaxSectors   ][ di ], 0000
        mov word ptr [ _dskBPB. _bpbHugeSectors. _low  ][ di ], ax
        mov word ptr [ _dskBPB. _bpbHugeSectors. _high ][ di ], dx

_mapStandardPartitions_20:
        mov ax, word ptr [ _additionalSectorOffset. _low ][ bp ]
        mov dx, word ptr [ _additionalSectorOffset. _high ][ bp ]
        mov word ptr [ _dskExtHiddenSectors. _low  ][ di ], ax
        mov word ptr [ _dskExtHiddenSectors. _high ][ di ], dx

        add ax, word ptr ss:[ _ptStartSector. _low  ][ si ]
        adc dx, word ptr ss:[ _ptStartSector. _high ][ si ]
        mov word ptr [ _dskBPB. _bpbHiddenSectors. _low  ][ di ], ax
        mov word ptr [ _dskBPB. _bpbHiddenSectors. _high ][ di ], dx

        lea bx, sizeDISKBLOCK [ di ]
        mov word ptr [ _dskNextPointer. _segment ][ di ], cs
        mov word ptr [ _dskNextPointer. _pointer ][ di ], bx
        mov di, bx                                      ; go to next drive
        mov word ptr [ _DiskParameterTablePtr. _pointer ][ bp ], di
        inc word ptr [ _devices ][ bp ]

_mapStandardPartitions_24:
        add si, sizePARTITION
        dec word ptr [ _entries ][ bp ]                 ; more entries ?
        ifnz _mapStandardPartitions_12                  ; yes -->

        clc
        mov ax, word ptr [ _devices ][ bp ]             ; return updated devices

_mapStandardPartitions_36:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read Partition Table                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;  dl        drive                                              ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;  cs:si     partition table address                            ;
        ;  ax        signature word                                     ;
        ;                                                               ;
        ;...............................................................;

ReadPartitionTable:

        SaveSegments di, bx

        setDS cs
        setES cs
        push dx
        xor ax, ax
        call DiskFunctionsWithRetry 
        jc ReadPartitionTable_12

        pop dx
        mov ax, 0201h
        mov bx, offset RxDOSBIOS_DiskBuffer
        call DiskFunctionsWithRetry 
        jc ReadPartitionTable_12

        mov si, offset RxDOSBIOS_DiskBuffer + sizeSector - 2 - sizePARTITIONTABLE
        mov ax, word ptr [ RxDOSBIOS_DiskBuffer + sizeSector - 2 ]
        cmp ax, RxDOS_PARTITIONSIGNATURE
        clc

ReadPartitionTable_12:
        RestoreSegments bx, di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Disk Functions                                               ;
        ;...............................................................;

DiskFunctions:
        push bp
        int 13h
        pop bp
      ; call SetDiskError  
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Disk Functions                                               ;
        ;...............................................................;

DiskFunctionsWithRetry:
        Entry
        def  _retries, 03
        def  _errorcode
        def  _drive, dx
        def  _function, ax

DiskFctRetry_08:
        push bp
        int 13h
        pop bp
        jnc DiskFctRetry_12

        push bp
        mov byte ptr [ _errorcode ][ bp ], ah

        mov ah, 00h
        mov dl, byte ptr [ _drive ][ bp ]
        int 13h
        pop bp

        mov ax, word ptr [ _function ][ bp ]
        dec word ptr [ _retries ][ bp ]
        jnz DiskFctRetry_08

        mov ah, byte ptr [ _errorcode ][ bp ]
        stc

DiskFctRetry_12:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Disk Functions                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  AX  contains BIOS error status                               ;
        ;...............................................................;

setPacketReqError:
        Entry 2
        darg  _packet
        jnc setPacketReqError_08                        ; if not an error -->

        pushf
        push ds
        push bx
        call convertErrorCodefromBIOSStatus

        getdarg ds, bx, _packet
        mov word ptr [ rhStatus ][ bx ], ax             ; set error flag

        pop bx
        pop ds
        popf

setPacketReqError_08:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Error Trap Functions                                         ;
        ;...............................................................;

SetInvalidDriveError:
        Entry 2
        darg  _packet

        jnc SetInvalidDriveError_08

        pushf
        push ds
        push bx
        getdarg ds, bx, _packet
        mov word ptr [ rhStatus ][ bx ], devErrUnknownUnit + OP_ERROR

        pop bx
        pop ds
        popf

SetInvalidDriveError_08:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Disk Functions                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  AX  contains BIOS error status                               ;
        ;...............................................................;

convertErrorCodefromBIOSStatus:

        xor bx, bx
        mov bl, ah                                      ; BIOS error code to index register
        mov ax, devErrDeviceNotReady
        cmp bx, dskErrDriveTimeOut
        jz convErr_08
        cmp bx, dskErrDriveNotReady
        jz convErr_08

        xor ax, ax
        cmp bx, Max_BIOSErrorCodeTable
        jnc convErr_08
        mov al, byte ptr cs:[ BIOSErrorCodeTable ][ bx ]

convErr_08:
        or ax, OP_ERROR
        ret

BIOSErrorCodeTable:

        db 0                                            ;  0 - dskErrNoError
        db devErrBadDriveReq                            ;  1 - dskErrInvalidParameter
        db devErrBadDriveReq                            ;  2 - dskErrAddrMarkNotFound
        db devErrWriteProtectViol                       ;  3 - dskErrWriteProtected
        db devErrSectorNotFound                         ;  4 - dskErrSectorNotFound
        db devErrGeneralFailure                         ;  5 - dskErrResetFailed
        db devErrGeneralFailure                         ;  6 - dskErrDisketteRemoved
        db devErrGeneralFailure                         ;  7 - dskErrBadParameterTable
        db devErrGeneralFailure                         ;  8 - dskErrDMAOverrun
        db devErrGeneralFailure                         ;  9 - dskErrCross64kBoundary
        db devErrGeneralFailure                         ; 10 - dskErrBadSector
        db devErrGeneralFailure                         ; 11 - dskErrBadCylinder
        db devErrGeneralFailure                         ; 12 - dskErrMediaTypeNotFound
        db devErrGeneralFailure                         ; 13 - dskErrInvalidNumSectors
        db devErrGeneralFailure                         ; 14 - dskErrControlAddrMark
        db devErrGeneralFailure                         ; 15 - dskErrDMAArbOutOfRange
        db devErrCRCerr                                 ; 16 - dskErrCRCerror
        db devErrCRCerr                                 ; 17 - dskErrECCCorrectedErr

Max_BIOSErrorCodeTable          equ ($ - BIOSErrorCodeTable - 1)

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Reallocated Space                                            ;
        ;...............................................................;

RxDOSBIOS_DiskBuffer:
        db sizeSector dup(?)

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
; The address remains valid but the contents are destroyed
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

RxDOSBIOS_LastAddress:
RxDOS_StartBlockedDeviceTable:

        ; anything allocated after this line will either be clobbered or
        ; reallocated

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
; this message is destroyed once the device table is initialized
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

RxDOSBIOS               ENDS
                        END RxDOSBIOS_Start

