; ============================================================================
;
; LT-DOS - Int 21h disk
;
; ============================================================================
MAXPATH EQU 80h ; maximum length of path
; ------------- DOS Drive Parameter Block, DDPB
struc DDPB
DDPB_disk: resb 1 ; 0: logical drive number (0=A:)
DDPB_unit: resb 1 ; 1: unit number within device driver
DDPB_sector: resw 1 ; 2: bytes per sector
DDPB_maxsect: resb 1 ; 4: highest sector number in cluster
DDPB_shifts: resb 1 ; 5: shift count to convert clusters
; into sectors
DDPB_res: resw 1 ; 6: number of reserved sectors (BOOT)
DDPB_fat: resb 1 ; 8: number of FATs
DDPB_root: resw 1 ; 9: number of ROOT entries
DDPB_databeg: resw 1 ; 0Bh: number of first data sector
DDPB_maxclust: resw 1 ; 0Dh: highest cluster number
; (= number of data clusters + 1)
; FAT16 if > 0FF6h, else FAT12
DDPB_fatsec: resb 1 ; 0Fh: sectors per FAT
DDPB_rootbeg: resw 1 ; 10h: sector number of root start
DDPB_DDH: resd 1 ; 12h: address of device DDH
DDPB_media: resb 1 ; 16H: media ID byte
DDPB_access: resb 1 ; 17h: 0=disk accessed, 0FFh=not
DDPB_next: resd 1 ; 18h: pointer to next DDPB
DDPB_curdir: resw 1 ; 1Ch: cluster of start of current
; directory (0=root, -1=unknown)
DDPB_path: resb MAXPATH ; 1Eh: ASCIIZ pathname of current dir
endstruc
DDPB_SIZE EQU DDPB_size ; 9Eh = 158 bytes
; ------------- Media ID byte
MEDIAID_320K EQU 0ffh ; floppy 320K, double-sided, 8 sectors
MEDIAID_160K EQU 0feh ; floppy 160K, single-sided, 8 sectors
MEDIAID_360K EQU 0fdh ; floppy 360K, double-sided, 9 sectors
MEDIAID_180K EQU 0fch ; floppy 180K, single-sided, 9 sectors
MEDIAID_995K EQU 0fah ; HP 200LX D: ROM disk, 16 sectors
MEDIAID_1M2 EQU 0f9h ; floppy 1.2M, double-sided, 15 sectors
MEDIAID_720K EQU MEDIAID_1M2 ; floppy 720K, double-sided, 9 sectors
MEDIAID_HD EQU 0f8h ; hard disk
MEDIAID_OTHER EQU 0f0h ; other media
MEDIAID_1M44 EQU MEDIAID_OTHER ; floppy 1.44M, double-sided, 18 sect.
; ----------------------------------------------------------------------------
; Int 21h, function 1Bh - get allocation information for default drive
; ----------------------------------------------------------------------------
; INT21 INPUT: AH = 1Bh (function code)
; INT21 OUTPUT: AL = sectors per cluser
; CX = bytes per sector
; DX = total number of clusters
; DS:BX = pointer to media ID byte
; ----------------------------------------------------------------------------
Int211B: mov dl,0 ; DL <- 0 default drive
; Int211C must follow!
; ----------------------------------------------------------------------------
; Int 21h, function 1Ch - get allocation information for specific drive
; ----------------------------------------------------------------------------
; INT21 INPUT: AH = 1Ch (function code)
; DL = drive (0=default, 1=A: etc)
; INT21 OUTPUT: AL = sectors per cluser or 0FFh = invalid drive
; CX = bytes per sector
; DX = total number of clusters
; DS:BX = pointer to media ID byte
; ----------------------------------------------------------------------------
Int211C: push cs ; push CS
pop ds ; DS <- CS
; ------------- Set working disk
xchg ax,dx ; AL <- required drive
call SetWorkDisk ; set working disk
mov al,0ffh ; AL <- error code, invalid drive
jc Int211C9 ; error, invalid drive
; ------------- Load disk information
call LoadDisk ; load disk information
; ------------- Get parameters
mov al,[es:bp+DDPB_maxsect] ; AL <- highest sector
inc ax ; AL <- sectors per cluster
lea bx,[bp+DDPB_media] ; BX <- media descriptor address
mov cx,[es:bp+DDPB_sector] ; CX <- bytes per sector
mov dx,[es:bp+DDPB_maxclust] ; DX <- highest cluster
dec dx ; DX <- number of cluster
; ------------- Set output parameters
call SetRegBX ; set register BX (offset of Media ID)
call SetRegCX ; set register CX (bytes per sector)
call SetRegDX ; set register DX (number of clusters)
call GetReg ; get register pointer
mov [si+REG21DS],es ; segment of pointer to media ID byte
Int211C9: ret
; ----------------------------------------------------------------------------
; Set working disk
; ----------------------------------------------------------------------------
; INPUT: AL = working disk (0=default, 1=A: etc)
; OUTPUT: CY = invalid disk number
; ----------------------------------------------------------------------------
SetWorkDisk: cmp [cs:DiskCount],al ; check maximum disk number
jb SetWorkDisk9 ; invalid disk number
dec al ; correct disk number
jns SetWorkDisk8 ; disk number is OK
mov al,[cs:DefDisk] ; use default disk
SetWorkDisk8: mov [cs:WorkDisk],al ; set new working disk
SetWorkDisk9: ret
; ----------------------------------------------------------------------------
; Load disk information
; ----------------------------------------------------------------------------
; OUTPUT: ES:BP = working disk table
; DS = data segment
; DESTROYS: AX, BX, SI, DI
; ----------------------------------------------------------------------------
; ------------- Get disk drive
LoadDisk: mov al,[WorkDisk] ; AL <- working disk
call GetDDPB ; find DDPB (DOS Drive Parameter Block)
; ------------- Prepare pointer to DDR table
mov bx,DOSDDR ; BX <- DDR table
; ------------- Init DDR table
mov ah,[es:bp+DDPB_unit] ; AH <- unit number
mov al,15 ; AL <- length of DDR
mov [bx+DDR_len],ax ; set unit number and length of DDR
mov byte [bx+DDR_cmd],CMD_CHECK ; set command
mov word [bx+DDR_errorstatus],0 ; set error code + status
mov al,[es:bp+DDPB_media] ; AL <- media ID byte
mov [bx+DDR_media],al ; set media ID byte
; ------------- Media check
push es ; push ES
lds si,[es:bp+DDPB_DDH] ; DS:SI <- Device Driver Header
push cs ; push CS
pop es ; ES <- CS
call DevExec ; execute device service
pop es ; pop ES
push cs ; push CS
pop ds ; DS <- CS
jnc LoadDisk2 ; operatin OK
; ------------- Int 24h error routine
mov ah,0 ; AX = error code
xchg ax,di ; AX <- error code
mov ah,B1 ; AH <- error code (bad FAT table)
mov al,[WorkDisk] ; AL <- working disk
call DoInt24 ; do Int 24h error routine
jmp short LoadDisk ; repeat operation
; ------------- Test status
LoadDisk2: mov ah,0 ; AH <- 0
xchg ah,[es:bp+DDPB_access] ; AH <- disc accessed flag
mov al,[WorkDisk] ; AL <- working disk
or ah,[DOSDDR+14] ; media changed?
js LoadDisk6 ; media changed
jz LoadDisk4 ; media state is unknown
ret ; media has not been changed
; ------------- Media state unknown
LoadDisk4: mov ah,1 ; AH <- 1,
; ------------- Media changed
LoadDisk6:
; ----------------------------------------------------------------------------
; Find DOS Drive Parameter Block
; ----------------------------------------------------------------------------
; INPUT: AL = logical disk (0=A:, 1=B:, ...,)
; OUTPUT: CY = error, invalid logical disk
; ES:BP = address of DDPB
; ----------------------------------------------------------------------------
; ------------- Prepare pointer to first logical disk
GetDDPB: les bp,[cs:FirstDDPB] ; ES:BP <- address of first DDPB
; ------------- Check maximal disk number
cmp al,[cs:DiskCount] ; check disk number
jae GetDDPB4 ; error, invalid disk number
; ------------- Check if it is required disk
GetDDPB2: cmp al,[es:bp+DDPB_disk] ; is it required disk?
je GetDDPB5 ; it is required disk (set NC)
les bp,[es:bp+DDPB_next] ; ES:BP <- next DDPB
jmp short GetDDPB2 ; next disk
; ------------- Store pointer to current disk table
GetDDPB4: stc ; error
GetDDPB5: mov [cs:DiskTable],bp ; disk table LOW
mov [cs:DiskTable+2],es ; disk table HIGH
ret
; ----------------------------------------------------------------------------
; Prepare DDR table for 16-bit read operation
; ----------------------------------------------------------------------------
; INPUT: AL = unit number (ignored for character device)
; AH = media descriptor (ignored for character device)
; CX = number of sectors or bytes to read (not 0ffffh)
; DX = starting sector number or byte offset
; DS:BX = data buffer
; ----------------------------------------------------------------------------
InitR16DDR: push ax ; push AX
mov al,CMD_IN ; AL <- read command code
jmp short InitRW16DDR2
; ----------------------------------------------------------------------------
; Prepare DDR table for 16-bit write operation
; ----------------------------------------------------------------------------
; INPUT: AL = unit number (ignored for character device)
; AH = media descriptor (ignored for character device)
; CX = number of sectors or bytes to write (not 0ffffh)
; DX = starting sector number or byte offset
; DS:BX = data buffer
; ----------------------------------------------------------------------------
InitW16DDR: push ax ; push AX
mov al,CMD_OUT ; AL <- write command code
add al,[cs:Verify] ; AL <- write or write+verify code
; ------------- Store command code
InitRW16DDR2: mov [cs:DOSDDR_cmd],al ; store command code
pop ax ; pop AX
; ------------- Prepare DOS DDR table pointer
InitRW16DDR4: push si ; push SI
mov si,DOSDDR ; SI <- DOS DDR table pointer
; ------------- Store segment of data buffer
mov [cs:si+DDR_trans+2],ds ; store segment of address
; ------------- Prepare data segment
push ds ; push DS
push cs ; push CS
pop ds ; DS <- CS
; ------------- Set table parameters
mov byte [si+DDR_len],22 ; set length of table
mov [si+DDR_unit],al ; store unit number
mov word [si+DDR_errorstatus],0 ; init status word
mov [si+DDR_media],ah ; store media descriptor
mov [si+DDR_trans],bx ; store offset of address
mov [si+DDR_count],cx ; store number of sectors/bytes
mov [si+DDR_start],dx ; store starting sector number
; ------------- Pop registers
pop ds ; pop DS
pop si ; pop SI
ret
; ----------------------------------------------------------------------------
; Prepare DDR table for disk read operation (16 or 32 bit)
; ----------------------------------------------------------------------------
; INPUT: CX = number of sectors to read (not 0ffffh)
; DI:DX = starting sector number
; DS:BX = data buffer
; ES:BP = DOS Drive Parameter Block (DDPB)
; ----------------------------------------------------------------------------
; ------------- Init 16-bit read operation
InitReadDDR: push ax ; push AX
mov al,[es:bp+DDPB_unit] ; AL <- unit number
mov ah,[es:bp+DDPB_media] ; AH <- media ID byte
call InitR16DDR ; init 16-bit read operation
jmp short InitRWDDR2
; ----------------------------------------------------------------------------
; Prepare DDR table for disk write operation (16 or 32 bit)
; ----------------------------------------------------------------------------
; INPUT: CX = number of sectors to write (not 0ffffh)
; DI:DX = starting sector number
; DS:BX = data buffer
; ES:BP = DOS Drive Parameter Block (DDPB)
; ----------------------------------------------------------------------------
; ------------- Init 16-bit write operation
InitWriteDDR: push ax ; push AX
mov al,[es:bp+DDPB_unit] ; AL <- unit number
mov ah,[es:bp+DDPB_media] ; AH <- media ID byte
call InitW16DDR ; init 16-bit write operation
; ------------- Push registers
InitRWDDR2: push si ; push SI
push ds ; push DS
; ------------- Test if drive supports 32-bit operations
lds si,[es:bp+DDPB_DDH] ; ES:BP <- Device Driver Header
test byte [ds:si+DDH_attr],B1 ; supports 32-bit mode?
jz InitRWDDR6 ; it doesn't support 32-bit mode
; ------------- Prepare DOS DDR table pointer
mov si,DOSDDR ; SI <- DOS DDR table pointer
push cs ; push CS
pop ds ; DS <- CS
; ------------- Set parameters for 32-bit mode
mov byte [si+DDR_len],30 ; set length of table
mov word [si+DDR_start],-1 ; flag to use 32-bit mode
mov [si+DDR_start2],dx ; store starting sector LOW
mov [si+DDR_start2+2],di ; store starting sector HIGH
; ------------- Pop registers
InitRWDDR6: pop ds ; pop DS
pop si ; pop SI
pop ax ; pop AX
ret
; ----------------------------------------------------------------------------
; Execute device service
; ----------------------------------------------------------------------------
; INPUT: ES:BX = DDR (Device Driver Request)
; DS:SI = DDH (Device Driver Header)
; OUTPUT: CY = there was an error
; AH = status (see "Device driver status")
; AL = error code (see "Device driver error codes")
; ----------------------------------------------------------------------------
; ------------- Call strategy routine (set DDR)
DevExec: mov [cs:DevAddr+2],ds ; set device segment
mov ax,[si+DDH_strat] ; AX <- device strategy entry point
mov [cs:DevAddr],ax ; set device strategy offset
call far [cs:DevAddr]; execute device strategy
; ------------- Call interrupt routine
mov ax,[si+DDH_inter] ; AX <- device interrupt entry point
mov [cs:DevAddr],ax ; set device interupt offset
call far [cs:DevAddr]; execute device interrupt
; ------------- Result code
mov ax,[es:bx+DDR_errorstatus] ; AH <- status, AL <- error
or ah,ah ; any error?
jns DevExec2 ; operation OK
stc ; set errro flag
DevExec2: ret
; ----------------------------------------------------------------------------
; Read FAT sector from disk with Int 24h service
; ----------------------------------------------------------------------------
; INPUT: CX = number of sectors to read (not 0ffffh)
; DI:DX = starting sector number
; DS:BX = data buffer
; ES:BP = DOS Drive Parameter Block (DDPB)
; DESTROYS: AX
; ----------------------------------------------------------------------------
; ------------- Push registers
DOS24ReadFAT: push dx ; push DX
push di ; push DI
; ------------- Prepare number of FAT tables (-> AL)
mov al,[es:bp+DDPB_fat] ; AX <- number of FAT tables
; ------------- Read one sector
DOS24ReadFAT2: push ax ; push AX
call DOSRead ; read one sector from disk
pop ax ; pop AX
jnc DOS24ReadFAT4 ; read operation OK
; ------------- Next FAT table
add dl,[es:bp+DDPB_fatsec] ; shift to next FAT table
adc dh,0 ; carry
adc di,byte 0 ; carry
dec al ; counter of FAT tables
jnz DOS24ReadFAT2 ; next attempt
stc ; error flag
; ------------- Pop registers
DOS24ReadFAT4: pop di ; pop DI
pop dx ; pop DX
jnc DOS24Read8 ; sector read OK
; DOS24Read must follow!
; ----------------------------------------------------------------------------
; Read sectors from disk with Int 24h service
; ----------------------------------------------------------------------------
; INPUT: CX = number of sectors to read (not 0ffffh)
; DI:DX = starting sector number
; DS:BX = data buffer
; ES:BP = DOS Drive Parameter Block (DDPB)
; DESTROYS: AX
; ----------------------------------------------------------------------------
; ------------- Read sectors
DOS24Read: call DOSRead ; read sectors from disk
jnc DOS24Read8 ; operation is OK
; ------------- Error type = read
mov byte [cs:ErrorType],0 ; set error type = read
; ------------- Call Int 24h error service
call DoDiskInt24 ; call disk error routine
cmp al,1 ; repeat operation?
je DOS24Read ; repeat read operation
DOS24Read8: ret
; ----------------------------------------------------------------------------
; Write sectors to disk with Int 24h service
; ----------------------------------------------------------------------------
; INPUT: CX = number of sectors to write (not 0ffffh)
; DI:DX = starting sector number
; DS:BX = data buffer
; ES:BP = DOS Drive Parameter Block (DDPB)
; DESTROYS: AX
; ----------------------------------------------------------------------------
; ------------- Write sectors
DOS24Write: call DOSWrite ; write sectors to disk
jnc DOS24Write8 ; operation is OK
; ------------- Error type = write
mov byte [cs:ErrorType],1 ; set error type = write
; ------------- Call Int 24h error service
call DoDiskInt24 ; call disk error routine
cmp al,1 ; repeat operation?
je DOS24Write ; repeat write operation
DOS24Write8: ret
; ----------------------------------------------------------------------------
; Read sectors from disk
; ----------------------------------------------------------------------------
; INPUT: CX = number of sectors to read (not 0ffffh)
; DI:DX = starting sector number
; DS:BX = data buffer
; ES:BP = DOS Drive Parameter Block (DDPB)
; OUTPUT: CY = error
; AH = status (see "BIOS disk error codes")
; AL = error code (see "Device driver error codes")
; ----------------------------------------------------------------------------
; ------------- Prepare DDR table
DOSRead: call InitReadDDR ; prepare DDR table
jmp short DOSWrite2
; ----------------------------------------------------------------------------
; Write sectors to disk
; ----------------------------------------------------------------------------
; INPUT: CX = number of sectors to write (not 0ffffh)
; DI:DX = starting sector number
; DS:BX = data buffer
; ES:BP = DOS Drive Parameter Block (DDPB)
; OUTPUT: CY = error
; AH = status (see "Device driver status")
; AL = error code (see "Device driver error codes")
; CX = number of remaining sectors
; ----------------------------------------------------------------------------
; ------------- Prepare DDR table
DOSWrite: call InitWriteDDR ; prepare DDR table
; ------------- Push registers
DOSWrite2: push bx ; push BX
push si ; push SI
push ds ; push DS
push es ; push ES
; ------------- Call device service
lds si,[es:bp+DDPB_DDH] ; DS:SI <- Device Driver Header
push cs ; push CS
pop es ; ES <- CS
mov bx,DOSDDR ; BX <- DOS Device Driver Request
call DevExec ; execute device service
; ------------- Number of remaining sectors (-> CX)
pushf ; push flags
sub cx,[cs:DOSDDR_count] ; CX <- remaining sectors
popf ; pop flags
; ------------- Pop registers
pop es ; pop ES
pop ds ; pop DS
pop si ; pop SI
pop bx ; pop BX
ret
; ----------------------------------------------------------------------------
; Int 25h interrupt routine - read from disk
; ----------------------------------------------------------------------------
; INPUT: AL = drive number (0=A:, 1=B:,...)
; old style call:
; CX = number of sectors to read (not 0ffffh)
; DX = starting sector number
; DS:BX = data buffer
; new style call:
; CX = 0ffffh
; DS:BX = disk read packet
; 0: (4) starting sector number
; 4: (2) number of sectors to read
; 6: (4) transfer address
; OUTPUT: CY = error
; AH = status (see "Device driver status")
; AL = DOS error code (see "Device driver error codes")
; NOTES: Original flags are left on stack and must be popped by caller
; ----------------------------------------------------------------------------
; ------------- Push stack pointer
MyInt25: cli ; disable interrupts
mov [cs:OldStack],sp ; push SP
mov [cs:OldStack+2],ss ; push SS
; ------------- Init internal stack - other functions
push cs ; push CS
pop ss ; SS <- CS
mov sp,Int21Stack1 ; SS:SP <- INT 21h internal stack 1
; ------------- Set DOS active flag
inc byte [cs:DOSActive] ; set DOS active flag
; ------------- Push registers
push bx ; push BX
push cx ; push CX
push dx ; push DX
push si ; push SI
push di ; push DI
push bp ; push BP
push ds ; push DS
push es ; push ES
sti ; enable interrupts
; ------------- Find disk (-> ES:BP)
call GetDDPB ; find disk DDPB
mov ax,(S_ERROR+S_DONE)*256+ERR_UNIT ; invalid disk unit
jc MyInt254 ; invalid disk
; ------------- Read data from disk
xor di,di ; DI <- 0 starting sector HIGH
cmp cx,byte -1 ; use new style call?
jne MyInt252 ; use old style call
mov dx,[bx] ; DX <- starting sector LOW
mov di,[bx+2] ; DI <- starting sector HIGH
mov cx,[bx+4] ; CX <- number of sectors
lds bx,[bx+6] ; DS:BX <- transfer address
MyInt252: call DOSRead ; read sectors from disk
; ------------- Pop registers (CY=error)
MyInt254: cli ; disable interrupts
pop es ; pop ES
pop ds ; pop DS
pop bp ; pop BP
pop di ; pop DI
pop si ; pop SI
pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
; ------------- Reset DOS active flag
dec byte [cs:DOSActive] ; reset DOS active flag
; ------------- Return old stack pointer
mov sp,[cs:OldStack] ; SP <- old SP
mov ss,[cs:OldStack+2] ; SS <- old SS
sti ; enable interrupts
retf ; ret from interrupt, flag is on stack
; ----------------------------------------------------------------------------
; Int 26h interrupt routine - write to disk
; ----------------------------------------------------------------------------
; INPUT: AL = drive number (0=A:, 1=B:,...)
; old style call:
; CX = number of sectors to write (not 0ffffh)
; DX = starting sector number
; DS:BX = data buffer
; new style call:
; CX = 0ffffh
; DS:BX = disk write packet
; 0: (4) starting sector number
; 4: (2) number of sectors to write
; 6: (4) transfer address
; OUTPUT: CY = error
; AH = status (see "Device driver status")
; AL = DOS error code (see "Device driver error codes")
; NOTES: Original flags are left on stack and must be popped by caller
; ----------------------------------------------------------------------------
; ------------- Push stack pointer
MyInt26: cli ; disable interrupts
mov [cs:OldStack],sp ; push SP
mov [cs:OldStack+2],ss ; push SS
; ------------- Init internal stack - other functions
push cs ; push CS
pop ss ; SS <- CS
mov sp,Int21Stack1 ; SS:SP <- INT 21h internal stack 1
; ------------- Set DOS active flag
inc byte [cs:DOSActive] ; set DOS active flag
; ------------- Push registers
push bx ; push BX
push cx ; push CX
push dx ; push DX
push si ; push SI
push di ; push DI
push bp ; push BP
push ds ; push DS
push es ; push ES
sti ; enable interrupts
; ------------- Find disk (-> ES:BP)
call GetDDPB ; find disk DDPB
mov ax,(S_ERROR+S_DONE)*256+ERR_UNIT ; invalid disk unit
jc MyInt264 ; invalid disk
; ------------- Write data to disk
xor di,di ; DI <- 0 starting sector HIGH
cmp cx,byte -1 ; use new style call?
jne MyInt262 ; use old style call
mov dx,[bx] ; DX <- starting sector LOW
mov di,[bx+2] ; DI <- starting sector HIGH
mov cx,[bx+4] ; CX <- number of sectors
lds bx,[bx+6] ; DS:BX <- transfer address
MyInt262: call DOSWrite ; write sectors to disk
MyInt264: jmp short MyInt254
; ----------------------------------------------------------------------------
; Data
; ----------------------------------------------------------------------------
Verify: db 1 ; verify after write flag (1=on, 0=off)
DevAddr: dd 0 ; device execute address
ErrorType: db 0 ; error type: 1=write, 0=read
; ------------- Disk
DefDisk: db 0 ; default disk (0=A: etc)
WorkDisk: db 0 ; working disk (0=A: etc)
LastDiskError: db 0ffh ; last disk with error (0ffh = none)
DiskTable: dd 0 ; current disk table DDPB
|