Modification of GigaByte GA-586T2 BIOS rev 2.2 for support of HDD above 32 GiB

With new bigger hard disks available, more often is seen problem with old BIOSes those do not support bigger disks. Special case is Award BIOS 4.51PG prior to 05/1999 that freezes with disks above 34 GB. The only possibility how to boot the computer is to set the disk to "None". Some operating systems can use these disks, but not all of them will correctly set up the chipset registers for fastest operation - so the disk could be slow. And of course, it is not possible to boot from such a disk. The solutions are:

This document describes all modification that are necessary to modify Award 4.51PG BIOS that does not support disks above 34 GB. The modification is described in detail for GA586T2 BIOS ver. 2.2 so anyone with sufficient knowledge can modify other BIOSes too.

First modification - breaking the 32 GiB (34 GB) barrier

The behavior of bad BIOS is that it freezes during disk detection just after memory test. Here is the code:
E000:0D0D  89 04			mov	[si],ax
E000:0D0F  26: 8B 45 02			mov	ax,WORD PTR ES:[DI+1*2]		;word 1 = cylinders
E000:0D13  26: 8A 75 06			mov	dh,BYTE PTR ES:[DI+3*2]		;word 3 = heads
E000:0D17  26: 8A 55 0C			mov	dl,BYTE PTR ES:[DI+6*2]		;word 6 = sectors
E000:0D1B  26: F6 45 01 80		test	byte ptr es:[di+1],80h		;Is ATA device?
E000:0D20  75 29			jnz	short @F			;No,skip
E000:0D22  26: F6 45 63 02		test	byte ptr ES:[DI+49*2+1],2	;check word 49(LBA) to
E000:0D27  74 22			jz	short @F			;confirm word 60-61 is valid?
E000:0D29  26: 66| 83 7D 78 00		cmp	dword ptr ES:[DI+60*2],0
E000:0D2F  74 1A			je	short @F
E000:0D31  26: 66| 83 7D 78 FF		cmp	dword ptr ES:[DI+60*2],0ffffffffh
E000:0D37  74 12			je	short @F
E000:0D39  52				push	dx
E000:0D3A  8A C6			mov	al,dh
E000:0D3C  F6 E2			mul	dl
E000:0D3E  8B C8			mov	cx,ax
E000:0D40  26: 8B 45 78			mov	ax,ES:[DI+60*2]			;word 60 = physical sectors low dword
E000:0D44  26: 8B 55 7A			mov	dx,ES:[DI+61*2]			;word 61 = physical sectors high dword
E000:0D48  F7 F1			div	cx                              ;Divide overflow here!!!
E000:0D4A  5A				pop	dx
				@@:
E000:0D4B  89 44 02			mov	[si+2],ax
E000:0D4E  80 FE 10			cmp	dh,16
E000:0D51  0F 87 01B6			ja	Set_HDD_none
E000:0D55  88 74 04			mov	BYTE PTR [si+4],dh		;store heads
E000:0D58  88 54 10			mov	BYTE PTR [si+10],dl		;store sectors
All hard disks with capacity above 8.4GB shall report 16383 cylinders, 16 heads and 63 sectors per track. The correct size is read LBA_sectors. Maximum disk size without divide overflow is 65535 * 16 * 63 * 512 = 33822351360 bytes = 34 GB.
The simplest solution is to replace HEADS value with 255, then the maximum disk size is 539 GB (without other limits). The easiest way how to modify the BIOS is to overwrite part of the code that is usually not necessary - the check for invalid bytes in words 60 and 61. The replacement code then will be:
E000:0D29  26: 81 7D 7A 03F0		cmp	word ptr ES:[DI+61*2],03f0h
E000:0D2F  72 02			jb      small
E000:0D31  B2 FF			mov     dl,255
E000:0D32  90		small:		nop
E000:0D33  90				nop
E000:0D34  90				nop
E000:0D35  90				nop
E000:0D36  90				nop
E000:0D37  90				nop
E000:0D38  90				nop

Later I decided not to remove tests for all ones and all zeroes and did other modification:

E000:0D31  E8 DFA6			call	Patch_32GB
E000:0D34  90				nop
E000:0D35  90				nop
E000:0D36  90				nop

E000:ECDA  		Patch_32GB:
E000:ECDA  26: 81 7D 7A 03F0		cmp	word ptr ES:[DI+61*2],03f0h
E000:ECE0  72 02			jb      small
E000:ECE2  B2 FF			mov     dl,255
E000:ECE4  		small:
E000:ECE4  26: 66| 83 7D 78 FF		cmp	dword ptr ES:[DI+60*2],0ffffffffh
E000:ECE9  C3				retn

Now all disks with capacity 37, 40 and 60 GB are correctly autodetected, and it is possible to boot from them.

But new problem arises with disks with capacity over 65535 MB - there is divide overflow in displaying the value on the POST screen, and with displaying the size in "Standard CMOS Setup".

Second modification - breaking the 65 GB barrier

The right solution is more complicated. At first, for HX BIOS, the code is located in different part of ROM, called XGROUP CODE or Extended system BIOS. It can be extracted from the compressed image by Award CBROM utility by executing:

cbrom 5t2.22 /other 4100:0 extract
released from the compressed BIOS:
cbrom 5t2.22 /other 4100:0 release
added to the compressed BIOS:
cbrom 5t2.22 /other 4100:0 awardext.rom

It must be noted that some other BIOSes contain the same code in the main (F000 segment) BIOS.

At first, new variable on the stack HDDSIZE_Unit is created, its value is 2 if HDDSIZE variable contains size in megabytes, and 3 for gigabytes.

HDDSIZE_Unit		equ	USERINTSTACK+96		;byte (0220h)
HDDSIZE_MB		equ	2
HDDSIZE_GB		equ	3
New procedure for calculating HDD size is created:
SPACE IS HERE:
F000:D95E  06A2[00]			db	1698 dup (0)
;----------------------------------------------
;calculate HDD size
;Input : AL = heads of HDD
;      	 AH = sectors of HDD
;      	 CX = cylinders of HDD
;Output: HDDSIZE[bp] = size of HDD
;	 HDDSIZE_Unit[bp] = unit of HDD's size
;----------------------------------------------
					public	Cal_HDD_Size
F000:D95E		Cal_HDD_Size:
F000:D95E  66| 50			push	eax
F000:D960  66| 51			push	ecx
F000:D962  66| 52			push	edx

F000:D964  F6 E4			mul	ah			;heads * sectors
F000:D966  F7 E1			mul	cx			; * cylinders

F000:D968  66| C1 C8 10			ror	eax,16			;shift high word to low
F000:D96C  8B C2			mov	ax,dx			;set high word = DX
F000:D96E  66| C1 C8 10			ror	eax,16
F000:D972  66| 33 D2			xor	edx,edx			;clear divided number
F000:D975  66| B9 000007A1		mov	ecx,1000000/512		;set diving number
F000:D97B  66| F7 F1			div	ecx
F000:D97E  B2 02			mov	dl,HDDSIZE_MB		;assume MB (below 0ffffh)
F000:D980  66| 3D 0000FFFF		cmp	eax,0ffffh		;size over 0ffffh
F000:D986  72 11			jb	short Fill_HDDSIZE_Stack;No,skip
F000:D988  66| C1 C8 10			ror	eax,16
F000:D98C  8B D0			mov	dx,ax
F000:D98E  66| C1 C8 10			ror	eax,16
F000:D992  B9 03E8			mov	cx,1000			;transfer to GB
F000:D995  F7 F1			div	cx
F000:D997  B2 03			mov	dl,HDDSIZE_GB		;set GB string
F000:D999		Fill_HDDSIZE_Stack:
F000:D999  88 96 0220			mov	HDDSIZE_Unit[bp],dl	;set unit of HDD size
F000:D99D  89 86 009C			mov	HDDSIZE[bp],ax		;set HDD size
	
F000:D9A1  66| 5A			pop	edx
F000:D9A3  66| 59			pop	ecx
F000:D9A5  66| 58			pop	eax
F000:D9A7  C3				ret
Old code causing divide overflow during displaying the POST (configuration) screen before boot:
;-------------------
;calculate HDD size
;-------------------
-- MACRO				F000_call	Get_HDD_CMOS_Info
X:05D4  0E				push	cs
X:05D5  68 05E0				push	ret_addr_1
X:05D8  68 7B4A				push	offset Get_HDD_CMOS_Info
X:05DB  EA E000ECC0			jmp	far ptr F000_call_proc
X:05E0			ret_addr_1
X:05E0  1E				push	ds
X:05E1  53				push	bx
X:05E2  75 1D				jnz	short Get_User_Type
X:05E4  0F B6 02			movzx	ax,byte ptr [bp+si]		;Hdd type no.
X:05E7  FE C8				dec	al
X:05E9  C1 E0 04			shl	ax,4
X:05EC  BB 56F2				mov	bx,offset HDISK_PARMS
X:05EF  03 D8				add	bx,ax
X:05F1  68 F000				push	0f000h
X:05F4  1F				pop	ds
X:05F5  0F B6 47 02			movzx	ax,byte ptr [bx+2]		;heads
X:05F9  0F B6 4F 0E			movzx	cx,byte ptr [bx+14]		;sectors
X:05FD  8B 1F				mov	bx,[bx]				;cylinders
X:05FF  EB 0A				jmp	short Disp_HDD_Size
X:0601			Get_User_Type:
X:0601  0F B6 43 02			movzx	ax,byte ptr [bp+di+2]		;heads
X:0605  0F B6 4B 07			movzx	cx,byte ptr [bp+di+7]		;sectors
X:0609  8B 1B				mov	bx,[bp+di+0]			;cylinders
X:060B			Disp_HDD_Size:
X:060B  C1 E1 02			shl	cx,2
X:060E  F7 E1				mul	cx
X:0610  F7 E3				mul	bx
X:0612  5B				pop	bx
X:0613  1F				pop	ds
X:0614  B9 1E84				mov	cx,15625/2
X:0617  F7 F1				div	cx				;Divide overflow here!!!
X:0619  89 86 009C			mov	HDDSIZE[bp],ax
-- MACRO				F000_call Disp_Word_Int5
X:061D  0E				push	cs
X:061F  68 0629				push	offset ret_addr_2
X:0621  68 43AA				push	offset Disp_Word_Int5
X:0624  EA E000ECC0			jmp	far ptr F000_call_proc
X:0629			ret_addr_2:
X:0629  BE 02EC				mov	si,offset MB_Str
X:062C			@@:
-- MACRO				post_func_call	Disp_Str_In_BIOS
X:062C  0E				push	cs
X:062D  68 0638				push	offset ret_addr_3
X:0630  68 6681				push	offset Disp_Str_In_BIOS
X:0633  EA F0004F32			jmp	far ptr Post_call_proc		; (F000:4F32)
X:0638			ret_addr_3:
X:0638  C3				ret
New modified code, using new procedure and variables:
;-------------------
;calculate HDD size
;-------------------
-- MACRO				F000_call	Get_HDD_CMOS_Info
X:05D4  0E				push	cs
X:05D5  68 05E0				push	ret_addr_1
X:05D8  68 7B4A				push	offset Get_HDD_CMOS_Info
X:05DB  EA E000ECC0			jmp	far ptr F000_call_proc
X:05E0			ret_addr_1
X:05E0  1E				push	ds
X:05E1  53				push	bx
X:05E2  75 1B				jnz	short Get_User_Type
X:05E4  0F B6 02			movzx	ax,byte ptr [bp+si]		;Hdd type no.
X:05E7  FE C8				dec	al
X:05E9  C1 E0 04			shl	ax,4
X:05EC  BB 52D2				mov	bx,offset HDISK_PARMS
X:05EF  03 D8				add	bx,ax
X:05F1  68 F000				push	0f000h
X:05F4  1F				pop	ds
X:05F5  8A 47 02			mov	al,byte ptr [bx+2]		;heads
X:05F8  8A 67 0E			mov	ah,byte ptr [bx+14]		;sectors
X:05FB  8B 0F				mov	cx,[bx]				;cylinders
X:05FD  EB 08				jmp	short Disp_HDD_Size		; (090E)
X:05FF			Get_User_Type:
X:05FF  8A 43 02			mov	al,[bp+di+2]			;heads
X:0602  8A 63 07			mov	ah,[bp+di+7]			;sectors
X:0605  8B 0B				mov	cx,[bp+di+0]			;cylinders
X:0607			Disp_HDD_Size:
X:0607  E8 119B				call	X_Cal_HDD_Size			; (17A5)
X:060A  5B				pop	bx
X:060B  1F				pop	ds
X:060C  8B 86 009C			mov	ax,HDDSIZE[bp]
-- MACRO				F000_call Disp_Word_Int5
X:0610  0E				push	cs
X:0611  68 061F				push	offset ret_addr_2
X:0614  68 43AA				push	offset Disp_Word_Int5
X:0617  EA E000ECC0			jmp	far ptr F000_call_proc

X:061C  47 42 00	GB_str		db	'GB', 0
X:061F			ret_addr_2:
X:061F  BE 02EC				mov	si,offset MB_Str
X:0622  80 BE 0220 02			cmp	byte ptr HDDSIZE_Unit[bp],HDDSIZE_MB
X:0627  76 03				jbe	short @F
X:0629  BE 061C				mov	si,offset GB_Str

X:062C			@@:
-- MACRO				post_func_call	Disp_Str_In_BIOS
X:062C  0E				push	cs
X:062D  68 0638				push	offset ret_addr_3
X:0630  68 6681				push	offset Disp_Str_In_BIOS
X:0633  EA F0004F32			jmp	far ptr Post_call_proc		; (F000:4F32)
X:0638			ret_addr_3:
X:0638  C3				ret

New X_Call_HDD_Size procedure located in XGROUP CODE:

X:17A5			X_Call_HDD_Size	proc	near
-- MACRO				F000_call Call_HDD_Size
X:17A5  0E				push	cs
X:17A6  68 17B1				push	offset ret_addr_x
X:17A9  68 D95E				push	offset Call_HDD_Size
X:17AC  EA E000ECC0			jmp	far F000_call_proc
X:17B1			ret_addr_x
X:17B1  C3				ret
			X_Call_HDD_Size	endp

The second place with divide overflow is displaying HDD size during manual setup (User setting). The divide overflow occurs in Transfer_Hdd_Parms procedure, it is too long, so I will comment only important place:

F000:7CF1  E9 00C4			jmp	Not_Hdd			; (7DB8)
........
F000:7CF9  E9 00BC			jmp	Not_Hdd			; (7DB8)
........
........
;-------------------
;calculate HDD size
;-------------------

F000:7D98  51				push	cx
F000:7D99  32 E4			xor	ah,ah
F000:7D9B  8A 86 0096			mov	al,HEAD[bp]
F000:7D9F  32 ED			xor	ch,ch
F000:7DA1  8A 8E 009B			mov	cl,SECTOR[bp]
F000:7DA5  C1 E1 02			shl	cx,2
F000:7DA8  F7 E1			mul	cx
F000:7DAA  F7 A6 0094			mul	word ptr CYLINDER[bp]
F000:7DAE  B9 1E84			mov	cx,15625/2	
F000:7DB1  F7 F1			div	cx				;Divide overflow here!!!
F000:7DB3  89 86 009C			mov	HDDSIZE[bp],ax
F000:7DB7  59				pop	cx
F000:7DB8		Not_Hdd:
F000:7DB8  5E				pop	si
F000:7DB9  C3				ret

and new version, using new procedure and variables:

F000:7CF1  E9 00B3				jmp	Not_Hdd			; (7DA7)
........
F000:7CF9  E9 00AB				jmp	Not_Hdd			; (7DA7)
........
........
;-------------------
;calculate HDD size
;-------------------
F000:7D98  8A 86 0096			mov	al,HEAD[bp]
F000:7D9C  8A A6 009B			mov	ah,SECTOR[bp]
F000:7DA0  8B 8E 0094			mov	cx,CYLINDER[bp]
F000:7DA4  E8 5BB7			call	Cal_HDD_Size			; (D95E)
F000:7DA7		Not_Hdd:
F000:7DA7  5E				pop	si
F000:7DA8  C3				ret

and remaining 17 bytes are not important.

In the old standard setup, the size of HDD is in megabytes, but there is no indication of it. With new routines, the size may be either in MB or GB so it is necessary to display it. The following patch achieves it:

F000:7AFE  E8 0032			call	Check_If_HDD_Type_Item		; (7B33)
F000:7B01  75 23			jnz	short Show_User_Exit
F000:7B03  E8 BCFF			call	Read_Item_Value			; (3805)
to new code
F000:7AFE  E8 0032			call	Check_If_HDD_Type_Item		; (7B33)
F000:7B01  75 23			jnz	short Show_User_Exit
F000:7B03  E8 5EA2			call	Patch_GB			; (D9A8)
and add special "patch" procedure is created at the end of the code:
F000:D9A8		Patch_GB:
F000:D9A8  B0 4D			mov	al,'M'
F000:D9AA  80 BE 0220 02		cmp	byte ptr HDDSIZE_Unit[bp],HDDSIZE_MB
F000:D9AF  76 02			jbe	short @F
F000:D9B1  B0 47			mov	al,'G'
F000:D9B3		@@:
F000:D9B3  FF B6 0080			push	word ptr ATTRIBUTE[bp]
F000:D9B7  FF B6 0081			push	word ptr CURSOR_X[bp]
F000:D9BB  80 86 0081 08		add	byte ptr CURSOR_X[bp],8
F000:D9C0  E8 63A3			call	VNORMAL				; (3D66)
F000:D9C3  E8 63FD			call	Display_Char			; (3DC3)
F000:D9C6  8F 86 0081			pop	word ptr CURSOR_X[bp]
F000:D9CA  8F 86 0080			pop	word ptr ATTRIBUTE[bp]
F000:D9CE  E8 5E34			call	Read_Item_Value			; (3805)
F000:D9D1  C3				retn

It will be not so easy to find addresses of VNORMAL and Display_Char procedures. In case they are located in main F000 segment BIOS, the names are VNORMAL and Display_Char respectively. VNORMAL procedure looks like this:

;[]========================================================================[]
;Function :	Set the display attrib to NORMAL
;
;Input	:	None
;
;Output :	byte ptr ATTRIBUTE[bp] updated
;
;Registers:	All preserved
;[]========================================================================[]
F000:3D66		Vnormal		proc	near
F000:3D66  50				push	ax
F000:3D67  53				push	bx
F000:3D68  8B 9E 014B			mov	bx,Color_Offset[bp]
F000:3D6C  2E: 8A 07			mov	al,cs:[bx].CNORMAL
F000:3D6F  88 86 0080			mov	ATTRIBUTE[bp],al
F000:3D73  5B				pop	bx
F000:3D74  58				pop	ax
F000:3D75  C3				ret
			Vnormal		endp

Display_Char procedure in the main BIOS. It may slightly differ.

;[]========================================================================[]
;Funtion :	Display 1 character on screen
;
;Input	:	al = ASCII code
;
;Output	:	CURSOR_X[bp] & CURSOR_Y[bp] changed
;
;Registers:	FLAG    - Destroyed
;		Others	- Preserved
;[]========================================================================[]

F000:3DC3		Display_Char	proc	near
F000:3DC3  50				push	ax
F000:3DC4  53				push	bx
F000:3DC5  51				push	cx
F000:3DC6  52				push	dx
F000:3DC7  9C				pushf
F000:3DC8  80 BE 014D AA		cmp	byte ptr CALLTYPE[bp],POST_CALL
F000:3DCD  75 2E			jne	short Is_Text_POST
F000:3DCF  FA				cli
F000:3DD0  F6 86 01E1 10		test	byte ptr Post_Temp_Byte[bp],Text_POST
F000:3DD5  75 26			jnz	short Is_Text_POST
F000:3DD7  8A D8			mov	bl,al				;Store display ASCII
F000:3DD9  B0 A0			mov	al,80*2				;1 char = 2 bytes
F000:3DDB  F6 A6 0082			mul	byte ptr CURSOR_Y[bp]
F000:3DDF  8A 8E 0081			mov	cl,CURSOR_X[bp]
F000:3DE3  32 ED			xor	ch,ch
F000:3DE5  03 C1			add	ax,cx
F000:3DE7  03 C1			add	ax,cx
F000:3DE9  8B F8			mov	di,ax
F000:3DEB  81 C7 7000			add	di,Temp_VGA_Off
F000:3DEF  B8 0000			mov	ax,Temp_VGA_Seg
F000:3DF2  8E C0			mov	es,ax
F000:3DF4  8A A6 0080			mov	ah,ATTRIBUTE[bp]		;Get display attribute
F000:3DF8  8A C3			mov	al,bl
F000:3DFA  AB				stosw
F000:3DFB  EB 1D			jmp	short Exit_Display_Char		; (3E1A)
F000:3DFD		Is_Text_POST:
F000:3DFD  50				push	ax
					;Set cursor position to display item name
F000:3DFE  8A 96 0081			mov	dl,CURSOR_X[bp]		;get column position
F000:3E02  8A B6 0082			mov	dh,CURSOR_Y[bp]		;get row position
F000:3E06  32 FF			xor	bh,bh			;first page
F000:3E08  B4 02			mov	ah,2			;set cursor position
F000:3E0A  E8 0017			Call	Check_Call_Source	; (3E24)
F000:3E0D  58				pop	ax
F000:3E0E  B4 09			mov	ah,09h			;write TTY
F000:3E10  8A 9E 0080			mov	bl,byte ptr ATTRIBUTE[bp]
F000:3E14  B9 0001			mov	cx,1
F000:3E17  E8 000A			Call	Check_Call_Source	; (3E24)
F000:3E1A		Exit_Display_Char:
F000:3E1A  FE 86 0081			inc	byte ptr CURSOR_X[bp]
F000:3E1E  9D				popf
F000:3E1F  5A				pop	dx
F000:3E20  59				pop	cx
F000:3E21  5B				pop	bx
F000:3E22  58				pop	ax
F000:3E23  C3				ret
			Display_Char	endp

F000_call_proc is used for calling procedures in F000 segment from other segment

E000:ECC0		F000_call_proc:
E000:ECC0  68 EC31			push	offset F000_func_end
E000:ECC3  50				push	ax
E000:ECC4  9C				pushf
E000:ECC5  FA				cli
E000:ECC6  87 EC			xchg	bp,sp
E000:ECC8  8B 46 04			mov	ax,[bp+4]
E000:ECCB  87 46 06			xchg	[bp+6],ax
E000:ECCE  89 46 04			mov	[bp+4],ax
E000:ECD1  87 EC			xchg	bp,sp
E000:ECD3  9D				popf
E000:ECD4  58				pop	ax
E000:ECD5  EA F000EC30			jmp	far F000_VECT		; (F000:EC30)
....
....
F000:EC30   		F000_VECT:
F000:EC30  C3				retn
F000:EC31		F000_func_end:
F000:EC31  CB				retf

Post_call_proc is used for calling procedures in E000 segment (POST) from other segment

F000:4F32		Post_call_proc:
F000:4F32  68 5C71			push	offset POST_func_end
F000:4F35  50				push	ax
F000:4F36  9C				pushf
F000:4F37  FA				cli
F000:4F38  87 EC			xchg	bp,sp
F000:4F3A  8B 46 04			mov	ax,[bp+4]
F000:4F3D  87 46 06			xchg	[bp+6],ax
F000:4F40  89 46 04			mov	[bp+4],ax
F000:4F43  87 EC			xchg	bp,sp
F000:4F45  9D				popf
F000:4F46  58				pop	ax
F000:4F47  EA E0005C70			jmp	far ptr POST_VECT		; (E000:8030)
....
....
E000:5C70   		POST_VECT:
E000:5C70  C3				retn
E000:5C71		POST_func_end:
E000:5C71  CB				retf

Adding AMD K6-2/450 display

Processor speed table - original

E000:34BF  90 8E 80			db	400-256, 398-256, CPU66		;66x6
E000:34C2  6E 6C 80			db	366-256, 364-256, CPU66		;66x5.5
E000:34C5  5E 5C B0			db	350-256, 348-256, CPU100	;100x3.5
E000:34C8  4D 4B 80			db	333-256, 331-256, CPU66		;66x5
E000:34CB  2C 2A 80			db	300-256, 298-256, CPU66		;66x4.5
E000:34CE  0A 08 80			db	266-256, 264-256, CPU66		;66x4
E000:34D1  00				db	0

modified - added 450 MHz (75x6) and removed 350 MHz (100x3.5)

E000:34BF  C2 C0 90			db	450-256, 448-256, CPU75		;75x6
E000:34C2  90 8E 80			db	400-256, 398-256, CPU66		;66x6
E000:34C5  6E 6C 80			db	366-256, 364-256, CPU66		;66x5.5
E000:34C8  4D 4B 80			db	333-256, 331-256, CPU66		;66x5

All modifications are done by the same way as are done in newer Award BIOSes (after 04/1999), and if possible, the routines are the same.

Ultra DMA mode limitation

Original code:
E000:0EDF  26: F6 45 6A 04		test	byte ptr ES:[DI+53*2],4		;test word 88 is valid?
E000:0EE4  74 18			jz	short Ultra_DMA_Exit
E000:0EE6  26: 8A 85 00B0		mov	al,ES:[DI+88*2]			;Get Ultra DMA mode value
E000:0EEB  A8 01			test	al,1				;test supported?
E000:0EED  74 0F			jz	short Ultra_DMA_Exit		;Not support then skip
E000:0EEF  32 C9			xor	cl,cl
E000:0EF1		Ultra_DMA_Loop:
E000:0EF1  D0 E8			shr	al,1
E000:0EF3  73 02			jnc	short @F
E000:0EF5  8A E1			mov	ah,cl
E000:0EF7		@@:
E000:0EF7  FE C1			inc	cl
E000:0EF9  80 F9 08			cmp	cl,8
E000:0EFC  72 F3			jb	short Ultra_DMA_Loop
E000:0EFE		Ultra_DMA_Exit:
E000:0EFE  88 22			mov	byte ptr [bp+si],ah
E000:0F00  26: 8B 55 62			mov	dx,ES:[DI+49*2]
E000:0F04  66| C1 E2 10			shl	edx,16
E000:0F08  F8				clc
E000:0F09  EB 08			jmp	short exit_ok

Modified code:

E000:0EF1  E8 DDF7			call	Ultra_DMA_Loop
E000:0EF4  B0 02			mov	al,2			;default mode 2
E000:0EF6  38 C4			cmp	ah,al			;HDD ultra DMA mode over chipset supported
E000:0EF8  76 02			jbe	short @F		;No,skip
E000:0EFA  88 C4			mov	ah,al			;Yes,limitation by chipset
E000:0EFC		@@:
E000:0EFC  90				nop
E000:0EFD  90				nop

New code at the end of the code in E000 segment:

E000:ECEB		Ultra_DMA_Loop:
E000:ECEB  D0 E8			shr	al,1
E000:ECED  73 02			jnc	short @F
E000:ECEF  8A E1			mov	ah,cl
E000:ECF1		@@:
E000:ECF1  FE C1			inc	cl
E000:ECF3  80 F9 08			cmp	cl,8
E000:ECF6  72 F3			jb	short Ultra_DMA_Loop
E000:ECF8		Ultra_DMA_Exit:
E000:ECF8				ret

Petr Soucek, 07-Jul-2002