MS-DOS/v2.0/source/DISK.ASM

1302 lines
37 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;
; Disk routines for MSDOS
;
INCLUDE DOSSEG.ASM
CODE SEGMENT BYTE PUBLIC 'CODE'
ASSUME SS:DOSGROUP,CS:DOSGROUP
.xlist
.xcref
INCLUDE DOSSYM.ASM
INCLUDE DEVSYM.ASM
.cref
.list
TITLE DISK - Disk utility routines
NAME Disk
i_need COUTDSAV,BYTE
i_need COUTSAV,DWORD
i_need CINDSAV,BYTE
i_need CINSAV,DWORD
i_need CONSWAP,BYTE
i_need IDLEINT,BYTE
i_need THISFCB,DWORD
i_need DMAADD,DWORD
i_need DEVCALL,BYTE
i_need CALLSCNT,WORD
i_need CALLXAD,DWORD
i_need CONTPOS,WORD
i_need NEXTADD,WORD
i_need CONBUF,BYTE
i_need User_SS,WORD
i_need User_SP,WORD
i_need DSKStack,BYTE
i_need InDOS,BYTE
i_need NumIO,BYTE
i_need CurDrv,BYTE
i_need ThisDrv,BYTE
i_need ClusFac,BYTE
i_need SecClusPos,BYTE
i_need DirSec,WORD
i_need ClusNum,WORD
i_need NxtClusNum,WORD
i_need ReadOp,BYTE
i_need DskErr,BYTE
i_need RecCnt,WORD
i_need RecPos,4
i_need Trans,BYTE
i_need BytPos,4
i_need SecPos,WORD
i_need BytSecPos,WORD
i_need BytCnt1,WORD
i_need BytCnt2,WORD
i_need SecCnt,WORD
i_need ThisDPB,DWORD
i_need LastPos,WORD
i_need ValSec,WORD
i_need GrowCnt,DWORD
SUBTTL LOAD -- MAIN READ ROUTINE AND DEVICE IN ROUTINES
PAGE
; * * * * Drivers for file input from devices * * * *
procedure SWAPBACK,NEAR
ASSUME DS:DOSGROUP,ES:NOTHING
PUSH ES
PUSH DI
PUSH SI
PUSH BX
MOV BX,1
invoke get_sf_from_jfn
ADD DI,sf_fcb
MOV BL,BYTE PTR [COUTDSAV]
LDS SI,[COUTSAV]
ASSUME DS:NOTHING
MOV WORD PTR ES:[DI.fcb_FIRCLUS],SI
MOV WORD PTR ES:[DI.fcb_FIRCLUS+2],DS
MOV ES:[DI.fcb_DEVID],BL
PUSH SS
POP DS
ASSUME DS:DOSGROUP
XOR BX,BX
invoke get_sf_from_jfn
ADD DI,sf_fcb
MOV BL,BYTE PTR [CINDSAV]
LDS SI,[CINSAV]
ASSUME DS:NOTHING
MOV WORD PTR ES:[DI.fcb_FIRCLUS],SI
MOV WORD PTR ES:[DI.fcb_FIRCLUS+2],DS
MOV ES:[DI.fcb_DEVID],BL
PUSH SS
POP DS
ASSUME DS:DOSGROUP
MOV BYTE PTR [CONSWAP],0
MOV BYTE PTR [IDLEINT],1
SWAPRET:
POP BX
POP SI
POP DI
POP ES
return
SWAPBACK ENDP
procedure SWAPCON,NEAR
ASSUME DS:DOSGROUP,ES:NOTHING
PUSH ES
PUSH DI
PUSH SI
PUSH BX
MOV BYTE PTR [CONSWAP],1
MOV BYTE PTR [IDLEINT],0
XOR BX,BX
invoke get_sf_from_jfn
ADD DI,sf_fcb
MOV BL,ES:[DI.fcb_DEVID]
MOV BYTE PTR [CINDSAV],BL
LDS SI,DWORD PTR ES:[DI.fcb_FIRCLUS]
ASSUME DS:NOTHING
MOV WORD PTR [CINSAV],SI
MOV WORD PTR [CINSAV+2],DS
LDS SI,[THISFCB]
MOV BL,[SI.fcb_DEVID]
LDS SI,DWORD PTR [SI.fcb_FIRCLUS]
MOV ES:[DI.fcb_DEVID],BL
MOV WORD PTR ES:[DI.fcb_FIRCLUS],SI
MOV WORD PTR ES:[DI.fcb_FIRCLUS+2],DS
PUSH SS
POP DS
ASSUME DS:DOSGROUP
MOV BX,1
invoke get_sf_from_jfn
ADD DI,sf_fcb
MOV BL,ES:[DI.fcb_DEVID]
MOV BYTE PTR [COUTDSAV],BL
LDS SI,DWORD PTR ES:[DI.fcb_FIRCLUS]
ASSUME DS:NOTHING
MOV WORD PTR [COUTSAV],SI
MOV WORD PTR [COUTSAV+2],DS
LDS SI,[THISFCB]
MOV BL,[SI.fcb_DEVID]
LDS SI,DWORD PTR [SI.fcb_FIRCLUS]
MOV ES:[DI.fcb_DEVID],BL
MOV WORD PTR ES:[DI.fcb_FIRCLUS],SI
MOV WORD PTR ES:[DI.fcb_FIRCLUS+2],DS
PUSH SS
POP DS
JMP SWAPRET
SWAPCON ENDP
procedure LOAD,NEAR
ASSUME DS:NOTHING,ES:NOTHING
;
; Inputs:
; DS:DI point to FCB
; DX:AX = Position in file to read
; CX = No. of records to read
; Outputs:
; DX:AX = Position of last record read
; CX = No. of bytes read
; ES:DI point to FCB
; fcb_LSTCLUS, fcb_CLUSPOS fields in FCB set
call SETUP
ASSUME DS:DOSGROUP
OR BL,BL ; Check for named device I/O
JS READDEV
call DISKREAD
return
READDEV:
ASSUME DS:DOSGROUP,ES:NOTHING
LES DI,[DMAADD]
TEST BL,40H ; End of file?
JZ ENDRDDEVJ3
TEST BL,ISNULL ; NUL device?
JZ TESTRAW ; NO
XOR AL,AL ; Indicate EOF
ENDRDDEVJ3: JMP ENDRDDEVJ2
DVRDRAW:
ASSUME DS:DOSGROUP
PUSH ES
POP DS
ASSUME DS:NOTHING
DVRDRAWR:
MOV BX,DI ; DS:BX transfer addr
XOR DX,DX ; Start at 0
XOR AX,AX ; Media Byte, unit = 0
invoke SETREAD
LDS SI,[THISFCB]
invoke DEVIOCALL
MOV DX,DI ; DX is preserved by INT 24
MOV AH,86H ; Read error
MOV DI,[DEVCALL.REQSTAT]
TEST DI,STERR
JZ CRDROK ; No errors
invoke CHARHARD
MOV DI,DX
CMP AL,1
JZ DVRDRAWR ; Retry
CRDROK:
MOV DI,DX
ADD DI,[CALLSCNT] ; Amount transferred
JMP SHORT ENDRDDEVJ2
TESTRAW:
TEST BL,020H ; Raw mode?
JNZ DVRDRAW
TEST BL,ISCIN ; Is it console device?
JZ NOTRDCON
JMP READCON
NOTRDCON:
MOV AX,ES
MOV DS,AX
ASSUME DS:NOTHING
MOV BX,DI
XOR DX,DX
MOV AX,DX
PUSH CX
MOV CX,1
invoke SETREAD
POP CX
LDS SI,[THISFCB]
LDS SI,DWORD PTR [SI.fcb_FIRCLUS]
DVRDLP:
invoke DSKSTATCHK
invoke DEVIOCALL2
PUSH DI
MOV AH,86H
MOV DI,[DEVCALL.REQSTAT]
TEST DI,STERR
JZ CRDOK
invoke CHARHARD
POP DI
MOV [CALLSCNT],1
CMP AL,1
JZ DVRDLP ;Retry
XOR AL,AL ;Pick some random character
JMP SHORT DVRDIGN
CRDOK:
POP DI
CMP [CALLSCNT],1
JNZ ENDRDDEVJ2
PUSH DS
MOV DS,WORD PTR [CALLXAD+2]
MOV AL,BYTE PTR [DI]
POP DS
DVRDIGN:
INC WORD PTR [CALLXAD]
MOV [DEVCALL.REQSTAT],0
INC DI
CMP AL,1AH ; ^Z?
JZ ENDRDDEVJ
CMP AL,c_CR ; CR?
LOOPNZ DVRDLP
ENDRDDEVJ:
DEC DI
ENDRDDEVJ2:
JMP SHORT ENDRDDEV
ASSUME DS:NOTHING,ES:NOTHING
TRANBUF:
LODSB
STOSB
CMP AL,c_CR ; Check for carriage return
JNZ NORMCH
MOV BYTE PTR [SI],c_LF
NORMCH:
CMP AL,c_LF
LOOPNZ TRANBUF
JNZ ENDRDCON
XOR SI,SI ; Cause a new buffer to be read
invoke OUT ; Transmit linefeed
OR AL,1 ; Clear zero flag--not end of file
ENDRDCON:
PUSH SS
POP DS
ASSUME DS:DOSGROUP
CALL SWAPBACK
MOV [CONTPOS],SI
ENDRDDEV:
PUSH SS
POP DS
ASSUME DS:DOSGROUP
MOV [NEXTADD],DI
JNZ SETFCBC ; Zero set if Ctrl-Z found in input
LES DI,[THISFCB]
AND ES:BYTE PTR [DI.fcb_DEVID],0FFH-40H ; Mark as no more data available
SETFCBC:
call SETFCB
return
ASSUME DS:NOTHING,ES:NOTHING
READCON:
ASSUME DS:DOSGROUP
CALL SWAPCON
MOV SI,[CONTPOS]
OR SI,SI
JNZ TRANBUF
CMP BYTE PTR [CONBUF],128
JZ GETBUF
MOV WORD PTR [CONBUF],0FF80H ; Set up 128-byte buffer with no template
GETBUF:
PUSH CX
PUSH ES
PUSH DI
MOV DX,OFFSET DOSGROUP:CONBUF
invoke $STD_CON_STRING_INPUT ; Get input buffer
POP DI
POP ES
POP CX
MOV SI,2 + OFFSET DOSGROUP:CONBUF
CMP BYTE PTR [SI],1AH ; Check for Ctrl-Z in first character
JNZ TRANBUF
MOV AL,1AH
STOSB
DEC DI
MOV AL,10
invoke OUT ; Send linefeed
XOR SI,SI
JMP SHORT ENDRDCON
LOAD ENDP
SUBTTL STORE -- MAIN WRITE ROUTINE AND DEVICE OUT ROUTINES
PAGE
ASSUME DS:NOTHING,ES:NOTHING
procedure STORE,NEAR
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; DS:DI point to FCB
; DX:AX = Position in file of disk transfer
; CX = Record count
; Outputs:
; DX:AX = Position of last record written
; CX = No. of records written
; ES:DI point to FCB
; fcb_LSTCLUS, fcb_CLUSPOS fields in FCB set
call SETUP
ASSUME DS:DOSGROUP
OR BL,BL
JS WRTDEV
invoke DATE16
MOV ES:[DI.fcb_FDATE],AX
MOV ES:[DI.fcb_FTIME],DX
call DISKWRITE
return
WRITECON:
PUSH DS
PUSH SS
POP DS
ASSUME DS:DOSGROUP
CALL SWAPCON
POP DS
ASSUME DS:NOTHING
MOV SI,BX
PUSH CX
WRCONLP:
LODSB
CMP AL,1AH ; ^Z?
JZ CONEOF
invoke OUT
LOOP WRCONLP
CONEOF:
POP AX ; Count
SUB AX,CX ; Amount actually written
POP DS
ASSUME DS:DOSGROUP
CALL SWAPBACK
JMP SHORT ENDWRDEV
DVWRTRAW:
ASSUME DS:NOTHING
XOR AX,AX ; Media Byte, unit = 0
invoke SETWRITE
LDS SI,[THISFCB]
invoke DEVIOCALL
MOV DX,DI
MOV AH,87H
MOV DI,[DEVCALL.REQSTAT]
TEST DI,STERR
JZ CWRTROK
invoke CHARHARD
MOV BX,DX ; Recall transfer addr
CMP AL,1
JZ DVWRTRAW ; Try again
CWRTROK:
POP DS
ASSUME DS:DOSGROUP
MOV AX,[CALLSCNT] ; Get actual number of bytes transferred
ENDWRDEV:
LES DI,[THISFCB]
XOR DX,DX
DIV ES:[DI.fcb_RECSIZ]
MOV CX,AX ; Partial record is ignored
call ADDREC
return
ASSUME DS:DOSGROUP
WRTDEV:
OR BL,40H ; Reset EOF for input
XOR AX,AX
JCXZ ENDWRDEV ; problem of creating on a device.
PUSH DS
MOV AL,BL
LDS BX,[DMAADD]
ASSUME DS:NOTHING
MOV DI,BX
XOR DX,DX ; Set starting point
TEST AL,020H ; Raw?
JNZ DVWRTRAW
TEST AL,ISCOUT ; Console output device?
JNZ WRITECON
TEST AL,ISNULL
JNZ WRTNUL
MOV AX,DX
CMP BYTE PTR [BX],1AH ; ^Z?
JZ WRTCOOKDONE ; Yes, transfer nothing
PUSH CX
MOV CX,1
invoke SETWRITE
POP CX
LDS SI,[THISFCB]
LDS SI,DWORD PTR [SI.fcb_FIRCLUS]
DVWRTLP:
invoke DSKSTATCHK
invoke DEVIOCALL2
PUSH DI
MOV AH,87H
MOV DI,[DEVCALL.REQSTAT]
TEST DI,STERR
JZ CWROK
invoke CHARHARD
POP DI
MOV [CALLSCNT],1
CMP AL,1
JZ DVWRTLP
JMP SHORT DVWRTIGN
CWROK:
POP DI
CMP [CALLSCNT],0
JZ WRTCOOKDONE
DVWRTIGN:
INC DX
INC WORD PTR [CALLXAD]
INC DI
PUSH DS
MOV DS,WORD PTR [CALLXAD+2]
CMP BYTE PTR [DI],1AH ; ^Z?
POP DS
JZ WRTCOOKDONE
MOV [DEVCALL.REQSTAT],0
LOOP DVWRTLP
WRTCOOKDONE:
MOV AX,DX
POP DS
JMP ENDWRDEV
WRTNUL:
MOV DX,CX ;Entire transfer done
JMP WRTCOOKDONE
STORE ENDP
procedure get_io_fcb,near
ASSUME DS:NOTHING,ES:NOTHING
; Convert JFN number in BX to FCB in DS:SI
PUSH SS
POP DS
ASSUME DS:DOSGROUP
PUSH ES
PUSH DI
invoke get_sf_from_jfn
JC RET44P
MOV SI,DI
ADD SI,sf_fcb
PUSH ES
POP DS
ASSUME DS:NOTHING
RET44P:
POP DI
POP ES
return
get_io_fcb ENDP
SUBTTL GETTHISDRV -- FIND CURRENT DRIVE
PAGE
; Input: AL has drive identifier (1=A, 0=default)
; Output: AL has physical drive (0=A)
; Carry set if invalid drive (and AL is garbage anyway)
procedure GetThisDrv,NEAR
ASSUME DS:NOTHING,ES:NOTHING
CMP BYTE PTR [NUMIO],AL
retc
DEC AL
JNS PHYDRV
MOV AL,[CURDRV]
PHYDRV:
MOV BYTE PTR [THISDRV],AL
return
GetThisDrv ENDP
SUBTTL DIRREAD -- READ A DIRECTORY SECTOR
PAGE
procedure DirRead,NEAR
ASSUME DS:DOSGROUP,ES:NOTHING
; Inputs:
; AX = Directory block number (relative to first block of directory)
; ES:BP = Base of drive parameters
; [DIRSEC] = First sector of first cluster of directory
; [CLUSNUM] = Next cluster
; [CLUSFAC] = Sectors/Cluster
; Function:
; Read the directory block into [CURBUF].
; Outputs:
; [NXTCLUSNUM] = Next cluster (after the one skipped to)
; [SECCLUSPOS] Set
; ES:BP unchanged [CURBUF] Points to Buffer with dir sector
; All other registers destroyed.
MOV CL,[CLUSFAC]
DIV CL ; AL # clusters to skip, AH position in cluster
MOV [SECCLUSPOS],AH
MOV CL,AL
XOR CH,CH
MOV DX,[DIRSEC]
ADD DL,AH
ADC DH,0
MOV BX,[CLUSNUM]
MOV [NXTCLUSNUM],BX
JCXZ FIRSTCLUSTER
SKPCLLP:
invoke UNPACK
XCHG BX,DI
CMP BX,0FF8H
JAE HAVESKIPPED
LOOP SKPCLLP
HAVESKIPPED:
MOV [NXTCLUSNUM],BX
MOV DX,DI
MOV BL,AH
invoke FIGREC
entry FIRSTCLUSTER
XOR AL,AL ; Indicate pre-read
MOV AH,DIRPRI
invoke GETBUFFR
ret
DirRead ENDP
SUBTTL FATSECRD -- READ A FAT SECTOR
PAGE
procedure FATSecRd,NEAR
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; Same as DREAD
; DS:BX = Transfer address
; CX = Number of sectors
; DX = Absolute record number
; ES:BP = Base of drive parameters
; Function:
; Calls BIOS to perform FAT read.
; Outputs:
; Same as DREAD
MOV DI,CX
MOV CL,ES:[BP.dpb_FAT_count]
MOV AL,ES:[BP.dpb_FAT_size]
XOR AH,AH
MOV CH,AH
PUSH DX
NXTFAT:
PUSH CX
PUSH AX
MOV CX,DI
CALL DSKREAD
POP AX
POP CX
JZ RET41P
ADD DX,AX
LOOP NXTFAT
POP DX
MOV CX,DI
; NOTE FALL THROUGH
SUBTTL DREAD -- DO A DISK READ
PAGE
entry DREAD
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; DS:BX = Transfer address
; CX = Number of sectors
; DX = Absolute record number
; ES:BP = Base of drive parameters
; Function:
; Calls BIOS to perform disk read. If BIOS reports
; errors, will call HARDERR for further action.
; DS,ES:BP preserved. All other registers destroyed.
CALL DSKREAD
retz
MOV BYTE PTR [READOP],0
invoke HARDERR
CMP AL,1 ; Check for retry
JZ DREAD
return ; Ignore otherwise
RET41P: POP DX
return
FATSecRd ENDP
SUBTTL DSKREAD -- PHYSICAL DISK READ
PAGE
procedure DskRead,NEAR
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; DS:BX = Transfer addr
; CX = Number of sectors
; DX = Absolute record number
; ES:BP = Base of drive parameters
; Function:
; Call BIOS to perform disk read
; Outputs:
; DI = CX on entry
; CX = Number of sectors unsuccessfully transfered
; AX = Status word as returned by BIOS (error code in AL if error)
; Zero set if OK (from BIOS)
; Zero clear if error
; SI Destroyed, others preserved
PUSH CX
MOV AH,ES:[BP.dpb_media]
MOV AL,ES:[BP.dpb_UNIT]
PUSH BX
PUSH ES
invoke SETREAD
JMP DODSKOP
SUBTTL DWRITE -- SEE ABOUT WRITING
PAGE
entry DWRITE
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; DS:BX = Transfer address
; CX = Number of sectors
; DX = Absolute record number
; ES:BP = Base of drive parameters
; Function:
; Calls BIOS to perform disk write. If BIOS reports
; errors, will call HARDERR for further action.
; BP preserved. All other registers destroyed.
CALL DSKWRITE
retz
MOV BYTE PTR [READOP],1
invoke HARDERR
CMP AL,1 ; Check for retry
JZ DWRITE
return
SUBTTL DSKWRITE -- PHYSICAL DISK WRITE
PAGE
entry DSKWRITE
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; DS:BX = Transfer addr
; CX = Number of sectors
; DX = Absolute record number
; ES:BP = Base of drive parameters
; Function:
; Call BIOS to perform disk read
; Outputs:
; DI = CX on entry
; CX = Number of sectors unsuccessfully transfered
; AX = Status word as returned by BIOS (error code in AL if error)
; Zero set if OK (from BIOS)
; Zero clear if error
; SI Destroyed, others preserved
PUSH CX
MOV AH,ES:[BP.dpb_media]
MOV AL,ES:[BP.dpb_UNIT]
PUSH BX
PUSH ES
invoke SETWRITE
DODSKOP:
MOV CX,DS ; Save DS
POP DS ; DS:BP points to DPB
PUSH DS
LDS SI,DS:[BP.dpb_driver_addr]
invoke DEVIOCALL2
MOV DS,CX ; Restore DS
POP ES ; Restore ES
POP BX
MOV CX,[CALLSCNT] ; Number of sectors transferred
POP DI
SUB CX,DI
NEG CX ; Number of sectors not transferred
MOV AX,[DEVCALL.REQSTAT]
TEST AX,STERR
return
DskRead ENDP
SUBTTL SETUP -- SETUP A DISK READ OR WRITE FROM USER
PAGE
ASSUME DS:DOSGROUP,ES:NOTHING
procedure SETUP,NEAR
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; DS:DI point to FCB
; DX:AX = Record position in file of disk transfer
; CX = Record count
; Outputs:
; DS = DOSGROUP
; BL = fcb_DEVID from FCB
; CX = No. of bytes to transfer (0 = 64K)
; [THISDPB] = Base of drive parameters
; [RECCNT] = Record count
; [RECPOS] = Record position in file
; ES:DI Points to FCB
; [THISFCB] = ES:DI
; [NEXTADD] = Displacement of disk transfer within segment
; [SECPOS] = Position of first sector
; [BYTPOS] = Byte position in file
; [BYTSECPOS] = Byte position in first sector
; [CLUSNUM] = First cluster
; [SECCLUSPOS] = Sector within first cluster
; [DSKERR] = 0 (no errors yet)
; [TRANS] = 0 (No transfers yet)
; [THISDRV] = Physical drive unit number
PUSH AX
MOV AL,[DI]
DEC AL
MOV BYTE PTR [THISDRV],AL
MOV AL,[DI.fcb_DEVID]
MOV SI,[DI.fcb_RECSIZ]
OR SI,SI
JNZ HAVRECSIZ
MOV SI,128
MOV [DI.fcb_RECSIZ],SI
HAVRECSIZ:
MOV WORD PTR [THISFCB+2],DS
PUSH SS
POP DS ; Set DS to DOSGROUP
ASSUME DS:DOSGROUP
MOV WORD PTR [THISFCB],DI
OR AL,AL ; Is it a device?
JNS NOTDEVICE
XOR AL,AL ; Fake in drive 0 so we can get BP
NOTDEVICE:
invoke GETBP
POP AX
JNC CheckRecLen
XOR CX,CX
MOV BYTE PTR [DSKERR],4
POP BX
return
CheckRecLen:
CMP SI,64 ; Check if highest byte of RECPOS is significant
JB SMALREC
XOR DH,DH ; Ignore MSB if record >= 64 bytes
SMALREC:
MOV [RECCNT],CX
MOV WORD PTR [RECPOS],AX
MOV WORD PTR [RECPOS+2],DX
MOV BX,WORD PTR [DMAADD]
MOV [NEXTADD],BX
MOV BYTE PTR [DSKERR],0
MOV BYTE PTR [TRANS],0
MOV BX,DX
MUL SI
MOV WORD PTR [BYTPOS],AX
PUSH DX
MOV AX,BX
MUL SI
POP BX
ADD AX,BX
ADC DX,0 ; Ripple carry
JNZ EOFERR
MOV WORD PTR [BYTPOS+2],AX
MOV DX,AX
MOV AX,WORD PTR [BYTPOS]
MOV BX,ES:[BP.dpb_sector_size]
CMP DX,BX ; See if divide will overflow
JNC EOFERR
DIV BX
MOV [SECPOS],AX
MOV [BYTSECPOS],DX
MOV DX,AX
AND AL,ES:[BP.dpb_cluster_mask]
MOV [SECCLUSPOS],AL
MOV AX,CX ; Record count
MOV CL,ES:[BP.dpb_cluster_shift]
SHR DX,CL
MOV [CLUSNUM],DX
MUL SI ; Multiply by bytes per record
MOV CX,AX
ADD AX,WORD PTR [DMAADD] ; See if it will fit in one segment
ADC DX,0
JZ OK ; Must be less than 64K
MOV AX,WORD PTR [DMAADD]
NEG AX ; Amount of room left in segment
JNZ PARTSEG
DEC AX
PARTSEG:
XOR DX,DX
DIV SI ; How many records will fit?
MOV [RECCNT],AX
MUL SI ; Translate that back into bytes
MOV BYTE PTR [DSKERR],2 ; Flag that trimming took place
MOV CX,AX
JCXZ NOROOM
OK:
LES DI,[THISFCB]
MOV BL,ES:[DI.fcb_DEVID]
return
EOFERR:
MOV BYTE PTR [DSKERR],1
XOR CX,CX
NOROOM:
LES DI,[THISFCB]
POP BX ; Kill return address
return
SETUP ENDP
SUBTTL BREAKDOWN -- CUT A USER READ OR WRITE INTO PIECES
PAGE
procedure BREAKDOWN,near
ASSUME DS:DOSGROUP,ES:NOTHING
; Inputs:
; CX = Length of disk transfer in bytes
; ES:BP = Base of drive parameters
; [BYTSECPOS] = Byte position witin first sector
; Outputs:
; [BYTCNT1] = Bytes to transfer in first sector
; [SECCNT] = No. of whole sectors to transfer
; [BYTCNT2] = Bytes to transfer in last sector
; AX, BX, DX destroyed. No other registers affected.
MOV AX,[BYTSECPOS]
MOV BX,CX
OR AX,AX
JZ SAVFIR ; Partial first sector?
SUB AX,ES:[BP.dpb_sector_size]
NEG AX ; Max number of bytes left in first sector
SUB BX,AX ; Subtract from total length
JAE SAVFIR
ADD AX,BX ; Don't use all of the rest of the sector
XOR BX,BX ; And no bytes are left
SAVFIR:
MOV [BYTCNT1],AX
MOV AX,BX
XOR DX,DX
DIV ES:[BP.dpb_sector_size] ; How many whole sectors?
MOV [SECCNT],AX
MOV [BYTCNT2],DX ; Bytes remaining for last sector
OR DX,[BYTCNT1]
retnz ; NOT (BYTCNT1 = BYTCNT2 = 0)
CMP AX,1
retnz
MOV AX,ES:[BP.dpb_sector_size] ; Buffer EXACT one sector I/O
MOV [BYTCNT2],AX
MOV [SECCNT],DX ; DX = 0
return
BreakDown ENDP
SUBTTL DISKREAD -- PERFORM USER DISK READ
PAGE
procedure DISKREAD,NEAR
ASSUME DS:DOSGROUP,ES:NOTHING
; Inputs:
; Outputs of SETUP
; Function:
; Perform disk read
; Outputs:
; DX:AX = Position of last record read
; CX = No. of records read
; ES:DI point to FCB
; fcb_LSTCLUS, fcb_CLUSPOS fields in FCB set
MOV AX,ES:WORD PTR [DI.fcb_FILSIZ]
MOV BX,ES:WORD PTR [DI.fcb_FILSIZ+2]
SUB AX,WORD PTR [BYTPOS]
SBB BX,WORD PTR [BYTPOS+2]
JB RDERR
JNZ ENUF
OR AX,AX
JZ RDERR
CMP AX,CX
JAE ENUF
MOV CX,AX
ENUF:
LES BP,[THISDPB]
CALL BREAKDOWN
MOV CX,[CLUSNUM]
invoke FNDCLUS
OR CX,CX
JZ SHORT SKIPERR
RDERR:
JMP WRTERR
RDLASTJ:JMP RDLAST
SETFCBJ2: JMP SETFCB
SKIPERR:
MOV [LASTPOS],DX
MOV [CLUSNUM],BX
CMP [BYTCNT1],0
JZ RDMID
invoke BUFRD
RDMID:
CMP [SECCNT],0
JZ RDLASTJ
invoke NEXTSEC
JC SETFCBJ2
MOV BYTE PTR [TRANS],1 ; A transfer is taking place
ONSEC:
MOV DL,[SECCLUSPOS]
MOV CX,[SECCNT]
MOV BX,[CLUSNUM]
RDLP:
invoke OPTIMIZE
PUSH DI
PUSH AX
PUSH BX
MOV DS,WORD PTR [DMAADD+2]
ASSUME DS:NOTHING
PUSH DX
PUSH CX
CALL DREAD
POP BX
POP DX
ADD BX,DX ; Upper bound of read
MOV AL,ES:[BP.dpb_drive]
invoke SETVISIT
NXTBUF: ; Must see if one of these sectors is buffered
MOV [DI.VISIT],1 ; Mark as visited
CMP AL,[DI.BUFDRV]
JNZ DONXTBUF ; Not for this drive
CMP [DI.BUFSECNO],DX
JC DONXTBUF ; Below first sector
CMP [DI.BUFSECNO],BX
JNC DONXTBUF ; Above last sector
CMP BYTE PTR [DI.BUFDIRTY],0
JZ CLBUFF ; Buffer is clean, so OK
; A sector has been read in when a dirty copy of it is in a buffer
; The buffered sector must now be read into the right place
POP AX ; Recall transfer address
PUSH AX
PUSH DI ; Save search environment
PUSH DX
SUB DX,[DI.BUFSECNO] ; How far into transfer?
NEG DX
MOV SI,DI
MOV DI,AX
MOV AX,DX
MOV CX,ES:[BP.dpb_sector_size]
MUL CX
ADD DI,AX ; Put the buffer here
ADD SI,BUFINSIZ
SHR CX,1
PUSH ES
MOV ES,WORD PTR [DMAADD+2]
REP MOVSW
JNC EVENMOV
MOVSB
EVENMOV:
POP ES
POP DX
POP DI
MOV AL,ES:[BP.dpb_drive]
CLBUFF:
invoke SCANPLACE
DONXTBUF:
invoke SKIPVISIT
JNZ NXTBUF
PUSH SS
POP DS
ASSUME DS:DOSGROUP
POP CX
POP CX
POP BX
JCXZ RDLAST
CMP BX,0FF8H
JAE SETFCB
MOV DL,0
INC [LASTPOS] ; We'll be using next cluster
JMP RDLP
RDLAST:
MOV AX,[BYTCNT2]
OR AX,AX
JZ SETFCB
MOV [BYTCNT1],AX
invoke NEXTSEC
JC SETFCB
MOV [BYTSECPOS],0
invoke BUFRD
entry SETFCB
LES SI,[THISFCB]
MOV AX,[NEXTADD]
MOV DI,AX
SUB AX,WORD PTR [DMAADD] ; Number of bytes transfered
XOR DX,DX
MOV CX,ES:[SI.fcb_RECSIZ]
DIV CX ; Number of records
CMP AX,[RECCNT] ; Check if all records transferred
JZ FULLREC
MOV BYTE PTR [DSKERR],1
OR DX,DX
JZ FULLREC ; If remainder 0, then full record transfered
MOV BYTE PTR [DSKERR],3 ; Flag partial last record
SUB CX,DX ; Bytes left in last record
PUSH ES
MOV ES,WORD PTR [DMAADD+2]
XCHG AX,BX ; Save the record count temporarily
XOR AX,AX ; Fill with zeros
SHR CX,1
JNC EVENFIL
STOSB
EVENFIL:
REP STOSW
XCHG AX,BX ; Restore record count to AX
POP ES
INC AX ; Add last (partial) record to total
FULLREC:
MOV CX,AX
MOV DI,SI ; ES:DI point to FCB
SETCLUS:
TEST ES:[DI].fcb_DEVID,-1
JS ADDREC ; don't set clisters if device
MOV AX,[CLUSNUM]
AND ES:[DI.fcb_LSTCLUS],0F000h ; fcb_lstclus is packed with dir clus
OR ES:[DI.fcb_LSTCLUS],AX ; drop in the correct part of fcb_lstclus
MOV AX,[LASTPOS]
MOV ES:[DI.fcb_CLUSPOS],AX
entry AddRec
MOV AX,WORD PTR [RECPOS]
MOV DX,WORD PTR [RECPOS+2]
JCXZ RET28 ; If no records read, don't change position
DEC CX
ADD AX,CX ; Update current record position
ADC DX,0
INC CX
RET28: return
DISKREAD ENDP
SUBTTL DISKWRITE -- PERFORM USER DISK WRITE
PAGE
procedure DISKWRITE,NEAR
ASSUME DS:DOSGROUP,ES:NOTHING
; Inputs:
; Outputs of SETUP
; Function:
; Perform disk write
; Outputs:
; DX:AX = Position of last record written
; CX = No. of records written
; ES:DI point to FCB
; fcb_LSTCLUS, fcb_CLUSPOS fields in FCB set
AND BL,3FH ; Mark file as dirty
MOV ES:[DI.fcb_DEVID],BL
LES BP,[THISDPB]
CALL BREAKDOWN
MOV AX,WORD PTR [BYTPOS]
MOV DX,WORD PTR [BYTPOS+2]
JCXZ WRTEOFJ
ADD AX,CX
ADC DX,0 ; AX:DX=last byte accessed
DIV ES:[BP.dpb_sector_size] ; AX=last sector accessed
MOV BX,AX ; Save last full sector
OR DX,DX
JNZ CALCLUS
DEC AX ; AX must be zero base indexed
CALCLUS:
MOV CL,ES:[BP.dpb_cluster_shift]
SHR AX,CL ; Last cluster to be accessed
PUSH AX
PUSH DX ; Save the size of the "tail"
PUSH ES
LES DI,[THISFCB]
MOV AX,ES:WORD PTR [DI.fcb_FILSIZ]
MOV DX,ES:WORD PTR [DI.fcb_FILSIZ+2]
POP ES
DIV ES:[BP.dpb_sector_size]
MOV CX,AX ; Save last full sector of current file
OR DX,DX
JZ NORNDUP
INC AX ; Round up if any remainder
NORNDUP:
MOV [VALSEC],AX ; Number of sectors that have been written
XOR AX,AX
MOV WORD PTR [GROWCNT],AX
MOV WORD PTR [GROWCNT+2],AX
POP AX
SUB BX,CX ; Number of full sectors
JB NOGROW
JZ TESTTAIL
MOV CX,DX
XCHG AX,BX
MUL ES:[BP.dpb_sector_size] ; Bytes of full sector growth
SUB AX,CX ; Take off current "tail"
SBB DX,0 ; 32-bit extension
ADD AX,BX ; Add on new "tail"
ADC DX,0 ; ripple tim's head off
JMP SHORT SETGRW
HAVSTART:
MOV CX,AX
invoke SKPCLP
JCXZ DOWRTJ
invoke ALLOCATE
JNC DOWRTJ
WRTERR:
XOR CX,CX
MOV BYTE PTR [DSKERR],1
MOV AX,WORD PTR [RECPOS]
MOV DX,WORD PTR [RECPOS+2]
LES DI,[THISFCB]
return
DOWRTJ: JMP DOWRT
WRTEOFJ:
JMP WRTEOF
TESTTAIL:
SUB AX,DX
JBE NOGROW
XOR DX,DX
SETGRW:
MOV WORD PTR [GROWCNT],AX
MOV WORD PTR [GROWCNT+2],DX
NOGROW:
POP AX
MOV CX,[CLUSNUM] ; First cluster accessed
invoke FNDCLUS
MOV [CLUSNUM],BX
MOV [LASTPOS],DX
SUB AX,DX ; Last cluster minus current cluster
JZ DOWRT ; If we have last clus, we must have first
JCXZ HAVSTART ; See if no more data
PUSH CX ; No. of clusters short of first
MOV CX,AX
invoke ALLOCATE
POP AX
JC WRTERR
MOV CX,AX
MOV DX,[LASTPOS]
INC DX
DEC CX
JZ NOSKIP
invoke SKPCLP
NOSKIP:
MOV [CLUSNUM],BX
MOV [LASTPOS],DX
DOWRT:
CMP [BYTCNT1],0
JZ WRTMID
MOV BX,[CLUSNUM]
invoke BUFWRT
WRTMID:
MOV AX,[SECCNT]
OR AX,AX
JZ WRTLAST
ADD [SECPOS],AX
invoke NEXTSEC
MOV BYTE PTR [TRANS],1 ; A transfer is taking place
MOV DL,[SECCLUSPOS]
MOV BX,[CLUSNUM]
MOV CX,[SECCNT]
WRTLP:
invoke OPTIMIZE
PUSH DI
PUSH AX
PUSH DX
PUSH BX
MOV AL,ES:[BP.dpb_drive]
MOV BX,CX
ADD BX,DX ; Upper bound of write
invoke SETVISIT
ASSUME DS:NOTHING
NEXTBUFF: ; Search for buffers
MOV [DI.VISIT],1 ; Mark as visited
CMP AL,[DI.BUFDRV]
JNZ DONEXTBUFF ; Not for this drive
CMP [DI.BUFSECNO],DX
JC DONEXTBUFF ; Buffer is not in range of write
CMP [DI.BUFSECNO],BX
JNC DONEXTBUFF ; Buffer is not in range of write
MOV WORD PTR [DI.BUFDRV],00FFH ; Free the buffer, it is being over written
invoke SCANPLACE
DONEXTBUFF:
invoke SKIPVISIT
JNZ NEXTBUFF
POP BX
POP DX
MOV DS,WORD PTR [DMAADD+2]
CALL DWRITE
POP CX
POP BX
PUSH SS
POP DS
ASSUME DS:DOSGROUP
JCXZ WRTLAST
MOV DL,0
INC [LASTPOS] ; We'll be using next cluster
JMP SHORT WRTLP
WRTERRJ: JMP WRTERR
WRTLAST:
MOV AX,[BYTCNT2]
OR AX,AX
JZ FINWRT
MOV [BYTCNT1],AX
invoke NEXTSEC
MOV [BYTSECPOS],0
invoke BUFWRT
FINWRT:
LES DI,[THISFCB]
MOV AX,WORD PTR [GROWCNT]
MOV CX,WORD PTR [GROWCNT+2]
OR AX,AX
JNZ UPDATE_size
OR CX,CX
JZ SAMSIZ
Update_size:
ADD WORD PTR ES:[DI.fcb_FILSIZ],AX
ADC WORD PTR ES:[DI.fcb_FILSIZ+2],CX
SAMSIZ:
MOV CX,[RECCNT]
JMP SETCLUS
WRTEOF:
MOV CX,AX
OR CX,DX
JZ KILLFIL
SUB AX,1
SBB DX,0
DIV ES:[BP.dpb_sector_size]
MOV CL,ES:[BP.dpb_cluster_shift]
SHR AX,CL
MOV CX,AX
invoke FNDCLUS
JCXZ RELFILE
invoke ALLOCATE
JC WRTERRJ
UPDATE:
LES DI,[THISFCB]
MOV AX,WORD PTR [BYTPOS]
MOV ES:WORD PTR [DI.fcb_FILSIZ],AX
MOV AX,WORD PTR [BYTPOS+2]
MOV ES:WORD PTR [DI.fcb_FILSIZ+2],AX
XOR CX,CX
JMP ADDREC
RELFILE:
MOV DX,0FFFH
invoke RELBLKS
JMP SHORT UPDATE
KILLFIL:
XOR BX,BX
PUSH ES
LES DI,[THISFCB]
MOV ES:[DI.fcb_CLUSPOS],BX
XCHG BX,ES:[DI.fcb_FIRCLUS]
AND ES:[DI.fcb_LSTCLUS],0F000H
POP ES
OR BX,BX
JZ UPDATE
invoke RELEASE
JMP SHORT UPDATE
DISKWRITE ENDP
do_ext
CODE ENDS
END