        TITLE   'dev - device interface and search support'
        PAGE 59, 132
        .LALL

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  DEV Device Interface for RxDOS                               ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  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

RxDOS   SEGMENT PUBLIC 'CODE'
        assume cs:RxDOS, ds:RxDOS, es:RxDOS, ss:RxDOS

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Driver Support                                               ;
        ;...............................................................;

        public checkforDeviceType
        public checkforDeviceName
        public maxBlockDevices
        public DevMediaRequest
        public DevRead
        public DevWrite
        public devCharRead
        public devCharWrite
        public devCharReadLine
        public devCharWriteLine
        public initReqBlock
        public BlockedDevRequest
        public getDPB
        public getAddrDPB
        public DefineDPB
        public incorrectDiskMedia

        public getSysDate
        public setSysDate
        public getExpandedDateTime
        public getExpandedDate

        public readConsoleIn
        public writeConsoleOut
        public CharDevRequest
        public _callCriticalError

        extrn upperCase                         : near
        extrn _bitShiftTable                    : near
        extrn sizeShiftTable                    : abs
        extrn _RxDOS_bLastDrive                 : byte
        extrn _RxDOS_NULLDev                    : dword
        extrn _RxDOS_pCDS                       : dword
        extrn _RxDOS_pCLOCKdriver               : dword
        extrn _RxDOS_pCONdriver                 : dword
        extrn _RxDOS_Verify                     : word
        extrn _RxDOS_AbortInProgress            : word
        extrn _RxDOS_StackLongJump              : word
        extrn _RxDOS_bCtrlBreakCheck            : byte

        extrn RxDOS_StackProtect                : word
        extrn RxDOS_StackTop                    : word
        extrn _RxDOS_CurrentInstance            : word
        extrn _RxDOS_CurrentStackTop            : word
        extrn _RxDOS_INDOSFlag                  : word
        extrn _RxDOS_CurrentPSP                 : word
        extrn SDAInt24_SPSave                   : dword

        extrn _RetCallersStackFrame             : near
        extrn updateAllChangedCCBBuffers        : near
        extrn invalidateBuffers                 : near

        extrn AmountFreeSpace                   : near
        extrn _TerminateProcess                 : near

        extrn LogTraceBlockDevRequest           : near
        extrn LogTraceBlockDevReturn            : near
        extrn LogTraceCharDevRequest            : near
        extrn LogTraceCharDevReturn             : near

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Default Stdin Access                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:bx  buffer address.                                      ;
        ;   cx     length.                                              ;
        ;                                                               ;
        ;...............................................................;

readConsoleIn:

        or cx, cx
        jz readConsoleIn_08

        mov di, bx
        push word ptr ss:[ _RxDOS_pCONdriver. _segment ]
        push word ptr ss:[ _RxDOS_pCONdriver. _pointer ]
        call devCharReadLine                            ; read til cr or eof.

readConsoleIn_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Default Stdout Access                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:bx  buffer address.                                      ;
        ;   cx     length.                                              ;
        ;                                                               ;
        ;...............................................................;

writeConsoleOut:

        or cx, cx
        jz writeConsoleOut_08

        mov di, bx
        push word ptr ss:[ _RxDOS_pCONdriver. _segment ]
        push word ptr ss:[ _RxDOS_pCONdriver. _pointer ]
        call devCharWrite

writeConsoleOut_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Locate Device Driver By Type                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     type to match.  must match all bits set.             ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:bx  pointer to driver header block                       ;
        ;...............................................................;

checkforDeviceType:

        push es
        currSegment es                                  ; point to NULL device
        mov bx, offset _RxDOS_NULLDev                   ; get start of chain

chkDevType_08:
        cmp bx, -1                                      ; end of list ?
        stc                                             ; set error
        jz chkDevType_16                                ; if end of list -->

        mov cx, word ptr es:[ devAttributes ][ bx ]
        and cx, ax                                      ; strip away only mask bits 
        cmp cx, ax                                      ; all bits match ?
        jnz chkDevType_14                               ; no, go to next -->

        add sp, 2                                       ; pop old es:
        push es                                         ; save current es: for return
        jmp short chkDevType_16                         ; return -->

chkDevType_14:
        les bx, dword ptr es:[ devLink ][ bx ]
        jmp chkDevType_08                               ; go to next ->

chkDevType_16:
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Locate Device Driver By Name                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  name to match.  character devices only.              ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:bx  pointer to driver header block                       ;
        ;   cy     driver was not located                               ;
        ;...............................................................;

checkforDeviceName:

        Entry
        defbytes _tempdevname, sizedevName

        push ds
        push di
        push si
        push es

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init by creating a blank filled upper case mask
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov si, di
        lea di, offset _tempdevname [ bp ]
        mov cx, sizedevName

chkDevName_08:
        mov al, byte ptr es:[ si ]
        call upperCase

        cmp al, ' ' + 1                                 ; null or special character ?
        jle chkDevName_12
        cmp al, ':'                                     ; colon ?
        jz chkDevName_12

        mov byte ptr ss:[ di ], al                      ; store character
        inc si
        inc di
        loop chkDevName_08

chkDevName_12:
        or cx, cx
        jz chkDevName_14

        mov byte ptr ss:[ di ], ' '                     ; blank fill
        inc di
        loop chkDevName_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  match against all known driver names
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

chkDevName_14:
        setDS ss        
        currSegment es
        mov bx, offset _RxDOS_NULLDev                   ; get start of chain

chkDevName_20:
        cmp bx, -1                                      ; end of list ?
        stc                                             ; set error
        jz chkDevName_26                                ; if end of list -->

        test word ptr es:[ devAttributes ][ bx ], ( DEV_CHAR )
        jz chkDevName_24                                ; not a character device ->

        lea si, offset _tempdevname [ bp ]
        lea di, offset [ devName ][ bx ]
        mov cx, sizedevName
        rep cmpsb                                       ; compare names
        jnz chkDevName_24                               ; if not equal, go to next -->

        clc                                             ; no carry means we have a valid device
        pop si                                          ; remove old es:
        push es                                         ; return this es:
        jmp short chkDevName_26

chkDevName_24:
        les bx, dword ptr es:[ devLink ][ bx ]
        jmp chkDevName_20                               ; go to next ->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

chkDevName_26:
        pop es
        pop si
        pop di
        pop ds
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Locate Blocked Devices                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  Input:                                                       ;
        ;   al     drive                                                ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx:cx  pointer to driver header block                       ;
        ;...............................................................;

findInstalledBlockDevice:

        Entry
        def  _drive, ax

        push es
        push bx
        call maxBlockDevices                            ; get number of blocked devices

        currSegment es
        mov bx, offset _RxDOS_NULLDev                   ; get start of chain
        getarg ax, _drive

findBlockedDev_08:
        cmp bx, -1                                      ; end of list ?
        stc                                             ; set error
        jz findBlockedDev_16                            ; if end of list -->

        test word ptr es:[ devAttributes ][ bx ], ( DEV_CHAR )
        jnz findBlockedDev_14                           ; if a character device -->

        sub cl, es:[ devUnits ][ bx ]                   ; # units suported
        cmp al, cl                                      ; compare against drive code
        jl findBlockedDev_14                            ; if not device of interest -->

        or ax, ax                                       ; clear carry
        jmp short findBlockedDev_16                     ; return -->

findBlockedDev_14:
        les bx, dword ptr es:[ devLink ][ bx ]
        jmp findBlockedDev_08                           ; go to next ->

findBlockedDev_16:
        mov cx, es
        mov dx, bx
        getarg ax, _drive
        pop bx
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Report Max Block Devices Available                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     number of blocked devices                            ;
        ;...............................................................;

maxBlockDevices:

        push es
        currSegment es
        mov bx, offset _RxDOS_NULLDev                   ; get start of chain
        xor cx, cx                                      ; # blocked devices

maxBlockDev_08:
        cmp bx, -1                                      ; end of list ?
        jz maxBlockDev_16                               ; if end of list -->

        test word ptr es:[ devAttributes ][ bx ], ( DEV_CHAR )
        jnz maxBlockDev_14                              ; if a character device -->

        add cl, byte ptr es:[ devName ][ bx ]

maxBlockDev_14:
        les bx, dword ptr es:[ devLink ][ bx ]
        jmp maxBlockDev_08                              ; go to next ->

maxBlockDev_16:
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Driver Media Request                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     drive to check                                       ;
        ;   ah     media type expected                                  ;
        ;   ss:bx  ptr for volume id return value                       ;
        ;           (value set only if line changed)                    ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cy     if abort/ not ready                                  ;
        ;                                                               ;
        ;   ax     original drive info                                  ;
        ;   ch     new media type                                       ;
        ;   cl     if changed flags                                     ;
        ;...............................................................;

DevMediaRequest:

        Entry
        def  _drive, ax
        def  _ptrVolumeID, bx                           ; ss:
        defbytes reqBlock, sizeMaxReqHeader

        push es
        push bx
        push di
        push ax

        mov ah, MEDIAREQUEST
        lea di, offset reqBlock [ bp ]
        call initReqBlock

        mov byte ptr [ reqBlock. mrMediaID  ][ bp ], ah
        mov byte ptr [ reqBlock. mrLength   ][ bp ], sizeMEDIAReqHeader
        mov byte ptr [ reqBlock. mrFunction ][ bp ], MEDIAREQUEST
        getarg ax, _drive                               ; restore drive
        call BlockedDevRequest                          ; call blocked device

        mov bx, word ptr [ _ptrVolumeID ][ bp ]
        mov ax, word ptr [ reqBlock. mrVolumeID. _Low  ][ bp ]
        mov cx, word ptr [ reqBlock. mrVolumeID. _High ][ bp ]
        mov word ptr ss:[ _Low  ][ bx ], ax
        mov word ptr ss:[ _High ][ bx ], cx

        mov ch, byte ptr [ reqBlock. mrMediaID ][ bp ]
        mov cl, byte ptr [ reqBlock. mrReturn  ][ bp ]

        pop ax
        pop di
        pop bx
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Incorrect Disk Media                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   cx:dx  serial number required                               ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cy     if abort/ not ready                                  ;
        ;   ax     original drive info                                  ;
        ;                                                               ;
        ;   All Registers Saved                                         ;
        ;...............................................................;

incorrectDiskMedia:

        Entry
        defbytes _devVolumeId, sizeVolumeID
        defbytes reqBlock, sizeMaxReqHeader

        mov ah, MEDIAREQUEST
        lea di, offset reqBlock [ bp ]
        call initReqBlock

        mov ax, offset devErrInvalidDiskChange
        lea bx, offset reqBlock [ bp ]
        call _callCriticalError
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Driver READ                                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     drive                                                ;
        ;   bx     # sectors to read                                    ;
        ;   cx:dx  starting sector address to read                      ;
        ;   es:di  buffer to read (buffer address cannot wrap-around)   ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cy     if abort/ not ready                                  ;
        ;...............................................................;

DevRead:

        Entry
        def  _drive, ax
        def  _sectors, bx
        ddef _bufferPtr, es, di
        ddef _sector, cx, dx
        defbytes volumeID, sizevolumeLabel
        defbytes reqBlock, sizeMaxReqHeader

        push es
        push bx
        push di
        push ax
        call getAddrDPB                                 ; (es:bx) Device Parameter Block
        jc DevRead_16                                   ; if invalid drive -->

        push word ptr es:[ _dpbMediaDescriptor ][ bx ]

        mov ah, DEVICEREAD
        lea di, offset reqBlock [ bp ]
        call initReqBlock

        getarg ax, _sectors
        mov word ptr [ reqBlock.rwrBytesReq ][ bp ], ax

        pop ax                                          ; media descriptor
        mov byte ptr [ reqBlock.rwrMediaID ][ bp ], al

        mov dx, word ptr [ _bufferPtr. _segment ][ bp ]
        mov ax, word ptr [ _bufferPtr. _pointer ][ bp ]
        mov word ptr [ reqBlock.rwrBuffer. _segment ][ bp ], dx
        mov word ptr [ reqBlock.rwrBuffer. _pointer ][ bp ], ax

        lea di, offset volumeID [ bp ]
        mov word ptr [ reqBlock. rwrVolumeID. _pointer ][ bp ], di
        mov word ptr [ reqBlock. rwrVolumeID. _segment ][ bp ], ss

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if address is huge, store in huge request area
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg cx, dx, _sector
        or cx, cx                                       ; if huge address
        jz DevRead_08                                   ; must save in huge, else skip -->

        mov word ptr [ reqBlock. rwrHugeStartSec. _high ][ bp ], cx
        mov word ptr [ reqBlock. rwrHugeStartSec. _low  ][ bp ], dx
        mov dx, -1

DevRead_08:
        mov word ptr [ reqBlock.rwrStartSec ][ bp ], dx ; sector (or -1 if huge)
        getarg ax, _drive                               ; restore drive
        call BlockedDevRequest                          ; call blocked device

DevRead_16:
        pop ax
        pop di
        pop bx
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Driver CHAR READ LINE                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Reads until CR or end of buffer                              ;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     characters to read                                   ;
        ;   es:di  buffer to read                                       ;
        ;   stack  driver to read from                                  ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     characters actually returned                         ;
        ;   zr     if end of file                                       ;
        ;...............................................................;

DevCharReadLine:

        Entry 2
        darg _device

        def  _endoffile, FALSE
        def  _updatedBuffers, FALSE
        ddef _clockticks        
        ddef _bufferPtr, es, di
        defbytes _tempBuffer, 8
        defbytes _lineEdit, sizeLINEEDITOR
        defbytes reqBlock, sizeMaxReqHeader

        push es
        push bx
        push di
        push ax
        push cx                                         ; max characters

        mov al, -1                                      ; not a block device
        mov ah, NONDESTRREAD
        lea di, offset reqBlock [ bp ]
        call initReqBlock                               ; init non-destructive read

        call ClockTimer                                 ; dx: ax
        stordarg _clockticks, dx, ax

        pop cx
        push di
        lea di, offset _lineEdit [ bp ]
        mov dx, word ptr [ _bufferPtr. _segment ][ bp ]
        mov ax, word ptr [ _bufferPtr. _pointer ][ bp ]
        call _lineEditorInit                            ; init line editor control block

        lea di, offset _tempBuffer [ bp ]
        mov word ptr [ reqBlock.rwrBuffer. _segment ][ bp ], ss
        mov word ptr [ reqBlock.rwrBuffer. _pointer ][ bp ], di
        mov word ptr [ reqBlock.rwrBytesReq         ][ bp ], 1
        pop di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  wait for character
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DevCharReadLine_12:
        mov byte ptr [ reqBlock.rwrFunction ][ bp ], NONDESTRREAD
        mov word ptr [ reqBlock.rwrStatus   ][ bp ], 0000

        push word ptr [ _device. _segment ][ bp ]
        push word ptr [ _device. _pointer ][ bp ]
        call CharDevRequest                             ; test for character

        test word ptr [ reqBlock.rwrStatus ][ bp ], OP_DONE
        jz DevCharReadLine_16                           ; time out -->
        test word ptr [ reqBlock.rwrStatus ][ bp ], OP_BUSY
        jz DevCharReadLine_20                           ; we have a character -->

DevCharReadLine_16:
        cmp word ptr [ _updatedBuffers ][ bp ], TRUE    ; already updated buffers ?
        jz DevCharReadLine_18                           ; skip around -->
        call updateAllChangedCCBBuffers                 ; optimized update changed CCBs
        mov word ptr [ _updatedBuffers ][ bp ], TRUE    ; already updated buffers ?

DevCharReadLine_18:
        int intIDLELOOP                                 ; let other applications run
        jmp DevCharReadLine_12                          ; continue looping -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get character, see if end of buffer or cr
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DevCharReadLine_20:
        push di
        mov byte ptr [ reqBlock.rwrFunction ][ bp ], DEVICEREAD
        push word ptr [ _device. _segment ][ bp ]
        push word ptr [ _device. _pointer ][ bp ]
        call CharDevRequest                             ; get character

        mov ax, word ptr [ _tempBuffer ][ bp ]          ; get character
        or al, al
        jz DevCharReadLine_22
        mov ah, 0

DevCharReadLine_22:
        lea di, offset _lineEdit [ bp ]
        call _lineEditor                                ; store character/ line editor

        mov cx, word ptr ss:[ editMaxAvail  ][ di ]     ; get max chars
        cmp cx, word ptr ss:[ editMaxBuffer ][ di ]     ; at max end of line ?
        pop di
        jge DevCharReadLine_36                          ; if exit -->

        call SetExit_IfControlC                         ; control C or Control Z ?
        storarg _endoffile, dx                          ; end of file status
        jz DevCharReadLine_36                           ; yes, exit -->
        cmp ax, ControlM                                ; return character ?
        jz DevCharReadLine_26                           ; yes -->
        cmp ax, ControlJ                                ; return character ?
        jnz DevCharReadLine_12                          ; no -->

DevCharReadLine_26:
        mov ax, ControlJ                                ; do line feed
        lea di, offset _lineEdit [ bp ]
        call _lineEditor                                ; use editor to display

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DevCharReadLine_36:
        getdarg es, di, _bufferPtr
        mov cx, word ptr [ _lineEdit. editMaxAvail ][ bp ] ; get chars entered
        cmp word ptr [ _endoffile ][ bp ], TRUE         ; zr if end of file
        clc                                             ; no carry

        pop ax
        pop dx

        pop ax
        pop di
        pop bx
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Driver CHAR READ                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     characters to read                                   ;
        ;   es:di  buffer to read                                       ;
        ;   stack  driver to read from                                  ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     characters actually read                             ;
        ;...............................................................;

DevCharRead:

        Entry 2
        darg _device

        def  _count, cx
        ddef _bufferPtr, es, di
        defbytes reqBlock, sizeMaxReqHeader

        push bx
        push ax

        mov al, -1                                      ; not a block device
        mov ah, IOCTLREAD
        lea di, offset reqBlock [ bp ]
        call initReqBlock

        mov dx, word ptr [ _bufferPtr. _segment ][ bp ]
        mov ax, word ptr [ _bufferPtr. _pointer ][ bp ]
        mov word ptr [ reqBlock.rwrBuffer. _segment ][ bp ], dx
        mov word ptr [ reqBlock.rwrBuffer. _pointer ][ bp ], ax

        getarg cx, _count
        mov word ptr [ reqBlock.rwrBytesReq         ][ bp ], cx

        push word ptr [ _device. _segment ][ bp ]
        push word ptr [ _device. _pointer ][ bp ]
        call CharDevRequest                             ; call device (get character)

        mov cx, word ptr [ reqBlock.rwrBytesReq ][ bp ] ; actual bytes read
        getdarg es, di, _bufferPtr                      ; restore es: di

        pop ax
        pop bx
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Driver WRITE OR WRITE/VERIFY                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     drive to write to                                    ;
        ;   bx     # sectors to write                                   ;
        ;   cx:dx  starting sector address where to write               ;
        ;   es:di  buffer to write (buffer address cannot wrap-around)  ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cy     if abort/ not ready                                  ;
        ;...............................................................;

DevWrite:

        Entry
        def  _drive, ax
        def  _sectors, bx
        ddef _bufferPtr, es, di
        ddef _sector, cx, dx
        defbytes volumeID, sizevolumeLabel
        defbytes reqBlock, sizeMaxReqHeader

        push es
        push bx
        push di
        push ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  ok to write
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DevWrite_02:
        call getAddrDPB                                 ; (es:bx) Device Parameter Block
        jc DevWrite_36                                  ; if invalid drive -->

        push word ptr es:[ _dpbMediaDescriptor ][ bx ]

        mov ah, DEVICEWRITE
        cmp byte ptr ss:[ _RxDOS_Verify ], 00           ; non-zero means verify
        jz DevWrite_06                                  ; if not verify -->
        mov ah, DEVICEWRITEVERIFY                       ; if verify writes

DevWrite_06:
        lea di, offset reqBlock [ bp ]
        call initReqBlock                               ; al contains [logcl unit]

        getarg ax, _sectors
        mov word ptr [ reqBlock.rwrBytesReq ][ bp ], ax

        pop ax                                          ; media descriptor
        mov byte ptr [ reqBlock.rwrMediaID ][ bp ], al

        mov dx, word ptr [ _bufferPtr. _segment ][ bp ]
        mov ax, word ptr [ _bufferPtr. _pointer ][ bp ]
        mov word ptr [ reqBlock.rwrBuffer. _segment ][ bp ], dx
        mov word ptr [ reqBlock.rwrBuffer. _pointer ][ bp ], ax

        lea di, offset volumeID [ bp ]
        mov word ptr [ reqBlock. rwrVolumeID. _pointer ][ bp ], di
        mov word ptr [ reqBlock. rwrVolumeID. _segment ][ bp ], ss

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if address is huge, store in huge request area
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg cx, dx, _sector
        or cx, cx
        jz DevWrite_08

        mov word ptr [ reqBlock. rwrHugeStartSec. _high ][ bp ], cx
        mov word ptr [ reqBlock. rwrHugeStartSec. _low  ][ bp ], dx
        mov dx, -1

DevWrite_08:
        mov word ptr [ reqBlock.rwrStartSec ][ bp ], dx
        getarg ax, _drive                               ; restore drive
        or ax, ax                                       ; zero ?
;;        jnz DevWrite_36                                 ; WRITE PROTECT DURING DEBUG -->
        cmp ax, 03
        jnz DevWrite_34
        int 3

DevWrite_34:
        call BlockedDevRequest                          ; call blocked device
        nop                                             ; DEBUG nop

DevWrite_36:
        pop ax
        pop di
        pop bx
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Driver CHAR WRITE                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     characters to write                                  ;
        ;   es:di  buffer to write                                      ;
        ;   stack  driver address to write to                           ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cy     if abort/ not ready                                  ;
        ;...............................................................;

DevCharWrite:
DevCharWriteLine:                               ; intentional alias

        Entry 2
        darg _device

        def _count, cx
        ddef _bufferPtr, es, di
        defbytes volumeID, sizevolumeLabel
        defbytes reqBlock, sizeMaxReqHeader

        push es
        push bx
        push di
        push ax

        mov al, -1                                      ; not a block device
        mov ah, DEVICEWRITE
        lea di, offset reqBlock [ bp ]
        call initReqBlock

        mov dx, word ptr [ _bufferPtr. _segment ][ bp ]
        mov ax, word ptr [ _bufferPtr. _pointer ][ bp ]
        mov word ptr [ reqBlock.rwrBuffer. _segment ][ bp ], dx
        mov word ptr [ reqBlock.rwrBuffer. _pointer ][ bp ], ax

        lea di, offset volumeID [ bp ]
        mov word ptr [ reqBlock. rwrVolumeID. _pointer ][ bp ], di
        mov word ptr [ reqBlock. rwrVolumeID. _segment ][ bp ], ss

        getarg cx, _count
        mov word ptr [ reqBlock.rwrBytesReq        ][ bp ], cx

        push word ptr [ _device. _segment ][ bp ]
        push word ptr [ _device. _pointer ][ bp ]
        call CharDevRequest                             ; call device

        getarg cx, _count                               ; assume written
        pop ax
        pop di
        pop bx
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get System Date and Time                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   es:di  points to date structure                             ;
        ;...............................................................;

getSysDate:

        Entry
        defbytes reqBlock, sizeMaxReqHeader

        saveRegisters es, di, dx, cx

        push es
        push di

        mov al, -1                                      ; not a block device
        mov ah, DEVICEREAD
        lea di, offset reqBlock [ bp ]
        call initReqBlock

        mov word ptr [ reqBlock.rwrBytesReq ][ bp ], sizeCLOCKDATA
        pop word ptr [ reqBlock.rwrBuffer. _pointer ][ bp ]     ; di
        pop word ptr [ reqBlock.rwrBuffer. _segment ][ bp ]     ; es

        push word ptr [ _RxDOS_pCLOCKdriver. _segment ]
        push word ptr [ _RxDOS_pCLOCKdriver. _pointer ]
        call CharDevRequest

        restoreRegisters cx, dx, di, es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Set System Date and Time                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   es:di  points to date structure                             ;
        ;...............................................................;

setSysDate:

        Entry
        defbytes reqBlock, sizeMaxReqHeader

        saveRegisters es, di, dx, cx

        push es
        push di

        mov al, -1                                      ; not a block device
        mov ah, DEVICEWRITE
        lea di, offset reqBlock [ bp ]
        call initReqBlock

        mov word ptr [ reqBlock.rwrBytesReq ][ bp ], sizeCLOCKDATA
        pop word ptr [ reqBlock.rwrBuffer. _pointer ][ bp ]      ; di
        pop word ptr [ reqBlock.rwrBuffer. _segment ][ bp ]      ; es

        push word ptr [ _RxDOS_pCLOCKdriver. _segment ]
        push word ptr [ _RxDOS_pCLOCKdriver. _pointer ]
        call CharDevRequest

        restoreRegisters cx, dx, di, es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Days/ Time From Bios                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ch     hours                                                ;
        ;   cl     minutes                                              ;
        ;   dh     seconds                                              ;
        ;   dl     hundredths of seconds                                ;
        ;...............................................................;

getExpandedDateTime:

        Entry
        defbytes _datedef, sizeCLOCKDATA

        saveRegisters es, di

        setES ss
        lea di, offset _datedef [ bp ]
        call getSysDate                                 ; get system date

        mov cx, word ptr [ _datedef. cl_minutes   ][ bp ]
        mov dx, word ptr [ _datedef. cl_hseconds  ][ bp ]
        mov ax, word ptr [ _datedef. cl_daysSince1980 ][ bp ]

        restoreRegisters di, es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Date From Bios                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dh     month                                                ;
        ;   dl     day                                                  ;
        ;   cx     year                                                 ;
        ;...............................................................;

getExpandedDate:

        Entry
        defbytes _datedef, sizeCLOCKDATA

        saveRegisters es, di

        setES ss
        lea di, offset _datedef [ bp ]
        call getSysDate                                 ; get system date

        mov dl, byte ptr [ _datedef. cl_day     ][ bp ]
        mov dh, byte ptr [ _datedef. cl_month   ][ bp ]
        mov cx, word ptr [ _datedef. cl_year    ][ bp ]
        mov ax, word ptr [ _datedef. cl_daysSince1980 ][ bp ]

        restoreRegisters di, es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Call Character Device Strategy /Interrupt Routines           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:bx  request block                                        ;
        ;   stack  device                                               ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cy     abort requested on error                             ;
        ;...............................................................;

CharDevRequest:

        Entry 2
        darg _device

        ddef _strategy
        ddef _interrupt
        ddef _packet, es, bx

        saveSegments bp, bx, ax

        getdarg es, bx, _device                         ; get device
        mov ax, es
        or ax, bx                                       ; if no device address
        jz CharDevRequest_12                            ; error exit -->

        mov ax, word ptr es:[ devStrategy  ][ bx ]
        stordarg _strategy, es, ax

        mov ax, word ptr es:[ devInterrupt ][ bx ]
        stordarg _interrupt, es, ax

 IFDEF RxDOS_TRACEBUILD
        getdarg es, bx, _packet
        cmp byte ptr es:[ rwrFunction ][ bx ], NONDESTRREAD
        jz CharDevRequest_06                            ; if idling -->

        push word ptr [ _interrupt. _segment ][ bp ]    ; driver address
        push word ptr [ _interrupt. _pointer ][ bp ]    ; driver address
        call LogTraceCharDevRequest                     ; save/log

CharDevRequest_06:
 ENDIF

        push ds
        push bp
        getdarg es, bx, _packet
        mov ds, word ptr [ _strategy. _segment ][ bp ]
        call dword ptr [ _strategy ][ bp ]              ; strategy

        pop bp
        push bp
        getdarg es, bx, _packet
        mov ds, word ptr [ _strategy. _segment ][ bp ]
        call dword ptr [ _interrupt ][ bp ]             ; interrupt
        pop bp
        pop ds

 IFDEF RxDOS_TRACEBUILD
        getdarg es, bx, _packet
        cmp byte ptr es:[ rwrFunction ][ bx ], NONDESTRREAD
        jz CharDevRequest_08                            ; if idling -->
        call LogTraceCharDevReturn                      ; save/ echo log trace

CharDevRequest_08:
 ENDIF

        getdarg es, bx, _packet
        cmp word ptr es:[ mrStatus ][ bx ], 0
        jz CharDevRequest_12
        test word ptr es:[ mrStatus ][ bx ], OP_ERROR
        jz CharDevRequest_16

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  time out, other error exit.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

CharDevRequest_12:
        stc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

CharDevRequest_16:
        restoreSegments ax, bx, bp
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Call Blocked Device Strategy /Interrupt Routines             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     drive unit                                           ;
        ;   es:bx  request block                                        ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cy     abort requested on error                             ;
        ;...............................................................;

BlockedDevRequest:

        Entry
        def  _drive, ax
        def  _sectors
        def  _sectorsRead
        ddef _strategy
        ddef _interrupt
        ddef _packet, es, bx

        saveSegments bp, bx, ax

        mov cx, word ptr es:[ rwrBytesReq ][ bx ]
        storarg _sectors, cx                            ; sectors

        call getAddrDPB                                 ; (es:bx) Device Parameter Block
        ifc BlockedDevRequest_28                        ; no device info -->
        
        cmp word ptr es:[ _dpbptrDeviceDriver. _segment ][ bx ], 0000
        ifz BlockedDevRequest_28                        ; no device info -->

        les bx, dword ptr es:[ _dpbptrDeviceDriver ][ bx ]
        mov ax, word ptr es:[ devStrategy  ][ bx ]
        stordarg _strategy, es, ax

        mov ax, word ptr es:[ devInterrupt ][ bx ]
        stordarg _interrupt, es, ax

BlockedDevRequest_06:
        getarg ax, _sectors
        getdarg es, bx, _packet                         ; restore packet address
        mov word ptr es:[ rwrBytesReq ][ bx ], ax       ; bytes request 
        mov word ptr es:[ rhStatus ][ bx ], 0000        ; (kill prev error code)

 IFDEF RxDOS_TRACEBUILD
        push word ptr [ _interrupt. _segment ][ bp ]    ; driver address
        push word ptr [ _interrupt. _pointer ][ bp ]    ; driver address
        call LogTraceBlockDevRequest                    ; save/ echo log trace

 ENDIF
        push ds
        push bp
        mov ds, word ptr [ _strategy. _segment ][ bp ]
        call dword ptr [ _strategy ][ bp ]              ; strategy

        pop bp
        push bp
        getdarg es, bx, _packet

        nop
        mov ds, word ptr [ _strategy. _segment ][ bp ]
        call dword ptr [ _interrupt ][ bp ]             ; interrupt

        nop
        pop bp
        pop ds

 IFDEF RxDOS_TRACEBUILD
        getdarg es, bx, _packet                         ; restore packet address
        call LogTraceBlockDevReturn                     ; save/ echo log trace

 ENDIF

        getdarg es, bx, _packet                         ; restore packet address
        mov cx, word ptr es:[ rwrBytesReq ][ bx ]       ; actual sectors read
        storarg _sectorsRead, cx                        ; sectors read

        cmp word ptr es:[ rhStatus ][ bx ], 0
        jz BlockedDevRequest_08
        test word ptr es:[ rhStatus ][ bx ], OP_ERROR
        jz BlockedDevRequest_32

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Abort, Retry, Ignore ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BlockedDevRequest_08:
        cmp word ptr ss:[ _RxDOS_AbortInProgress ], 0   ; already in critical error ?
        jnz BlockedDevRequest_28                        ; ignore error -->

        mov ax, word ptr es:[ rhStatus ][ bx ]          ; get error report
        call _callCriticalError                         ; switch context stack

        Goto CRITERROR_TERMINATE    , BlockedDevRequest_24
        Goto CRITERROR_IGNORE       , BlockedDevRequest_32
        Goto CRITERROR_FAIL         , BlockedDevRequest_28
        Goto CRITERROR_RETRY        , BlockedDevRequest_20
        jmp short BlockedDevRequest_28

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  retry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BlockedDevRequest_20:
        SaveAllRegisters
        xor ax, ax
        mov al, byte ptr [ _drive ][ bp ]               ; get drive letter
        call getDPB                                     ; check for drive change/ inv buffers
        RestoreAllRegisters                             ; restore registers
        jmp BlockedDevRequest_06

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  terminate application
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BlockedDevRequest_24:
        xor ax, ax
        call _TerminateProcess                          ; terminate curr process

        cli
        mov ax, -2
        add ax, word ptr ss:[ _RxDOS_StackLongJump ]
        mov sp, ax                                      ; cancel stack, return
        xor ax, ax
        sti
        ret                                             ; does a long jump out of RxDOS

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BlockedDevRequest_28:
        stc

BlockedDevRequest_32:
        getarg cx, _sectorsRead                         ; sectors read
        restoreSegments ax, bx, bp
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Init Request Block                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive (-1 if char device )                           ;
        ;   cx     bytes requested                                      ;
        ;   ss:di  request block offset on stack                        ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:bx  request block                                        ;
        ;...............................................................;

initReqBlock:

        push ax                                         ; drive
        cmp al, -1                                      ; character device ?
        jz initReqBlock_08                              ; yes, skip address -->
        call getAddrDPB                                 ; get address of Drive Paramater Block
        mov al, es:[ _dpbUnit ][ bx ]                   ; get unit 

initReqBlock_08:
        push ax                                         ; save unit
        push di

        setES ss        
        xor al, al
        mov cx, sizeMaxReqHeader
        rep stosb                                       ; init request block

        pop di
        mov al, sizeMaxReqHeader
        cmp ah, sizeDevDefaultLength
        jnc initReqBlock_12

        mov bl, ah
        xor bh, bh
        mov al, byte ptr ss:[ devDefaultLength ][ bx ]

initReqBlock_12:
        mov byte ptr es:[ rwrLength   ][ di ], al

        setES ss
        pop ax                                          ; unit
        cmp al, -1                                      ; character device ?
        jnz initReqBlock_16                             ; no -->
        xor al, al                                      ; set unit to 00

initReqBlock_16:
        mov byte ptr es:[ rwrUnit     ][ di ], al       ; set unit
        mov byte ptr es:[ rwrFunction ][ di ], ah       ; set function
        mov bx, di
        pop ax                                          ; restore drive
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  default length table
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
devDefaultLength:
        db sizeMaxReqHeader                             ; device init
        db sizeMEDIAReqHeader                           ; media request
        db sizeBUILDBPBReqHeader                        ; build dpb
        db sizeMaxReqHeader                             ; ioctl read
        db sizeREADReqHeader                            ; device read
        db sizeREADReqHeader                            ; non destr read
        db sizeMaxReqHeader                             ; input status
        db sizeMaxReqHeader                             ; input flush
        db sizeMaxReqHeader                             ; device write
        db sizeMaxReqHeader                             ; device write verify
        db sizeMaxReqHeader                             ; output status
        db sizeMaxReqHeader                             ; output flush
        db sizeMaxReqHeader                             ; ioctl write
        db sizeMaxReqHeader                             ; open device 
        db sizeMaxReqHeader                             ; close device 
        db sizeMaxReqHeader                             ; removable media
        db sizeMaxReqHeader                             ; output till busy
        db sizeMaxReqHeader                             ; generic ioctl
        db sizeMaxReqHeader                             ; get logical device
        db sizeMaxReqHeader                             ; set logical device
        db sizeMaxReqHeader                             ; ioctl query

sizeDevDefaultLength    equ ($ - devDefaultLength)

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Address of Device Parameter Block                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     drive                                                ;
        ;   es:bx  address of device parameter block for drive          ;
        ;...............................................................;

getAddrDPB:

        push ax
        push cx

        les bx, dword ptr ss:[ _RxDOS_pCDS ]            ; point to CDS
        and ax, 7fh                                     ; optimize for drive A:
        jz getAddrDPB_08                                ; if true -->

        mov cl, sizeCDS
        mul cl                                          ; create CDS offset
        add bx, ax

getAddrDPB_08:
        mov ax, word ptr es:[ _cdsPtrToDPB. _segment ][ bx ]
        or ax, word ptr es:[ _cdsPtrToDPB. _pointer ][ bx ]
        stc                                             ; set error if not intialized
        jz getAddrDPB_12                                ; not initialized -->

        clc
        les bx, dword ptr es:[ _cdsPtrToDPB ][ bx ]     ; else, pointer is valid.

getAddrDPB_12:
        pop cx
        pop ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Device Parameter Block                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     drive                                                ;
        ;   es:bx  address of device parameter block for drive          ;
        ;...............................................................;

getDPB:

        Entry
        def  _drive, ax
        ddef _dpbAddress
        defbytes _devVolumeId, sizeVolumeID

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if valid and initialized
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        saveAllRegisters
        call getAddrDPB
        jc getDPB_36                                    ; if device does not exist -->

        stordarg _dpbAddress, es, bx                    ; save valid address

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if removable media and disk changed
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getDPB_08:
        getarg ax, _drive                               ; restore drive
        getdarg es, bx, _dpbAddress                     ; get address of dpb
        mov ah, byte ptr es:[ _dpbMediaDescriptor ][ bx ]
        lea bx, _devVolumeId [ bp ]                     ; location to store new label id
        call DevMediaRequest                            ; rebuild Media ID if removable
        jc getDPB_24                                    ; if error, attempt initialize -->

        getdarg es, bx, _dpbAddress                     ; get address of dpb
        cmp byte ptr es:[ _dpbAccessFlag ][ bx ], -1    ; this block used before ?
        jz getDPB_24                                    ; no, so just initialize now -->

        cmp cl, MEDIA_HASCHANGED                        ; has media changed ?
        jnz getDPB_30                                   ; no, it has not changed -->

getDPB_10:
        cmp ch, byte ptr es:[ _dpbMediaDescriptor ][ bx ]
        jnz getDPB_24                                   ; if different media type -->

        mov ax, word ptr [ _devVolumeId. _Low  ][ bp ]
        cmp ax, word ptr es:[ _dpbVolumeId. _Low  ][ bx ]
        jnz getDPB_24                                   ; if different volume -->

        mov ax, word ptr [ _devVolumeId. _High ][ bp ]
        cmp ax, word ptr es:[ _dpbVolumeId. _High ][ bx ]
        jz getDPB_30                                    ; if same volume, then ok -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init drive parameters
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getDPB_24:
        getarg ax, _drive
        call invalidateBuffers                          ; invalidate buffers
        call initDriveParameters                        ; rebuild drive parameters

        getdarg es, bx, _dpbAddress                     ; get address of dpb
        mov word ptr es:[ _dpbFreeCount ][ bx ], -1     ; invalidate disk free space

        mov ax, word ptr [ _devVolumeId. _Low  ][ bp ]
        mov dx, word ptr [ _devVolumeId. _High ][ bp ]
        mov word ptr es:[ _dpbVolumeId. _Low  ][ bx ], ax
        mov word ptr es:[ _dpbVolumeId. _High ][ bx ], dx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getDPB_30:
        clc

getDPB_36:
        restoreAllRegisters
        getdarg es, bx, _dpbAddress                     ; get address of dpb
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Translate BIOS Parameter Block to DPB                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   arg    pointer to bios parameter block                      ;
        ;   arg    pointer to drive parameter block                     ;
        ;                                                               ;
        ;...............................................................;

DefineDPB:                                              ; aka BuildDPB:

        Entry 4
        darg  _biosParamBlock
        darg  _dosDiskParamBlock

        SaveSegments 

        getdarg ds, di, _biosParamBlock
        getdarg es, bx, _dosDiskParamBlock

        mov ax, word ptr [ _bpbBytesPerSector    ][ di ]
        mov  word ptr es:[ _dpbBytesPerSector    ][ bx ], ax

        mov ax, word ptr [ _bpbResSectors        ][ di ]
        mov  word ptr es:[ _dpbFirstFAT          ][ bx ], ax

        mov al, byte ptr [ _bpbNumCopiesFAT      ][ di ]
        mov  byte ptr es:[ _dpbNumCopiesFAT      ][ bx ], al

        mov al, byte ptr [ _bpbMediaDescriptor   ][ di ]
        mov  byte ptr es:[ _dpbMediaDescriptor   ][ bx ], al

        mov ax, word ptr [ _bpbSectorsPerFat     ][ di ]
        mov  word ptr es:[ _dpbSectorsPerFat     ][ bx ], ax

     ; compute shift and mask factors for sectors/cluster

        mov al, byte ptr [ _bpbSectorsPerCluster ][ di ] 

        push di
        push es
        currSegment es
        mov di, offset _bitShiftTable
        mov cx, sizeShiftTable
        repnz scasb

        sub cx, sizeShiftTable - 1
        neg cx                                          ; proper sense

        pop es
        mov  byte ptr es:[ _dpbClusterSizeShift  ][ bx ], cl

        dec al
        mov  byte ptr es:[ _dpbClusterSizeMask   ][ bx ], al

        pop di
        push cx                                         ; save shift

     ; compute reserved/ max sectors

        xor ax, ax
        mov al, byte ptr [ _bpbNumCopiesFAT ][ di ]
        mul word ptr [ _bpbSectorsPerFat     ][ di ]    ; ignore dx

        add ax, word ptr [ _bpbResSectors        ][ di ]
        mov  word ptr es:[ _dpbFirstDirSector    ][ bx ], ax

        xor dx, dx
        mov ax, word ptr [ _bpbMaxAllocRootDir   ][ di ]
        mov  word ptr es:[ _dpbMaxAllocRootDir   ][ bx ], ax

        mov cx, sizeDIRENTRY
        mul cx

        add ax, word ptr [ _bpbBytesPerSector    ][ di ]
        dec ax
        div word ptr [ _bpbBytesPerSector    ][ di ]    ;** convert to div 32

        add ax, word ptr es:[ _dpbFirstDirSector ][ bx ]
        mov  word ptr es:[ _dpbFirstDataSector   ][ bx ], ax

        xor dx, dx
        mov ax, word ptr [ _bpbMaxSectors        ][ di ]
        or ax, ax
        jnz DefineDPB_08                                ; value is legitimate
        mov dx, word ptr [ _bpbHugeSectors. _high ][ di ]
        mov ax, word ptr [ _bpbHugeSectors. _low  ][ di ]

DefineDPB_08:
        sub ax, word ptr es:[ _dpbFirstDataSector   ][ bx ]
        sbb dx, 0000
        pop cx                                          ; restore shift value

DefineDPB_12:
        or cx, cx
        jz DefineDPB_14
        shr dx, 1
        rcr ax, 1
        loop DefineDPB_12

DefineDPB_14:
      ; inc ax
        mov  word ptr es:[ _dpbMaxClusterNumber  ][ bx ], ax

        or ax, ax
        mov byte ptr es:[ _dpbAccessFlag ][ bx ], 00
        RestoreSegments
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Init Drive Parameters                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive to check                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     drive to check                                       ;
        ;   cy     if drive is invalid or contains no media             ;
        ;...............................................................;

initDriveParameters:

        Entry
        def   _drive, ax
        ddef  _dpbAddress
        defbytes reqBlock, sizeMaxReqHeader
        defbytes sectorBuffer, sizeSector

        call getAddrDPB                                 ; (es:bx) Device Parameter Block
        jc initDriveParameters_22                       ; if error -->
        stordarg _dpbAddress, es, bx                    ; save address

        mov word ptr es:[ _dpbFreeCount ][ bx ], -1

        mov dx, word ptr es:[ _dpbptrDeviceDriver. _pointer ][ bx ]
        or dx, word ptr es:[ _dpbptrDeviceDriver. _segment ][ bx ]
        jnz initDriveParameters_10

        call findInstalledBlockDevice
        jc initDriveParameters_22
        mov word ptr es:[ _dpbptrDeviceDriver. _pointer ][ bx ], dx
        mov word ptr es:[ _dpbptrDeviceDriver. _segment ][ bx ], cx

initDriveParameters_10:
        getarg ax, _drive                               ; restore drive
        mov ah, BUILDBPB
        lea di, offset reqBlock [ bp ]
        call initReqBlock                               ; initialize

        lea di, offset sectorBuffer [ bp ]
        mov word ptr es:[ bbrFATSector. _pointer ][ bx ], di
        mov word ptr es:[ bbrFATSector. _segment ][ bx ], ss

        getarg ax, _drive                               ; restore drive
        call BlockedDevRequest                          ; call blocked device
        jc initDriveParameters_22                       ; if error or wrong dev -->

        push word ptr es:[ bbrBPBAddress. _segment ][ bx ]
        push word ptr es:[ bbrBPBAddress. _pointer ][ bx ]
        push word ptr [ _dpbAddress. _segment ][ bp ]
        push word ptr [ _dpbAddress. _pointer ][ bp ]   ; address of DPB
        call DefineDPB                                  ; build DPB

initDriveParameters_22:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Find Drive from Unit Code                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     Unit code ( 0, 1, 80, 81, ...                        ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   al     Drive code                                           ;
        ;...............................................................;

_GetDriveCodeFromUnitCode:
        
        push es
        push si

        les si, dword ptr [ _RxDOS_pCDS ]
        mov cl, byte ptr [ _RxDOS_bLastDrive ]
        mov ch, 0

_GetDriveCode_08:
        mov dx, word ptr es:[ _cdsPtrToDPB. _pointer ][ si ]
        or dx, word ptr es:[ _cdsPtrToDPB. _segment ][ si ]
        jz _GetDriveCode_16

        push es
        push si
        les si, dword ptr es:[ _cdsPtrToDPB ][ si ]
        cmp al, byte ptr es:[ _dpbUnit  ][ si ]         ; unit 
        pop si
        pop es
        jnz _GetDriveCode_16

        mov al, byte ptr [ _RxDOS_bLastDrive ]
        sub al, cl                                      ; logical driver number
        clc
        jmp short _GetDriveCode_22

_GetDriveCode_16:
        add si, sizeCDS
        loop _GetDriveCode_08

        mov al, -1
        stc

_GetDriveCode_22:
        pop si
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Set Exit Code If Control-C                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     character                                            ;
        ;   dx     TRUE if end of file                                  ;
        ;   zr     if end of file                                       ;
        ;...............................................................;

SetExit_IfControlC:

        xor dx, dx                                      ; end of file flag is FALSE
        cmp ax, ControlZ                                ; control Z ?
        jz SetExit_IfControlC_06                        ; yes, exit -->
        cmp ax, ControlC                                ; control C ?
        jnz SetExit_IfControlC_08                       ; no -->
        mov byte ptr ss:[ _RxDOS_bCtrlBreakCheck ], TRUE; else set control break.

SetExit_IfControlC_06:
        mov dl, TRUE                                    ; else end of file is TRUE

SetExit_IfControlC_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Call User's Critical Error Handler                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:bx  pointer to current device header                     ;
        ;   ax     error code                                           ;
        ;...............................................................;

_callCriticalError:

        Entry
        ddef _devicereqblock, es, bx
        def  _errorCode, ax
        def  _allowed, 0000

        push word ptr cs:[ SDAInt24_SPSave. _segment ]
        push word ptr cs:[ SDAInt24_SPSave. _pointer ]

        push es
        push ds
        push bx
        push bp

    ; even if PSP == 0, we should display error message, so we'll change
    ; this code at a later date.

        mov dl, CRITERROR_FAIL
        cmp word ptr ss:[ _RxDOS_CurrentPSP ], 0000
        ifz _callCriticalError_32                       ; if no valid PSP -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set arguments for int 24 call
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        xor ah, ah                                      ; upper bits zero
        mov di, ax                                      ; set error code
        mov al, byte ptr es:[ rwrUnit ][ bx ]           ; unit
        mov ah, byte ptr cs:[ DevAllowedTable ][ di ]   ; get allowed bits
        mov byte ptr [ _allowed ][ bp ], ah             ; save value
        call _GetDriveCodeFromUnitCode                  ; drive code

        cmp di, sizeDevAllowedTable                     ; is error within device allowed errors ?
        jc _callCriticalError_06                        ; if ok -->
        mov ah, CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED

_callCriticalError_06:
        or ah, 110b                                     ; data area
        cmp byte ptr es:[ rwrFunction ][ bx ], DEVICEWRITE
        jz _callCriticalError_08
        cmp byte ptr es:[ rwrFunction ][ bx ], DEVICEWRITEVERIFY
        jnz _callCriticalError_10

_callCriticalError_08:
        or ah, 1                                        ; write function

_callCriticalError_10:
        mov bp, es                                      ; point to driver's header
        mov si, bx                                      ; es:bx --> bp:si

        mov bx, word ptr ss:[ _RxDOS_CurrentInstance ]  ; base address of current stack
        les bx, dword ptr ss:[ _pointer ][ bx ]

        cli
        mov word ptr cs:[ SDAInt24_SPSave. _segment ], ss
        mov word ptr cs:[ SDAInt24_SPSave. _pointer ], sp

        mov dx, es
        mov ss, dx
        mov sp, bx
        sub sp, sizeStackFrame                          ; adjust user stack ptr
        sti

        push word ptr es:[ _DataSegment ][ bx ]
        push word ptr es:[ _ExtraSegment ][ bx ]
        pop es
        pop ds

        Int intCRITICALERROR                            ; perform int 24h

        cli
        mov dl, al                                      ; save value returned
        mov ss, word ptr cs:[ SDAInt24_SPSave. _segment ]
        mov sp, word ptr cs:[ SDAInt24_SPSave. _pointer ]
        sti

;  fix-up returned codes if conflict between allowed and returned

        pop bp
        push bp
        cmp dl, CRITERROR_IGNORE                        ; ignore returned ?
        jnz _callCriticalError_22                       ; no -->
        test word ptr [ _allowed ][ bp ], CRITERROR_IGNOREALLOWED
        jnz _callCriticalError_32                       ; yes -->
        mov dl, CRITERROR_FAIL                          ; otherwise default to fail

_callCriticalError_22:
        cmp dl, CRITERROR_RETRY                         ; retry returned ?
        jnz _callCriticalError_24                       ; no -->
        test word ptr [ _allowed ][ bp ], CRITERROR_RETRYALLOWED
        jnz _callCriticalError_32                       ; yes -->
        mov dl, CRITERROR_FAIL                          ; otherwise default to fail

_callCriticalError_24:
        cmp dl, CRITERROR_FAIL                          ; fail returned ?
        jnz _callCriticalError_32                       ; no -->
        test word ptr [ _allowed ][ bp ], CRITERROR_FAILALLOWED
        jnz _callCriticalError_32                       ; yes -->
        mov dl, CRITERROR_TERMINATE                     ; otherwise default to terminate 

_callCriticalError_32:
        pop bp
        pop bx
        pop ds
        pop es

        pop word ptr cs:[ SDAInt24_SPSave. _pointer ]
        pop word ptr cs:[ SDAInt24_SPSave. _segment ]

        mov al, dl
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Allowed attributes for specific errors                       ;
        ;...............................................................;

DevAllowedTable:        

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrWriteProtectViol  

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrUnknownUnit       

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrDeviceNotReady    

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrUnknownCommand    

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrCRCerr            

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrBadDriveReq       

                db CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrSeekError         

                db CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrUnknownMedia      

                db CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrSectorNotFound    

                db CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrPrinterOutPaper   

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrWriteFault        

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrReadFault         

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrGeneralFailure    

                db CRITERROR_IGNOREALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrSharingViolation

                db CRITERROR_IGNOREALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrLockViolation

                db CRITERROR_IGNOREALLOWED + CRITERROR_RETRYALLOWED + CRITERROR_FAILALLOWED
                                                ; devErrInvalidDiskChange 

sizeDevAllowedTable     equ ($ - DevAllowedTable)

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Clock Tick Counter                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   dx:ax  clock ticks                                          ;
        ;                                                               ;
        ;...............................................................;

ClockTimer:

        push ds
        push bx

        xor bx, bx
        mov ds, bx
        mov ax, word ptr [ bx + 46Ch ]
        mov dx, word ptr [ bx + 46Eh ]

        pop bx
        pop ds
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Line Editor Control Block Init                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  pointer to editor control block                      ;
        ;   dx:ax  pointer to client buffer                             ;
        ;   cx     max characters                                       ;
        ;...............................................................;

_lineEditorInit:

        push ax
        push bx
        push cx
        push dx
        push di
        push es
        setES ss

        xor ax, ax
        mov cx, sizeLINEEDITOR
        rep stosb

        mov ax, 0F00h                                   ; read current mode
        int 10h                                         ; get page into BH

        mov ax, 0300h                                   ; get current cursor position
        int 10h                                         ; get page into BH

        pop es
        pop di
        mov byte ptr ss:[ editPhysCursorRow ][ di ], dh
        mov byte ptr ss:[ editPhysCursorCol ][ di ], dl

        pop dx
        pop cx
        pop bx
        pop ax

        mov word ptr ss:[ editBufPointer. _low  ][ di ], ax
        mov word ptr ss:[ editBufPointer. _high ][ di ], dx
        mov word ptr ss:[ editMaxBuffer         ][ di ], cx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Line Editor                                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ss:di  pointer to editor control block                      ;
        ;   ax     character to process                                 ;
        ;...............................................................;

_lineEditor:

        Entry
        def  _DisplayPage, 0000
        ddef _lineEditControlBlock, es, di

        push es
        push di
        push bx
        push ax

        les bx, dword ptr ss:[ editBufPointer ][ di ]
        cmp word ptr ss:[ editMaxBuffer ][ di ], 0001   ; just one character ?
        jnz _lineEditor_04                              ; even if NULL -->

        mov byte ptr es:[ bx ], al                      ; store character into buffer
        inc word ptr ss:[ editMaxAvail ][ di ]          ; character stored
        inc word ptr ss:[ editCursor   ][ di ]          ; 
        jmp _lineEditor_60                              ; exit -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  use line editor
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_lineEditor_04:
        mov ax, 0F00h
        int 10h                                         ; get current display page
        mov byte ptr [ _DisplayPage ][ bp ], bh

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  lookup character
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        pop ax
        push ax                                         ; restore character
        les bx, dword ptr ss:[ editBufPointer ][ di ]
        add bx, word ptr ss:[ editCursor ][ di ]
        test word ptr ss:[ editFlags ][ di ], editControlFlag
        jz _lineEditor_08                               ; not in control mode -->

        xor word ptr ss:[ editFlags ][ di ], editControlFlag

        cmp al, 4Bh                                     ; 4Bh 00h  Left
        ifz _editorLeft
        cmp al, 4Dh                                     ; 4Dh 00h  Right
        ifz _editorRight
        cmp al, 52h                                     ; 52h 00h  Insert
        ifz _editorInsert
        cmp al, 53h                                     ; 53h 00h  Delete
        ifz _editorDelete
        cmp al, 47h                                     ; 47h 00h  Home
        ifz _editorHome
        cmp al, 4Fh                                     ; 4Fh 00h  End
        ifz _editorEnd
        cmp al, 3Ch                                     ; 3Ch 00h  F2
        ifz _editorF2SearchTemplate
        cmp al, 3Dh                                     ; 3Dh 00h  F3
        ifz _editorF3CopyTemplate
        jmp _lineEditor_60

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  lookup character
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_lineEditor_08:
        cmp al, ' '
        jge _lineEditor_16                              ; if character ->

        or al, al
        ifz _editor_SetInControlMode                    ; if 00 -->
        cmp al, 'C'-40h
        ifz _editorControlC                             ; if ctrl-C (03) -->
        cmp al, 'H'-40h
        ifz _editorBackspaceDelete                      ; if bs/del (08) -->
        cmp al, 'I'-40h
        ifz _editorTab                                  ; if tab (09) -->
        cmp al, '['-40h
        ifz _editorEscape                               ; if escape (1B) -->
        cmp al, 'M'-40h
        ifz _editorCRLF                                 ; if cr (0D) -->
        cmp al, 'J'-40h
        ifz _editorCRLF                                 ; if lf (0A) -->

      ; goto  07h,  _editorBell

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  control-Z
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        cmp al, 'Z'-40h
        ifnz _lineEditor_60                             ; if not ctrl-Z (1A) -->

        mov bh, byte ptr [ _DisplayPage ][ bp ]
        call _lineEditor_displayCharacter               ; display ctrl-Z
        jmp _lineEditor_60                              ; exit if control character ->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  just store character
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_lineEditor_16:
        mov cx, word ptr ss:[ editMaxBuffer ][ di ]     ; max buffer
        cmp cx, word ptr ss:[ editMaxAvail  ][ di ]     ; characters so far 
        jl _lineEditor_60                               ; if can't insert -->

        test word ptr ss:[ editFlags ][ di ], editInsertFlag
        jz _lineEditor_52                               ; if not insert mode -->

        mov cx, word ptr ss:[ editMaxAvail ][ di ]      ; get max chars
        sub cx, word ptr ss:[ editCursor   ][ di ]      ; at max end of line ?
        jz _lineEditor_52                               ; yes, can't go right -->

        inc cx                                          ; incr cursor

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; if insert
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_lineEditor_20:
        push ax
        push bx
        push cx

        mov bh, byte ptr [ _DisplayPage ][ bp ]
        call _lineEditor_displayCharacter

        pop cx
        pop bx
        pop ax

        xchg al, byte ptr es:[ bx ]                     ; ripple insert character
        inc bx                                          ; point to next column 
        loop _lineEditor_20                             ; loop thru all characters -->

        inc word ptr ss:[ editCursor ][ di ]
        inc word ptr ss:[ editMaxAvail ][ di ]          ; cursor is max length
        jmp short _lineEditor_54                        ; display cursor

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; if insert
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_lineEditor_52:
        mov byte ptr es:[ bx ], al
        mov bh, byte ptr [ _DisplayPage ][ bp ]
        call _lineEditor_displayCharacter

        inc word ptr ss:[ editCursor    ][ di ]         ; increment cursor
        mov cx, word ptr ss:[ editCursor ][ di ]
        cmp cx, word ptr ss:[ editMaxAvail ][ di ]      ; is cursor past max length ?
        jc _lineEditor_54                               ; no -->
        mov word ptr ss:[ editMaxAvail ][ di ], cx      ; cursor is max length

_lineEditor_54:
        call _editorSetCursorPosition

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_lineEditor_60:
        pop ax
        pop bx
        pop di
        pop es
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Set Control Mode
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editor_SetInControlMode:
        or word ptr ss:[ editFlags ][ di ], editControlFlag
        jmp _lineEditor_60

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Insert
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorInsert:
        xor word ptr ss:[ editFlags  ][ di ], editInsertFlag
        jmp _lineEditor_60

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  CR/LF
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorCRLF:
        mov cx, word ptr ss:[ editMaxAvail  ][ di ]     ; get max chars
        cmp cx, word ptr ss:[ editMaxBuffer ][ di ]     ; at max end of line ?
        jge _editorHome                                 ; if can't insert -->

        mov bh, byte ptr [ _DisplayPage ][ bp ]
        mov ah, 0Eh                                     ; echo character in AL
        int 10h

      ; les bx, dword ptr ss:[ editBufPointer ][ di ]
      ; add bx, word ptr ss:[ editMaxAvail ][ di ]      ; get max chars
      ; inc word ptr ss:[ editMaxAvail ][ di ]          ; incr max chars
      ; mov byte ptr es:[ bx ], al                      ; save character

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Home
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorHome:
        mov word ptr ss:[ editCursor   ][ di ], 0000    ; set end pointer
        jmp _lineEditor_54

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  End
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorEnd:
        mov cx, word ptr ss:[ editMaxAvail ][ di ]      ; get max chars
        mov word ptr ss:[ editCursor   ][ di ], cx      ; set end pointer
        jmp _lineEditor_54

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Left
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorLeft:
        cmp word ptr ss:[ editCursor ][ di ], 0000      ; at start of line ?
        jz _editorLeft_08                               ; yes, can't go backwards -->
        dec word ptr ss:[ editCursor ][ di ]

_editorLeft_08:
        jmp _lineEditor_54

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Right
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorRight:
        mov cx, word ptr ss:[ editMaxAvail ][ di ]      ; get max chars
        cmp cx, word ptr ss:[ editCursor   ][ di ]      ; at max end of line ?
        jz _editorRight_08                              ; yes, can't go right -->
        inc word ptr ss:[ editCursor ][ di ]

_editorRight_08:
        jmp _lineEditor_54

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Backspace/ Delete
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorBackspaceDelete:
        cmp word ptr ss:[ editCursor ][ di ], 0000      ; at start of line ?
        jz _editorDelete_16                             ; yes, can't go backwards -->
        dec word ptr ss:[ editCursor ][ di ]            ; set cursor
        dec bx                                          ; set position
        call _editorSetCursorPosition                   ; redisplay cursor

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Delete
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorDelete:
        mov byte ptr es:[ bx ], ' '                     ; rubout echo space

        mov cx, word ptr ss:[ editMaxAvail ][ di ]      ; get max chars
        sub cx, word ptr ss:[ editCursor   ][ di ]      ; at max end of line ?
        dec cx                                          ; at end of buffer ?
        jle _editorDelete_14                            ; yes, echo rubout space -->

_editorDelete_06:
        push bx
        push cx
        mov ah, 0Eh
        mov al, byte ptr es:[ bx + 1 ]
        mov byte ptr es:[ bx ], al                      ; copy characters

        mov bh, byte ptr [ _DisplayPage ][ bp ]
        int 10h

        pop cx
        pop bx
        inc bx                                          ; point to next column 
        loop _editorDelete_06                           ; loop thru all characters -->

_editorDelete_14:
        dec word ptr ss:[ editMaxAvail ][ di ]          ; adjust max chars

        mov bh, byte ptr [ _DisplayPage ][ bp ]
        mov ax, 0E20h                                   ; else just update a space
        int 10h

_editorDelete_16:
        jmp _lineEditor_54

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Exit, No Text
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorControlC:
        mov word ptr ss:[ editMaxAvail ][ di ], 0000    ; cursor is max length

        les bx, dword ptr ss:[ editBufPointer ][ di ]
        mov byte ptr es:[ bx ], 00                      ; null out line

        mov ax, 0E5Eh
        mov bh, byte ptr [ _DisplayPage ][ bp ]
        int 10h                                         ; ^

        mov ax, 0E43h
        mov bh, byte ptr [ _DisplayPage ][ bp ]
        int 10h                                         ; C
        jmp _lineEditor_60                              ; exit -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Copy Template
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorF3CopyTemplate:
_editorF2SearchTemplate:
_editorTab:
        jmp _lineEditor_60

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Escape
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_editorEscape:
        mov word ptr ss:[ editCursor   ][ di ], 0000    ; set cursor
        call _editorSetCursorPosition                   ; position cursor at home

        cmp word ptr ss:[ editMaxAvail ][ di ], 0000    ; already at start of line ?
        jz _editorEscape_12                             ; yes -->

_editorEscape_08:
        mov bh, byte ptr [ _DisplayPage ][ bp ]
        mov ax, 0E20h
        int 10h                                         ; set cursor position

        dec word ptr ss:[ editMaxAvail ][ di ]          ; already at start of line ?
        jnz _editorEscape_08                            ; keep looping -->

_editorEscape_12:
        mov word ptr ss:[ editMaxAvail ][ di ], 0000    ; cursor is max length
        mov word ptr ss:[ editCursor   ][ di ], 0000    ; set cursor
        call _editorSetCursorPosition                   ; position cursor at home
        jmp _lineEditor_60

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Line Editor Display - Set Cursor Position                    ;
        ;...............................................................;

_editorSetCursorPosition:

        push bx
        mov dh, byte ptr ss:[ editPhysCursorRow ][ di ]
        mov dl, byte ptr ss:[ editPhysCursorCol ][ di ]
        add dl, byte ptr ss:[ editCursor        ][ di ]
        mov bh, byte ptr [ _DisplayPage ][ bp ]
        mov ax, 0200h
        int 10h                                         ; set cursor position

        pop bx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Line Editor Display Character                                ;
        ;...............................................................;

_lineEditor_displayCharacter:

        mov ah, 0Eh                                     ; echo character in AL
        cmp al, ' '                                     ; if control character, echo ^
        jge _lineEditorDisplayChar_12                   ; else just echo character

        push ax
        mov al, '^'
        int 10h
        pop ax                                          ; restore character
        add ax, 'A' - 1                                 ; make it a letter

_lineEditorDisplayChar_12:
        int 10h
        ret

RxDOS   ENDS
        END
