mirror of
https://github.com/SEPPDROID/DR-DOS-OpenDOS.git
synced 2025-10-22 07:54:28 +00:00
1475 lines
41 KiB
Plaintext
1475 lines
41 KiB
Plaintext
title 'BDEVIF - Block DEVice Input/Output support'
|
|
; File : $BDEVIO.A86$
|
|
;
|
|
; Description :
|
|
;
|
|
; Original Author : DIGITAL RESEARCH
|
|
;
|
|
; Last Edited By : $CALDERA$
|
|
;
|
|
;-----------------------------------------------------------------------;
|
|
; Copyright Work of Caldera, Inc. All Rights Reserved.
|
|
;
|
|
; THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
|
|
; PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
|
|
; ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
|
|
; WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
|
|
; THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
|
|
; HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
|
|
; AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
|
|
; AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
|
|
; COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
|
|
; CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
|
|
; TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
|
|
; CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
|
|
; AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
|
|
; CIVIL LIABILITY.
|
|
;-----------------------------------------------------------------------;
|
|
;
|
|
; *** Current Edit History ***
|
|
; *** End of Current Edit History ***
|
|
;
|
|
; $Log$
|
|
; BDEVIO.A86 1.27 94/11/30 16:25:22
|
|
; added delayed retry for read/write to locked region
|
|
; added support for using multiple FAT copies on reads if one fails;
|
|
; BDEVIO.A86 1.26 94/02/22 17:11:25
|
|
; Fix where corrupt dir entry results in read beyond end-of-chain (Filelink bug)
|
|
; BDEVIO.A86 1.25 93/12/15 03:07:11
|
|
; New ddioif entry point so Int 25/26 bypasses address normalisation
|
|
; BDEVIO.A86 1.24 93/12/08 03:15:14
|
|
; Force rebuild_ldt_root if root in JOIN's subdirectory
|
|
; BDEVIO.A86 1.23 93/11/19 18:29:29
|
|
; Fix for SERVER print queue viewing problem
|
|
; BDEVIO.A86 1.22 93/09/21 12:43:37
|
|
; On fdos read/write do EOF checks before SHARE LOCK checks
|
|
; BDEVIO.A86 1.21 93/09/14 20:02:50
|
|
; Trust LFLG_PHYSICAL
|
|
; BDEVIO.A86 1.20 93/09/02 22:22:56
|
|
; Use 32 bit sectors to read fat for build bpb if appropriate (SYQUEST bug)
|
|
; BDEVIO.A86 1.19 93/08/27 18:46:49
|
|
; int 26 discards hash codes
|
|
; BDEVIO.A86 1.18 93/07/20 22:42:25
|
|
; Even fewer checks on int 25/26
|
|
; BDEVIO.A86 1.12 93/06/23 02:57:07
|
|
; Add auto-commit to fdowrw
|
|
; BDEVIO.A86 1.11 93/05/14 13:47:41
|
|
; Shorten media change code slightly
|
|
; BDEVIO.A86 1.9 93/03/16 22:30:21 IJACK
|
|
; UNDELETE support changes
|
|
; ENDLOG
|
|
|
|
eject ! include i:mserror.equ ; F_DOS erros
|
|
eject ! include i:fdos.equ
|
|
eject ! include i:driver.equ
|
|
eject ! include i:doshndl.def
|
|
eject ! include bdos.equ
|
|
eject ! include rh.equ
|
|
|
|
;*****************************************************
|
|
;*
|
|
;* bdos data area
|
|
;*
|
|
;*****************************************************
|
|
|
|
PCMODE_DATA dseg word
|
|
extrn current_ddsc:dword
|
|
extrn current_dhndl:dword
|
|
extrn current_dsk:byte ; default drive
|
|
extrn current_ldt:dword ; currently selected LDT
|
|
extrn dma_offset:word ; DTA offset
|
|
extrn dma_segment:word ; DTA segment
|
|
extrn ddsc_ptr:dword
|
|
extrn err_drv:byte
|
|
extrn error_dev:dword ; failing device for Int 24's
|
|
extrn fdos_stub:dword
|
|
extrn ioexerr:byte
|
|
extrn last_drv:byte
|
|
extrn ldt_ptr:dword
|
|
extrn lock_bios:dword
|
|
extrn phys_drv:byte
|
|
extrn rwmode:byte
|
|
extrn share_stub:dword
|
|
extrn unlock_bios:dword
|
|
extrn verify_flag:byte
|
|
extrn net_retry:word
|
|
|
|
BDOS_DATA dseg word
|
|
|
|
extrn bcb_root:dword
|
|
extrn deblock_seg:word
|
|
extrn fdos_hds_drv:byte
|
|
extrn fdos_hds_blk:word
|
|
extrn fdos_hds_root:word
|
|
extrn fdos_ret:word
|
|
|
|
public adrive
|
|
public clsize
|
|
public dosfat
|
|
public cur_dma
|
|
public cur_dma_seg
|
|
public datadd
|
|
public diradd
|
|
public dirinroot
|
|
public dirperclu
|
|
public fatadd
|
|
public hdsaddr
|
|
public lastcl
|
|
public logical_drv
|
|
public mult_sec
|
|
public nfatrecs
|
|
public nfats
|
|
public pblock
|
|
public physical_drv
|
|
public psecsiz
|
|
public req_hdr
|
|
public secperclu
|
|
|
|
eject
|
|
; The following specify the drive selected for the current operation
|
|
|
|
hdsaddr dw 0 ; current HDS address (0 means at root)
|
|
logical_drv db 0 ; logical drive number
|
|
physical_drv db 0 ; physical disk number
|
|
|
|
; The following describe the currently active drive - not this may differ from
|
|
; the currently selected drive above due to eg. flushing dirty buffers
|
|
|
|
; Local copy of DDSC_ variables - ORDER CRITICAL - must match DDSC_
|
|
|
|
local_ddsc rb 0
|
|
psecsiz dw 0 ; byte size of sector
|
|
clmsk db 0
|
|
clshf db 0
|
|
fatadd dw 0 ; sector offset of 1st FAT sector
|
|
byte_nfats db 0 ; number of FAT's
|
|
dirinroot dw 0 ; # dir entries in root
|
|
datadd dw 0 ; sector offset of data sector
|
|
lastcl dw 0 ; # last cluster (after adjustment)
|
|
if DOS5
|
|
dw 0 ; # sectors per FAT
|
|
else
|
|
db 0 ; # sectors per FAT (nb. may be inaccurate on large drives)
|
|
endif
|
|
diradd dw 0 ; sector offset of 1st root DIR sector
|
|
|
|
LOCAL_DDSC_LEN equ offset $ - offset local_ddsc
|
|
|
|
; some extra parameters calculated from local_ddsc for convenience
|
|
|
|
nfats dw 0 ; # FAT's (WORD is handier)
|
|
nfatrecs dw 0 ; # sectors per FAT (accurate version)
|
|
clsize dw 0 ; cluster size in bytes
|
|
secperclu dw 0 ; # sectors per cluster
|
|
dirperclu dw 0 ; # dir enrties in subdir
|
|
|
|
dosfat dw 0 ; FAT length indicator (FAT12 or FAT16)
|
|
|
|
; The following specify the next block read/write operation on the active drive
|
|
|
|
adrive db 0ffh ; currently active disk
|
|
pblock dw 0, 0 ; absolute block address
|
|
mult_sec dw 1 ; multi sector count passed to xios
|
|
cur_dma dw 0
|
|
cur_dma_seg dw 0
|
|
|
|
fdrwreq dw 0 ; requested count (roundup)
|
|
|
|
public fdrwflg
|
|
fdrwflg db 0 ; bdosrw flags
|
|
fdrwcnt dw 0 ; requested byte count for read/write
|
|
fdrwptr rd 0 ; disk transfer address for read/write
|
|
fdrwoff dw 0 ; offset for R/W DTA
|
|
fdrwseg dw 0 ; segment for R/W DTA
|
|
|
|
fdrwsec rd 1 ; physical block for fdosrw
|
|
fdrwsecoff dw 0 ; offset within sector
|
|
fdrwdircnt dw 0 ; # sectors in direct xfer
|
|
|
|
byteoff dw 0 ; fdosrw local variable
|
|
dw 0 ; byte offset with file
|
|
|
|
blk dw 0 ; current cluster of filepos
|
|
blkidx dw 0 ; current cluster index within file
|
|
blkoffset dw 0 ; offset within cluster
|
|
|
|
; static request header for DOS device driver I/O
|
|
|
|
Public req_hdr
|
|
|
|
req_hdr rb 0
|
|
req_len db 22
|
|
req_unit rb 1
|
|
req_cmd rb 1
|
|
req_status rw 1
|
|
req_rwmode db 0 ; action hint for device drivers
|
|
rb 7
|
|
req_media rb 1
|
|
rb 16
|
|
|
|
req1_return equ byte ptr req_media+1
|
|
req1_volid equ word ptr req_media+2
|
|
|
|
req2_buffer equ word ptr req_media+1
|
|
req2_bpb equ word ptr req_media+5
|
|
|
|
req3_buffer equ word ptr req_media+1
|
|
req3_count equ word ptr req_media+5
|
|
req3_sector equ word ptr req_media+7
|
|
req3_volid equ word ptr req_media+9
|
|
|
|
req4_buffer equ word ptr req_media+1
|
|
req4_count equ word ptr req_media+5
|
|
req4_sector equ word ptr req_media+7
|
|
req4_volid equ dword ptr req_media+9
|
|
req4_bigsector equ dword ptr req_media+13
|
|
|
|
eject
|
|
BDOS_CODE cseg
|
|
|
|
extrn alloc_chain:near
|
|
extrn bpb2ddsc:near ; converts BPB to DDSC
|
|
extrn buffers_check:near ; look for buffers
|
|
extrn delfat:near
|
|
extrn discard_all:near ; discard all buffers
|
|
extrn discard_dir:near ; discard directory buffers
|
|
extrn discard_dirty:near ; discard all dirty buffers
|
|
extrn discard_files:near ; discard open files
|
|
extrn fdos_error:near
|
|
extrn fdos_restart:near
|
|
extrn file_update:near
|
|
extrn fixfat:near
|
|
extrn getnblk:near ; get block value from FAT
|
|
extrn get_ldt:near
|
|
extrn get_ldt_raw:near
|
|
extrn hdsblk:near ; get current HDS block
|
|
extrn hshdscrd:near
|
|
extrn locate_buffer:near
|
|
extrn rebuild_ldt_root:near
|
|
extrn timestamp_dhndl:near
|
|
extrn update_dat:near
|
|
extrn update_fat:near
|
|
extrn share_delay:near
|
|
|
|
public block_device_driver
|
|
public clus2sec
|
|
public device_driver
|
|
public read_block
|
|
public select_adrive
|
|
public select_logical_drv
|
|
public select_physical_drv
|
|
public write_block
|
|
|
|
eject
|
|
|
|
eject
|
|
|
|
Public get_ddsc
|
|
|
|
get_ddsc:
|
|
;--------
|
|
; On Entry:
|
|
; AL = physical drive
|
|
; On Exit:
|
|
; CY set if bad drive, else
|
|
; ES:BX -> DDSC_
|
|
; (All other registers preserved)
|
|
;
|
|
cmp al,ss:phys_drv
|
|
jae get_ddsc30
|
|
les bx,ss:ddsc_ptr
|
|
get_ddsc10:
|
|
cmp bx,0FFFFh ; end of the line
|
|
je get_ddsc30
|
|
cmp al,es:DDSC_UNIT[bx] ; does the unit match ?
|
|
je get_ddsc20 ; no, try the next
|
|
les bx,es:DDSC_LINK[bx]
|
|
jmps get_ddsc10
|
|
get_ddsc20:
|
|
; clc
|
|
ret
|
|
get_ddsc30:
|
|
stc
|
|
ret
|
|
|
|
eject
|
|
; Read/Write from/to disk file
|
|
|
|
; entry: CURRENT_DNHDL -> file handle
|
|
; BDRWFLG = 1 => read
|
|
; 0 => write
|
|
; ES:DI = buffer (32 bit: off/seg)
|
|
; CX = requested byte count (16 bit)
|
|
|
|
; exit: FDOS_RET = number of bytes read/written
|
|
; CURRENT_DHNDL incremented by FDOS_RET
|
|
|
|
public fdosrw ; read/write to/from disk file
|
|
|
|
fdosrw:
|
|
;------
|
|
call fdrw_prepare ; set up address, where we are in file
|
|
jc fdrw_error ; stop if we have a problem
|
|
call fdrw_size ; extend file if necessary
|
|
jc fdrw_error ; bail out if we can't
|
|
cmp fdrwcnt,0 ; are we truncating?
|
|
jne fdrw_loop ; read/write if non-zero count
|
|
test fdrwflg,1 ; writing zero bytes?
|
|
jnz fdrw_error ; (reading has no meaning)
|
|
call fdw_trunc ; writing truncates the file
|
|
jmps fdrw_nobigger
|
|
fdrw_error:
|
|
ret
|
|
|
|
fdrw_loop: ; loop here for long reads/writes
|
|
call fdrw_seek ; seek to position for xfer
|
|
jc fdrw_exit ; should get error's now...
|
|
jnz fdrw_buffered ; deblocking required if not aligned
|
|
mov cx,fdrwcnt ; CX = requested transfer size
|
|
cmp cx,psecsiz ; at least one sector transferred?
|
|
jb fdrw_buffered ; if less, need deblocked transfer
|
|
mov fdrwreq,cx ; requested count for direct r/w
|
|
call direct_rw ; transfer straight to/from TPA
|
|
jmps fdrw_more
|
|
fdrw_buffered: ; perform deblocked read/write
|
|
call deblock_rw ; transfer via BDOS buffer
|
|
fdrw_more:
|
|
add fdrwoff,ax ; adjust buffer address
|
|
add fdos_ret,ax ; adjust return code
|
|
|
|
add byteoff,ax ; adjust file offset
|
|
adc byteoff+2,0
|
|
|
|
sub fdrwcnt,ax ; adjust remaining count
|
|
ja fdrw_loop ; still more to do
|
|
fdrw_exit:
|
|
les bx,current_dhndl
|
|
mov ax,fdos_ret ; get total xfered and update position
|
|
add es:DHNDL_POSLO[bx],ax
|
|
adc es:DHNDL_POSHI[bx],0
|
|
test fdrwflg,1
|
|
jnz fdrw_return ; skip if reading
|
|
mov ax,byteoff ; has the file grown ?
|
|
mov dx,byteoff+WORD
|
|
sub ax,es:DHNDL_SIZELO[bx]
|
|
sbb dx,es:DHNDL_SIZEHI[bx]
|
|
jb fdrw_nobigger ; yes, update the file size
|
|
add es:DHNDL_SIZELO[bx],ax
|
|
adc es:DHNDL_SIZEHI[bx],dx
|
|
fdrw_nobigger:
|
|
call timestamp_dhndl ; record the current time
|
|
test es:DHNDL_MODE[bx],DHM_COMMIT
|
|
jz fdrw_return ; is auto-commit in place ?
|
|
call file_update ; yes, commit the file
|
|
fdrw_return:
|
|
ret
|
|
|
|
|
|
|
|
fdw_trunc:
|
|
;---------
|
|
; On Entry:
|
|
; BLKIDX = block number within file
|
|
; BLKOFFSET = block offset
|
|
; On Exit:
|
|
; DHNDL_SIZE adjusted, any excess clusters freed
|
|
;
|
|
|
|
les bx,current_dhndl
|
|
mov cx,blkoffset ; get offset within current block
|
|
mov ax,blkidx ; get logical block number
|
|
jcxz fdw_t10 ; skip if no data in last block
|
|
inc ax ; else add in another cluster
|
|
fdw_t10: ; AX = # of clusters required in file
|
|
test ax,ax
|
|
jnz fdw_t20
|
|
xchg ax,es:DHNDL_BLK1[bx] ; forget about chain
|
|
jmps fdw_t50
|
|
|
|
fdw_t20:
|
|
xchg ax,cx ; CX = # of blocks to keep
|
|
mov ax,es:DHNDL_BLK1[bx] ; get first block in file
|
|
fdw_t30: ; scan all block we want to keep
|
|
push cx
|
|
push ax
|
|
call getnblk ; get next block
|
|
pop bx
|
|
pop cx
|
|
cmp ax,lastcl ; stop on premature end of chain
|
|
ja fdw_t60
|
|
loop fdw_t30
|
|
push ax ; yep, remember what
|
|
mov ax,dosfat
|
|
xchg ax,bx ; truncate chain at cluster AX
|
|
call fixfat ; as thats all we need
|
|
pop ax
|
|
fdw_t50:
|
|
call delfat ; release the chain
|
|
fdw_t60:
|
|
les bx,current_dhndl
|
|
mov ax,byteoff ; now truncate the file
|
|
mov es:DHNDL_SIZELO[bx],ax
|
|
mov ax,byteoff+2
|
|
mov es:DHNDL_SIZEHI[bx],ax
|
|
xor ax,ax ; cause reads/writes to scan
|
|
mov es:DHNDL_BLK[bx],ax ; block chain from start
|
|
mov es:DHNDL_IDX[bx],ax
|
|
mov fdos_ret,ax ; no logical errors
|
|
ret
|
|
|
|
|
|
|
|
fdrw_prepare:
|
|
;------------
|
|
; Normalise the xfer address and count
|
|
; Calculate current position in the file
|
|
;
|
|
; On Entry:
|
|
; ES:DI -> buffer
|
|
; CX = bytes to xfer
|
|
; On Exit:
|
|
; FDRWSEG:FDRWOFF -> normalised buffer
|
|
; FDRWCNT = bytes to xfer
|
|
; FDOS_RET = bytes xfer'd (0)
|
|
; PREREAD = TRUE
|
|
; BYTEOFF = current offset in file
|
|
; BLKIDX = cluster containing current file position
|
|
; BLKOFFSET = offset within cluster
|
|
; CY set if current position theoretically impossible
|
|
;
|
|
|
|
xor ax,ax ; AX = 0
|
|
mov fdos_ret,ax ; initialize byte return count
|
|
mov fdrwcnt,cx ; save byte count for read/write
|
|
mov ax,000Fh
|
|
and ax,di ; get offset within paragraph
|
|
mov fdrwoff,ax ; save normalized offset for read/write
|
|
add ax,cx ; do we overflow 64k ?
|
|
jnc fdrw_p10 ; yes, then forget about what would
|
|
sub fdrwcnt,ax ; overflow this segment
|
|
fdrw_p10:
|
|
mov cl,4
|
|
shr di,cl ; DI = paragraph offset
|
|
mov ax,es
|
|
add ax,di ; AX = effective segment
|
|
jnc fdrw_p20 ; if above 1 MByte base it at FFFF
|
|
inc ax ; AX = para's above FFFF
|
|
shl ax,cl ; make it bytes
|
|
add fdrwoff,ax ; add to offset
|
|
mov ax,0ffffh ; use our magic segment
|
|
fdrw_p20:
|
|
mov fdrwseg,ax ; save normalized segment for read/write
|
|
les bx,current_dhndl
|
|
mov ax,es:DHNDL_POSLO[bx]
|
|
mov byteoff,ax ; copy position to local variables
|
|
mov ax,es:DHNDL_POSHI[bx]
|
|
mov byteoff+WORD,ax
|
|
mov cx,clsize
|
|
mov ax,lastcl
|
|
mul cx ; DX:AX = maximum size of disk
|
|
sub ax,byteoff
|
|
sbb dx,byteoff+WORD ; beyond this we can't go
|
|
jc fdrw_p30
|
|
mov ax,byteoff ; DX:AX = current file size
|
|
mov dx,byteoff+WORD
|
|
div clsize
|
|
mov blkidx,ax ; save it for later
|
|
mov blkoffset,dx ; DX = offset within cluster
|
|
clc ; theoretically possible
|
|
fdrw_p30:
|
|
ret
|
|
|
|
|
|
|
|
|
|
fdrw_size:
|
|
;---------
|
|
; On reads check xfer starts within file, and clip size to reflect EOF.
|
|
; On writes try to extend to cluster chain so it is big enough to contain
|
|
; the data we wish to write.
|
|
;
|
|
; On Entry:
|
|
; BYTEOFF = current position in file
|
|
; FDRWCNT = extra bytes requested
|
|
; On Exit:
|
|
; FDRWCNT adjusted if read past EOF
|
|
; CY set if problem extending file
|
|
;
|
|
les bx,current_dhndl
|
|
mov ax,es:DHNDL_SIZELO[bx] ; are we past the end of file
|
|
mov dx,es:DHNDL_SIZEHI[bx] ; if so we may wish to extend on write
|
|
sub ax,byteoff ; AX,DX = current offset
|
|
sbb dx,byteoff+WORD ; are we already beyond EOF ?
|
|
jb fdrw_s40
|
|
sub ax,fdrwcnt ; will we be going beyond EOF ?
|
|
sbb dx,0
|
|
jnb fdrw_s10 ; no, whole xfer is OK
|
|
test fdrwflg,1 ; check if we're reading
|
|
jz fdrw_s50 ; if we are just adjust the
|
|
add fdrwcnt,ax ; amount we can xfer
|
|
fdrw_s10:
|
|
; We call share concerning the XFER to check if any of the proposed
|
|
; file region is locked.
|
|
|
|
; les bx,current_dhndl ; check for locked regions
|
|
mov cx,net_retry
|
|
fdrw_s15:
|
|
push cx
|
|
mov cx,fdrwcnt ; in the file
|
|
callf share_stub+S_FDOSRW
|
|
pop cx
|
|
jnc fdrw_s20 ; CY set on error
|
|
dec cx
|
|
jz fdrw_s30
|
|
call share_delay
|
|
jmps fdrw_s15
|
|
fdrw_s20:
|
|
ret
|
|
|
|
fdrw_s30:
|
|
jmp fdos_error ; CY clear, AX = error code
|
|
|
|
|
|
fdrw_s40:
|
|
; We are going beyond EOF - if it is a read we fail it, if a write
|
|
; try to extend the file
|
|
test fdrwflg,1 ; check if we're reading
|
|
stc ; assume failure
|
|
jnz fdrw_s20 ; reads fail now, writes extend file
|
|
fdrw_s50:
|
|
call fdrw_s10 ; make sure SHARE doesn't object
|
|
; jmp fdwrite_extend ; if not try to extend the file
|
|
|
|
|
|
fdwrite_extend:
|
|
;--------------
|
|
; Try to extend to file to the required size before we write to it
|
|
; On Entry:
|
|
; ES:BX -> DHNDL_
|
|
; BYTEOFF = current position in file
|
|
; FDRWCNT = extra requested
|
|
; On Exit:
|
|
; CY clear if cluster chain now big enough for desired file size
|
|
;
|
|
|
|
mov ax,byteoff ; AX,DX = current offset
|
|
mov dx,byteoff+2
|
|
add ax,fdrwcnt ; AX,DX = offset after r/w if success
|
|
adc dx,0 ; add offset from lower 16 bits
|
|
div clsize ; AX whole blocks required
|
|
test dx,dx ; any remainder ?
|
|
jz fdw_e05 ; yes, we have a partial block
|
|
inc ax ; round up blocks required
|
|
fdw_e05:
|
|
xchg ax,cx ; CX blocks are required
|
|
mov ax,es:DHNDL_BLK1[bx] ; assume we need to follow from start
|
|
test ax,ax
|
|
jz fdw_e30 ; if no starting block do the lot
|
|
dec cx ; else count # extra blocks required
|
|
mov dx,es:DHNDL_BLK[bx] ; do we have a current block ?
|
|
test dx,dx ; if not we have to start
|
|
jz fdw_e10 ; with the first block
|
|
mov ax,dx ; new starting block as this must
|
|
sub cx,es:DHNDL_IDX[bx] ; be less than extended size
|
|
fdw_e10:
|
|
jcxz fdw_e20 ; bail out of we have enough
|
|
fdw_e15:
|
|
push ax ; save current block
|
|
push cx ; save # required
|
|
call getnblk ; AX = next block in chain
|
|
pop cx ; restore # required
|
|
pop bx ; recover previous block
|
|
cmp ax,lastcl ; end of chain yet ?
|
|
ja fdw_e40
|
|
loop fdw_e15 ; try another one
|
|
fdw_e20:
|
|
clc ; chain is already long enough
|
|
ret
|
|
|
|
fdw_e30:
|
|
; We have no initial block, so allocate them all
|
|
; xor ax,ax ; no preconceptions over where we
|
|
call alloc_chain ; allocate chain of CX clusters
|
|
jc fdw_e35
|
|
les bx,current_dhndl
|
|
mov es:DHNDL_BLK1[bx],ax ; remember initial block
|
|
clc
|
|
fdw_e35:
|
|
ret
|
|
|
|
fdw_e40:
|
|
; We have a partial chain, ending at cluster BX
|
|
push bx ; save current end of chain
|
|
xchg ax,bx ; start allocating from cluster AX a
|
|
call alloc_chain ; a chain of CX clusters
|
|
pop bx
|
|
jc fdw_e45
|
|
xchg ax,bx ; AX = previous cluster, link cluster
|
|
call fixfat ; BX to end of the chain
|
|
clc
|
|
fdw_e45:
|
|
ret
|
|
|
|
|
|
|
|
fdrw_seek:
|
|
;---------
|
|
; On Entry:
|
|
; BYTEOFF = offset within file
|
|
; On Exit:
|
|
; BLK = cluster containing current filepos
|
|
; BLKOFFSET = offset within cluster
|
|
; BLKIDX = cluster index within file
|
|
; PBLOCK = sector containing current filepos
|
|
; POFFSET = offset within sector (reflected in ZF)
|
|
;
|
|
mov ax,byteoff ; where are we now ?
|
|
mov dx,byteoff+WORD
|
|
div clsize
|
|
mov blkidx,ax ; save cluster
|
|
mov blkoffset,dx ; and offset within it
|
|
les bx,current_dhndl
|
|
cmp ax,es:DHNDL_IDX[bx] ; do we know this block ?
|
|
jb fdrw_seek10 ; we can't go backwards, use 1st block
|
|
mov cx,es:DHNDL_BLK[bx] ; get last index block
|
|
jcxz fdrw_seek10 ; use 1st block if it isn't valid
|
|
sub ax,es:DHNDL_IDX[bx] ; skip this many
|
|
jmps fdrw_seek20
|
|
fdrw_seek10:
|
|
mov cx,es:DHNDL_BLK1[bx] ; start with 1st block
|
|
fdrw_seek20:
|
|
xchg ax,cx ; AX = starting cluster
|
|
jcxz fdrw_seek40 ; CX = clusters to skip
|
|
fdrw_seek30:
|
|
push cx
|
|
call getnblk ; get next block
|
|
pop cx
|
|
cmp ax,lastcl ; stop on premature end of chain
|
|
ja fdrw_seek_error ; (file size must be wrong..)
|
|
loop fdrw_seek30
|
|
fdrw_seek40:
|
|
les bx,current_dhndl
|
|
mov dx,blkidx
|
|
mov es:DHNDL_IDX[bx],dx ; remember this position for next time
|
|
mov es:DHNDL_BLK[bx],ax
|
|
mov blk,ax ; save the block for coniguous checks
|
|
mov bx,blkoffset
|
|
call clus2sec ; convert to sector/offset
|
|
mov word ptr fdrwsec,ax ; remember this block
|
|
mov word ptr fdrwsec+WORD,dx
|
|
mov fdrwsecoff,bx ; and offset within it
|
|
test bx,bx ; set ZF
|
|
; clc ; no problems
|
|
ret
|
|
|
|
fdrw_seek_error:
|
|
stc ; we hit unexpected end of chain
|
|
ret ; (shouldn't happen)
|
|
|
|
; Read/write partial sector via deblocking code
|
|
; On Entry:
|
|
; FDRWSEC = sector address on disk
|
|
; FDRWSECOFF = offset within sector
|
|
; FDRWCNT = byte count for read/write
|
|
; On Exit:
|
|
; AX = # of bytes transferred
|
|
|
|
deblock_rw:
|
|
;----------
|
|
mov cx,0FF00h+BF_ISDAT ; CH = preread, buffer is data
|
|
mov dx,word ptr fdrwsec ; set sector to xfer from
|
|
mov ah,byte ptr fdrwsec+WORD
|
|
call locate_buffer ; ES:SI -> buffer
|
|
mov bx,fdrwsecoff ; BX = offset within sector
|
|
mov ax,psecsiz
|
|
mov dx,ax ; DX = physical sector size
|
|
sub ax,bx ; AX = bytes left in sector
|
|
cmp ax,fdrwcnt ; more than we want to transfer?
|
|
jb deblkrw10 ; yes, only do up to end of sector
|
|
mov ax,fdrwcnt ; else do up to end of request
|
|
deblkrw10:
|
|
mov cx,ax ; AX, CX = byte count
|
|
; (AX for return, CX for MOVSW)
|
|
push ds
|
|
test fdrwflg,1 ; check if reading or writing
|
|
jz dblkrw30 ; skip if writing
|
|
|
|
push es
|
|
les di,fdrwptr ; destination is user memory
|
|
pop ds ; source segment is data buffer
|
|
lea si,BCB_DATA[si+bx] ; DS:SI -> data
|
|
jmps dblkrw40 ; copy the data
|
|
|
|
dblkrw30: ; we're writing
|
|
or es:BCB_FLAGS[si],BF_DIRTY; mark buffer as dirty
|
|
lea di,BCB_DATA[si+bx] ; ES:DI -> data
|
|
lds si,fdrwptr ; source is user memory
|
|
|
|
dblkrw40:
|
|
shr cx,1 ; make it a word count
|
|
rep movsw ; move the words
|
|
jnc dblkrw50 ; skip if even # of bytes
|
|
movsb ; else move last byte
|
|
dblkrw50:
|
|
pop ds ; restore registers
|
|
ret
|
|
|
|
|
|
; entry: BYTEOFF = 32-bit offset into file
|
|
; BLKOFFSET = byte offset within cluster
|
|
; PRVBLK = block in which transfer starts
|
|
; FDRWREQ = requested transfer length
|
|
|
|
;---------
|
|
direct_rw:
|
|
;---------
|
|
sub dx,dx ; assume no extra blocks required
|
|
mov ax,fdrwreq ; total byte count
|
|
mov cx,clsize ; get number of bytes
|
|
sub cx,blkoffset ; CX = bytes remaining in this block
|
|
sub ax,cx ; if wholly containined within block
|
|
jbe direct_rw10 ; then leave it alone
|
|
div clsize ; else get # of extra clusters
|
|
xchg ax,dx ; DX = clusters, AX = remainder
|
|
or ax,ax ; round up if any remainder
|
|
jz direct_rw10 ; skip if even number
|
|
inc dx ; else one more cluster
|
|
direct_rw10: ; DX = # of contiguous clusters req'd
|
|
call check_cont ; check how many contiguous blocks
|
|
mov ax,clsize ; space = cnt * dpbptr->clsize;
|
|
mul cx ; AX:DX = # of bytes transferrable
|
|
sub ax,blkoffset ; BX = skipped bytes in 1st cluster
|
|
sbb dx,0
|
|
; AX:DX = max # of bytes transferrable
|
|
; from current position
|
|
test dx,dx
|
|
jnz direct_rw20 ; if > 64 K, use up request
|
|
cmp ax,fdrwreq ; if less than we requested
|
|
jb direct_rw30 ; then lets do it
|
|
direct_rw20:
|
|
xor dx,dx
|
|
mov ax,fdrwreq ; else use requested count
|
|
direct_rw30:
|
|
div psecsiz ; AX = # complete sectors
|
|
mov fdrwdircnt,ax ; save direct sector count
|
|
mov mult_sec,ax ; set multi sector count
|
|
mul psecsiz ; AX = bytes to xfer
|
|
push ax ; save for later
|
|
|
|
mov ax,fdrwoff ; FDRWPTR = disk transfer address
|
|
mov cur_dma,ax
|
|
mov ax,fdrwseg
|
|
mov cur_dma_seg,ax
|
|
mov ax,word ptr fdrwsec ; set sector to xfer from
|
|
mov word ptr pblock,ax
|
|
mov ax,word ptr fdrwsec+WORD
|
|
mov word ptr pblock+WORD,ax
|
|
mov rwmode,0000$0110b ;data read/write
|
|
mov cl,fdrwflg
|
|
and cl,1 ; CL = read/write flag
|
|
jz direct_rw40
|
|
xor cx,cx ; indicate no retries
|
|
call read_block ; read in the data
|
|
jmps direct_rw50
|
|
direct_rw40:
|
|
call write_block ; write out the data
|
|
direct_rw50:
|
|
call SynchroniseBuffers ; synchronize BCBs with direct transfer
|
|
pop ax ; recover bytes xfered
|
|
push ds ! pop es ; restore ES = SYSDAT
|
|
ret
|
|
|
|
|
|
check_cont: ; check for adjacent blocks or space
|
|
;----------
|
|
; entry: DX = # of extra contiguous blocks req'd
|
|
; exit: CX = # of contiguous blocks available
|
|
|
|
; We first check all adjacent allocated clusters.
|
|
; If we'd like more and we find the end of file
|
|
; and we are writing and the adjacent blocks aren't
|
|
; allocated, then we count them as well and link
|
|
; them into the file.
|
|
|
|
mov ax,blk ; current block number
|
|
xor cx,cx ; contiguous blocks found = 0
|
|
test dx,dx ; any extra required ?
|
|
jz check_cont20
|
|
check_cont10: ; get link of current block
|
|
push ax ; save current block
|
|
push cx ; save extra blocks so far
|
|
push dx ; save extra blocks we'd like
|
|
call getnblk ; get the link
|
|
pop dx
|
|
pop cx
|
|
pop bx
|
|
inc bx ; BX = current block + 1
|
|
cmp ax,bx ; check if next block is contiguous
|
|
jne check_cont20 ; and try for another
|
|
inc cx ; extra contiguous cluster
|
|
dec dx ; one less block to check
|
|
jnz check_cont10 ; try again if we still want more
|
|
check_cont20: ; we can do CX extra clusters
|
|
inc cx ; include 1st cluster too..
|
|
ret
|
|
|
|
|
|
;------------------
|
|
SynchroniseBuffers: ; synchronize BCBs after multi sector transfer
|
|
;------------------
|
|
; On Entry:
|
|
; FDRWSEG:FDRWOFF = transfer address for IO_READ/IO_WRITE
|
|
; FDRWDIRCNT = physical sector count for direct transfer
|
|
; FDRWSEC = sector address for transfer
|
|
; FDWRFLG = even for write, odd for read
|
|
; On Exit:
|
|
; direct transfer buffer or BCB updated if BCB overlap
|
|
;
|
|
; If any data buffer is found, that falls into the region affected
|
|
; by the direct sector transfer, the following action is performed:
|
|
; If the operation was a read and the sector buffer is clean,
|
|
; no action is required. If it was dirty, the buffer contents is
|
|
; copied to the corresponding location in the DTA buffer.
|
|
; If the operation was a write, the sector buffer is discarded.
|
|
;
|
|
;
|
|
mov dx,word ptr fdrwsec
|
|
mov ah,byte ptr fdrwsec+WORD
|
|
mov al,adrive ; get our drive number
|
|
lds bx,bcb_root ; DS:BX -> 1st buffer
|
|
SynchroniseBuffers10:
|
|
test ds:BCB_FLAGS[bx],BF_ISDAT; is this a data buffer?
|
|
jz SynchroniseBuffers30 ; skip if directory or FAT
|
|
cmp al,ds:BCB_DRV[bx] ; does the drive match?
|
|
jne SynchroniseBuffers30 ; skip if different
|
|
mov si,ds:BCB_REC[bx] ; compute bcb->rec - prec
|
|
sub si,dx ; result in SI,CL (lsb..msb)
|
|
mov cl,ds:BCB_REC2[bx]
|
|
sbb cl,ah ; get bits 16-23 of result
|
|
jne SynchroniseBuffers30 ; skip if bcb->rec < prec
|
|
cmp si,ss:fdrwdircnt ; else check against transfer length
|
|
jae SynchroniseBuffers30 ; skip if beyond transfer length
|
|
|
|
test ss:fdrwflg,1 ; test direction: read or write
|
|
jz SynchroniseBuffers20 ; skip if disk write
|
|
|
|
test ds:BCB_FLAGS[bx],BF_DIRTY; if buffer dirty, did read old data
|
|
jz SynchroniseBuffers30 ; else data read was valid
|
|
|
|
push ax ! push dx ; save record address
|
|
|
|
mov ax,ss:psecsiz ; # of bytes in sector buffer
|
|
mov cx,ax
|
|
shr cx,1 ; CX = words per sector
|
|
mul si ; AX = byte offset from start buffer
|
|
add ax,ss:fdrwoff ; AX = offset
|
|
xchg ax,di ; DI = offset
|
|
mov es,ss:fdrwseg ; ES:DI -> data to be replaced
|
|
lea si,BCB_DATA[bx]
|
|
rep movsw ; move CX words (one physical sector)
|
|
pop dx ! pop ax ; restore record address
|
|
jmps SynchroniseBuffers30
|
|
|
|
SynchroniseBuffers20: ; multi sector write
|
|
mov ds:BCB_DRV[bx],0FFh ; discard this sector
|
|
SynchroniseBuffers30:
|
|
if DOS5
|
|
mov bx,ds:BCB_NEXT[bx]
|
|
cmp bx,ss:word ptr bcb_root
|
|
else
|
|
lds bx,ds:BCB_NEXT[bx] ; get next buffer address
|
|
cmp bx,0ffffh
|
|
endif
|
|
jne SynchroniseBuffers10 ; if so stop
|
|
push ss ! pop ds ; restore DS
|
|
ret
|
|
|
|
eject
|
|
Public blockif, ddioif
|
|
|
|
;======= ================================
|
|
blockif: ; disk read/write bios interface
|
|
;======= ================================
|
|
; entry: AL = BIOS Request function number
|
|
; ADRIVE = block device to xfer to/from
|
|
; RWMODE = read/write mode
|
|
; CUR_DMA_SEG:CUR_DMA -> xfer address
|
|
; PBLOCK = starting block of xfer
|
|
; MULT_CNT = # blocks to xfer
|
|
; exit: AX = BX = output
|
|
|
|
mov req_cmd,al
|
|
mov al,rwmode ; copy rwmode to where the device
|
|
mov req_rwmode,al ; driver can get the hint
|
|
mov ax,cur_dma ; get DMA offset
|
|
push ax ; (save it)
|
|
and ax,000Fh ; get offset within paragraph
|
|
mov req4_buffer,ax ; set transfer offset
|
|
pop ax ; (restore offset)
|
|
mov cl,4
|
|
shr ax,cl ; convert to paragraphs
|
|
add ax,cur_dma_seg ; add in the segment
|
|
mov req4_buffer+2,ax ; set transfer segment
|
|
mov ax,mult_sec ; get requested sector count
|
|
mov req4_count,ax ; set requested sector count
|
|
;------
|
|
ddioif:
|
|
;------
|
|
push es
|
|
mov al,adrive ; get selected drive
|
|
call get_ddsc ; ES:BX -> DDSC
|
|
mov ax,word ptr pblock
|
|
mov dx,word ptr pblock+WORD ; DX:AX = starting block
|
|
push es
|
|
les si,es:DDSC_DEVHEAD[bx] ; ES:SI -> device driver
|
|
if DOS5
|
|
; DOS 4 support
|
|
mov word ptr req4_bigsector,ax
|
|
mov word ptr req4_bigsector+2,dx
|
|
mov req_len,RH4_LEN ; set length of request header
|
|
test es:DH_ATTRIB[si],DA_BIGDRV ; large sector number support?
|
|
jz blockif10 ; no, normal request header
|
|
mov ax,-1 ; indicate we use 32-bit sector number
|
|
blockif10:
|
|
mov req4_sector,ax ; set requested sector address
|
|
else
|
|
mov word ptr req4_bigsector,ax
|
|
mov word ptr req4_bigsector+2,dx
|
|
|
|
mov req4_sector,ax ; set requested sector address
|
|
mov req4_sector+2,dx ; (support large DOS drives)
|
|
mov req_len,RH4_LEN ; assume 22 bytes in request header
|
|
test es:DH_ATTRIB[si],DA_BIGDRV ; large sector number support?
|
|
jz blockif10 ; no, normal request header
|
|
mov req_len,RH4_LEN+2 ; else indicate long request
|
|
blockif10:
|
|
endif
|
|
pop es
|
|
|
|
call block_device_driver ; make call to device driver
|
|
js blockif20
|
|
xor ax,ax ; no error
|
|
blockif20:
|
|
mov mult_sec,1 ; reset sector count
|
|
mov bx,ax ; AX, BX = return code
|
|
pop es
|
|
ret
|
|
|
|
|
|
|
|
block_device_driver:
|
|
;------------------
|
|
; entry: ES:BX -> DDSC, req_hdr partly filled in
|
|
; exit: AX = status after function
|
|
; SF = 1 if error occurred
|
|
; note: BX preserved
|
|
|
|
mov al,es:DDSC_MEDIA[bx]
|
|
mov req_media,al ; set current media byte
|
|
mov al,es:DDSC_RUNIT[bx] ; get relative unit #
|
|
mov req_unit,al ; set the unit
|
|
push ds
|
|
push es
|
|
push bx
|
|
push ds
|
|
lds si,es:DDSC_DEVHEAD[bx]
|
|
pop es
|
|
mov bx,offset req_hdr ; ES:BX -> request packet
|
|
call device_driver ; do operation
|
|
pop bx
|
|
pop es
|
|
pop ds
|
|
ret
|
|
|
|
; On Entry:
|
|
; DS:SI Device Header
|
|
; ES:BX Current Request Header
|
|
;
|
|
; On Exit:
|
|
; AX Request Header Status
|
|
;
|
|
device_driver:
|
|
;------------
|
|
xor ax,ax
|
|
mov es:RH_STATUS[bx],ax ; Initialise return status
|
|
push es
|
|
push bx
|
|
push bp
|
|
callf ss:lock_bios ; lock access to BIOS
|
|
push cs
|
|
call device_driver10 ; fake a callf
|
|
callf ss:unlock_bios ; unlock access to BIOS
|
|
pop bp
|
|
pop bx
|
|
pop es
|
|
sti
|
|
cld ; Restore Flags
|
|
mov ax,es:RH_STATUS[bx] ; Return the Status to the caller
|
|
test ax,ax ; set SF=1 if error
|
|
ret
|
|
|
|
device_driver10:
|
|
push ds
|
|
push ds:DH_INTERRUPT[si] ; interrupt routine address on stack
|
|
push ds
|
|
push ds:DH_STRATEGY[si] ; strategy routine address on stack
|
|
retf ; retf to strategy, interrupt, us
|
|
|
|
eject
|
|
; Select drive and check for door open ints
|
|
; Build fdos_hds to refer to the drive
|
|
|
|
; Exit: DL = drive to be selected (0-15)
|
|
|
|
select_logical_drv:
|
|
;------------------
|
|
; On Entry:
|
|
; AL = logical drive to select (with change media checks)
|
|
; On Exit:
|
|
; ES:BX -> LDT_
|
|
;
|
|
cmp al,last_drv ; is it a legal drive ?
|
|
jae select_drv_bad ; no, reject it now
|
|
mov logical_drv,al ; save logical drive
|
|
call get_ldt ; ES:BX -> LDT_ for drive
|
|
jc select_physical_drv ; no LDT_ during init, must be physical
|
|
mov word ptr current_ldt,bx
|
|
mov word ptr current_ldt+WORD,es
|
|
mov al,es:byte ptr LDT_FLAGS+1[bx] ; is the drive valid ?
|
|
test al,(LFLG_NETWRKD+LFLG_JOINED)/100h
|
|
jnz select_drv_bad ; reject networked/joined drives
|
|
test al,LFLG_PHYSICAL/100h
|
|
jz select_drv_bad ; reject non-physical drives
|
|
mov al,es:LDT_NAME[bx] ; get the drive from the ascii name
|
|
and al,1fh ; as the drive may require rebuilding
|
|
dec ax ; make it zero based
|
|
push es ! push bx
|
|
call select_physical_drv ; select the physical root
|
|
pop bx ! pop es
|
|
cmp es:LDT_ROOTLEN[bx],2 ; if logical and physical roots
|
|
jbe select_logical_drv30 ; are the same we are OK now
|
|
if JOIN
|
|
mov al,es:LDT_DRV[bx] ; should we be on a different
|
|
cmp al,fdos_hds_drv ; physical drive ?
|
|
jne select_logical_drv10 ; if so then we'd better rebuild
|
|
endif
|
|
cmp es:LDT_BLK[bx],0FFFFh ; did we have a media change ?
|
|
jne select_logical_drv20 ; then we'd better rebuild
|
|
select_logical_drv10:
|
|
call rebuild_ldt_root ; the LDT_ root block
|
|
select_logical_drv20:
|
|
mov ax,es:LDT_ROOT[bx] ; get virtual root from LDT
|
|
mov fdos_hds_root,ax ; move there
|
|
mov fdos_hds_blk,ax
|
|
if JOIN
|
|
mov al,es:LDT_DRV[bx] ; same with drive
|
|
mov fdos_hds_drv,al
|
|
endif
|
|
select_logical_drv30:
|
|
ret
|
|
|
|
select_physical_drv:
|
|
;-------------------
|
|
; On Entry:
|
|
; AL = physical drive to select (with change media checks)
|
|
; On Exit:
|
|
; None
|
|
;
|
|
xor dx,dx
|
|
mov fdos_hds_blk,dx ; put it in the root by default
|
|
mov fdos_hds_root,dx
|
|
mov fdos_hds_drv,al ; set physical drive in working HDS
|
|
cmp al,phys_drv ; should we have a DDSC_ for this drive
|
|
jae select_drv_bad ; no, we can't select it then
|
|
mov physical_drv,al ; save physical drive number
|
|
call select_adrive ; no, better select it
|
|
jc select_drv_critical_error
|
|
ret
|
|
|
|
select_drv_bad:
|
|
;--------------
|
|
; An attempt has been made to select a bad drive,
|
|
; return a logical error "invalid drive"
|
|
mov ax,ED_DRIVE ; ED_DRIVE "invalid drive"
|
|
jmp fdos_error
|
|
|
|
select_drv_critical_error:
|
|
;-------------------------
|
|
; The drive is logically correct, so all error at this point must
|
|
; be physical ones - so we want a critical error
|
|
jmp generate_critical_error
|
|
|
|
eject
|
|
select_adrive:
|
|
;-------------
|
|
; This entry is called to physically select a drive (eg. when flushing buffers)
|
|
; It does not alter the current physical_drv setting, which must be re-selected
|
|
; afterwards by the caller.
|
|
;
|
|
; On Entry:
|
|
; AL = disk to select (range validated)
|
|
; On Exit:
|
|
; CY set if a problem selecting the drive
|
|
|
|
mov adrive,al
|
|
mov err_drv,al ; save error drive
|
|
call get_ddsc ; ES:BX -> DDSC_ for drive
|
|
mov al,1 ; AL = "Unknown Unit"
|
|
jc select_drv_err ; error if no DDSC_
|
|
mov ax,es:word ptr DDSC_DEVHEAD[bx]
|
|
mov word ptr error_dev+0,ax
|
|
mov ax,es:word ptr DDSC_DEVHEAD+2[bx]
|
|
mov word ptr error_dev+2,ax
|
|
push es ; remember driver address for error's
|
|
push bx ; preserve DDSC_
|
|
call check_media ; see if media has changed
|
|
pop bx ; restore DDSC_
|
|
pop es
|
|
jc select_drv_err
|
|
|
|
; select the disk drive and fill the drive specific variables
|
|
; entry: ES:BX -> DDSC_ of disk to select
|
|
; AX <> 0 if drive requires BPB rebuilt
|
|
; exit: CY flag set on error
|
|
|
|
test ax,ax ; device driver, new select?
|
|
jz select_ddsc ; use current DDSC if old select
|
|
call build_ddsc_from_bpb ; else get BPB and build new DDSC
|
|
jc select_drv_err ; carry flag reset
|
|
call select_ddsc ; use to DDSC for select
|
|
if DELWATCH
|
|
mov ah,DELW_NEWDISK ; we have a new disk so I guess
|
|
mov al,physical_drv ; I'd better tell delwatch
|
|
les bx,current_ddsc ; about the new disk so it
|
|
callf fdos_stub ; knows to update itself
|
|
endif
|
|
clc ;select disk function ok
|
|
ret
|
|
|
|
select_drv_err:
|
|
; On Entry:
|
|
; AL = extended error code
|
|
; CY set
|
|
;
|
|
mov ioexerr,al ; save error code
|
|
ret
|
|
|
|
|
|
select_ddsc:
|
|
;-----------
|
|
; On Entry:
|
|
; ES:BX -> DDSC_ of drive to be selected
|
|
mov word ptr current_ddsc,bx
|
|
mov word ptr current_ddsc+WORD,es
|
|
push ds ! push es
|
|
pop ds ! pop es ; swap ES and DS
|
|
lea si,DDSC_SECSIZE[bx] ; DS:SI -> DDSC_ original
|
|
mov di,offset local_ddsc ; ES:DI -> DDSC_ copy
|
|
mov cx,LOCAL_DDSC_LEN
|
|
rep movsb ; make a local copy of interesting bits
|
|
push es ! pop ds ; DS=ES=local data segment
|
|
mov ax,psecsiz ; now initialise some other vaiiables
|
|
mov cl,clshf
|
|
shl ax,cl ; AX = bytes per cluster
|
|
mov clsize,ax
|
|
xor ax,ax
|
|
mov al,clmsk
|
|
inc ax ; AX = sectors per cluster
|
|
mov secperclu,ax
|
|
mov al,byte_nfats ; AX = number of FATs
|
|
mov nfats,ax ; (it's handier as a word
|
|
mov ax,diradd ; number of FAT records can be
|
|
sub ax,fatadd ; bigger than 255
|
|
xor dx,dx
|
|
div nfats
|
|
mov nfatrecs,ax
|
|
mov cx,FCBLEN
|
|
mov ax,clsize ; convert from cluster size
|
|
xor dx,dx ; to number of dir entries
|
|
div cx ; per cluster - handy for
|
|
mov dirperclu,ax ; subdirectories
|
|
mov ax,FAT12
|
|
cmp lastcl,MAX12 ; is it a 12 bit FAT ?
|
|
jbe select_ddsc10
|
|
mov ax,FAT16 ; no, it's 16 bit
|
|
select_ddsc10:
|
|
mov dosfat,ax ; remember which for later
|
|
clc ; drive all selected
|
|
ret
|
|
|
|
|
|
|
|
|
|
eject
|
|
|
|
build_ddsc_from_bpb: ; call device driver to build BPB, convert to DDSC_
|
|
;-------------------
|
|
; On Entry:
|
|
; ES:BX -> DDSC_ to rebuild
|
|
; On Exit:
|
|
; ES:BX preserved
|
|
; CY set on error
|
|
; AL = error code
|
|
|
|
push es
|
|
push bx ; save DDSC_ address
|
|
xor di,di
|
|
mov ax,deblock_seg
|
|
mov es,ax ; ES:DI -> deblock seg
|
|
test ax,ax ; if we are deblocking spare buffer
|
|
jnz build_bpb10 ; might be in high memory
|
|
dec ax ; AX = FFFF
|
|
mov dx,ax ; compute impossible record #
|
|
mov cx,BF_ISDIR ; locate directory sector w/o preread
|
|
call locate_buffer ; this will find the cheapest buffer
|
|
mov es:BCB_DRV[si],0FFh ; don't really want this...
|
|
lea di,BCB_DATA[si] ; ES:DI -> disk buffer
|
|
build_bpb10:
|
|
mov req4_buffer,di ; xfer to ES:DI
|
|
mov req4_buffer+2,es
|
|
pop bx ; restore DDSC_ address
|
|
pop es
|
|
|
|
push ds
|
|
lds si,es:DDSC_DEVHEAD[bx] ; DS:SI -> device header
|
|
mov ax,ds:DH_ATTRIB[si] ; non-FAT ID driver ("non-IBM") bit
|
|
pop ds ; in device header attributes
|
|
test ax,DA_NONIBM
|
|
jnz bldbpb30 ; skip if media byte in FAT not used
|
|
|
|
mov req_rwmode,0 ; read of system area
|
|
mov req_len,RH4_LEN ; set length field
|
|
mov req_cmd,CMD_INPUT ; read first FAT sector off disk
|
|
if DOS5
|
|
test ax,DA_BIGDRV ; large sector numbers ?
|
|
endif
|
|
mov ax,1
|
|
mov req4_count,ax ; read 1st FAT sector
|
|
cwd ; DS:AX = sector 1
|
|
mov word ptr req4_bigsector,ax
|
|
mov word ptr req4_bigsector+2,dx
|
|
if DOS5
|
|
jz bldbpb20
|
|
dec ax ! dec ax ; AX = 0FFFFh
|
|
bldbpb20:
|
|
endif
|
|
mov req4_sector,ax ; set requested sector address
|
|
mov req4_sector+2,dx ; (support large DOS drives)
|
|
call block_device_driver ; try to read FAT sector, AX = status
|
|
js bldbpb_err ; skip if errors (AX negative)
|
|
bldbpb30:
|
|
mov req_len,RH2_LEN ; length of req
|
|
mov req_cmd,CMD_BUILD_BPB ; "build bpb"
|
|
call block_device_driver ; call the device driver
|
|
js bldbpb_err ; skip if errors (AX negative)
|
|
push ds
|
|
push es
|
|
push bx
|
|
mov di,bx ; ES:DI -> DDSC_ to initialise
|
|
lds si,dword ptr req2_bpb ; DS:SI -> BPB to convert
|
|
call bpb2ddsc ; rebuild the DDSC_
|
|
pop bx
|
|
pop es
|
|
pop ds
|
|
clc ; success - we have a new DDSC_
|
|
ret
|
|
|
|
|
|
bldbpb_err:
|
|
stc ; we had a problem
|
|
ret
|
|
|
|
|
|
eject
|
|
|
|
;-----------
|
|
check_media: ; check media if DPH media flag set
|
|
;-----------
|
|
; On Entry:
|
|
; ES:BX -> DDSC_ of physical drive to check
|
|
; On Exit:
|
|
; CY set on error, AX = error code
|
|
; else
|
|
; AX <> 0 if disk requires BPB rebuild
|
|
; If definite/possible change then LDT's marked as invalid
|
|
; If possible then buffers/hashing discarded provided they are clean
|
|
; If definite then all buffers/hashing for drive discarded even if dirty
|
|
;
|
|
mov req_len,RH1_LEN ; set length field
|
|
mov req_cmd,CMD_MEDIA_CHECK ; media check routine
|
|
call block_device_driver ; call the device driver
|
|
jns chkmed10
|
|
stc ; we have a problem, generate
|
|
ret ; an error
|
|
chkmed10:
|
|
mov al,req_media+1 ; else get returned value
|
|
xor ah,ah ; watch out for 1st access too..
|
|
xchg ah,es:DDSC_FIRST[bx] ; treat never accessed as changed
|
|
cmp al,1 ; 1 = no change
|
|
jne chkmed20
|
|
dec ax ; AL=0, build bpb only if DDSC_FIRST
|
|
; clc ; it all went OK
|
|
ret
|
|
|
|
chkmed20:
|
|
mov dl,adrive ; media may have/has changed
|
|
call mark_ldt_unsure ; so force LDT's to unsure
|
|
|
|
; AL = 00 if maybe changed, FF for definitely changed
|
|
test al,al
|
|
jz chkmed_maybe ; media may have changed
|
|
|
|
chkmed_changed: ; disk has changed for sure
|
|
call discard_files ; discard open files
|
|
jmps chkmed30 ; discard buffers, build bpb required
|
|
|
|
chkmed_maybe: ; disk has possibly changed
|
|
call discard_dir ; we can always discard dir as they
|
|
mov ah,BF_DIRTY ; won't be dirty
|
|
mov al,adrive
|
|
call buffers_check ; any dirty buffers on adrive?
|
|
jnz chkmed40 ; yes, can't discard FAT
|
|
chkmed30:
|
|
call discard_all ; discard buffers for drive
|
|
chkmed40:
|
|
or ax,0FFFFh ; better rebuild bpb
|
|
; clc
|
|
ret
|
|
|
|
|
|
Public mark_ldt_unsure
|
|
|
|
mark_ldt_unsure:
|
|
;---------------
|
|
; On Entry:
|
|
; DL = physical drive
|
|
; On Exit:
|
|
; All corresponding LDT's marked as unsure
|
|
; All reg preserved
|
|
;
|
|
push es
|
|
push ax
|
|
push bx
|
|
xor ax,ax ; start with drive A:
|
|
mlu10:
|
|
call get_ldt_raw ; ES:BX -> LDT_
|
|
jc mlu30 ; CY = no more LDT's
|
|
test es:LDT_FLAGS[bx],LFLG_NETWRKD+LFLG_JOINED
|
|
jnz mlu20 ; if networked leave it alone
|
|
cmp dl,es:LDT_DRV[bx] ; does the physical drive match ?
|
|
jne mlu20
|
|
mov es:LDT_BLK[bx],0FFFFh ; indicate we shouldn't trust BLK
|
|
mlu20:
|
|
inc ax ; onto next LDT
|
|
jmps mlu10
|
|
mlu30:
|
|
pop bx
|
|
pop ax
|
|
pop es
|
|
ret
|
|
|
|
;-----------
|
|
write_block:
|
|
;-----------
|
|
; entry: RWMODE = write type
|
|
; bit 0:
|
|
; 1 - write, not read
|
|
; bits 2-1 (affected disk area)
|
|
; 0 0 - system area
|
|
; 0 1 - FAT area
|
|
; 1 0 - root or sub directory
|
|
; 1 1 - data area
|
|
|
|
or rwmode,1 ; mark it as a write
|
|
xor cx,cx ; indicate no second attempt
|
|
mov al,CMD_OUTPUT ; assume normal write
|
|
cmp verify_flag,0 ; is verify on ?
|
|
je rdwr_block
|
|
mov al,CMD_OUTPUT_VERIFY ; assume use write w/ verify
|
|
jmps rdwr_block
|
|
|
|
;----------
|
|
read_block:
|
|
;----------
|
|
; entry: RWMODE = read type
|
|
; bit 0:
|
|
; 0 - read, not write
|
|
; bits 2-1 (affected disk area)
|
|
; 0 0 - system area
|
|
; 0 1 - FAT area
|
|
; 1 0 - root or sub directory
|
|
; 1 1 - data area
|
|
; CX <> 0 if FAT retry possible (critical error should then
|
|
; be avoided)
|
|
; exit: SF = 0 if success
|
|
; SF = 1 if failure (CX was non-zero on call)
|
|
|
|
and rwmode,not 1 ;mark it as a read
|
|
mov al,CMD_INPUT
|
|
rdwr_block:
|
|
push cx
|
|
call blockif ;current drive, track,....
|
|
pop cx
|
|
jns rdwrb5
|
|
jcxz rdwrb10 ; test if any disk error detected
|
|
rdwrb5:
|
|
ret ; skip if yes
|
|
rdwrb10:
|
|
mov ioexerr,al ; save extended error
|
|
test al,al ; is it write protect error ?
|
|
jnz rdwrb20 ; we have dirty buffers we can't write
|
|
call discard_dirty ; out, so throw 'em away
|
|
rdwrb20:
|
|
mov al,adrive ; if error on different drive
|
|
cmp al,physical_drv ; treat error as media change
|
|
je generate_critical_error ; if same drive, report error
|
|
call discard_all ; discard all buffers on drive
|
|
call discard_files ; and flush files
|
|
jmp fdos_restart ; try to restart the instruction
|
|
|
|
generate_critical_error:
|
|
;-----------------------
|
|
; On Entry:
|
|
; err_drv, rwmode, ioexerr set up
|
|
; On Exit:
|
|
; None - we don't come back
|
|
;
|
|
mov al,ioexerr ; AL = BIOS error return byte
|
|
cbw ; make it a word
|
|
cmp ax,15 ; only handle sensible errors
|
|
jb gen_crit_err10 ; anything else becomes
|
|
mov ax,12 ; general failure
|
|
gen_crit_err10:
|
|
neg ax ; convert to our negative errors
|
|
add ax,ED_PROTECT ; and start with write protect
|
|
jmp fdos_error ; now return with error
|
|
|
|
eject
|
|
|
|
clus2sec: ; convert from cluster/offset to sector/offset
|
|
;--------
|
|
; On Entry:
|
|
; AX = cluster
|
|
; BX = byte offset in cluster
|
|
; On Exit:
|
|
; DX:AX = sector
|
|
; BX = byte offset in sector
|
|
;
|
|
xchg ax,cx ; remember cluster in CX
|
|
xor dx,dx
|
|
xchg ax,bx ; DX:AX = byte offset
|
|
div psecsiz ; AX = sector offset, DX = byte offset
|
|
mov bx,dx ; BX = byte offset in sector
|
|
xchg ax,cx ; AX = cluster, CX = sector offset
|
|
dec ax
|
|
dec ax ; forget about 2 reserved clusters
|
|
mul secperclu ; DX:AX = offset of cluster
|
|
add ax,datadd
|
|
adc dx,0 ; DX:AX = offset of start of dir
|
|
add ax,cx ; DX:AX - add in sector offset
|
|
adc dx,0
|
|
ret
|
|
|
|
|
|
end
|