MS-DOS/v2.0/source/BUF.ASM
2018-09-21 17:53:34 -07:00

509 lines
14 KiB
NASM

;
; buffer management 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
i_need BuffHead,DWORD
i_need PreRead,WORD
i_need LastBuffer,DWORD
i_need CurBuf,DWORD
i_need WPErr,BYTE
SUBTTL SETVISIT,SKIPVISIT -- MANAGE BUFFER SCANS
PAGE
procedure SETVISIT,near
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; None
; Function:
; Set up a scan of I/O buffers
; Outputs:
; All visit flags = 0
; NOTE: This pre-scan is needed because a hard disk error
; may cause a scan to stop in the middle leaving some
; visit flags set, and some not set.
; DS:DI Points to [BUFFHEAD]
; No other registers altered
LDS DI,[BUFFHEAD]
PUSH AX
XOR AX,AX
SETLOOP:
MOV [DI.VISIT],AL
LDS DI,[DI.NEXTBUF]
CMP DI,-1
JNZ SETLOOP
LDS DI,[BUFFHEAD]
POP AX
return
entry SKIPVISIT
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; DS:DI Points to a buffer
; Function:
; Skip visited buffers
; Outputs:
; DS:DI Points to next unvisited buffer
; Zero is set if skip to LAST buffer
; No other registers altered
CMP DI,-1
retz
CMP [DI.VISIT],1
retnz
LDS DI,[DI.NEXTBUF]
JMP SHORT SKIPVISIT
return
SetVisit ENDP
SUBTTL SCANPLACE, PLACEBUF -- PUT A BUFFER BACK IN THE POOL
PAGE
procedure ScanPlace,near
ASSUME DS:NOTHING,ES:NOTHING
; Inputs:
; Same as PLACEBUF
; Function:
; Save scan location and call PLACEBUF
; Outputs:
; DS:DI Points to saved scan location
; SI destroyed, other registers unchanged
PUSH ES
LES SI,[DI.NEXTBUF] ; Save scan location
CALL PLACEBUF
PUSH ES
POP DS ; Restore scan location
MOV DI,SI
POP ES
return
ScanPlace ENDP
NRETJ: JMP SHORT NRET
procedure PLACEBUF,NEAR
ASSUME DS:NOTHING,ES:NOTHING
; Input:
; DS:DI points to buffer
; Function:
; Remove buffer from queue and re-insert it in proper place.
; If buffer doesn't go at end, and isn't free, decrement
; priorities.
; NO registers altered
;
; DS:SI -- Curbuf, current buffer in list
; ES:DI -- Buf, buffer passed as argument
; BP:CX -- Pointsave, saved Buf.nextbuf
; DX:BX -- Lastbuf, previous buffer in list
; AL -- Inserted, Buf has been inserted
; AH -- Removed, Buf has been removed
IF IBM
IF NOT IBM
invoke save_world
XOR AX,AX ; Inserted = Removed = FALSE
LES CX,[DI.NEXTBUF]
MOV BP,ES ; Pointsave = Buf.nextbuf
MOV SI,DS
MOV ES,SI ; Buf is ES:DI
LDS SI,[BUFFHEAD] ; Curbuf = HEAD
CALL POINTCOMP ; Buf == HEAD?
JNZ TNEWHEAD
CMP CX,-1 ; Buf is LAST?
JZ NRETJ ; Only one buffer, nothing to do
MOV WORD PTR [BUFFHEAD],CX
MOV WORD PTR [BUFFHEAD+2],BP ; HEAD = Pointsave
INC AH ; Removed = TRUE
MOV DS,BP
MOV SI,CX ; Curbuf = HEAD
TNEWHEAD:
MOV BL,ES:[DI.BUFPRI]
CMP BL,[SI.BUFPRI]
JGE BUFLOOP
NEWHEAD: ; If Buf.pri < HEAD.pri
MOV WORD PTR ES:[DI.NEXTBUF],SI
MOV WORD PTR ES:[DI.NEXTBUF+2],DS ; Buf.nextbuf = HEAD
MOV WORD PTR [BUFFHEAD],DI
MOV WORD PTR [BUFFHEAD+2],ES ; HEAD = Buf
INC AL ; Inserted = TRUE
OR AH,AH
JNZ NRET ; If Removed == TRUE
BUFLOOP:
PUSH DS
PUSH SI
LDS SI,[SI.NEXTBUF]
CALL POINTCOMP
POP SI
POP DS
JNZ TESTINS
MOV WORD PTR [SI.NEXTBUF],CX ; If Curbuf.nextbuf == buf
MOV WORD PTR [SI.NEXTBUF+2],BP ; Curbuf.nextbuf = Pointsave
INC AH ; Removed = TRUE
OR AL,AL
JNZ SHUFFLE ; If Inserted == TRUE
TESTINS:
OR AL,AL
JNZ LOOKBUF
PUSH CX ; If NOT Inserted
MOV CL,ES:[DI.BUFPRI]
CMP CL,[SI.BUFPRI]
POP CX
JGE LOOKBUF
PUSH DS ; If Buf.pri < Curbuf.pri
MOV DS,DX
MOV WORD PTR [BX.NEXTBUF],DI
MOV WORD PTR [BX.NEXTBUF+2],ES ; Lastbuf.nextbuf = Buf
POP DS
MOV WORD PTR ES:[DI.NEXTBUF],SI
MOV WORD PTR ES:[DI.NEXTBUF+2],DS ; Buf.nextbuf = Curbuf
INC AL ; Inserted = TRUE
OR AH,AH
JNZ SHUFFLE ; If Removed == TRUE
LOOKBUF:
MOV BX,SI
MOV DX,DS ; Lastbuf = Curbuf
CMP WORD PTR [SI.NEXTBUF],-1
JZ ISLAST
LDS SI,[SI.NEXTBUF] ; Curbuf = Curbuf.nextbuf
JMP SHORT BUFLOOP
ISLAST: ; If Curbuf is LAST
MOV WORD PTR [SI.NEXTBUF],DI
MOV WORD PTR [SI.NEXTBUF+2],ES ; Curbuf.nextbuf = Buf
MOV WORD PTR ES:[DI.NEXTBUF],-1
MOV WORD PTR ES:[DI.NEXTBUF+2],-1 ; Buf is LAST
NRET:
invoke restore_world
return
SHUFFLE:
LDS DI,[BUFFHEAD]
DECLOOP:
CMP [DI.BUFPRI],FREEPRI
JZ NODEC
DEC [DI.BUFPRI]
NODEC:
LDS DI,[DI.NEXTBUF]
CMP DI,-1
JNZ DECLOOP
JMP SHORT NRET
ENDIF
ENDIF
invoke save_world
LES CX,[DI.NEXTBUF]
CMP CX,-1 ; Buf is LAST?
JZ NRET ; Buffer already last
MOV BP,ES ; Pointsave = Buf.nextbuf
PUSH DS
POP ES ; Buf is ES:DI
LDS SI,[BUFFHEAD] ; Curbuf = HEAD
CALL POINTCOMP ; Buf == HEAD?
JNZ BUFLOOP
MOV WORD PTR [BUFFHEAD],CX
MOV WORD PTR [BUFFHEAD+2],BP ; HEAD = Pointsave
JMP SHORT LOOKEND
BUFLOOP:
PUSH DS
PUSH SI
LDS SI,[SI.NEXTBUF]
CALL POINTCOMP
JZ GOTTHEBUF
POP AX
POP AX
JMP SHORT BUFLOOP
GOTTHEBUF:
POP SI
POP DS
MOV WORD PTR [SI.NEXTBUF],CX ; If Curbuf.nextbuf == buf
MOV WORD PTR [SI.NEXTBUF+2],BP ; Curbuf.nextbuf = Pointsave
LOOKEND:
PUSH DS
PUSH SI
LDS SI,[SI.NEXTBUF]
CMP SI,-1
JZ GOTHEEND
POP AX
POP AX
JMP SHORT LOOKEND
GOTHEEND:
POP SI
POP DS
MOV WORD PTR [SI.NEXTBUF],DI
MOV WORD PTR [SI.NEXTBUF+2],ES ; Curbuf.nextbuf = Buf
MOV WORD PTR ES:[DI.NEXTBUF],-1
MOV WORD PTR ES:[DI.NEXTBUF+2],-1 ; Buf is LAST
NRET:
invoke restore_world
return
PLACEBUF ENDP
procedure PLACEHEAD,NEAR
ASSUME DS:NOTHING,ES:NOTHING
; SAME AS PLACEBUF except places buffer at head
invoke save_world
PUSH DS
POP ES
LDS SI,[BUFFHEAD]
MOV WORD PTR [BUFFHEAD],DI
MOV WORD PTR [BUFFHEAD+2],ES
MOV WORD PTR ES:[DI.NEXTBUF],SI
MOV WORD PTR ES:[DI.NEXTBUF+2],DS
LOOKEND2:
PUSH DS
PUSH SI
LDS SI,[SI.NEXTBUF]
CALL POINTCOMP
JZ GOTHEEND2
POP AX
POP AX
JMP SHORT LOOKEND2
GOTHEEND2:
POP SI
POP DS
MOV WORD PTR [SI.NEXTBUF],-1
MOV WORD PTR [SI.NEXTBUF+2],-1 ; Buf is LAST
JMP SHORT NRET
PLACEHEAD ENDP
SUBTTL POINTCOMP -- 20 BIT POINTER COMPARE
PAGE
procedure PointComp,NEAR
ASSUME DS:NOTHING,ES:NOTHING
; Compare DS:SI to ES:DI (or DS:DI to ES:SI) for equality
; DO NOT USE FOR < or >
; No Registers altered
CMP SI,DI
retnz
PUSH CX
PUSH DX
MOV CX,DS
MOV DX,ES
CMP CX,DX
POP DX
POP CX
return
PointComp ENDP
SUBTTL GETBUFFR -- GET A SECTOR INTO A BUFFER
PAGE
procedure GETBUFFR,NEAR
ASSUME DS:DOSGROUP,ES:NOTHING
; Input:
; AH = Priority buffer is to have
; AL = 0 means sector must be pre-read
; ELSE no pre-read
; DX = Desired physical sector number
; ES:BP = Pointer to drive parameters
; Function:
; Get the specified sector into one of the I/O buffers
; And shuffle the queue
; Output:
; [CURBUF] Points to the Buffer for the sector
; DX,ES:BP unchanged, all other registers destroyed
XOR SI,SI
entry GETBUFFRB
MOV [PREREAD],AX
MOV AL,ES:[BP.dpb_drive]
LDS DI,[LASTBUFFER]
ASSUME DS:NOTHING
CMP DI,-1 ; Recency pointer valid?
JZ SKBUF ; No
CMP DX,[DI.BUFSECNO]
JNZ SKBUF ; Wrong sector
CMP AL,[DI.BUFDRV]
JNZ SKBUF ; Wrong Drive
JMP SHORT JUSTBUF ; Just asked for same buffer
SKBUF:
LDS DI,[BUFFHEAD]
NXTBFF:
CMP DX,[DI.BUFSECNO]
JNZ BUMP
CMP AL,[DI.BUFDRV]
JNZ BUMP
JMP SHORT SETINF
BUMP:
LDS DI,[DI.NEXTBUF]
CMP DI,-1
JNZ NXTBFF
LDS DI,[BUFFHEAD]
PUSH SI
PUSH DX
PUSH BP
PUSH ES
CALL BUFWRITE ; Write out the dirty buffer
POP ES
POP BP
POP DX
POP SI
RDSEC: ; Read in the new sector
TEST BYTE PTR [PREREAD],-1
JNZ SETBUF
LEA BX,[DI.BufInSiz] ; Point at buffer
MOV CX,1
PUSH SI
PUSH DI
PUSH DX
OR SI,SI
JZ NORMSEC
invoke FATSECRD
JMP SHORT GOTTHESEC ; Buffer is marked free if read barfs
NORMSEC:
invoke DREAD ; Buffer is marked free if read barfs
GOTTHESEC:
POP DX
POP DI
POP SI
SETBUF:
MOV [DI.BUFSECNO],DX
MOV WORD PTR [DI.BUFDRVDP],BP
MOV WORD PTR [DI.BUFDRVDP+2],ES
XOR AH,AH
MOV AL,ES:[BP.dpb_drive]
MOV WORD PTR [DI.BUFDRV],AX
SETINF:
MOV AX,1 ; Default to not a FAT sector
OR SI,SI
JZ SETSTUFFOK
MOV AL,ES:[BP.dpb_FAT_count]
MOV AH,ES:[BP.dpb_FAT_size]
SETSTUFFOK:
MOV WORD PTR [DI.BUFWRTCNT],AX
CALL PLACEBUF
JUSTBUF:
MOV WORD PTR [CURBUF+2],DS
MOV WORD PTR [LASTBUFFER+2],DS
PUSH SS
POP DS
ASSUME DS:DOSGROUP
MOV WORD PTR [CURBUF],DI
MOV WORD PTR [LASTBUFFER],DI
return
GETBUFFR ENDP
SUBTTL FLUSHBUF -- WRITE OUT DIRTY BUFFERS
PAGE
procedure FlushBuf,NEAR
ASSUME DS:DOSGROUP,ES:NOTHING
; Input:
; DS = DOSGROUP
; AL = Physical unit number
; = -1 for all units
; Function:
; Write out all dirty buffers for unit, and flag them as clean
; DS Preserved, all others destroyed (ES too)
LDS DI,[BUFFHEAD]
ASSUME DS:NOTHING
MOV AH,-1
NXTBUFF:
CMP [DI.BUFDRV],AH
JZ SKIPBFF ; Skip free buffers
CMP AH,AL
JZ DOBUFFER ; Do all dirty buffers
CMP AL,[DI.BUFDRV]
JNZ SKIPBFF ; Buffer not for this unit
DOBUFFER:
CMP BYTE PTR [DI.BUFDIRTY],0
JZ SKIPBFF ; Buffer not dirty
PUSH AX
PUSH WORD PTR [DI.BUFDRV]
CALL BUFWRITE
POP AX
XOR AH,AH ; Buffer is clean
CMP AL,BYTE PTR [WPERR]
JNZ NOZAP
MOV AL,0FFH ; Invalidate buffer, it is inconsistent
NOZAP:
MOV WORD PTR [DI.BUFDRV],AX
POP AX ; Search info
SKIPBFF:
LDS DI,[DI.NEXTBUF]
CMP DI,-1
JNZ NXTBUFF
PUSH SS
POP DS
return
FlushBuf ENDP
SUBTTL BUFWRITE -- WRITE OUT A BUFFER IF DIRTY
PAGE
procedure BufWrite,NEAR
ASSUME DS:NOTHING,ES:NOTHING
; Input:
; DS:DI Points to the buffer
; Function:
; Write out all the buffer if dirty.
; Output:
; Buffer marked free
; DS:DI Preserved, ALL others destroyed (ES too)
MOV AX,00FFH
XCHG AX,WORD PTR [DI.BUFDRV] ; Free, in case write barfs
CMP AL,0FFH
retz ; Buffer is free.
OR AH,AH
retz ; Buffer is clean.
CMP AL,BYTE PTR [WPERR]
retz ; If in WP error zap buffer
LES BP,[DI.BUFDRVDP]
LEA BX,[DI.BufInSiz] ; Point at buffer
MOV DX,[DI.BUFSECNO]
MOV CX,WORD PTR [DI.BUFWRTCNT]
MOV AL,CH ; [DI.BUFWRTINC]
XOR CH,CH
MOV AH,CH
PUSH DI
WRTAGAIN:
PUSH CX
PUSH AX
MOV CX,1
PUSH BX
PUSH DX
invoke DWRITE ; Write out the dirty buffer
POP DX
POP BX
POP AX
POP CX
ADD DX,AX
LOOP WRTAGAIN
POP DI
return
BufWrite ENDP
do_ext
CODE ENDS
END