; ============================================================================
;
; LT-DOS - Int 21h buffers
;
; ============================================================================
; ------------- Disk Buffer, BUF
struc BUF
BUF_Next: resd 1 ; 0: pointer to next disk buffer
; offset 0ffffh = last
BUF_DiskFlags:
BUF_Disk: resb 1 ; 4: drive (0=A:, ..., 0ffh=unused)
BUF_Flags: resb 1 ; 5: flags (see Buffer flags)
BUF_Sector:
BUF_SectorH: resw 1 ; 6: logical sector number HIGH
BUF_SectorL: resw 1 ; 8: logical sector number LOW
BUF_CopyOffset:
BUF_Copies: resb 1 ; 0Ah: number of copies to write
; (1 for non-FAT sectors)
BUF_Offset: resb 1 ; 0Bh: sector offset between copies
BUF_DDPB: resd 1 ; 0Ch: pointer to DDPB (drive)
BUF_Data: ; 10h: buffered data
endstruc
BUF_SIZE EQU BUF_size ; 10h = 16 bytes
; ------------- Buffer flags
BUFFLAG_MODI EQU B0 ; buffer is modified
;BUFFLAG_MARK EQU B1 ; buffer is marked as processed
BUFFLAG_DATA EQU B2 ; buffer contains data
; ----------------------------------------------------------------------------
; Mark all disk buffers as free
; ----------------------------------------------------------------------------
; OUTPUT: DS:SI = first disk buffer
; ----------------------------------------------------------------------------
; ------------- First disk buffer
MarkFreeAll: lds si,[cs:FirstDiskBuff] ; DS:SI <- first disk buffer
push si ; push SI
push ds ; push DS
; ------------- Mark disk buffer as free
MarkFreeAll2: and byte [si+BUF_Flags],~BUFFLAG_DATA ; mark buffer as free
; ------------- Next disk buffer
lds si,[si+BUF_Next]; DS:SI <- next disk buffer
cmp si,byte -1 ; check if it is was buffer
jne MarkFreeAll2 ; next buffer
; ------------- Pop address of first disk buffer
pop ds ; pop DS
pop si ; pop SI
ret
; ----------------------------------------------------------------------------
; Find next free buffer (without data)
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk buffer (-1=invalid)
; OUTPUT: ZY = there is no next free buffer
; NOTES: - Entry point is FindFreeBuf
; ----------------------------------------------------------------------------
; ------------- Check if it is unmarked buffer
FindFreeBuf2: test byte [si+BUF_Flags],BUFFLAG_DATA ; contains any data?
jz FindFreeBuf4 ; this buffer does not contain data
; ------------- Next disk buffer
lds si,[si+BUF_Next]; DS:SI <- next disk buffer
; ------------- Check if it was last buffer
FindFreeBuf: cmp si,byte -1 ; was it last buffer?
jne FindFreeBuf2 ; it was not last buffer
FindFreeBuf4: ret
; ----------------------------------------------------------------------------
; Shift disk buffer to end of buffer chain
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk buffer
; ----------------------------------------------------------------------------
; ------------- Push registers
ShiftEnd: call PushAll ; push all registers
; ------------- Check if shifted buffer is already last in chain
mov ax,[si+BUF_Next] ; offset of next disk buffer
inc ax ; is it last disk buffer (=-1)?
je ShiftStart8 ; it is already last disk buffer
dec ax ; return offset of next disk buffer
; ------------- Check if shifted buffer is first disk buffer
les di,[cs:FirstDiskBuff] ; ES:DI <- first disk buffer
call CompAddr ; is it first disk buffer?
jne ShiftEnd2 ; it is not first disk buffer
; ------------- Shifted buffer is first, set next buffer as first disk buffer
les di,[si+BUF_Next] ; ES:DI <- next disk buffer
mov [cs:FirstDiskBuff],di ; set offset of disk buffer
mov [cs:FirstDiskBuff+2],es ; set segment of disk buffer
jmp short ShiftEnd5 ; move disk buffer to the end of chain
; ------------- Find previous disk buffer
ShiftEnd2: push di ; push DI (buffer offset)
push es ; push ES (buffer segment)
; ------------- Address of next disk buffer (-> ES:DI)
les di,[es:di+BUF_Next] ; ES:DI <- next disk buffer
; ------------- Check if it is shifted buffer
call CompAddr ; is it shifted buffer?
je ShiftEnd4 ; it is shifted buffer
; ------------- Destroy disk buffer address in stack
pop bx ; destroy ES
pop bx ; destroy DI
jmp short ShiftEnd2 ; next disk buffer
; ------------- Return address of previous buffer (-> ES:DI)
ShiftEnd4: pop es ; pop ES
pop di ; pop DI
; ------------- Skip shifted buffer (here is AX = offset of next disk buffer)
mov [es:di+BUF_Next],ax ; set offset of next buffer
mov ax,[si+BUF_Next+2] ; segment of next disk buffer
mov [es:di+BUF_Next+2],ax ; set segment of next buffer
; ------------- Find last disk buffer
ShiftEnd5: cmp word [es:di+BUF_Next],byte -1 ; is it last disk buffer?
je ShiftEnd6 ; it is last disk buffer
les di,[es:di+BUF_Next] ; ES:DI <- next disk buffer
jmp short ShiftEnd5
; ------------- Set shifted buffer as last
ShiftEnd6: mov [es:di+BUF_Next],si ; link to shifted buffer - offset
mov [es:di+BUF_Next+2],ds ; link to shifted buffer - segment
jmp short ShiftStart6
; ----------------------------------------------------------------------------
; Shift last disk buffer to start of buffer chain
; ----------------------------------------------------------------------------
; INPUT: DS:SI = last disk buffer
; ----------------------------------------------------------------------------
; ------------- Push registers
ShiftStart: call PushAll ; push all registers
; ------------- First disk buffer (-> ES:DI)
les di,[cs:FirstDiskBuff] ; ES:DI <- first disk buffer
; ------------- Set shifted buffer DS:SI as first disk buffer
mov [cs:FirstDiskBuff],si ; set offset of buffer
mov [cs:FirstDiskBuff+2],ds ; set segment of buffer
; ------------- Link first disk buffer to shifted buffer
mov [si+BUF_Next],di ; offset of first buffer
mov [si+BUF_Next+2],es ; segment of first buffer
; ------------- Push disk buffer address
ShiftStart2: push di ; push DI (buffer offset)
push es ; push ES (buffer segment)
; ------------- Address of next disk buffer (-> ES:DI)
les di,[es:di+BUF_Next] ; ES:DI <- next disk buffer
; ------------- Check if it is shifted buffer
call CompAddr ; is it shifted buffer?
je ShiftStart4 ; it is shifted buffer
; ------------- Destroy disk buffer address in stack
pop ax ; destroy ES
pop ax ; destroy DI
jmp short ShiftStart2 ; next disk buffer
; ------------- Return address of buffer (-> DS:SI)
ShiftStart4: pop ds ; pop DS
pop si ; pop SI
; ------------- Mark buffer as end of chain
ShiftStart6: mov word [si+BUF_Next],-1 ; set end mark to offset
; ------------- Pop registers
ShiftStart8: call PopAll ; pop all registers
ret
; ----------------------------------------------------------------------------
; Shift disk buffer to end of buffer chain and get next buffer
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk buffer
; OUTPUT: DS:SI = next disk buffer in chain
; ----------------------------------------------------------------------------
ShiftNext: push word [si+BUF_Next] ; push offset of next buffer
push word [si+BUF_Next+2] ; push segment of next buffer
call ShiftEnd ; shift buffer to end of chain
pop ds ; pop DS (segment of buffer)
pop si ; pop SI (offset of buffer
ret
; ----------------------------------------------------------------------------
; Compare addresses
; ----------------------------------------------------------------------------
; INPUT: DS:SI = first address
; ES:DI = second address
; OUTPUT: ZY = addresses are equal
; ----------------------------------------------------------------------------
; ------------- Compare offsets
CompAddr: cmp si,di ; are offsets equal?
jne CompAddr2 ; offsets are not equal
; ------------- Push registers
push ax ; push AX
push bx ; push BX
; ------------- Compare segments
mov ax,ds ; AX <- DS
mov bx,es ; BX <- ES
cmp ax,bx ; compare segments
; ------------- Pop registers
pop bx ; pop BX
pop ax ; pop AX
CompAddr2: ret
; ----------------------------------------------------------------------------
; Flush all disk buffers
; ----------------------------------------------------------------------------
; INPUT: AL = disk (0ffh = all disks)
; ----------------------------------------------------------------------------
; ------------- Push registers
FlushAll: push bx ; push BX
push si ; push SI
push ds ; push DS
; ------------- First disk buffer (-> DS:SI)
lds si,[cs:FirstDiskBuff] ; DS:SI <- first disk buffer
; ------------- Check if this buffer is modified
FlushAll2: mov bx,[si+BUF_DiskFlags] ; BL <- disk, BH <- flags
test bh,BUFFLAG_MODI ; is this buffer modified?
jz FlushAll6 ; this buffer is not modified
; ------------- Check if it is correct disk
cmp bl,0ffh ; is it valid disk?
je FlushAll6 ; it is not valid disk
cmp al,0ffh ; flush all disks?
je FlushAll4 ; flush all disks
cmp al,bl ; is it required disk?
jne FlushAll6 ; it is not required disk
; ------------- Flush buffer
FlushAll4: call FlushBuff ; flush buffer
; ------------- Return disk (to reuse buffer)
cmp bl,[cs:LastDiskError] ; is it last disk with error?
je FlushAll6 ; it is last disk with error
mov [si+BUF_Disk],bl ; return disk to reuse data
; ------------- Next disk buffer (-> DS:SI)
FlushAll6: lds si,[si+BUF_Next] ; DS:SI <- next disk buffer
cmp si,byte -1 ; is it last disk buffer?
jne FlushAll2 ; there is no next disk buffer
; ------------- Pop registers
pop ds ; pop DS
pop si ; pop SI
pop bx ; pop BX
ret
; ----------------------------------------------------------------------------
; Flush disk buffer
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk buffer
; ----------------------------------------------------------------------------
; ------------- Push registers
FlushBuff: call PushAll ; push all registers
; ------------- Check if buffer is used
mov ax,[si+BUF_DiskFlags] ; AL <- disk, AH <- flags
cmp al,0ffh ; is this buffer used?
je FlushBuff8 ; this buffer is not used
mov byte [si+BUF_Disk],0ffh ; buffer si unused
; ------------- Check if buffer is modified
test ah,BUFFLAG_MODI ; is this buffer modified?
jz FlushBuff8 ; buffer is not modified
and byte [si+BUF_Flags],~BUFFLAG_MODI ; clear modified flag
; ------------- Check if it is last disk with error
cmp al,[cs:LastDiskError] ; is it last disk with error?
je FlushBuff8 ; it is last disk with error
; ------------- Prepare parameters
les bp,[si+BUF_DDPB] ; ES:BP <- pointer to DDPB
lea bx,[si+BUF_Data] ; BX <- data buffer
mov dx,[si+BUF_SectorL] ; DX <- sector LOW
mov di,[si+BUF_SectorH] ; DI <- sector HIGH
xor cx,cx ; CX <- 0
mov cl,[si+BUF_Copies] ; CX <- number of copies
; ------------- Write sector
FlushBuff4: push cx ; push CX
mov cl,1 ; write 1 sector
call DOS24Write ; write sector to disk with Int 24h
pop cx ; pop CX
; ------------- Next sector
xor ax,ax ; AX <- 0
mov al,[si+BUF_Offset] ; AX <- offset of copies
add dx,ax ; shift sector number LOW
adc di,byte 0 ; shift sector number HIGH
loop FlushBuff4 ; write next sector
; ------------- Pop registers
FlushBuff8: call PopAll ; pop all registers
ret
; ----------------------------------------------------------------------------
; Test one buffer
; ----------------------------------------------------------------------------
; INPUT: AL = disk number
; DI:DX = sector number
; ES:BP = DOS Drive Parameter Block (DDPB)
; DS:SI = disk buffer
; OUTPUT: ZY = disk buffer does correspond
; ----------------------------------------------------------------------------
GetSectorTest: cmp al,[si+BUF_Disk] ; does disk number correspond?
jne GetSectorTest2 ; disk sector does not correspond
cmp dx,[si+BUF_SectorL] ; does disk sector LOW correspond?
jne GetSectorTest2 ; disk sector does not correspond
cmp di,[si+BUF_SectorH] ; does disk sector HIGH correspond?
GetSectorTest2: ret
; ----------------------------------------------------------------------------
; Get sector using disk buffer
; ----------------------------------------------------------------------------
; INPUT: AH = flags
; bit 0: don't read sector (else don't read, it is created)
; bit 1: it is FAT sector, use multiple write
; DI:DX = sector number
; ES:BP = DOS Drive Parameter Block (DDPB)
; OUTPUT: DS:SI = disk buffer
; DESTROYS: AX
; ----------------------------------------------------------------------------
; ------------- Push registers
GetSector: push bx ; push BX
push cx ; push CX
; ------------- Prepare disk number (-> AL)
mov al,[es:bp+DDPB_disk] ; AL <- disk number
; ------------- Check last used disk buffer
lds si,[cs:LastBuffer] ; DS:SI <- last used disk buffer
inc si ; is it valid buffer?
jz GetSector2 ; it is not valid buffer
dec si ; return offset of disk buffer
call GetSectorTest ; test sector
je GetSector9 ; sector does correspond
; ------------- Find corresponding disk buffer
GetSector2: lds si,[cs:FirstDiskBuff] ; DS:SI <- first disk buffer
GetSector3: call GetSectorTest ; test sector
je GetSector7 ; disk buffer does correspond
lds si,[si+BUF_Next] ; DS:SI <- next buffer
cmp si,byte -1 ; is it valid buffer?
jne GetSector3 ; it is valid buffer
; ------------- Buffer not found, flush first disk buffer (=oldest)
lds si,[cs:FirstDiskBuff] ; DS:SI <- first disk buffer
call FlushBuff ; flush disk buffer
; ------------- Read sector into disk buffer
lea bx,[si+BUF_Data] ; BX <- data buffer
mov cx,1 ; read 1 sector
test ah,B0 ; don't read sector?
jnz GetSector6 ; don't read sector
test ah,B1 ; is it FAT sector?
jz GetSector5 ; it is not FAT sector
call DOS24ReadFAT ; read FAT into buffer
jmp short GetSector6
GetSector5: call DOS24Read ; read sector into buffer
; ------------- Store parameters into disk buffer
GetSector6: mov [si+BUF_Disk],al ; disk number
and byte [si+BUF_Flags],~BUFFLAG_DATA ; unmark buffer
mov [si+BUF_SectorH],di ; disk sector HIGH
mov [si+BUF_SectorL],dx ; disk sector LOW
mov [si+BUF_DDPB],bp ; offset of DDPB
mov [si+BUF_DDPB+2],es ; segment of DDPB
; ------------- Set number of copies
GetSector7: test ah,B1 ; is it FAT sector?
mov ax,1 ; number of copies for non-FAT
jz GetSector8 ; it is non-FAT sector
mov al,[es:bp+DDPB_fat] ; AL <- number of FATs
mov ah,[es:bp+DDPB_fatsec] ; AH <- sector per FAT
GetSector8: mov [si+BUF_CopyOffset],ax ; set number of copies + offset
; ------------- Shift buffer to end of buffer chain
call ShiftEnd ; shift buffer to end of buffer chain
; ------------- Store last used disk buffer
GetSector9: mov [cs:LastBuffer],si ; store offset of last disk buffer
mov [cs:LastBuffer+2],ds ; store segment of last disk buffer
; ------------- Pop registers
pop cx ; pop CX
pop bx ; pop BX
ret
; ----------------------------------------------------------------------------
; Data
; ----------------------------------------------------------------------------
LastBuffer: dd -1 ; pointer to last used disk buffer
|