; ; 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