Tvůrce webu je i pro tebe! Postav třeba web. Bez grafika. Bez kodéra. Hned.
wz

IO_DSK.ASM

Ovladač disku


; ============================================================================
;
;                            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

Zpět na stránku systému LT-DOS