Index: trunk/XTIDE_Universal_BIOS/Src/Boot/BootMenuInfo.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Boot/BootMenuInfo.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Boot/BootMenuInfo.asm	(revision 363)
@@ -17,16 +17,34 @@
 ;		ES:BX:	Ptr to BOOTMENUINFO (if successful)
 ;	Corrupts registers:
-;		AX, BX, CX, DX, DI
+;		AX, CX, DX, DI
 ;--------------------------------------------------------------------
 BootMenuInfo_CreateForHardDisk:
 	call	BootMenuInfo_ConvertDPTtoBX			; ES:BX now points to new BOOTMENUINFO
+	push	ds									; Preserve RAMVARS...
+	push	si									; ...and SI
+
+	push	es									; ES to be copied to DS
+
+%ifdef MODULE_ADVANCED_ATA
+	; Copy DPT_ADVANCED_ATA to BOOTMENUINFO to keep DPTs small.
+	; DPT_ADVANCED_ATA has variables that are only needed during initialization.
+	mov		ax, [di+DPT_ADVANCED_ATA.wIdeBasePort]
+	mov		[es:bx+BOOTMENUINFO.wIdeBasePort], ax
+	mov		dx, [di+DPT_ADVANCED_ATA.wMinPioActiveTimeNs]
+	mov		[es:bx+BOOTMENUINFO.wMinPioActiveTimeNs], dx
+
+	mov		ax, [di+DPT_ADVANCED_ATA.wMinPioRecoveryTimeNs]
+	mov		cx, [di+DPT_ADVANCED_ATA.wControllerID]
+	mov		dx, [di+DPT_ADVANCED_ATA.wControllerBasePort]
+	pop		ds									; ES copied to DS
+	mov		[bx+BOOTMENUINFO.wMinPioRecoveryTimeNs], ax
+	mov		[bx+BOOTMENUINFO.wControllerID], cx
+	mov		[bx+BOOTMENUINFO.wControllerBasePort], dx
+
+%else
+	pop		ds									; ES copied to DS
+%endif
 
 	; Store Drive Name
-	push	ds									; Preserve RAMVARS
-	push	si
-
-	push	es									; ES copied to DS
-	pop		ds
-
 	add		si, BYTE ATA1.strModel				; DS:SI now points drive name
 	lea		di, [bx+BOOTMENUINFO.szDrvName]		; ES:DI now points to name destination
@@ -42,5 +60,4 @@
 	pop		si
 	pop		ds
-		
 	ret
 
@@ -55,5 +72,4 @@
 ;		CX
 ;--------------------------------------------------------------------
-ALIGN JUMP_ALIGN
 BootMenuInfo_GetTotalSectorCount:
 	test	BYTE [di+DPT.bFlagsLow], FLG_DRVNHEAD_LBA
@@ -62,4 +78,21 @@
 .ReturnFullCapacity:
 	jmp		AccessDPT_GetLbaSectorCountToBXDXAX
+
+
+;--------------------------------------------------------------------
+; BootMenuInfo_IsAvailable
+;	Parameters:
+;		Nothing
+;	Returns:
+;		ES:		Segment to BOOTVARS with BOOTMENUINFOs
+;		ZF:		Set if BOOTVARS with BOOTMENUINFOs is available
+;				Cleared if not available (no longer initializing)
+;	Corrupts registers:
+;		BX
+;--------------------------------------------------------------------
+BootMenuInfo_IsAvailable:
+	LOAD_BDA_SEGMENT_TO	es, bx
+	cmp		WORD [es:BOOTVARS.wMagicWord], BOOTVARS_MAGIC_WORD
+	ret
 
 
@@ -73,13 +106,14 @@
 ;		BX:		Offset to BOOTMENUINFO struct
 ;	Corrupts registers:
-;		AX
+;		Nothing
 ;--------------------------------------------------------------------
-ALIGN JUMP_ALIGN
 BootMenuInfo_ConvertDPTtoBX:
+	push	ax
 	mov		ax, di
-	sub		ax, RAMVARS_size						; subtract off base of DPTs
+	sub		ax, BYTE RAMVARS_size					; subtract off base of DPTs
 	mov		bl, DPT_BOOTMENUINFO_SIZE_MULTIPLIER	; BOOTMENUINFO's are a whole number multiple of DPT size
 	mul		bl								
 	add		ax, BOOTVARS.rgBootNfo					; add base of BOOTMENUINFO
 	xchg	ax, bx
+	pop		ax
 	ret			
Index: trunk/XTIDE_Universal_BIOS/Src/Boot/BootMenuPrintCfg.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Boot/BootMenuPrintCfg.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Boot/BootMenuPrintCfg.asm	(revision 363)
@@ -111,10 +111,10 @@
 ;		Nothing (falls to next push below)
 ;	Corrupts registers:
-;		AX
+;		AX, BX, DX, ES
 ;--------------------------------------------------------------------
 .PushResetStatus:
-	mov		al, [di+DPT.bFlagsHigh]
-	and		al, MASKH_DPT_RESET			;  ah already zero from last push
-	push	ax
+	call	BootMenuInfo_IsAvailable	; Load segment to ES
+	call	BootMenuInfo_ConvertDPTtoBX
+	push	WORD [es:bx+BOOTMENUINFO.wInitErrorFlags]
 
 ;;; fall-out to BootMenuPrint_HardDiskRefreshInformation.
Index: trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeCommand.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeCommand.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeCommand.asm	(revision 363)
@@ -56,4 +56,7 @@
 	mov		[di+DPT.bIdevarsOffset], bp
 	mov		BYTE [di+DPT_ATA.bSetBlock], 1	; Block = 1 sector
+%ifdef MODULE_ADVANCED_ATA
+	call	IdeDPT_StoreDeviceTypeFromIdevarsInCSBPtoDPTinDSDI
+%endif
 %ifdef ASSEMBLE_SHARED_IDE_DEVICE_FUNCTIONS
 	call	IdeDPT_StoreReversedAddressLinesFlagIfNecessary
Index: trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeDPT.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeDPT.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeDPT.asm	(revision 363)
@@ -14,8 +14,7 @@
 ;		CF:		Clear, IDE interface only supports hard disks
 ;	Corrupts registers:
-;		AX
+;		AX, BX, CX, DX
 ;--------------------------------------------------------------------
 IdeDPT_Finalize:
-	; Fall to .StoreBlockMode
 
 ;--------------------------------------------------------------------
@@ -34,5 +33,71 @@
 	mov		ah, [es:si+ATA1.bBlckSize]		; Max block size in sectors
 	mov		[di+DPT_ATA.wSetAndMaxBlock], ax
-	; Fall to IdeDPT_StoreReversedAddressLinesFlagIfNecessary
+
+%ifdef MODULE_ADVANCED_ATA
+;--------------------------------------------------------------------
+; .StoreDeviceType
+;	Parameters:
+;		DS:DI:	Ptr to Disk Parameter Table
+;		ES:SI:	Ptr to 512-byte ATA information read from the drive
+;		CS:BP:	Ptr to IDEVARS for the controller
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+.StoreDeviceType:
+	call	IdeDPT_StoreDeviceTypeFromIdevarsInCSBPtoDPTinDSDI
+
+;--------------------------------------------------------------------
+; .StorePioModeAndTimings
+;	Parameters:
+;		DS:DI:	Ptr to Disk Parameter Table
+;		ES:SI:	Ptr to 512-byte ATA information read from the drive
+;		CS:BP:	Ptr to IDEVARS for the controller
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		AX, CX, DX
+;--------------------------------------------------------------------
+.StorePioMode:
+	call	AtaID_GetMaxPioModeToAXandMinCycleTimeToDX
+	call	AtaID_ConvertPioModeFromAXandMinCycleTimeFromDXtoActiveAndRecoveryTime
+	mov		[di+DPT_ATA.bPioMode], al
+	mov		[di+DPT_ADVANCED_ATA.wMinPioActiveTimeNs], cx
+	mov		[di+DPT_ADVANCED_ATA.wMinPioRecoveryTimeNs], dx
+
+;--------------------------------------------------------------------
+; .DetectAdvancedIdeController
+;	Parameters:
+;		DS:DI:	Ptr to Disk Parameter Table
+;		ES:SI:	Ptr to 512-byte ATA information read from the drive
+;		CS:BP:	Ptr to IDEVARS for the controller
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		AX, BX, CX, DX
+;--------------------------------------------------------------------
+.DetectAdvancedIdeController:
+	mov		dx, [cs:bp+IDEVARS.wPort]
+	mov		[di+DPT_ADVANCED_ATA.wIdeBasePort], dx
+	call	AdvAtaInit_DetectControllerForIdeBaseInDX
+	mov		[di+DPT_ADVANCED_ATA.wControllerID], ax	; Store zero if none detected
+	mov		[di+DPT_ADVANCED_ATA.wControllerBasePort], cx
+	jnc		SHORT .NoAdvancedControllerDetected
+
+	; Use highest common PIO mode from controller and drive.
+	; Many VLB controllers support PIO modes up to 2.
+	call	AdvAtaInit_GetControllerMaxPioModeToAL
+	jnc		SHORT .ChangeTo32bitDevice
+	MIN_U	[di+DPT_ATA.bPioMode], al
+
+	; We have detected 32-bit controller so change Device Type since
+	; it might have been set to 16-bit on IDEVARS
+.ChangeTo32bitDevice:
+	mov		BYTE [di+DPT_ATA.bDevice], DEVICE_32BIT_ATA
+
+.NoAdvancedControllerDetected:
+
+%endif ; MODULE_ADVANCED_ATA
 
 ;--------------------------------------------------------------------
@@ -42,5 +107,5 @@
 ;		CS:BP:	Ptr to IDEVARS for the controller
 ;	Returns:
-;		CF:		Always clear, we don't support floppies on the IDE inteface
+;		CF:		Always clear
 ;	Corrupts registers:
 ;		Nothing
@@ -53,6 +118,23 @@
 .SetFlagForSwappedA0andA3:
 	or		BYTE [di+DPT.bFlagsHigh], FLGH_DPT_REVERSED_A0_AND_A3
-
 .EndDPT:
 	clc
 	ret
+
+
+%ifdef MODULE_ADVANCED_ATA
+;--------------------------------------------------------------------
+; IdeDPT_StoreDeviceTypeFromIdevarsInCSBPtoDPTinDSDI
+;	Parameters:
+;		DS:DI:	Ptr to Disk Parameter Table
+;		CS:BP:	Ptr to IDEVARS for the controller
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		AL
+;--------------------------------------------------------------------
+IdeDPT_StoreDeviceTypeFromIdevarsInCSBPtoDPTinDSDI:
+	mov		al, [cs:bp+IDEVARS.bDevice]
+	mov		[di+DPT_ATA.bDevice], al
+	ret
+%endif
Index: trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeTransfer.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeTransfer.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Device/IDE/IdeTransfer.asm	(revision 363)
@@ -5,11 +5,10 @@
 ; This struct must not be larger than IDEPACK without INTPACK.
 struc PIOVARS
-	.wWordsInBlock			resb	2	; 0, Block size in WORDs
-	.wWordsLeft				resb	2	; 2, WORDs left to transfer
-	.wWordsDone				resb	2	; 4, Number of sectors xferred
-							resb	1	; 6,
+	.wDataPort				resb	2	; 0, IDE Data Port
+	.fnXfer					resb	2	; 2, Offset to transfer function
+	.wSectorsInBlock		resb	2	; 4, Block size in sectors
+	.bSectorsLeft			resb	1	; 6, Sectors left to transfer
 							resb	1	; 7, IDEPACK.bDeviceControl
-	.wDataPort				resb	2	; 8, IDE Data Port
-	.fnXfer					resb	2	; 10, Offset to transfer function
+	.bSectorsDone			resb	1	; 8, Number of sectors xferred
 endstruc
 
@@ -69,10 +68,10 @@
 	xchg	si, di								; ES:DI now points buffer
 
-	mov		cx, [bp+PIOVARS.wWordsInBlock]
+	mov		cx, [bp+PIOVARS.wSectorsInBlock]	; Max 128
 
 ALIGN JUMP_ALIGN
 .ReadNextBlockFromDrive:
 	mov		dx, [bp+PIOVARS.wDataPort]
-	cmp		[bp+PIOVARS.wWordsLeft], cx
+	cmp		[bp+PIOVARS.bSectorsLeft], cl
 	jbe		SHORT .ReadLastBlockFromDrive
 	call	[bp+PIOVARS.fnXfer]
@@ -84,13 +83,13 @@
 	xchg	si, di								; ES:DI now points buffer
 
-	; Increment number of successfully read WORDs
-	mov		cx, [bp+PIOVARS.wWordsInBlock]
-	sub		[bp+PIOVARS.wWordsLeft], cx
-	add		[bp+PIOVARS.wWordsDone], cx
+	; Increment number of successfully read sectors
+	mov		cx, [bp+PIOVARS.wSectorsInBlock]
+	sub		[bp+PIOVARS.bSectorsLeft], cl
+	add		[bp+PIOVARS.bSectorsDone], cl
 	jmp		SHORT .ReadNextBlockFromDrive
 
 ALIGN JUMP_ALIGN
 .ReadLastBlockFromDrive:
-	mov		cx, [bp+PIOVARS.wWordsLeft]
+	mov		cl, [bp+PIOVARS.bSectorsLeft]		; CH is already zero
 	call	[bp+PIOVARS.fnXfer]					; Transfer possibly partial block
 
@@ -103,9 +102,6 @@
 	; Return number of successfully read sectors
 ReturnWithTransferErrorInAH:
-	mov		cx, [bp+PIOVARS.wWordsDone]
-	jc		SHORT .ConvertTransferredWordsInCXtoSectors
-	add		cx, [bp+PIOVARS.wWordsLeft]			; Never sets CF
-.ConvertTransferredWordsInCXtoSectors:
-	xchg	cl, ch
+	mov		cl, [bp+PIOVARS.bSectorsDone]
+	mov		ch, 0								; Preserve CF
 	ret
 
@@ -137,10 +133,10 @@
 	jc		SHORT ReturnWithTransferErrorInAH
 
-	mov		cx, [bp+PIOVARS.wWordsInBlock]
+	mov		cx, [bp+PIOVARS.wSectorsInBlock]	; Max 128
 
 ALIGN JUMP_ALIGN
 .WriteNextBlockToDrive:
 	mov		dx, [bp+PIOVARS.wDataPort]
-	cmp		[bp+PIOVARS.wWordsLeft], cx
+	cmp		[bp+PIOVARS.bSectorsLeft], cl
 	jbe		SHORT .WriteLastBlockToDrive
 	call	[bp+PIOVARS.fnXfer]
@@ -150,13 +146,13 @@
 	jc		SHORT ReturnWithTransferErrorInAH
 
-	; Increment number of successfully written WORDs
-	mov		cx, [bp+PIOVARS.wWordsInBlock]
-	sub		[bp+PIOVARS.wWordsLeft], cx
-	add		[bp+PIOVARS.wWordsDone], cx
+	; Increment number of successfully written sectors
+	mov		cx, [bp+PIOVARS.wSectorsInBlock]
+	sub		[bp+PIOVARS.bSectorsLeft], cl
+	add		[bp+PIOVARS.bSectorsDone], cl
 	jmp		SHORT .WriteNextBlockToDrive
 
 ALIGN JUMP_ALIGN
 .WriteLastBlockToDrive:
-	mov		cx, [bp+PIOVARS.wWordsLeft]
+	mov		cl, [bp+PIOVARS.bSectorsLeft]		; CH is already zero
 %ifdef USE_186
 	push	CheckErrorsAfterTransferringLastBlock
@@ -183,10 +179,8 @@
 InitializePiovarsInSSBPwithSectorCountInAH:
 	; Store sizes
-	xor		al, al
-	mov		[bp+PIOVARS.wWordsLeft], ax
-	mov		ah, [di+DPT_ATA.bSetBlock]
-	mov		[bp+PIOVARS.wWordsInBlock], ax
-	cbw
-	mov		[bp+PIOVARS.wWordsDone], ax			; Zero
+	mov		[bp+PIOVARS.bSectorsLeft], ah
+	eMOVZX	ax, BYTE [di+DPT_ATA.bSetBlock]
+	mov		[bp+PIOVARS.wSectorsInBlock], ax
+	mov		[bp+PIOVARS.bSectorsDone], ah		; Zero
 
 	; Get transfer function based on bus type
@@ -194,6 +188,11 @@
 	mov		bl, [di+DPT.bIdevarsOffset]			; CS:BX now points to IDEVARS
 	mov		dx, [cs:bx+IDEVARS.wPort]			; Load IDE Data port address
+%ifdef MODULE_ADVANCED_ATA
+	mov		bl, [di+DPT_ATA.bDevice]
+%else
 	mov		bl, [cs:bx+IDEVARS.bDevice]			; Load device type to BX
+%endif
 	add		bx, ax
+
 	mov		[bp+PIOVARS.wDataPort], dx
 	mov		ax, [cs:bx]							; Load offset to transfer function
@@ -208,5 +207,5 @@
 ; ReadBlockFrom32bitDataPort	VLB/PCI 32-bit IDE
 ;	Parameters:
-;		CX:		Block size in WORDs
+;		CX:		Block size in 512 byte sectors
 ;		DX:		IDE Data port address
 ;		ES:DI:	Normalized ptr to buffer to receive data
@@ -218,5 +217,5 @@
 ALIGN JUMP_ALIGN
 ReadBlockFromXtideRev1:
-	eSHR_IM	cx, 2		; Loop unrolling
+	UNROLL_SECTORS_IN_CX_TO_QWORDS
 	mov		bx, 8		; Bit mask for toggling data low/high reg
 ALIGN JUMP_ALIGN
@@ -233,5 +232,5 @@
 ALIGN JUMP_ALIGN
 ReadBlockFromXtideRev2:
-	times 2 shr	cx, 1	; WORD count to QWORD count
+	UNROLL_SECTORS_IN_CX_TO_QWORDS
 ALIGN JUMP_ALIGN
 .ReadNextQword:
@@ -251,4 +250,5 @@
 ALIGN JUMP_ALIGN
 ReadBlockFrom16bitDataPort:
+	xchg	cl, ch		; Sectors to WORDs
 	rep
 	db		6Dh			; INSW (we want this in XT build)
@@ -258,5 +258,7 @@
 ALIGN JUMP_ALIGN
 ReadBlockFrom32bitDataPort:
-	shr		cx, 1		; WORD count to DWORD count
+	db		0C1h		; SHL
+	db		0E1h		; CX
+	db		7			; 7	(Sectors to DWORDs)
 	rep
 	db		66h			; Override operand size to 32-bit
@@ -272,5 +274,5 @@
 ; WriteBlockTo32bitDataPort		VLB/PCI 32-bit IDE
 ;	Parameters:
-;		CX:		Block size in WORDs
+;		CX:		Block size in 512-byte sectors
 ;		DX:		IDE Data port address
 ;		ES:SI:	Normalized ptr to buffer containing data
@@ -284,5 +286,5 @@
 	push	ds
 	push	bx
-	eSHR_IM	cx, 2		; Loop unrolling
+	UNROLL_SECTORS_IN_CX_TO_QWORDS
 	mov		bx, 8		; Bit mask for toggling data low/high reg
 	push	es			; Copy ES...
@@ -302,6 +304,6 @@
 ALIGN JUMP_ALIGN
 WriteBlockToXtideRev2:
+	UNROLL_SECTORS_IN_CX_TO_QWORDS
 	push	ds
-	eSHR_IM	cx, 2		; Loop unrolling
 	push	es			; Copy ES...
 	pop		ds			; ...to DS
@@ -320,5 +322,5 @@
 ALIGN JUMP_ALIGN
 WriteBlockToFastXtide:
-	times 2 shr	cx, 1	; WORD count to QWORD count
+	UNROLL_SECTORS_IN_CX_TO_QWORDS
 	push	ds
 	push	es
@@ -342,4 +344,5 @@
 ALIGN JUMP_ALIGN
 WriteBlockTo16bitDataPort:
+	xchg	cl, ch		; Sectors to WORDs
 	es					; Source is ES segment
 	rep
@@ -350,5 +353,7 @@
 ALIGN JUMP_ALIGN
 WriteBlockTo32bitDataPort:
-	shr		cx, 1		; WORD count to DWORD count
+	db		0C1h		; SHL
+	db		0E1h		; CX
+	db		7			; 7	(Sectors to DWORDs)
 	es					; Source is ES segment
 	rep
Index: trunk/XTIDE_Universal_BIOS/Src/Handlers/Int13h/AH9h_HInit.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Handlers/Int13h/AH9h_HInit.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Handlers/Int13h/AH9h_HInit.asm	(revision 363)
@@ -38,52 +38,70 @@
 ;		CF:		0 if successful, 1 if error
 ;	Corrupts registers:
-;		AL, BX, DX
+;		AL, BX, CX, DX
 ;--------------------------------------------------------------------
 AH9h_InitializeDriveForUse:
+	push	es
 	push	si
-	push	cx
+	call	ClearErrorFlagFromBootMenuInfo	; Do this for serial devices as well
 
 %ifdef MODULE_SERIAL
-	;
-	; no need to do this for serial devices, and we use the DPT_RESET flag bits
-	; to store the drive type for serial floppy drives (MODULE_SERIAL_FLOPPY)
-	;
+	; no need to do this for serial devices
 	xor		ah, ah
 	test	byte [di+DPT.bFlagsHigh], FLGH_DPT_SERIAL_DEVICE	; Clears CF
-	jnz		.ReturnNotSuccessful
+	jnz		.ReturnWithErrorCodeInAH
+
+%else
+	; Clear Initialization Error flag from DPT
+	and		BYTE [di+DPT.bFlagsHigh], ~FLGH_DPT_INITERROR
 %endif
 
 	; Try to select drive and wait until ready
-	or		BYTE [di+DPT.bFlagsHigh], MASKH_DPT_RESET		; Everything uninitialized
 	call	AccessDPT_GetDriveSelectByteToAL
 	mov		[bp+IDEPACK.bDrvAndHead], al
 	call	Device_SelectDrive
-	jc		SHORT .ReturnNotSuccessful
-	and		BYTE [di+DPT.bFlagsHigh], ~FLGH_DPT_RESET_nDRDY	; Clear since success
+	mov		al, FLG_INIT_FAILED_TO_SELECT_DRIVE
+	call	SetErrorFlagFromALwithErrorCodeInAHtoBootMenuInfo
+	jc		SHORT .ReturnWithErrorCodeInAH
 
 	; Initialize CHS parameters if LBA is not used
 	call	InitializeDeviceParameters
-	jc		SHORT .SetWriteCache
-	and		BYTE [di+DPT.bFlagsHigh], ~FLGH_DPT_RESET_nINITPRMS
+	mov		al, FLG_INIT_FAILED_TO_INITIALIZE_CHS_PARAMETERS
+	call	SetErrorFlagFromALwithErrorCodeInAHtoBootMenuInfo
 
 	; Enable or Disable Write Cache
-.SetWriteCache:
 	call	SetWriteCache
+	mov		al, FLG_INIT_FAILED_TO_SET_WRITE_CACHE
+	call	SetErrorFlagFromALwithErrorCodeInAHtoBootMenuInfo
 
 	; Recalibrate drive by seeking to cylinder 0
 .RecalibrateDrive:
 	call	AH11h_RecalibrateDrive
-	jc		SHORT .InitializeBlockMode
-	and		BYTE [di+DPT.bFlagsHigh], ~FLGH_DPT_RESET_nRECALIBRATE
+	mov		al, FLG_INIT_FAILED_TO_RECALIBRATE_DRIVE
+	call	SetErrorFlagFromALwithErrorCodeInAHtoBootMenuInfo
 
 	; Initialize block mode transfers
 .InitializeBlockMode:
 	call	InitializeBlockMode
-	jc		SHORT .ReturnNotSuccessful
-	and		BYTE [di+DPT.bFlagsHigh], ~FLGH_DPT_RESET_nSETBLOCK	; Keeps CF clear
-
-.ReturnNotSuccessful:
-	pop		cx
+	mov		al, FLG_INIT_FAILED_TO_SET_BLOCK_MODE
+	call	SetErrorFlagFromALwithErrorCodeInAHtoBootMenuInfo
+
+%ifdef MODULE_ADVANCED_ATA
+; Initialize fastest supported PIO mode
+.InitializePioMode:
+	call	InitializePioMode
+	mov		al, FLG_INIT_FAILED_TO_SET_PIO_MODE
+	call	SetErrorFlagFromALwithErrorCodeInAHtoBootMenuInfo
+%endif
+
+	; There might have been several errors so just return
+	; one error code for them all
+	test	BYTE [di+DPT.bFlagsHigh], FLGH_DPT_INITERROR
+	jz		SHORT .ReturnWithErrorCodeInAH
+	mov		ah, RET_HD_RESETFAIL
+	stc
+
+.ReturnWithErrorCodeInAH:
 	pop		si
+	pop		es
 	ret
 
@@ -140,4 +158,28 @@
 
 
+%ifdef MODULE_ADVANCED_ATA
+;--------------------------------------------------------------------
+; InitializePioMode
+;	Parameters:
+;		DS:DI:	Ptr to DPT (in RAMVARS segment)
+;	Returns:
+;		AH:		BIOS Error code
+;		CF:		Cleared if successful
+;				Set if any error
+;	Corrupts registers:
+;		AL, BX, CX, DX
+;--------------------------------------------------------------------
+InitializePioMode:
+	xor		dx, dx						; Parameter to Sector Count Register = 0 = PIO_DEFAULT_MODE
+	mov		al, [di+DPT_ATA.bPioMode]
+	cmp		al, 3						; PIO mode 3 and above require IORDY
+	jb		SHORT .SetTransferMode
+	or		dl, al
+.SetTransferMode:
+	mov		si, FEATURE_SET_TRANSFER_MODE
+	jmp		AH23h_SetControllerFeatures
+%endif
+
+
 ;--------------------------------------------------------------------
 ; InitializeBlockMode
@@ -160,5 +202,53 @@
 	eCMOVNZ	al, [di+DPT_ATA.bMaxBlock]	; Load max block size
 	jmp		AH24h_SetBlockSize
+
+
+;--------------------------------------------------------------------
+; ClearErrorFlagFromBootMenuInfo
+;	Parameters:
+;		DS:DI:	Ptr to DPT
+;	Returns:
+;		Nothing
+;	Corrupts registers:
+;		BX, ES
+;--------------------------------------------------------------------
+ClearErrorFlagFromBootMenuInfo:
+	call	BootMenuInfo_IsAvailable	; Load BOOTMENUINFO segment to ES
+	jne		SHORT .DoNotStoreErrorFlags
+	call	BootMenuInfo_ConvertDPTtoBX
+	mov		WORD [es:bx+BOOTMENUINFO.wInitErrorFlags], 0	; Must clear whole WORD!
+.DoNotStoreErrorFlags:
+	ret
+
+
+;--------------------------------------------------------------------
+; SetErrorFlagFromALwithErrorCodeInAHtoBootMenuInfo
+;	Parameters:
+;		AH:		BIOS Error Code
+;		AL:		Error flag to set
+;		DS:DI:	Ptr to DPT
+;	Returns:
+;		CF:		Clear if no error
+;				Set if error flag was set
+;	Corrupts registers:
+;		BX, ES
+;--------------------------------------------------------------------
+SetErrorFlagFromALwithErrorCodeInAHtoBootMenuInfo:
+	jnc		SHORT NoErrorFlagToSet
+	cmp		ah, RET_HD_INVALID
+	jbe		SHORT .IgnoreInvalidCommandError
+
+	call	BootMenuInfo_IsAvailable
+	jne		SHORT .BootvarsNotAvailableSoDoNotSetErrorFlag
+
+	call	BootMenuInfo_ConvertDPTtoBX
+	or		[es:bx+BOOTMENUINFO.wInitErrorFlags], al
+.BootvarsNotAvailableSoDoNotSetErrorFlag:
+	or		BYTE [di+DPT.bFlagsHigh], FLGH_DPT_INITERROR
+	stc
+	ret
+.IgnoreInvalidCommandError:
 ReturnSuccessSinceInitializationNotNeeded:
 	xor		ah, ah
-	ret
+NoErrorFlagToSet:
+	ret
Index: trunk/XTIDE_Universal_BIOS/Src/Handlers/Int13h/AHDh_HReset.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Handlers/Int13h/AHDh_HReset.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Handlers/Int13h/AHDh_HReset.asm	(revision 363)
@@ -53,8 +53,8 @@
 
 	; Initialize Master and Slave drives
-	eMOVZX	ax, [di+DPT.bIdevarsOffset]			; (AL) pointer to controller we are looking to reset
+	eMOVZX	ax, BYTE [di+DPT.bIdevarsOffset]	; (AL) pointer to controller we are looking to reset
 												; (AH) initialize error code, assume success
 
-	mov		si, IterateAndResetDrives
+	mov		si, IterateAndResetDrives			; Callback function for FindDPT_IterateAllDPTs
 	call	FindDPT_IterateAllDPTs
 
@@ -73,4 +73,14 @@
 ; merged into overall error code for this controller.  Master will be reset
 ; first.  Note that the iteration will go until the end of the DPT list.
+;
+;	Parameters:
+;		AL:		Offset to IDEVARS for drives to initialize
+;		AH:		Error status from previous initialization
+;		DS:DI:	Ptr to DPT to examine
+;	Returns:
+;		AH:		Error status from initialization
+;		CF:		Set to iterate all DPTs
+;	Corrupts registers:
+;		AL, BX, DX
 ;--------------------------------------------------------------------
 IterateAndResetDrives:
@@ -78,5 +88,14 @@
 	jne		.done
 	push	ax
+	push	cx
 	call	AH9h_InitializeDriveForUse			; Reset Master and Slave (Master will come first in DPT list)
+
+%ifdef MODULE_ADVANCED_ATA
+	jc		SHORT .SkipControllerInitSinceError
+	call	InitializeAdvancedIdeControllers	; Done after drive init so drives are first set to advanced PIO mode, then the controller
+.SkipControllerInitSinceError:
+%endif
+
+	pop		cx
 	pop		ax
 	jnc		.done
@@ -85,2 +104,36 @@
 	stc											; From IterateAllDPTs perspective, the DPT is never found (continue iteration)
 	ret
+
+
+%ifdef MODULE_ADVANCED_ATA
+;--------------------------------------------------------------------
+; Here we initialize the more advanced controllers (VLB and PCI)
+; to get better performance for systems with 32-bit bus.
+;
+; This step is optional since the controllers use slowest possible
+; settings by default if they are not initialized.
+;
+; InitializeAdvancedIdeController
+;	Parameters:
+;		DS:DI:	Ptr to DPT
+;	Returns:
+;		CF:		Cleared if success or no controller to initialize
+;				Set if error
+;	Corrupts registers:
+;		AX, BX, CX, DX
+;--------------------------------------------------------------------
+InitializeAdvancedIdeControllers:
+	; We want to initialize the advanced controller only after both
+	; Master and Slave drive are initialized to correct PIO mode.
+	; We check if next DPT is for the same IDE controller. If it is,
+	; we skip the initialization.
+	mov		al, [di+DPT.bIdevarsOffset]
+	cmp		al, [di++LARGEST_DPT_SIZE+DPT.bIdevarsOffset]
+	je		SHORT .SkipInitializationUntilNextDrive	; CF cleared
+
+	jmp		AdvAtaInit_InitializeControllerForDPTinDSDI
+.SkipInitializationUntilNextDrive:
+	clc
+	ret
+
+%endif	; MODULE_ADVANCED_ATA
Index: trunk/XTIDE_Universal_BIOS/Src/Initialization/AdvAtaInit.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Initialization/AdvAtaInit.asm	(revision 363)
+++ trunk/XTIDE_Universal_BIOS/Src/Initialization/AdvAtaInit.asm	(revision 363)
@@ -0,0 +1,135 @@
+; Project name	:	XTIDE Universal BIOS
+; Description	:	Common functions for initializing different
+;					VLB and PCI IDE Controllers.
+
+; Section containing code
+SECTION .text
+
+;--------------------------------------------------------------------
+; AdvAtaInit_DetectControllerForIdeBaseInDX
+;	Parameters:
+;		DX:		IDE Controller base port
+;	Returns:
+;		AX:		ID WORD specific for detected controller
+;				Zero if no controller detected
+;		CX:		Controller base port (not IDE)
+;		CF:		Set if controller detected
+;				Cleared if no controller
+;	Corrupts registers:
+;		BX
+;--------------------------------------------------------------------
+AdvAtaInit_DetectControllerForIdeBaseInDX:
+	call	Vision_DetectAndReturnIDinAXandPortInCXifControllerPresent
+	jne		SHORT .NoAdvancedControllerForPortDX
+	call	Vision_DoesIdePortInDXbelongToControllerWithIDinAX
+	jne		SHORT .NoAdvancedControllerForPortDX
+
+	stc		; Advanced Controller found for port DX
+	ret
+
+.NoAdvancedControllerForPortDX:
+	xor		ax, ax
+	ret
+
+
+;--------------------------------------------------------------------
+; AdvAtaInit_GetControllerMaxPioModeToAL
+;	Parameters:
+;		AX:		ID WORD specific for detected controller
+;	Returns:
+;		AL:		Max supported PIO mode (if CF set)
+;		CF:		Set if PIO limit necessary
+;				Cleared if no need to limit timings
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+AdvAtaInit_GetControllerMaxPioModeToAL	equ	Vision_GetMaxPioModeToAL
+
+
+;--------------------------------------------------------------------
+; AdvAtaInit_InitializeControllerForDPTinDSDI
+;	Parameters:
+;		DS:DI:	Ptr to DPT for Single or Slave Drive
+;	Returns:
+;		CF:		Cleared if success or no controller to initialize
+;				Set if error
+;	Corrupts registers:
+;		AX, BX, CX, DX
+;--------------------------------------------------------------------
+AdvAtaInit_InitializeControllerForDPTinDSDI:
+	push	ds
+	push	si
+	push	di
+
+	; PIO and Advanced Controller variables are stored to BOOTMENUINFO
+	; to keep the DPTs as small as possible.
+	call	GetMasterAndSlaveBootMenuInfosToSIandDI
+	cmp		WORD [BOOTVARS.wMagicWord], BOOTVARS_MAGIC_WORD
+	clc
+	jne		SHORT .BootMenuInfosAreNoLongerAvailable
+
+	; Call Controller Specific initialization function
+	mov		ax, [si+BOOTMENUINFO.wControllerID]
+	test	ax, ax
+	jz		SHORT .NoAdvancedController
+	call	Vision_InitializeWithIDinAHandConfigInAL	; The only we support at the moment
+
+.BootMenuInfosAreNoLongerAvailable:
+.NoAdvancedController:
+	pop		di
+	pop		si
+	pop		ds
+	ret
+
+
+;--------------------------------------------------------------------
+; AdvAtaInit_GetMasterAndSlaveBootMenuInfosToSIandDI
+;	Parameters:
+;		DS:DI:	Ptr to DPT for Single or Slave Drive
+;	Returns:
+;		DS:SI:	Ptr to Single or Master Drive BOOTMENUINFO
+;		DI:		Offset to Slave Drive BOOTMENUINFO
+;				Zero if Slave Drive not present
+;	Corrupts registers:
+;		BX, DX, (DS will change!)
+;--------------------------------------------------------------------
+GetMasterAndSlaveBootMenuInfosToSIandDI:
+	call	BootMenuInfo_ConvertDPTtoBX
+	LOAD_BDA_SEGMENT_TO	ds, di, !				; Zero DI to assume no Slave Drive present
+
+	mov		dx, [bx+BOOTMENUINFO.wIdeBasePort]	; Load IDE Port from Single or Slave Drive
+	lea		si, [bx+BOOTMENUINFO_size]			; SI points to Slave Drive if present
+	cmp		dx, [si+BOOTMENUINFO.wIdeBasePort]
+	jne		SHORT .BootMenuInfoForSingleDriveInDSBX
+
+	mov		di, si								; Slave Drive detected, copy pointer to DS:DI
+.BootMenuInfoForSingleDriveInDSBX:
+	mov		si, bx
+	ret
+
+
+;--------------------------------------------------------------------
+; AdvAtaInit_SelectSlowestTimingsToBXandCX
+;	Parameters:
+;		DS:SI:	Ptr to BOOTMENUINFO for Master or Single Drive
+;		DI:		Offset to BOOTMENUINFO for Slave Drive
+;				Zero if Slave Drive not present
+;	Returns:
+;		BX:		Min Active Time in nanosecs
+;		CX:		Min Recovery Time in nanosecs
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+AdvAtaInit_SelectSlowestTimingsToBXandCX:
+	mov		bx, [si+BOOTMENUINFO.wMinPioActiveTimeNs]
+	mov		cx, [si+BOOTMENUINFO.wMinPioRecoveryTimeNs]
+	test	di, di
+	jz		SHORT .ReturnSlowestTimingInBXandCX	; No Slave Drive
+
+	; If Active Time is greater, then must be the Recovery Time as well
+	cmp		bx, [di+BOOTMENUINFO.wMinPioActiveTimeNs]
+	jbe		SHORT .ReturnSlowestTimingInBXandCX
+	mov		bx, [di+BOOTMENUINFO.wMinPioActiveTimeNs]
+	mov		cx, [di+BOOTMENUINFO.wMinPioRecoveryTimeNs]
+.ReturnSlowestTimingInBXandCX:
+	ret
Index: trunk/XTIDE_Universal_BIOS/Src/Initialization/AtaID.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Initialization/AtaID.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Initialization/AtaID.asm	(revision 363)
@@ -72,2 +72,84 @@
 	mul		WORD [di+ATA1.wCylCnt]	; DX:AX=Sectors per track * number of heads * number of cylinders
 	ret
+
+
+%ifdef MODULE_ADVANCED_ATA
+;--------------------------------------------------------------------
+; AtaID_GetMaxPioModeToAXandMinCycleTimeToDX
+;	Parameters:
+;		ES:SI:	Ptr to 512-byte ATA information read from the drive
+;	Returns:
+;		AX:		Max supported PIO mode
+;		DX:		Minimum Cycle Time in nanosecs
+;	Corrupts registers:
+;		BX
+;--------------------------------------------------------------------
+AtaID_GetMaxPioModeToAXandMinCycleTimeToDX:
+	; Get PIO mode and cycle time for PIO 0...2
+	mov		bx, [es:si+ATA1.bPioMode]
+	shl		bx, 1					; Shift for WORD lookup
+	mov		dx, [cs:bx+.rgwPio0to2CycleTimeInNanosecs]
+	shr		bx, 1
+	xchg	ax, bx					; AL = PIO mode 0, 1 or 2
+
+	; Check if Advanced PIO modes are supported (3 and above)
+	test	BYTE [es:si+ATA2.wFields], A2_wFields_64to70
+	jz		SHORT .ReturnPioTimings
+
+	; Get Advanced PIO mode
+	; (Hard Disks supports up to 4 but CF cards might support 5)
+	mov		bx, [es:si+ATA2.bPIOSupp]
+.CheckNextFlag:
+	inc		ax
+	shr		bx, 1
+	jnz		SHORT .CheckNextFlag
+	mov		dx, [es:si+ATA2.wPIOMinCyF]	; Advanced modes use IORDY
+.ReturnPioTimings:
+	ret
+
+
+.rgwPio0to2CycleTimeInNanosecs:
+	dw		PIO_0_MIN_CYCLE_TIME_NS
+	dw		PIO_1_MIN_CYCLE_TIME_NS
+	dw		PIO_2_MIN_CYCLE_TIME_NS
+
+
+;--------------------------------------------------------------------
+; AtaID_ConvertPioModeFromAXandMinCycleTimeFromDXtoActiveAndRecoveryTime
+;	Parameters:
+;		AX:		Max supported PIO mode
+;		DX:		Minimum PIO Cycle Time in nanosecs
+;	Returns:
+;		CX:		Minimum Active time in nanosecs
+;		DX:		Minimum Recovery time in nanosecs
+;	Corrupts registers:
+;		BX
+;--------------------------------------------------------------------
+AtaID_ConvertPioModeFromAXandMinCycleTimeFromDXtoActiveAndRecoveryTime:
+	; Subtract Address Valid Time (t1) from Cycle Time (t0)
+	mov		bx, ax
+	eMOVZX	cx, BYTE [cs:bx+.rgbPioModeToAddressValidTimeNs]
+	sub		dx, cx
+
+	; Subtract Active Time (t2) from previous result to get Recovery Time (t2i)
+	shl		bx, 1			; Shift PIO Mode for WORD lookup
+	mov		cx, [cs:bx+.rgwPioModeToActiveTimeNs]
+	sub		dx, cx
+	ret
+
+
+.rgbPioModeToAddressValidTimeNs:
+	db		PIO_0_MIN_ADDRESS_VALID_NS
+	db		PIO_1_MIN_ADDRESS_VALID_NS
+	db		PIO_2_MIN_ADDRESS_VALID_NS
+	db		PIO_3_MIN_ADDRESS_VALID_NS
+	db		PIO_4_MIN_ADDRESS_VALID_NS
+
+.rgwPioModeToActiveTimeNs:
+	dw		PIO_0_MIN_ACTIVE_TIME_NS
+	dw		PIO_1_MIN_ACTIVE_TIME_NS
+	dw		PIO_2_MIN_ACTIVE_TIME_NS
+	dw		PIO_3_MIN_ACTIVE_TIME_NS
+	dw		PIO_4_MIN_ACTIVE_TIME_NS
+
+%endif ; MODULE_ADVANCED_ATA
Index: trunk/XTIDE_Universal_BIOS/Src/Initialization/DetectDrives.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Initialization/DetectDrives.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Initialization/DetectDrives.asm	(revision 363)
@@ -204,4 +204,2 @@
 	call	BootMenuInfo_CreateForHardDisk
 	jmp		short DetectPrint_DriveNameFromBootnfoInESBX
-
-
Index: trunk/XTIDE_Universal_BIOS/Src/Initialization/Initialize.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Initialization/Initialize.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Initialization/Initialize.asm	(revision 363)
@@ -53,4 +53,5 @@
 ;--------------------------------------------------------------------
 Initialize_AndDetectDrives:
+	mov		WORD [es:BOOTVARS.wMagicWord], BOOTVARS_MAGIC_WORD
 	call	BootMenuPrint_InitializeDisplayContext
 	call	DetectPrint_RomFoundAtSegment
@@ -98,3 +99,4 @@
 	call	AH0h_ResetAllOurHardDisksAtTheEndOfDriveInitialization
 	add		sp, BYTE EXTRA_BYTES_FOR_INTPACK
+	mov		WORD [es:BOOTVARS.wMagicWord], 0
 	ret
Index: trunk/XTIDE_Universal_BIOS/Src/Initialization/Vision.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Initialization/Vision.asm	(revision 363)
+++ trunk/XTIDE_Universal_BIOS/Src/Initialization/Vision.asm	(revision 363)
@@ -0,0 +1,223 @@
+; Project name	:	XTIDE Universal BIOS
+; Description	:	Functions for initializing QDI Vision
+;					QD6500 and QD6580 VLB IDE Controllers.
+
+; Section containing code
+SECTION .text
+
+;--------------------------------------------------------------------
+; Vision_DetectAndReturnIDinAXandPortInCXifControllerPresent
+;	Parameters:
+;		Nothing
+;	Returns:
+;		AX:		ID WORD specific for QDI Vision Controllers
+;				(AL = QD65xx Config Register contents)
+;				(AH = QDI Vision Controller ID (bits 4...7))
+;		CX:		Controller port (not IDE port)
+;		ZF:		Set if controller found
+;				Cleared if supported controller not found (AX,DX = undefined)
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+Vision_DetectAndReturnIDinAXandPortInCXifControllerPresent:
+	; Check QD65xx base port
+	mov		cx, QD65XX_BASE_PORT
+	in		al, QD65XX_BASE_PORT + QD65XX_CONFIG_REGISTER_in
+	call	IsConfigRegisterWithIDinAL
+	je		SHORT VisionControllerDetected
+
+	; Check QD65xx alternative base port
+	or		cl, QD65XX_ALTERNATIVE_BASE_PORT
+	in		al, QD65XX_ALTERNATIVE_BASE_PORT + QD65XX_CONFIG_REGISTER_in
+	; Fall to IsConfigRegisterWithIDinAL
+
+;--------------------------------------------------------------------
+; IsConfigRegisterWithIDinAL
+;	Parameters:
+;		AL:		Possible QD65xx Config Register contents
+;	Returns:
+;		AH		QDI Vision Controller ID or undefined
+;		ZF:		Set if controller found
+;				Cleared if supported controller not found (AH = undefined)
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+IsConfigRegisterWithIDinAL:
+	mov		ah, al
+	and		ah, MASK_QDCONFIG_CONTROLLER_ID
+	cmp		ah, ID_QD6500 << 4
+	je		SHORT VisionControllerDetected
+	cmp		ah, ID_QD6580 << 4
+	je		SHORT VisionControllerDetected
+	cmp		ah, ID_QD6580_ALTERNATE << 4
+VisionControllerDetected:
+	ret
+
+
+;--------------------------------------------------------------------
+; Vision_DoesIdePortInDXbelongToControllerWithIDinAX
+;	Parameters:
+;		AX:		ID WORD specific for QDI Vision Controllers
+;				(AL = QD65xx Config Register contents)
+;				(AH = QDI Vision Controller ID (bits 4...7))
+;		CX:		Vision Controller port
+;		DX:		IDE base port to check
+;	Returns:
+;		ZF:		Set if port belongs to controller
+;				Cleared if port belongs to another controller
+;	Corrupts registers:
+;		BX
+;--------------------------------------------------------------------
+Vision_DoesIdePortInDXbelongToControllerWithIDinAX:
+	cmp		ah, ID_QD6500 << 4
+	je		SHORT .DoesIdePortInDXbelongToQD6500
+
+	; QD6580 always have Primary IDE at 1F0h
+	; Secondary IDE at 170h can be enabled or disabled
+	cmp		dx, DEVICE_ATA_DEFAULT_PORT
+	je		SHORT .ReturnResultInZF
+
+	; Check if Secondary IDE channel is enabled
+	xchg	bx, ax		; Backup AX
+	xchg	dx, cx		; Swap ports
+
+	add		dx, BYTE QD6580_CONTROL_REGISTER
+	in		al, dx
+	sub		dx, BYTE QD6580_CONTROL_REGISTER
+
+	xchg	cx, dx
+	xchg	ax, bx		; Restore AX, Control Register to BL
+	test	bl, FLG_QDCONTROL_SECONDARY_DISABLED_in
+	jz		SHORT .CompareDXtoSecondaryIDE
+	ret
+
+	; QD6500 has only one IDE channel that can be at 1F0h or 170h
+.DoesIdePortInDXbelongToQD6500:
+	test	al, FLG_QDCONFIG_PRIMARY_IDE
+	jz		SHORT .CompareDXtoSecondaryIDE
+	cmp		dx, DEVICE_ATA_DEFAULT_PORT
+	ret
+
+.CompareDXtoSecondaryIDE:
+	cmp		dx, DEVICE_ATA_DEFAULT_SECONDARY_PORT
+.ReturnResultInZF:
+	ret
+
+
+;--------------------------------------------------------------------
+; Vision_GetMaxPioModeToAL
+;	Parameters:
+;		AX:		ID WORD specific for QDI Vision Controllers
+;				(AH = QDI Vision Controller ID (bits 4...7))
+;	Returns:
+;		AL:		Max supported PIO mode (if CF set)
+;		CF:		Set if PIO limit necessary
+;				Cleared if no need to limit timings
+;	Corrupts registers:
+;		Nothing
+;--------------------------------------------------------------------
+Vision_GetMaxPioModeToAL:
+	cmp		ah, ID_QD6500 << 4
+	clc
+	jne		SHORT .NoNeedToLimitForQD6580
+
+	mov		al, 2	; Limit to PIO 2 because QD6500 supports PIO 3 but without IORDY
+	stc
+.NoNeedToLimitForQD6580:
+	ret
+
+
+;--------------------------------------------------------------------
+; Vision_InitializeWithIDinAHandConfigInAL
+;	Parameters:
+;		AX:		ID WORD specific for QDI Vision Controllers
+;				(AL = QD65xx Config Register contents)
+;				(AH = QDI Vision Controller ID (bits 4...7))
+;		DS:SI:	Ptr to BOOTMENUINFO for Single or Master Drive
+;		DS:DI:	Ptr to BOOTMENUINFO for Slave Drive
+;				Zero if Slave not present
+;	Returns:
+;		CF:		Cleared if success
+;				Set if error
+;	Corrupts registers:
+;		AX, BX, CX, DX, SI, DI
+;--------------------------------------------------------------------
+Vision_InitializeWithIDinAHandConfigInAL:
+	; QD6580 has a Control Register that needs to be programmed
+	mov		dx, [si+BOOTMENUINFO.wControllerBasePort]
+	cmp		ah, ID_QD6500 << 4
+	je		SHORT .GetPioTimingsInNanosecs
+
+	; Program QD6580 Control Register (not available on QD6500) to
+	; Enable or Disable Read-Ahead and Post-Write Buffer to match
+	; jumper setting on the multi I/O card.
+.ProgramControlRegisterForQD6580:
+	xchg	bx, ax									; Backup AX
+	add		dx, BYTE QD6580_CONTROL_REGISTER
+	in		al, dx									; Read to get ATAPI jumper status
+	test	al, FLG_QDCONTROL_HDONLY_in
+	mov		al, MASK_QDCONTROL_FLAGS_TO_SET
+	jz		SHORT .SkipHdonlyBitSinceAtapiPossible
+	or		al, FLG_QDCONTROL_NONATAPI
+.SkipHdonlyBitSinceAtapiPossible:
+	out		dx, al
+	sub		dx, BYTE QD6580_CONTROL_REGISTER		; Back to base port
+	xchg	ax, bx									; Restore AX
+
+	; If we have Master and Slave drive in the system, we must select
+	; timings from the slower drive (this is why it is a bad idea to use
+	; fast and slow drive on the same IDE channel)
+.GetPioTimingsInNanosecs:
+	call	AdvAtaInit_SelectSlowestTimingsToBXandCX
+
+	; Now we need to determine is the drive connected to the Primary or Secondary channel.
+	; QD6500 has only one channel that can be Primary at 1F0h or Secondary at 170h.
+	; QD6580 always has Primary channel at 1F0h. Secondary channel at 170h can be Enabled or Disabled.
+	cmp		ah, ID_QD6500 << 4
+	je		SHORT .CalculateTimingTicksForQD6500
+	cmp		WORD [si+BOOTMENUINFO.wIdeBasePort], DEVICE_ATA_DEFAULT_PORT
+	je		SHORT .CalculateTimingTicksForQD6580
+	times 2 inc dx									; Secondary Channel IDE Timing Register
+
+	; Now we must translate the PIO timing nanosecs in CX and DX to VLB ticks
+	; suitable for QD65xx IDE Timing Register.
+	; Both of the controllers require slightly different calculations.
+.CalculateTimingTicksForQD6580:
+	mov		si, QD6580_MIN_ACTIVE_TIME_CLOCKS
+	mov		di, QD6580_MAX_ACTIVE_TIME_CLOCKS
+	jmp		SHORT .CalculateTimingForQD65xx
+
+.CalculateTimingTicksForQD6500:
+	mov		si, QD6500_MIN_ACTIVE_TIME_CLOCKS
+	mov		di, QD6500_MAX_ACTIVE_TIME_CLOCKS
+
+.CalculateTimingForQD65xx:
+	test	al, FLG_QDCONFIG_ID3		; Set ZF if 40 MHz VLB bus
+	mov		al, VLB_33MHZ_CYCLE_TIME	; Assume 33 MHz or slower VLB bus
+	xchg	ax, bx						; Active Time to AX
+	eCMOVZ	bl, VLB_40MHZ_CYCLE_TIME
+
+	div		bl
+	inc		ax							; Round up
+	xor		ah, ah
+	xchg	cx, ax						; CX = Active Time in VLB ticks
+	MAX_U	cx, si						; Limit ticks to valid values...
+	MIN_U	cx, di						; ...for QD65xx
+
+	div		bl
+	inc		ax							; Round up
+	xchg	bx, ax						; BL = Recovery Time in VLB ticks
+	mov		al, QD65xx_MAX_RECOVERY_TIME_CLOCKS
+	MAX_U	bl, QD65xx_MIN_RECOVERY_TIME_CLOCKS
+	MIN_U	bl, al
+
+	; Not done yet, we need to invert the ticks since 0 is the slowest
+	; value on the timing register
+	sub		di, cx						; DI = Active Time value to program
+	sub		al, bl						; AL = Recovery Time value to program
+
+	; Finally we can shift the values in places and program the Timing Register
+	eSHIFT_IM	al, POSITON_QD65XXIDE_RECOVERY_TIME, shl
+	or		ax, di
+	out		dx, al
+	ret									; Return with CF cleared
Index: trunk/XTIDE_Universal_BIOS/Src/Main.asm
===================================================================
--- trunk/XTIDE_Universal_BIOS/Src/Main.asm	(revision 361)
+++ trunk/XTIDE_Universal_BIOS/Src/Main.asm	(revision 363)
@@ -34,4 +34,5 @@
 	%include "IDE_8bit.inc"			; For IDE 8-bit data port macros
 	%include "DeviceIDE.inc"		; For IDE device equates
+	%include "Vision.inc"			; For QDI Vision QD65xx VLB IDE Controllers
 
 
@@ -69,4 +70,10 @@
 %endif
 
+%ifdef MODULE_ADVANCED_ATA
+	MAIN_FLG_MODULE_ADVATA	equ	FLG_ROMVARS_MODULE_ADVATA
+%else
+	MAIN_FLG_MODULE_ADVATA	equ	0
+%endif
+
 
 ;---------------------------;
@@ -74,5 +81,5 @@
 ;---------------------------;
 %ifdef USE_AT
-	at	ROMVARS.wFlags,			dw	FLG_ROMVARS_FULLMODE | FLG_ROMVARS_DRVXLAT | MAIN_FLG_MODULE_SERIAL | MAIN_FLG_MODULE_EBIOS | MAIN_FLG_MODULE_JRIDE
+	at	ROMVARS.wFlags,			dw	FLG_ROMVARS_FULLMODE | FLG_ROMVARS_DRVXLAT | MAIN_FLG_MODULE_SERIAL | MAIN_FLG_MODULE_EBIOS | MAIN_FLG_MODULE_JRIDE | MAIN_FLG_MODULE_ADVATA
 	at	ROMVARS.wDisplayMode,	dw	DEFAULT_TEXT_MODE
 	at	ROMVARS.wBootTimeout,	dw	BOOT_MENU_DEFAULT_TIMEOUT
@@ -89,6 +96,6 @@
 	at	ROMVARS.ideVars0+IDEVARS.drvParamsSlave+DRVPARAMS.wFlags,	db	DISABLE_WRITE_CACHE | FLG_DRVPARAMS_BLOCKMODE
 
-	at	ROMVARS.ideVars1+IDEVARS.wPort,			dw	170h			; Controller Command Block base port
-	at	ROMVARS.ideVars1+IDEVARS.wPortCtrl,		dw	370h			; Controller Control Block base port
+	at	ROMVARS.ideVars1+IDEVARS.wPort,			dw	DEVICE_ATA_DEFAULT_SECONDARY_PORT
+	at	ROMVARS.ideVars1+IDEVARS.wPortCtrl,		dw	DEVICE_ATA_DEFAULT_SECONDARY_PORTCTRL
 	at	ROMVARS.ideVars1+IDEVARS.bDevice,		db	DEVICE_16BIT_ATA
 	at	ROMVARS.ideVars1+IDEVARS.bIRQ,			db	0
@@ -117,5 +124,5 @@
 ; XT and XT+ Build default settings ;
 ;-----------------------------------;
-	at	ROMVARS.wFlags,			dw	FLG_ROMVARS_DRVXLAT | MAIN_FLG_MODULE_SERIAL | MAIN_FLG_MODULE_EBIOS | MAIN_FLG_MODULE_JRIDE
+	at	ROMVARS.wFlags,			dw	FLG_ROMVARS_DRVXLAT | MAIN_FLG_MODULE_SERIAL | MAIN_FLG_MODULE_EBIOS | MAIN_FLG_MODULE_JRIDE | MAIN_FLG_MODULE_ADVATA
 	at	ROMVARS.wDisplayMode,	dw	DEFAULT_TEXT_MODE
 	at	ROMVARS.wBootTimeout,	dw	BOOT_MENU_DEFAULT_TIMEOUT
@@ -205,4 +212,8 @@
 
 	; IDE Device support
+%ifdef MODULE_ADVANCED_ATA
+	%include "AdvAtaInit.asm"		; For initializing VLB and PCI controllers
+	%include "Vision.asm"			; QDI Vision QD6500 and QD6580 support
+%endif
 %define IDEDEVICE Ide
 %define ASSEMBLE_SHARED_IDE_DEVICE_FUNCTIONS
