; ============================================================================
;
; MicroDOS - Disk device
;
; ============================================================================
; ------------- BIOS Parameter Block, BPB
struc BPB
BPB_sector: resw 1 ; 0: bytes per sector
BPB_cluster: resb 1 ; 2: sectors per cluster (0FFh=unknown)
BPB_res: resw 1 ; 3: number of reserved sectors (BOOT)
BPB_fat: resb 1 ; 5: number of FATs
BPB_root: resw 1 ; 6: number of ROOT entries
BPB_total: resw 1 ; 8: total sectors
BPB_media: resb 1 ; 0Ah: media descriptor (0=unknown)
; see Media ID byte
BPB_fatsec: resw 1 ; 0Bh: sectors per FAT
BPB_trksec: resw 1 ; 0Dh: sectors per track
BPB_heads: resw 1 ; 0Fh: number of heads
endstruc
BPB_SIZE equ BPB_size ; 11h = 17 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_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.
; ------------- BIOS disk error codes
BIOS_WRITE EQU 0cch ; write fault
BIOS_READY EQU 80h ; device not ready (timeout)
BIOS_SEEK EQU 40h ; seek failure
BIOS_CONTROL EQU 20h ; controller failed
BIOS_CRC EQU 10h ; CRC failure
BIOS_DMA EQU 8 ; DMA overrun
BIOS_CHANGE EQU 6 ; ivalid disk change
BIOS_SECTOR EQU 4 ; required sector not found
BIOS_PROT EQU 3 ; write-protect violation
BIOS_ADDRESS EQU 2 ; bad address mark
BIOS_COMMAND EQU 1 ; bad command
BIOS_GENERAL EQU 0 ; unknown error
; ------------- Device driver error codes
ERR_PROT EQU 0 ; write-protect violation
ERR_UNIT EQU 1 ; unknown unit
ERR_READY EQU 2 ; device not ready
ERR_COMMAND EQU 3 ; unknown command
ERR_CRC EQU 4 ; CRC failure
ERR_LENGTH EQU 5 ; bad drive request structure length
ERR_SEEK EQU 6 ; seek failure
ERR_MEDIA EQU 7 ; unknown media
ERR_SECTOR EQU 8 ; sector not found
ERR_PAPER EQU 9 ; printer out of paper
ERR_WRITE EQU 10 ; write fault
ERR_READ EQU 11 ; read fault
ERR_GENERAL EQU 12 ; general failure
ERR_CHANGE EQU 15 ; ivalid disk change
; ----------------------------------------------------------------------------
; Change phantom disk (only floppy disk)
; ----------------------------------------------------------------------------
; INPUT: AL = required disk (0=A: or 1=B:)
; ----------------------------------------------------------------------------
; ------------- Check, if there is need to change disk
SetPhantom: cmp al,2 ; is it hard disk?
jae SetPhantom9 ; it is hard disk
test byte [cs:DiskFlags],B0 ; use phantom disk?
jz SetPhantom9 ; don't use phantom disk
; ------------- Push registers
push ax ; push AX
push dx ; push DX
push ds ; push DS
; ------------- Check if phantom disk has been changed
mov ah,al ; AH <- required disk
xor dx,dx ; DX <- 0
mov ds,dx ; DS <- 0
xchg ah,[CurPhantom] ; AH <- get old phantom disk status
cmp al,ah ; has been phantom disk changed?
je SetPhantom8 ; phantom disk has not been changed
; ------------- Display prompt text
add al,"A" ; AL <- ASCII name of disk
mov [cs:PhantTextA],al ; store phantom disk name
mov dx,PhantText ; DX <- message to change disk
call DispText ; display message
; ------------- Flush keyboard input buffer
mov ax,[41Ch] ; AX <- write pointer to buffer
mov [41Ah],ax ; read pointer from bufferu
; ------------- Wait for key input
mov ah,0 ; AH <- 0 function code
int 16h ; read character from keyboard
; ------------- Pop registers
SetPhantom8: pop ds ; pop DS
pop dx ; pop DX
pop ax ; pop AX
SetPhantom9: ret
; ----------------------------------------------------------------------------
; Prepare disk time and time pointer (only floppy disk)
; ----------------------------------------------------------------------------
; INPUT: AL = drive number (0=A: or 1=B:)
; OUTPUT: CX:DX = current time
; BX = pointer to last access time
; ----------------------------------------------------------------------------
; ------------- Prepare pointer to last access time (-> BX)
GetDiskTime: xor bx,bx ; BX <- 0
mov bl,al ; BL <- drive number
shl bx,1 ; drive number * 2
shl bx,1 ; drive number * 4
add bx,DiskParAccess ; BX <- pointer to last acces time
; ------------- Load system time (-> CX:DX)
call GetTimer ; load system timer
ret
; ----------------------------------------------------------------------------
; Test time of access to removable disk (without changeline)
; ----------------------------------------------------------------------------
; INPUT: AL = drive number (0=A: or 1=B:)
; OUTPUT: CY = medium has not been changed (else unknown state)
; ----------------------------------------------------------------------------
; ------------- Push registers
TestDiskTime: push bx ; push BX
push cx ; push CX
push dx ; push DX
; ------------- Prepare disk time (-> CX:DX) and time pointer (-> BX)
call GetDiskTime ; prepare time and pointer
; ------------- Time interval
sub dx,[cs:bx] ; time LOW
sbb cx,[cs:bx+2] ; time HIGH
; ------------- Check time interval
clc ; flag - unknown media state
jnz TestDiskTime2 ; it is too long interval
cmp dx,byte 2*18 ; check 2 seconds interval
; ------------- Pop registers
TestDiskTime2: pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
ret
; ----------------------------------------------------------------------------
; Set time of access to removable disk (without changeline)
; ----------------------------------------------------------------------------
; INPUT: AL = logical drive number (0=A:, 1=B:,...)
; ----------------------------------------------------------------------------
; ------------- Check if it is valid disk
SetDiskTime: cmp al,2 ; is it hard disk?
jae SetDiskTime8 ; it is hard disk
; ------------- Push registers
push bx ; push BX
push cx ; push CX
push dx ; push DX
; ------------- Check if this drive supports changeline
xor bx,bx ; BX <- 0
mov bl,al ; BL <- drive number
test byte [cs:bx+DiskParFlags],B0 ; supports changeline?
jnz SetDiskTime6 ; disk supports changeline
; ------------- Prepare disk time (-> CX:DX) and time pointer (-> BX)
call GetDiskTime ; prepare time and pointer
; ------------- Store new time of disk access
mov [cs:bx],dx ; push time LOW
mov [cs:bx+2],cx ; push time HIGH
; ------------- Pop registers
SetDiskTime6: pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
SetDiskTime8: ret
; ----------------------------------------------------------------------------
; Test disk change
; ----------------------------------------------------------------------------
; INPUT: AL = logical drive number (0=A:, 1=B:,...)
; OUTPUT: CY = medium has not been changed (else unknown state)
; ----------------------------------------------------------------------------
; ------------- Push registers
DiskChange: push ax ; push AX
push dx ; push DX
; ------------- Check if it is fixed disk (it cannot be changed)
cmp al,2 ; is it fixed disk?
jae DiskChange6 ; medium has not been changed
; ------------- Check if changeline is supported
mov ah,0 ; AX = drive number
xchg ax,bx ; BX <- drive number
test byte [cs:bx+DiskParFlags],B0 ; changeline supported?
xchg ax,bx ; AX <- drive number
jnz DiskChange4 ; changeline is supported
; ------------- Changeline not supported - test last access time
call TestDiskTime ; check last access time
jmp short DiskChange8 ; CY = medium has not been changed
; ------------- Test changeline state
DiskChange4: xchg ax,dx ; DL <- disk number
mov ah,22 ; AH <- function code
call Int13 ; test changeline state
jnc DiskChange6 ; no error, disk has not been changed
cmp ah,6 ; change flag?
je DiskChange8 ; disk has been changed
DiskChange6: stc ; flag - medium has not been changed
; ------------- Pop registers
DiskChange8: pop dx ; pop DX
pop ax ; pop AX
ret
; ----------------------------------------------------------------------------
; Current disk reset
; ----------------------------------------------------------------------------
; ------------- Push registers
DiskReset: push ax ; push AX
push dx ; push DX
; ------------- Disk reset
mov ah,0 ; AH <- function code
mov dl,[cs:DiskDrive] ; DL <- disk
call Int13 ; disk reset
; ------------- Pop registers
pop dx ; pop DX
pop ax ; pop AX
ret
; ----------------------------------------------------------------------------
; Transfer LBA address to CHS address (for current drive)
; ----------------------------------------------------------------------------
; INPUT: DX = absolute sector
; CS:SI = disk parameter table BPB
; OUTPUT: CL = bit 0 - 5: sector number (1 to 63)
; bit 6 - 7: cylinder (bits 8 to 9)
; CH = cylinder (bits 0 to 7)
; DH = head number (0 to 255)
; DL = disk number
; CONDITIONS: sectors per track: max. 63
; heads per cylinder: max. 256
; cylinders: max. 1024
; ----------------------------------------------------------------------------
; ------------- Push registers
LBAtoCHS: push ax ; push AX
push bx ; push BX
; ------------- Prepare number of sectors per cylinder (-> AX)
push dx ; push DX (absolute sector)
mov ax,[cs:si+BPB_trksec] ; AX <- sectors per track
mov bx,ax ; BX <- sectors per track
mul word [cs:si+BPB_heads] ; AX <- sectors per cylinder
xchg ax,cx ; CX <- sectors per cylinder
pop ax ; pop AX (absolute sector)
; ------------- Calculate cylinder number (->AX) and sector in cylinder (->DX)
xor dx,dx ; DX <- 0
div cx ; AX <- cylinder, DX <- sector in cyl.
; ------------- Prepare cylinder number (-> CX)
xchg al,ah ; AH <- cylinder LOW, AL <- cyl. HIGH
ror al,1
ror al,1 ; AL <- bits 8 - 9 to position 6 - 7
and al,B7+B6 ; mask bits 6 and 7 of cylinder HIGH
xchg ax,cx ; CX <- cylinder
; ------------- Calculate head (-> DH) and sector (-> CL)
xchg ax,dx ; AX <- sector in cylinder
div bl ; AL <- head, AH <- sector
inc ah ; AH <- sector + 1
or cl,ah ; CL <- cylinder HIGH + sector
mov dh,al ; DH <- head number
; ------------- Pop registers
pop bx ; pop BX
pop ax ; pop AX
; ------------- Disk number (-> DL)
mov dl,[cs:DiskDrive] ; DL <- disk
ret
; ----------------------------------------------------------------------------
; Low-level read/write/verify data from/to disk (with repeat on error)
; ----------------------------------------------------------------------------
; INPUT: AH = function code 2=read, 3=write
; DX = starting sector
; CX = number of sectors (minimal 1)
; ES:BX = transfer buffer
; OUTPUT: CY = error
; AH = DOS error code (valid if CY, else undefined)
; DX = new sector
; CX = number of remaining sectors (0 if NC)
; ES:BX = new transfer buffer (normalized)
; DESTROYS: AL
; ----------------------------------------------------------------------------
; ------------- Push registers
ReadWriteLow: push bp ; push BP
; ------------- Prepare error counter
ReadWriteLow0: mov bp,2 ; BP <- number of repetition
; ------------- Next try on error
ReadWriteLow1: push ax ; push AX
push bx ; push BX
push cx ; push CX
push dx ; push DX
push si ; push SI
; ------------- Prepare disk table (-> SI)
mov si,[cs:DiskParBPBCurr] ; get BPB table
; ------------- Limit number of sectors to 127 (due to some BIOSes)
cmp cx,127 ; max. number of sectors
jbe ReadWriteLow2 ; number of sectors is OK
mov cx,127 ; limit number of sectors
ReadWriteLow2: mov al,cl ; AL <- number of sectors
; ------------- Translate LBA address to CHS address
call LBAtoCHS ; translate to CHS address
; ------------- Prepare number of sectors in one operation (-> AL)
push bx ; push BX
mov bl,cl ; BL <- sector number
and bl,3Fh ; mask sector number (1 to 63)
dec bx ; BL = sector number (0 to 62)
mov bh,[cs:si+BPB_trksec] ; BH <- sectors per track
sub bh,bl ; BH <- sectors to end of track
cmp al,bh ; test number of sectors
jbe ReadWriteLow4 ; number of sectors is OK
mov al,bh ; AL <- limit number of sectors
ReadWriteLow4: pop bx ; pop BX
; ------------- Do operation
push ax ; push AX (AL=number of sectors)
call Int13 ; transfer data
xchg ax,bx ; BH <- error
pop ax ; pop AX, number of required sectors
mov ah,bh ; AH <- error code
; ------------- Pop registers (AL = sectors, AH = error, CY = error)
pop si ; pop SI
pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
; ------------- Repeate on error
jnc ReadWriteLow6 ; operation OK
dec bp ; error counter
jz ReadWriteLow9 ; no other attempt (CY is set)
; ------------- Reset disk and try again
call DiskReset ; disk reset
pop ax ; pop AX (function code)
jmp short ReadWriteLow1 ; next attempt
; ------------- Shift registers
ReadWriteLow6: mov ah,0 ; AX = number of sectors
add dx,ax ; shift starting sector LOW
sub cx,ax ; decrease remaining sectors
push dx ; push DX
push si ; push SI
mov si,SECTORSIZE ; SI <- size of sector
mul si ; * number of bytes per sector
add ax,bx ; DX:AX <- shift offset
adc dx,byte 0 ; overflow
mov bp,16 ; BP <- 16 divisor
div bp ; AX <- offset HIGH
mov bx,dx ; BX <- offset LOW
mov dx,es ; DX <- segment
add ax,dx ; AX <- new segment
mov es,ax ; ES <- new segment
pop si ; pop SI
pop dx ; pop DX
pop ax ; pop AX
; ------------- Next group of sectors (and set NC)
or cx,cx ; all sectors are transfered?
jnz ReadWriteLow0 ; transfer next group of sectors
push ax ; push AX
; ------------- Prepare registers
ReadWriteLow9: pop bp ; destroy AX
pop bp ; pop BP
ret
; ----------------------------------------------------------------------------
; read/write data from/to disk (with DMA check)
; ----------------------------------------------------------------------------
; INPUT: AH = function code 2=read, 3=write
; AL = logical drive number (0=A:, 1=B:,...)
; CX = number of sectors
; DX = starting sector number
; ES:BX = transfer buffer
; OUTPUT: CY = error
; AH = BIOS error code
; CX = number of remaining sectors (0 if NC)
; DX = new sector
; ES:BX = new transfer buffer
; ----------------------------------------------------------------------------
; ------------- Check zero number of sectors (set NC)
ReadWrite: or cx,cx ; remain any sectors?
jnz ReadWrite1 ; remain any sectors
; ------------- Update system timer for removable media without changeline
call SetDiskTime ; set last disk access time
clc ; clear error flag
ret ; end of transfer
; ------------- Push registers
ReadWrite1: push si ; push SI
push di ; push DI
push bp ; push BP
push ds ; push DS
push cx ; push CX (number of sectors)
push ax ; push AX (function code)
mov bp,cx ; BP <- number of sectors
; ------------- Normalize buffer (-> ES(AX):BX)
push dx ; push DX
mov ax,bx ; AX <- offset of buffer
and bx,byte 0fh ; normalize offset
mov cl,4 ; CL <- 4 number of shifts
shr ax,cl ; AX <- offset as segment
mov dx,es ; DX <- destination segment
add ax,dx ; AX <- normalized segment
mov es,ax ; ES <- normalized segment
; ------------- Limit number of sectors to next 64 KB boundary (-> BP)
shl ax,cl ; AX * 16 (=segment as offset)
add ax,bx ; AX <- absolute offset in 64 KB
xor dx,dx ; DX <- 0 offset HIGH
neg ax ; AX <- complement to 64 KB boundary
sbb dx,byte -1 ; DX <- 1 if AX=0, else DX <- 0
mov cx,SECTORSIZE ; SI <- size of sector
div cx ; AX <- calculate number of sectors
cmp ax,bp ; remain less sectors?
jb ReadWrite2 ; remain less sectors
xchg ax,bp ; AX <- limit number of sectors
ReadWrite2: xchg ax,bp ; BP <- number of sectors
pop dx ; pop DX
; ------------- No whole sector - use temporary buffer
pop ax ; pop AX (AH = function code)
push ax ; push AX
or bp,bp ; remains any sector?
jnz ReadWrite5 ; remains any sector
mov bp,1 ; required 1 sector
; ------------- On "write" transfer sector to temporary buffer
cmp ah,3 ; write?
jne ReadWrite3 ; it is not write
push es ; push ES
mov cx,SECTORSIZE/2 ; CX <- sector size in words
push es ; push ES (segment of buffer)
pop ds ; DS <- segment of buffer
mov si,bx ; SI <- offset of buffer
push cs ; push CS
pop es ; ES <- segment of temporary buffer
mov di,DiskBuff ; DI <- offset of temporary buffer
cld ; direction up
rep movsw ; transfer sector
pop es ; pop ES
; ------------- Transfer data of one sector
ReadWrite3: push bx ; push BX
push es ; push ES
mov di,bp ; transfer 1 sector
push cs ; push CS
pop es ; ES <- segment of temporary buffer
mov bx,DiskBuff ; BX <- offset of temporary buffer
call ReadWriteLow ; read/write 1 sector
pop es ; pop ES
pop bx ; pop BX
jc ReadWrite7 ; error (AH=error code)
; ------------- On "read" transfer sector from temporary buffer
pop ax ; pop AX (AH = function code)
push ax ; push AX
cmp ah,2 ; read ?
jne ReadWrite4 ; it is not read
mov cx,SECTORSIZE/2 ; CX <- sector size in words
mov di,bx ; DI <- offset of buffer
push cs ; push CS
pop ds ; DS <- segment of temporary buffer
mov si,DiskBuff ; SI <- offset of temporary buffer
cld ; direction up
rep movsw ; transfer sector
; ------------- New transfer address (-> ES:BX, here AH = function code)
ReadWrite4: add bx,SECTORSIZE ; shift offset of buffer
jmp short ReadWrite6
; ------------- Transfer group of sectors
ReadWrite5: mov cx,bp ; DI <- number of sectors to transfer
call ReadWriteLow ; transfer sectors
pushf ; push flags
sub bp,cx ; BP <- number of transfered sectors
popf ; pop flags
jc ReadWrite7 ; error
; ------------- Return function code
pop ax ; pop AX (AH = function code)
push ax ; push AX
; ------------- Change number of sectors (here AH = function code)
ReadWrite6: pop ax ; pop AX
pop cx ; pop CX
sub cx,bp ; change number of sectors
push cx ; push CX
push ax ; push AX
clc ; operation OK
; ------------- Pop registers (CY=error, AH=function code or error code)
ReadWrite7: pop bp ; destroy AX
ReadWrite8: pop cx ; pop CX
pop ds ; pop DS
pop bp ; pop BP
pop di ; pop DI
pop si ; pop SI
jc ReadWrite9 ; error
jmp ReadWrite ; next group of sectors
ReadWrite9: ret
; ----------------------------------------------------------------------------
; Init default BPB disk parameters
; ----------------------------------------------------------------------------
; INPUT: CS:SI = BPB disk parameters
; ----------------------------------------------------------------------------
; ------------- Push registers
DefDiskPar: push ds ; push DS
; ------------- Prepare registers
push cs ; push CS
pop ds ; DS <- data segment
; ------------- Init parameters
mov word [si+BPB_sector],SECTORSIZE ; sector size
mov byte [si+BPB_cluster],0ffh ; sectors per cluster
mov word [si+BPB_res],1 ; reserved sectors (BOOT)
mov byte [si+BPB_fat],2 ; number of FATs
mov word [si+BPB_root],64 ; number of ROOT entries
mov word [si+BPB_total],8*40 ; total sectors
mov byte [si+BPB_media],0 ; media descriptor
mov word [si+BPB_fatsec],2 ; sectors per FAT
mov byte [si+BPB_trksec],8 ; sectors per track
mov word [si+BPB_heads],1 ; number of heads
; ------------- Pop registers
pop ds ; pop DS
ret
; ----------------------------------------------------------------------------
; Load disk parameters of current disk
; ----------------------------------------------------------------------------
; INPUT: AL = logical drive number (0=A:, 1=B:,...)
; ----------------------------------------------------------------------------
; ------------- Push registers
LoadDiskPar: call PushAll ; push all registers
; ------------- Prepare DS
push cs ; push CS
pop ds ; DS <- data segment
; ------------- Read BOOT sector
mov ah,2 ; AH <- 2 function code for reading
mov bx,DiskBuff ; BX <- disk buffer
mov cx,1 ; CX <- 1 number of sectors
xor dx,dx ; DX <- 0 starting sector
push cs ; push CS
pop es ; ES <- data segment
call ReadWrite ; read BOOT sector
jc LoadDiskPar8 ; error
; ------------- Check if it is valid BOOT sector
cmp word [DiskBuff+512-2],0aa55h ; check BOOT identifier
jne LoadDiskPar8 ; invalid BOOT sector
mov si,DiskBuff+11 ; BPB of BOOT sector
cmp word [si+BPB_sector],SECTORSIZE ; check sector size
jne LoadDiskPar8 ; invalid sector size
; ------------- Load parameters from BOOT sector
push cs ; push CS
pop es ; ES <- data segment
mov di,[DiskParBPBCurr] ; DI <- current disk table
mov cx,BPB_SIZE ; CX <- size of BPB table
cld ; set direction up
rep movsb ; store BPB table
; ------------- Pop registers
LoadDiskPar8: call PopAll ; pop all registers
ret
; ----------------------------------------------------------------------------
; Init disk parameters of current disk
; ----------------------------------------------------------------------------
; INPUT: AL = logical drive number (0=A:, 1=B:,...)
; ----------------------------------------------------------------------------
; ------------- Push registers
InitDiskPar: push ax ; push AX
push bx ; push BX
push cx ; push CX
push dx ; push DX
push si ; push SI
; ------------- Prepare pointer to disk parameters (-> SI)
mov si,[cs:DiskParBPBCurr] ; SI <- pointer to table
; ------------- Check if it is hard drive
mov dl,[cs:DiskDrive] ; DL <- disk drive
or dl,dl ; is it hard drive?
jns InitDiskPar4 ; it is not hard drive
; ------------- Check if fixed disk parameters are initialized
cmp byte [cs:si+BPB_media],0 ; is table initialized?
jne InitDiskPar9 ; table is already initialized
; ------------- Init default BPB disk parameters
call DefDiskPar ; inti default BPB disk parameters
; ------------- Load fixed disk parameters
mov ah,8 ; AH <- 8 function code
call Int131 ; load fixed disk parameters
jc InitDiskPar9 ; error
; ------------- Check number of hard drives
or dl,dl ; is any hard disk?
jz InitDiskPar9 ; no hard disk
; ------------- Set media type
mov byte [cs:si+BPB_media],MEDIAID_HD ; hard disk
; ------------- Set number of sectors per track
mov al,cl ; AL <- maximum sector number
and ax,byte 3Fh ; mask sector number
mov [cs:si+BPB_trksec],ax ; number of sectors per track
push ax ; push AX (sectors per track)
; ------------- Set number of heads
mov al,dh ; AL <- maximum head number
inc ax ; AX <- number of heads
mov [cs:si+BPB_heads],ax ; number of heads
; ------------- Prepare number of sectors per cylinder
pop dx ; pop DX (sectors per track)
mul dx ; prepare sectors per cylindex
xchg ax,dx ; DX <- sectors per cylinder
; ------------- Prepare number of cylinders
xchg ax,cx ; AX <- cylinders and sectors
xchg al,ah ; AX <- cylinders
rol ah,1 ; rotate 1 bit of cylinder HIGH
rol ah,1 ; rotate 1 bit of cylinder HIGH
and ah,3 ; mask high 2 bits of cylinders
inc ax ; AX <- number of cylinders
; ------------- Prepare total number of sectors
mul dx ; AX <- total number of sectors
or dx,dx ; overflow number of sectors?
jz InitDiskPar2 ; number of sectors is OK
mov ax,0ffffh ; limit number of sectors
InitDiskPar2: mov [cs:si+BPB_total],ax ; set total number of sectors
jmp short InitDiskPar6
; ------------- Check if floppy disk has been changed
InitDiskPar4: call DiskChange ; disk changed?
jc InitDiskPar9 ; disk has not been changed
; ------------- Load disk parameters
InitDiskPar6: call LoadDiskPar ; load disk parameters
; ------------- Pop registers
InitDiskPar9: pop si ; pop SI
pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
pop ax ; pop AX
ret
; ----------------------------------------------------------------------------
; Int 25h interrupt routine - read from disk
; ----------------------------------------------------------------------------
; INPUT: AL = drive number (0=A:, 1=B:,...)
; CX = number of sectors to read
; DX = starting sector number
; DS:BX = data buffer
; OUTPUT: CY = error
; AH = status (see "BIOS disk error codes")
; AL = DOS error code (see "Device driver error codes")
; CX = number of remaining sectors (0 if NC)
; ----------------------------------------------------------------------------
; NOTES: Original flags are left on stack and must be popped by caller
; ----------------------------------------------------------------------------
MyInt25: mov ah,2 ; AH <- 2 function code (read)
jmp short MyInt256
; ----------------------------------------------------------------------------
; Int 26h interrupt routine - write to disk
; ----------------------------------------------------------------------------
; INPUT: AL = drive number (0=A:, 1=B:,...)
; CX = number of sectors to write
; DX = starting sector number
; DS:BX = data buffer
; OUTPUT: CY = error
; AH = status (see "BIOS disk error codes")
; AL = DOS error code (see "Device driver error codes")
; CX = number of remaining sectors (0 if NC)
; ----------------------------------------------------------------------------
; NOTES: Original flags are left on stack and must be popped by caller
; ----------------------------------------------------------------------------
MyInt26: mov ah,3 ; AH <- 3 function code (write)
; ------------- Check disk number
MyInt256: cmp al,MAXDISK ; check disk number
jb MyInt2561 ; disk number is OK
stc ; set error flag
mov ax,BIOS_GENERAL*256+ERR_UNIT ; invalid disk unit
retf
; ------------- Change phantom disk
MyInt2561: call SetPhantom ; set new phantom disk
; ------------- Change phantom drive B: to A:
cmp al,1 ; is it drive B: ?
jne MyInt2562 ; it is not drive B:
test byte [cs:DiskFlags],B0 ; use phantom disk?
jz MyInt2562 ; don't use phantom disk
mov al,0 ; change to drive A:
; ------------- Prepare disk number (floppy or hard drive)
MyInt2562: push ax ; push AX
cmp al,2 ; is it hard disk?
jb MyInt2563 ; it is floppy disk
%ifndef NOHD
add al,80h-2 ; change to hard disk
%endif
MyInt2563: mov [cs:DiskDriveFnc],ax ; store disk and function code
pop ax ; pop AX
; ------------- Pointer to current disk parameter table
push ax ; push AX
mov ah,BPB_SIZE ; AH <- size of one table
mul ah ; AX <- offset of disk param table
add ax,DiskParBPB ; AX <- address of disk param table
mov [cs:DiskParBPBCurr],ax ; store pointer to table
pop ax ; pop AX
; ------------- Init disk parameters
call InitDiskPar ; init disk parameters
; ------------- Do disk operation
push bx ; push BX
push dx ; push DX
push es ; push ES
push ds ; push DS
pop es ; ES <- segment of buffer
call ReadWrite ; read/write
pop es ; pop ES
pop dx ; pop DX
pop bx ; pop BX
; ------------- Remap BIOS error code to DOS error code
jnc MyInt2564 ; there is no error
call BIOSError ; remap error code
MyInt2564: retf
; ----------------------------------------------------------------------------
; Remap BIOS error to DOS error
; ----------------------------------------------------------------------------
; INPUT: AH = BIOS error code
; OUTPUT: AL = DOS error code
; CY = set errorflag
; ----------------------------------------------------------------------------
BIOSError: push bx ; push BX
mov bx,ErrorTab-2 ; BX <- error table - 2
BIOSError2: inc bx ; skip BIOS error code
inc bx ; skip DOS error code
cmp ah,[cs:bx] ; check BIOS error code
je BIOSError4 ; found error code
cmp byte [cs:bx],0 ; was it last code?
jne BIOSError2 ; check next error code
BIOSError4: mov al,[cs:bx+1] ; AL <- DOS error code
pop bx ; pop BX
stc ; set errorflag
ret
; ----------------------------------------------------------------------------
; Call Int 13h interrupt with saving registers 1
; ----------------------------------------------------------------------------
; ------------- Push registers
Int131: push si ; push SI
push di ; push DI
push bp ; push BP
push ds ; push DS
push es ; push ES
; ------------- Call INT 13h
stc ; preset error flag (due to some BIOSes)
int 13h ; call INT 13h
sti ; enable interrupt (due to some BIOSes)
; ------------- Pop registers
pop es ; pop ES
pop ds ; pop DS
pop bp ; pop BP
pop di ; pop DI
pop si ; pop SI
ret
; ----------------------------------------------------------------------------
; Call Int 13h interrupt with saving registers
; ----------------------------------------------------------------------------
; ------------- Push registers
Int13: push bx ; push BX
push cx ; push CX
push dx ; push DX
; ------------- Call INT 13h
call Int131 ; call int 13h
; ------------- Pop registers
pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
ret
; ----------------------------------------------------------------------------
; Data
; ----------------------------------------------------------------------------
; ------------- Disk setup
DiskNum: db 0 ; total number of disk drives
DiskFlags: db 0 ; disk flags
; bit 0: use phantom disk
PhantText: db CR,LF,'Insert diskette for drive '
PhantTextA: db 'A: and press any key when ready',CR,LF,LF,0
; ------------- Disk parameter tables
DiskParBPBCurr: dw DiskParBPB ; pointer to current disk table
DiskParFlags: times 2 db 0 ; parameters (only floppy)
; bit 0: changeline supported
DiskParAccess: times 2 dd -1 ; last access time (only floppy)
; ------------- Current disk
DiskDriveFnc:
DiskDrive: db 0 ; current disk drive (0=A:...80h=C:)
DiskFnc: db 2 ; disk function code (2=read, 3=write)
; ------------- BIOS disk error codes
ErrorTab: db BIOS_WRITE, ERR_WRITE ; write fault
db BIOS_READY, ERR_READY ; timeout (not ready)
db BIOS_SEEK, ERR_SEEK ; seek failed
db BIOS_CONTROL, ERR_READY ; controller failed
db BIOS_CRC, ERR_CRC ; CRC error
db BIOS_DMA, ERR_CRC ; DMA overrun
db BIOS_CHANGE, ERR_CHANGE ; disk changed
db BIOS_SECTOR, ERR_SECTOR ; sector not found
db BIOS_PROT, ERR_PROT ; write-protection
db BIOS_ADDRESS, ERR_SECTOR; bad address mark
db 0, ERR_GENERAL ; unknown error (must be last!)
|