; ============================================================================
;
; LT-DOS - Disk device
;
; ============================================================================
SECTOR_SIZE EQU 512 ; sector size
VOLUME_SIZE EQU 11 ; length of volume label
FATNAME_SIZE EQU 8 ; length of filesystem name
REPEAT EQU 2 ; number of tries on read/write error
; ------------- 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 (0 if > 0FFFFh)
BPB_media: resb 1 ; 0Ah: media descriptor (0=unknown)
BPB_fatsec: resw 1 ; 0Bh: sectors per FAT
BPB_trksec: resw 1 ; 0Dh: sectors per track
BPB_heads: resw 1 ; 0Fh: number of heads
BPB_hidden: resd 1 ; 11h: hidden sectors
BPB_total2: resd 1 ; 15h: total sectors (if BPB_total = 0)
endstruc
BPB_SIZE equ BPB_size ; 19h = 25 bytes
; ------------- Drive Parameter Block, DPB
struc DPB
DPB_next: resd 1 ; 0: addres of next DPB
; (offset -1=last DPB)
DPB_hwdisk: resb 1 ; 4: physical unit number (for INT 13h)
DPB_disk: resb 1 ; 5: logical drive number (0=A:)
DPB_BPB: ; --- BIOS Parameter Block (BPB)
DPB_sector: resw 1 ; 6: bytes per sector
DPB_cluster: resb 1 ; 8: sectors per cluster (0FFh=unknown)
DPB_res: resw 1 ; 9: number of reserved sectors (BOOT)
DPB_fat: resb 1 ; 0Bh: number of FATs
DPB_root: resw 1 ; 0Ch: number of ROOT entries
DPB_total: resw 1 ; 0Eh: total sectors (0 if > 0FFFFh)
DPB_media: resb 1 ; 10h: media descriptor (0=unknown)
DPB_fatsec: resw 1 ; 11h: sectors per FAT
DPB_trksec: resw 1 ; 13h: sectors per track
DPB_heads: resw 1 ; 15h: number of heads
DPB_hidden: resd 1 ; 17h: hidden sectors
DPB_total2: resd 1 ; 1Bh: total sectors (if DPB_total = 0)
; ---
DPB_FATtype: resb 1 ; 1Fh: FAT type (see below)
DPB_open: resw 1 ; 20h: device-open count
DPB_mediatype: resb 1 ; 22h: media type (see below)
DPB_flags: resw 1 ; 23h: flags (see below)
DPB_tracks: resw 1 ; 25h: number of cylinders (harddisk)
DPB_defBPB: ; --- BPB for default (highest) capacity
DPB_defsector: resw 1 ; 27h: default bytes per sector
DPB_defclust: resb 1 ; 29h: default sectors per cluster
DPB_defres: resw 1 ; 2Ah: default reserved sectors (BOOT)
DPB_deffat: resb 1 ; 2Ch: default number of FATs
DPB_defroot: resw 1 ; 2Dh: default number of ROOT entries
DPB_deftotal: resw 1 ; 2Fh: default total sectors <= 0FFFFh
DPB_defmedia: resb 1 ; 31h: default media descriptor
DPB_deffatsec: resw 1 ; 32h: default sectors per FAT
DPB_deftrksec: resw 1 ; 34h: default sectors per track
DPB_defheads: resw 1 ; 36h: default number of heads
DPB_defhidden: resd 1 ; 38h: default hidden sectors
DPB_deftotal2: resd 1 ; 3Ch: default total sectors > 0FFFFh
resb 6 ; 40h: ...reserved for BPB
; ---
DPB_track: resb 1 ; 46h: last track accessed
; only for removable media
DPB_time: ; 47h: (4) time of last access,-1=never
; only for fixed media
DPB_part: ; 47h: (2) partition type (see below)
DPB_start: ; 49h: (2) cylinder of start of part.
resd 1
DPB_label: resb VOLUME_SIZE ; 4Bh: volume label ('NO NAME' = none)
resb 1 ; 56h: terminating 0 for volume label
DPB_serial: ; (57h: (4) serial number)
DPB_serdate: resw 1 ; 57h: serial number - date
DPB_sertime: resw 1 ; 59h: serial number - time
DPB_fatname: resb FATNAME_SIZE ; 5Bh: name of filesystem ('FAT12')
resb 1 ; 63h: terminating 0 for filesys. type
DPB_offset: resd 1 ; 64h: offset of start of partition
endstruc
DPB_SIZE EQU DPB_size ; 64h = 100 bytes
; ------------- Device parameter block FAT types
FAT_16 EQU B6 ; 40h = FAT16 instead of FAT12
FAT_NO EQU B7 ; 80h = unsupportable FAT disk
; ------------- Device parameter block media types
MEDIA_360K EQU 0 ; 320 KB, 360 KB
MEDIA_1M2 EQU 1 ; 1.2MB
MEDIA_720K EQU 2 ; 720KB
MEDIA_SD8 EQU 3 ; single-density SD 8"
MEDIA_DD8 EQU 4 ; double-density DD 8"
MEDIA_HD EQU 5 ; hard disk
MEDIA_TAPE EQU 6 ; tape drive
MEDIA_1M44 EQU 7 ; other type (1.44 MB 3.5")
MEDIA_OPTICAL EQU 8 ; R/W optical disk
MEDIA_2M88 EQU 9 ; 2.88 MB 3.5"
; ------------- Device parameter block flags
FLAG_FIXED EQU B0 ; 0001h = fixed media (no exchange)
FLAG_CHANGELINE EQU B1 ; 0002h = changeline supported
FLAG_LOCKED EQU B2 ; 0004h = current BPB locked
FLAG_SAMESIZE EQU B3 ; 0008h = all sectors in a track are
; the same size
FLAG_MULTI EQU B4 ; 0010h = physical drive has multiple
; logical units
FLAG_CURRENT EQU B5 ; 0020h = current logical drive for
; shared physical drive
FLAG_CHANGE EQU B6 ; 0040h = disk change detected
FLAG_PARCHANGE EQU B7 ; 0080h = device parameters changed
FLAG_REFORMAT EQU B8 ; 0100h = disk reformated (BPB changed)
FLAG_NOACCESS EQU B9 ; 0200h = access flag (fixed media
; only, disables reads+writes)
FLAG_LBA EQU B10 ; 0400h = use LBA mode
; --- shortcuts to use HIGH byte of flags
FLAG_REFORMAT2 EQU FLAG_REFORMAT/256 ; 0100h = disk reformated
FLAG_NOACCESS2 EQU FLAG_NOACCESS/256 ; 0200h = access flag
FLAG_LBA2 EQU FLAG_LBA/256 ; 0400h = use LBA mode
; ------------- Device parameter block partition type
PART_PRIMARY EQU 0ffffh ; primary partition
PART_EXTENDED EQU 1 ; extended partition
; ------------- Disk Address Packet, DAP (used for LBA transfer)
struc DAP
DAP_len: resb 1 ; 0: length of packet (16)
DAP_res: resb 1 ; 1: ... reserved (set to 0)
DAP_num: resw 1 ; 2: number of sectors (1 to 127)
DAP_buff: resd 1 ; 4: address of transfer buffer
DAP_start: resd 2 ; 8: starting sector (0...)
endstruc
DAP_SIZE EQU DAP_size ; 10h = 16 bytes
; ------------- Boot Block
struc BOOT
BOOT_jump: resb 3 ; 0: jump opcode
BOOT_OEM: resb 8 ; 3: OEM identifier
BOOT_BPB: ; --- BIOS Parameter Block (BPB)
BOOT_sector: resw 1 ; 0Bh: bytes per sector
BOOT_cluster: resb 1 ; 0Dh: sectors per cluster (0FFh=unknown)
BOOT_res: resw 1 ; 0Eh: number of reserved sectors (BOOT)
BOOT_fat: resb 1 ; 10h: number of FATs
BOOT_root: resw 1 ; 11h: number of ROOT entries
BOOT_total: resw 1 ; 13h: total sectors (0 if > 0FFFFh)
BOOT_media: resb 1 ; 15h: media descriptor (0=unknown)
BOOT_fatsec: resw 1 ; 16h: sectors per FAT
BOOT_trksec: resw 1 ; 18h: sectors per track
BOOT_heads: resw 1 ; 1Ah: number of heads
BOOT_hidden: resd 1 ; 1Ch: hidden sectors
BOOT_total2: resd 1 ; 20h: total sectors (if DPB_total = 0)
; ---
BOOT_disk: resb 1 ; 24h: disk (0=floppy, 0x80=hard disk)
BOOT_head: resb 1 ; 25h: head
BOOT_ext: resb 1 ; 26h: extension head magic (=29h)
BOOT_serial: resd 1 ; 27h: disk serial number
BOOT_volume: resb VOLUME_SIZE ; 2Bh: volume label ('NO NAME' = none)
BOOT_fatname: resb FATNAME_SIZE ; 36h: name of filesystem ('FAT12')
endstruc
BOOT_SIZE EQU BOOT_size ; 3Eh = 62
; ------------- Function codes for read/write
DSK_READ EQU 2 ; read
DSK_WRITE EQU 3 ; write
DSK_VERIFY EQU 4 ; verify after write (for internal use)
DSK_WRITEVERIFY EQU 5 ; write with verify
; ------------- 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 service tables
; ----------------------------------------------------------------------------
; ------------- DISK service table
FNCDSK: db 12 ; number of functions
dw DSKInit ; 0 init
dw DSKCheck ; 1 media check
dw DSKBuild ; 2 build BPB
dw DevIntInv ; 3 IOCTL input
dw DSKRData ; 4 read data
dw DevIntBusy ; 5 test read
dw DevIntOK ; 6 input status
dw DevIntOK ; 7 flush input
dw DSKWData ; 8 write data
dw DSKWVData ; 9 write with verify
dw DevIntOK ; 10 output status
dw DevIntOK ; 11 flush output
; ----------------------------------------------------------------------------
; Device interrupt service - Driver init
; ----------------------------------------------------------------------------
; INPUT: DS = data segment
; OUTPUT: AX = status word
; DESTROYS: DS, BX
; ----------------------------------------------------------------------------
DSKInit: lds bx,[Strat] ; DS:BX <- strategy address
mov al,[cs:TotalLogDisk] ; Al <- number of logical disks
mov [bx+13],al ; number of logical disks
mov word [bx+18],DiskBPBTab ; table of disk BPB address
mov [bx+20],cs ; this segment
mov ah,S_DONE ; status OK
ret
; ----------------------------------------------------------------------------
; Device interrupt service - Media check
; ----------------------------------------------------------------------------
; INPUT: AL = disk unit
; OUTPUT: AX = status word
; DESTROYS: DS, BX, CX, SI
; ----------------------------------------------------------------------------
; ------------- Find disk table (-> DS:SI)
DSKCheck: call FindLogDisk ; find logical disk
jnc DSKCheck1 ; disk found OK
jmp DevIntUnit ; invalid unit
; ------------- Check media change state (-> AL)
DSKCheck1: call MediaChange ; check media change state
; ------------- Reset flags
and word [si+DPB_flags],~(FLAG_REFORMAT+FLAG_CHANGE)
; ------------- Store status and label address
mov cx,ds ; CX <- segment of disk table
lea si,[si+DPB_label] ; SI <- offset of disk label
lds bx,[cs:Strat] ; DS:BX <- strategy address
mov [bx+14],al ; store status
cmp al,CHANGE_YES ; media changed?
jne DSKCheck2 ; media not changed
mov [bx+15],si ; offset of disk label
mov [bx+17],cx ; segment of disk label
mov byte [cs:CurrLogDisk],-1 ; invalidate current disk
DSKCheck2: mov ah,S_DONE ; status OK
ret
; ----------------------------------------------------------------------------
; Device interrupt service - Build BPB
; ----------------------------------------------------------------------------
; INPUT: AL = disk unit
; OUTPUT: AX = status word
; DESTROYS: DS, BX, CX, SI
; ----------------------------------------------------------------------------
; ------------- Find disk table (-> DS:SI)
DSKBuild: call FindLogDisk ; find logical disk
jnc DSKBuild1 ; disk found OK
jmp DevIntUnit ; invalid unit
; ------------- Check whether it is fixed disk
DSKBuild1: mov ah,[es:di] ; AH <- media descriptor
test byte [si+DPB_flags],FLAG_FIXED ; is it fixed disk?
jnz DSKBuild8 ; it is fixed disk
; ------------- Reset extended media parameters (serial, label, filesystem)
call ResetExt ; reset extended parameters
; ------------- Init media parameters if media was changed
call LoadBPB ; load BPB table of disk
; ------------- Set parameters
DSKBuild8: mov cx,ds ; CX <- segment of DPB
lea si,[si+DPB_BPB] ; SI <- current BPB table
lds bx,[cs:Strat] ; DS:BX <- strategy address
mov [bx+13],ah ; media descriptor
mov [bx+18],si ; offset of BPB table
mov [bx+20],cx ; segment of BPB table
mov ah,S_DONE ; status OK
ret
; ----------------------------------------------------------------------------
; Device interrupt service - Read data
; ----------------------------------------------------------------------------
; INPUT: AL = disk unit
; CX = number of sectors
; ES:DI = data buffer
; OUTPUT: AX = status word
; DESTROYS: DS, BX, CX, SI
; ----------------------------------------------------------------------------
; ------------- Command - read (-> AH)
DSKRData: mov ah,DSK_READ ; AH <- "read" command
jmp short DSKData
; ----------------------------------------------------------------------------
; Device interrupt service - Write data
; ----------------------------------------------------------------------------
; INPUT: AL = disk unit
; CX = number of sectors
; ES:DI = data buffer
; OUTPUT: AX = status word
; DESTROYS: DS, BX, CX, SI
; ----------------------------------------------------------------------------
; ------------- Command - write (-> AH)
DSKWData: mov ah,DSK_WRITE ; AH <- "write" command
jmp short DSKData
; ----------------------------------------------------------------------------
; Device interrupt service - Write data with verify
; ----------------------------------------------------------------------------
; INPUT: AL = disk unit
; CX = number of sectors
; ES:DI = data buffer
; OUTPUT: AX = status word
; DESTROYS: DS, BX, CX, SI
; ----------------------------------------------------------------------------
; ------------- Command - write with verify (-> AH)
DSKWVData: mov ah,DSK_WRITEVERIFY ; AH <- "write+verify" command
; ------------- Find disk table (-> DS:SI)
DSKData: call FindLogDisk ; find logical disk
jnc DSKData2 ; disk found OK
jmp DevIntUnit ; invalid unit
; ------------- Disk cannot be accessed
DSKData2: test byte [si+DPB_flags+1],FLAG_NOACCESS2 ; disk enabled?
jz DSKData3 ; disk can be accessed OK
mov al,ERR_MEDIA ; unknown media
DSKData22: jmp DevIntError ; error
; ------------- No data to transfer
DSKData3: jcxz DSKData9 ; no sectors required
mov bx,di ; BX <- offset of data buffer
mov di,cx ; DI <- number of sectors
; ------------- Prepare starting sector (-> DX:CX)
push si ; push SI
push ds ; push DS
lds si,[cs:Strat] ; DS:SI <- strategy address
xor dx,dx ; DX <- starting sector HIGH
mov cx,[si+DDR_start] ; CX <- starting sector LOW
cmp cx,0ffffh ; use large sectors?
jne DSKData4 ; no, use 16-bit addressing
mov cx,[si+DDR_start2] ; CX <- starting sector LOW
mov dx,[si+DDR_start2+2] ; DX <- starting sector HIGH
DSKData4: pop ds ; pop DS
pop si ; pop SI
; ------------- Prepare total sectors (-> BP:AX)
push ax ; push AX
push si ; push SI
call GetBPB ; get BPB of disk
xor bp,bp ; BP <- total sectors HIGH
mov ax,[si+BPB_total] ; AX <- total sectors LOW
or ax,ax ; is it valid number?
jnz DSKData5 ; disk size is valid
mov ax,[si+BPB_total2] ; AX <- total sectors LOW
mov bp,[si+BPB_total2+2] ; BP <- total sectors HIGH
; ------------- Number of sectors to end of disk (-> BP:AX)
DSKData5: sub ax,cx ; AX <- remaining sectors LOW
sbb bp,dx ; DX <- remaining sectors HIGH
jne DSKData6 ; disk overflow or enough space
; ------------- Check number of sectors
cmp ax,di ; check number of sectors
DSKData6: pop si ; pop SI
pop ax ; pop AX
jae DSKData6 ; number of sectors is OK
mov al,ERR_SECTOR ; sector not found
jmp short DSKData22 ; error
; ------------- Check, if it is removable media
DSKData7: test byte [si+DPB_flags],FLAG_FIXED ; is it fixed disk?
jnz DSKData8 ; it is fixed disk
; ------------- Change phantom disk
call SetPhantom ; change phantom disk
; ------------- Update diskette parameters
call UpdateDisk ; update diskette parameters
; ------------- Set new diskette parameters
push ax ; push AX
push si ; push SI
mov al,[cs:Int1ESect] ; AL <- sectors per track
mov [cs:Old1ESect],al ; save sectors per track
call GetBPB ; get BPB of disk
mov al,[si+BPB_trksec] ; AL <- sectors per track
mov [cs:Int1ESect],al ; new sectors per track
pop si ; pop SI
pop ax ; pop AX
; ------------- Do disk transfer
DSKData8: call ReadWrite ; read/write/verify data
; ------------- Return old diskette parameters
pushf ; push flags
test byte [si+DPB_flags],FLAG_FIXED ; is it fixed disk?
jnz DSKData82 ; it is fixed disk
push ax ; push AX
mov al,[cs:Old1ESect] ; old sectors per track
mov [cs:Int1ESect],al ; restore sectors per track
pop ax ; pop AX
DSKData82: popf ; pop flags
jmp short DSKData22 ; error
; ------------- Status OK
DSKData9: mov ah,S_DONE ; status OK
ret
; ----------------------------------------------------------------------------
; Find DPB of logical disk
; ----------------------------------------------------------------------------
; INPUT: AL = logical disk
; OUTPUT: DS:SI = disk table
; CY = disk not found
; ----------------------------------------------------------------------------
FindLogDisk: lds si,[cs:DiskTabAddr] ; DS:SI <- address of first table
FindLogDisk2: cmp al,[si+DPB_disk] ; is it right disk?
je FindLogDisk3 ; it is right disk
lds si,[si] ; DS:SI <- addres of next table
cmp si,byte -1 ; was it last table?
jne FindLogDisk2 ; it was last table
stc ; error flag - disk not found
FindLogDisk3: ret
; ----------------------------------------------------------------------------
; Change phantom disk
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; ----------------------------------------------------------------------------
; ------------- Check, if there is need to change disk
SetPhantom: test byte [si+DPB_flags],FLAG_CURRENT+FLAG_FIXED ; test flags
jnz SetPhantom9 ; it is current disk or fixed disk
test byte [si+DPB_flags],FLAG_MULTI ; test multi flag
jz SetPhantom9 ; this disk has not multiple units
; ------------- Push registers
push ax ; push AX
; ------------- Push registers - current disk table
push si ; push SI
push ds ; push DS
; ------------- Set flag - disk has been changed
or byte [si+DPB_flags],FLAG_CHANGE ; disk changed
; ------------- Shared physical disk (-> AL)
mov al,[si+DPB_hwdisk] ; AL <- shared physical disk
; ------------- Prepare for first disk table (-> DX:SI)
push cs ; push CS
pop ds ; DS <- CS
mov si,DiskTabAddr ; SI <- addres of first table
; ------------- Next disk table (-> DS:SI)
SetPhantom2: lds si,[si] ; DS:SI <- addres of next table
; ------------- Check, if it was last disk
cmp si,byte -1 ; was it last table?
je SetPhantom7 ; it was last table
; ------------- Check, if it is shared physical disk
cmp al,[si+DPB_hwdisk] ; is it shared physical disk?
jne SetPhantom2 ; it is not shared physical disk
; ------------- Check, if it is current logical disk
test byte [si+DPB_flags],FLAG_CURRENT ; is it current disk?
jz SetPhantom2 ; it is not current disk
; ------------- Reset current disk flag
and byte [si+DPB_flags],~FLAG_CURRENT ; reset current flag
or byte [si+DPB_flags],FLAG_CHANGE ; disk changed
; ------------- Set current disk flag on original disk
pop ds ; pop DS
pop si ; pop SI
or byte [si+DPB_flags],FLAG_CURRENT ; set current flag
; ------------- Temporaly don't ask to change phantom disk
cmp byte [cs:TempPhantOff],TRUE ; don't ask to change disk?
je SetPhantom8 ; don't ask to change phantom disk
; ------------- Push registers - current disk table
push si ; push SI
push ds ; push DS
; ------------- Get logical disk (-> AL)
mov al,[si+DPB_disk] ; AL <- logical disk
; ------------- Change current phantom disk, if using 2 phantom disks
cmp byte [cs:PhantomDisks],2 ; using 2 phantom disks?
jne SetPhantom6 ; not using 2 phantom disks
; ------------- Test logical disk
mov ah,al ; AH <- logical disk
xor si,si ; SI <- 0
mov ds,si ; DS <- 0
xchg ah,[CurPhantom] ; exchange current phantom disk
cmp al,ah ; is it same disk?
je SetPhantom7 ; disk is already set
; ------------- Display message to change phantom disk
SetPhantom6: push cs ; push CS
pop ds ; DS <- CS
add al,"A" ; AL <- ASCII name of disk
mov [PhantomTextA],al ; store phantom disk name
mov si,PhantomText ; SI <- message to change disk
call DispText ; display message
; ------------- Wait for key input
call CONRFlush ; flush keyboard input
call CONRChar ; input character from keyboard
; ------------- Pop registers - current disk table
SetPhantom7: pop ds ; pop DS
pop si ; pop SI
; ------------- Pop registers
SetPhantom8: pop ax ; pop AX
SetPhantom9: ret
; ----------------------------------------------------------------------------
; Test disk change (without reseting state)
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; OUTPUT: AL = change state (CHANGE_UNKNOWN, CHANGE_NO, CHANGE_YES)
; DESTROYS: AH
; ----------------------------------------------------------------------------
; ------------- Push registers
DiskChange: push dx ; push DX
; ------------- Fixed disk cannot be changed
test byte [si+DPB_flags],FLAG_FIXED ; is it fixed disk?
jnz DiskChange8 ; medium has not been changed
; ------------- Change phantom disk
call SetPhantom ; change phantom disk
; ------------- Test last changeline state
test byte [si+DPB_flags],FLAG_CHANGE ; disk changed?
jnz DiskChange7 ; disk has been changed
; ------------- Check, if changeline is supported
test byte [si+DPB_flags],FLAG_CHANGELINE ; changeline?
jnz DiskChange3 ; changeline is supported
; ------------- Changeline not supported - test last access time
call TestDiskTime ; check last access time
jc DiskChange8 ; disk has not been changed
mov al,CHANGE_UNKNOWN ; unknown state
jmp short DiskChange9 ; unknown state
; ------------- Test changeline state
DiskChange3: mov dl,[si+DPB_hwdisk]; DL <- physical disk
mov ah,22 ; AH <- function code
call Int13 ; test changeline state
jnc DiskChange8 ; disk has not been changed
cmp ah,6 ; change flag?
jne DiskChange7 ; not changed
or byte [si+DPB_flags],FLAG_CHANGE ; disk changed
; ------------- Disk has been changed
DiskChange7: mov al,CHANGE_YES ; disk has been changed
jmp short DiskChange9
; ------------- Disk has not been changed
DiskChange8: mov al,CHANGE_NO ; disk has not been changed
; ------------- Pop registers
DiskChange9: pop dx ; pop DX
ret
; ----------------------------------------------------------------------------
; Test media change (without reseting state)
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; OUTPUT: AL = change state (CHANGE_UNKNOWN, CHANGE_NO, CHANGE_YES)
; DESTROYS: AH
; ----------------------------------------------------------------------------
; ------------- Push registers
MediaChange: push cx ; push CX
push dx ; push DX
; ------------- Disk was reformated
test byte [si+DPB_flags+1],FLAG_REFORMAT2 ; reformated?
jnz MediaChange7 ; media has been changed
; ------------- Test disk change
call DiskChange ; test disk change
cmp al,CHANGE_UNKNOWN ; unknown state?
jne MediaChange9 ; dsk change is known
; ------------- Load serial number
call LoadSerial ; load serial number
jc MediaChange6 ; error
; ------------- Check disk serial number
cmp cx,[si+DPB_serial] ; check serial LOW
jne MediaChange7 ; media has been changed
cmp dx,[si+DPB_serial+2] ; check serial HIGH
jne MediaChange7 ; media has been changed
MediaChange6: mov al,CHANGE_UNKNOWN ; unknown state
jmp short MediaChange9
; ------------- Media has been changed
MediaChange7: mov al,CHANGE_YES ; media has been changed
jmp short MediaChange9
; ------------- Media has not been changed
MediaChange8: mov al,CHANGE_NO ; media has not been changed
; ------------- Pop registers
MediaChange9: pop dx ; pop DX
pop cx ; pop CX
ret
; ----------------------------------------------------------------------------
; Test time of access to removable disk (without changeline)
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; OUTPUT: CY = medium has not been changed (else unknown state)
; ----------------------------------------------------------------------------
; ------------- Only for removable media without changeline
TestDiskTime: test byte [si+DPB_flags],FLAG_FIXED+FLAG_CHANGELINE
stc ; set flag - not changed
jnz TestDiskTime4 ; fixed disk or supports changeline
; ------------- Push registers
push cx ; push CX
push dx ; push DX
; ------------- Load system time (-> CX:DX)
call GetTimer ; load system timer
; ------------- Time interval
sub dx,[si+DPB_time] ; time LOW
sbb cx,[si+DPB_time+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
TestDiskTime4: ret
; ----------------------------------------------------------------------------
; Set time of access to removable disk (without changeline)
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; ----------------------------------------------------------------------------
; ------------- Only for removable media without changeline
SetDiskTime: test byte [si+DPB_flags],FLAG_FIXED+FLAG_CHANGELINE
jnz SetDiskTime2 ; fixed disk or supports changeline
; ------------- Push registers
push cx ; push CX
push dx ; push DX
; ------------- Load system time (-> CX:DX)
call GetTimer ; load system timer
; ------------- Store new time of disk access
mov [si+DPB_time],dx ; push time LOW
mov [si+DPB_time+2],cx ; push time HIGH
; ------------- Pop registers
pop dx ; pop DX
pop cx ; pop CX
SetDiskTime2: ret
; ----------------------------------------------------------------------------
; Test Int 13h LBA extensions
; ----------------------------------------------------------------------------
; INPUT: DL = disk number
; OUTPUT: CY = no LBA extensions
; ----------------------------------------------------------------------------
; ------------- Push registers
TestLBA: push ax ; push AX
push bx ; push BX
push cx ; push CX
push dx ; push DX
; ------------- Check LBA extensions
mov ah,41h ; AH <- function code
mov bx,55aah ; BX <- magic number
int 13h ; check LBA extensions
jc TestLBA2 ; error
cmp bx,0aa55h ; check magic
je TestLBA2 ; magic is OK
stc ; error
; ------------- Pop registers
TestLBA2: pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
pop ax ; pop AX
ret
; ----------------------------------------------------------------------------
; Get BPB of disk
; ----------------------------------------------------------------------------
; INPUT: DS:SI = DPB disk table (default for harddrive, else current)
; OUTPUT: SI = BPB
; ----------------------------------------------------------------------------
GetBPB: test byte [si+DPB_flags],FLAG_FIXED ; is it harddisk?
lea si,[si+DPB_BPB] ; current BPB
jz GetBPB2 ; it is not harddisk
lea si,[si+DPB_defBPB-DPB_BPB] ; default BPB for harddisk
GetBPB2: ret
; ----------------------------------------------------------------------------
; Transfer LBA address to CHS address
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; DX:CX = absolute sector
; 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, on AWARD BIOS 0 to 63)
; on AWARD BIOS: bit 6 - 7: cylinder (bits 10 to 11)
; DL = disk number
; CONDITIONS: sectors per track: max. 63
; heads per cylinder: max. 256 (or 64 on AWARD BIOS)
; cylinders: max. 1024 (or 4096 on AWARD BIOS)
; ----------------------------------------------------------------------------
; ------------- Push registers
LBAtoCHS: push ax ; push AX
push si ; push SI
; ------------- Prepare BPB pointer (using default BPB for hard drive)
call GetBPB ; prepare BPB
; ------------- Prepare number of sectors per cylinder (-> AX)
push dx ; push DX
mov ax,[si+BPB_trksec] ; AX <- sectors per track
mul word [si+BPB_heads] ; AX <- sectors per cylinder
pop dx ; pop DX
; ------------- Calculate cylinder number (->AX) and sector in cylinder (->DX)
xchg ax,cx ; AX <- sector LOW, CX <- sec.per cyl.
div cx ; AX <- cylinder, DX <- sector in cyl.
; ------------- Prepare cylinder number (-> CX, AH)
xchg al,ah ; AH <- cylinder LOW, AL <- cyl. HIGH
ror al,1
ror al,1 ; AL <- bits 8 - 9 to position 6 - 7
mov ch,al ; CH <- cylinder number HIGH
ror ch,1
ror ch,1 ; CH <- bits 10 - 11 to position 6 - 7
and al,B7+B6 ; mask bits 6 and 7 of cylinder HIGH
and ch,B7+B6 ; mask bits 6 and 7 of cylinder HIGH 2
xchg ax,cx ; CX <- cylinder, AH <- cylinder HIGH 2
; ------------- Calculate head (-> DH) and sector (-> CL)
xchg ax,dx ; AX <- sector in cylinder, DH <- cyl.
div byte [si+BPB_trksec] ; AL <- head, AH <- sector
inc ah ; AH <- sector + 1
or cl,ah ; CL <- cylinder HIGH + sector
or dh,al ; DH <- head number + cylinder HIGH 2
; ------------- Pop registers
pop si ; pop SI
pop ax ; pop AX
; ------------- Disk number (-> DL)
mov dl,[si+DPB_hwdisk] ; DL <- disk
ret
; ----------------------------------------------------------------------------
; Low-level read/write/verify data from/to disk (with repeat on error)
; ----------------------------------------------------------------------------
; INPUT: AH = function code DSK_READ(2)=read, DSK_WRITE(3)=write
; DSK_WRITEVERIFY(5)=write with verify
; DS:SI = disk table
; DX:CX = starting sector on partition
; DI = number of sectors (minimal 1)
; ES:BX = transfer buffer
; OUTPUT: CY = error
; AH = DOS error code (valid if CY, else undefined)
; DX:CX = new sector
; DI = number of remaining sectors (0 if NC)
; ES:BX = new transfer buffer (normalized)
; DESTROYS: AL
; ----------------------------------------------------------------------------
; NOTE: If error occurs when verifying written sectors it does no other
; attempt to write data (as it is usual in DOS) - user should be notified
; about not-trusted media and let her/him decide to try next attempt.
; ----------------------------------------------------------------------------
; ------------- Push registers
ReadWriteLBA: push bp ; push BP
; ------------- Prepare error counter
ReadWriteLBA0: mov bp,REPEAT ; BP <- number of repetition
; ------------- Next try on error
ReadWriteLBA1: push ax ; push AX
push bx ; push BX
push cx ; push CX
push dx ; push DX
push si ; push SI
push di ; push DI
push ds ; push DS
; ------------- Prepare function code
cmp ah,DSK_WRITEVERIFY ; write before verify?
jne ReadWriteLBA2 ; no
mov ah,DSK_WRITE ; substitute with write
; ------------- Add hidden sectors
ReadWriteLBA2: push si ; push SI
call GetBPB ; SI <- get BPB of disk
add cx,[si+BPB_hidden] ; CX <- start sector LOW
adc dx,[si+BPB_hidden+2]; DX <- start sectors HIGH
pop si ; pop SI
; ------------- Limit number of sectors to 127 (due to some BIOSes)
cmp di,127 ; max. number of sectors
jbe ReadWriteLBA3 ; number of sectors is OK
mov di,127 ; limit number of sectors
; ------------- Test if using LBA
ReadWriteLBA3: test byte [si+DPB_flags+1],FLAG_LBA2 ; using LBA mode?
jnz ReadWriteLBA5 ; using LBA mode
; ------------- Translate LBA address to CHS address
call LBAtoCHS ; translate to CHS address
; ------------- Prepare number of sectors in one operation (-> AL)
push bx ; push BX
xchg ax,bx ; BH <- function code
xchg ax,di ; AL <- number of sectors
mov ah,bh ; AH <- function code
mov bl,cl ; BL <- sector number
and bl,3Fh ; mask sector number (1 to 63)
dec bx ; BL = sector number (0 to 62)
call GetBPB ; get BPB of disk
mov bh,[si+BPB_trksec] ; BH <- sectors per track
sub bh,bl ; BH <- sectors to end of track
cmp al,bh ; test number of sectors
jbe ReadWriteLBA4 ; number of sectors is OK
mov al,bh ; AL <- limit number of sectors
ReadWriteLBA4: pop bx ; pop BX
; ------------- Do operation
push ax ; push AX (AL=number of sectors)
jmp short ReadWriteLBA6
; ------------- Address of DAP structure (-> DS:SI)
ReadWriteLBA5: mov dl,[si+DPB_hwdisk] ; DL <- disk
push cs
pop ds ; DS <- CS
mov si,DAPTab ; SI <- disk address packet
; ------------- Prepared DAP structure
mov byte [si+DAP_len],DAP_SIZE ; length of packet
mov [si+DAP_num],di ; number of sectors
mov [si+DAP_buff],bx ; transfer buffer LOW
mov [si+DAP_buff+2],es ; transfer buffer HIGH
mov [si+DAP_start],cx ; starting sector LOW
mov [si+DAP_start+2],dx ; starting sector HIGH
xor cx,cx ; CX <- 0
mov [si+DAP_start+4],cx ; starting sector
mov [si+DAP_start+6],cx ; starting sector
; ------------- Transfer data (-> AL number of sectors, -> AH error code)
push di ; push DI, number of required sectors
add ah,40h ; AH <- function code
mov al,0 ; AL <- write flags
ReadWriteLBA6: call Int13 ; transfer data
xchg ax,bx ; BH <- error
pop ax ; pop AX, number of required sectors
mov ah,bh ; AH <- error code
; jnc ReadWriteLBA7 ; operation OK
; call BIOSError ; konvert do DOS error (sets CY)
; stc
; !!!!!!!!!!!!!!!!!!! Mapování chyby BIOS -> DOS má být asi do AL !!!!!!!!!!!!!
; ------------- Pop registers (AL = sectors, AH = error, CY = error)
ReadWriteLBA7: pop ds ; pop DS
pop di ; pop DI
pop si ; pop SI
pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
; ------------- Repeate on error
jnc ReadWriteLBA8 ; operation OK
cmp ah,ERR_CHANGE ; medium changed?
jne ReadWriteLBA70 ; it is not changed
or byte [si+DPB_flags],FLAG_CHANGE ; disk changed
ReadWriteLBA70: stc ; set error flag
ReadWriteLBA71: dec bp ; error counter
jz ReadWriteLBA9 ; no other attempt (CY is set)
; ------------- Reset disk and try again
call DiskReset ; disk reset
ReadWriteLBA72: pop ax ; pop AX (function code)
jmp ReadWriteLBA1 ; next attempt
; ------------- Go to verify after write
ReadWriteLBA8: mov ah,0 ; AX = number of sectors
xchg ax,bp ; BP <- number of sectors
pop ax ; AH <- function code
cmp ah,DSK_WRITEVERIFY ; write before verify?
jne ReadWriteLBA82 ; no
mov ah,DSK_VERIFY ; verify code
ReadWriteLBA81: jmp ReadWriteLBA0 ; verify sectors
; ------------- Shift registers
ReadWriteLBA82: push ax ; push AX (function code)
xchg ax,bp ; AX <- number of sectors
add cx,ax ; shift starting sector LOW
adc dx,byte 0 ; shift starting sector HIGH
sub di,ax ; decrease remaining sectors
push dx ; push DX
push si ; push SI
call GetBPB ; SI <- get BPB of disk
mul word [si+BPB_sector] ; * 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
; ------------- Switch from verify to write
cmp ah,DSK_VERIFY ; is it verify after write?
jne ReadWriteLBA84 ; no
mov ah,DSK_WRITEVERIFY ; switch to write before verify
; ------------- Next group of sectors (and set NC)
ReadWriteLBA84: or di,di ; all sectors are transfered?
jnz ReadWriteLBA81 ; transfer next group of sectors
push ax ; push AX
; ------------- Prepare registers
ReadWriteLBA9: pop bp ; destroy AX
pop bp ; pop BP
ret
; ----------------------------------------------------------------------------
; read/write data from/to disk (with DMA check and change phantom disk)
; ----------------------------------------------------------------------------
; INPUT: AH = function code DSK_READ(2)=read, DSK_WRITE(3)=write
; DSK_WRITEVERIFY(5)=write with verify
; DS:SI = disk table
; DX:CX = starting sector on partition
; DI = number of sectors
; ES:BX = transfer buffer
; OUTPUT: CY = error
; AH = DOS error code (valid if CY, else undefined)
; DX:CX = new sector
; DI = number of remaining sectors (0 if NC)
; ES:BX = new transfer buffer
; DESTROYS: AL
; ----------------------------------------------------------------------------
; ------------- Change phantom disk
ReadWrite: call SetPhantom ; change phantom disk
; ------------- Check zero number of sectors (set NC)
ReadWrite0: or di,di ; 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 bp ; push BP
push di ; push DI (number of sectors)
push ax ; push AX (function code)
; ------------- Normalize buffer (-> ES(AX):BX)
push cx ; push CX
push dx ; push DX
push si ; push SI
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
call GetBPB ; SI <- get BPB of disk
div word [si+BPB_sector]; AX <- calculate number of sectors
cmp ax,di ; remain less sectors?
jb ReadWrite2 ; remain less sectors
mov ax,di ; AX <- limit number of sectors
ReadWrite2: xchg ax,bp ; BP <- number of sectors
pop si ; pop SI
pop dx ; pop DX
pop cx ; pop CX
; ------------- 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,DSK_READ ; read?
je ReadWrite3 ; it is read
push si ; push SI
push di ; push DI
push cx ; push CX
push ds ; push DS
push es ; push ES
call GetBPB ; SI <- get BPB of disk
mov cx,[si+BPB_sector] ; CX <- sector size
shr cx,1 ; 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,SectBuff ; DI <- offset of temporary buffer
cld ; direction up
rep movsw ; transfer sector
pop es ; pop ES
pop ds ; pop DS
pop cx ; pop CX
pop di ; pop DI
pop si ; pop SI
; ------------- Transfer data of one sector
ReadWrite3: push bx ; push BX
push di ; push DI
push es ; push ES
mov di,bp ; transfer 1 sector
push cs ; push CS
pop es ; ES <- segment of temporary buffer
mov bx,SectBuff ; BX <- offset of temporary buffer
call ReadWriteLBA ; read/write 1 sector
pop es ; pop ES
pop di ; pop DI
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,DSK_READ ; read ?
jne ReadWrite4 ; it is not read
push si ; push SI
push di ; push DI
push cx ; push CX
push ds ; push DS
call GetBPB ; SI <- get BPB of disk
mov cx,[si+BPB_sector] ; CX <- sector size
shr cx,1 ; 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,SectBuff ; SI <- offset of temporary buffer
cld ; direction up
rep movsw ; transfer sector
pop ds ; pop DS
pop cx ; pop CX
pop di ; pop DI
pop si ; pop SI
; ------------- New transfer address (-> ES:BX, here AH = function code)
ReadWrite4: push si ; push SI
call GetBPB ; SI <- get BPB of disk
add bx,[si+BPB_sector] ; shift offset of buffer
pop si ; pop SI
jmp short ReadWrite6
; ------------- Transfer group of sectors
ReadWrite5: push di ; push DI
mov di,bp ; DI <- number of sectors to transfer
call ReadWriteLBA ; transfer sectors
pushf ; push flags
sub bp,di ; BP <- number of transfered sectors
popf ; pop flags
pop di ; pop DI
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 di ; pop DI
sub di,bp ; change number of sectors
push di ; push DI
push ax ; push AX
clc ; operation OK
; ------------- Pop registers (CY=error, AH=function code or error code)
ReadWrite7: pop bp ; destroy AX
ReadWrite8: pop di ; pop DI
pop bp ; pop BP
jc ReadWrite9 ; error
jmp ReadWrite0 ; next group of sectors
ReadWrite9: ret
; ----------------------------------------------------------------------------
; Load BOOT sector into sector buffer
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; OUTPUT: CY = error
; AH = DOS error code (valid if CY, else undefined)
; DESTROYS: AL
; ----------------------------------------------------------------------------
; ------------- Push registers
LoadBoot: push bx ; push BX
push cx ; push CX
push dx ; push DX
push di ; push DI
push es ; push ES
; ------------- Load boot sector
mov ah,DSK_READ ; AH <- function code
xor cx,cx ; CX <- sector LOW
xor dx,dx ; DX <- sector HIGH
mov di,1 ; DI <- 1 number of sectors
mov bx,SectBuff ; BX <- offset of sector buffer
push cs ; push CS
pop es ; ES <- segment of sector buffer
call ReadWrite ; read boot sector
; ------------- Pop registers
pop es ; pop ES
pop di ; pop DI
pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
ret
; ----------------------------------------------------------------------------
; Reset extended media parameters (serial, volume, fat)
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; ----------------------------------------------------------------------------
; ------------- Push registers
ResetExt: push cx ; push CX
push si ; push SI
push di ; push DI
push ds ; push DS
push es ; push ES
; ------------- Reset serial number
xor cx,cx ; CX <- 0
mov [si+DPB_serial],cx ; reset serial LOW
mov [si+DPB_serial+2],cx ; reset serial HIGH
; ------------- Prepare registers
push ds ; push DS
pop es ; ES <- DS
push cs ; push CS
pop ds ; DS <- CS
cld ; direction UP
; ------------- Reset volume label
mov cl,VOLUME_SIZE ; CX <- length of volume label
lea di,[si+DPB_label] ; DI <- buffer of volume label
lea si,[VolumeDef] ; SI <- default volume label
rep movsb ; reset volume label
; ------------- Reset filesystem name
mov si,FAT16 ; SI <- FAT16 filesystem name
test byte [es:di+DPB_FATtype-DPB_label-VOLUME_SIZE],FAT_16
jnz ResetExt2 ; use FAT 16 filesystem
mov si,FAT12 ; SI <- FAT12 filesystem name
ResetExt2: mov cl,FATNAME_SIZE ; CX <- length of filesystem name
lea di,[di+DPB_fatname-DPB_label-VOLUME_SIZE] ; filesystem
rep movsb ; load filesystem name
; ------------- Pop registers
pop es ; pop ES
pop ds ; pop DS
pop di ; pop DI
pop si ; pop SI
pop cx ; pop CX
ret
; ----------------------------------------------------------------------------
; Get extended media parameters from boot sector
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; OUTPUT: CY = error, no extension
; ----------------------------------------------------------------------------
; ------------- Push registers
GetBootExt: push cx ; push CX
push si ; push SI
push di ; push DI
push ds ; push DS
push es ; push ES
; ------------- Prepare registers
push ds ; push DS
pop es ; ES <- DS
push cs ; push CS
pop ds ; DS <- CS
cld ; direction UP
; ------------- Check boot extension
cmp byte [SectBuff+BOOT_ext],29h ; check extension
stc ; set error flag
jne GetBootExt4 ; no valid extension
; ------------- Copy serial number
mov cx,[SectBuff+BOOT_serial] ; CX <- serial LOW
mov [es:si+DPB_serial],cx ; store serial LOW
mov cx,[SectBuff+BOOT_serial+2] ; CX <- serial HIGH
mov [es:si+DPB_serial+2],cx ; store serial HIGH
; ------------- Copy volume label
mov cx,VOLUME_SIZE ; CX <- length of volume label
lea di,[si+DPB_label] ; DI <- buffer of volume label
mov si,SectBuff+BOOT_volume ; SI <- volume label
rep movsb ; load volume label
; ------------- Load filesystem name
mov cl,FATNAME_SIZE ; CX <- length of filesystem name
lea di,[di+DPB_fatname-DPB_label-VOLUME_SIZE] ; filesystem
mov si,SectBuff+BOOT_fatname ; SI <- filesystem name
rep movsb ; load filesystem name
; ------------- Pop registers
clc ; flag OK
GetBootExt4: pop es ; pop ES
pop ds ; pop DS
pop di ; pop DI
pop si ; pop SI
pop cx ; pop CX
ret
; ----------------------------------------------------------------------------
; Load serial number (loads boot sector into sector buffer)
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; OUTPUT: CY = error
; AH = DOS error code (valid if CY, else undefined)
; DX:CX = serial number
; DESTROYS: AL
; ----------------------------------------------------------------------------
LoadSerial: call LoadBoot ; load boot sector
mov cx,[cs:SectBuff+BOOT_serial] ; CX <- serial LOW
mov dx,[cs:SectBuff+BOOT_serial+2] ; DX <- serial HIGH
ret
; ----------------------------------------------------------------------------
; Load BPB table of disk
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; OUTPUT: CY = error
; AH = DOS error code (valid if CY, else undefined)
; DESTROYS: AL
; ----------------------------------------------------------------------------
; ------------- Push registers
LoadBPB: push cx ; push CX
push di ; push DI
push es ; push ES
; ------------- Test if loading BPB is possible
test byte [si+DPB_flags],FLAG_FIXED+FLAG_LOCKED ; enable?
jnz LoadBPB9 ; fixed disk or BPB locked
; ------------- Make media changed
call DiskChange ; test disk change
cmp al,CHANGE_NO ; not changed?
je LoadBPB2 ; not changed
or byte [si+DPB_flags],FLAG_CHANGE ; media changed
; ------------- Load boot sector
LoadBPB2: call LoadBoot ; load boot sector
jc LoadBPB9 ; error
; ------------- Prepare registers
push ds ; push DS
pop es ; ES <- DS
lea di,[si+DPB_BPB] ; DI <- current BPB table
mov cx,BPB_SIZE ; size of BPB
cld ; direction UP
; ------------- Check boot identifiers
cmp word [SectBuff+512-2],0AA55h ; check identifier
jne LoadBPB3 ; invalid BOOT sector
cmp word [SectBuff+BOOT_sector],SECTOR_SIZE ; sector size
je LoadBPB4 ; sector parameters are OK
; ------------- Use default media parameters
LoadBPB3: push si ; push SI
lea si,[si+DPB_defBPB] ; SI <- default BPB table
rep movsb ; copy default BPB
pop di ; pop DI
jmp short LoadBPB8
; ------------- Copy disk parameters
LoadBPB4: push si ; push SI
push ds ; push DS
lea si,[SectBuff+BOOT_BPB] ; SI <- default BPB table
push cs ; push CS
pop ds ; DS <- CS
rep movsb ; copy default BPB
pop ds ; pop DS
pop si ; pop SI
; ------------- Copy extended parameters
call GetBootExt ; load extended parameters
; ------------- Reset disk change flag
and byte [si+DPB_flags],~FLAG_CHANGE ; reset change flag
LoadBPB8: clc ; no error
; ------------- Pop registers
LoadBPB9: pop es ; pop ES
pop di ; pop DI
pop cx ; pop CX
ret
; ----------------------------------------------------------------------------
; Update disk parameters if disk has been changed
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; OUTPUT: CY = error
; ----------------------------------------------------------------------------
; ------------- Check, if disk is open
UpdateDisk: cmp word [si+DPB_open],0 ; is disk open?
je UpdateDisk9 ; disk is not open
; ------------- Check, if disk was changed
test byte [si+DPB_flags],FLAG_CHANGE ; disk was changed?
jz UpdateDisk9 ; disk was not changed
; ------------- Load disk parameters
call LoadBPB ; load disk parameters
UpdateDisk9: ret
; ----------------------------------------------------------------------------
; Disk reset
; ----------------------------------------------------------------------------
; INPUT: DS:SI = disk table
; ----------------------------------------------------------------------------
; ------------- Push registers
DiskReset: push ax ; push AX
push dx ; push DX
; ------------- Disk reset
mov ah,0 ; AH <- function code
mov dl,[si+DPB_hwdisk] ; DL <- physical disk
call Int13 ; disk reset
; ------------- Pop registers
pop dx ; pop DX
pop ax ; pop AX
ret
; ----------------------------------------------------------------------------
; 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
; ----------------------------------------------------------------------------
; ------------- Push registers
Int13: 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
; ------------- 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
pop dx ; pop DX
pop cx ; pop CX
pop bx ; pop BX
ret
; ----------------------------------------------------------------------------
; Interrupt 13h handler (disk services)
; ----------------------------------------------------------------------------
MyInt13: jmp far [cs:OldInt13] ; jump to old Int 13h routine
; ----------------------------------------------------------------------------
; Data
; ----------------------------------------------------------------------------
BootDisk: db 0 ; boot disk (as went from boot loader)
TotalLogDisk: db 1 ; number of logical disks
DAPTab: times DAP_SIZE db 0 ; buffer of DAP structure
FAT12 db 'FAT12 ' ; FAT12 filesystem name
FAT16 db 'FAT16 ' ; FAT16 filesystem name
; ------------- Physical disks
; Disk names: A:, B: firt 2 floppy disks, C: primary HD, D: first CD-ROM,
; E:.. other HD, follows other CD-ROMs, other floppies and other devices
FloppyNum: db 0 ; count of floppy disks (2 to 4)
FloppyName: db 0,1,2,3 ; names of floppy disks
HDNum: db 0 ; count of BIOS hard disks (0 to 4)
; ------------- Disk variables
DiskTabAddr: dw DiskTab,BOOTSEG ; pointer to first disk table
CurrLogDisk: db 0ffh ; currect logical disk (0ffh=invalid)
Medium: db 0 ; media descriptor
; --- these variables must follow
DiskCommand: db 2 ; required disk command
DiskVerify: db 1 ; 1=verification required
ReqSectors: dw 0 ; required sectors
DiskDrives: db 100 ; number of disk drives
; ------------- Phantom disk
PhantomDisks: db 0 ; number of phantom disks
TempPhantOff: db 0 ; 1=don't ask to change phantom disk
PhantomText: db 13,10,'Insert diskette for drive '
PhantomTextA: db 'A: and press any key when ready',13,10,10,0
; ------------- 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!)
; ------------- Disk tables
DiskTab: ; disk tables
DiskTab0: dw DiskTab1,BOOTSEG ; A: floppy 0
times DPB_SIZE db 0
DiskTab1: dw DiskTab2,BOOTSEG ; B: floppy 1
times DPB_SIZE db 0
DiskTab2: dw DiskTab3,BOOTSEG ; (G): floppy 2
times DPB_SIZE db 0
DiskTab3: dw DiskTab4,BOOTSEG ; (H): floppy 3
times DPB_SIZE db 0
DiskTab4: dw DiskTab5,BOOTSEG ; C: hard disk 0
times DPB_SIZE db 0
DiskTab5: dw DiskTab6,BOOTSEG ; D: hard disk 1
times DPB_SIZE db 0
DiskTab6: dw DiskTab7,BOOTSEG ; E: hard disk 2
times DPB_SIZE db 0
DiskTab7: dw 0ffffh,BOOTSEG ; F: hard disk 3
times DPB_SIZE db 0
SectBuff: times SECTOR_SIZE db 0 ; sector buffer
; ------------- Volume label
VolumeBuff: db 'NO NAME ' ; buffer of current volume label
VolumeDef: db 'NO NAME ' ; default volume label
; ------------- Table of BPB of disks
DiskBPBTab: dw DiskTab0 + DPB_BPB ; floppy 0
dw DiskTab1 + DPB_BPB ; floppy 1
dw DiskTab2 + DPB_BPB ; floppy 2
dw DiskTab3 + DPB_BPB ; floppy 3
dw DiskTab4 + DPB_BPB ; hard disk 0
dw DiskTab5 + DPB_BPB ; hard disk 1
dw DiskTab6 + DPB_BPB ; hard disk 2
dw DiskTab7 + DPB_BPB ; hard disk 3
; ------------- Old Int 1Eh diskette parameters
Int1EOldSize: db 2 ; (3:) old sector size
Int1EOldSect: db 18 ; (4:) old max. sectors petr track
Int1EOldHead: db 15 ; (9:) old head settle time
Int1EOldStart: db 8 ; (10:) old motor start time
; ------------- Int 1Eh, diskette parameter table (size 16 bytes)
Old1ESect: db 18 ; old sectors per track
MyInt1E:
Int1EStep: db 0dfh ; 0: bit 0 to 3: step rate (32 to 2 ms)
; = 32 - (n*2) [ms]
; bit 4 to 7: head unload time
; 15 = 240 ms
Int1ELoad: db 2 ; 1: bit 0: 1=use DMA mode
; bit 1 to 7: head load time
; 1 = 4 ms
Int1EOff: db 37 ; 2: turn motor off in 1/18 seconds
Int1ESize: db 2 ; 3: sector size
; 0 = 128
; 1 = 256
; 2 = 512
; 3 = 1024
Int1ESect: db 18 ; 4: max. sectors per track
Int1EGap: db 27 ; 5: length of gap between sectors
; 42 for 5.25"
; 27 for 3.5"
Int1EData: db 255 ; 6: data transfer length
Int1EForm: db 84 ; 7: gap length when formatting
; 80 for 5.25"
; 108 for 3.5"
Int1EFill: db 0f6h ; 8: format filler byte
Int1EHead: db 15 ; 9: head settle time in [ms]
Int1EStart: db 8 ; 10: motor start time in 1/8 seconds
; --- extended parameters
Int1ETrack: db 79 ; 11: maximum track number
Int1ERate: db 80h ; 12: data transfer rate
Int1EType: db 0 ; 13: drive type in CMOS
db 0,0 ; 14: align to 16 bytes
|