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

1302 lines
37 KiB
NASM
Raw Permalink Normal View History

1983-08-13 01:53:34 +01:00
;
; 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