; =============================================================================
;
;                        Litos8 BIOS disk (physical disk)
;
; =============================================================================
; Limitations: Sector size must be 512 bytes. Max. 2 floppy and 4 hard disks.
;
; Format of floppy disks is preset and cannot be changed:
;	drive 360 KB: double side, 40 cylinders, 9 sectors per track
;	drive 720 KB: double side, 80 cylinders, 9 sectors per track
;	drive 1.2 MB: double side, 80 cylinders, 15 sectors per track
;	drive 1.44 MB: double side, 80 cylinders, 18 sectors per track
;	drive 2.88 MB: double side, 80 cylinders, 36 sectors per track

; -----------------------------------------------------------------------------
; Exported user functions (15):
;	BDiskGetNum - get number of BIOS disks
;	BDiskGetDesc - get BIOS disk descriptor
;	BDiskGetDescBIOS - get BIOS disk descriptor by BIOS disk number
;	BDiskGetType - get BIOS disk type TEXT
;	BDiskGetInfo - get BIOS disk info TEXT
;	BDiskGetAllInfo - get BIOS all disks info TEXT
;	LBAToCHS - translate absolute sector to CHS
;	BDiskRead - read sectors
;	BDiskWrite - write sectors (without verify)
;	BDiskWriteV - write sectors with verify
;	BDiskVerify - verify sectors
;	BDiskReadSect - read cached sector (into sector buffer)
;	BDiskWriteSect - write cached sector (from sector buffer)
;	HDDCheck - check hard disk
;	FDDCheck - check floppy disk
; -----------------------------------------------------------------------------

		CODEINIT_SECTION

; -----------------------------------------------------------------------------
;                       INIT: Initialize BIOS disk driver
; -----------------------------------------------------------------------------

; ------------- invalid sector buffer

BDiskInit:	and	word [OldSectBDD],byte 0 ; invalid sector buffer

; ------------- save current disk parameter table

		push	ds		; push DS
		mov	cl,1eh		; CL <- INT 1eh 
		call	SysGetInt32	; get INT 1Eh address -> DX:AX
		mov	ds,dx		; DS <- INT 1Eh segment
		xchg	ax,si		; SI <- INT 1Eh offset
		mov	di,FloppyDPT	; DI <- new INT 1Eh table
		mov	cx,DPT_size	; CX <- DPT size
		push	di		; push DI
		rep	movsb		; copy old INT 1Eh DPT table
		pop	di		; pop DI
		pop	ds		; pop DS

; ------------- correct DPT table

		mov	byte [di+DPT_Settle],15 ; head-settle time
		mov	byte [di+DPT_LastSect],50 ; set last sector

; ------------- set new disk parameter table

		mov	dx,ds		; DX <- INT 1Eh segment
		xchg	ax,di		; AX <- INT 1Eh offset
		mov	cl,1eh		; CL <- INT 1Eh
		call	SysSetInt32	; set new INT 1Eh address

; ------------- get number of floppy disks -> SI (max. 2)

		mov	di,BDDTab	; DI <- descriptors
		test	byte [Equipment],B0 ; any floppy disk?
		jz	short BDiskInit3 ; no floppy disk
		mov	al,[Equipment]	; AL <- equipment
		mov	cl,6		; CL <- number of rotations
		shr	al,cl		; AL <- number of floppy disks - 1
		inc	ax		; AL <- number of floppy disks
		cmp	al,2		; max. 2 disks
		jb	short BDiskInit0 ; number of disks is OK
		mov	al,2		; AL <- limit to 2 disks
BDiskInit0:	mov	ah,0		; AX = number of floppy disks
		xchg	ax,si		; SI <- number of floppy disks

; ------------- get floppy disk type

		xor	dx,dx		; DL <- 0, disk number
BDiskInit1:	push	dx		; push DX (disk number)
		mov	ah,8		; AH <- function code
		mov	bl,BDDF_Floppy360 ; preset to 360 KB
		call	Int13		; get disk parameters
		pop	dx		; pop DX (disk number)

; ------------- initialize floppy descriptors

		mov	[di+BDD_Disk],dl ; set disk number

		mov	al,BDDiskNameFD2-BDDiskNameFD1 ; length of name
		mul	dl		; AX <- offset of device name
		add	ax,BDDiskNameFD1 ; AX <- device name
		mov	[di+BDD_DevName],ax ; device name

		cmp	bl,BDDF_FloppyLast ; check floppy type
		jbe	short BDiskInit2 ; valid floppy type
		mov	bl,BDDF_Floppy288 ; BL <- floppy is 2.88 MB or more
BDiskInit2:	or	bl,BDDF_Removable ; BL <- flags
		mov	[di+BDD_Flags],bl ; set flags

		and	bx,byte BDDF_TypeMask ; BX <- type
		mov	ah,[bx+FloppyCyl] ; AL <- number of cylinders
		mov	[di+BDD_Cylinders],ah ; set cylinders
		mov	al,[bx+FloppySect] ; AL <- sectors per track
		mov	[di+BDD_SectTrack],al ; set sectors per track
		shl	al,1		; AL <- sectors per cylinder
		mov	[di+BDD_SectCyl],al ; set sectors per cylinder
		mov	byte [di+BDD_Heads],2 ; set number of heads
		mul	ah		; AX <- total sectors
		mov	[di+BDD_SectNum],ax ; set total sectors

		inc	byte [FloppyNum] ; increase number of floppy disks
		inc	byte [BDDNum]	; increase number of disks

; ------------- next floppy disk

		lea	di,[di+BDD_size]; DI <- next descriptor
		inc	dx		; increase disk number
		dec	si		; counter of floppy disks
		jnz	short BDiskInit1 ; initialize next floppy

; ------------- get number of hard disks -> SI (max. 4)

BDiskInit3:	mov	dl,80h		; DL <- first hard disk
		mov	ah,8		; AH <- function code
		call	Int13		; get number of hard didks
		jnc	short BDiskInit4
BDiskInit32:	ret			; no hard disk

BDiskInit4:	and	dl,7fh		; DL <- number of hard disks
		jz	short BDiskInit32 ; no hard disk
		cmp	dl,4		; max. hard disks
		jb	short BDiskInit5 ; number of hard disks is OK
		mov	dl,4		; DL <- limit number of hard disks
BDiskInit5:	mov	dh,0		; DX = number of hard disks
		mov	si,dx		; SI <- number of hard disks

; ------------- get hard disk parameters

		mov	dl,80h		; DL <- 80h, disk number
BDiskInit6:	push	dx		; push DX (disk number)
		mov	ah,8		; AH <- function code
		call	Int13		; get disk parameters
		mov	al,dh		; AL <- last head
		pop	dx		; pop DX (disk number)
                                 
; ------------- initialize hard disk descriptors

		mov	ah,0		; AX = last head
		inc	ax		; AX <- number of heads
		mov	[di+BDD_Heads],ax ; set number of heads

		mov	al,dl		; AL <- disk number (80h...)
		and	al,7fh		; AL <- disk number relative
		mov	al,BDDiskNameHD2-BDDiskNameHD1 ; length of name
		mul	ah		; AX <- offset of device name
		add	ax,BDDiskNameHD1 ; AX <- device name
		mov	[di+BDD_DevName],ax ; device name

		mov	al,ch		; AL <- max. cylinder LOW
		mov	ah,cl		; AL <- max. cylinder HIGH
		rol	ah,1
		rol	ah,1		; rotate bits 6,7 to 0,1
		and	ah,B0+B1	; AX = max. cylinder
		inc	ax		; AX <- number of cylinders
		mov	[di+BDD_Cylinders],ax ; set number of cylinders

		and	cl,3fh		; CL <- max. sector
		mov	[di+BDD_SectTrack],cl ; set sectors per track

		mov	[di+BDD_Disk],dl ; set disk number
		mov	byte [di+BDD_Flags],BDDF_HardDisk; set flags

		push	dx		; push DX (disk number)
		mov	ax,[di+BDD_SectTrack] ; AX <- sectors per track
		mul	word [di+BDD_Heads] ; AX <- sectors per cylinder
		mov	[di+BDD_SectCyl],ax ; set sectors per cylinder

		mul	word [di+BDD_Cylinders] ; DX:AX <- total sectors
		mov	[di+BDD_SectNum],ax ; set total sectors LOW
		mov	[di+BDD_SectNum+2],dx ; set total sectors HIGH
		pop	dx		; pop DX (disk number)

		inc	byte [HardNum]	; increase number of hard disks
		inc	byte [BDDNum]	; increase number of disks

; ------------- check LBA extension

		push	dx		; push DX (disk number)
		mov	ah,41h		; AH <- function code
		mov	bx,55aah	; BX <- magic
		call	Int13		; check LBA extension
		pop	dx		; pop DX (disk number)
		jc	short BDiskInit7 ; no LBA extension
		cmp	bx,0aa55h	; check magic
		jne	short BDiskInit7 ; no LBA extension

; ------------- get drive extended parameters

		push	dx		; push DX (disk number)
		push	si		; push SI (disk counter)

		mov	ah,48h		; AH <- function code
		mov	si,BDD_Res	; SI <- result buffer
		mov	word [si],BDD_RES_SIZE ; set size of buffer
		call	Int13		; get drive parameters
		mov	bx,si		; BX <- result buffer

		pop	si		; pop SI (disk counter)
		pop	dx		; pop DX (disk number)
		jc	short BDiskInit7 ; invalid LBA extension

; ------------- initialize LBA extended parameters

		cmp	word [bx+24],SECTSIZE ; check sector size
		jne	short BDiskInit7 ; invalid sector size

		or	byte [di+BDD_Flags],BDDF_UseLBA ; use LBA extension

		push	dx		; push DX

		mov	ax,[bx+16]	; AX <- number of sectors LOW
		mov	dx,[bx+18]	; DX <- number of sectors HIGH

		cmp	word [bx+20],byte 0 ; overflow? ( > 2000 GB)
		je	BDiskInit62	; size is OK

		xor	ax,ax		; AX <- 0
		dec	ax		; AX <- -1
		mov	dx,ax		; DX:AX <- limit disk size

BDiskInit62:	mov	[di+BDD_SectNum],ax ; set total sectors LOW
		mov	[di+BDD_SectNum+2],dx ; set total sectors HIGH

		or	word [di+BDD_Cylinders],byte -1 ; limit cylinders
		cmp	dx,[di+BDD_SectCyl] ; check sectors per cylinder
		jae	BDiskInit64	; overflow should came up
		div	word [di+BDD_SectCyl] ; AX <- new number of cylinders
		mov	[di+BDD_Cylinders],ax ; set new number of cylinders

BDiskInit64:	pop	dx		; pop DX

; ------------- next hard disk

BDiskInit7:	lea	di,[di+BDD_size]; DI <- next descriptor
BDiskInit8:	inc	dx		; increase disk number
		dec	si		; counter of hard disks
		jz	short BDiskInit9 ; it was last disk
		jmp	BDiskInit6	; initialize next hard disk
BDiskInit9:	ret


		CODE_SECTION

; -----------------------------------------------------------------------------
;                         Get number of BIOS disks
; -----------------------------------------------------------------------------
; OUTPUT:	AL = number of disks
;		CL = number of floppy disks
;		CH = number of hard disks
; -----------------------------------------------------------------------------

BDiskGetNum:	mov	al,[BDDNum]
		mov	cx,[FloppyHardNum]
		ret

; -----------------------------------------------------------------------------
;                         Get BIOS disk descriptor
; -----------------------------------------------------------------------------
; INPUT:	AL = disk number (0...)
; OUTPUT:	BX = pointer to BDD descriptor (or NULL on CY error)
;		CY = error, invalid disk number (returns BX = NULL)
; -----------------------------------------------------------------------------

; ------------- check disk

BDiskGetDesc:	xor	bx,bx		; BX <- 0
		cmp	al,[BDDNum]	; check disk number
		cmc			; CY = error
		jc	short BDiskGetDesc8 ; error, invalid disk

; ------------- get BDD address -> BX (clears CF)

		xchg	ax,bx		; BL <- disk number
		mov	al,BDD_size	; AL <- descriptor size
		mul	bl		; AX <- offset of descriptor
		add	ax,BDDTab	; AX <- BDD address
		xchg	ax,bx		; BX <- BDD address, AL <- disk number
BDiskGetDesc8:	ret

; -----------------------------------------------------------------------------
;                   Get BIOS disk descriptor by BIOS disk number
; -----------------------------------------------------------------------------
; INPUT:	AL = BIOS disk number (0=fd1, ... 80h=hd1, ... 0C0h=rd1, ...)
; OUTPUT:	BX = pointer to BDD descriptor (or NULL on CY error)
;		CY = error, invalid disk number (returns BX = NULL)
; -----------------------------------------------------------------------------

BDiskGetDescBIOS:
		push	cx		; push CX

		mov	bx,BDDTab	; BX <- BDD tables
		mov	cx,[BDDNum]	; CX <- number of BIOS disks
		jcxz	BDiskGetDescB3	; no BIOS disk
		
BDiskGetDescB2:	cmp	al,[bx+BDD_Disk] ; check BIOS disk
		je	short BDiskGetDescB4 ; found BIOS disk
		add	bx,byte BDD_size ; BX <- shift pointer
		loop	BDiskGetDescB2	; next BIOS disk
BDiskGetDescB3:	xor	bx,bx		; BX <- 0, invalid disk
		stc			; set error flag

BDiskGetDescB4:	pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                         Get BIOS disk type TEXT
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to BDD descriptor
; OUTPUT:	SI = pointer to CTEXT disk type name
; -----------------------------------------------------------------------------

BDiskGetType:	mov	si,[bx+BDD_Flags] ; SI <- type
		and	si,byte BDDF_TypeMask ; SI <- disk type
		shl	si,1		; SI <- offset in table
		mov	si,[si+BDTypeTab] ; SI <- pointer to type name
		ret

; -----------------------------------------------------------------------------
;                           Get BIOS disk info TEXT
; -----------------------------------------------------------------------------
; INPUT:	AL = disk number (0...)
; OUTPUT:	BX = pointer to TEXT structure (NULL on memory error)
;		CY = disk or memory error (BX = NULL)
; NOTES:	Returned text should be destroyed using TextFree.
; -----------------------------------------------------------------------------

; ------------- push registers

BDiskGetInfo:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI

; ------------- get BIOS disk descriptor -> DI

		call	BDiskGetDesc	; get disk descriptor
		jnc	short BDiskGetInfo0
BDiskGetInfo99:	jmp	BDiskGetInfo9	; invalid disk

BDiskGetInfo0:	mov	di,bx		; DI <- disk descriptor
		call	BDiskGetType	; get disk type -> SI

; ------------- create TEXT

		xchg	ax,bx		; BL <- disk
		call	TextNewEmpty	; create new text
		xchg	bx,ax		; BX <- TEXT structure, AL <- disk
		jc	short BDiskGetInfo99 ; memory error

; ------------- disk number

		mov	ax,[di+BDD_DevName] ; AX <- device name
		call	TextAddText	; add device name
		mov	al,":"
		call	TextAddChar	; add char ":"
		call	TextAddSpc	; add space

; ------------- BIOS disk number

		mov	al,"("
		call	TextAddChar	; add "(" char
		mov	al,[di+BDD_Disk] ; AL <- BIOS disk
		call	TextAddHByte	; add BIOS disk number
		mov	ax,BDTextH	; text "h) "
		call	TextAddText	; add "h) " text
		
; ------------- disk type

		xchg	ax,si		; AX <- type name
		call	TextAddText	; add type name

; ------------- disk size in MB (not removable media)
; Sector: 9 bits, 1 MB: 20 bits, conversion: 20-9 = 11 shifts

		test	byte [di+BDD_Flags],BDDF_Removable ; removable?
		jnz	short BDiskGetInfo2 ; removable

		call	TextAddSpc	; add space
		mov	ax,[di+BDD_SectNum] ; AX <- number of sectors LOW
		mov	dx,[di+BDD_SectNum+2] ; DX <- number of sectors HIGH
		call	TextAddDiskSize	; add disk size
		jmp	short BDiskGetInfo3

; ------------- flags

BDiskGetInfo2:	mov	ax,BDFlagsTextRem ; AX <- text Removable
		call	TextAddText	; add flag Removable

BDiskGetInfo3:	test	byte [di+BDD_Flags],BDDF_UseLBA ; LBA?
		jz	short BDiskGetInfo4 ; not LBA
		mov	ax,BDFlagsTextLBA ; AX <- text LBA
		call	TextAddText	; add flag LBA

; ------------- cylinders

BDiskGetInfo4:	mov	ax,BDTextCyl	; AX <- text "cyl="
		call	TextAddText	; add text "cyl="
		mov	ax,[di+BDD_Cylinders] ; AX <- cylinders
		call	TextAddWord	; add cylinders

; ------------- heads

		mov	ax,BDTextHead	; AX <- text "heads="
		call	TextAddText	; add text "heads="
		mov	ax,[di+BDD_Heads] ; AX <- heads
		call	TextAddWord	; add heads

; ------------- sectors per track

		mov	ax,BDTextSTrk	; AX <- text "sectrk="
		call	TextAddText	; add text "sectrk="
		mov	ax,[di+BDD_SectTrack] ; AX <- sectors per track
		call	TextAddWord	; add sectors per track

; ------------- total sectors

		mov	ax,BDTextTotal	; AX <- text "total"
		call	TextAddText	; add text "total="
		mov	ax,[di+BDD_SectNum] ; AX <- total sectors LOW
		mov	dx,[di+BDD_SectNum+2] ; DX <- total sectors HIGH
		call	TextAddDWord	; add total sectors

; ------------- pop registers

		clc			; clear error flag
BDiskGetInfo9:	pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                           Get BIOS all disks info TEXT
; -----------------------------------------------------------------------------
; OUTPUT:	BX = pointer to TEXT structure (NULL on memory error)
;		CY = memory error (BX = NULL)
; NOTES:	Returned text should be destroyed using TextFree.
; -----------------------------------------------------------------------------

; ------------- push registers

BDiskGetAllInfo:push	ax		; push AX
		push	dx		; push DX

; ------------- create TEXT

		call	TextNewEmpty	; create new text
		xchg	bx,ax		; BX <- TEXT structure
		jc	short BDiskGetAllInf9 ; memory error

; ------------- total number of disks

		mov	ax,BDTextDiskNum ; AX <- text "Physical drives="
		call	TextAddText	; add text "Physical drives="
		mov	al,[BDDNum]	; AL <- total number of disks
		call	TextAddByte	; add total number of disks

; ------------- number of floppy disks

		mov	ax,BDTextFlopNum ; AX <- text "Floppy drives count="
		call	TextAddText	; add text "Floppy drives count="
		mov	al,[FloppyNum]	; AL <- number of floppy disks
		call	TextAddByte	; add number of floppy disks

; ------------- number of hard disks

		mov	ax,BDTextHardNum ; AX <- text "Hard drives count="
		call	TextAddText	; add text "Hard drives count="
		mov	al,[HardNum]	; AL <- number of hard disks
		call	TextAddByte	; add number of hard disks
		call	TextAddNewLine	; add new line

; ------------- add disc descriptors

		mov	al,0		; AL <- disk index
		mov	ah,[BDDNum]	; AH <- number of disks
		or	ah,ah		; any disk?
		jz	short BDiskGetAllInf9 ; no disk

BDiskGetAllInf2:push	ax		; push AX

		mov	dx,bx		; DX <- TEXT descriptor
		call	BDiskGetInfo	; get disk info
		xchg	ax,bx		; AX <- disk TEXT
		mov	bx,dx		; BX <- TEXT descriptor
		jc	short BDiskGetAllInf6 ; error

		call	TextAddText	; add text
		xchg	ax,bx		; AX <- TEXT, BX <- disk TEXT
		call	TextFree	; free disk TEXT 
		xchg	ax,bx		; BX <- TEXT
		call	TextAddNewLine	; add new line

BDiskGetAllInf6:pop	ax		; pop AX

		inc	ax		; increase disk index
		dec	ah		; decrease disk counter
		jnz	short BDiskGetAllInf2 ; get next disk

; ------------- pop registers

		clc			; clear error flag
BDiskGetAllInf9:pop	dx		; pop DX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                        Translate absolute sector to CHS
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = absolute sector (0...)
;		BX = pointer to BDD descriptor
; OUTPUT:	CL = bit 0..5: sector number (1..63)
;		     bit 6..7: cylinder number HIGH (bits 8..9)
;		CH = cylinder number LOW (bits 0..7)
;		DH = head number (0..255)
;			or	bit 0..5 head number (0..63)
;				bit 6,7: cylinder bits 10..11
; DESTROYS:	DL
; -----------------------------------------------------------------------------
; Requirements: sector: 1..63, head: 0..255 (or 0..63),
; cylinder: 0..1023 (or 0..4095)

; ------------- push registers

LBAToCHS:	push	ax		; push AX

; ------------- get cylinder number -> AX, and sector in cylinder -> DX

		div	word [bx+BDD_SectCyl] ; AX <- cylinder, DX <- sector

; ------------- prepare cylinder number -> CX, AH

		xchg	al,ah		; AH <- cylinder LOW, AL <- HIGH
		ror	al,1
		ror	al,1		; AL <- shift HIGH to bits 6..7
		mov	ch,al		; CH <- cylinder number HIGH
		ror	ch,1
		ror	ch,1		; CH <- bits 10..11 to bits 6..7
		and	al,B6+B7	; mask bits 6..7 (cylinder 8..9)
		and	ch,B6+B7	; mask bits 6..7 (cylinder 10..11)
		xchg	ax,cx		; CX <- cylinder, AH <- HIGH

; ------------- prepare head -> DH, and sector -> CL

		xchg	ax,dx		; AX <- sector in cylinder, DH <- cyl.
		div	byte [bx+BDD_SectTrack] ; AL <- head, AH <- sector
		inc	ah		; AH <- sector + 1
		or	cl,ah		; CL <- cylinder HIGH + sector
		or	dh,al		; DH <- head number + cylinder 10..11

; ------------- pop registers

		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                            Read/write sectors
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = absolute sector (0..)
;		BX = pointer to BDD descriptor
;		CH = command (BCMD_READ, BCMD_WRITE, BCMD_VERIFY)
;		CL = number of sectors (1..127)
;		DI = data buffer
; OUTPUT:	CY = operation error
;		DX:AX = new sector
;		CL = 0
;		DI = new data buffer
; DESTROY:	CH
; NOTES:	Verify after write is performed automatically.
; -----------------------------------------------------------------------------

; ------------- push registers

BDiskRW:	push	bp		; push BP

BDiskRW0:	mov	bp,2		; BP <- number of tries on error
BDiskRW1:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- command correction

		cmp	ch,BCMD_WRITEV	; write with verify?
		jne	BDiskRW12	; no
		mov	ch,BCMD_WRITE	; CH <- change to Write command
BDiskRW12:	cmp	ch,BCMD_VERIFYW	; verify after write?
		jne	BDiskRW14	; no
		mov	ch,BCMD_VERIFY	; CH <- change to Verify command

; ------------- use LBA

BDiskRW14:	test	byte [bx+BDD_Flags],BDDF_UseLBA ; use LBA mode?
		jz	short BDiskRW2	; not using LBA

; ------------- prepare DAP structure

		mov	si,BDD_DAP	; SI <- LBA descriptor
		mov	word [si+DAP_len],DAP_size ; size of structure
		mov	[si+DAP_num],cl	; number of sectors
		mov	[si+DAP_buff],di ; buffer LOW
		mov	[si+DAP_buff+2],ds ; buffer HIGH
		mov	[si+DAP_start],ax ; sector LOW
		mov	[si+DAP_start+2],dx ; sector HIGH
		xor	ax,ax		; AX <- 0
		mov	[si+DAP_res2],al ; reserved
		mov	[si+DAP_start+4],ax ; sector 3
		mov	[si+DAP_start+6],ax ; sector 4
		xchg	ax,cx		; AH<-command, AL<-number of sectors
		add	ah,40h		; AH <- command
		push	ax		; push AX (number of sectors)
		mov	al,0		; AL <- flags, verify off
		jmp	short BDiskRW4
                                            
; ------------- translate absolute sector DX:AX to CHS -> CX, DX

BDiskRW2:	mov	si,cx		; SI <- command and number of sectors
		call	LBAToCHS	; translate sector to CHS
		xchg	ax,si		; AH <- command, AL <- number of sectors

; ------------- limit number of sectors -> AL

		mov	si,bx		; SI <- pointer to BDD descriptor
		mov	bl,cl		; BL <- sector number and cylinder
		and	bl,3fh		; BL <- mask sector number (1..63)
		dec	bx		; BL <- sector number (0..62)
		mov	bh,[si+BDD_SectTrack] ; BH <- sectors per track
		sub	bh,bl		; BH <- remaining sectors
		cmp	al,bh		; check number of sectors
		jb	short BDiskRW3	; number of sectors is OK
		mov	al,bh		; AL <- limit number of sectors
BDiskRW3:	mov	bx,si		; BX <- pointer to BDD descriptor

; ------------- perform required operation

		push	ax		; push AX (number of sectors)
BDiskRW4:	xchg	bx,di		; BX <- data buffer, DI <- BDD descr.
		mov	dl,[di+BDD_Disk] ; DL <- disk number
		cmp	dl,RAMDISK	; RAM disk?
		jne	short BDiskRW5	; no
		call	RAMDiskRW	; RAM disk read/write
		jmp	short BDiskRW56
BDiskRW5:	cmp	dl,MEMDISK	; memory disk?
		jne	short BDiskRW52	; no
		call	MEMDiskRW	; memory disk read/write
		jmp	short BDiskRW56
BDiskRW52:	call	Int13All	; call required operation
BDiskRW56:	xchg	bx,di		; DI <- data buffer, BX <- BDD descr.
		pop	ax		; pop AX (number of required sectors)

; ------------- pop registers (without AX and BP)

		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX

; ------------- repeat on error

		jnc	short BDiskRW6	; operation OK
		dec	bp		; BP <- error counter
		jz	short BDiskRW9	; no next attempt

; ------------- reset disk and try again

		push	dx		; push DX
		mov	ah,0		; AH <- function code
		mov	dl,[bx+BDD_Disk] ; DL <- disk number
		call	Int13All	; disk reset
		pop	dx		; pop DX
		pop	ax		; pop AX
		jmp	BDiskRW1	; next attempt

; ------------- verify after write

BDiskRW6:	mov	ah,0		; AX <- number of sectors
		xchg	ax,bp		; BP <- number of sectors
		pop	ax		; pop AX
		cmp	ch,BCMD_WRITEV	; write before verify?
		jne	short BDiskRW8	; no
		mov	ch,BCMD_VERIFYW	; CH <- change to verify after write
BDiskRW7:	jmp	BDiskRW0	; verify sectors

; ------------- shift registers

BDiskRW8:	add	ax,bp		; AX <- shift sector LOW
		adc	dx,byte 0	; DX <- shift sector HIGH
		xchg	ax,bp		; AX <- number of sectors
		sub	cl,al		; CL <- decrease number of sectors
		xchg	ax,bp		; BP <- number of sectors

		push	ax		; push AX
		push	dx		; push DX
		mov	ax,SECTSIZE	; AX <- sector size
		mul	bp		; AX <- data size
		add	di,ax		; DI <- shift data buffer
		pop	dx		; pop DX
		pop	ax		; pop AX

; ------------- change from verify to write

		cmp	ch,BCMD_VERIFYW	; verify after write?
		jne	short BDiskRW84	; no
		mov	ch,BCMD_WRITEV	; change to write before verify

; ------------- next group of sectors

BDiskRW84:	or	cl,cl		; all sectors are transfered?
		jnz	short BDiskRW7	; next group of sectors
		push	ax		; push AX

; ------------- pop rest of registers

BDiskRW9:	pop	ax		; pop AX
		pop	bp		; pop BP
		ret

; -----------------------------------------------------------------------------
;                              Read sectors
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = absolute sector (0..)
;		BX = pointer to BDD descriptor
;		CL = number of sectors (1..127)
;		DI = data buffer
; OUTPUT:	CY = operation error
;		DX:AX = new sector
;		DI = new data buffer
; -----------------------------------------------------------------------------

BDiskRead:	push	cx		; push CX
		mov	ch,BCMD_READ	; CH <- command
		jmp	short BDiskWrite4

; -----------------------------------------------------------------------------
;                         Write sectors (without verify)
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = absolute sector (0..)
;		BX = pointer to BDD descriptor
;		CL = number of sectors (1..127)
;		DI = data buffer
; OUTPUT:	CY = operation error
;		DX:AX = new sector
;		DI = new data buffer
; -----------------------------------------------------------------------------

BDiskWrite:	push	cx		; push CX
		mov	ch,BCMD_WRITE	; CH <- command
BDiskWrite4:	call	BDiskRW		; write sectors
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                          Write sectors with verify
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = absolute sector (0..)
;		BX = pointer to BDD descriptor
;		CL = number of sectors (1..127)
;		DI = data buffer
; OUTPUT:	CY = operation error
;		DX:AX = new sector
;		DI = new data buffer
; -----------------------------------------------------------------------------

BDiskWriteV:	push	cx		; push CX
		mov	ch,BCMD_WRITEV	; CH <- command
		jmp	short BDiskWrite4

; -----------------------------------------------------------------------------
;                              Verify sectors
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = absolute sector (0..)
;		BX = pointer to BDD descriptor
;		CL = number of sectors (1..127)
; OUTPUT:	CY = operation error
;		DX:AX = new sector
; -----------------------------------------------------------------------------

BDiskVerify:	push	cx		; push CX
		push	di		; push DI

		mov	ch,BCMD_VERIFY	; CH <- command
		xor	di,di		; DI <- buffer
		call	BDiskRW		; verify sectors

		pop	di		; pop DI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                         Read cached sector (into SectBuf)
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = absolute sector (0..)
;		BX = pointer to BDD descriptor
; OUTPUT:	SI = sector buffer SectBuf
;		CY = operation error
; -----------------------------------------------------------------------------

BDiskReadSect:	mov	si,SectBuf	; SI <- sector buffer

; ------------- check parameters

		cmp	bx,[OldSectBDD]	; check BDD descriptor
		jne	short BDiskReadSect2 ; invalid sector
		cmp	ax,[OldSect]	; check sector index LOW
		jne	short BDiskReadSect2 ; invalid sector
		cmp	dx,[OldSect+2]	; check sector index HIGH
		je	short BDiskReadSect8 ; sector is OK

; ------------- set parameters	

BDiskReadSect2:	mov	[OldSectBDD],bx	; set BDD descriptor
		mov	[OldSect],ax	; set sector index LOW
		mov	[OldSect+2],dx	; set sector index HIGH

; ------------- push registers

		push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI

; ------------- read sector

		mov	cl,1		; CL <- read 1 sector
		mov	di,si		; DI <- sector buffer
		call	BDiskRead	; read sector

; ------------- destroy buffer on error

		jnc	short BDiskReadSect4 ; sector readed OK
		mov	word [OldSectBDD],0 ; invalid sector buffer

; ------------- pop registers

BDiskReadSect4:	pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
BDiskReadSect8:	ret

; -----------------------------------------------------------------------------
;                  Write cached sector (from SectBuf, with verify)
; -----------------------------------------------------------------------------
; OUTPUT:	CY = operation error
; NOTES:	Valid parameters must be set in OldSectBDD and OldSect.
; -----------------------------------------------------------------------------

; ------------- push registers

BDiskWriteSect:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI

; ------------- get sector parameters

		mov	bx,[OldSectBDD]	; BX <- BDD descriptor
		mov	ax,[OldSect],	; AX <- sector index LOW
		mov	dx,[OldSect+2]	; DX <- sector index HIGH

; ------------- read sector

		mov	cl,1		; CL <- read 1 sector
		mov	di,SectBuf	; DI <- sector buffer
		call	BDiskWriteV	; write sector with verify

; ------------- pop registers

		pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;               Call INT 13h service, saving some registers
; -----------------------------------------------------------------------------
; DESTROY:	AX, BX, CX, DX
; -----------------------------------------------------------------------------

; ------------- push registers

Int13:		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP
		push	ds		; push DS
		push	es		; push ES

; ------------- protect writes to hard disk

%ifdef PROTECT
		cmp	ah,3		; write sectors
		je	short Int132
		cmp	ah,5		; format track
		je	short Int132
		cmp	ah,6		; format track set bad sectors
		je	short Int132
		cmp	ah,7		; format track set bad sectors
		je	short Int132
		cmp	ah,43h		; extended write sectors
		jne	short Int136
Int132:		or	dl,dl		; hard disk?
		jns	short Int136	; no

		push	cs
		pop	ds
		push	cs
		pop	es
		cld
		call	DispClear	; clear display
		mov	bx,BDiskWProtErr ; BX <- text "panic"
		call	DispConTextStr	; display error text
Int134:		jmp	short Int134	; system halt
Int136:

%endif

; ------------- call INT 13h

		stc			; preset error (due to some BIOSes)
		int	13h		; call Int 13h
		sti			; reenable interrupt (some BIOSes)
		cld			; set direction UP

; ------------- pop registers

		pop	es		; pop ES
		pop	ds		; pop DS
		pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		ret

; -----------------------------------------------------------------------------
;              Call INT 13h service, saving (almost) all registers
; -----------------------------------------------------------------------------
; DESTROYS:	AX
; -----------------------------------------------------------------------------

; ------------- push registers

Int13All:	push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX

; ------------- call INT 13h

		call	Int13		; call Int 13h with saving registers

; ------------- pop registers

		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                         Check hard disk
; -----------------------------------------------------------------------------
; INPUT:	BL = hard disk (0..)
; OUTPUT:	CY = invalid hard disk
; -----------------------------------------------------------------------------

HDDCheck:	push	bx		; push BX
		add	bl,80h		; add hard disk base
		jmp	short FDDCheck2	; check disk

; -----------------------------------------------------------------------------
;                         Check memory disk
; -----------------------------------------------------------------------------
; INPUT:	BL = memory disk (0..)
; OUTPUT:	CY = invalid memory disk
; -----------------------------------------------------------------------------

RAMCheck:	push	bx		; push BX
		add	bl,MEMDISK	; add memory disk base
		jmp	short FDDCheck2	; check disk

; -----------------------------------------------------------------------------
;                         Check RAM disk
; -----------------------------------------------------------------------------
; INPUT:	BL = RAM disk (0..)
; OUTPUT:	CY = invalid RAM disk
; -----------------------------------------------------------------------------

RDDCheck:	push	bx		; push BX
		add	bl,RAMDISK	; add RAM disk base
		jmp	short FDDCheck2	; check disk

; -----------------------------------------------------------------------------
;                         Check floppy disk
; -----------------------------------------------------------------------------
; INPUT:	BL = floppy disk (0..)
; OUTPUT:	CY = invalid floppy disk
; -----------------------------------------------------------------------------

FDDCheck:	push	bx		; push BX
FDDCheck2:	push	ax		; push AX
		xchg	ax,bx		; AL <- BIOS disk number
		call	BDiskGetDescBIOS ; get BDD descriptor
		pop	ax		; pop AX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                         Hard disk size
; -----------------------------------------------------------------------------
; INPUT:	BL = hard disk (0..)
; OUTPUT:	DX:AX = media size (0 on error)
;		CY = invalid hard disk (DX:AX = 0)
; -----------------------------------------------------------------------------

HDDSize:	push	bx		; push BX
		add	bl,80h		; add hard disk base
		jmp	short FDDSize2	; disk size

; -----------------------------------------------------------------------------
;                         Memory disk size
; -----------------------------------------------------------------------------
; INPUT:	BL = memory disk (0..)
; OUTPUT:	DX:AX = media size (0 on error)
;		CY = invalid memory disk (DX:AX = 0)
; -----------------------------------------------------------------------------

RAMSize:	push	bx		; push BX
		add	bl,MEMDISK	; add memory disk base
		jmp	short FDDSize2	; disk size

; -----------------------------------------------------------------------------
;                         RAM disk size
; -----------------------------------------------------------------------------
; INPUT:	BL = RAM disk (0..)
; OUTPUT:	DX:AX = media size (0 on error)
;		CY = invalid RAM disk (DX:AX = 0)
; -----------------------------------------------------------------------------

RDDSize:	push	bx		; push BX
		add	bl,RAMDISK	; add RAM disk base
		jmp	short FDDSize2	; disk size

; -----------------------------------------------------------------------------
;                         Floppy disk size
; -----------------------------------------------------------------------------
; INPUT:	BL = floppy disk (0..)
; OUTPUT:	DX:AX = media size (0 on error)
;		CY = invalid floppy disk (DX:AX = 0)
; -----------------------------------------------------------------------------

FDDSize:	push	bx		; push BX

FDDSize2:	xchg	ax,bx		; AL <- BIOS disk number
		xor	dx,dx		; DX <- 0
		call	BDiskGetDescBIOS ; get BDD descriptor
		mov	ax,dx		; AX <- 0
		jc	short FDDSize4	; error, invalid disk

		mov	ah,[bx+BDD_SectNum] ; AX <- number of sectors * 256
		mov	dx,[bx+BDD_SectNum+1] ; DX <- number of sectors HIGH
		shl	ax,1		; AX <- size LOW
		rcl	dx,1		; DX <- size HIGH
		clc			; clear error flag

FDDSize4:	pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                      Read from hard disk device
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = current sector offset
;		BL = hard disk (0..1)
;		CX = number of sectors
;		DI = destination buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining sectors (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

HDDRead:	push	bx		; push BX
		add	bl,80h		; add hard disk base
		jmp	short FDDRead2	; read from disk

; -----------------------------------------------------------------------------
;                      Read from memory disk device
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = current sector offset
;		BL = memory disk (0..1)
;		CX = number of sectors
;		DI = destination buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining sectors (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

RAMRead:	push	bx		; push BX
		add	bl,MEMDISK	; add memory disk base
		jmp	short FDDRead2	; read from disk

; -----------------------------------------------------------------------------
;                      Read from RAM disk device
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = current sector offset
;		BL = RAM disk (0..1)
;		CX = number of sectors
;		DI = destination buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining sectors (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

RDDRead:	push	bx		; push BX
		add	bl,RAMDISK	; add RAM disk base
		jmp	short FDDRead2	; read from disk

; -----------------------------------------------------------------------------
;                      Read from floppy disk device
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = current sector offset
;		BL = floppy disk (0..1)
;		CX = number of sectors
;		DI = destination buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining sectors (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

; ------------- push registers 1

FDDRead:	push	bx		; push BX

; ------------- get BDD descriptor -> BX

FDDRead2:	push	ax		; push AX
		xchg	ax,bx		; AL <- BIOS disk number
		call	BDiskGetDescBIOS ; get BDD descriptor
		pop	ax		; pop AX
		jc	short FDDRead9	; invalid disk

; ------------- push registers 2

		push	ax		; push AX
		push	dx		; push DX
		push	di		; push DI

; ------------- read data from disk

		call	BDiskRead	; read sectors
		jc	short FDDRead6	; error
		xor	cx,cx		; CX <- 0, data OK

; ------------- pop registers

FDDRead6:	pop	di		; pop DI
		pop	dx		; pop DX
		pop	ax		; pop AX

FDDRead9:	pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                      Write to hard disk device
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = current sector offset
;		BL = hard disk (0..1)
;		CX = number of sectors
;		DI = source buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining sectors (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

HDDWrite:	push	bx		; push BX
		add	bl,80h		; add hard disk base
		jmp	short FDDWrite2	; write to disk

; -----------------------------------------------------------------------------
;                      Write to memory disk device
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = current sector offset
;		BL = memory disk (0..1)
;		CX = number of sectors
;		DI = source buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining sectors (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

RAMWrite:	push	bx		; push BX
		add	bl,MEMDISK	; add memory disk base
		jmp	short FDDWrite2	; write to disk

; -----------------------------------------------------------------------------
;                      Write to RAM disk device
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = current sector offset
;		BL = RAM disk (0..1)
;		CX = number of sectors
;		DI = source buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining sectors (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

RDDWrite:	push	bx		; push BX
		add	bl,RAMDISK	; add RAM disk base
		jmp	short FDDWrite2	; write to disk

; -----------------------------------------------------------------------------
;                      Write to floppy disk device
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = current sector offset
;		BL = floppy disk (0..1)
;		CX = number of sectors
;		DI = source buffer
; OUTPUT:	CY = error (CX > 0)
;		CX = remaining sectors (or CX = 0 if no error NC)
; -----------------------------------------------------------------------------

; ------------- push registers 1

FDDWrite:	push	bx		; push BX

; ------------- get BDD descriptor -> BX

FDDWrite2:	push	ax		; push AX
		xchg	ax,bx		; AL <- BIOS disk number
		call	BDiskGetDescBIOS ; get BDD descriptor
		pop	ax		; pop AX
		jc	short FDDWrite9	; invalid disk

; ------------- push registers 2

		push	ax		; push AX
		push	dx		; push DX
		push	di		; push DI

; ------------- write data to disk

		call	BDiskWriteV	; write sectors (with verify)
		jc	short FDDWrite6	; error
		xor	cx,cx		; CX <- 0, data OK

; ------------- pop registers

FDDWrite6:	pop	di		; pop DI
		pop	dx		; pop DX
		pop	ax		; pop AX

FDDWrite9:	pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                            Constant data
; -----------------------------------------------------------------------------

		CONST_SECTION

		align	2, db 0

; ------------- floppy disk cylinders and sectors (2.88M,360K,1.2M,720K,1.44M)

FloppyCyl:	db	80,40,80,80,80	; number of cylinders
FloppySect:	db	36,9,15,9,18	; number of sectors per track

; ------------- floppy disk type name

BDType288:	CTEXT	"floppy_2.88M"
BDType360:	CTEXT	"floppy_360K"
BDType12:	CTEXT	"floppy_1.2M"
BDType720:	CTEXT	"floppy_720K"
BDType144:	CTEXT	"floppy_1.44M"
BDTypeHD:	CTEXT	"harddisk"
BDTypeMEM:	CTEXT	"memory"
BDTypeRAMDISK:	CTEXT	"RAMdisk"

BDTypeTab:	dw	BDType288
		dw	BDType360
		dw	BDType12
		dw	BDType720
		dw	BDType144
		dw	BDTypeHD
		dw	BDTypeMEM
		dw	BDTypeRAMDISK

BDTextH:	CTEXT	"h) "

BDFlagsTextRem:	CTEXT	" removable"
BDFlagsTextLBA:	CTEXT	" LBA"

BDTextCyl:	CTEXT	" C="
BDTextHead:	CTEXT	" H="
BDTextSTrk:	CTEXT	" S="
BDTextTotal:	CTEXT	" total="

BDTextDiskNum:	CTEXT	"Physical drives="
BDTextFlopNum:	CTEXT	", fdd="
BDTextHardNum:	CTEXT	", hdd="

; ------------- BIOS disk device names

BDDiskNameFD1:	CTEXT	"fd1"		; floppy disk 1
BDDiskNameFD2:	CTEXT	"fd2"		; floppy disk 2
BDDiskNameHD1:	CTEXT	"hd1"		; hard disk 80h
BDDiskNameHD2:	CTEXT	"hd2"		; hard disk 81h
BDDiskNameHD3:	CTEXT	"hd3"		; hard disk 82h
BDDiskNameHD4:	CTEXT	"hd4"		; hard disk 83h

; ------------- protect writes to hard disk

%ifdef PROTECT
BDiskWProtErr:	CTEXT	"PANIC: Unauthorized write to hard disk!",13,10
%endif

; -----------------------------------------------------------------------------
;                            Uninitialised data
; -----------------------------------------------------------------------------

		DATA_SECTION

; ------------- BIOS disks descriptors

		align	4, resb 1
BDDTab:		resb	BDD_size * BDISK_MAX ; disk descriptors
BDDNum:		resw	1		; number of BIOS disks (word or byte)

FloppyHardNum:
FloppyNum:	resb	1		; number of floppy disks
HardNum:	resb	1		; number of hard disks

; ------------- Disk Address Packet DAP (for LBA)

BDD_DAP:	; resb	DAP_size	; LBA descriptor (common with BDD_Res)
BDD_Res:	resb	BDD_RES_SIZE	; LBA result buffer

; ------------- sector buffer

		align	4, resb 1
SectBuf:	resb	SECTSIZE	; sector buffer
		align	4, resb 1
OldSect:	resd	1		; old sector index
OldSectBDD:	resw	1		; old sector BDD descriptor

; ------------- diskette parameter table (INT 1Eh)

		align	4, resb 1
FloppyDPT:	resb	DPT_size	; diskette parameter table
