mirror of
https://github.com/SEPPDROID/DR-DOS-OpenDOS.git
synced 2025-10-22 07:54:28 +00:00
1301 lines
36 KiB
Plaintext
1301 lines
36 KiB
Plaintext
title 'BUFFERS - buffer handling routines'
|
|
; File : $BUFFERS.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$
|
|
;
|
|
; BUFFERS.A86 1.13 94/11/30 16:26:08
|
|
; added support for using multiple FAT copies on reads if one fails
|
|
; BUFFERS.A86 1.12 93/08/06 16:19:11
|
|
; make geblk public
|
|
; BUFFERS.A86 1.8 93/07/07 21:06:25
|
|
; Smirnoff'd
|
|
; BUFFERS.A86 1.6 93/03/16 22:30:29
|
|
; UNDELETE support changes
|
|
; BUFFERS.A86 1.5 93/03/05 18:00:26
|
|
; Fix bug clearing cluster of new sub directory
|
|
; ENDLOG
|
|
|
|
; Date Who Modification
|
|
; --------- --- ---------------------------------------
|
|
; 9 Sep 91 Initial version created for VLADIVAR
|
|
; 3 mar 93 correct zeroblk bug
|
|
|
|
NOLIST
|
|
eject ! include i:fdos.equ
|
|
eject ! include bdos.equ
|
|
eject ! include i:doshndl.def
|
|
eject
|
|
LIST
|
|
|
|
eject
|
|
PCMODE_DATA dseg
|
|
|
|
extrn current_ddsc:dword
|
|
if DELWATCH
|
|
extrn fdos_stub:dword
|
|
endif
|
|
|
|
BDOS_DATA dseg word
|
|
|
|
fatrec rw 1 ; current FAT record
|
|
|
|
fatbytl rb 1 ; low byte of split FAT entry
|
|
fatbyth rb 1 ; high byte of split FAT entry
|
|
split_fat rb 1 ; 0/FFh to indicate split entry
|
|
|
|
eject
|
|
extrn adrive:byte
|
|
EXTRN chdblk:WORD
|
|
EXTRN clsize:WORD
|
|
EXTRN cur_dma:WORD
|
|
EXTRN cur_dma_seg:WORD
|
|
extrn dosfat:WORD
|
|
EXTRN fatadd:WORD
|
|
extrn lastcl:word
|
|
EXTRN mult_sec:WORD
|
|
EXTRN nfatrecs:WORD
|
|
EXTRN nfats:WORD
|
|
extrn pblock:dword
|
|
extrn physical_drv:byte
|
|
extrn psecsiz:word
|
|
extrn rwmode:byte ; data/directory/FAT, read/write
|
|
extrn secperclu:word
|
|
extrn bcb_root:dword ; PCMODE disk buffer root
|
|
extrn deblock_seg:word
|
|
|
|
BDOS_CODE cseg
|
|
|
|
extrn clus2sec:near
|
|
extrn discard_dirbuf:near
|
|
extrn fdos_error:near
|
|
extrn flush_dirbuf:near
|
|
extrn hshdscrd:near
|
|
extrn read_block:near
|
|
extrn select_adrive:near ; select drive AL
|
|
extrn write_block:near
|
|
|
|
public alloc_cluster ; allocate data block
|
|
public alloc_chain ; allocate a chain
|
|
public buffers_check ; check if buffers exist for this drive
|
|
PUBLIC delfat ; release data blocks
|
|
PUBLIC discard_all ; discard all buffers on ADRIVE
|
|
public discard_dir ; discard directory buffers on ADRIVE
|
|
public discard_dirty ; discard directory buffers on ADRIVE
|
|
PUBLIC fixfat ; set value of FAT entry
|
|
public flush_drive ; flush buffers to disk
|
|
public locate_buffer ; locate a buffer
|
|
PUBLIC update_dat ; flush write pending buffers
|
|
public update_ddsc_free ; count free blocks on drive
|
|
PUBLIC update_dir ; update directory entry
|
|
PUBLIC update_fat ; write out modified FAT records
|
|
public zeroblk ; zero cluster (MKDIR)
|
|
if DELWATCH
|
|
public allocate_cluster ; allocate free cluster on adrive
|
|
public change_fat_entry ; write a new value into the FAT
|
|
endif
|
|
|
|
|
|
|
|
|
|
update_ddsc_free:
|
|
;----------------
|
|
; make sure DDSC_FREE is up to date
|
|
; a by-product of this is to checksum the FAT, so we can spot changes
|
|
; of removable media
|
|
push es
|
|
les bx,ss:current_ddsc
|
|
mov cx,es:DDSC_FREE[bx] ; get current free space
|
|
jcxz update_ddsc_free30 ; if none recount to make sure
|
|
inc cx ; is count uninitialised ? (=FFFF)
|
|
jz update_ddsc_free30 ; if so better count the free space
|
|
update_ddsc_free10:
|
|
pop es
|
|
ret
|
|
|
|
update_ddsc_free30:
|
|
; rebuild our free space count
|
|
xor ax,ax ; assume no free space yet
|
|
lea di,DDSC_BLOCK[bx] ; ES:DI -> DDSC_BLOCK
|
|
stosw ; DDSC_BLOCK = 0
|
|
stosw ; DDSC_FREE = 0
|
|
inc ax ; skip reserved block #'s 0 and 1
|
|
update_ddsc_free40:
|
|
inc ax ; move to next data block #
|
|
cmp ax,lastcl ; are we beyond end of disk
|
|
ja update_ddsc_free10 ; stop if all free blocks counted
|
|
push ax ; save current index
|
|
call getblk ; get contents of FAT entry, update ZF
|
|
pop ax ; restore current FAT index
|
|
jnz update_ddsc_free40 ; try next block if not free
|
|
inc es:DDSC_FREE[bx] ; one more free block
|
|
jmps update_ddsc_free40 ; try next block
|
|
|
|
|
|
|
|
discard_dirty:
|
|
;-------------
|
|
; This gets called after a write-protect error is returned
|
|
|
|
mov ah,BF_DIRTY ; discard dirty FAT, dir & data
|
|
jmps discard_buffers
|
|
|
|
discard_all:
|
|
;-----------
|
|
mov ah,BF_ISFAT+BF_ISDIR+BF_ISDAT
|
|
jmps discard_buffers ; discard all the buffers
|
|
|
|
discard_dir:
|
|
;-----------
|
|
mov ah,BF_ISDIR ; dir only, leave data and FAT
|
|
; jmps discard_buffers
|
|
|
|
discard_buffers:
|
|
;---------------
|
|
; entry: adrive = drive to discard
|
|
; AH = flags for type to discard i.e. BF_ISFAT, etc.
|
|
|
|
mov al,adrive ; get the work drive
|
|
call discard_dirbuf ; discard 32-byte directory buffer
|
|
call hshdscrd ; discard hashing info for drive
|
|
les si,bcb_root ; get first buffer
|
|
discard_buffers10:
|
|
cmp al,es:BCB_DRV[si] ; does the drive match?
|
|
jne discard_buffers20 ; try next one if not
|
|
test ah,es:BCB_FLAGS[si] ; does the type match?
|
|
jz discard_buffers20 ; try next one if not
|
|
mov es:BCB_DRV[si],0FFh ; else discard the buffer
|
|
mov es:BCB_FLAGS[si],0
|
|
discard_buffers20:
|
|
if DOS5
|
|
mov si,es:BCB_NEXT[si] ; get next buffer address
|
|
cmp si,word ptr bcb_root
|
|
else
|
|
les si,es:BCB_NEXT[si] ; get next buffer address
|
|
cmp si,0ffffh
|
|
endif
|
|
jne discard_buffers10 ; and repeat until all done
|
|
discard_buffers30:
|
|
push ds ! pop es ; restore ES and return
|
|
ret
|
|
|
|
|
|
;-------------
|
|
buffers_check:
|
|
;-------------
|
|
; entry: AL = drive to check (preserved)
|
|
; AH = flags
|
|
; exit: ZF = 1 if all buffers clean on this drive
|
|
|
|
push ds ; we use DS here cause it's quicker...
|
|
lds si,ss:bcb_root ; start with most recently used
|
|
buffers_check10:
|
|
cmp al,BCB_DRV[si] ; check if for different drive
|
|
jne buffers_check20 ; skip if not our problem
|
|
test ah,BCB_FLAGS[si] ; test if its one we are looking for
|
|
jnz buffers_check30 ; return with non-zero condition
|
|
buffers_check20:
|
|
if DOS5
|
|
mov si,BCB_NEXT[si] ; get next buffer address
|
|
cmp si,ss:word ptr bcb_root
|
|
else
|
|
lds si,BCB_NEXT[si] ; get next buffer address
|
|
cmp si,0ffffh
|
|
endif
|
|
jne buffers_check10 ; loop back if more to do
|
|
xor dx,dx ; set ZF = 1
|
|
buffers_check30:
|
|
pop ds ; restore DS after BCBs done
|
|
ret
|
|
|
|
|
|
eject
|
|
|
|
; entry: AX = first block to release
|
|
; exit: AX and following released
|
|
|
|
delfat: ; release chain of clusters
|
|
;------
|
|
cmp ax,2 ; is block number too small?
|
|
jb delfat10 ; yes, then stop it
|
|
cmp ax,lastcl ; is block number too large?
|
|
ja delfat10 ; yes, then stop it
|
|
push ax ; else save the number
|
|
call getblk ; get the next link
|
|
xchg ax,cx ; CX = link
|
|
pop ax ; AX = this block
|
|
sub bx,bx ; set it to 0000
|
|
push cx ; save the link for next pass
|
|
call fixfat ; release the block
|
|
pop ax ; AX = next block or end
|
|
jmps delfat ; try again until all released
|
|
delfat10: ; all blocks in chain freed
|
|
ret
|
|
|
|
|
|
; On Entry:
|
|
; AX = block to read
|
|
; On Exit:
|
|
; AX = next FAT block index
|
|
;
|
|
Public getnblk
|
|
|
|
getnblk: ;UWORD getnblk(blk);
|
|
;-------
|
|
;
|
|
push ax
|
|
call getblk ; get current setting
|
|
pop bx
|
|
jz getnblk10 ; return if something there
|
|
ret
|
|
getnblk10:
|
|
mov ax,dosfat ; if unallocated then allocate it
|
|
push ax
|
|
xchg ax,bx ; AX = blk, BX = i
|
|
call fixfat
|
|
pop ax
|
|
mov dx,ax ; DX = end of chain
|
|
xor cx,cx ; no blocks follow this one
|
|
ret
|
|
|
|
; On Entry:
|
|
; AX = block to read
|
|
; On Exit:
|
|
; AX = contents
|
|
; ZF = 1 if AX == 0000h (disk full)
|
|
|
|
Public getblk
|
|
|
|
;------
|
|
getblk:
|
|
;------
|
|
push es ! push bx
|
|
call fatptr ; get address of block AX in buffer
|
|
mov ax,es:[bx] ; get the word from FAT
|
|
jnz getblk10 ; skip if on odd address (must be 12 bit)
|
|
cmp dosfat,FAT12 ; else check if 16 or 12 bit
|
|
je getblk20 ; skip if even 12 bit
|
|
pop bx ! pop es
|
|
test ax,ax ; update ZF
|
|
ret
|
|
|
|
getblk10:
|
|
shr ax,1 ; shift top 12 bits down
|
|
shr ax,1
|
|
shr ax,1
|
|
shr ax,1
|
|
getblk20:
|
|
and ax,0FFFh ; leave bottom 12 bits only
|
|
pop bx ! pop es
|
|
ret
|
|
|
|
|
|
|
|
alloc_cluster:
|
|
;-------------
|
|
; On Entry:
|
|
; AX = previous cluster (hint for desired start)
|
|
; On Exit:
|
|
; AX = start of chain
|
|
; CY set on failure
|
|
;
|
|
mov cx,1
|
|
; jmp alloc_chain
|
|
|
|
alloc_chain:
|
|
;-----------
|
|
; On Entry:
|
|
; AX = previous cluster (hint for desired start)
|
|
; CX = # clusters wanted
|
|
; On Exit:
|
|
; AX = start of chain, 0 on failure
|
|
; CY set on failure
|
|
;
|
|
; We want to allocate a chain of CX clusters, AX was previous cluster
|
|
; We return with CY clear and AX = 1st cluster in chain on success,
|
|
; CY set on failure
|
|
;
|
|
; When allocating a new chain we first ask SSTOR how much physical space is
|
|
; present on the disk. Until SSTOR reports at least 2 clusters free we
|
|
; repeatedly call DELWATCH to purge files and recover space. If DELWATCH is
|
|
; unable to free space we return "disk full".
|
|
;
|
|
; When allocating a block we normally are normally given a target block to
|
|
; start searching from. We allow DELWATCH to alter this value when it frees
|
|
; space to optimise the search.
|
|
;
|
|
push ax ! push cx ; save entry parameters
|
|
call update_ddsc_free ; make sure DDSC_FREE is correct
|
|
if DELWATCH
|
|
alloc_chain10:
|
|
pop dx ! push dx ; DX = clusters wanted
|
|
les bx,ss:current_ddsc
|
|
mov cx,es:DDSC_FREE[bx] ; CX = clusters available
|
|
mov al,adrive ; AL = current drive
|
|
cmp cx,dx ; do we have enough room in the FAT ?
|
|
jb alloc_chain20 ; if not ask DELWATCH to purge
|
|
mov ah,SSTOR_SPACE ; does Superstore have room for data?
|
|
callf ss:fdos_stub ; call stub routine
|
|
test cx,cx ; are we out of space ?
|
|
jnz alloc_chain40 ; no, go ahead and allocate the chain
|
|
mov es:DDSC_FREE[bx],cx ; SSTOR says there's none, lets agree
|
|
call update_fat ; flush FAT to bring SSTOR up to date
|
|
jmps alloc_chain10 ; go round again and ask DELWATCH to
|
|
; free up some more space
|
|
; we loop until either SSTOR says OK
|
|
; or DELWATCH frees all it can
|
|
alloc_chain20:
|
|
mov ah,DELW_FREECLU ; ask DELWATCH to purge a file
|
|
callf ss:fdos_stub ; call stub routine
|
|
cmp cx,es:DDSC_FREE[bx] ; can DELWATCH free up any space ?
|
|
jne alloc_chain10 ; yes, go and try again
|
|
alloc_chain30:
|
|
pop cx ! pop ax ; failure, restore stack
|
|
jmps alloc_chain80 ; and exit in failure
|
|
|
|
alloc_chain40:
|
|
endif
|
|
pop cx ! pop ax ; restore entry parameters
|
|
push cx ; save # required
|
|
call allocate_cluster ; try to allocate 1st cluster
|
|
pop cx ; recover # required
|
|
test ax,ax ; could we ?
|
|
jz alloc_chain80
|
|
dec cx ; one less to allocate
|
|
|
|
push ax ; save head of chain
|
|
jcxz alloc_chain60
|
|
alloc_chain50:
|
|
push cx
|
|
|
|
push ax ; save current end of chain
|
|
call allocate_cluster ; allocate another cluster
|
|
pop bx ; BX = end of chain
|
|
|
|
test ax,ax ; could we allocate anything ?
|
|
jz alloc_chain70 ; no, bail out and free partial chain
|
|
|
|
xchg ax,bx ; AX = previous cluster, link cluster
|
|
push bx ; BX to end of the chain
|
|
call fixfat
|
|
pop ax ; AX = new end of chain
|
|
|
|
pop cx
|
|
loop alloc_chain50
|
|
alloc_chain60:
|
|
pop ax ; return the start of the chain as it's
|
|
clc ; long enough now...
|
|
ret
|
|
|
|
alloc_chain70:
|
|
; We haven't enough free clusters - lets free what we allocated so far
|
|
pop cx ; discard count
|
|
pop ax ; AX = start of chain
|
|
call delfat ; release the chain
|
|
alloc_chain80:
|
|
xor ax,ax
|
|
stc ; we couldn't manage it
|
|
ret
|
|
|
|
allocate_cluster:
|
|
;----------------
|
|
; On Entry:
|
|
; AX = cluster to start from (AX = none known)
|
|
; On Exit:
|
|
; AX = cluster allocated
|
|
;
|
|
test ax,ax ; previous block known?
|
|
jnz alloc_cl10 ; skip if it is
|
|
push ds
|
|
lds bx,ss:current_ddsc
|
|
mov ax,ds:DDSC_BLOCK[bx] ; else continue from last allocated block
|
|
pop ds
|
|
alloc_cl10:
|
|
mov bx,lastcl ; highest block number on current disk
|
|
cmp ax,bx ; is it within disk size?
|
|
jb alloc_cl20 ; skip if it is
|
|
sub ax,ax ; start at the beginning
|
|
|
|
alloc_cl20:
|
|
mov si,ax ; remember start of search
|
|
test ax,ax ; is this the 1st block?
|
|
jnz alloc_cl30 ; no
|
|
inc ax ; start at beginning
|
|
alloc_cl30: ; main loop:
|
|
inc ax ; skip to block after current
|
|
push ax ! push si ; quick save
|
|
call getblk ; get the content of this block
|
|
pop si ! pop ax
|
|
jz alloc_cl50 ; return if free
|
|
cmp ax,bx ; are we at the end yet?
|
|
jb alloc_cl30 ; no, try next block
|
|
xor ax,ax ; wrap to start of disk
|
|
mov bx,si ; remember starting position last time
|
|
test bx,bx ; have we been all the way round ?
|
|
jnz alloc_cl20 ; no, lets search from start
|
|
push ds
|
|
lds bx,ss:current_ddsc
|
|
mov ds:DDSC_FREE[bx],ax ; we definitely have none left
|
|
pop ds
|
|
ret ; return (0);
|
|
|
|
alloc_cl50:
|
|
push ds ; block # AX is available
|
|
lds bx,ss:current_ddsc
|
|
mov ds:DDSC_BLOCK[bx],ax ; remember for next time
|
|
pop ds
|
|
|
|
push ax
|
|
mov bx,dosfat ; mark this block as end of file
|
|
call fixfat ; for convenience
|
|
pop ax
|
|
|
|
test ax,ax ; update ZF from AX
|
|
ret ; return block number
|
|
|
|
|
|
|
|
|
|
|
|
if DELWATCH
|
|
|
|
; Update a FAT entry with a new value
|
|
|
|
change_fat_entry:
|
|
;----------------
|
|
; On Entry:
|
|
; AX = block number to change
|
|
; DX = new value
|
|
; On Exit:
|
|
; None
|
|
;
|
|
mov bx,dx
|
|
; jmps fixfat
|
|
endif
|
|
|
|
; entry: AX = block number to change
|
|
; BX = new value
|
|
; exit: DS,ES = sysdat
|
|
|
|
;------
|
|
fixfat:
|
|
;------
|
|
push bx ; save new value
|
|
push ax
|
|
call update_ddsc_free ; make sure DDSC_FREE is correct
|
|
pop ax
|
|
cmp dosfat,FAT16 ; check if 16-bit FAT
|
|
jne fixfat30 ; skip if 12 bit FAT
|
|
call fatptr ; ES:BX -> FAT word to modify
|
|
pop ax ; restore new value
|
|
xor dx,dx ; get a zero (no change of space)
|
|
test ax,ax ; are we setting to 0 or non-zero?
|
|
xchg ax,es:[bx] ; set the word in the buffer
|
|
jnz fixfat10 ; skip if releasing block
|
|
test ax,ax ; check if word was 0 before
|
|
jz fixfat20 ; skip if setting 0 to 0
|
|
inc dx ; DX = 0001h, one free cluster more
|
|
jmps fixfat15
|
|
fixfat10: ; allocating or fixing block
|
|
test ax,ax ; check if word was 0 before
|
|
jnz fixfat20 ; skip if setting non-0 to non-0
|
|
dec dx ; one free cluster less now
|
|
fixfat15: ; DX = change in free space (-1,1)
|
|
les si,current_ddsc
|
|
add es:DDSC_FREE[si],dx ; update free space count
|
|
fixfat20:
|
|
les si,bcb_root ; ES:SI -> buffer control block
|
|
or es:BCB_FLAGS[si],BF_DIRTY
|
|
; mark the buffer as dirty
|
|
push ds ! pop es ; ES back to local DS
|
|
ret
|
|
|
|
|
|
; We're dealing with a 12-bit FAT...
|
|
|
|
fixfat30: ; changing 12-bit FAT entry
|
|
call fatptr ; get address of block AX in ES:BX
|
|
pop cx ; get new value
|
|
mov dx,es:[bx] ; get old value
|
|
jz fixfat40 ; skip if even word
|
|
mov ax,0FFF0h ; set mask for new value
|
|
add cx,cx ; else shift new value into top bits
|
|
add cx,cx
|
|
add cx,cx
|
|
add cx,cx
|
|
jmps fixfat50 ; set the new word
|
|
fixfat40:
|
|
mov ax,00FFFh ; set mask for new value
|
|
and cx,ax
|
|
fixfat50: ; AX = mask, CX = new, DX = old
|
|
mov si,0 ; assume space doesn't change
|
|
jnz fixfat60 ; skip if new value is zero
|
|
test dx,ax ; test if old value was zero as well
|
|
jz fixfat70 ; yes, no change in free space
|
|
inc si ; else one more block available
|
|
jmps fixfat70
|
|
fixfat60: ; new value is non-zero
|
|
test dx,ax ; is old value non-zero as well?
|
|
jnz fixfat70 ; yes, no change in free space
|
|
dec si ; else one block less free now
|
|
fixfat70:
|
|
not ax ; flip the mask bits around
|
|
and dx,ax ; zero out old value
|
|
or dx,cx ; combine old & new value
|
|
mov es:[bx],dx ; update the FAT
|
|
xchg ax,si ; AX = free space change (-1, 0 , 1)
|
|
les si,current_ddsc
|
|
add es:DDSC_FREE[si],ax ; update free space count
|
|
les si,bcb_root ; get buffer control block
|
|
or es:BCB_FLAGS[si],BF_DIRTY
|
|
; mark the buffer as dirty
|
|
cmp split_fat,0 ; is 12-bit entry split across sectors
|
|
je fixfat80 ; need some magic if so
|
|
; handle a split FAT update
|
|
mov dx,fatrec ; lower sector number
|
|
inc dx ; get the upper sector
|
|
call locate_fat ; find the buffer
|
|
or es:BCB_FLAGS[si],BF_DIRTY
|
|
; mark buffer as write pending
|
|
mov al,fatbyth ; get the high byte
|
|
mov es:BCB_DATA[si],al ; store the high byte at the beginning
|
|
mov dx,fatrec ; get the previous sector
|
|
call locate_fat ; read into memory
|
|
or es:BCB_FLAGS[si],BF_DIRTY
|
|
; mark buffer as write pending
|
|
mov bx,psecsiz
|
|
dec bx ; BX = sector size - 1
|
|
mov al,fatbytl ; get the low byte
|
|
mov es:BCB_DATA[si+bx],al
|
|
|
|
fixfat80:
|
|
push ds ! pop es ; ES back to local DS
|
|
ret
|
|
|
|
|
|
; On Entry:
|
|
; AX = cluster number
|
|
; On Exit:
|
|
; AX preserved
|
|
; ES:BX -> address of word
|
|
; BCBSEG = segment of FAT FCB
|
|
; ZF = 1 if word on even address
|
|
; SPLIT_FAT = 0FFh if xing sector boundary
|
|
;
|
|
; CX = entries left in sector (if FAT16 - performance optimisation)
|
|
;
|
|
|
|
Public fatptr
|
|
fatptr:
|
|
;------
|
|
push ax ; save block number
|
|
mov bx,ax
|
|
sub dx,dx ; AX/DX = cluster #
|
|
cmp dosfat,FAT16 ; is it 16 bit FAT?
|
|
je fatptr10
|
|
shr ax,1 ; shift for 1 1/2 byte, else 2 byte
|
|
fatptr10:
|
|
add ax,bx ; AX = offset into FAT
|
|
adc dx,0 ; AX/DX = 32 bit offset
|
|
mov cx,psecsiz ; CX = sector size
|
|
div cx ; AX = sector offset
|
|
dec cx ; CX = sector size - 1
|
|
push dx ; DX = offset within FAT sector
|
|
push cx
|
|
add ax,fatadd ; make it absolute sector address
|
|
mov fatrec,ax ; save FAT sector for FIXFAT
|
|
xchg ax,dx ; DX = FAT sector
|
|
call locate_fat ; locate the sector
|
|
pop cx ; CX = sector size - 1
|
|
pop bx ; restore offset within FAT sector
|
|
pop ax ; restore cluster #
|
|
sub cx,bx ; CX = bytes left in sector - 1
|
|
lea bx,BCB_DATA[si+bx] ; ES:BX -> buffer data
|
|
cmp dosfat,FAT16 ; is it 16 bit media
|
|
jne fatptr20 ; skip if 12 bit media
|
|
shr cx,1 ; CX = extra entries left in sector
|
|
cmp ax,ax ; always set ZF = 1
|
|
ret ; return ES:BX -> word in FAT
|
|
|
|
fatptr20: ; it's a 12 bit FAT, is it a split FAT?
|
|
mov split_fat,0 ; assume no boundary crossing
|
|
jcxz fatptr30 ; end of sector, it's a split FAT
|
|
test al,1 ; ZF = 1 if even cluster
|
|
ret ; return ES:BX -> word in FAT buffer
|
|
|
|
fatptr30: ; block split across two sectors
|
|
push ax
|
|
mov split_fat,0FFh ; yes, the difficult case
|
|
mov al,es:[bx] ; get the low byte from 1st sector
|
|
mov fatbytl,al ; save it for later
|
|
mov dx,fatrec ; get the FAT record is
|
|
inc dx ; get 2nd sector
|
|
call locate_fat ; read the 2nd sector
|
|
sub bx,bx
|
|
lea bx,BCB_DATA[si+bx] ; ES:BX -> buffer data
|
|
mov al,es:[bx] ; get 1st byte from next sector
|
|
mov fatbyth,al ; save the high byte
|
|
push ds ; ES = local DS
|
|
pop es
|
|
mov bx,offset fatbytl ; ES:BX -> <fatbytl,fatbyh>
|
|
pop ax
|
|
test al,1 ; set non-zero condition, odd word
|
|
ret
|
|
|
|
|
|
if DOS5
|
|
|
|
; entry: DX = sector number to read
|
|
; exit: ES:SI = BCB
|
|
|
|
locate_fat:
|
|
;----------
|
|
mov ah,0 ; set sector address overflow = 0
|
|
mov cx,0ff00h+BF_ISFAT ; request a FAT buffer w/ preread
|
|
locate_buffer:
|
|
;-------------
|
|
; On Entry:
|
|
; AH:DX = sector to locate
|
|
; adrive = driver
|
|
; CH = 0FFh if preread required
|
|
; CL = buffer type
|
|
; On Exit:
|
|
; ES:SI -> BCB_
|
|
;
|
|
|
|
mov al,adrive ; get our drive number
|
|
les si,bcb_root ; get it from the right buffer list
|
|
locate10:
|
|
cmp dx,es:BCB_REC[si] ; does our sector address match?
|
|
jne locate20 ; skip if it doesn't
|
|
cmp ah,es:BCB_REC2[si] ; does record address overflow match?
|
|
jne locate20 ; skip if not
|
|
cmp al,es:BCB_DRV[si] ; does the drive match?
|
|
je locate30 ; found if it all matches
|
|
locate20: ; MRU buffer doesn't match
|
|
mov si,es:BCB_NEXT[si] ; try the next
|
|
cmp si,word ptr bcb_root ; while there are more buffers
|
|
jne locate10
|
|
|
|
push ax ! push cx ! push dx ; save all registers
|
|
mov si,es:BCB_PREV[si] ; recycle least recently used buffer
|
|
call flush_buffer ; write buffer to disk
|
|
pop dx ! pop cx ! pop ax ; restore all registers
|
|
|
|
mov es:BCB_DRV[si],al ; fill in the BCB: drive
|
|
mov es:BCB_REC[si],dx ; record low,middle
|
|
mov es:BCB_REC2[si],ah ; record high
|
|
mov es:BCB_FLAGS[si],cl ; mark as clean, ISFAT,ISDIR or ISDAT
|
|
test ch,ch ; is preread required?
|
|
jz locate30 ; skip if it isn't
|
|
call fill_buffer ; read it from disk
|
|
locate30:
|
|
cmp si,word ptr bcb_root ; are we already at the head ?
|
|
jne locate40 ; if not move ourself there
|
|
ret
|
|
locate40:
|
|
mov bx,es:BCB_NEXT[si] ; BX = next buffer
|
|
mov di,es:BCB_PREV[si] ; DI = previous buffer
|
|
mov es:BCB_NEXT[di],bx ; unlink buffer from the
|
|
mov es:BCB_PREV[bx],di ; chain
|
|
mov bx,si
|
|
xchg bx,word ptr bcb_root ; become the new head, BX = old head
|
|
mov es:BCB_NEXT[si],bx ; old chain follow us
|
|
mov di,si
|
|
xchg di,es:BCB_PREV[bx] ; back link to our buffer, DI = LRU buffer
|
|
mov es:BCB_PREV[si],di ; link ourselves to LRU buffer
|
|
mov es:BCB_NEXT[di],si ; forward link to our buffer
|
|
ret
|
|
|
|
|
|
; Flush all dirty FAT buffers for drive AL
|
|
; entry: AL = drive to flush (0-15)
|
|
; exit: CY = 0 if no error
|
|
; ax,bx,cx,dx,es preserved
|
|
|
|
flush_fat:
|
|
;---------
|
|
; entry: AL = drive for FAT flush
|
|
|
|
mov ah,BF_ISFAT ; flush all dirty FAT buffers
|
|
jmps flush_drive ; shared code for all flushes
|
|
|
|
;----------
|
|
update_dir:
|
|
;----------
|
|
call flush_dirbuf ; flush local dirbuf to buffers
|
|
;---------
|
|
flush_dir:
|
|
;---------
|
|
mov ah,BF_ISDIR ; write out dirty directories
|
|
jmps flush_adrive ; update the disk
|
|
|
|
|
|
;----------
|
|
update_dat:
|
|
;----------
|
|
mov ah,BF_ISDAT ; write out dirty data
|
|
jmps flush_adrive ; update the disk
|
|
|
|
;----------
|
|
update_fat: ;write out modified FAT buffers
|
|
;----------
|
|
mov ah,BF_ISFAT ; flush all dirty FAT buffers
|
|
; jmp flush_adrive ; update the disk if dirty
|
|
|
|
flush_adrive:
|
|
;------------
|
|
mov al,adrive ; AL = currently selected drive
|
|
; jmp flush_drive
|
|
|
|
; Write out all dirty data buffers for a given drive
|
|
; entry: AL = drive to be flushed
|
|
; AH = mask of buffer types to be flushed
|
|
; exit: AX,DX preserved
|
|
; Note: sector buffers will be written in the
|
|
; sequence in which they appear on disk (low to high)
|
|
|
|
flush_drive:
|
|
;-----------
|
|
push es
|
|
push si
|
|
flush_drive10:
|
|
les si,bcb_root ; start with the first buffer
|
|
mov bx,0FFFFh ; assume no buffer found
|
|
flush_drive20:
|
|
test es:BCB_FLAGS[si],BF_DIRTY
|
|
; has buffer been written to?
|
|
jz flush_drive40 ; no, do the next one
|
|
test es:BCB_FLAGS[si],ah ; is it one of these buffers?
|
|
jz flush_drive40 ; no, do the next one
|
|
cmp al,es:BCB_DRV[si] ; does the drive match?
|
|
jne flush_drive40 ; skip if wrong drive
|
|
; we've found a buffer to flush
|
|
cmp bx,0FFFFh ; first buffer ever found in list?
|
|
jz flush_drive30 ; yes, save as new best candidate
|
|
; else check if < previous lowest addr
|
|
mov dx,es:BCB_REC[si]
|
|
sub dx,ds:BCB_REC[bx]
|
|
mov dl,es:BCB_REC2[si] ; compare the disk addresss
|
|
sbb dl,ds:BCB_REC2[bx]
|
|
jnb flush_drive40 ; CY = 0 if new BCB higher
|
|
flush_drive30: ; else ES = best BCB so far
|
|
mov bx,si ; save it for later
|
|
flush_drive40:
|
|
mov si,es:BCB_NEXT[si] ; get next buffer address
|
|
cmp si,ss:word ptr bcb_root
|
|
jne flush_drive20
|
|
cmp bx,0FFFFh ; did we find a dirty buffer?
|
|
jz flush_drive50 ; no, all buffers cleaned
|
|
mov si,bx ; ES:SI -> BCB to flush
|
|
call flush_buffer ; write sector to disk
|
|
jmps flush_drive10 ; check if more dirty buffers
|
|
flush_drive50:
|
|
pop si
|
|
pop es
|
|
ret
|
|
|
|
else
|
|
; entry: DX = sector number to read
|
|
; exit: ES:SI = BCB
|
|
|
|
locate_fat:
|
|
;----------
|
|
mov ah,0 ; set sector address overflow = 0
|
|
mov cx,0ff00h+BF_ISFAT ; request a FAT buffer w/ preread
|
|
locate_buffer:
|
|
;-------------
|
|
; On Entry:
|
|
; AH:DX = sector to locate
|
|
; adrive = driver
|
|
; CH = 0FFh if preread required
|
|
; CL = buffer type
|
|
; On Exit:
|
|
; ES:SI -> BCB_
|
|
;
|
|
|
|
|
|
mov al,adrive ; get our drive number
|
|
les si,bcb_root ; get it from the right buffer list
|
|
mov bx,0FFFFh ; no previous buffers yet
|
|
locate1:
|
|
cmp dx,es:BCB_REC[si] ; does our sector address match?
|
|
jne locate2 ; skip if it doesn't
|
|
cmp ah,es:BCB_REC2[si] ; does record address overflow match?
|
|
jne locate2 ; skip if not
|
|
cmp al,es:BCB_DRV[si] ; does the drive match?
|
|
je locate6 ; found if it all matches
|
|
locate2: ; MRU buffer doesn't match
|
|
cmp es:BCB_LINK_OFF[si],0FFFFh
|
|
je locate3 ; are there more buffers?
|
|
push es ! pop ds
|
|
mov bx,si ; remember previous buffer
|
|
les si,es:BCB_NEXT[si] ; move on to next buffer
|
|
jmps locate1
|
|
|
|
locate3: ; we found the LRU buffer
|
|
push ax ! push cx ! push dx ; save all registers
|
|
|
|
call pick_cheapest ; determine cheapest buffer
|
|
; ES:SI -> cheapest buffer
|
|
; DS:BX -> previous link
|
|
test es:BCB_FLAGS[si],BF_DIRTY
|
|
jz locate5 ; skip if buffer not dirty
|
|
|
|
mov al,es:BCB_DRV[si] ; get the buffer's drive
|
|
mov ah,es:BCB_FLAGS[si] ; flush all buffers of same type
|
|
and ah,BF_ISFAT+BF_ISDIR+BF_ISDAT
|
|
push ss ! pop ds
|
|
call flush_drive ; gives us burst mode behaviour
|
|
; but might re-arrange buffers
|
|
call find_prev ; find preceding BCB for re-link
|
|
; so DS:BX -> BCB_LINK == ES:SI
|
|
locate5:
|
|
pop dx ! pop cx ! pop ax ; restore all registers
|
|
mov es:BCB_DRV[si],al ; fill in the BCB: drive
|
|
mov es:BCB_REC[si],dx ; record low,middle
|
|
mov es:BCB_REC2[si],ah ; record high
|
|
|
|
mov es:BCB_FLAGS[si],cl ; mark as clean, ISFAT,ISDIR or ISDAT
|
|
test ch,ch ; is preread required?
|
|
jz locate6 ; skip if it isn't
|
|
|
|
push es ! push si
|
|
push ds ! push bx ; save the previous buffer segment
|
|
push ss ! pop ds ; for unlinking our buffer
|
|
|
|
call fill_buffer ; read it from disk, don't return
|
|
; on physical errors
|
|
pop bx ! pop ds
|
|
pop si ! pop es
|
|
locate6:
|
|
cmp bx,0FFFFh ; are we the MRU buffer
|
|
jz locate9 ; yes, leave it at the head
|
|
locate8: ; arrive here if found as not 1st
|
|
mov ax,es:BCB_LINK_OFF[si]
|
|
mov BCB_LINK_OFF[bx],ax
|
|
mov ax,es:BCB_LINK_SEG[si]
|
|
mov BCB_LINK_SEG[bx],ax ; unlink this buffer
|
|
|
|
; we now want to attach the
|
|
push ss ! pop ds ; BCB at ES:BX to the root
|
|
mov ax,ds:word ptr bcb_root
|
|
mov es:BCB_LINK_OFF[si],ax
|
|
mov ax,ds:word ptr bcb_root+2
|
|
mov es:BCB_LINK_SEG[si],ax
|
|
mov ds:word ptr bcb_root,si ; insert the new entry
|
|
mov ds:word ptr bcb_root+2,es
|
|
locate9:
|
|
push ss ! pop ds ; DS back to normal
|
|
ret
|
|
|
|
|
|
pick_cheapest: ; find cheapest replacement sector
|
|
;-------------
|
|
; entry: ES:SI = least recently used BCB
|
|
; DS:BX = previous buffer
|
|
; exit: ES, BX unmodified if LRU buffer marked as cheap
|
|
; or no other cheap buffer found
|
|
; -or-
|
|
; ES:SI, DS:BX modified to cheapest buffer and previous buffer
|
|
|
|
test es:BCB_FLAGS[si],BF_DIRTY
|
|
; is this buffer very expensive?
|
|
jz pck_chp5 ; return if it is cheapest "cheap" buffer
|
|
; else is cheapest "expensive" buffer
|
|
; find the cheapest "cheap" buffer
|
|
les si,ss:bcb_root ; as we start at the root with
|
|
mov ax,0FFFFh ; no previous buffer
|
|
pck_chp1: ; check if buffer at DS is cheap
|
|
test es:BCB_FLAGS[si],BF_ISFAT+BF_ISDIR+BF_DIRTY
|
|
; check the "not cheap" flag
|
|
jnz pck_chp2 ; skip if expensive buffer
|
|
mov ds,dx
|
|
mov bx,ax ; remember previous buffer
|
|
pck_chp2:
|
|
mov dx,es ; remember this buffer
|
|
mov ax,si ; when we move onto next one
|
|
les si,es:BCB_NEXT[si] ; get next buffer
|
|
cmp si,0FFFFh ; end of the line ?
|
|
jne pck_chp1 ; go again if still buffers
|
|
pck_chp3: ; done all buffers
|
|
cmp bx,0FFFFh ; did we find the root ?
|
|
jne pck_chp4
|
|
les si,ss:bcb_root ; return ES:SI -> root
|
|
ret
|
|
|
|
pck_chp4:
|
|
les si,ds:BCB_NEXT[bx] ; ES:SI = cheapest buffer
|
|
pck_chp5: ; ES:SI = cheapest buffer
|
|
ret ; DS:BX = previous
|
|
|
|
|
|
find_prev:
|
|
;---------
|
|
; entry: ES:SI = BCB to find previous buffer for
|
|
; exit: DS:BX = predecessor, BX = FFFF if first
|
|
;
|
|
mov ax,es
|
|
mov dx,si ; AX:DX -> buffer we want to find link for
|
|
mov bx,0FFFFh ; assume it's the first buffer
|
|
les si,ss:bcb_root ; we start at the root with
|
|
find_prv1:
|
|
mov cx,es
|
|
cmp dx,si ; does offset match ?
|
|
jne find_prv2
|
|
cmp ax,cx ; does segment match ?
|
|
je find_prv3 ; yes, return this one then
|
|
find_prv2:
|
|
mov ds,cx ; remember previous link
|
|
mov bx,si
|
|
les si,es:BCB_NEXT[si] ; else have a go at the next one
|
|
jmps find_prv1 ; and repeat until match
|
|
find_prv3:
|
|
ret
|
|
|
|
; Flush all dirty FAT buffers for drive AL
|
|
; entry: AL = drive to flush (0-15)
|
|
; exit: CY = 0 if no error
|
|
; ax,bx,cx,dx,es preserved
|
|
|
|
flush_fat:
|
|
;---------
|
|
; entry: AL = drive for FAT flush
|
|
|
|
mov ah,BF_ISFAT ; flush all dirty FAT buffers
|
|
jmps flush_drive ; shared code for all flushes
|
|
|
|
;----------
|
|
update_dir:
|
|
;----------
|
|
call flush_dirbuf ; flush local dirbuf to buffers
|
|
;---------
|
|
flush_dir:
|
|
;---------
|
|
mov ah,BF_ISDIR ; write out dirty directories
|
|
jmps flush_adrive ; update the disk
|
|
|
|
|
|
;----------
|
|
update_dat:
|
|
;----------
|
|
mov ah,BF_ISDAT ; write out dirty data
|
|
jmps flush_adrive ; update the disk
|
|
|
|
;----------
|
|
update_fat: ;write out modified FAT buffers
|
|
;----------
|
|
mov ah,BF_ISFAT ; flush all dirty FAT buffers
|
|
; jmp flush_adrive ; update the disk if dirty
|
|
|
|
flush_adrive:
|
|
;------------
|
|
mov al,adrive ; AL = currently selected drive
|
|
; jmp flush_drive
|
|
|
|
; Write out all dirty data buffers for a given drive
|
|
; entry: AL = drive to be flushed
|
|
; AH = mask of buffer types to be flushed
|
|
; exit: AX,DX preserved
|
|
; Note: sector buffers will be written in the
|
|
; sequence in which they appear on disk (low to high)
|
|
|
|
flush_drive:
|
|
;-----------
|
|
push ds
|
|
push es
|
|
push bx ; save registers
|
|
push si
|
|
flush_dr0:
|
|
les si,ss:bcb_root ; start with the first buffer
|
|
mov bx,0FFFFh ; assume no buffer found
|
|
flush_dr1:
|
|
test es:BCB_FLAGS[si],BF_DIRTY
|
|
; has buffer been written to?
|
|
jz flush_dr3 ; no, do the next one
|
|
test es:BCB_FLAGS[si],ah ; is it one of these buffers?
|
|
jz flush_dr3 ; no, do the next one
|
|
cmp al,es:BCB_DRV[si] ; does the drive match?
|
|
jne flush_dr3 ; skip if wrong drive
|
|
; we've found a buffer to flush
|
|
cmp bx,0FFFFh ; first buffer ever found in list?
|
|
jz flush_dr2 ; yes, save as new best candidate
|
|
; else check if < previous lowest addr
|
|
mov dl,es:BCB_REC2[si] ; compare the disk addresss
|
|
sub dl,ds:BCB_REC2[bx]
|
|
mov dx,es:BCB_REC[si]
|
|
sbb dx,ds:BCB_REC[bx]
|
|
jnb flush_dr3 ; CY = 0 if old BCB lower
|
|
flush_dr2: ; else ES = best BCB so far
|
|
push es
|
|
pop ds
|
|
mov bx,si ; save it for later
|
|
flush_dr3:
|
|
les si,es:BCB_NEXT[si] ; get next buffer address
|
|
cmp si,0ffffh
|
|
jne flush_dr1
|
|
flush_dr4: ; DS:BX = best BCB
|
|
cmp bx,0FFFFh ; did we find a dirty buffer?
|
|
jz flush_dr5 ; no, all buffers cleaned
|
|
mov si,bx ; ES:SI -> BCB to flush
|
|
push ds ! pop es
|
|
push ss ! pop ds
|
|
call flush_buffer ; write sector to disk
|
|
jmps flush_dr0 ; check if more dirty buffers
|
|
flush_dr5:
|
|
pop si
|
|
pop bx
|
|
pop es
|
|
pop ds ; restore registers
|
|
ret
|
|
|
|
endif
|
|
|
|
flush_buffer:
|
|
;------------
|
|
; entry: ES:SI = address of BCB
|
|
; exit: buffer flushed if BCB_FLAGS & BF_DIRTY
|
|
|
|
; note: preserves AX,BX,CX,DX,ES
|
|
|
|
test es:BCB_FLAGS[si],BF_DIRTY
|
|
; is the buffer dirty?
|
|
jz flush_buf9 ; skip update if not modified
|
|
flush_buf1:
|
|
push es ! push si
|
|
push ax ! push bx ; else save all registers
|
|
push cx ! push dx
|
|
mov al,es:BCB_DRV[si] ; get the buffer drive
|
|
cmp al,adrive ; same as the selected drive?
|
|
je flush_buf2 ; skip if already selected
|
|
push es ; save the BCB
|
|
push si
|
|
push ds ! pop es ; ES = SYSDAT
|
|
call select_adrive ; select drive AL, ZF = 1 if logged in
|
|
pop si
|
|
pop es ; recover BCB
|
|
jc flush_buf5 ; don't flush to bad drive
|
|
flush_buf2:
|
|
mov cx,nfats ; else FAT sectors written CX times
|
|
mov al,0000$0011b ; mark as FAT write
|
|
test es:BCB_FLAGS[si],BF_ISFAT
|
|
jnz flush_buf3 ; go ahead
|
|
mov cx,1 ; directory/data written once only
|
|
mov al,0000$0101b ; mark as directory write
|
|
test es:BCB_FLAGS[si],BF_ISDIR
|
|
jnz flush_buf3 ; if not dir, must be data
|
|
mov al,0000$0111b ; mark as data buffer write
|
|
flush_buf3: ; CX = # of times to write sector
|
|
mov rwmode,al
|
|
sub ax,ax ; offset for write = 0
|
|
flush_buf4: ; loop back to here for other copies
|
|
push ax
|
|
push cx ; save loop variables
|
|
call setup_rwx ; compute disk address
|
|
call write_buff ; write the sector
|
|
pop cx
|
|
pop ax
|
|
add ax,nfatrecs ; move to next FAT copy
|
|
loop flush_buf4 ; repeat for all FAT copies
|
|
flush_buf5:
|
|
and es:BCB_FLAGS[si],not BF_DIRTY
|
|
; mark it as no longer dirty
|
|
mov al,physical_drv ; work drive for BDOS function
|
|
cmp al,adrive ; drive from last IO_SELDSK
|
|
je flush_buf6 ; skip if flush to work drive
|
|
; else reselect BDOS drive after flush
|
|
push ds ! pop es ; ES = SYSDAT
|
|
call select_adrive ; reselect the work drive
|
|
flush_buf6:
|
|
pop dx ! pop cx ; restore all registers
|
|
pop bx ! pop ax
|
|
pop si ! pop es
|
|
flush_buf9: ; all done, CY = 0 if O.K.
|
|
ret
|
|
|
|
|
|
;-------
|
|
zeroblk: ; AX = blk
|
|
;-------
|
|
xor bx,bx ; Start at begining of cluster
|
|
call clus2sec ; translate to sector address
|
|
xchg ax,dx ; DX = low 16 bits of address
|
|
mov ah,al ; AH:DX = 24 bit sector address
|
|
mov cx,secperclu ; CX == sectors/cluster
|
|
zeroblk10: ; repeat for all sectors in cluster
|
|
push ax
|
|
push cx
|
|
push dx
|
|
mov cx,BF_ISDIR ; locate directory sector w/o preread
|
|
call locate_buffer ; this will find the cheapest buffer
|
|
or es:BCB_FLAGS[si],BF_DIRTY
|
|
lea di,BCB_DATA[si] ; ES:DI -> disk buffer
|
|
mov cx,psecsiz ; CX = byte count for REP STOSB
|
|
xor ax,ax
|
|
rep stosb ; zero the whole data buffer
|
|
pop dx
|
|
pop cx
|
|
pop ax
|
|
add dx,1 ; onto the next block
|
|
adc ah,0
|
|
loop zeroblk10 ; repeat for all sectors in cluster
|
|
jmp flush_dir
|
|
|
|
|
|
|
|
|
|
fill_buffer:
|
|
;-----------
|
|
; On Entry:
|
|
; ES:SI = address of BCB to be filled
|
|
; On Exit:
|
|
; ES:SI preserved
|
|
; data read into buffer
|
|
;
|
|
test es:BCB_FLAGS[si],BF_ISFAT
|
|
; are we reading a FAT sector?
|
|
jz fill_buf1 ; skip if directory/data
|
|
mov al,es:BCB_DRV[si] ; get the drive
|
|
call flush_fat ; write out all dirty buffers
|
|
mov al,0000$0010b ; reading from FAT area
|
|
jmps fill_buf3 ; go ahead
|
|
fill_buf1:
|
|
mov al,0000$0100b ; else mark as directory
|
|
test es:BCB_FLAGS[si],BF_ISDIR; test if directory read
|
|
jnz fill_buf3 ; go ahead
|
|
fill_buf2: ; neither FAT nor directory => data
|
|
mov al,0000$0110b ; mark read as data buffer read
|
|
fill_buf3:
|
|
mov rwmode,al
|
|
push cx
|
|
xor cx,cx
|
|
cmp al,0000$0010b
|
|
jne fill_buf4
|
|
mov cx,nfats
|
|
dec cx
|
|
fill_buf4:
|
|
mov es:BCB_DRV[si],0FFh ; discard in case of error
|
|
sub ax,ax ; no offset for 2nd copy yet
|
|
fill_buf5:
|
|
push ax
|
|
call setup_rwx ; compute disk address
|
|
call read_buff ; read the sector
|
|
pop ax
|
|
jns fill_buf6
|
|
; we can end here only if CX was non-zero above and we failed to read a
|
|
; FAT copy while there is still another one we could use
|
|
add ax,nfatrecs
|
|
dec cx
|
|
jmps fill_buf5
|
|
fill_buf6:
|
|
pop cx
|
|
mov al,adrive ; restore the drive
|
|
mov es:BCB_DRV[si],al ; set the drive #
|
|
ret
|
|
|
|
read_buff:
|
|
;---------
|
|
push es
|
|
push si ; save BCB_
|
|
push cur_dma_seg
|
|
push cur_dma ; save DMA address
|
|
push cx
|
|
mov cx,ss:deblock_seg
|
|
jcxz read_buff10
|
|
mov cur_dma_seg,cx
|
|
mov cur_dma,0 ; xfer via deblocking buffer
|
|
read_buff10:
|
|
pop cx
|
|
call read_block
|
|
pop cur_dma ; restore DMA address
|
|
pop cur_dma_seg
|
|
js read_buff20 ; can happen only on FAT read
|
|
mov cx,ss:deblock_seg ; if deblocked, copy data
|
|
jcxz read_buff20
|
|
les di,dword ptr cur_dma ; point to destination
|
|
mov cx,psecsiz ; CX = sector size
|
|
shr cx,1 ; CX = words per sector
|
|
push ds
|
|
mov ds,ss:deblock_seg
|
|
xor si,si ; DS:SI = source
|
|
rep movsw ; copy the data
|
|
pop ds
|
|
read_buff20: ; SF still indicating error here
|
|
pop si ; recover BCB_
|
|
pop es
|
|
ret
|
|
|
|
write_buff:
|
|
;----------
|
|
push es
|
|
push si
|
|
push cur_dma_seg
|
|
push cur_dma
|
|
mov cx,ss:deblock_seg ; if deblocking we have to
|
|
jcxz write_buff10 ; copy the data first
|
|
push ds ; save SYSDAT
|
|
les si,dword ptr cur_dma ; ES:SI -> source
|
|
push es ; save source seg
|
|
mov es,cx
|
|
xor di,di ; ES:DI -> deblocking buffer
|
|
mov cur_dma_seg,es
|
|
mov cur_dma,di ; do xfer via deblocking buffer
|
|
mov cx,psecsiz ; CX = sector size
|
|
shr cx,1 ; CX = words per sector
|
|
pop ds ; DS:SI -> source
|
|
rep movsw ; copy to deblocking buffer
|
|
pop ds ; restore SYSDAT
|
|
write_buff10:
|
|
call write_block
|
|
pop cur_dma
|
|
pop cur_dma_seg
|
|
pop si
|
|
pop es
|
|
ret
|
|
|
|
setup_rwx:
|
|
;---------
|
|
; entry: AX = sector offset (multiple FAT writes)
|
|
; ES:SI = BCB, BCB_REC filled in
|
|
; exit: all values set up for RWXIOSIF
|
|
|
|
mov cur_dma_seg,es ; segment = BCB_SEGMENT
|
|
lea dx,BCB_DATA[si]
|
|
mov cur_dma,dx ; offset
|
|
xor dx,dx
|
|
add ax,es:BCB_REC[si]
|
|
adc dl,es:BCB_REC2[si]
|
|
mov word ptr pblock,ax ; xfer starts at this block
|
|
mov word ptr pblock+WORD,dx
|
|
mov mult_sec,1 ; single sector transfer
|
|
ret
|
|
|
|
END
|