        TITLE   'rxdos - Copyright, 1990 1997 Api Software'
        PAGE 59, 132
        .LALL

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Real Time Dos                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This material  was created as a published version  of a DOS  ;
        ;  equivalent product.   This program  logically  functions in  ;
        ;  the same way as  MSDOS functions and it  is  internal  data  ;
        ;  structure compliant with MSDOS 6.0                           ;
        ;                                                               ;
        ;  This product is distributed  AS IS and contains no warranty  ;
        ;  whatsoever,   including  warranty  of   merchantability  or  ;
        ;  fitness for a particular purpose.                            ;
        ;                                                               ;
        ;                                                               ;
        ;  (c) Copyright 1990, 1997. Api Software and Mike Podanoffsky  ;
        ;      All Rights Reserved Worldwide.                           ;
        ;                                                               ;
        ;  This product is protected under copyright laws and  may not  ;
        ;  be reproduced  in whole  or in part, in any form  or media,  ;
        ;  included but not limited to source listing, facsimile, data  ;
        ;  transmission, cd-rom, or  floppy disk without the expressed  ;
        ;  written consent of the author.                               ;
        ;                                                               ;
        ;  License  for  distribution  for commercial  use  or  resale  ;
        ;  required from:                                               ;
        ;                                                               ;
        ;  Api Software                                                 ;
        ;  12 South Walker Street                                       ;
        ;  Lowell,  MA   01851                                          ;
        ;                                                               ;
        ;  internet: mikep@world.std.com                                ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  Compile with MASM 5.1                                        ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Comments                                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  DOS file system is divided into the following layers:        ;
        ;                                                               ;
        ;                                                               ;
        ;  Named File System                                            ;
        ;                                                               ;
        ;       Uses the handle or FCB for file requests based on file  ;
        ;       name and file position.  A file pointer navigates thru  ;
        ;       file.                                                   ;
        ;                                                               ;
        ;  Redirector                                                   ;
        ;                                                               ;
        ;       Passes file requests to a file redirector layer that    ;
        ;       handles any proprietary file system requests, special   ;
        ;       or networked file calls.                                ;
        ;                                                               ;
        ;  FAT File System                                              ;
        ;                                                               ;
        ;       Maps file requests to clusters using the cluster chain  ;
        ;       in the FAT (File Access Table).                         ; 
        ;                                                               ;
        ;  Device Driver                                                ;
        ;                                                               ;
        ;       Performs the actual disk (int 13) i/o.                  ;
        ;                                                               ;
        ;...............................................................;

        include rxdosmac.asm
        include rxdosdef.asm

        public RxDOS_start
        public DaysInMonthTable
        public AccumDaysPerMonthTable
        public sizeInvFnChars
        public sizeShiftTable
        public _bitShiftTable
        public _CallDOS
        public _mul32
        public _div32

        public _Interrupt_20
        public _Interrupt_21
        public _Interrupt_23
        public _Interrupt_24
        public _Interrupt_25
        public _Interrupt_26
        public _Interrupt_27
        public _Interrupt_28
        public _Interrupt_2a
        public _Interrupt_2f
        public _IRet

        public _invalidFnCharacters
        public _RetCallersStackFrame
        public stdDeviceAssignTable
        public _DebugInterruptTrap
        public _TerminateProcess

        public _RxDOS_CurrentSeg
        public _RxDOS_SharedBuffer
        public _RxDOS_UserCodePage
        public _RxDOS_SystemCodePage
        public _RxDOS_CurrCountryInfo

        public _RxDOS_AllocStrategy
        public _RxDOS_bCtrlBreakCheck
        public _RxDOS_bLastDrive
        public _RxDOS_bNumJoinDev
        public _RxDOS_BootDrive
        public _RxDOS_bSwitchChar
        public _RxDOS_BufferList
        public _RxDOS_Buffers
        public _RxDOS_CommandShell
        public _RxDOS_CurrentDrive
        public _RxDOS_CurrentInstance
        public _RxDOS_CurrentPSP
        public _RxDOS_data
        public _RxDOS_DOSProgramName
        public _RxDOS_DOSVersion
        public _RxDOS_ExtendedMem
        public _RxDOS_INDOSFlag
        public _RxDOS_StackLongJump
        public _RxDOS_MaxMemory
        public _RxDOS_nProtFCBs
        public _RxDOS_NULLDev
        public _RxDOS_pCDS
        public _RxDOS_pCLOCKdriver
        public _RxDOS_pCONdriver
        public _RxDOS_pDPB
        public _RxDOS_pDTA
        public _RxDOS_pFCBs
        public _RxDOS_pFT
        public _RxDOS_bNumBlockDev
        public _RxDOS_pStartMemBlock
        public _RxDOS_ShareRetry
        public _RxDOS_ShareDelay
        public _RxDOS_Verify
        public _RxDOS_wMaxBlock
        public _RxDOS_wSpecialNames
        public RxDOS_USA_DefaultUpperCaseFunction
        public _RxDOS_AbortInProgress

        public RxDOS_StackTop
        public RxDOS_StackTemp
        public RxDOS_StackProtect
        public _RxDOS_CurrentStackTop
        public _RxDOS_TabPosition

        public SDAInt24_SPSave
        public SDAFirstName
        public SDASecondName
        public SDApCurrentCDS
        public SDAExtendedSwapArea

        public pexterrInvalidFunction
        public pexterrFileNotFound
        public pexterrPathNotFound
        public pexterrIllegalName
        public pexterrNoHandlesAvailable
        public pexterrAccessDenied
        public pexterrInvalidHandle
        public pexterrArenaTrashed
        public pexterrNotEnoughMemory
        public pexterrInvalidBlock
        public pexterrInvalidAccess
        public pexterrInvalidDrive
        public pexterrCurrentDirectory
        public pexterrNoMoreFiles
        public pexterrFileExists

    ; defined in rxdosccb

        extrn CCBChanged                        : near
        extrn linkBegCCB                        : near
        extrn locateCCBPHeader                  : near
        extrn readBuffer                        : near
        extrn unlinkCCB                         : near
        extrn updateAllChangedCCBBuffers        : near
        extrn updateDriveBuffers                : near

    ; defined in rxdosdev

        extrn DefineDPB                         : near
        extrn getDPB                            : near
        extrn getSysDate                        : near
        extrn setSysDate                        : near
        extrn getExpandedDateTime               : near
        extrn getExpandedDate                   : near
        extrn DevRead                           : near
        extrn DevWrite                          : near
        extrn readConsoleIn                     : near
        extrn writeConsoleOut                   : near

        extrn initReqBlock                      : near
        extrn CharDevRequest                    : near
        extrn devCharRead                       : near
        extrn devCharReadLine                   : near

    ; defined in rxdosexe

        extrn loadProgram                       : near
        extrn copyCurrentPSP                    : near

    ; defined in rxdosfat

        extrn AllocateInitCluster               : near
        extrn AmountFreeSpace                   : near
        extrn ReleaseClusterChain               : near
        extrn _FATReadRandom                    : near

    ; defined in rxdosfcb

        extrn initFCBfromSFT                    : near
        extrn buildFindFromFCB                  : near
        extrn buildDTAfcbFind                   : near

    ; defined in rxdosfil

        extrn blankinitDirName                  : near
        extrn initdiskAccess                    : near
        extrn compareDirEntries                 : near
        extrn computeLogSectorNumber            : near
        extrn ExpandFileName                    : near
        extrn LocateFile                        : near
        extrn LocateFreeDirSlot                 : near
        extrn LocateFileByAttribute             : near

        extrn GetActualDrive                    : near
        extrn getCurrDirCluster                 : near
        extrn getDevice                         : near
        extrn getDrive                          : near
        extrn getWhereInDir                     : near

    ; defined in rxdosifs

        extrn Interrupt2F                       : far

    ; defined in rxdosini

        extrn RxDOS_initialize                  : near
        extrn LogTraceInt21Calls                : near

    ; defined in rxdosmem

        extrn _initializeMemoryBlock            : near
        extrn _collectMemoryBlocks              : near
        extrn _releaseOwnerMemoryBlocks         : near
        extrn _allocateUpperMB                  : near
        extrn _allocateConvMB                   : near
        extrn _modifyMemBlock                   : near

    ; defined in rxdossft

        extrn createSFTEntry                    : near
        extrn FindAvailableSFTHandle            : near
        extrn findmatchingFCBSFT                : near
        extrn FindSFTbyHandle                   : near
        extrn MapAppToSFTHandle                 : near
        extrn MapSFTtoAppHandle                 : near
        extrn releaseSFT                        : near
        extrn VerifyAvailableHandle             : near
        extrn TestIfMoveHandleTable             : near

        extrn _SFTReadLine                      : near
        extrn _SFTReadFile                      : near
        extrn _SFTWriteFile                     : near
        extrn _SFTOpenFile                      : near
        extrn _SFTCreateFile                    : near
        extrn _SFTCloseFile                     : near
        extrn _SFTCloseAllFiles                 : near
        extrn _SFTCommitFile                    : near

    ; defined in rxdosstr

        extrn CopyString                        : near
        extrn CopyBlock                         : near
        extrn convFCBNametoASCIZ                : near
        extrn convFilenametoFCBString           : near
        extrn getSysDateinDirFormat             : near
        extrn upperCase                         : near
        extrn lowerCase                         : near
        extrn __ascii_stosb                     : near
        extrn getMonthDayYear                   : near
        extrn getDaysSince1980                  : near
        extrn StringLength                      : near
        extrn condStringLength                  : near
        extrn getSystemDateValue                : near

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  DOS Data                                                     ;
        ;...............................................................;

RxDOS SEGMENT PARA PUBLIC 'CODE'
        assume cs:RxDOS, ds:RxDOS, es:RxDOS, ss:RxDOS

                        org 0000h

RxDOS_start:            jmp RxDOS_initialize
                        db 0
_RxDOS_SDALayoutStyle   db DOS5_SDASTYLE        ; DOS 4.0 + Style
_RxDOS_data             db 21 dup (0)           ; uninitialized

_RxDOS_ShareRetry       dw 0                    ; sharing retry count
_RxDOS_ShareDelay       dw 0                    ; sharing retry delay
_RxDOS_pDTA             dd 0                    ; ptr to current disk buffer (* violates m/tasking)
_RxDOS_UnreadCON        dw 0                    ; ptr to unread CON input
_RxDOS_pStartMemBlock   dw 0                    ; seg ptr to start of memory allocation

_RxDOS_pDPB             dd 0                    ; ptr to Drive Parameter Block (DPB)
_RxDOS_pFT              dd 0                    ; ptr to File Tables (FT)
_RxDOS_pCLOCKdriver     dd 0                    ; ptr to CLOCK$ device driver
_RxDOS_pCONdriver       dd 0                    ; ptr to CON device driver
_RxDOS_wMaxBlock        dw sizeSector           ; maximum bytes per block for any/all devices
_RxDOS_BufferList       dd 0                    ; pointer set for buffer list
_RxDOS_pCDS             dd 0                    ; ptr to array of current directory structures
_RxDOS_pFCBs            dd 0                    ; ptr to System FCB table
_RxDOS_nProtFCBs        dw 0                    ; number of protected fcbs (no longer supported)
_RxDOS_bNumBlockDev     db 0                    ; number of block devices
_RxDOS_bLastDrive       db 0                    ; lastdrive from config.sys

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  NULL Device Driver                                           ;
        ;...............................................................;

_RxDOS_NULLDev          dd -1                   ; link to other device
                        dw ( DEV_CHAR + DEV_NULL + DEV_FASTCHARIO )
                        dw null_strategy
                        dw null_interrupt
                        db 'NUL     '

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Installable File System Parameters                           ;
        ;...............................................................;

_RxDOS_bNumJoinDev      db 0                    ; number of JOIN'ed drives
_RxDOS_wSpecialNames    dw 0                    ; pointer to list of special names
_RxDOS_pSETVERList      dd 0                    ; pointer to SETVER program list
_RxDOS_wDOSHIGHOffset   dw 0                    ; DOS High offset A20 fix
                        dw 0                    ; DOS High PSP
_RxDOS_Buffers          dw 0                    ; BUFFERS x
_RxDOS_BuffersY         dw 0                    ; BUFFERS y (lookahead buffers)
_RxDOS_BootDrive        db 0                    ; Boot Drive (A: = 1, ...)
_RxDOS_MachineType      db 0                    ; 1 if 80386+, else 0   
_RxDOS_ExtendedMem      dw 0                    ; extended memory size in K

        ; the remainder of these arguments are not compatible with MS-DOS

_RxDOS_Verify           dw 0                    ; NonZero if Verify.
_RxDOS_AllocStrategy    dw 0                    ; Allocation strategy.
_RxDOS_bSwitchChar      db '/'                  ; Switch Char.
_RxDOS_MaxMemory        dw 0                    ; max memory
_RxDOS_NumFileHandles   dw sizePSPHandleTable   ; default
_RxDOS_Remainder        dw 0000                 ; div32 remainder.

_RxDOS_pExtErrorCode    dw 0                    ; Error code.
_RxDOS_ExtErrorFlag     dw 0                    ; flag if infor set
_RxDOS_ExtErrorInfo     db size ERROR dup(0)    ; extended error info
_RxDOS_AbortInProgress  dw 0                    ; if NZ, skip critical error

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Current PSP                                                  ;
        ;...............................................................;

                       even
_RxDOS_CurrentInstance  dw 0                    ; base address of current stack
_RxDOS_CurrentStackTop  dw RxDOS_StackTop       ; Reserved Stack Top ...
_RxDOS_CurrentStackBot  dw RxDOS_StackTop       ;       ... and Bottom.
_RxDOS_StackLongJump    dw 0                    ; long jump

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  UMB Control Status Word                                      ;
        ;...............................................................;

_RxDOS_UMBEnabled       dw 0                    ; set by init
_RxDOS_UMBAllowed       dw 0                    ; selectable by user

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Tab Expansion                                                ;
        ;...............................................................;

_RxDOS_TabPosition      dw 0                    ; tab position

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Country, Code Page, and Double Byte Character Set Info       ;
        ;...............................................................;

                       even
_RxDOS_UserCodePage     dw DEFAULT_CODEPAGE
_RxDOS_SystemCodePage   dw DEFAULT_CODEPAGE     ; system code page
_RxDOS_DBCS_Table       dw 0                    ; DBCS (NULL )

_RxDOS_UserCountryCode  dw DEFAULT_COUNTRYCODE  ; user set country
_RxDOS_SysCountryCode   dw DEFAULT_COUNTRYCODE  ; system country code
_RxDOS_SegCountryTables dw 0000                 ; if non-zero, country info table

_RxDOS_CurrCountryInfo  dw  DATE_USA 
                        db  '$', 0, 0, 0, 0     ; currency symbol (asciz)       5 bytes
                        dw  ','                 ; thousands separator           2 bytes
                        dw  '.'                 ; decimal separator             2 bytes
                        dw  '-'                 ; date separator                2 bytes
                        dw  ':'                 ; time separator                2 bytes
                        db  0                   ; currency format (before or after)
                        db  2                   ; places after decimal point
                        db  TIME_12HOUR         ; 12-hour or 24-hour format
                        dd RxDOS_USA_DefaultUpperCaseFunction                                
                        dw  ','                 ; data-list ...
                        db 10 dup(?)            ; reserved

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Command Shell                                                ;
        ;...............................................................;

                       even
_RxDOS_CommandShell     db 128 dup(?)
_RxDOS_SharedBuffer     db 128 dup (?)          ; shared buffer.

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Swappable Data Area                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Variables whose names begin with _RxDOS are used and will    ;
        ;  contain valid data. The remainder of the arguments are       ;
        ;  placeholders for compatibility reasons and are not used.     ;
        ;...............................................................;

SDABeginArea             equ $
_RxDOS_CritErrorFlag    db 0
_RxDOS_INDOSFlag        db 0                    ; INDOS flag.
_RxDOS_CritErrorDrive   db 0                    ; drive where crit error occurred
_RxDOS_LocusLasterror   db 0
_RxDOS_ExtErrorcode     dw 0
_RxDOS_SuggestedAction  db 0
_RxDOS_ClassOfError     db 0
SDApLastError           dd 0                    ; pointer for last error

SDACurrentDTA           dd 0                    ; current disk transfer address
_RxDOS_CurrentPSP       dw 0                    ; current PSP
SDASaveSP               dw 0                    ; SP across int 23h
_RxDOS_ChildReturnCode  dw 0                    ; Child return code.
_RxDOS_CurrentDrive     db 0                    ; current drive ( a=0, ... )
SDAExtendedBreakFlag    db 0                    ; extended break flag
SDAEndArea               equ $

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Extended Swappable Data Area
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_RxDOS_FunctionCall     dw 0                    ; AX on call
_RxDOS_PSPShare         dw 0
_RxDOS_MachineIDShare   dw 0

SDAFirstUsableAlloc     dw 0
SDABestUsableAlloc      dw 0
SDALastUsableAlloc      dw 0
SDAMemSizeinParas       dw 0
                        dw 0

SDAInt24_RetFailStatus  db 0
SDAInt24_AllowedActions db 0                    ; Abort, Retry, Fail bits
                        db 0
_RxDOS_bCtrlBreakCheck  dw 0                    ; Ctrl Break Flag.
                        db 0
SDADayOfMonth           db 0
SDAMonth                db 0
SDAYear                 dw 0                    ; year since 1980
SDADaysSince            dw 0                    ; since 1-1-1980
SDADayOfWeek            db 0                    ; 0 = Sunday

SDASFTValidPointer      db 0
SDAInt28_Safe           db 0
SDAInt24_Fail           db 0

SDADeviceDriverRequest  db 26 dup(0)
SDADeviceEntryPoint     dd 0
SDADeviceRequest01      db 22 dup(0)
SDADeviceRequest02      db 22 dup(0)

SDAPSPCopyType          dw 0                    ; not used by RxDOS
_RxDOS_UserSerialNumber db  0, 0, 0             ; User Identification
_RxDOS_DOSOEMVersion    db 94, 0, 0             ; OEM Version.

SDAClockTransfer        db 6 dup(0)             ; not used by RxDOS
SDATransferWord         dw 0                    ; not used

SDAFirstName            db sizeEXPANDNAME dup(0)
SDASecondName           db sizeEXPANDNAME dup(0)
SDAFindBlock            db 21 dup(0)
SDADirEntry             db 32 dup(0)
SDACDSCopy              db 81 dup(0)            ; not used by RxDOS
SDAFCBName              db 12 dup(0)            ; not used by RxDOS
SDAFCBRename            db 12 dup(0)            ; not used by RxDOS
                        db 8 dup(0)             ; not used by RxDOS

SDAExtendedAttrib       db 0                    ; not used by RxDOS
SDAFCBType              db 0                    ; not used by RxDOS
SDADirSearchAttrib      db 0                    ; not used by RxDOS
SDAFileOpenMode         db 0                    ; not used by RxDOS
                        dw 0, 0, 0              ; not used by RxDOS
SDAReadWriteFlag        db 0                    ; not used by RxDOS
SDADriveType            db 0                    ; not used by RxDOS
                        dw 0                    ; not used by RxDOS
SDALineEditInsert       db 0                    ; not used by RxDOS
SDAFileLocated          dw 0                    ; not used by RxDOS
SDATypeProcessTerm      dw 0                    ; not used by RxDOS
SDADeleteCode           db DIRENTRY_DELETED     ; not used by RxDOS

SDADPBPointer           dd 0                    ; not used by RxDOS
SDAUserStackFrame       dd 0                    ; not used by RxDOS
SDAInt24_SPSave         dd 0                    ; not used by RxDOS
                        dw 4 dup(0)             ; not used by RxDOS
SDAMediaID              dw 0                    ; not used by RxDOS
SDApCurrentDPB          dd 0                    ; not used by RxDOS
SDApCurrentSFT          dd 0                    ; not used by RxDOS
SDApCurrentCDS          dd 0                    ; not used by RxDOS
SDApCurrentFCB          dd 0                    ; not used by RxDOS
SDASFTHandle            dw 0, 0                 ; not used by RxDOS
SDApJobHandleTable      dd 0, 0                 ; not used by RxDOS
SDAPathNameLast         dw 0                    ; not used by RxDOS

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Stacks                                                       ;
        ;...............................................................;

                       even
RxDOS_StackProtect      dw 5E5Eh
                        dw 1024 dup(?)
RxDOS_StackTemp         dw 2048 dup(?)
RxDOS_StackTop          dw 5E5Eh

SDAExtendedSwapArea     equ $

        ; ** ROMABLE BEYOND THIS ADDRESS **

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Real Time Dos Product Identification                         ;
        ;...............................................................;

_RxDOS_DOSROMStatusFlag db ROM_IDENTIFICATION   ; ROM Version Status
_RxDOS_DOSVersion       db 6, 20                ; Dos Version (6.20)
_RxDOS_DOSProgramName   db 'RxDOS6.0'           ; Product Name.

_RxDOS_ContrlC_Message  db '^C', ControlM, ControlJ, 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Device Assign Table                                          ;
        ;...............................................................;

stdDeviceAssignTable:   StdRedirec  < 'AUX     ', -1, sftIsDevice >
                        StdRedirec  < 'CON     ', -1, sftIsDevice + sftIsstdout + sftIsstdin >
                        StdRedirec  < 'PRN     ', -1, sftIsDevice >
                        dw -1

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Code Segment Data                                            ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Days In Month Table                                          ;
        ;...............................................................;

DaysInMonthTable:       db 31                   ; Jan
                        db 28                   ; Feb
                        db 31                   ; Mar
                        db 30                   ; Apr
                        db 31                   ; May
                        db 30                   ; Jun
                        db 31                   ; Jul
                        db 31                   ; Aug
                        db 30                   ; Sep
                        db 31                   ; Oct
                        db 30                   ; Nov
                        db 31                   ; Dec

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Accumulated Days Per Month Table                             ;
        ;...............................................................;

AccumDaysPerMonthTable: dw 0                    ; Jan
                        dw 31                   ; Feb
                        dw 59                   ; Mar
                        dw 90                   ; Apr
                        dw 120                  ; May
                        dw 151                  ; Jun
                        dw 181                  ; Jul
                        dw 212                  ; Aug
                        dw 243                  ; Sep
                        dw 273                  ; Oct
                        dw 304                  ; Nov
                        dw 334                  ; Dec

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Bit Shift Table                                              ;
        ;...............................................................;

_bitShiftTable:         db 1                    ; 0
                        db 2                    ; 1
                        db 4                    ; 2
                        db 8                    ; 3
                        db 16                   ; 4
                        db 32                   ; 5
                        db 64                   ; 6
                        db 128                  ; 7

sizeShiftTable          equ ($ - _bitShiftTable)

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Invalid Characters in Filename                               ;
        ;...............................................................;

_invalidFnCharacters:   db '[]<>|",;=+:', 0
sizeInvFnChars          equ ($ - _invalidFnCharacters)

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Extended Error Tables                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  macro requires error code, locus, sug-action, class.         ;
        ;                                                               ;
        ;...............................................................;

pexterrInvalidFunction    EXTERRORCODE  <errInvalidFunction,          \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrFileNotFound       EXTERRORCODE  <errFileNotFound,             \
                                             ERRLOC_DISK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrPathNotFound       EXTERRORCODE  <errPathNotFound,             \
                                             ERRLOC_DISK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrIllegalName        EXTERRORCODE  <errIllegalName,              \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrNoHandlesAvailable EXTERRORCODE  <errNoHandlesAvailable,       \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_OUTRES >

pexterrAccessDenied       EXTERRORCODE  <errAccessDenied,             \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrInvalidHandle      EXTERRORCODE  <errInvalidHandle,            \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrArenaTrashed       EXTERRORCODE  <errArenaTrashed,             \
                                             ERRLOC_MEM, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrNotEnoughMemory    EXTERRORCODE  <errNotEnoughMemory,          \
                                             ERRLOC_MEM, ERRACT_RETRY, ERRCLASS_NOTFND >

pexterrInvalidBlock       EXTERRORCODE  <errInvalidBlock,             \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrInvalidAccess      EXTERRORCODE  <errInvalidAccess,            \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrInvalidDrive       EXTERRORCODE  <errInvalidDrive,             \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrCurrentDirectory   EXTERRORCODE  <errCurrentDirectory,         \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_AUTH   >

pexterrNoMoreFiles        EXTERRORCODE  <errNoMoreFiles,              \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_NOTFND >

pexterrFileExists         EXTERRORCODE  <errFileExists,               \
                                             ERRLOC_UNK, ERRACT_USER,  ERRCLASS_NOTFND >

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Real Time Dos                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;                                                               ;
        ;  Items marked by (*) are Undocumented DOS Functions.          ;
        ;...............................................................;

_RxDOS_functions:

        dw _TerminateProcess_00                 ; 00 -  Program terminate
        dw _KeyboardInput                       ; 01 -  Keyboard input
        dw _DisplayOutput                       ; 02 -  Display output
        dw _AuxInput                            ; 03 -  Aux input
        dw _AuxOutput                           ; 04 -  Aux output
        dw _PrinterOutput                       ; 05 -  Printer output
        dw _DirectConsole                       ; 06 -  Direct console
        dw _DirectConsoleInputNoEcho            ; 07 -  Direct console input noecho
        dw _ConsoleInputNoEcho                  ; 08 -  Console input noecho
        dw _DisplayString                       ; 09 -  Display string
        dw _BufferedKeyboardInput               ; 0A -  Buffered keyboard input
        dw _CheckKeyboardInput                  ; 0B -  Check keyboard input
        dw _ClearBufferedKeyboardInput          ; 0C -  Clear buffered keyboard input
        dw _DiskReset                           ; 0D -  Disk reset
        dw _SelectDisk                          ; 0E -  Select disk
        dw _OpenFileFCB                         ; 0F -  Open file FCB
        dw _CloseFileFCB                        ; 10 -  Close file FCB
        dw _SearchFirstFileFCB                  ; 11 -  Search first file FCB
        dw _SearchNextFileFCB                   ; 12 -  Search next file FCB
        dw _DeleteFileFCB                       ; 13 -  Delete file FCB
        dw _SeqReadFileFCB                      ; 14 -  Seq read file FCB
        dw _SeqWriteFileFCB                     ; 15 -  Seq write file FCB
        dw _CreateFileFCB                       ; 16 -  Create file FCB
        dw _RenameFileFCB                       ; 17 -  Rename file FCB
        dw _UnusedReturnInst                    ; 18 -  Unused
        dw _CurrentDisk                         ; 19 -  Current disk
        dw _SetDiskTransferAddress              ; 1A -  Set disk transfer address
        dw _GetDefaultDriveData                 ; 1B -  Get default drive data
        dw _GetDriveData                        ; 1C -  Get drive data
        dw _UnusedReturnInst                    ; 1D -  Unused
        dw _UnusedReturnInst                    ; 1E -  Unused
        dw _GetDefaultDriveParameterBlock       ; 1F -  Get default drive parameter block
        dw _UnusedReturnInst                    ; 20 -  Unused
        dw _ReadFileFCB                         ; 21 -  Read file FCB
        dw _WriteFileFCB                        ; 22 -  Write file FCB
        dw _FileSizeFCB                         ; 23 -  File size FCB
        dw _SetRelativeRecordFCB                ; 24 -  Set relative record FCB
        dw _SetInterruptVector                  ; 25 -  Set interrupt vector
        dw _CreateNewProgramSeg                 ; 26 -  Create new program seg
        dw _RandomBlockReadFCB                  ; 27 -  Random block read FCB
        dw _RandomBlockWriteFCB                 ; 28 -  Random block write FCB
        dw _ParseFilenameFCB                    ; 29 -  Parse filename FCB
        dw _GetDate                             ; 2A -  Get date 
        dw _SetDate                             ; 2B -  Set date
        dw _GetTime                             ; 2C -  Get time
        dw _SetTime                             ; 2D -  Set time
        dw _SetVerifySwitch                     ; 2E -  Set verify switch
        dw _GetDiskTransferAddress              ; 2F -  Get disk transfer address
        dw _GetDOSVersion                       ; 30 -  Get DOS version
        dw _TerminateStayResident               ; 31 -  Terminate stay resident
        dw _GetDriveParameterBlock              ; 32 -  Get drive parameter block
        dw _CtrlBreakCheck                      ; 33 -  Ctrl break check
        dw _GetInDOSFlagAddress                 ; 34 -  Get INDOS flag address
        dw _GetInterruptVector                  ; 35 -  Get interrupt vector
        dw _GetFreeDiskSpace                    ; 36 -  Get free disk space
        dw _GetSetSwitchChar                    ; 37 -  Get/set switch char
        dw _CountryDependentInfo                ; 38 -  Country dependent info
        dw _CreateSubdirectory                  ; 39 -  Create subdirectory
        dw _RemoveSubdirectory                  ; 3A -  Remove subdirectory
        dw _ChangeSubdirectory                  ; 3B -  Change subdirectory
        dw _CreateFile                          ; 3C -  Create file
        dw _OpenFile                            ; 3D -  Open file
        dw _CloseFile                           ; 3E -  Close file
        dw _ReadFile                            ; 3F -  Read file
        dw _WriteFile                           ; 40 -  Write file
        dw _DeleteFile                          ; 41 -  Delete file
        dw _MoveFilePointer                     ; 42 -  Move file pointer
        dw _ChangeFileMode                      ; 43 -  Change file mode
        dw _IoControl                           ; 44 -  Io Control
        dw _DuplicateFileHandle                 ; 45 -  Duplicate file handle
        dw _ForceFileHandle                     ; 46 -  Force file handle
        dw _GetCurrentDirectory                 ; 47 -  Get current directory
        dw _AllocateMemory                      ; 48 -  Allocate memory
        dw _FreeAllocatedMemory                 ; 49 -  Free allocated memory
        dw _ModifyAllocatedMemory               ; 4A -  Modify allocated memory
        dw _ExecuteProgram                      ; 4B -  ExecuteProgram
        dw _TerminateProcess                    ; 4C -  Terminate process
        dw _GetReturnCode                       ; 4D -  Get return code
        dw _FindFirstFile                       ; 4E -  Find first file
        dw _FindNextFile                        ; 4F -  Find next file
        dw _SetPSPAddress                       ; 50 -  Set PSP Address
        dw _GetPSPAddress                       ; 51 -  Get PSP Address
        dw _GetDosDataTablePtr                  ; 52*-  (Get DOS Data Table)
        dw _TranslateBIOSParameterBlock         ; 53*-  (Translate BIOS Parameter Block )
        dw _GetVerify                           ; 54 -  Get verify
        dw _DuplicatePSP                        ; 55*-  (Duplicate PSP block)
        dw _RenameFile                          ; 56 -  Rename file
        dw _SetFileDateTime                     ; 57 -  Set file date time
        dw _GetAllocationStrategy               ; 58 -  Get allocation strategy
        dw _GetExtendedError                    ; 59 -  Get extended error
        dw _CreateUniqueFile                    ; 5A -  Create unique file
        dw _CreateNewFile                       ; 5B -  Create new file
        dw _LockFileAccess                      ; 5C -  Lock file access
        dw _ServerShareAndSwap                  ; 5D*-  (Server, Share And Swap)
        dw _GetMachineName                      ; 5E -  Get machine name
        dw _GetRedirectionList                  ; 5F -  Get redirection list
        dw _GetActualFileName                   ; 60*-  (Get Actual FileName )
        dw _Unused                              ; 61 -  Unused
        dw _GetPSPAddress                       ; 62 -  Get PSP Address
        dw _GetDBCSString                       ; 63*-  (Get DBCS String)
        dw _Unused                              ; 64 -  Unused
        dw _ExtCountryDependentFunctions        ; 65 -  Extended Country Dependent Functions
        dw _GlobalCodePage                      ; 66 -  Get/Set Global Code Page
        dw _SetHandlesCount                     ; 67 -  Set Handles Count
        dw _CommitFile                          ; 68 -  Commit File
        dw _GetDiskSerialNumber                 ; 69*-  (Get disk serial number)
        dw _CommitFile                          ; 6A*-  (Commit File (same as 68))
        dw _Unused                              ; 6B -  Unused
        dw _ExtendedOpenCreate                  ; 6C -  Extended Open/ Create
        dw _Unused                              ; 6D -  DOS in ROM Functions
        dw _Unused                              ; 6E -  DOS in ROM Functions
        dw _Unused                              ; 6F -  DOS in ROM Functions
        dw _Unused                              ; 70 -  Unused or unknown
        dw _Unused                              ; 71 -  Chicago long filenames
        dw _Unused                              ; 72 -  Chicago long filenames
                
_RxDOS_functionsLast:
        dw _Unused                              ; *-  items: Undocumented DOS Functions

_RxDOS_maxFunctionCode = (_RxDOS_functionsLast - _RxDOS_functions)/2

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Critical Section Patch Area                                  ;
        ;...............................................................;

_RxDOS_CritSectPatches  dw 5 dup (0)            ; added later

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 21                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ah   contains function request                               ;
        ;...............................................................;

_Interrupt_21   proc far

        push es
        push ds
        push bp
        push di
        push si
        push dx
        push cx
        push bx
        push ax

        mov bp, sp
        add bp, sizeStackFrame
        and word ptr [ _Flags ][ bp ], 0fffeh           ; clear carry bit.

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  switch to internal stack
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cli
        setES ss                                        ; caller's stack
        mov bx, bp                                      ; caller's stack to es: bx

        mov ds, word ptr cs:[ _RxDOS_CurrentSeg ]       ; current segment.
        mov ss, word ptr cs:[ _RxDOS_CurrentSeg ]       ; current segment.
        mov sp, word ptr ss:[ _RxDOS_CurrentStackTop ]  ; point to current stack.

        push bx                                         ; caller's stack reference
        push es

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  current stack parameters
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        Entry                                           ; set [ bp ]
        def  _FctAddress

        push word ptr [ _RxDOS_CurrentStackTop ]
        sub word ptr [ _RxDOS_CurrentStackTop ], 384    ; reserve stack

        push word ptr [ _RxDOS_CurrentInstance ]        ; base address of current stack
        mov word ptr [ _RxDOS_CurrentInstance ], bp     ; base address of current stack

        push word ptr [ _RxDOS_INDOSFlag ]
        inc word ptr [ _RxDOS_INDOSFlag  ]              ; INDOS

        push word ptr [ _RxDOS_StackLongJump ]          ; long jump

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  save PSP values
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov dx, word ptr [ _RxDOS_CurrentPSP ]
        or dx, dx                                       ; was PSP zero ?
        jz _Interrupt_21_12                             ; if no valid PSP -->

        mov ds, dx                                      ; see if PSP was ever set
        mov word ptr ds:[ pspUserStack. _segment ], es
        mov word ptr ds:[ pspUserStack. _pointer ], bx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  determine function address
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Interrupt_21_12:
        sti
        cld
        currSegment ds                                  ; point to current segment
        mov word ptr [ _RxDOS_FunctionCall ], ax        ; AX on call

        cmp ah, _RxDOS_maxFunctionCode                  ; max function code ?
        jnc _Interrupt_21_48                            ; if out of range -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;
; [ds] from user call is passed in [es]
; [dx] from user call is passed in [di]
; [ds] is changed to current segment
; [ss] == [ds] is assumed 
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor bh, bh
        mov bl, ah                                      ; offset into functions table
        add bx, bx
        mov bx, word ptr [ _RxDOS_functions ][ bx ]     ;
        mov word ptr [ _FctAddress ][ bp ], bx          ; save dispatch address

        RetCallersStackFrame ds, bx
        mov es, word ptr [ _DataSegment][ bx ]          ; ds on call
        mov di, word ptr [ _DX     ][ bx ]              ; set di from DX
        mov bx, word ptr [ _BX     ][ bx ]              ; restore bx
        mov dx, di                                      ; and also passed in DI

        clc
        currSegment ds                                  ; point to current segment
        mov word ptr [ _RxDOS_StackLongJump ], sp       ; save stack long jump

 IFDEF RxDOS_TRACEBUILD
        call LogTraceInt21Calls                         ; trace build

 ENDIF

        call word ptr [ _FctAddress ][ bp ]             ; go do function
        jnc _Interrupt_21_48

_Interrupt_21_22:
        RetCallersStackFrame es, bx
        or  word ptr es:[ _Flags ][ bx ], 1             ; set carry bit

        mov si, ax                                      ; error pointer in AX
        cmp ax, -1                                      ; error an FCB error ?
        mov ah, byte ptr es:[ _AX. _AH ][ bx ]          ; retain ah in case fcb error
        jz _Interrupt_21_32                             ; yes -->

        mov word ptr ss:[ _RxDOS_pExtErrorCode ], si    ; save ptr to error code
        mov ax, word ptr cs:[ ExtErrorCodeValue ][ si ] ; get actual error value

_Interrupt_21_32:
        mov word ptr es:[ _AX ][ bx ], ax               ; get actual error code.

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return registers
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Interrupt_21_48:
        cli
        pop word ptr ss:[ _RxDOS_StackLongJump ]        ; long jump
        pop word ptr ss:[ _RxDOS_INDOSFlag       ]      ; restore INDOS
        pop word ptr ss:[ _RxDOS_CurrentInstance ]      ; _Instance
        pop word ptr ss:[ _RxDOS_CurrentStackTop ]

        pop bx                                          ; fct address
        pop bx                                          ; caller's stack pointer
        sub bx, sizeStackFrame                          ; adjusted stack pointer
        pop ss                                          ; caller's stack reference
        mov sp, bx

        pop ax                                          ; restore registers
        pop bx
        pop cx
        pop dx

        pop si
        pop di
        pop bp

        pop ds
        pop es
        iret
_Interrupt_21   endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Call DOS Interface                                           ;
        ;...............................................................;

_CallDOS        proc far

        mov ah, cl                                      ; old compatability
        Int21
        ret                                             ; far return

_CallDOS        endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 20                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Terminate Program                                            ;
        ;...............................................................;

_Interrupt_20   proc far

        Int21 TerminateProcess, 00
        iret                                            ; no return expected

_Interrupt_20   endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Where Data Segment Relocated                                 ;
        ;...............................................................;

_RxDOS_CurrentSeg       dw 0000                         ; Current Segment.

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  System Wide Functions                                        ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  NULL Device Driver                                           ;
        ;...............................................................;

null_interrupt  proc far

                mov word ptr es:[ rwrStatus ][ bx ], ( OP_DONE )
null_strategy:  ret

null_interrupt  endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 23 - Control Break Exit Address                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This is a default Int 23 handler.                            ;
        ;                                                               ;
        ;  Returns CY to cancel current application.                    ;
        ;                                                               ;
        ;...............................................................;

_Interrupt_23   proc far

        stc
        ret 2                                           ; returns to appl (iret w/o status)

_Interrupt_23   endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 24 - Critical Error Handler                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This is a default Int 24 handler.                            ;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   bp:si  driver's header                                      ;
        ;   di     error code                                           ;
        ;   al     drive code                                           ;
        ;   ah     allowed responses                                    ;
        ;            08h CRITERROR_IGNOREALLOWED                        ;
        ;            10h CRITERROR_RETRYALLOWED                         ;
        ;            20h CRITERROR_FAILALLOWED                          ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al     contains one of these values                         ;
        ;            00h CRITERROR_IGNORE                               ;
        ;            01h CRITERROR_RETRY                                ;
        ;            02h CRITERROR_TERMINATE                            ;
        ;            03h CRITERROR_FAIL                                 ;
        ;...............................................................;

_Interrupt_24   proc far

        push ds
        push es

        push cs
        pop ds

        push ax
        xchg ax, di
        mov ah, 0
        xchg di, ax                                     ; upper bits zero
        add di, di
        mov di, word ptr [ CriticalErrorMessages ][ di ]
        call _CritErrorDisplayNewLine

        pop ax
        push ax
        cmp al, -1                                      ; valid unit ?
        jz _Interrupt_24_08                             ; no drive -->

        mov di, offset perr_OnDrive
        call _CritErrorDisplayMessage

        pop ax
        push ax
        add al, 'A'
        int 29h

        mov al, ':'
        int 29h

_Interrupt_24_08:
        pop ax                                          ; abort, retry, fail, ...
        push ax                                         ;
        mov di, offset CritError_AbortRetryIgnoreFail
        test ah, CRITERROR_FAILALLOWED
        jnz _Interrupt_24_12

        mov di, offset CritError_AbortRetryIgnore

_Interrupt_24_12:
        call _CritErrorDisplayNewLine

        xor ax, ax
        int 16h

        push ax
        int 29h

        pop cx                                          ; character
        pop ax                                          ; allowed to ah
        push ax

        or cl, 20h                                      ; lower case
        cmp cl, "f"                                     ; Fail ?
        jnz _Interrupt_24_22                            ; if not fail -->
        mov dx, CRITERROR_FAIL
        test ah, CRITERROR_FAILALLOWED                  ; fail allowed ?
        jnz _Interrupt_24_36                            ; exit with fail -->

_Interrupt_24_22:
        cmp cl, "r"                                     ; Retry ?
        jnz _Interrupt_24_26                            ; if not retry -->
        mov dx, CRITERROR_RETRY
        test ah, CRITERROR_RETRYALLOWED                 ; retry allowed ?
        jnz _Interrupt_24_36                            ; exit with retry -->

_Interrupt_24_26:
        cmp cl, "i"                                     ; Ignore ?
        jnz _Interrupt_24_30                            ; if not ignore -->
        mov dx, CRITERROR_IGNORE
        test ah, CRITERROR_IGNOREALLOWED                ; ignore allowed ?
        jnz _Interrupt_24_36                            ; exit with ignore -->

_Interrupt_24_30:
        cmp cl, "a"                                     ; Abort ?
        jnz _Interrupt_24_08                            ; if not abort -->
        mov dx, CRITERROR_TERMINATE

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Interrupt_24_36:
        mov ax, dx                                      ; return value in ax
        pop dx                                          ; throw away allowed

        pop es
        pop ds

        clc                                             ; no exit flag
        ret 2                                           ; returns to appl (iret w/o status)
_Interrupt_24   endp

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Display New Line
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_CritErrorDisplayNewLine:

        push di
        mov al, ControlM
        int 29h
        mov al, ControlJ
        int 29h

        pop di
     ;  jmp short _CritErrorDisplayMessage

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Display Message
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_CritErrorDisplayMessage:

        mov al, byte ptr [ di ]
        or al, al
        jz _CritErrorDisp_08

        int 29h
        inc di
        jmp _CritErrorDisplayMessage

_CritErrorDisp_08:
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Abort, Retry, Ignore
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

CritError_AbortRetryIgnoreFail:
        db "Abort, Retry, Ignore, Fail ?", 0

CritError_AbortRetryIgnore:
        db "Abort, Retry, Ignore ?", 0

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Critical Error Messages
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

CriticalErrorMessages:
        dw perr_devErrWriteProtectViol
        dw perr_devErrUnknownUnit
        dw perr_devErrDeviceNotReady
        dw perr_devErrUnknownCommand
        dw perr_devErrCRCerr
        dw perr_devErrBadDriveReq
        dw perr_devErrSeekError
        dw perr_devErrUnknownMedia
        dw perr_devErrSectorNotFound
        dw perr_devErrPrinterOutPaper
        dw perr_devErrWriteFault
        dw perr_devErrReadFault
        dw perr_devErrGeneralFailure
        dw perr_devErrSharingViolation
        dw perr_devErrLockViolation
        dw perr_devErrInvalidDiskChange

perr_devErrWriteProtectViol:
        db "Error Write Protect", 0

perr_devErrUnknownUnit:
        db "Error Unknown Unit", 0

perr_devErrDeviceNotReady:
        db "Error Device Not Ready", 0

perr_devErrUnknownCommand:
        db "Error Unknown Command", 0

perr_devErrCRCerr:
        db "Error CRC Error", 0

perr_devErrBadDriveReq:
        db "Error Bad Drive Request", 0

perr_devErrSeekError:
        db "Error Seek Error", 0

perr_devErrUnknownMedia:
        db "Error Unknown Media", 0

perr_devErrSectorNotFound:
        db "Error Sector Not Found", 0

perr_devErrPrinterOutPaper:
        db "Error Printer Out Paper", 0

perr_devErrWriteFault:
        db "Error Write Fault", 0

perr_devErrReadFault:
        db "Error Read Fault", 0

perr_devErrGeneralFailure:
        db "Error General Failure", 0

perr_devErrSharingViolation:
        db "Error Sharing Violation", 0

perr_devErrLockViolation:
        db "Error Lock Violation", 0

perr_devErrInvalidDiskChange:
        db "Error Invalid Disk Change", 0

perr_OnDrive:
        db " On Drive ", 0

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 25 - Read From Disk                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Like MSDOS, the Int 25 and 26 definitions do not  allow  for ;
        ;  32 bit sector addressing.  These functions support only disk ;
        ;  drives up to 32 MByte storage.                               ;
        ;                                                               ;
        ;  To access > 32 MByte disks, use the device driver calls.     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   al     drive (0= A, ... )                                   ;
        ;   cx     sectors                                              ;
        ;   dx     physical disk sector address                         ;
        ;   ds:bx  buffer address                                       ;
        ;                                                               ;
        ;...............................................................;

_Interrupt_25   proc far

        saveSegments di, si, bp, dx, cx, bx

        push ds
        push bx
        pop di
        pop es                                          ; buffer to es: di

        mov bx, cx                                      ; sectors
        xor cx, cx                                      ; extended sector address
        cmp bx, -1                                      ; points to DISKIO struct ?
        jnz _Int25_06                                   ; no -->

        mov bx, word ptr [ diskioSectors            ][ di ]
        mov cx, word ptr [ diskioStartSector. _high ][ di ]
        mov dx, word ptr [ diskioStartSector. _low  ][ di ]
        mov es, word ptr [ diskioBuffer. _segment   ][ di ]
        mov di, word ptr [ diskioBuffer. _pointer   ][ di ]

_Int25_06:
        or bx, bx                                       ; nothing to read ?
        jz _Int25_08                                    ; if none -->

        call DevRead

_Int25_08:
        restoreSegments bx, cx, dx, bp, si, di
        retf

_Interrupt_25   endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 26 - Write To Disk                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Like MSDOS, the Int 25 and 26 definitions do not  allow  for ;
        ;  32 bit sector addressing.  These functions support only disk ;
        ;  drives up to 32 MByte storage.                               ;
        ;                                                               ;
        ;  To access > 32 MByte disks, use the device driver calls.     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   al     drive (0= A, ... )                                   ;
        ;   cx     sectors                                              ;
        ;   dx     physical disk sector address                         ;
        ;   ds:bx  buffer address                                       ;
        ;                                                               ;
        ;...............................................................;

_Interrupt_26   proc far

        saveSegments di, si, bp, dx, cx, bx

        push ds
        push bx
        pop di
        pop es                                          ; buffer to es: di

        mov bx, cx                                      ; sectors
        xor cx, cx                                      ; extended sector address
        cmp bx, -1                                      ; points to DISKIO struct ?
        jnz _Int26_06                                   ; no -->

        mov bx, word ptr [ diskioSectors            ][ di ]
        mov cx, word ptr [ diskioStartSector. _high ][ di ]
        mov dx, word ptr [ diskioStartSector. _low  ][ di ]
        mov es, word ptr [ diskioBuffer. _segment   ][ di ]
        mov di, word ptr [ diskioBuffer. _pointer   ][ di ]

_Int26_06:
        or bx, bx                                       ; nothing to write ?
        jz _Int26_08                                    ; if none -->
        call DevWrite

_Int26_08:
        restoreSegments bx, cx, dx, bp, si, di
        retf

_Interrupt_26   endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 27                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Terminate Stay Resident                                      ;
        ;...............................................................;

_Interrupt_27   proc far

        Int21 TerminateStayResident, 00                 ; no return code

_Interrupt_27   endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 28                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Idle Loop                                                    ;
        ;...............................................................;

_Interrupt_28   proc far

        iret
_Interrupt_28   endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 2f                                                 ;
        ;...............................................................;

_Interrupt_2a   proc far

        xor ax, ax
        iret
_Interrupt_2a   endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Interrupt 2f                                                 ;
        ;...............................................................;

_Interrupt_2f   proc far

        xor bx, bx
        iret
_Interrupt_2f   endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  All other interrupts                                         ;
        ;...............................................................;

_IRet           proc far

        iret
_IRet           endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  System Functions                                             ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Default USA Upper Case Routine
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   al     Contains character. Converted to Upper Case.         ;
        ;...............................................................;

RxDOS_USA_DefaultUpperCaseFunction      proc far

        cmp al, 128
        jc _USA_Default08

        push ds
        push bx
        xor bx, bx
        mov bl, al                                      ; pointer to 
        mov al, byte ptr cs:[ _RxDOS_USA_UpperCaseTable ][ bx ]
        pop bx
        pop ds

_USA_Default08:
        ret

RxDOS_USA_DefaultUpperCaseFunction      endp

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Default USA Upper Case Table                                 ;
        ;...............................................................;

_RxDOS_USA_UpperCaseTable:
                                ;  128, 129, 130, 131, 132, 133, 134, 135
                                db 128, 154, "E", "A", 143, "A", 143, 128

                                ;  136, 137, 138, 139, 140, 141, 142, 143
                                db "E", "E", "E", "I", "I", "I", 142, 143

                                ;  144, 145, 146, 147, 148, 149, 150, 151
                                db 144, 146, 146, "O", 153, "O", "U", "U"

                                ;  152, 153, 154, 155, 156, 157, 158, 159
                                db "Y", 153, 154, 155, 156, 157, 158, 159

                                ; 160, 161, 162, 163, 164, 165, 166, 167
                                db "A", "I", "O", "U", 164, 165, 166, 167

                                ;  168, 169, 170, 171, 172, 173, 174, 175
                                db 168, 169, 170, 171, 172, 173, 174, 175

                                ;  176, 177, 178, 179, 180, 181, 182, 183
                                db 176, 177, 178, 179, 180, 181, 182, 183

                                ;  184, 185, 186, 187, 188, 189, 190, 191
                                db 184, 185, 186, 187, 188, 189, 190, 191

                                ;  192, 193, 194, 195, 196, 197, 198, 199
                                db 192, 193, 194, 195, 196, 197, 198, 199

                                ;  200, 201, 202, 203, 204, 205, 206, 207
                                db 200, 201, 202, 203, 204, 205, 206, 207

                                ;  208, 209, 210, 211, 212, 213, 214, 215
                                db 208, 209, 210, 211, 212, 213, 214, 215

                                ;  216, 217, 218, 219, 220, 221, 222, 223
                                db 216, 217, 218, 219, 220, 221, 222, 223

                                ;  224, 225, 226, 227, 228, 229, 230, 231
                                db 224, 225, 226, 227, 228, 229, 230, 231

                                ;  232, 233, 234, 235, 236, 237, 238, 239
                                db 232, 233, 234, 235, 236, 237, 238, 239

                                ;  240, 241, 242, 243, 244, 245, 246, 247
                                db 240, 241, 242, 243, 244, 245, 246, 247

                                ;  248, 249, 250, 251, 252, 253, 254, 255
                                db 248, 249, 250, 251, 252, 253, 254, 255

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Current PSP                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es     PSP address                                          ;
        ;...............................................................;

getPSP:
        push ax
        mov ax, word ptr ss:[ _RxDOS_CurrentPSP ]
        or ax, ax
        jz getPSP_08

        mov es, ax

getPSP_08:
        pop ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Current Handle Table                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:bx  Handle Table                                         ;
        ;...............................................................;

getHandleTable:
        call getPSP
        jz getHandleTable_08
        les bx, dword ptr es:[ pspFileHandlePtr ]

getHandleTable_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  For Directory Functions, Change Error to 'Path Not Found'    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ax     contains error reference                             ;
        ;   cy     error flag retained                                  ;
        ;...............................................................;

_chgErrorToPathNotFound:

        jnc _chgtoPathError_14                          ; if no error -->

        pushf                                           ; save carry, status
        cmp ax, offset pexterrFileNotFound              ; file not found ?
        jnz _chgtoPathError_12                          ; no, ignore change -->
        mov ax, offset pexterrPathNotFound              ; else change error

_chgtoPathError_12:
        popf

_chgtoPathError_14:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Return Pointer to Caller's Stack Frame                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   returns on stack segment and offset                         ;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   call RetCallersStackFrame                                   ;
        ;   pop reg                                                     ;
        ;   pop seg                                                     ;
        ;...............................................................;

_RetCallersStackFrame:

        push bp
        mov bp, sp
        push word ptr [ bp + 2 ]                        ; create return stack space
        push word ptr [ bp ]
        push bx

        pushf                                           ; save interrupts
        cli
        mov bx, word ptr ss:[ _RxDOS_CurrentInstance ]  ; base address of current stack
        popf                                            ; restore interrupts

        push word ptr ss:[ _segment ][ bx ]
        pop word ptr [ bp + 2 ]
        push word ptr ss:[ _pointer ][ bx ]
        pop word ptr [ bp ]

        pop bx                                          ; restore registers
        pop bp
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Set FCB Error if Carry                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;   if carry, set AL to -1, else set AL to 0.                   ;
        ;...............................................................;

setFCBErrorIfCarry:

        pushf
        jnc setFCBErrorIfCarry_08                       ; if no error -->
        mov word ptr ss:[ _RxDOS_pExtErrorCode ], ax    ; save ptr to error code

setFCBErrorIfCarry_08:
        mov ax, 0000
        sbb ax, ax                                      ; nc -> 00; cy -> FF

        push ds
        push di
        RetCallersStackFrame ds, di
        mov byte ptr [ _AX. _AL ][ di ], al

        pop di
        pop ds
        popf
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  32 Bit Multiply                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   dx:ax  numerator                                            ;
        ;   cx     multiplier                                           ;
        ;                                                               ;
        ;...............................................................;

_mul32: or dx, dx                                       ; simple multiply ?
        jnz _mul32_12                                   ; not really -->

        mul cx
        ret

_mul32_12:
        push bx
        mov bx, ax
        mov ax, dx
        mul cx
        xchg ax, bx

        mul cx
        add dx, bx
        pop bx                                          ; restore bx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  32 Bit Divide                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   dx:ax  numerator                                            ;
        ;   cx     divisor                                              ;
        ;                                                               ;
        ;...............................................................;

_div32: or cx, cx                                       ; protect from zero divisor
        stc                                             ; in case of error
        jz _div32_return                                ; if so, just return with carry

        push bx
        mov bx, dx
        xchg ax, bx
        xor dx, dx
        div cx                                          ; divide high order first
      
        xchg ax, bx
        div cx                                          ; REMAINDER WILL BE IN DX
        mov word ptr ss:[ _RxDOS_Remainder ], dx        ; save remainder
        mov dx, bx                                      ; full 32-bit answer
        or bx, ax                                       ; set zr flag if result is zero
        pop bx

_div32_return:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Check if Keyboard Chars Available                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   zr     none available                                       ;
        ;   nz     available                                            ;
        ;                                                               ;
        ;   AL     00 if none, -1 if available                          ;
        ;...............................................................;

CheckKeyboardCharsAvailable:

        Entry
        defbytes reqBlock, sizeMaxReqHeader

        mov al, -1                                      ; character device
        mov ah, NONDESTRREAD
        lea di, offset reqBlock [ bp ]
        call initReqBlock

        mov ax, word ptr [ _RxDOS_pCONdriver. _segment ]
        or ax, word ptr [ _RxDOS_pCONdriver. _pointer ]
        jz checkKeybd_None                              ; if no CON driver -->

        push word ptr [ _RxDOS_pCONdriver. _segment ]
        push word ptr [ _RxDOS_pCONdriver. _pointer ]
        call CharDevRequest                             ; test for character

        mov al, 00                                      ; set character available
        test word ptr [ reqBlock. ndrStatus ][ bp ], OP_DONE
        jz checkKeybd_None                              ; if no character -->
        test word ptr [ reqBlock. ndrStatus ][ bp ], OP_BUSY
        jnz checkKeybd_None                             ; if no character -->

        mov al, -1                                      ; set character available

checkKeybd_None:
        or al, al
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Redirected Input                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     device handle                                        ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al     character from device                                ;
        ;...............................................................;

_RedirectedInput:

        Entry
        def      _handle, ax
        defbytes _tempbuffer, 8                         ; just need 2, but give a little.

        call CtrlC_CheckAlways                          ; control C ?
        mov word ptr _tempbuffer [ bp ], 0000           ; just place a null in case of error

        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _redirectedInput_22                          ; if sft NOT located -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  read from redirected file (SFT pointer at es:di)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call updateAllChangedCCBBuffers

        mov cx, 0001                                    ; bytes to read
        getarg ax, _handle                              ; 00 if stdin
        lea bx, offset _tempbuffer [ bp ]

        push ss                                         ; buffer pointer. _segment
        push bx                                         ; buffer pointer. _pointer
        call _SFTReadFile                               ; read using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_redirectedInput_22:
        xor ax, ax
        RetCallersStackFrame es, bx
        mov al, byte ptr _tempbuffer [ bp ]
        mov word ptr es:[ _AX ][ bx ], ax
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Redirected Output                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     device handle                                        ;
        ;   dl     character to display to standard output              ;
        ;...............................................................;

_RedirectedOutput:

        Entry
        defbytes _tempbuffer, 8                         ; just need 2, but give a little.

        mov byte ptr _tempbuffer [ bp ], dl             ; save character to display

        call CtrlC_CheckAlways                          ; control C ?

        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _redirectedOutput_22                         ; if sft NOT located -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  display to redirected file (SFT pointer at es:di)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov cx, 0001                                    ; bytes to write
        lea bx, offset _tempbuffer [ bp ]

        push ss                                         ; buffer pointer. _segment
        push bx                                         ; buffer pointer. _pointer
        call _SFTWriteFile                              ; write using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_redirectedOutput_22:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Control C Check                                              ;
        ;...............................................................;

CtrlC_Check:

        cmp byte ptr ss:[ _RxDOS_bCtrlBreakCheck ], 00  ; check for control C ?
        jnz CtrlC_CheckAlways                           ; if perform check -->
        clc                                             ; not terminate !
        ret                                             ; return

CtrlC_CheckAlways:
        Entry
        defbytes reqBlock, sizeMaxReqHeader
        defbytes _tempBuffer, 8

        SaveAllRegisters

        mov al, -1                                      ; character device
        mov ah, NONDESTRREAD
        lea di, offset reqBlock [ bp ]
        call initReqBlock

        mov dx, word ptr [ _RxDOS_pCONdriver. _segment ]
        or dx, word ptr [ _RxDOS_pCONdriver. _pointer ]
        jz CtrlC_Check18                                ; if no CON driver -->

        push word ptr [ _RxDOS_pCONdriver. _segment ]
        push word ptr [ _RxDOS_pCONdriver. _pointer ]
        call CharDevRequest                             ; test for character

        clc                                             ; clear carry
        test word ptr [ reqBlock. ndrStatus ][ bp ], OP_DONE
        jz CtrlC_Check18                                ; if no character -->
        test word ptr [ reqBlock. ndrStatus ][ bp ], OP_BUSY
        jnz CtrlC_Check18                               ; if no character -->

        mov al, byte ptr [ reqBlock. ndrCharRead ][ bp ]
        cmp al, 03                                      ; control C ?
        jz CtrlC_CheckCallBack                          ; if control Break -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit without terminate
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

CtrlC_Check18:
        clc                                             ; clear carry
        RestoreAllRegisters                             ; restore all
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  process control-c
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

CtrlC_CheckCallBack:
        setDS ss

        lea dx, offset _tempBuffer [ bp ]
        mov word ptr [ reqBlock.rwrBuffer. _segment ][ bp ], ss
        mov word ptr [ reqBlock.rwrBuffer. _pointer ][ bp ], dx
        mov word ptr [ reqBlock.rwrBytesReq         ][ bp ], 1
        mov byte ptr [ reqBlock.rwrFunction ][ bp ], DEVICEREAD

        push word ptr [ _RxDOS_pCONdriver. _segment ]
        push word ptr [ _RxDOS_pCONdriver. _pointer ]
        call CharDevRequest                             ; read ^C character 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  echo control-c 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setES cs
        mov cx, 4
        mov bx, offset _RxDOS_ContrlC_Message
        call writeConsoleOut
        
        cli
        mov bx, word ptr [ _RxDOS_CurrentInstance ]     ; base address of current user stack

        mov ax, word ptr [ bx - 4 ]                     ; stack top
        mov word ptr [ _RxDOS_CurrentStackTop ], ax
        mov ax, word ptr [ bx - 6 ]                     ; current instance
        mov word ptr [ _RxDOS_CurrentInstance ], ax
        mov ax, word ptr [ bx - 8 ]                     ; INDOS flag
        mov word ptr [ _RxDOS_INDOSFlag ], ax

        mov dx, word ptr [ _segment ][ bx ]             ; get seg address of user's stack
        mov ax, word ptr [ _pointer ][ bx ]             ; get ptr address of user's stack
        sub ax, sizeStackFrame                          ; adjusted stack pointer
        mov sp, ax
        mov ss, dx                                      ; switch stacks
        sti                                             ; restore interrupts
        nop                                             ; convenience for debugging

        pop ax                                          ; restore registers
        pop bx
        pop cx
        pop dx

        pop si
        pop di
        pop bp

        pop ds
        pop es

        int intCONTROLC                                 ; cause int 23 if Control C
        jnc CtrlC_CheckCallBack_08                      ; if not terminate -->
        mov ah, TerminateProcess                        ; terminate process

CtrlC_CheckCallBack_08:
        Int 21h                                         ; should never return

        stc                                             ; exit with carry
        retf 2                                          ; if it does, we left a return address

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  DOS Functions                                                ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  01h Keyboard Input (from redirected STDIN)                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al     character from standard input                        ;
        ;...............................................................;

_KeyboardInput:

        mov ax, STDIN                                   ; handle is stdin
        call _RedirectedInput                           ; byte saved in AL on return

        mov dl, al                                      ; set to echo character
        mov ax, STDOUT                                  ; handle
        jmp _RedirectedOutput

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  02h Display Output                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   dl     character to display to standard output              ;
        ;...............................................................;

_DisplayOutput:

        mov ax, STDOUT                                  ; handle
        jmp _RedirectedOutput

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  03h Aux (Comm Port) Input                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al     character from standard aux                          ;
        ;...............................................................;

_AuxInput:

        mov ax, STDAUX
        jmp _RedirectedInput

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  04h Aux (Comm Port) Output                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   dl     character from standard aux                          ;
        ;...............................................................;

_AuxOutput:

        mov ax, STDAUX
        jmp _RedirectedOutput

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  05h Prn (Printer) Output                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   dl     character from standard aux                          ;
        ;...............................................................;

_PrinterOutput:

        mov ax, STDPRN
        jmp _RedirectedOutput

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  06h Direct Console I/O                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   dl     = ff, read                                           ;
        ;          = anything else, writes dl character                 ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   dl     character from standard aux                          ;
        ;...............................................................;

_DirectConsole:

        cmp dl, -1                                      ; read ?
        jz _DirectConsoleInput                          ; yes -->

        mov ax, STDOUT                                  ; else output char in dl
        jmp _RedirectedOutput                           ; to stdout -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  input
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_DirectConsoleInput:

        mov ax, STDIN
        jmp _RedirectedInput

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  07h Unfiltered Console Input No Echo                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al     character from keyboard                              ;
        ;...............................................................;

_DirectConsoleInputNoEcho:

        mov ax, STDIN
        jmp _RedirectedInput

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  08h Direct Console Input No Echo                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al     character from keyboard                              ;
        ;...............................................................;

_ConsoleInputNoEcho:

        mov ax, STDIN
        jmp _RedirectedInput

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  09h Display String                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ds:dx  pointer to display string                            ;
        ;...............................................................;

_DisplayString:

        Entry
        ddef _sftPointer
        ddef _buffer, es, dx

        call CtrlC_CheckAlways                          ; look-ahead CtrlC Check

        mov ax, STDOUT                                  ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _displayOutput_22                            ; if sft NOT located -->
        stordarg _sftPointer, es, di                    ; save sft buffer pointer

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  display to redirected file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov al, '$'
        mov cx, -1
        les di, dword ptr [ _buffer ][ bp ]
        repnz scasb                                     ; compute length of string
        jnz _displayOutput_22                           ; strange, but exit if none -->

        neg cx
        sub cx, 2                                       ; remove $ from output
        push word ptr [ _buffer. _segment ][ bp ]
        push word ptr [ _buffer. _pointer ][ bp ]

        getdarg es, di, _sftPointer                     ; get sft buffer pointer
        call _SFTWriteFile                              ; write using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_displayOutput_22:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  0Ah Get Buffered Keyboard Input                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   es:dx  pointer to a buffer                                  ;
        ;          (lead byte contains max length)                      ;
        ;...............................................................;

_BufferedKeyboardInput:

        Entry
        ddef _buffer, es, dx
        def  _needsEcho, FALSE

        mov bx, dx
        mov byte ptr es:[ bufActualLength ][ bx ], 00   ; actual bytes read (none so far )

        call CtrlC_CheckAlways                          ; look-ahead CtrlC Check

        mov ax, STDIN                                   ; handle is stdin
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _buffkbdInput_Exit                           ; if sft NOT located -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  read from redirected file (SFT pointer at es:di)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call updateAllChangedCCBBuffers                 ; if stdin input, must flsuh buffers

        getdarg ds, bx, _buffer
        mov ch, 0 
        mov cl, byte ptr [ bufMaxLength ][ bx ]         ; max bytes
        mov byte ptr [ bufActualLength ][ bx ], 00      ; actual bytes read
        lea bx, offset [ bufData        ][ bx ]         ; pointer to actual data

        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jz _buffkbdInput_14                             ; if file -->

        push word ptr es:[ sftDCB. _segment ][ di ]
        push word ptr es:[ sftDCB. _pointer ][ di ]
        mov es, word ptr [ _buffer. _segment ][ bp ]    ; return buffer segment
        mov di, bx                                      ; buffer address
        call devCharReadLine                            ; read till cr or eof.
        mov ax, cx
        jmp short _buffkbdInput_16

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if read from file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_buffkbdInput_14:
        push ds                                         ; buffer pointer. _segment
        push bx                                         ; buffer pointer. _pointer
        call _SFTReadLine                               ; read using sft (at es:di )
        mov word ptr [ _needsEcho ][ bp ], TRUE
        
_buffkbdInput_16:
        getdarg es, bx, _buffer
        mov byte ptr es:[ bufActualLength ][ bx ], al   ; actual bytes read
        cmp ax, word ptr es:[ bufMaxLength ][ bx ]      ; at maximum buffer ?
        jge _buffkbdInput_22                            ; yes -->

        add bx, ax
        mov byte ptr es:[ bufData ][ bx ], ControlM     ; add cr to end
        inc ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  echo line to stdout
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_buffkbdInput_22:
        cmp word ptr [ _needsEcho ][ bp ], FALSE
        jz _buffkbdInput_Exit                           ; if no echo -->

        push ax                                         ; count
        mov ax, STDOUT                                  ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        pop cx
        jc _buffkbdInput_Exit                           ; if sft NOT located -->

        push word ptr [ _buffer. _segment ][ bp ]
        mov bx, word ptr [ _buffer. _pointer ][ bp ]
        lea bx, offset [ bufData ][ bx ]                ; pointer to actual data
        push bx 
        call _SFTWriteFile                              ; write using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_buffkbdInput_Exit:
        getdarg es, bx, _buffer
        mov al, byte ptr es:[ bufActualLength ][ bx ]   ; actual bytes read
        mov ah, 0
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  0Bh Check Keyboard Input                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   AL     = -1 if data available in STDIN                      ;
        ;...............................................................;

_CheckKeyboardInput:

        call CtrlC_CheckAlways                          ; look-ahead CtrlC Check

        mov ax, STDIN                                   ; handle is stdin
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )

        mov al, 00
        jc _checkkbdInput_22                            ; if sft not located -->

        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jz _checkkbdInput_22                            ; if not a keyboard device -->
        call CheckKeyboardCharsAvailable                ; set if character is available

_checkkbdInput_22:
        RetCallersStackFrame es, bx
        mov byte ptr es:[ _AX. _AL ][ bx ], al          ; set chars available
        or ax, ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  0Ch Clear buffered keyboard input                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   AL     function number                                      ;
        ;                                                               ;
        ;...............................................................;

_ClearBufferedKeyboardInput:

        push es
        push dx
        push ax                                         ; save service

        mov ax, STDIN                                   ; handle is stdin
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _ClrBuf_16                                   ; if sft not located -->

        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jz _ClrBuf_16                                   ; if not a keyboard device -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if characters are available
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_ClrBuf_14:
        call CheckKeyboardCharsAvailable
        jz _ClrBuf_16

        mov ax, STDIN                                   ; handle is stdin
        call _RedirectedInput                           ; read keyboard
        jmp _ClrBuf_14                                  ; loop until cleared buffer -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  dispatch to follow-up service
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_ClrBuf_16:
        pop ax
        pop dx
        pop es                                          ; restore registers

        cmp al, KeyboardInput
        ifz _KeyboardInput                              ; if function 01 -->
        cmp al, DirectConsole
        ifz _DirectConsole                              ; if function 06 -->
        cmp al, DirectConsoleInputNoEcho
        ifz _DirectConsoleInputNoEcho                   ; if function 07 -->
        cmp al, ConsoleInputNoEcho
        ifz _ConsoleInputNoEcho                         ; if function 08 -->
        cmp al, BufferedKeyboardInput
        ifz _BufferedKeyboardInput                      ; if function 0A -->

        RetCallersStackFrame es, bx
        mov byte ptr es:[ _AX. _AL ][ bx ], 00          ; else clear character
        or ax, ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  0Dh Reset Disk                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Update disk buffers.                                         ;
        ;                                                               ;
        ;...............................................................;

_DiskReset:

        call updateAllChangedCCBBuffers
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  0Eh Select Disk                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   dl     select drive (0 = A:, 1 = B:, ... )                  ;
        ;...............................................................;

_SelectDisk:

        RetCallersStackFrame es, bx
        mov al, byte ptr [ _RxDOS_bLastDrive ]          ; max logical drives
        mov byte ptr es:[ _AX. _AL ][ bx ], al          ; return max.

        cmp dl, al                                      ; greater than max drives ?
        jge _selectDisk_08                              ; yes, ignore change -->

        push dx
        xor ah, ah
        mov al, dl                                      ; drive letter
        call getDPB                                     ; see if valid drive
        pop dx                                          ; restore disk select
        jc _selectDisk_08                               ; if invalid drive -->

        cmp word ptr es:[ _dpbptrDeviceDriver. _segment ][ bx ], 0000
        stc
        jz _selectDisk_08                               ; if invalid drive -->

        mov byte ptr [ _RxDOS_CurrentDrive ], dl        ; else change drives.

_selectDisk_08:
        or ax, ax                                       ; no carry
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  0Fh Open File with FCB                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_OpenFileFCB:

        Entry
        def  _openMode
        ddef _fcbPointer, es, dx
        defbytes _expandedName, 128                     ; expanded file name (128 bytes )
        
        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  convert FCB name to name usable by SFT Open
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov si, dx                                      ; source FCB address
        lea di, offset _expandedName [ bp ]             ; expand name
        saveRegisters es, si, ss, di                    ; arguments
        call convFCBNametoASCIZ                         ; build asciz name

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  try SFT Open
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        setES ss                                        ; es:
        lea dx, offset _expandedName [ bp ]             ; es: dx filename
        mov al, DEFAULT_FCBOPENMODE                     ; mode is default open mode
        call _SFTOpenFile                               ; build an SFT
        jc _OpenFileFCB_26                              ; if error -->

        getdarg ds, si, _fcbPointer                     ; fcb address
        call initFCBfromSFT                             ; [ax] handle, [es:di] ptr to sft
        or ax, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_OpenFileFCB_26:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  10h Close FCB                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_CloseFileFCB:
        call findmatchingFCBSFT                         ; find matching SFT
        jc _CloseFileFCB_Error                          ; if can't locate, can't close -->

        call _SFTCloseFile
        or bx, bx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_CloseFileFCB_Error:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  11h Search First File FCB                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;    or, pointer to an extended FCB                             ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_SearchFirstFileFCB:

        Entry
        ddef _fcb, es, dx
        defbytes _findEntry, sizeFINDENTRY

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  build find entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        lea di, offset _findEntry [ bp ]
        push es
        push dx
        push ss
        push di
        xor dx, dx                                      ; begin at start of dir
        call buildFindFromFCB

        call LocateFileByAttribute                      ; locate item
        jc _SearchFirstFileFCB_20                       ; if not found -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  build a return entry in DTA
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        push word ptr [ _fcb. _segment ][ bp ]          ; fcb address
        push word ptr [ _fcb. _pointer ][ bp ]
        push es                                         ; find block
        push di
        call buildDTAfcbFind

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  all set, return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SearchFirstFileFCB_20:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  12h Search Next File FCB                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_SearchNextFileFCB:

        Entry
        ddef _fcb, es, dx
        defbytes _findEntry, sizeFINDENTRY

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  build find entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        lea di, offset _findEntry [ bp ]
        push es
        push dx
        push ss
        push di

        mov bx, dx
        mov dx, word ptr es:[ fcbCurrRecNo ][ bx ]
        inc dx                                          ; point to next entry
        call buildFindFromFCB

        call LocateFileByAttribute                      ; locate item
        jc _SearchNextFileFCB_08                        ; if none found -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  build a return entry in DTA
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        push word ptr [ _fcb. _segment ][ bp ]          ; fcb address
        push word ptr [ _fcb. _pointer ][ bp ]
        push es                                         ; find block
        push di
        call buildDTAfcbFind

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SearchNextFileFCB_08:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  13h Delete File FCB                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB, wild cards allowed                     ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_DeleteFileFCB:

        Entry
        ddef _fcb, es, dx
        def  _deleted, 0000
        defbytes _findEntry, sizeFINDENTRY

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  build find entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        lea di, offset _findEntry [ bp ]
        push es
        push dx
        push ss
        push di
        xor dx, dx                                      ; begin at start of dir
        call buildFindFromFCB

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  keep looping until all matching are deleted
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_deleteFileFCB_08:
        setES ss
        lea di, offset _findEntry [ bp ]
        call LocateFileByAttribute                      ; locate item
        jc _deleteFileFCB_20

        mov si, word ptr [ _findEntry. findCCBPointer ][ bp ]
        call locateCCBPHeader                           ; get buffers segment

        test byte ptr es:[ deAttributes ][ si ], ATTR_READONLY
        jnz _deleteFileFCB_08

        mov al, byte ptr [ _findEntry. findSrchDrive  ][ bp ]
        mov dx, word ptr es:[ deStartCluster  ][ si ]
        mov byte ptr es:[ si ], DIRENTRY_DELETED
        call ReleaseClusterChain                        ; release cluster chain
        call CCBChanged                                 ; release buffer at [es:di]

        inc word ptr [ _deleted ][ bp ]                 ; say some items deleted
        jmp _deleteFileFCB_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_deleteFileFCB_20:
        cmp word ptr [ _deleted ][ bp ], 0000           ; any deleted ?
        jg _deleteFileFCB_24                            ; yes -->
        stc                                             ; if none found.

_deleteFileFCB_24:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  14h Read Sequential/ FCB                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_SeqReadFileFCB:

        Entry
        def _readCount
        ddef _position
        ddef _fcbPointer, es, dx

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  ok to read if valid fcb
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov di, dx
        mov cx, word ptr es:[ fcbRecordSize ][ di ]
        mov word ptr [ _readCount ][ bp ], cx           ; bytes to read to dta
        or cx, cx
        stc
        jz _SeqReadFileFCB_Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute byte offset from block/rec nos
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, 128
        mul word ptr es:[ fcbCurrBlockNo ][ di ]        ; block # times 128
        add al, byte ptr es:[ fcbCurrRecNo   ][ di ]    ; full 23 bit address
        mul cx                                          ; actual byte offset
        stordarg _position, dx, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find corresponding sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call findmatchingFCBSFT                         ; find matching SFT
        jc _SeqReadFileFCB_Return                       ; if no space -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg cx, dx, _position                       ; restor position
        mov word ptr es:[ sftFilePosition. _low  ][ di ], dx
        mov word ptr es:[ sftFilePosition. _high ][ di ], cx
        mov ax, -1                                      ; ! stdin

        mov cx, word ptr [ _readCount ][ bp ]           ; bytes to read to dta
        push word ptr [ _RxDOS_pDTA. _segment ]         ; disk transfer address
        push word ptr [ _RxDOS_pDTA. _pointer ]
        call _SFTReadFile                               ; read using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute return logical position
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg es, di, _fcbPointer
        getdarg dx, ax, _position
        add ax, word ptr es:[ fcbRecordSize  ][ di ]
        adc dx, 0000
        div word ptr es:[ fcbRecordSize  ][ di ]        ; actual byte offset

        xor dx, dx
        mov cx, 128
        div cx
        mov byte ptr es:[ fcbCurrRecNo   ][ di ], dl
        mov word ptr es:[ fcbCurrBlockNo ][ di ], ax
        or ax, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SeqReadFileFCB_Return:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  15h Write Sequential/ FCB                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_SeqWriteFileFCB:

        Entry
        def _writeCount
        ddef _position
        ddef _fcbPointer, es, dx

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  ok to write if valid fcb
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov di, dx
        mov cx, word ptr es:[ fcbRecordSize ][ di ]
        mov word ptr [ _writeCount ][ bp ], cx          ; bytes to write from dta
        or cx, cx                                       ; if bytes are zero
        stc
        jz _SeqWriteFileFCB_Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute byte offset from block/rec nos
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, 128
        mul word ptr es:[ fcbCurrBlockNo ][ di ]        ; block # times 128
        add al, byte ptr es:[ fcbCurrRecNo ][ di ]      ; full 23 bit address
        mul cx                                          ; actual byte offset
        stordarg _position, dx, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find corresponding sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call findmatchingFCBSFT                         ; find matching SFT
        jc _SeqWriteFileFCB_Return                      ; if no space -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg cx, dx, _position                       ; restore position
        mov word ptr es:[ sftFilePosition. _low  ][ di ], dx
        mov word ptr es:[ sftFilePosition. _high ][ di ], cx

        getarg cx, _writeCount                          ; bytes to write to dta
        push word ptr [ _RxDOS_pDTA. _segment ]         ; disk transfer address
        push word ptr [ _RxDOS_pDTA. _pointer ]
        call _SFTWriteFile                              ; write using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute return logical position
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg es, di, _fcbPointer
        inc byte ptr es:[ fcbCurrRecNo   ][ di ]
        test byte ptr es:[ fcbCurrRecNo   ][ di ], 128
        jz _SeqWriteFileFCB_22
        inc word ptr es:[ fcbCurrBlockNo ][ di ]

_SeqWriteFileFCB_22:
        and byte ptr es:[ fcbCurrRecNo   ][ di ], 127

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SeqWriteFileFCB_Return:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  16h Create File with FCB                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_CreateFileFCB:

        Entry
        ddef _fcbPointer, es, dx
        defbytes _expandedName, 128                     ; expanded file name (128 bytes )
        
        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  convert FCB name to name usable by SFT Open
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov si, dx                                      ; source FCB address
        lea di, offset _expandedName [ bp ]             ; expand name
        saveRegisters es, si, ss, di                    ; arguments
        call convFCBNametoASCIZ                         ; build asciz name

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  try SFT Open
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        setES ss                                        ; es:
        lea dx, offset _expandedName [ bp ]             ; es: dx filename
        mov al, DEFAULT_FCBOPENMODE                     ; mode is default open mode
        xor cx, cx                                      ; expected attributes
        call _SFTCreateFile                             ; build an SFT
        jc _CreateFileFCB_26                            ; if error -->

        getdarg ds, si, _fcbPointer                     ; fcb address
        call initFCBfromSFT                             ; [ax] handle, [es:di] ptr to sft
        or ax, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_CreateFileFCB_26:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  17h Rename File FCB                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB, wild cards allowed                     ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_RenameFileFCB:

        Entry
        ddef _fcb, es, dx
        defbytes _expandedName, 128                     ; expanded file name (128 bytes )

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  build first name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        mov si, dx                                      ; source FCB address
        lea di, offset _expandedName [ bp ]             ; expand name
        saveRegisters es, si, ss, di                    ; arguments
        call convFCBNametoASCIZ                         ; build asciz name

        push es
        push di                                         ; unexpanded filename

        push ds
        mov di, offset SDAFirstName
        push di                                         ; where to expand name
        mov ax, FILECANNOT_BEDIRECTORY
        call ExpandFileName                             ; expand first name

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  build second name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        getdarg es, si, _fcb                            ; source FCB address
        add si, 16                                      ; point to second part of fcb
        lea di, offset _expandedName [ bp ]             ; expand name
        saveRegisters es, si, ss, di                    ; arguments
        call convFCBNametoASCIZ                         ; build asciz name

        push es
        push di                                         ; unexpanded filename

        push ds
        mov di, offset SDASecondName
        push di                                         ; where to expand name
        mov ax, FILECANNOT_BEDIRECTORY
        call ExpandFileName                             ; expand second name

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Use IFS to rename
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        
        IFS IFSRenameFile                               ; rename file

        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  19h Current Disk                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al     current disk returned.                                ;
        ;...............................................................;

_CurrentDisk:

        RetCallersStackFrame es, bx
        mov al, byte ptr [ _RxDOS_CurrentDrive ]
        mov byte ptr es:[ _AX._AL ][ bx ], al           ; set max for return
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  1Ah Set Disk Transfer Address                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx   Disk Transfer Address                                ;
        ;                                                               ;
        ;  (see related function 2F - Get Disk Transfer Address)        ;
        ;...............................................................;

_SetDiskTransferAddress:

        mov word ptr [ _RxDOS_pDTA. _pointer ], dx
        mov word ptr [ _RxDOS_pDTA. _segment ], es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  1Bh Get Default Drive Data                                   ;
        ;...............................................................;

_GetDefaultDriveData:

        xor dx, dx                                      ; set current drive
        ;  jmp short _GetDriveData

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  1Ch Get Drive Data                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  dl    drive                                                  ;
        ;...............................................................;

_GetDriveData:

        push ds
        push dx                                         ; (argument expected on stack)
        call GetActualDrive                             ; actual drive (in dx)
        jc _getDriveData_20                             ; if invalid drive -->

        call getDPB                                     ; get pointer to DPB
        jc _getDriveData_20                             ; if invalid drive -->

        RetCallersStackFrame ds, si
        mov cx, word ptr es:[ _dpbBytesPerSector   ][ bx ]
        mov dx, word ptr es:[ _dpbMaxClusterNumber ][ bx ]
        mov al, byte ptr es:[ _dpbClusterSizeMask  ][ bx ]
        inc al

        mov byte ptr [ _AX._AL ][ si ], al              ; sectors/cluster
        mov word ptr [ _CX     ][ si ], cx              ; bytes per sector
        mov word ptr [ _DX     ][ si ], dx              ; clusters/drive

        lea di, offset _dpbMediaDescriptor [ bx ]
        mov word ptr [ _BX     ][ si ], di
        mov word ptr [ _DataSegment ][ si ], es         ; where to find media byte
        or ax, ax                                       ; clear carry

_getDriveData_20:
        jnc _getDriveData_22
        mov al, -1

_getDriveData_22:
        pop ds
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  1Fh Get Dos Default Drive Device Parameter Block             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   no parameters                                               ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al     0ffh if invalid drive                                ;
        ;   ds:bx  returns pointer to dos device parameter block        ;
        ;                                                               ;
        ;  (see related function 32 - Get Dos Device Parameter Block)   ;
        ;...............................................................;

_GetDefaultDriveParameterBlock:

        xor dx, dx                                      ; select default drive
        jmp _GetDriveParameterBlock

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  21h Read Sequential/ FCB                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_ReadFileFCB:

        Entry
        def  _readCount
        ddef _position
        ddef _fcbPointer, es, dx

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  ok to read if valid fcb
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov di, dx
        mov cx, word ptr es:[ fcbRecordSize ][ di ]
        mov word ptr [ _readCount ][ bp ], cx           ; bytes to read to dta
        or cx, cx
        stc
        jz _ReadFileFCB_Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute byte offset from block/rec nos
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, word ptr es:[ fcbRandomRecNo. _low  ][ di ]
        mov dx, word ptr es:[ fcbRandomRecNo. _high ][ di ]
        mov cx, word ptr es:[ fcbRecordSize  ][ di ]
        call _mul32
        stordarg _position, dx, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find corresponding sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call findmatchingFCBSFT                         ; find matching SFT
        jc _ReadFileFCB_Return                          ; if no space -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg cx, dx, _position                       ; restore position
        mov word ptr es:[ sftFilePosition. _low  ][ di ], dx
        mov word ptr es:[ sftFilePosition. _high ][ di ], cx
        mov ax, -1                                      ; ! stdin

        mov cx, word ptr [ _readCount ][ bp ]           ; bytes to read to dta
        push word ptr [ _RxDOS_pDTA. _segment ]         ; disk transfer address
        push word ptr [ _RxDOS_pDTA. _pointer ]
        call _SFTReadFile                               ; read using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute return logical position
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg es, di, _fcbPointer
        mov ax, word ptr es:[ fcbRandomRecNo. _low  ][ di ]
        mov dx, word ptr es:[ fcbRandomRecNo. _high ][ di ]
        mov cx, 128
        call _div32
        mov word ptr es:[ fcbCurrBlockNo ][ di ], ax

        mov dl, byte ptr es:[ fcbRandomRecNo. _low  ][ di ]
        and dl, 127
        mov byte ptr es:[ fcbCurrRecNo   ][ di ], dl

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ReadFileFCB_Return:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  22h Write Sequential/ FCB                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_WriteFileFCB:

        Entry
        def  _writeCount
        ddef _position
        ddef _fcbPointer, es, dx

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  ok to Write if valid fcb
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov di, dx
        mov cx, word ptr es:[ fcbRecordSize ][ di ]
        mov word ptr [ _writeCount ][ bp ], cx          ; bytes to write to dta
        or cx, cx
        stc
        jz _WriteFileFCB_Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute byte offset from block/rec nos
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, word ptr es:[ fcbRandomRecNo. _low  ][ di ]
        mov dx, word ptr es:[ fcbRandomRecNo. _high ][ di ]
        mov cx, word ptr es:[ fcbRecordSize  ][ di ]
        call _mul32
        stordarg _position, dx, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute block/ sector
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, word ptr es:[ fcbRandomRecNo. _low  ][ di ]
        mov dx, word ptr es:[ fcbRandomRecNo. _high ][ di ]
        mov cx, 128
        call _div32
        mov word ptr es:[ fcbCurrBlockNo ][ di ], ax

        mov dl, byte ptr es:[ fcbRandomRecNo. _low  ][ di ]
        and dl, 127
        mov byte ptr es:[ fcbCurrRecNo   ][ di ], dl

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find corresponding sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call findmatchingFCBSFT                         ; find matching SFT
        jc _WriteFileFCB_Return                         ; if no space -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg cx, dx, _position                       ; restore position
        mov word ptr es:[ sftFilePosition. _low  ][ di ], dx
        mov word ptr es:[ sftFilePosition. _high ][ di ], cx

        mov cx, word ptr [ _writeCount ][ bp ]          ; bytes to write to dta
        push word ptr [ _RxDOS_pDTA. _segment ]         ; disk transfer address
        push word ptr [ _RxDOS_pDTA. _pointer ]
        call _SFTWriteFile                              ; write using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_WriteFileFCB_Return:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  23h Get File Size FCB                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;...............................................................;

_FileSizeFCB:

        mov di, dx
        cmp word ptr es:[ fcbRecordSize      ][ di ], 0000
        stc
        jz _FileSizeFCB_12

        mov ax, word ptr es:[ fcbFileSize. _low  ][ di ]
        mov dx, word ptr es:[ fcbFileSize. _high ][ di ]
        mov cx, word ptr es:[ fcbRecordSize      ][ di ]
        call _div32                                     ; [ax:dx] / [cx]

        mov word ptr es:[ fcbRandomRecNo. _low   ][ di ], ax
        mov word ptr es:[ fcbRandomRecNo. _high  ][ di ], dx
        or ax, ax

_FileSizeFCB_12:
        call setFCBErrorIfCarry
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  24h Set Random Record Number FCB                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to FCB                                         ;
        ;...............................................................;

_SetRelativeRecordFCB:

        mov di, dx
        mov ax, 128                                     ; records/block
        mul word ptr es:[ fcbCurrBlockNo ][ di ]        ; block # times record address
        add al, byte ptr es:[ fcbCurrRecNo   ][ di ]    ; full 23 bit address

        mov word ptr es:[ fcbRandomRecNo. _low   ][ di ], ax
        mov word ptr es:[ fcbRandomRecNo. _high  ][ di ], dx

        RetCallersStackFrame es, bx
        mov byte ptr es:[ _AX. _AL ][ bx ], 00          ; al always set to 00.
        clc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  25h Set Interrupt Vector                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al     interrupt number                                      ;
        ;  ds:dx  interrupt routine address                             ;
        ;...............................................................;

_SetInterruptVector:

        pushf                                           ; save interrupts
        push ds
        push ax
        push bx
        push dx                                         ; address to set
        push es                                         ; user's [ds] at [es] inside code
        mov ah, 0
        mov bx, ax                                      ; int vector to [bx]
        add bx, bx
        add bx, bx
        cli

        xor ax, ax
        mov ds, ax
        pop word ptr [ bx. _segment ]                   ; segment
        pop word ptr [ bx. _pointer ]                   ; address

        pop bx
        pop ax
        pop ds
        popf                                            ; restore interrupts
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  26h Create New Program Segment Prefix                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  dx      segment address                                      ;
        ;                                                               ;
        ;  (see related function 55 - Duplicate PSP )                   ;
        ;...............................................................;

_CreateNewProgramSeg:

        mov es, dx                                      ; new PSP segment address
        call copyCurrentPSP                             ; create a new PSP here
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  27h Random Read / FCB                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  cx      number of blocks to read                             ;
        ;  ds:dx   pointer to FCB                                       ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_RandomBlockReadFCB:

        Entry
        def  _error, 0000
        def  _readCount
        def  _numBlocks, cx
        ddef _position
        ddef _fcbPointer, es, dx

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  ok to read if valid fcb
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov di, dx
        mov cx, word ptr es:[ fcbRecordSize ][ di ]
        mov word ptr [ _readCount ][ bp ], cx           ; bytes to read to dta
        or cx, cx
        stc
        jz _ReadBlockFCB_Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute byte offset from block/rec nos
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, word ptr es:[ fcbRandomRecNo. _low  ][ di ]
        mov dx, word ptr es:[ fcbRandomRecNo. _high ][ di ]
        mov cx, word ptr es:[ fcbRecordSize  ][ di ]
        call _mul32
        stordarg _position, dx, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find corresponding sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call findmatchingFCBSFT                         ; find matching SFT
        jc _ReadBlockFCB_Return                         ; if no space -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg cx, dx, _position                       ; restor position
        mov word ptr es:[ sftFilePosition. _low  ][ di ], dx
        mov word ptr es:[ sftFilePosition. _high ][ di ], cx

        mov ax, word ptr [ _numBlocks ][ bp ]           ; number of blocks
        mul word ptr [ _readCount ][ bp ]               ; bytes to read to dta
        mov cx, ax                                      ; total bytes to read
        or dx, dx                                       ; over 64k ?
        jz _ReadBlockFCB_12                             ; if ok -->
        mov word ptr [ _error ][ bp ], errFCBSizeTooLarge ; too large
        mov cx, 0FFFFh                                  ; read as much as possible

_ReadBlockFCB_12:
        mov ax, -1                                      ; ! stdin
        push word ptr [ _RxDOS_pDTA. _segment ]         ; disk transfer address
        push word ptr [ _RxDOS_pDTA. _pointer ]
        call _SFTReadFile                               ; read using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute return logical position
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg es, di, _fcbPointer
        getdarg dx, ax, _position
        add ax, word ptr es:[ fcbRecordSize  ][ di ]
        adc dx, 0000
        div word ptr es:[ fcbRecordSize  ][ di ]        ; actual byte offset

        xor dx, dx
        mov cx, 128
        div cx
        mov byte ptr es:[ fcbCurrRecNo   ][ di ], dl
        mov word ptr es:[ fcbCurrBlockNo ][ di ], ax

        mov ax, word ptr [ _numBlocks ][ bp ]           ; number of blocks
        add word ptr es:[ fcbRandomRecNo. _low  ][ di ], ax
        adc word ptr es:[ fcbRandomRecNo. _high ][ di ], 0000

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ReadBlockFCB_Return:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  28h Random Write / FCB                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  cx      number of blocks to write                            ;
        ;  ds:dx   pointer to FCB                                       ;
        ;                                                               ;
        ;  Return:                                                      ;
        ;   al = ff, if cannot locate any matching entry                ;
        ;        00, if match located                                   ;
        ;...............................................................;

_RandomBlockWriteFCB:

        Entry
        def  _error, 0000
        def  _writeCount
        def  _numBlocks, cx
        ddef _position
        ddef _fcbPointer, es, dx

        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute byte offset from block/rec nos
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov di, dx
        mov ax, word ptr es:[ fcbRandomRecNo. _low  ][ di ]
        mov dx, word ptr es:[ fcbRandomRecNo. _high ][ di ]
        mov cx, word ptr es:[ fcbRecordSize  ][ di ]
        call _mul32
        stordarg _position, dx, ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find corresponding sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call findmatchingFCBSFT                         ; find matching SFT
        jc _WriteBlockFCB_Return                        ; if no space -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg cx, dx, _position                       ; restor position
        mov word ptr es:[ sftFilePosition. _low  ][ di ], dx
        mov word ptr es:[ sftFilePosition. _high ][ di ], cx

        mov ax, word ptr [ _numBlocks ][ bp ]           ; number of blocks
        mul word ptr [ _writeCount ][ bp ]              ; bytes to write to dta
        mov cx, ax                                      ; total bytes to Write

        or dx, dx                                       ; over 64k ?
        jz _WriteBlockFCB_12                            ; if ok -->
        mov word ptr [ _error ][ bp ], errFCBSizeTooLarge ; too large
        mov cx, 0FFFFh                                  ; read as much as possible

_WriteBlockFCB_12:
        push word ptr [ _RxDOS_pDTA. _segment ]         ; disk transfer address
        push word ptr [ _RxDOS_pDTA. _pointer ]
        call _SFTWriteFile                              ; write using sft (at es:di )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  compute return logical position
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getdarg es, di, _fcbPointer
        getdarg dx, ax, _position
        add ax, word ptr es:[ fcbRecordSize  ][ di ]
        adc dx, 0000
        mov cx, word ptr es:[ fcbRecordSize  ][ di ]    ; record size
        call _div32

        mov word ptr es:[ fcbRandomRecNo. _low  ][ di ], ax
        mov word ptr es:[ fcbRandomRecNo. _high ][ di ], dx

        mov cx, 128
        call _div32                                     ; divide by 128
        mov word ptr es:[ fcbCurrBlockNo ][ di ], ax

        mov dx, word ptr es:[ fcbRandomRecNo. _low  ][ di ]
        and dl, 127
        mov byte ptr es:[ fcbCurrRecNo   ][ di ], dl

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_WriteBlockFCB_Return:
        call setFCBErrorIfCarry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  29h Parse Filename                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Usage:                                                       ;
        ;   ds:si  points to name to parse                              ;
        ;   es:di  points to uninitialized FCB                          ;
        ;   al     parse control                                        ;
        ;...............................................................;

_ParseFilenameFCB:

        Entry
        def  _parseControl, ax
        def  _wildChars, 0000
        ddef _FCBPointer

        RetCallersStackFrame ds, bx
        mov di, word ptr [ _DI           ][ bx ]
        mov es, word ptr [ _ExtraSegment ][ bx ]
        mov si, word ptr [ _SI           ][ bx ]
        mov ds, word ptr [ _DataSegment  ][ bx ]
        stordarg _FCBPointer, es, di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  initialize FCB
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        test word ptr [ _parseControl ][ bp ], FCBPARSE_LEAVEDRIVEUNCHANGED
        jnz _ParseFilenameFCB_08                        ; leave drive alone -->
        mov byte ptr es:[ fcbDrive ][ di ], 00h         ; clear drive info

_ParseFilenameFCB_08:
        test word ptr [ _parseControl ][ bp ], FCBPARSE_LEAVEFILENAMEUNCHANGED
        jnz _ParseFilenameFCB_12                        ; leave filename alone -->

        getdarg es, di, _FCBPointer
        lea di, [ fcbName ][ di ]
        mov cx, sizefnName
        mov al, ' '
        rep stosb                                       ; clear filename to blanks

_ParseFilenameFCB_12:
        test word ptr [ _parseControl ][ bp ], FCBPARSE_LEAVEEXTENSIONUNCHANGED
        jnz _ParseFilenameFCB_14                        ; leave alone -->

        getdarg es, di, _FCBPointer
        lea di, [ fcbExtension ][ di ]
        mov cx, sizefnExtension
        mov al, ' '
        rep stosb                                       ; clear filename to blanks

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  skip through any leading garb characters
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameFCB_14:
        lodsb                                           ; get char
        cmp al, ' '                                     ; space ?
        jz _ParseFilenameFCB_14                         ; skip leading white space -->
        ifc _ParseFilenameFCB_86                        ; if control char, exit -->

        call _ParseFilenameSeparator
        jnz _ParseFilenameFCB_36                        ; yes, see if skip allowed -->

        test word ptr [ _parseControl ][ bp ], FCBPARSE_IGNORELEADSEPARATOR
        jnz _ParseFilenameFCB_14

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  test for valid drive
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameFCB_36:
        cmp byte ptr [ si ], ':'                        ; drive follows ?
        jnz _ParseFilenameFCB_54                        ; no -->

        call lowerCase                                  ; convert to lower case
        sub al, 'a'                                     ; range 
        jge _ParseFilenameFCB_40                        ; if ok -->
        mov byte ptr [ _wildChars ][ bp ], -1

_ParseFilenameFCB_40:
        mov byte ptr es:[ fcbDrive ][ di ], al          ; store drive info

        inc si                                          ; skip over ':'
        lodsb                                           ; get lead character of filename

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  filename
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameFCB_44:
        dec si                                          ; position back at start of filename
        cmp byte ptr [ si ], '.'                        ; extension follows ?
        jnz _ParseFilenameFCB_48                        ; no -->

        test word ptr [ _parseControl ][ bp ], FCBPARSE_LEAVEFILENAMEUNCHANGED
        jnz _ParseFilenameFCB_64                        ; leave alone -->

_ParseFilenameFCB_48:
        mov al, ' '
        getdarg es, di, _FCBPointer
        lea di, [ fcbName ][ di ]
        mov cx, sizefnName

        push di
        push cx
        rep stosb                                       ; clear filename to blanks

        pop cx
        pop di

_ParseFilenameFCB_50:
        cmp byte ptr [ si ], '.'
        jz _ParseFilenameFCB_64                         ; extension follows

        lodsb
        call _ParseFilenameSeparator
        jz _ParseFilenameFCB_64

        cmp al, '*'
        jz _ParseFilenameFCB_58
        cmp al, '?'
        jnz _ParseFilenameFCB_52
        or byte ptr [ _wildChars ][ bp ], 01h

_ParseFilenameFCB_52:
        call upperCase
        stosb                                           ; save updated character
        loop _ParseFilenameFCB_50                       ; continue -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  continue to skip until '.' or end of field
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameFCB_54:
        cmp byte ptr [ si ], '.'
        jz _ParseFilenameFCB_64                         ; extension follows

        lodsb                                           ; skip rest until '.'
        call _ParseFilenameSeparator                    ; if end of field
        jz _ParseFilenameFCB_64
        jmp _ParseFilenameFCB_54

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  '*' fill filename
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameFCB_58:
        or cx, cx
        jz _ParseFilenameFCB_64
        mov al, '?'
        rep stosb
        or byte ptr [ _wildChars ][ bp ], 01h
        jmp _ParseFilenameFCB_54                        ; make sure we loop for '.'

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  extension
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameFCB_64:
        cmp byte ptr [ si ], '.'                        ; extension follows ?
        jz _ParseFilenameFCB_66                         ; yes -->

        test word ptr [ _parseControl ][ bp ], FCBPARSE_LEAVEEXTENSIONUNCHANGED
        jnz _ParseFilenameFCB_86                        ; leave alone -->

_ParseFilenameFCB_66:
        mov al, ' '
        getdarg es, di, _FCBPointer
        lea di, [ fcbExtension ][ di ]
        mov cx, sizefnExtension

        push di
        push cx
        rep stosb                                       ; clear filename to blanks

        pop cx
        pop di

        cmp byte ptr [ si ], '.'
        jnz _ParseFilenameFCB_86                        ; if no extension follows -->

_ParseFilenameFCB_70:
        lodsb
        call _ParseFilenameSeparator
        jz _ParseFilenameFCB_86

        cmp al, '*'
        jz _ParseFilenameFCB_78
        cmp al, '?'
        jnz _ParseFilenameFCB_72
        or byte ptr [ _wildChars ][ bp ], 01h

_ParseFilenameFCB_72:
        call upperCase
        stosb                                           ; save updated character
        loop _ParseFilenameFCB_70                       ; continue -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  continue to skip until end of field
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameFCB_74:
        lodsb                                           ; skip rest until '.'
        call _ParseFilenameSeparator                    ; if end of field
        jz _ParseFilenameFCB_86
        jmp _ParseFilenameFCB_74

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  '*' fill filename
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameFCB_78:
        or cx, cx
        jz _ParseFilenameFCB_86
        mov al, '?'
        rep stosb
        or byte ptr [ _wildChars ][ bp ], 01h
        jmp _ParseFilenameFCB_74                        ; make sure we loop for '.'

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  end of parse
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameFCB_86:
        RetCallersStackFrame ds, bx
        mov word ptr [ _SI        ][ bx ], si

        mov al, byte ptr [ _wildChars ][ bp ]
        mov byte ptr [ _AX._AL    ][ bx ], al           ; set wild character flag

        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  filename separator test
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ParseFilenameSeparator:

        cmp al, '+'                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, '='                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, ','                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, ';'                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, '/'                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, '['                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, ']'                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, '<'                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, '>'                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, '"'                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, '|'                                     ; file name separator ?
        jz _ParseFnSeparator_08                         ; yes -->
        cmp al, ' '                                     ; file name separator ?
        jge _ParseFnSeparator_08                        ; yes -->

        xor ax, ax                                      ; space or control is treated as space

_ParseFnSeparator_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  2Ah Get Date                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al     day of week ( 0 = Sun, 1 = Mon, ... )                ;
        ;   cx     year (1980 - 2099)                                   ;
        ;   dh     month (1 - 12)                                       ;
        ;   dl     day (1 - 31)                                         ;
        ;...............................................................;

_GetDate:

        call getExpandedDate

        push cx
        push dx
        call getSystemDateValue

        pop dx
        pop cx
        RetCallersStackFrame ds, si
        mov word ptr [ _AX ][ si ], ax
        mov word ptr [ _CX ][ si ], cx
        mov word ptr [ _DX ][ si ], dx                  ; return parameters
        clc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  2Bh Set Date                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  cx   year (1980 - 2099)                                      ;
        ;  dh   month (1 - 12)                                          ;
        ;  dl   day (1 - 31)                                            ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;  al   00, date was valid                                      ;
        ;       ff, date was not valid                                  ;
        ;...............................................................;

_SetDate:

        Entry
        defbytes _datedef, sizeCLOCKDATA

        or dh, dh
        jz _setdate_error
        or dl, dl
        jz _setdate_error
        cmp dh, 12 + 1
        jnc _setdate_error
        cmp dl, 31 + 1
        jnc _setdate_error

        cmp cx, 1980
        jl _setdate_error
        cmp cx, 2100
        jnc _setdate_error

        push dx
        push cx
        setES ss
        lea di, offset _datedef [ bp ]
        call getSysDate                                 ; get system date

        pop cx
        pop dx                                          ; restore values
        mov byte ptr [ _datedef. cl_day     ][ bp ], dl
        mov byte ptr [ _datedef. cl_month   ][ bp ], dh
        mov word ptr [ _datedef. cl_year    ][ bp ], cx

        call getDaysSince1980
        mov word ptr [ _datedef. cl_daysSince1980 ][ bp ], ax

        setES ss
        lea di, offset _datedef [ bp ]
        call setSysDate                                 ; set system date

        xor ax, ax
        call setFCBErrorIfCarry                         ; no error, no carry
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  problem with date
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_setdate_error:
        stc
        call setFCBErrorIfCarry                         ; set carry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  2Ch Get Time                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   ch     hour       ( 0 - 23)                                 ;
        ;   cl     minutes    ( 0 - 59)                                 ;
        ;   dh     seconds    ( 0 - 59)                                 ;
        ;   dl     hundredths ( 0 - 99)                                 ;
        ;...............................................................;

_GetTime:

        call getExpandedDateTime                        ; get system date

        RetCallersStackFrame ds, si
        mov word ptr [ _DX ][ si ], dx
        mov word ptr [ _CX ][ si ], cx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  2Dh Set Time                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;   ch     hour       ( 0 - 23)                                 ;
        ;   cl     minutes    ( 0 - 59)                                 ;
        ;   dh     seconds    ( 0 - 59)                                 ;
        ;   dl     hundredths ( 0 - 99)                                 ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;  al   00, date was valid                                      ;
        ;       ff, date was not valid                                  ;
        ;...............................................................;

_SetTime:

        Entry
        defbytes _datedef, sizeCLOCKDATA

        cmp ch, 24
        jnc _settime_error
        cmp cl, 60
        jnc _settime_error

        cmp dh, 60
        jnc _settime_error
        cmp dl, 100
        jnc _settime_error

        push dx                                         ; save time passed
        push cx

        setES ss
        lea di, offset _datedef [ bp ]
        call getSysDate                                 ; get system date

        pop word ptr [ _datedef. cl_minutes ][ bp ]
        pop word ptr [ _datedef. cl_hseconds ][ bp ]
        call setSysDate                                 ; set system date

        clc
        call setFCBErrorIfCarry                         ; no carry
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  problem with time
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_settime_error:
        stc
        call setFCBErrorIfCarry                         ; set carry
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  2E00h Reset DOS Write Verify Switch                          ;
        ;  2E01h Set  DOS Write Verify Switch                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al   00, set verify off                                      ;
        ;       01, set verify on                                       ;
        ;                                                               ;
        ;  (see related function 54 - Get DOS Verify Switch )           ;
        ;...............................................................;

_SetVerifySwitch:

        or al,al
        jz _setverifySwitch_20
        cmp al,1
        jz _setverifySwitch_20
        stc
        ret

_setverifySwitch_20:
        mov byte ptr [ _RxDOS_Verify ], al
        or ax, ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  2Fh Get Disk Transfer Address                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   es:bx  Disk Transfer Address                                ;
        ;                                                               ;
        ;  (see related function 1A - Set Disk Transfer Address)        ;
        ;...............................................................;

_GetDiskTransferAddress:

        les bx, dword ptr [ _RxDOS_pDTA. _pointer ]

        RetCallersStackFrame ds, si
        mov word ptr [ _ExtraSegment ][ si ], es
        mov word ptr [ _BX           ][ si ], bx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  30h Get DOS Version                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al   00h return OEM version number in bh                    ;
        ;        01h return version flag in bh                          ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al   major version                                          ;
        ;   ah   minor version                                          ;
        ;  bl:cx user's serial number                                   ;
        ;   bh   OEM identification number                              ;
        ;          or version flag                                      ;
        ;...............................................................;

_GetDOSVersion:

        mov bh, byte ptr ss:[ _RxDOS_DOSOEMVersion ]    ; always 94 for RxDOS

        cmp al, 1                                       ; ROM version status ?
        jnz _GetDOSVersion_08                           ; no -->

        mov bh, byte ptr ss:[ _RxDOS_DOSROMStatusFlag ] ; always 94 for RxDOS

_GetDOSVersion_08:
        mov cx, word ptr ss:[ _RxDOS_UserSerialNumber ]
        mov bl, byte ptr ss:[ _RxDOS_UserSerialNumber + 2 ]
        mov ax, word ptr ss:[ _RxDOS_DOSVersion ]       ; pre DOS 5 (see 3306h)

        mov dx, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get PSP
        or dx, dx                                       ; during device laod ?
        jz _GetDOSVersion_12                            ; yes -->

        mov es, dx                                      ; Seg Pointer of current PSP
        mov ax, word ptr es:[ pspVersion     ]          ; Major, Minor version (VERS)

_GetDOSVersion_12:
        RetCallersStackFrame ds, si
        mov word ptr [ _AX ][ si ], ax
        mov word ptr [ _BX ][ si ], bx
        mov word ptr [ _CX ][ si ], cx
        clc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  31h Terminate But Stay Resident                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al      return value                                         ;
        ;  dx      keep memory size                                     ;
        ;...............................................................;

_TerminateStayResident:

        Entry
        def  _parentPSP, 0000
        ddef _returnVector

        mov ah, TERMINATE_TSR
        mov word ptr [ _RxDOS_ChildReturnCode ], ax     ; save return status code 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  fix size of current PSP block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov es, word ptr [ _RxDOS_CurrentPSP ]
        mov bx, dx                                      ; keep size
        call _modifyMemBlock                            ; fix size

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if there is a parent process
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]
        mov dx, word ptr es:[ pspParentId ]             ; get parent PSP
        storarg _parentPSP, dx                          ; save for later

        mov word ptr es:[ pspParentId ], 0000           ; kill parent for next time.
        or dx, dx                                       ; no parent ?
        jz _TerminateStayResident_20                    ; can't terminate -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  restore interrupts/ return to parent PSP
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        cli
        push ds
        xor ax, ax
        mov ds, ax
        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]

        mov ax, word ptr es:[ pspTerminateVect. _pointer ]
        mov dx, word ptr es:[ pspTerminateVect. _segment ]
        stordarg _returnVector, dx, ax
        
        mov bx, offset ( intCONTROLC * 4 )              ; Int23 control-C vector
        mov ax, word ptr es:[ pspControlCVect. _pointer ]
        mov dx, word ptr es:[ pspControlCVect. _segment ]
        mov word ptr [ _pointer ][ bx ], ax
        mov word ptr [ _segment ][ bx ], dx

        mov bx, offset ( intCRITICALERROR * 4 )         ; Int24 criterror vector
        mov ax, word ptr es:[ pspCritErrorVect. _pointer ]
        mov dx, word ptr es:[ pspCritErrorVect. _segment ]
        mov word ptr [ _pointer ][ bx ], ax
        mov word ptr [ _segment ][ bx ], dx

        pop ds
        sti

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return to parent process
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getarg es, _parentPSP                           ; get PSP
        mov word ptr ss:[ _RxDOS_CurrentPSP ], es       ; restore

        mov dx, word ptr es:[ pspUserStack. _segment ]
        mov ax, word ptr es:[ pspUserStack. _pointer ]  ; parent user's stack

        mov bx, word ptr ss:[ _RxDOS_CurrentInstance ]  ; base address of current stack
        mov word ptr ss:[ _segment ][ bx ], dx
        mov word ptr ss:[ _pointer ][ bx ], ax

        RetCallersStackFrame es, bx
        getdarg dx, ax, _returnVector
        mov word ptr es:[ _CS ][ bx ], dx
        mov word ptr es:[ _IP ][ bx ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_TerminateStayResident_20:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  32h Get Dos Device Parameter Block                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   dl     0= default,                                          ;
        ;          1= a:, 2 = b:, ...                                   ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al     0ffh if invalid drive                                ;
        ;   ds:bx  returns pointer to dos device parameter block        ;
        ;                                                               ;
        ;  (see related function 1F - Get Default Drive DPB )           ;
        ;...............................................................;

_GetDriveParameterBlock:

        push dx
        call GetActualDrive                             ; actual drive (in ax)
        jnc _GetDriveParameterBlock_08                  ; if valid drive -->
        call setFCBErrorIfCarry                         ; else set error
        ret

_GetDriveParameterBlock_08:
        mov cx, sizeDPB
        mul cx                                          ; create DPB offset

        les bx, dword ptr ss:[ _RxDOS_pDPB ]
        add bx, ax

        RetCallersStackFrame ds, si
        mov word ptr [ _DataSegment ][ si ], es
        mov word ptr [ _BX ][ si ], bx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  3300h Get ControlC Check Flag                                ;
        ;  3301h Set ControlC Check Flag                                ;
        ;  3305h Get Startup Drive                                      ;
        ;  3306h Get DOS Version                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   dx     returns values                                       ;
        ;...............................................................;

_CtrlBreakCheck:

        RetCallersStackFrame es, si

        Goto GetControlC,      __getControlC
        Goto SetControlC,      __setControlC
        Goto GetStartupDrive,  __getStartupDrive
        Goto GetExtDosVersion, __getExtDosVersion

        stc
        call setFCBErrorIfCarry
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get control c flag
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__getControlC:
        mov dl, byte ptr ss:[ _RxDOS_bCtrlBreakCheck ]
        mov byte ptr es:[ _DX._DL ][ si ], dl
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set control c flag
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__setControlC:
        mov dl, byte ptr es:[ _DX._DL ][ si ]
        and dl, 0001h
        mov byte ptr ss:[ _RxDOS_bCtrlBreakCheck ], dl
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get startup drive 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__getStartupDrive:
        mov dl, byte ptr ss:[ _RxDOS_BootDrive ]
        mov byte ptr es:[ _DX._DL ][ si ], dl
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get extended DOS version
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

__getExtDosVersion:
        mov bx, word ptr ss:[ _RxDOS_DOSVersion ]
        mov word ptr es:[ _BX ][ si ], bx
        mov word ptr es:[ _DX ][ si ], 0000
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  34h Get INDOS Flag Address                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   es:bx  returns pointer INDOS flag                           ;
        ;...............................................................;

_GetInDOSFlagAddress:

        RetCallersStackFrame es, si

        mov word ptr es:[ _BX           ][ si ], offset _RxDOS_INDOSFlag
        mov word ptr es:[ _ExtraSegment ][ si ], ds
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  35h Get Interrupt Vector                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al      interrupt number                                     ;
        ;  es:bx   interrupt routine address                            ;
        ;...............................................................;

_GetInterruptVector:

        pushf                                           ; save interrupts
        push ds                                         ; save current segment
        mov ah, 0
        mov si, ax
        add si, si
        add si, si

        cli
        xor ax, ax
        mov ds, ax
        mov es, word ptr [ si ][ _segment ]             ; segment
        mov bx, word ptr [ si ][ _pointer ]             ; address

        RetCallersStackFrame ds, si
        mov word ptr [ _BX ][ si ], bx
        mov word ptr [ _ExtraSegment ][ si ], es

        pop ds
        popf                                            ; restore interrupts
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  36h Get Free Disk Space                                      ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  dl    drive (A=1, B=2, ... )                                 ;
        ;...............................................................;

_GetFreeDiskSpace:

        call CtrlC_Check                                ; look-ahead CtrlC Check

        RetCallersStackFrame es, bx
        mov word ptr es:[ _AX ][ bx ], -1               ; in case of error, -1

        push dx
        call GetActualDrive                             ; actual drive (in dx)
        jc _getFreeDiskSpace_20                         ; arg returend in ax

        call AmountFreeSpace                            ; get amount free space

        RetCallersStackFrame ds, si
        mov cx, word ptr es:[ _dpbFreeCount ][ bx ]
        mov word ptr [ _BX ][ si ], cx                  ; # free clusters

        mov cx, word ptr es:[ _dpbBytesPerSector   ][ bx ]
        mov dx, word ptr es:[ _dpbMaxClusterNumber ][ bx ]

        mov al, byte ptr es:[ _dpbClusterSizeMask  ][ bx ]
        cbw
        inc ax

        mov word ptr [ _AX ][ si ], ax                  ; sectors/cluster
        mov word ptr [ _CX ][ si ], cx                  ; bytes per sector
        mov word ptr [ _DX ][ si ], dx                  ; clusters/drive
        or ax, ax                                       ; clear carry

_getFreeDiskSpace_20:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  3700h Get Switch Char                                        ;
        ;  3701h Set Switch Char                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  dl      switch character                                     ;
        ;                                                               ;
        ;                                                               ;
        ; -- Obsoleted.  This fct no longer supported in v.5 MSDOS ---- ;
        ;...............................................................;

_GetSetSwitchChar:

        or al, al
        jnz _GetSetSwitchChar_Set

        mov dl, byte ptr [ _RxDOS_bSwitchChar ]
        RetCallersStackFrame es, bx
        mov byte ptr es:[ _DX._DL ][ bx ], dl           ; return value.
        ret

_GetSetSwitchChar_Set:
        mov byte ptr [ _RxDOS_bSwitchChar ], dl
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  38h Country Dependent Info                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx points to get country info buffer                      ;
        ;        dx = FFFFh, Set country code                           ;
        ;                                                               ;
        ;  al    country code if less than 254, else FFh                ;
        ;                                                               ;
        ;  bx    country code if greater than 254                       ;
        ;        al = FFh                                               ;
        ;                                                               ;
        ;  If dx is NOT FFFFh, returns COUNTRYINFO structure, else      ;
        ;  sets country code.                                           ;
        ;...............................................................;

_CountryDependentInfo:

        RetCallersStackFrame es, bx
        cmp dx, -1                                      ; set or return country code ?
        jz _SetCountryInfo                              ; if set country info -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get country info
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov di, dx                                      ; destination address
        mov si, offset _RxDOS_CurrCountryInfo           ; current country info
        mov cx, sizeCOUNTRYINFO                         ; size
        rep movsb                                       ; copy

        mov ax, word ptr [ _RxDOS_UserCountryCode ]
        mov word ptr es:[ _BX      ][ bx ], ax          ; CC returned in BX
        mov byte ptr es:[ _AX. _AL ][ bx ], al          ; ... and AL
        clc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set country info
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetCountryInfo:
        cmp al, -1                                      ; country < 254 ?
        jz _SetCountryInfo_08                           ; no -->
        mov bx, ax
        xor bh, bh                                      ; country code to bx

_SetCountryInfo_08:
        mov word ptr [ _RxDOS_UserCountryCode ], bx     ; should check values
        clc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  39h Create Subdirectory                                      ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to subdirectory                                ;
        ;...............................................................;

_CreateSubdirectory:

        Entry
        ddef _pathname, es, dx                          ; arg passed internally as es:dx
        ddef _dirAddress                                ; dir reference
        def  _alloccluster                              ; allocated cluster
        ddef _datetime
        defbytes _dirAccess, sizeDIRACCESS
        
        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  does entry already exist ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, (FILE_NODEVICENAME + FILEHAS_NOFILENAME + FILECANNOT_BEDEFINED)
        getdarg es, si, _pathname                       ; arg passed internally as es:dx
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; check file path
        jnc _MakeSubDir_12                              ; if path is valid -->

        call _chgErrorToPathNotFound                    ; if name referenced path
        jmp _MakeSubDir_40                              ; if path invalid -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find next empty directory entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_MakeSubDir_12:
        and word ptr [ _dirAccess. fileAcDrive   ][ bp ], 7FFFh

        mov ax, word ptr [ _dirAccess. fileAcDrive   ][ bp ]
        mov dx, word ptr [ _dirAccess. fileAcDirCluster ][ bp ]
        call LocateFreeDirSlot                          ; valid empty entry ?
        ifc _MakeSubDir_40                              ; if can't find free slot -->

        push si
        call locateCCBPHeader                           ; get ccb header into es:di
        or byte ptr es:[ ccbStatus ][ di ], ccb_isONHOLD

        pop di                                          ; convert name to dir style
        stordarg _dirAddress, es, di                    ; address of empty loc found
        clearMemory sizeDIRENTRY                        ; init to zeroes

        lea si, word ptr [ _dirAccess ][ bp ]
        mov si, word ptr [ fileAcNameOffset ][ si ]
        call convFilenametoFCBString                    ; dir style name

        call getSysDateinDirFormat
        stordarg _datetime, dx, ax                      ; make sure we get these

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Allocate cluster for sub-directory
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, word ptr [ _dirAccess. fileAcDrive   ][ bp ]
        call AllocateInitCluster                        ; init/ allocate
        storarg  _alloccluster, dx                      ; save cluster
        ifc _MakeSubDir_40                              ; if error -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init (.) entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        lea si, offset ccbData [ di ]                   ; point to data
        call blankinitDirName                           ; initialize file name 
        mov byte ptr es:[ deName ][ si ], '.'           ; set dot entry

        getarg dx, _alloccluster                        ; get cluster
        mov word ptr es:[ deStartCluster ][ si ], dx

        getdarg dx, ax, _datetime                       ; get date/ time
        mov word ptr es:[ deTime ][ si ], ax            ; time created
        mov word ptr es:[ deDate ][ si ], dx            ; date created
        mov byte ptr es:[ deAttributes ][ si ], ATTR_DIRECTORY

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init (..) entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        lea si, offset ccbData.sizeDIRENTRY [ di ]
        call blankinitDirName                           ; initialize file name 
        mov word ptr es:[ deName ][ si ], '..'          ; set double dot entry

        mov dx, word ptr [ _dirAccess. fileAcDirCluster ][ bp ]
        mov word ptr es:[ deStartCluster ][ si ], dx

        getdarg dx, ax, _datetime                       ; get date time
        mov word ptr es:[ deTime ][ si ], ax            ; time created
        mov word ptr es:[ deDate ][ si ], dx            ; date created
        mov byte ptr es:[ deAttributes ][ si ], ATTR_DIRECTORY

        call CCBChanged                                 ; mark changes made
        mov ax, word ptr [ _dirAccess. fileAcDrive      ][ bp ]
      ; call updateDriveBuffers

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  in parent directory, build entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        getarg dx, _alloccluster
        getdarg es, si, _dirAddress                     ; restore si pointer
        mov word ptr es:[ deStartCluster ][ si ], dx
        mov byte ptr es:[ deAttributes   ][ si ], ATTR_DIRECTORY

        getdarg dx, ax, _datetime                       ; get date time
        mov word ptr es:[ deTime ][ si ], ax            ; time created
        mov word ptr es:[ deDate ][ si ], dx            ; date created

        call locateCCBPHeader                           ; get ccb header into es:di
        call CCBChanged                                 ; mark changes made
        mov ax, word ptr [ _dirAccess. fileAcDrive      ][ bp ]
      ; call updateDriveBuffers

        xor ax, ax                                      ; clear carry

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  normal exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_MakeSubDir_40:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  3Ah Remove Subdirectory                                      ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to subdirectory                                ;
        ;...............................................................;

_RemoveSubdirectory:

        Entry
        ddef _pathname, es, dx                          ; arg passed internally as es:dx
        defbytes _dirAccess, sizeDIRACCESS
        defbytes _diskAccess, sizeDISKACCESS
        defbytes _tempfilename, sizeTempFILENAME
        
        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  parse file name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, (FILE_NODEVICENAME + FILEHAS_NOFILENAME)
        getdarg es, si, _pathname                       ; arg passed internally as es:dx
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; check file path
        jnc _RemoveSubDir_12                            ; if path valid -->

        call _chgErrorToPathNotFound                    ; if name referenced path
        jmp _RemoveSubDir_40                            ; if path invalid -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  trying to remove current directory ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_RemoveSubDir_12:
        mov cx, dx                                      ; save returned dir cluster
        call getCurrDirCluster                          ; get cluster of curr directory

        cmp cx, dx                                      ; same directory as current ?
        jnz _RemoveSubDir_16                            ; dir is ok -->
        SetError pexterrCurrentDirectory, _RemoveSubDir_40

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if directory is empty (contains other than . and .. )
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_RemoveSubDir_16:
        setES ds
        mov ax, word ptr [ _dirAccess. fileAcDrive   ][ bp ]
        mov dx, word ptr [ _dirAccess. fileAcCluster ][ bp ]
        lea bx, offset _diskAccess [ bp ]               ; pointer to access block
        call initdiskAccess                             ; [ax] is drive, [dx] is cluster

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan directory sectors
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov word ptr [ _diskAccess. diskAcOptions        ][ bp ], (ccb_isDIR)
        mov word ptr [ _diskAccess. diskAcPosition. _low ][ bp ], sizeDIRENTRY

_RemoveSubDir_22:
        add word ptr [ _diskAccess. diskAcPosition. _low  ][ bp ], sizeDIRENTRY
        adc word ptr [ _diskAccess. diskAcPosition. _high ][ bp ], 0000
        lea bx, offset _diskAccess [ bp ]               ; pointer to access block
        call _FATReadRandom                             ; read into buffer
        jz _RemoveSubDir_32                             ; if no more data, ok to delete -->

        cmp byte ptr es:[ bx ], DIRENTRY_NEVERUSED
        jz _RemoveSubDir_32                             ; if no more data, ok to delete -->
        cmp byte ptr es:[ bx ], DIRENTRY_DELETED        ; entry deleted ?
        jz _RemoveSubDir_22                             ; yes, keep searching -->

        SetError pexterrIllegalName, _RemoveSubDir_40

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  ok to remove this entry.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_RemoveSubDir_32:
        mov ax, word ptr [ _dirAccess. fileAcDrive   ][ bp ]
        mov dx, word ptr [ _dirAccess. fileAcCluster ][ bp ]
        call ReleaseClusterChain

        les di, dword ptr [ _dirAccess. fileAcBufferPtr ][ bp ]
        mov bx, word ptr [ _dirAccess. fileAcDirOffset  ][ bp ]

        mov byte ptr es:[ di + bx ], DIRENTRY_DELETED
        call CCBChanged
        mov ax, word ptr [ _dirAccess. fileAcDrive      ][ bp ]
        call updateDriveBuffers
        clc                                             ; return no error

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  normal exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_RemoveSubDir_40:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  3Bh Change Subdirectory                                      ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to subdirectory                                ;
        ;...............................................................;

_ChangeSubdirectory:

        Entry
        ddef _pathname, es, dx                          ; arg passed internally as es:dx
        defbytes _dirAccess, sizeDIRACCESS
        
        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  parse file name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, (FILE_NODEVICENAME + FILEHAS_NOFILENAME)
        getdarg es, si, _pathname                       ; arg passed internally as es:dx
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; check file path
        jnc _ChgSubDir_12                               ; if path valid -->

        call _chgErrorToPathNotFound                    ; if name referenced path
        jmp _ChgSubDir_40                               ; if path invalid -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy path to CDS table.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ChgSubDir_12:
        push ds                                         ; save current segment
        push dx                                         ; cluster address

        getdarg es, si, _pathname                       ; arg passed internally as es:dx
        call getDrive

        les di, dword ptr [ _RxDOS_pCDS ]               ; actual address in CDS
        mov cl, sizeCDS
        mul cl                                          ; ax contains current drive
        add di, ax                                      ; from
        push di

        setDS ss                                        ; copy from stack
        lea si, offset _dirAccess. fileAcExpandedName [ bp ]
        lea di, offset _cdsActualDirectory [ di ]

_ChgSubDir_22:
        lodsb                                           ; copy buffer
        stosb
        or al, al
        jnz _ChgSubDir_22

        pop di                                          ; restore CDS pointer
        pop word ptr es:[ _cdsStartClusterDir ][ di ]
        pop ds                                          ; restore ds
        clc                                             ; no error

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  normal exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ChgSubDir_40:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  3Ch Create File                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  cx    attributes                                             ;
        ;  ds:dx pointer to filename( path included )                   ;
        ;...............................................................;

_CreateFile:

        call CtrlC_Check                                ; look-ahead CtrlC Check
        call VerifyAvailableHandle                      ; see if handle available
        jc _CreateFile_42                               ; exit if none -->

        RetCallersStackFrame es, si
        mov cx, word ptr es:[ _CX ][ si ]               ; attributes
        mov dx, word ptr es:[ _DX ][ si ]
        mov es, word ptr es:[ _DataSegment ][ si ]
        call _SFTCreateFile
        jc _CreateFile_42                               ; exit if error -->

        call MapSFTtoAppHandle                          ; if no space, create error

_CreateFile_42:
        RetCallersStackFrame ds, si
        mov word ptr [ _AX ][ si ], ax                  ; return handle or error code
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  3dh Open File                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al    open mode                                              ;
        ;  ds:dx pointer to filename( path included )                   ;
        ;...............................................................;

_OpenFile:

        Entry
        def _mode, ax

        call CtrlC_Check                                ; look-ahead CtrlC Check
        call VerifyAvailableHandle                      ; see if handle available
        jc _OpenFile_42                                 ; exit if none -->

        RetCallersStackFrame es, si
        mov dx, word ptr es:[ _DX           ][ si ]
        mov es, word ptr es:[ _DataSegment  ][ si ]
        mov ax, word ptr [ _mode ][ bp ]
        call _SFTOpenFile                               ; build an SFT
        jc _OpenFile_42                                 ; exit if error -->

        call MapSFTtoAppHandle                          ; record SFT handle into JHT

_OpenFile_42:
        RetCallersStackFrame ds, si
        mov word ptr [ _AX ][ si ], ax                  ; return handle or error code
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  3Eh Close File                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx handle to open file                                       ;
        ;...............................................................;

_CloseFile:

        Entry
        def _handle, bx
        ddef _sftPointer
        defbytes _diskAccess, sizeDISKACCESS

        mov ax, bx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _CloseFile_error                             ; if could not find -->

        call _SFTCloseFile                              ; close SFT Entry
        jc _CloseFile_error                             ; if error -->

        call getHandleTable                             ; get handle table
        add bx, word ptr [ _handle ][ bp ]
        mov byte ptr es:[ bx ], -1                      ; cancel handle reference in PSP's handle table

        or bx, bx                                       ; no carry

_CloseFile_error:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  3fh Read File                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx   handle                                                  ;
        ;  cx   max bytes to read                                       ;
        ;  ds:dx buffer address                                         ;
        ;...............................................................;

_ReadFile:

        Entry
        def _handle, bx
        def _readCount, cx
        ddef _sftPointer
        ddef _bufPtr, es, dx                            ; save buffer pointer

        call CtrlC_Check                                ; look-ahead CtrlC Check

        getarg ax, _handle                              ; get file handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        stordarg _sftPointer, es, di                    ; save sft address
        jc _ReadFile_Return                             ; if could not find -->

        push word ptr [ _bufPtr. _segment ][ bp ]
        push word ptr [ _bufPtr. _pointer ][ bp ]
        getarg cx, _readCount                           ; bytes to read
        getarg ax, _handle                              ;
        call _SFTReadFile                               ; read using sft (at es:di )

_ReadFile_Return:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  40h Write File                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx   handle                                                  ;
        ;  cx   bytes to write                                          ;
        ;  ds:dx buffer address                                         ;
        ;...............................................................;

_WriteFile:

        Entry
        def _handle, bx
        def _writeCount, cx
        ddef _sftPointer
        ddef _bufPtr, es, dx                            ; save buffer pointer

        call CtrlC_Check                                ; look-ahead CtrlC Check

        mov ax, bx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        stordarg _sftPointer, es, di                    ; save sft buffer pointer
        jc _WriteFile_error                             ; if could not find -->

        push word ptr [ _bufPtr. _segment ][ bp ]
        push word ptr [ _bufPtr. _pointer ][ bp ]
        getarg cx, _writeCount                          ; bytes to write
        call _SFTWriteFile                              ; write using sft (at es:di )

_WriteFile_error:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  41h Delete File                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx Asciz name of file to delete (no wild chars)           ;
        ;...............................................................;

_DeleteFile:

        Entry
        ddef _filename, es, dx                          ; arg passed internally as es:dx
        ddef _dirEntry                                  ; directory entry
        defbytes _dirAccess, sizeDIRACCESS
        
        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  parse file name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, (FILE_NODEVICENAME + FILECANNOT_BEDIRECTORY)
        mov si, dx                                      ; name from caller
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile
        jc _DeleteFile_18                                 ; if file/path valid -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  mark found entry in directory as deleted
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        les di, dword ptr [ _dirAccess. fileAcBufferPtr ][ bp ]
        mov bx, word ptr [ _dirAccess. fileAcDirOffset  ][ bp ]
        mov byte ptr es:[ di + bx ], DIRENTRY_DELETED
        call CCBChanged

        mov ax, word ptr [ _dirAccess. fileAcDrive   ][ bp ]
        mov dx, word ptr [ _dirAccess. fileAcCluster ][ bp ]
        call ReleaseClusterChain

        mov ax, word ptr [ _dirAccess. fileAcDrive      ][ bp ]
        call updateDriveBuffers
        or ax, ax                                       ; return no error

_DeleteFile_18:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  42h Lseek (Move) File Pointer                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al      move method                                          ;
        ;  bx      handle                                               ;
        ;  cx:dx   distance to move pointer                             ;
        ;...............................................................;

_MoveFilePointer:

        Entry
        def _method, ax
        def _handle, bx
        ddef _moveDistance, cx, dx
        ddef _newPosition

        call CtrlC_Check                                ; look-ahead CtrlC Check

        mov ax, bx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _moveFilePointer_36                          ; if could not find -->

        getdarg cx, dx, _moveDistance
        mov ax, word ptr [ _method ][ bp ]
        Goto SEEK_BEG,   _moveFilePointer_beg
        Goto SEEK_CUR,   _moveFilePointer_cur
        Goto SEEK_END,   _moveFilePointer_end
        SetError -1,     _moveFilePointer_36

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  seek from end
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_moveFilePointer_end:
        add dx, word ptr es:[ sftFileSize. _low  ][ di ]
        adc cx, word ptr es:[ sftFileSize. _high ][ di ]
        jmp short _moveFilePointer_beg

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  seek from current position
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_moveFilePointer_cur:
        add dx, word ptr es:[ sftFilePosition. _low  ][ di ]
        adc cx, word ptr es:[ sftFilePosition. _high ][ di ]
     ;  jmp short _moveFilePointer_beg
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  seek from beginning
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_moveFilePointer_beg:
        mov word ptr es:[ sftFilePosition. _low  ][ di ], dx
        mov word ptr es:[ sftFilePosition. _high ][ di ], cx
        clc                                             ; no error

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_moveFilePointer_36:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], dx
        mov word ptr [ _DX ][ bx ], cx
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  43h Get/Set File Attributes                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx   file name                                            ;
        ;  al      get/set flag                                         ;
        ;  cx      attributes                                           ;
        ;...............................................................;

_ChangeFileMode:

        Entry
        def  _mode, ax                                  ; get/set flag.
        def  _attributes, cx                            ; attributes, if change.
        ddef _filename, es, dx                          ; arg passed internally as es:dx
        ddef _dirEntry                                  ; directory entry
        defbytes _dirAccess, sizeDIRACCESS
        
        call CtrlC_Check                                ; look-ahead CtrlC Check

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  parse file name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, (FILE_NODEVICENAME)
        mov si, dx                                      ; name from caller
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; locate file
        jc _ChangeFileMode_18                           ; if file/path valid -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if found, either get or set the attribute 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        les di, dword ptr [ _dirAccess. fileAcBufferPtr ][ bp ]
        mov bx, word ptr [ _dirAccess. fileAcDirOffset ][ bp ]

        mov al, byte ptr [ _mode ][ bp ]
        Goto 00, _ChangeFileMode_GetAttrib              ; get
        Goto 01, _ChangeFileMode_SetAttrib              ; set

        SetError pexterrInvalidFunction, _ChangeFileMode_18 ; else -->
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if get file attributes
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ChangeFileMode_GetAttrib:
        RetCallersStackFrame ds, si
        mov cx, ATTR_DIRECTORY
        mov ax, es
        or ax, ax                                       ; if no dir entry, 
        jz _ChangeFileMode_GetAttrib_08                 ; then it must be root dir ->
        mov cl, byte ptr es:[ deAttributes ][ di + bx ]

_ChangeFileMode_GetAttrib_08:
        mov word ptr [ _CX ][ si ], cx
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if set file attributes
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_ChangeFileMode_SetAttrib:
        mov ax, es
        or ax, ax                                       ; root dir ?
        jnz _ChangeFileMode_SetAttrib_08                ; anything but root -->
        SetError pexterrPathNotFound, _ChangeFileMode_18

_ChangeFileMode_SetAttrib_08:
        mov cx, word ptr [ _attributes ][ bp ]
        test cx, not ATTR_SETTABLE
        jz _ChangeFileMode_12
        SetError pexterrAccessDenied, _ChangeFileMode_18

_ChangeFileMode_12:
        mov al, byte ptr es:[ deAttributes ][ di + bx ]
        and al, ATTR_SETTABLE
        cmp al, cl
        jz _ChangeFileMode_16                           ; if no change -->

        and byte ptr es:[ deAttributes ][ di + bx ], not ATTR_SETTABLE
        or byte ptr es:[ deAttributes ][ di + bx ], cl
        call CCBChanged                                 ; update changed buffer

_ChangeFileMode_16:
        clc

_ChangeFileMode_18:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  44h IoControl                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     subfunction                                          ;
        ;   bx     file handle                                          ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx     depends on function                                  ;
        ;                                                               ;
        ;...............................................................;

_IoControl:

        Entry
        def  _handle, bx
        def  _count, cx
        ddef _bufPtr, es, dx

        call CtrlC_Check                                ; look-ahead CtrlC Check

        Goto 00h,                   _IoControl_GetDeviceData
        Goto 01h,                   _IoControl_SetDeviceData
        Goto 02h,                   _IoControl_CharDev_ReadControlData
        Goto 03h,                   _IoControl_CharDev_WriteControlData
        Goto 04h,                   _IoControl_BlockDev_ReadControlData
        Goto 05h,                   _IoControl_BlockDev_WriteControlData
        Goto 06h,                   _IoControl_CheckDevInputStatus
        Goto 07h,                   _IoControl_CheckDevOutputStatus
        Goto 08h,                   _IoControl_CheckDeviceRemovableMedia
        Goto 09h,                   _IoControl_IsDriveRemote
        Goto 0Ah,                   _IoControl_IsFileOrDeviceRemote
        Goto 0Bh,                   _IoControl_SetSharingRetryCount

_IoControl_BlockDev_ReadControlData:
_IoControl_BlockDev_WriteControlData:

_IoControl_CheckDevInputStatus:
_IoControl_CheckDevOutputStatus:
_IoControl_CheckDeviceRemovableMedia:
_IoControl_IsFileOrDeviceRemote:
_IoControl_SetSharingRetryCount:
        mov ax, offset pexterrInvalidFunction
        stc
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  4400 - Get Device Data 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_IoControl_GetDeviceData:

        mov ax, bx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _IoControl_GetDevData_12                     ; if error -->

        mov dx, word ptr es:[ sftDevInfo ][ di ]
        RetCallersStackFrame ds, bx
        mov word ptr [ _DX ][ bx ], dx
        clc

_IoControl_GetDevData_12:
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  4401 - Set Device Data 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_IoControl_SetDeviceData:

        mov ax, bx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _IoControl_SetDevData_12                     ; if error -->

        RetCallersStackFrame ds, bx
        mov dx, word ptr [ _DX ][ bx ]                  ; get device data bits
        mov word ptr es:[ sftDevInfo ][ di ], dx        ; set data bits
        clc

_IoControl_SetDevData_12:
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  4402 - Read Control Data
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_IoControl_CharDev_ReadControlData:

        mov ax, bx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _IoControl_ReadCharData_22                   ; if error -->

        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jnz _IoControl_ReadCharData_14                  ; if device -->
        SetError pexterrAccessDenied, _IoControl_ReadCharData_22

_IoControl_ReadCharData_14:
        push word ptr es:[ sftDCB. _segment ][ di ]     ; device driver address
        push word ptr es:[ sftDCB. _pointer ][ di ]
        getarg cx, _count                               ; count 
        getdarg es, di, _bufPtr                         ; buffer pointer
        call devCharRead                                ; read data
        mov ax, cx                                      ; characters actually read
        clc                                             ; remove carry

_IoControl_ReadCharData_22:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  4403 - Write Control Data
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_IoControl_CharDev_WriteControlData:

        mov ax, bx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _IoControl_WriteCharData_12                   ; if error -->

        push word ptr [ _bufPtr. _segment ][ bp ]
        push word ptr [ _bufPtr. _pointer ][ bp ]
        getarg cx, _count                               ; bytes to write
        call _SFTWriteFile                               ; Write using sft (at es:di )

_IoControl_WriteCharData_12:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  4409 - Is Drive Remote
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_IoControl_IsDriveRemote:

        RetCallersStackFrame ds, bx
        mov word ptr [ _DX ][ bx ], 0000                ; we don't support anything
        clc
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  45h Duplicate File Handle                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   bx     existing (old) handle                                ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     new handle                                           ;
        ;...............................................................;

_DuplicateFileHandle:

        mov es, word ptr [ _RxDOS_CurrentPSP ]
        mov cx, word ptr es:[ pspFileHandleCount ]
        mov dx, cx                                      ; save original count
        cmp bx, word ptr es:[ pspFileHandleCount ]      ; illegal reference ?
        jc _duplFileHandle_12                           ; if valid ->

_duplFileHandle_InvHandleError:
        seterror pexterrInvalidHandle, _duplFileHandle_Return ; if illegal -->

_duplFileHandle_12:
        les di, dword ptr es:[ pspFileHandlePtr  ]
        mov bl, byte ptr es:[ di + bx ]                 ; get SFT handle from current
        cmp bl, -1                                      ; invalid entry ?
        jz _duplFileHandle_InvHandleError               ; if error -->

    ; is there an available entry ?

        mov al, -1
        repnz scasb                                     ; scan for empty slot
        jnz _duplFileHandle_InvHandleError              ; if error -->

        sub dx, cx                                      ; get count
        dec dx
        push dx
        mov byte ptr es:[ di - 1 ], bl                  ; duplicate handle

        xor ax, ax
        mov al, bl                                      ; old handle
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        inc word ptr es:[ sftRefCount ][ di ]           ; bump in use count

        pop ax                                          ; new handle
        or ax, ax                                       ; no carry

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if error, exit with error code.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_duplFileHandle_Return:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  46h Force File Handle Duplicate                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   bx     existing open handle                                 ;
        ;   cx     duplicate handle                                     ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     new handle                                           ;
        ;...............................................................;

_ForceFileHandle:

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Close duplicate handle if it is open
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        push bx                                         ; save regs
        push cx

        mov ax, cx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _ForceFileHandle_10                          ; if could not find -->

        call _SFTCloseFile                              ; close SFT Entry

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Duplicate Handle
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_ForceFileHandle_10:
        pop cx                                          ; restore regs
        pop bx

        mov es, word ptr [ _RxDOS_CurrentPSP ]
        les di, dword ptr es:[ pspFileHandlePtr  ]
        cmp bx, word ptr es:[ pspFileHandleCount ]
        jnc _ForceFileHandle_InvHandleError             ; if error value -->
        cmp cx, word ptr es:[ pspFileHandleCount ]
        jnc _ForceFileHandle_InvHandleError             ; if error value -->

        xor ah, ah
        mov al, byte ptr es:[ di + bx ]                 ; SFT handle for duplicate
        xchg cx, bx
        mov byte ptr es:[ di + bx ], al                 ; duplicate handle

        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        inc word ptr es:[ sftRefCount ][ di ]           ; bump in -use count
        or ax, ax                                       ; no carry
        ret

_ForceFileHandle_InvHandleError:
        mov ax, offset pexterrInvalidHandle             ; invalid handle
        stc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  47h Get Current Directory                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:si   pointer to max 64 byte user memory area              ;
        ;  dl      drive number                                         ;
        ;...............................................................;

_GetCurrentDirectory:

        push dx
        call GetActualDrive                             ; actual drive (in ax)
        jnc getCurrDir_14                               ; if no error -->

        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax                  ; else save error code
        ret                                             ; return

getCurrDir_14:
        mov cl, sizeCDS
        mul cl                                          ; ax contains offset to current drive

        lds si, dword ptr [ _RxDOS_pCDS ]               ; actual address in CDS
        add si, ax                                      ; from

        mov ax, _cdsActualDirectory                     ; proper offset
        add al, byte ptr [ _cdsNonSubstOffset ][ si ]
        add si, ax

        push ds
        RetCallersStackFrame ds, bx
        mov es, word ptr [ _DataSegment ][ bx ]
        mov di, word ptr [ _SI ][ bx ]
        pop ds

getCurrDir_22:
        lodsb                                           ; copy buffer
        stosb
        or al, al
        jnz getCurrDir_22

        clc
        ret                                             ; ds:si will be returned.

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  48h Allocate Memory                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx   # paragraphs of memory requested                        ;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;  ax   segment address of allocated memory block               ;
        ;  bx   size of largest block of memory available in            ;
        ;         paragraphs, if allocation fails.                      ;
        ;...............................................................;

_AllocateMemory:

        Entry
        def _allocation, bx

        call _allocateUpperMB                           ; allocate upper mem blocks
        jnc _AllocateMemory_12                          ; if allocation made -->

        getarg bx, _allocation
        call _allocateConvMB                            ; allocate lower mem blocks

_AllocateMemory_12:
        RetCallersStackFrame ds, si
        mov word ptr [ _AX ][ si ], ax                  ; error or segment allocation
        jnc _AllocateMemory_16                          ; if not error -->

        mov word ptr [ _BX ][ si ], dx                  ; largest block

_AllocateMemory_16:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  49h Free Allocated Memory                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es   paragraph to free                                       ;
        ;...............................................................;

_FreeAllocatedMemory:

        RetCallersStackFrame es, bx
        mov bx, word ptr es:[ _ExtraSegment ][ bx ]
        dec bx                                          ; preceding seg contains block

        mov es, bx                                      ; is this a valid memory block ?
        cmp byte ptr es:[ _memSignature ], _RxDOS_MEMSIGNATURE
        jz _freeallocateMemory_12
        cmp byte ptr es:[ _memSignature ], _RxDOS_ENDSIGNATURE
        jz _freeallocateMemory_12

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  invalid block.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        RetCallersStackFrame es, bx
        mov word ptr es:[ _AX ][ bx ], offset pexterrInvalidBlock
        stc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  block is valid.  free it.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_freeallocateMemory_12:
        xor ax, ax
        mov word ptr es:[ _memParent ], ax              ; free memory block
      ; call _collectMemoryBlocks                       ; save space
      ; or ax, ax                                       ; no carry
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  4ah Modify Allocate Memory                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es   paragraph to free                                       ;
        ;  bx   new paragraph size                                      ;
        ;...............................................................;

_ModifyAllocatedMemory:

        RetCallersStackFrame es, si
        mov es, word ptr es:[ _ExtraSegment ][ si ]
        call _modifyMemBlock                            ; fix size
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  4Bxxh Load and Execute Program                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx   program name                                         ;
        ;  es:bx   program arguments                                    ;
        ;...............................................................;

_ExecuteProgram:

        call loadProgram
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  4Ch Terminate Process                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al      return value                                         ;
        ;...............................................................;

_TerminateProcess_00:
        xor ax, ax                                      ; int 21/ ah = 0

_TerminateProcess:

        Entry
        def  _parentPSP, 0000
        ddef _returnVector

        setDS ss
        setES ss
        mov ah, TERMINATE_NORMAL
        mov word ptr [ _RxDOS_ChildReturnCode ], ax     ; save return status code
        inc word ptr [ _RxDOS_AbortInProgress ]         ; skip critical error if already in here

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if there is a parent process
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor ax, ax
        mov es, ax
        mov bx, offset ( intTERMINATEAPP * 4 )          ; Int22 terminate app
        mov ax, word ptr es:[ _pointer ][ bx ]
        mov dx, word ptr es:[ _segment ][ bx ]
        stordarg _returnVector, dx, ax                  ; default terminate address
        
        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]
        mov bx, word ptr es:[ pspParentId ]             ; get parent PSP
        storarg _parentPSP, bx                          ; save for later

        or bx, bx                                       ; no parent ?
        jz _TerminateProcess_20                         ; can't terminate -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  restore interrupts/ return to parent PSP
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        push ds
        xor ax, ax
        mov ds, ax

        mov bx, offset ( intTERMINATEAPP * 4 )          ; Int22 terminate app
        mov ax, word ptr es:[ pspTerminateVect. _pointer ]
        mov dx, word ptr es:[ pspTerminateVect. _segment ]
        mov word ptr [ _pointer ][ bx ], ax
        mov word ptr [ _segment ][ bx ], dx
        stordarg _returnVector, dx, ax                  ; actual terminate address

        mov bx, offset ( intCONTROLC * 4 )              ; Int23 control-C vector
        mov ax, word ptr es:[ pspControlCVect. _pointer ]
        mov dx, word ptr es:[ pspControlCVect. _segment ]
        mov word ptr [ _pointer ][ bx ], ax
        mov word ptr [ _segment ][ bx ], dx

        mov bx, offset ( intCRITICALERROR * 4 )         ; Int24 criterror vector
        mov ax, word ptr es:[ pspCritErrorVect. _pointer ]
        mov dx, word ptr es:[ pspCritErrorVect. _segment ]
        mov word ptr [ _pointer ][ bx ], ax
        mov word ptr [ _segment ][ bx ], dx

        pop ds

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  free all allocated memory
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        call _SFTCloseAllFiles                          ; close/ commit all files

        mov bx, word ptr ss:[ _RxDOS_CurrentPSP ]
        call _releaseOwnerMemoryBlocks                  ; release memory
        call _collectMemoryBlocks                       ; collect free blocks

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return to parent process
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg es, _parentPSP                           ; get PSP
        mov word ptr ss:[ _RxDOS_CurrentPSP ], es       ; restore

        mov ax, word ptr es:[ pspUserStack. _pointer ]  ; parent user's stack
        mov dx, word ptr es:[ pspUserStack. _segment ]

        mov bx, word ptr ss:[ _RxDOS_CurrentInstance ]  ; base address of current stack
        mov word ptr ss:[ _segment ][ bx ], dx
        mov word ptr ss:[ _pointer ][ bx ], ax

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_TerminateProcess_20:
        RetCallersStackFrame es, bx
        getdarg dx, ax, _returnVector
        mov word ptr es:[ _CS ][ bx ], dx
        mov word ptr es:[ _IP ][ bx ], ax

        dec word ptr [ _RxDOS_AbortInProgress ]         ; restore critical error
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  4Dh Get Return Code                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;...............................................................;

_GetReturnCode:

        mov ax, word ptr [ _RxDOS_ChildReturnCode ]
        mov word ptr [ _RxDOS_ChildReturnCode ], 0000

        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  4Eh Find First Matching Name                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx   file name                                            ;
        ;  cx      attributes                                           ;
        ;...............................................................;

_FindFirstFile:

        Entry
        def  _findType, 0000
        def  _attributes, cx
        ddef _filename, es, dx                          ; arg passed internally as es:dx
        defbytes _dirAccess, sizeDIRACCESS
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  make sure path is ok, try to match file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, (FILEHAS_WILDCHARS)
        mov si, dx                                      ; name from caller (es: si )
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; don't worry about finding a file
        storarg _findType, ax                           ; find type/ drive

    ;   mov ax, offset pexterrNoMoreFiles
        jc _FindFirstFile_50                            ; error code already set -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  setup find search template
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor ax, ax
        mov cx, sizeFINDENTRY
        les di, dword ptr [ _RxDOS_pDTA ]
        rep stosb                                       ; clear area ( 0 handle )

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  start at begining of directory, locate by attribute and name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov si, word ptr [ _dirAccess. fileAcNameOffset ][ bp ]
        mov di, word ptr [ _RxDOS_pDTA. _pointer ]
        lea di, offset findSrchName [ di ]
        call convFilenametoFCBString                    ; convert to a match template

        les di, dword ptr [ _RxDOS_pDTA ]
        lea si, offset findSrchDrive [ di ]
        lea di, offset findFileName  [ di ]
        saveRegisters es, si, es, di                    ; arguments
        call convFCBNametoASCIZ                         ; build asciz name

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  start at begining of directory, locate by attribute and name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov di, word ptr [ _RxDOS_pDTA. _pointer ]
        mov al, byte ptr [ _dirAccess. fileAcDrive ][ bp ]
        mov byte ptr es:[ findSrchDrive ][ di ], al

        mov cl, byte ptr [ _attributes ][ bp ]
        mov byte ptr es:[ findSrchAttributes ][ di ], cl

        mov ax, word ptr [ _dirAccess. fileAcDirCluster ][ bp ]
        mov word ptr es:[ findDirBegCluster  ][ di ], ax
        mov word ptr es:[ findDirCurrCluster ][ di ], ax
        mov word ptr es:[ findDirEntry       ][ di ], -1

        mov ax, word ptr [ _dirAccess. fileAcDevicePtr._segment ][ bp ]
        or ax, word ptr [ _dirAccess. fileAcDevicePtr._pointer ][ bp ]
        jnz _FindFirstFile_50                           ; if device, item found -->

        mov ax, offset pexterrNoMoreFiles
        test word ptr [ _findType ][ bp ], 8000h
        stc                                             ; if error
        jnz _FindFirstFile_50                           ; no need to search if device -->

        call LocateFileByAttribute                      ; lookup by attribute 

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_FindFirstFile_50:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax                  ; return possible error code
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  4Fh Find Next Matching Name                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  no parameters. uses find record in DTA.                      ;
        ;...............................................................;

_FindNextFile:

        les di, dword ptr [ _RxDOS_pDTA ]

        mov ax, offset pexterrNoMoreFiles
        cmp word ptr es:[ findDirEntry ][ di ], -1      ; 
        stc                                             ; assume not found in previous
        jz _FindNextFile_08                             ; if not found in find first -->

        inc word ptr es:[ findDirEntry ][ di ]
        call LocateFileByAttribute                      ; lookup by attribute 

_FindNextFile_08:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax                  ; return possible error code
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  50h Set PSP Address                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx      contains PSP address to use                          ;
        ;...............................................................;

_SetPSPAddress:

        mov word ptr [ _RxDOS_CurrentPSP ], bx          ; Seg Pointer to current PSP
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  51h Get PSP Address                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx      PSP value returned                                   ;
        ;...............................................................;

_GetPSPAddress:

        mov bx, word ptr [ _RxDOS_CurrentPSP ]          ; Seg Pointer of current PSP

        RetCallersStackFrame es, si
        mov word ptr es:[ _BX  ][ si ], bx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  52h Get Dos Data Table Pointer                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  es:bx returns pointer to dos device parameter block          ;
        ;                                                               ;
        ; --- DOS Undocumented Feature -------------------------------- ;
        ;...............................................................;

_GetDosDataTablePtr:

        RetCallersStackFrame es, si
        mov word ptr es:[ _ExtraSegment ][ si ], ds
        mov word ptr es:[ _BX ][ si ], offset _RxDOS_pDPB

        clc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  53h Translate a BIOS Parameter Block                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ds:si  pointer to BIOS Paramater Block                      ;
        ;   es:bp  pointer to DOS Drive Parameter Block                 ;
        ;                                                               ;
        ; --- DOS Undocumented Feature -------------------------------- ;
        ;...............................................................;

_TranslateBIOSParameterBlock:

        RetCallersStackFrame es, bx

        push word ptr es:[ _DataSegment ][ bx ]
        push word ptr es:[ _SI ][ bx ]                  ; BIOS Parameter Block
        push word ptr es:[ _ExtraSegment ][ bx ]
        push word ptr es:[ _BP ][ bx ]                  ; DOS Drive Parameter Block
        call DefineDPB
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  54h Get DOS Verify Setting                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;   al   00, if verify is off                                   ;
        ;        01, if verify is on                                    ;
        ;                                                               ;
        ;  (see related function 2E - Set/Reset Verify Switch )         ;
        ;...............................................................;

_GetVerify:

        mov al, byte ptr [ _RxDOS_Verify ]

        RetCallersStackFrame ds, bx
        mov byte ptr [ _AX._AL ][ bx ], al
        or al, al
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  55h Duplicate PSP                                            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  dx    segment where to setup new PSP                         ;
        ;  si    value to place in memory size field                    ;
        ;                                                               ;
        ;  (see related function 26 - Create PSP )                      ;
        ;                                                               ;
        ; --- DOS Undocumented Feature -------------------------------- ;
        ;...............................................................;

_DuplicatePSP:

        push dx                                         ; new seg
        push si                                         ; new size

        mov es, dx                                      ; new PSP segment address
        call copyCurrentPSP                             ; create a new PSP here

        pop si
        pop es                                          ; new PSP address
        mov word ptr es:[ pspNextParagraph ], si        ; set size
        mov word ptr [ _RxDOS_CurrentPSP ], es          ; change running PSP

        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  56h Rename File                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ds:dx pointer to ASCIZ existing file/path                    ;
        ;  es:di pointer to ASCIZ rename file/path                      ;
        ;...............................................................;

_RenameFile:

        Entry
        ddef _wherePointer
        defbytes _existfileAccess, sizeDIRACCESS
        defbytes _renfileAccess, sizeDIRACCESS

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  does existing file exist ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, (FILE_NODEVICENAME + FILECANNOT_BEDIRECTORY)
        mov si, dx                                      ; name from caller
        lea di, offset _existfileAccess [ bp ]          ; work dir access block
        call LocateFile                                 ; check file path
        ifc _renameFile_40                              ; if file does not exist -->

        push ds
        RetCallersStackFrame ds, bx
        mov es, word ptr [ _ExtraSegment ][ bx ]
        mov si, word ptr [ _DI           ][ bx ]        ; get user's parameter
        pop ds

        mov ax, (FILE_NODEVICENAME + FILECANNOT_BEDEFINED + FILECANNOT_BEDIRECTORY)
        lea di, offset _renfileAccess [ bp ]            ; renamed file dir access block
        call LocateFile                                 ; check file path
        ifc _renameFile_40                              ; if file does not exist -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  both files seem ok, so we'll move them 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov al, byte ptr [ _existfileAccess. fileAcDrive ][ bp ]
        cmp al, byte ptr [ _renfileAccess. fileAcDrive ][ bp ]
        mov ax, offset pexterrPathNotFound
        stc
        ifnz _renameFile_40                             ; cannot move across drives -->

        mov ax, word ptr [ _existfileAccess. fileAcDirCluster ][ bp ]
        cmp ax, word ptr [ _renfileAccess. fileAcDirCluster ][ bp ]
        jnz _renameFile_22                              ; if must move files across dirs -->

        setDS ss
        mov si,  word ptr [ _renfileAccess. fileAcNameOffset  ][ bp ]
        les di, dword ptr [ _existfileAccess. fileAcBufferPtr ][ bp ]
        add di,  word ptr [ _existfileAccess. fileAcDirOffset ][ bp ]
        call convFilenametoFCBString                    ; convert name

        les si, dword ptr [ _existfileAccess. fileAcBufferPtr ][ bp ]
        call locateCCBPHeader
        call CCBChanged                                 ; update buffer
        jmp short _renameFile_38                        ; exit -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  move entry between directories
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_renameFile_22:
        and word ptr [ _renfileAccess. fileAcDrive   ][ bp ], 7FFFh
        mov ax, word ptr [ _renfileAccess. fileAcDrive   ][ bp ]
        mov dx, word ptr [ _renfileAccess. fileAcDirCluster ][ bp ]
        call LocateFreeDirSlot                          ; can we find a valid empty entry ?
        ifc _renameFile_40                              ; if can't find free slot -->

        stordarg _wherePointer, es, si        
        mov di, si                                      ; where entry must be copied to

        lds si, dword ptr [ _existfileAccess. fileAcBufferPtr ][ bp ]
        add si,  word ptr [ _existfileAccess. fileAcDirOffset ][ bp ]
        mov cx, ( sizeDIRENTRY / 2 )
        rep movsw                                       ; copy entry from source dir

        setDS ss
        getdarg es, di, _wherePointer
        mov si, word ptr [ _renfileAccess. fileAcNameOffset ][ bp ]
        call convFilenametoFCBString                    ; rename file

        getdarg es, si, _wherePointer
        call locateCCBPHeader
        call CCBChanged                                 ; update buffer

        les di, dword ptr [ _existfileAccess. fileAcBufferPtr ][ bp ]
        mov bx,  word ptr [ _existfileAccess. fileAcDirOffset ][ bp ]
        mov byte ptr es:[ di + bx ], DIRENTRY_DELETED
        call CCBChanged

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_renameFile_38:
        mov ax, word ptr [ _renfileAccess. fileAcDrive   ][ bp ]
        call updateDriveBuffers
        mov ax, word ptr [ _existfileAccess. fileAcDrive ][ bp ]
        call updateDriveBuffers
        or ax, ax                                       ; no carry

_renameFile_40:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  5700h Get File Date Time                                     ;
        ;  5701h Set File Date Time                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al = 00, get date/time                                       ;
        ;       01, set date/time                                       ;
        ;                                                               ;
        ;  bx   file handle                                             ;
        ;  cx   time                                                    ;
        ;  dx   date                                                    ;
        ;                                                               ;
        ;...............................................................;

_SetFileDateTime:

        call CtrlC_Check                                ; look-ahead CtrlC Check

        push ax                                         ; save mode
        mov ax, bx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        pop ax                                          ; restore mode
        jc _SetFileDateTime_20                          ; if could not find -->

        Goto 00h, _GetFileDateTime                      ; get date/ time
        Goto 01h, _SetFileDateTime_06                   ; set date/ time

        SetError pexterrInvalidHandle, _SetFileDateTime_20

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set date/ time
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetFileDateTime_06:
        RetCallersStackFrame ds, bx
        mov cx, word ptr [ _CX ][ bx ]                  ; restore arguments
        mov dx, word ptr [ _DX ][ bx ]

        mov word ptr es:[ sftTime ][ di ], cx           ; time
        mov word ptr es:[ sftDate ][ di ], dx           ; date

        cmp word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jnz _SetFileDateTime_12                         ; if device -->
        or word ptr es:[ sftDevInfo ][ di ], sftDateset + sftWritten 

_SetFileDateTime_12:
        clc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get date/ time
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_GetFileDateTime:
        RetCallersStackFrame ds, bx
        mov cx, word ptr es:[ sftTime ][ di ]           ; time
        mov dx, word ptr es:[ sftDate ][ di ]           ; date
        mov word ptr [ _CX ][ bx ], cx
        mov word ptr [ _DX ][ bx ], dx
        or ax, ax                                       ; no carry

_SetFileDateTime_20:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  5800h Get Allocation Strategy                                ;
        ;  5801h Set Allocation Strategy                                ;
        ;  5802h Get Upper Memory Link                                  ;
        ;  5803h Set Upper Memory Link                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  al = mode, as shown above                                    ;
        ;                                                               ;
        ;  bx   allocation strategy on set                              ;
        ;  ax   allocation strategy on get                              ;
        ;                                                               ;
        ;...............................................................;

_GetAllocationStrategy:

        Goto 00, _GetAllocStrategy
        Goto 01, _SetAllocStrategy
        Goto 02, _GetUpperMemLink
        Goto 03, _SetUpperMemLink

        SetError pexterrInvalidFunction, _ChangeFileMode_18 ; else -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Get allocation strategy
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_GetAllocStrategy:
        RetCallersStackFrame es, bx
        mov ax, word ptr [ _RxDOS_AllocStrategy ]
        mov word ptr es:[ _AX ][ bx ], ax
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set allocation strategy
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetAllocStrategy:

        cmp bx, _MEM_FIRSTFIT_LOW                       ; must be greater than this
        jl _SetAllocStrategy_22                         ; if error -->
        cmp bx, _MEM_LASTFIT_LOW                        ; low memory range values
        jle _SetAllocStrategy_08                        ; if ok -->

        cmp bx, _MEM_FIRSTFIT_HIGHONLY                  ; must be greater than this
        jl _SetAllocStrategy_22                         ; if error -->
        cmp bx, _MEM_LASTFIT_HIGHONLY                   ; high only memory range values
        jle _SetAllocStrategy_08                        ; if ok -->

        cmp bx, _MEM_FIRSTFIT_HIGH                      ; must be greater than this
        jl _SetAllocStrategy_22                         ; if error -->
        cmp bx, _MEM_LASTFIT_HIGH                       ; high memory range values
        jle _SetAllocStrategy_08                        ; if ok -->

_SetAllocStrategy_08:
        clc
        mov word ptr [ _RxDOS_AllocStrategy ], bx       ; if valid.
        ret

_SetAllocStrategy_22:
        mov ax, offset pexterrInvalidFunction           ; if invalid.
        stc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Get allocation strategy
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_GetUpperMemLink:
        RetCallersStackFrame es, bx
        mov ax, word ptr [ _RxDOS_UMBAllowed ]          ; set allow/disallow flag
        mov word ptr es:[ _AX ][ bx ], ax
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Get allocation strategy
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetUpperMemLink:
        cmp word ptr [ _RxDOS_UMBEnabled ], 0000        ; UMB enabled ?
        jz _SetUpperMemLink_08                          ; no -->

        cmp bx, 0000h
        jz _SetUpperMemLink_04
        cmp bx, 0001h
        jnz _SetUpperMemLink_08

_SetUpperMemLink_04:
        clc
     ;  mov word ptr [ _RxDOS_UMBAllowed ], bx          ; set allow/disallow flag
     ;  ret

    ; always return error until UMB support can be provided

_SetUpperMemLink_08:
        mov ax, offset pexterrInvalidFunction
        stc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  59h Get Extended Error                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  ax      extended error code                                  ;
        ;  bh      error class                                          ;
        ;  bl      suggested action                                     ;
        ;  ch      locus                                                ;
        ;...............................................................;

_GetExtendedError:

        RetCallersStackFrame es, si

        cmp word ptr [ _RxDOS_ExtErrorFlag ], TRUE
        jnz _GetExtendedError_08                        ; lookup error -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; error set by app
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov word ptr [ _RxDOS_ExtErrorFlag ], FALSE

        mov ax, word ptr [ _RxDOS_ExtErrorInfo. errSI ]
        mov cx, word ptr [ _RxDOS_ExtErrorInfo. errDS ]
        mov dx, word ptr [ _RxDOS_ExtErrorInfo. errES ]

        mov word ptr es:[ _SI         ][ si ], ax
        mov word ptr es:[ _DataSegment  ][ si ], cx
        mov word ptr es:[ _ExtraSegment ][ si ], dx

        mov ax, word ptr [ _RxDOS_ExtErrorInfo. errAX ]
        mov bx, word ptr [ _RxDOS_ExtErrorInfo. errBX ]
        mov cx, word ptr [ _RxDOS_ExtErrorInfo. errCX ]
        mov dx, word ptr [ _RxDOS_ExtErrorInfo. errDX ]
        mov di, word ptr [ _RxDOS_ExtErrorInfo. errDI ]

        mov word ptr es:[ _AX ][ si ], ax
        mov word ptr es:[ _BX ][ si ], bx
        mov word ptr es:[ _CX ][ si ], cx
        mov word ptr es:[ _DX ][ si ], dx
        mov word ptr es:[ _DI ][ si ], di

        clc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; error set by RxDOS
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_GetExtendedError_08:

        xor ax, ax
        xor bx, bx
        xor cx, cx
        xor dx, dx                                      ; if no error 
        xchg bx, word ptr [ _RxDOS_pExtErrorcode ]      ; no longer need to save error ptr
        or bx, bx                                       ; no actual error saved ?
        jz _GetExtendedError_12                         ; skip around -->

        mov ax, word ptr cs:[ ExtErrorCodeValue ][ bx ] ; else get error values
        mov ch, byte ptr cs:[ Locus             ][ bx ]
        mov dl, byte ptr cs:[ SuggestedAction   ][ bx ]
        mov dh, byte ptr cs:[ ErrorClass        ][ bx ]
        
_GetExtendedError_12:
        mov word ptr [ _RxDOS_ExtErrorcode    ], ax
        mov byte ptr [ _RxDOS_LocusLasterror  ], ch
        mov byte ptr [ _RxDOS_SuggestedAction ], dl
        mov byte ptr [ _RxDOS_ClassOfError    ], dh

        mov word ptr es:[ _AX       ][ si ], ax         ; return extended error code
        mov byte ptr es:[ _CX. _CH  ][ si ], ch
        mov word ptr es:[ _BX       ][ si ], dx

        clc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  5Ah Create Unique File Name                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  cx      attributes                                           ;
        ;  ds:dx   pointer to asciz containing path                     ;
        ;...............................................................;

_CreateUniqueFile:

        Entry
        def  _handle, -1
        def  _tempnamePointer
        def  _bytecount
        def  _attributes, cx                            ; attributes
        ddef _returnnamePointer, es, dx
        defbytes _dirAccess, sizeDIRACCESS
        defbytes _tempname, 128
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  file handle available ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call CtrlC_Check                                ; look-ahead CtrlC Check
        call VerifyAvailableHandle                      ; see if handle available
        ifc _CreateUniqueFile_40                        ; exit if none -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy buffer
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        push word ptr [ _returnnamePointer. _segment ][ bp ]
        push word ptr [ _returnnamePointer. _pointer ][ bp ]
        mov cx, 128                                     ; maximum string length
        call condStringLength
        jz _CreateUniqueFile_08

        SetError pexterrIllegalName, _CreateUniqueFile_40

_CreateUniqueFile_08:
        storarg _bytecount, cx                          ; save byte count
        push ax                                         ; character just before null

        push word ptr [ _returnnamePointer. _segment ][ bp ]
        push word ptr [ _returnnamePointer. _pointer ][ bp ]
        push ss
        lea di, offset _tempname [ bp ]
        push di
        call CopyString

        pop ax
        cmp al, '\'                                     ; ended with \ ?
        jz _CreateUniqueFile_12                         ; yes -->
        cmp al, '/'                                     ; ended with / ?
        jz _CreateUniqueFile_12                         ; yes -->

        mov al, '\'
        stosb                                           ; we'll make one.

_CreateUniqueFile_12:
        storarg _tempnamePointer, di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  create a unique filename
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        call getExpandedDateTime                        ; expand time ch:cl dh:dl

        getarg di, _tempnamePointer

        mov al, ch                                      ; hours
        call __ascii_stosb

        mov al, cl                                      ; minutes
        call __ascii_stosb

        mov al, dh                                      ; seconds
        call __ascii_stosb

        mov al, dl                                      ; hundreths of second
        call __ascii_stosb

        xor al, al
        stosb                                           ; null term

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  make sure file does not exist
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_CreateUniqueFile_16:
        mov ax, (FILE_NODEVICENAME + FILECANNOT_BEDEFINED + FILECANNOT_BEDIRECTORY)
        lea si, offset _tempname [ bp ]
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; check file path
        jnc _CreateUniqueFile_36                        ; if path invalid -->

        getarg di, _tempnamePointer
        mov cx, (sizefnName - 1)
        add di, cx

_CreateUniqueFile_18:
        cmp byte ptr ss:[ di ], 'Z'                     ; already at end of alphabet ?
        jnz _CreateUniqueFile_32                        ; no -->
        mov byte ptr ss:[ di ], 'A'                     ; cycle to prev column
        dec di                                          ; adj address
        loop _CreateUniqueFile_18                       ; and loop -->
        stc                                             ; just in case
        jmp short _CreateUniqueFile_40                  ; we'll have an error exit -->

_CreateUniqueFile_32:
        inc byte ptr ss:[ di ]
        jmp _CreateUniqueFile_16

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  it doesn't, so we'll make one.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_CreateUniqueFile_36:
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        mov cx, word ptr [ _attributes ][ bp ]          ; get attributes
        and cx, not ( ATTR_VOLUME + ATTR_DIRECTORY )
        call createSFTEntry                             ; create SFT entry
        jc _CreateUniqueFile_40                         ; if error -->

        call MapSFTtoAppHandle                          ; if no space, create error

        push ax
        push ss
        push word ptr [ _tempnamePointer ][ bp ]
        getdarg es, di, _returnnamePointer
        add di, word ptr [ _bytecount ][ bp ]           ; point to null terminator

        push es
        push di
        call CopyString                                 ; copy name back to user

        pop ax
        clc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_CreateUniqueFile_40:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  5Bh Create New File                                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  cx      attributes                                           ;
        ;  ds:dx   pointer to filename( path included )                 ;
        ;...............................................................;

_CreateNewFile:

        Entry
        def  _attributes, cx                            ; attributes
        defbytes _dirAccess, sizeDIRACCESS
        
        call CtrlC_Check                                ; look-ahead CtrlC Check
        call VerifyAvailableHandle                      ; see if handle available
        jc _CreateNewFile_40                            ; exit if none -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  make sure file does not exist
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        RetCallersStackFrame es, bx
        mov si, word ptr es:[ _DX ][ bx ]
        mov es, word ptr es:[ _DataSegment ][ bx ]

        mov ax, (FILE_NODEVICENAME + FILECANNOT_BEDEFINED + FILECANNOT_BEDIRECTORY)
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; check file path
        jnc _CreateNewFile_16                           ; if valid -->

        test word ptr [ _dirAccess. fileAcDrive ][ bp ], 8000h  ; not found ?
        jnz _CreateNewFile_14                           ; ok, error code is fine ->
        mov ax, offset pexterrFileExists                ; upgrade error code

_CreateNewFile_14:
        stc
        jmp short _CreateNewFile_40

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  ok to create new.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_CreateNewFile_16:
        RetCallersStackFrame es, bx
        mov cx, word ptr es:[ _CX ][ bx ]
        mov dx, word ptr es:[ _DX ][ bx ]
        mov es, word ptr es:[ _DataSegment ][ bx ]

        lea di, offset _dirAccess [ bp ]                ; work dir access block
        mov cx, word ptr [ _attributes ][ bp ]          ; get attributes
        call createSFTEntry                             ; create SFT entry
        jc _CreateNewFile_40                            ; exit if none or error -->

        call MapSFTtoAppHandle                          ; if no space, create error

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_CreateNewFile_40:
        RetCallersStackFrame ds, bx
        mov word ptr [ _AX ][ bx ], ax
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  5Dxxh Server, Share and Swap                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;                                                               ;
        ; --- DOS Undocumented Feature -------------------------------- ;
        ;...............................................................;

_ServerShareAndSwap:

        RetCallersStackFrame es, bx
        Goto 00h, _ShareUnimplemented
        Goto 06h, _GetSwappableDataArea
        Goto 0Ah, _SetExtendedError

_ShareUnimplemented:
        mov ax, offset pexterrInvalidFunction
        stc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get swappable data area
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_GetSwappableDataArea:

        mov word ptr es:[ _DataSegment ][ bx ], ss
        mov word ptr es:[ _SI          ][ bx ], offset SDABeginArea
        mov word ptr es:[ _CX          ][ bx ], SDAExtendedSwapArea - SDABeginArea
        mov word ptr es:[ _DX          ][ bx ], SDAEndArea - SDABeginArea
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  set extended error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetExtendedError:

        mov word ptr [ _RxDOS_ExtErrorFlag ], TRUE      ; extended error is true

        mov ds, word ptr es:[ _DataSegment ][ bx ]
        mov si, word ptr es:[ _DataSegment ][ bx ]      ; where extended info available
        mov di, offset _RxDOS_ExtErrorInfo
        mov cx, sizeERROR / 2
        rep movsw                                       ; copy words

        clc        
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  60h Get Actual (Expanded) File Name                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ds:si pointer to filename( path included )                  ;
        ;   es:di pointer to expanded filename                          ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax  0000  if name is local.                                 ;
        ;   ax  005c  if name is networked.                             ;
        ;                                                               ;
        ; --- DOS Undocumented Feature -------------------------------- ;
        ;...............................................................;

_GetActualFileName:

        RetCallersStackFrame es, bx
        push word ptr es:[ _DataSegment ][ bx ]         ; non-canonical name
        push word ptr es:[ _SI ][ bx ]
        push word ptr es:[ _ExtraSegment ][ bx ]        ; expanded filename
        push word ptr es:[ _DI ][ bx ]

        mov ax, FILECANNOT_BEDIRECTORY
        call ExpandFileName
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  63h Get DBCS String Pointer                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ds:si points to DBCS string.                                ;
        ;   cy    if error                                              ;
        ;                                                               ;
        ;                                                               ;
        ;  This function is not supported in RxDOS.                     ;
        ;  Always returns ptr to NULL double bytes.                     ;
        ;                                                               ;
        ;                                                               ;
        ; --- DOS Undocumented Feature -------------------------------- ;
        ;...............................................................;

_GetDBCSString:

        RetCallersStackFrame ds, bx
        mov word ptr [ _SI          ][ bx ], offset _RxDOS_DBCS_Table
        mov word ptr [ _DataSegment ][ bx ], cs

        clc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  65h Country Dependent Functions                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al    function                                              ;
        ;         01h  get extended country info                        ;
        ;                                                               ;
        ;         02h  get uppercase table                              ;
        ;                                                               ;
        ;         04h  get filename uppercase table                     ;
        ;                                                               ;
        ;         05h  get filename character table                     ;
        ;                                                               ;
        ;         06h  get collate sequence table                       ;
        ;                                                               ;
        ;         07h  get double byte character set                    ;
        ;                                                               ;
        ;         20h  character capitalization                         ;
        ;               dl contains character                           ;
        ;               dh 2nd byte in double byte languages            ;
        ;                                                               ;
        ;         21h  length defined string capitalization             ;
        ;               ds:dx string pointer                            ;
        ;               cx    length                                    ;
        ;                                                               ;
        ;         22h  ASCIZ capitalization                             ;
        ;               ds:dx string pointer                            ;
        ;                                                               ;
        ;         23h  does character represent Yes/No response         ;
        ;               dl contains character                           ;
        ;               dh 2nd byte in double byte languages            ;
        ;                                                               ;
        ;               returns: ax = 00 if yes                         ;
        ;                             01 if no                          ;
        ;                             02 if other                       ;
        ;                                                               ;
        ;         A0h  filename character capitalization                ;
        ;               dl contains character                           ;
        ;                                                               ;
        ;         A1h  filename length defined string capitalization    ;
        ;               ds:dx string pointer                            ;
        ;               cx    length                                    ;
        ;                                                               ;
        ;         A2h  filename ASCIZ capitalization                    ;
        ;               ds:dx string pointer                            ;
        ;                                                               ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cy    if error                                              ;
        ;                                                               ;
        ;...............................................................;

_ExtCountryDependentFunctions:

        mov ah, al                                      ; copy function to ah also

;;;;        Goto 01h,                   _GetExtendedCountryInfo
        Goto 02h,                   _GetUpperCaseTable
        Goto 04h,                   _GetFilenameUpperCaseTable
        Goto 05h,                   _GetFilenameCharacterTable
        Goto 06h,                   _GetCollateSequenceTable
        Goto 07h,                   _GetDoubleByteCharacterSet

        Goto _CapCharacter,         _Capitalize_08
        Goto _CapLengthDefString,   _Capitalize_12
        Goto _CapString,            _Capitalize_22
        Goto _CountryDepYesNo,      _Capitalize_30

        Goto _CapFnCharacter,       _Capitalize_08
        Goto _CapFnLengthDefString, _Capitalize_12
        Goto _CapFnString,          _Capitalize_22

_GetUpperCaseTable:
_GetFilenameUpperCaseTable:
_GetFilenameCharacterTable:
_GetCollateSequenceTable:
_GetDoubleByteCharacterSet:

        mov ax, offset pexterrInvalidFunction
        stc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Get Extended Country Info
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_GetExtendedCountryInfo:
        cmp bx, -1                                      ; if get info
        jz _GetExtendedCountryInfo_08                   ; go do get -->

        mov word ptr [ _RxDOS_UserCodePage ], bx        ; should check values
        mov word ptr [ _RxDOS_UserCountryCode ], dx     ; should check values
        or bx, bx
        ret

_GetExtendedCountryInfo_08:
        mov ax, word ptr [ _RxDOS_UserCodePage ]        ; should check values
        mov dx, word ptr [ _RxDOS_UserCountryCode ]     ; should check values

        RetCallersStackFrame es, bx
        mov word ptr es:[ _BX ][ bx ], ax               ; code page
        mov word ptr es:[ _DX ][ bx ], dx               ; country code

        or bx, bx
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  capitalize character
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Capitalize_08:
        mov al, dl
        call upperCase
        RetCallersStackFrame ds, bx
        mov byte ptr [ _DX._DL ][ bx ], al
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  capitalize length defined string (was ds:dx, now es:di)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Capitalize_12:
        or cx, cx
        jz _Capitalize_18

_Capitalize_14:
        mov al, byte ptr es:[ di ]
        call upperCase
        mov byte ptr es:[ di ], al
        inc di
        loop _Capitalize_14

_Capitalize_18:
        clc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  capitalize ASCIZ string (was ds:dx, now es:di)
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Capitalize_22:
        mov al, byte ptr es:[ di ]
        or al, al
        jz _Capitalize_28

        call upperCase
        mov byte ptr es:[ di ], al
        inc di
        jmp _Capitalize_22

_Capitalize_28:
        clc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return country dependent yes/no answer
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_Capitalize_30:
        mov al, dl
        call upperCase

        ;** this is of course wrong for now !!

        mov dl, 00h                                     ; if yes 
        cmp al, 'Y'
        jz _Capitalize_34

        mov dl, 01h                                     ; if no 
        cmp al, 'N'
        jz _Capitalize_34

        mov dl, 02h                                     ; if neither

_Capitalize_34:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  86h Global Code Page Settings                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Get Global Code Page:                                        ;
        ;   al    01                                                    ;
        ;   bx    user code page                                        ;
        ;   dx    system code page                                      ;
        ;                                                               ;
        ;  Set Global Code Page:                                        ;
        ;   al    02                                                    ;
        ;   bx    user code page                                        ;
        ;   dx    system code page                                      ;
        ;                                                               ;
        ;...............................................................;

_GlobalCodePage:

        RetCallersStackFrame es, bx

        Goto 01,                    _GetCodePage
        Goto 02,                    _SetCodePage

        mov ax, offset pexterrInvalidFunction
        stc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Get Code Page Value
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_GetCodePage:
        mov ax, word ptr [ _RxDOS_UserCodePage ]
        mov dx, word ptr [ _RxDOS_SystemCodePage ]
        mov word ptr es:[ _BX    ][ bx ], ax
        mov word ptr es:[ _DX    ][ bx ], dx
        ret        

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  Set Code Page Value
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SetCodePage:
        mov ax, word ptr es:[ _BX    ][ bx ]
        mov dx, word ptr es:[ _DX    ][ bx ]
        mov word ptr [ _RxDOS_UserCodePage ], ax
        mov word ptr [ _RxDOS_SystemCodePage ], dx
        ret        

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  67h Set Handles Count                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx number of file handles to allocate to an application      ;
        ;...............................................................;

_SetHandlesCount:

        Entry
        def _handles, bx
        def _allocatedseg

        mov es, word ptr [ _RxDOS_CurrentPSP ]
        cmp bx, word ptr es:[ pspFileHandleCount ]      ; more than current ?
        jnc _SetHandlesCount_08                         ; yes, relocate -->
        call TestIfMoveHandleTable                      ; else, see if can relocate back to PSP
        jmp _SetHandlesCount_26

_SetHandlesCount_08:
        add bx, ( sizeParagraph - 1 )
        shr bx, 1
        shr bx, 1
        shr bx, 1
        shr bx, 1                                       ; compute segments required

        call _allocateUpperMB                           ; allocate upper mem blocks
        jnc _SetHandlesCount_12                         ; if allocation made -->
        call _allocateConvMB                            ; allocate lower mem blocks
        jc _SetHandlesCount_30                          ; if allocation not made -->

_SetHandlesCount_12:
        mov es, ax                                      ; new segment
        storarg _allocatedseg, ax                       ; save

        getarg cx, _handles                             ; allocated request
        add cx, ( sizeParagraph - 1 )
        and cx, not ( sizeParagraph - 1 )               ; allocated size
        shr cx, 1                                       ; optimize for words
        xor di, di
        mov ax, -1
        rep stosw                                       ; clear words

        push ds
        push es
        mov es, word ptr [ _RxDOS_CurrentPSP ]          ; get PSP
        mov cx, word ptr es:[ pspFileHandleCount ]      ; source count
        lds si, dword ptr es:[ pspFileHandlePtr ]       ; source pointer

        pop es
        xor di, di                                      ; where to copy to
        rep movsb                                       ; copy table

        pop ds                                          ; restore current segment
        getarg ax, _allocatedseg                        ; allocated segment
        mov es, word ptr [ _RxDOS_CurrentPSP ]
        mov word ptr es:[ pspFileHandlePtr. _segment ], ax
        mov word ptr es:[ pspFileHandlePtr. _pointer ], 0000

        getarg bx, _handles
        mov word ptr es:[ pspFileHandleCount ], bx

_SetHandlesCount_26:
        clc

_SetHandlesCount_30:
        Return 

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  68h Commit File                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx handle to open file                                       ;
        ;...............................................................;

_CommitFile:

        call CtrlC_Check                                ; look-ahead CtrlC Check

        mov ax, bx                                      ; handle
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _CommitFile_error                            ; if could not find -->

        call _SFTCommitFile                             ; commit SFT Entry
        or bx, bx                                       ; no carry

_CommitFile_error:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  6Ch Extended Open/Create                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx    mode                                                   ;
        ;  cx    attributes                                             ;
        ;  dx    action to take                                         ;
        ;  ds:si pointer to filename                                    ;
        ;...............................................................;

_ExtendedOpenCreate:

        Entry
        def  _mode, bx
        def  _attributes, cx
        def  _action, dx
        def  _actionTaken, 0000
        def  _found, FALSE
        ddef _filename, es, si
        defbytes _dirAccess, sizeDIRACCESS

        call CtrlC_Check                                ; look-ahead CtrlC Check
        call VerifyAvailableHandle                      ; see if handle available
        ifc _ExtendedOpenCreate_42                      ; if error -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  determine if file exists
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        RetCallersStackFrame es, bx
        mov si, word ptr es:[ _SI ][ bx ]
        mov es, word ptr es:[ _DataSegment ][ bx ]

        mov ax, (FILE_NODEVICENAME + FILECANNOT_BEDIRECTORY)
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; check file path
        jnc _ExtendedOpenCreate_FileFound               ; if file found, see if replace -->

        storarg _found, FALSE
        test word ptr [ _action ][ bp ], EXTENDEDACTION_CREATE
        jnz _ExtendedOpenCreate_Create                  ; ok to create -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  open file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_ExtendedOpenCreate_Open:
        RetCallersStackFrame es, si
        mov dx, word ptr es:[ _SI           ][ si ]
        mov es, word ptr es:[ _DataSegment  ][ si ]
        mov ax, word ptr [ _mode ][ bp ]
        call _SFTOpenFile                               ; build an SFT
        jc _ExtendedOpenCreate_42                       ; if error -->

        call MapSFTtoAppHandle                          ; record SFT handle into JHT
        mov word ptr [ _actionTaken ][ bp ], ACTION_OPENED
        jmp short _ExtendedOpenCreate_42

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  create file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_ExtendedOpenCreate_Create:
        RetCallersStackFrame es, si
        mov cx, word ptr es:[ _CX          ][ si ]      ; attributes
        mov dx, word ptr es:[ _SI          ][ si ]      ; filename in ds:si
        mov es, word ptr es:[ _DataSegment ][ si ]
        call _SFTCreateFile
        jc _ExtendedOpenCreate_42                       ; if error -->

        call MapSFTtoAppHandle                          ; record SFT handle into JHT

        mov cx, ACTION_REPLACED_OPENED
        cmp word ptr [ _found ][ bp ], TRUE
        jz _ExtendedOpenCreate_32                       ; if found, then was truncated ->
        mov cx, ACTION_CREATED_OPENED

_ExtendedOpenCreate_32:
        clc                                             ; no carry
        mov word ptr [ _actionTaken ][ bp ], cx
        jmp short _ExtendedOpenCreate_42

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if file found, determine if truncate
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_ExtendedOpenCreate_FileFound:
        storarg _found, TRUE
        test word ptr [ _action ][ bp ], EXTENDEDACTION_TRUNCATE
        jnz _ExtendedOpenCreate_Create                  ; ok to create/ truncate -->
        test word ptr [ _action ][ bp ], EXTENDEDACTION_OPEN
        jnz _ExtendedOpenCreate_Open                    ; ok to open -->

        stc
        mov ax, offset pexterrAccessDenied

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_ExtendedOpenCreate_42:
        RetCallersStackFrame es, bx
        mov word ptr es:[ _AX ][ bx ], AX

        getarg cx, _actionTaken
        mov word ptr es:[ _CX ][ bx ], CX

        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Unused / Unimplemented Functions                             ;
        ;...............................................................;

_UnusedReturnInst:
        RetCallersStackFrame es, bx
        mov word ptr es:[ _AX. _AL ][ bx ], 01h

        stc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Unused / Unimplemented Functions                             ;
        ;...............................................................;

_GetDiskSerialNumber:

_LockFileAccess:
_GetMachineName:
_GetRedirectionList:

_Unused:
        call _UnusedReturnInst
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Debug Interrupt Trap                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This code is called whenever one of the debug routines       ;
        ;                                                               ;
        ;                                                               ;
        ;                                                               ;
        ;...............................................................;

_DebugInterruptTrap:

        ret

RxDOS   ENDS
        END RxDOS_start
