Index: Kconfig
===================================================================
--- Kconfig	(revision 466)
+++ Kconfig	(working copy)
@@ -104,6 +104,15 @@
 	  You will be able to specify the location and file name of the
 	  payload image later.
 
+config PAYLOAD_ELF_ADLO
+	bool "An ADLO payload file"
+	help
+	  Select this option to use an ALDO payload image (an ELF file)
+	  which LinuxBIOS should run as soon as the basic hardware
+	  initialization is completed.
+ 
+          This option is used to boot Windows 2000/XP.
+
 config PAYLOAD_NONE
 	bool "No payload"
 	help
Index: util/ADLO/README.1st
===================================================================
--- util/ADLO/README.1st	(revision 0)
+++ util/ADLO/README.1st	(revision 0)
@@ -0,0 +1,37 @@
+Date: Sat, 23 Nov 2002 22:20:57 -0500
+From: Adam Sulmicki <adam@cfar.umd.edu>
+To: LinuxBIOS <linuxbios@clustermatic.org>
+Cc: William Arbaugh <waa@cs.umd.edu>, Adam Agnew <agnew@cs.umd.edu>
+Subject: [ANNOUNCE] LinuxBIOS boots Windows 2000, GRUB, and OpenBSD.
+
+We're happy to announce that we've been able to successfully boot  
+Windows 2000 without a legacy BIOS. We accomplished this by developing  
+software that combined elements from two very successful projects:  
+LinuxBIOS and BOCHS. The Etherboot project also helped in various ways.
+
+As a result now, we have a completely open source replacement for the  
+BIOS that can use (without modification) either LILO or GRUB as  
+bootloaders, and Linux, OpenBSD, and Windows 2000 as operating systems  
+(NOTE: We're still working on supporting FreeBSD and Windows XP. We  
+expect that improving ATA support will permit Win98 and WinXP to boot,  
+and finishing PIRQ support will permit FreeBSD to boot.) Motherboard  
+support is also limited at this time, but we hope to expand that along  
+with LinuxBIOS.
+
+More details can be found at:
+
+http://www.missl.cs.umd.edu/Projects/sebos/main.shtml
+
+and
+
+http://www.missl.cs.umd.edu/Projects/sebos/phase2.shtml
+
+Ironically, twenty years ago this month Compaq introduced their Compaq  
+portable computer with the first BIOS outside of IBM, see
+http://www.old-computers.com/museum/computer.asp?st=1&c=547 .
+
+Adam Sulmicki
+Adam Agnew
+William Arbaugh
+
+This work has been funded by a grant from DARPA under the CHATS program.
Index: util/ADLO/STATUS
===================================================================
--- util/ADLO/STATUS	(revision 0)
+++ util/ADLO/STATUS	(revision 0)
@@ -0,0 +1,154 @@
+$Id: STATUS,v 1.1 2002/11/25 02:07:53 rminnich Exp $
+==========================================
+==========================================
+
+		STATUS
+
+==========================================
+==========================================
+
+LILO
+	fully supported
+
+ ==========================================
+
+GRUB
+	fully supported
+
+	notes
+		int15/eAX=00C0 - not supported
+		int15/Eax=5300 - not supported
+		int 13 ; fn 41 -> dev 81
+		int 13 ; fn 08 -> dev 81
+
+ ==========================================
+
+LINUX
+	fully supported
+
+	notes
+		won't reboot
+		fix fb? 
+
+ ==========================================
+
+openbsd
+	fully supported
+
+	notes
+		won't reboot
+
+ ==========================================
+Win2k	
+
+	fully supported
+
+	notes
+		network?
+		better gfx?
+
+ ==========================================
+
+freebsd
+	somewhat supported
+
+	notes
+		won't reboot
+
+		right now it hangs somewhere around fsck/net startup
+
+		probably due to bad PIRQ table, 
+		it probably switches to polled IO and is dog slow.
+
+ ==========================================
+
+Win98
+	somewhat supported
+
+	lots of ata issues, but if get past
+	ata hurdles, desktop will show up
+
+	press F8 for safe mode.
+		the desktop comes up 
+		(can see "safe mode")
+
+	int 15 D8 (?) not supporte
+	int 15 ?? not supported
+	int 15 fn 24 (set A20 gate) not suported)
+
+ ==========================================
+
+winXP
+
+	no go
+
+safe/normal boot
+
+	progress bar finishes, switches to gfx mode
+	and immediatelly STOP 7B, no sub error.
+
+more timing problems!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+	more likely "unexpected previous state" problems.
+
+cold reboot, normal boot and safe mode
+	missing hal
+		\system32\hal.dll
+
+	missing system
+-------------------
+no lba - ie none
+	function 02, error 11 - drv not ready.
+
+	
+ ==========================================
+ ==========================================
+
+syslinux
+netbsd
+hurd
+dos
+other applications
+
+	not supported/tested at present time
+
+ ==========================================
+TODO:
+
+timing in ide read code (int13) [WIP]
+	grub boot
+		sometimes it work, sometimes it doesn't
+
+	openbsd boot /WinXP boot.
+		(AH=0x02) misreads sometimes.
+
+fix int15 memory functions. (E820, etc)
+	so that mem size is not hard coded.
+	(see prep "fixup" section in loader.s)
+
+fix bios to properly handle reboot
+
+setup PIRQ table for P6STMT mbo.
+	have it extract from linuxbios somehow..
+		find it in ram and copy...
+
+hack gcc to support 16 bit real mode.
+
+
+port 80 card broken? 
+
+hack dev86 to support rdmsr and wrmsr 		[done]
+
+
+ ==========================================
+problems:
+	-binary only video bios required
+	-binary only bios required
+	-warm reboot from pc bios is required	
+		chipset timings or something
+		P6STMT board only.
+
+
+	int 12 not stable, multiple reboot required
+		debug msg for slowing down required
+
+ ==========================================
Index: util/ADLO/HACKING
===================================================================
--- util/ADLO/HACKING	(revision 0)
+++ util/ADLO/HACKING	(revision 0)
@@ -0,0 +1,16 @@
+DEVELOPER/HACKING/DESIGN
+
+- bochs bios.
+
+	we expect most of the bochs patches will be rolled back into
+	the bochs mainline.
+
+	thus when designing an patch for rombios.c both needs of 
+	linuxbios as well as bochs needed to be taken into 
+	consideration.
+
+- there are motherboard specific code in loader.s. until this issue
+  	is fixed, you need to adjust various motherboard specific
+	settings in loader.s In particular control of the 
+	shadow ram.
+
Index: util/ADLO/elf/elf-header-113kb.payload
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: util/ADLO/elf/elf-header-113kb.payload
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Index: util/ADLO/elf/elf-header-065kb.payload
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: util/ADLO/elf/elf-header-065kb.payload
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Index: util/ADLO/elf/elf-header-129kb.payload
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: util/ADLO/elf/elf-header-129kb.payload
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Index: util/ADLO/loader.s
===================================================================
--- util/ADLO/loader.s	(revision 0)
+++ util/ADLO/loader.s	(revision 0)
@@ -0,0 +1,461 @@
+;*****************************************************
+; $Id: loader.s,v 1.1 2002/11/25 02:07:53 rminnich Exp $
+;*****************************************************
+USE32
+; code it is loaded into memory at 0x7C00
+;*****************************************************
+nop
+nop
+;*****************************************************
+; A) setup GDT, so that we do not depend on program 
+; that loaded us for GDT. 
+; Ex: LinuxBIOS and EtherBOOT use different GDT's.
+
+;-----------------------------------------------------
+; 0)
+
+cli
+
+;-----------------------------------------------------
+; I)
+
+lgdt [0x7C00+protected_gdt]
+
+;-----------------------------------------------------
+; II) setup CS
+
+jmp 0x08:0x7C00+newpgdt
+
+newpgdt: nop
+
+;-----------------------------------------------------
+; III) setup all other segments
+
+mov ax,  #0x10
+mov ss,  ax
+mov ds,  ax
+mov es,  ax
+mov fs,  ax
+mov gs,  ax
+
+;-----------------------------------------------------
+; IV) 
+
+; not now
+;sti
+
+;*****************************************************
+nop
+nop
+;*****************************************************
+; B) shadow - ON (enable/read/write)
+
+mov eax, #0x80000070
+mov dx,  #0x0cf8
+out dx,  eax
+
+mov eax, #0xFFFFFFFF
+mov dx,  #0x0cfc
+out dx,  eax
+
+;*****************************************************
+nop
+nop
+;*****************************************************
+; C) copy -- boch bios
+
+; counter - 64kb.     
+mov ecx, #0x10000
+
+; source - 0x8000  ( 0x7C00+0x400 = 0x8000 ) 
+mov ax,  #0x10        ; src-segment - 2nd entry in GDT
+mov ds,  ax
+mov eax, #0x8000      ; src-offset  - 0x8000
+mov esi, eax
+
+; destination - 0xE0000
+mov ax,  #0x10        ; dst-segment - 2nd entry in GDT
+mov es,  ax     
+mov eax, #0xF0000     ; dst-offset  - 0xF0000
+mov edi, eax
+
+; clear direction flag
+cld
+
+; the copy
+rep
+  movsb
+
+;*****************************************************
+nop
+nop
+;*****************************************************
+; X) copy -- LinuxBIOS table into safe place.
+
+	;; TODO.
+	;; Q1 :	 what is the size of table.
+	;; Q2 :	 where to copy?
+		
+;*****************************************************
+nop
+nop	
+;*****************************************************
+; E) shadow - OFF (write)
+
+mov eax, #0x80000070
+mov dx,  #0x0cf8
+out dx,  eax
+
+;mov eax, #0xFFFFFFFF
+mov eax, #0x0000FFFF
+mov dx,  #0x0cfc
+out dx,  eax
+
+;*****************************************************
+nop
+nop
+;*****************************************************
+; F) do a little prep work.
+
+;-----------------------------------------------------
+; I) disable cache
+
+; if you disable cache, GRUB's GFX mode will be VERY slow.
+; so DO NOT DISABLE
+
+;mov eax, cr0
+;or  eax, #0x60000000
+;wbinvd
+;mov cr0, eax
+;wbinvd
+
+;-----------------------------------------------------
+; II) disable MTRR
+; clear the "E" (0x800) and "FE" (0x400) flags in 
+; IA32_MTRRdefType register (0x2FF)
+
+;-----------------------
+
+;mov ECX,#0x2FF
+
+; select either of the two below 
+; depending on if your compiler suports 
+; {RD,WR}MSR or not
+;rdmsr
+; .byte 0x0F, 0x32
+
+;xor edx, edx
+; xor eax, eax
+;and eax, #0xFFFFF3FF
+
+; select either of the two below 
+; depending on if your compiler suports 
+; {RD,WR}MSR or not
+;wrmsr
+; .byte 0x0F, 0x30
+
+;-----------------------
+;; This is what PC BIOS is setting. -- P6STMT.
+; add VIDEO BIOS cacheable!!!!
+;-----------------------
+; Fixed Range C0--C8
+;mov ECX,#0x268
+;mov EDX,#0x05050505 
+;mov EAX,#0x05050505 
+;wrmsr
+;-----------------------
+; Fixed Range C8--CF
+;mov ECX,#0x269
+;mov EDX,#0x0 
+;mov EAX,#0x05050505 
+;wrmsr
+;-----------------------
+
+;-----------------------------------------------------
+; III) tell BOCHS' BIOS we want to boot from hdd.
+; 0x00 - floppy
+; 0x02 - hdd
+; In future there will be 'fd failover'option in bochs.
+
+;mov  al, #0x3d ;; cmos_reg
+;out  0x70, al
+;mov  al, #0x02 ;; val (hdd)
+;out  0x71, al
+
+;-----------------------------------------------------
+; IV) tell BOCHS' BIOS length of our mem block @ 1mb.
+;     This is for Int 15 / EAX=E820
+;     119mb = 0x77 00 00 00 
+;     (this is for 128mb of ram)
+;     (FIXME: this value is currently hard coded)
+;     (it should be being passed from LinuxBIOS )
+
+; for WinFast  6300
+; 07 70 = 0770
+; 06 80 = 0770 - 00F0		<< ALT (for unpatched bochs)
+
+; for P6STMT - 10kb less ram
+; 077F - 10     = 07 6F 
+; 07 6F - 00 F0 = 06 7F
+
+mov  al, #0x35 ;; cmos_reg
+out  0x70, al
+mov  al, #0x06 ;; val 
+out  0x71, al
+
+mov  al, #0x34 ;; cmos_reg
+out  0x70, al
+mov  al, #0x7F ;; val 
+out  0x71, al
+
+mov  al, #0x31 ;; cmos_reg
+out  0x70, al
+mov  al, #0x00 ;; val 
+out  0x71, al
+
+mov  al, #0x30 ;; cmos_reg
+out  0x70, al
+mov  al, #0x00 ;; val 
+out  0x71, al
+
+;-----------------------------------------------------
+; V) tell BOCHS' BIOS we want to have LBA translation.
+; 0x00 - NONE
+; 0x01 - LBA    <<<<
+; 0x02 - LARGE
+; 0x03 - R-CHS
+; In future there will be 'fd failover'option in bochs.
+
+mov  al, #0x39 ;; cmos_reg
+out  0x70, al
+mov  al, #0x01 ;; val (LBA)
+out  0x71, al
+
+;*****************************************************
+nop
+nop
+;*****************************************************
+; G) the switch -- protected to real mode
+
+; IASDM, Vol 3
+; (8-14) 8.8.2 Switching Back to Real-Address Mode
+
+;=====================================================
+; 1) disable interrupts
+
+cli
+
+;=====================================================
+nop
+;=====================================================
+; 2) paging
+
+;not enabled, so not applicable.
+
+;=====================================================
+; 3) setup CS segment limit (64kb)
+; I)
+
+lgdt [0x7C00+new_gdt]
+
+;-----------------------------------------------------
+; II)
+
+jmp 0x08:0x7C00+new64lim
+
+new64lim: nop
+
+;=====================================================
+nop
+;=====================================================
+; 4) setup all other segments
+
+mov ax,  #0x10
+mov ss,  ax
+mov ds,  ax
+mov es,  ax
+mov fs,  ax
+mov gs,  ax
+
+;=====================================================
+nop
+;=====================================================
+; 5) LIDT
+; I)
+
+; set up Real Mode IDT table (0...3FF)
+
+; for BOCH's BIOS the address 0xF000:0xFF53 
+; cantains value 0xCF which is IRET opcode.
+
+; counter 
+mov cx,  #0xFF ;1024 bytes(255 interrupts)(4*255=0x3FF)
+
+; destination - 0x00000 = ES:EDI
+mov ax,  #0x10        ; dst-segment - 2nd entry in GDT
+mov es,  ax
+mov eax, #0x00000     ; dst-offset  - 0x00000
+mov edi, eax
+
+; data to store -- 0xF000:FF53
+mov eax, #0xF000FF53
+
+; clear direction flag
+cld
+
+; the store 
+rep
+  stosd
+
+;-----------------------------------------------------
+; II)
+; load interrupt descriptor table
+
+lidt [0x7C00+new_idt]
+
+;=====================================================
+nop
+nop
+;=====================================================
+; 6) clear the PE flag in CR0 register.
+; I)
+
+; switch to 16 bit segments
+mov ax,  #0x20
+mov ss,  ax
+mov ds,  ax
+mov es,  ax
+mov fs,  ax
+mov gs,  ax
+
+;-----------------------------------------------------
+; II)
+
+; switch to 16 bit CS
+
+jmp 0x018:0x7C00+new16bit
+
+USE16
+
+new16bit: nop
+
+;-----------------------------------------------------
+; III)
+; the switch
+
+;xor eax, eax
+
+mov eax, cr0            
+and eax, #0xFFFFFFFE
+mov cr0, eax            ;switch to RM
+
+;=====================================================
+nop
+nop
+;=====================================================
+; 7) far jump -- (to real mode address)
+
+jmp 0x0:0x7C00+realcs
+
+realcs: nop
+
+;=====================================================
+; 8) set all segment registers to 0's
+
+mov ax,  #0x0
+mov ss,  ax
+mov ds,  ax
+mov es,  ax
+mov fs,  ax
+mov gs,  ax
+
+;=====================================================
+; 9) re-enable interrupts
+
+sti
+
+;*****************************************************
+nop
+nop
+;*****************************************************
+; G) jump to BIOS.
+
+jmp 0xFFFF:0x0000
+;jmp 0xF000:0xFFF0
+
+;*****************************************************
+;*****************************************************
+nop
+nop
+nop
+nop
+;*****************************************************
+;*****************************************************
+
+USE32
+
+new_idt:
+dw 0x03ff ;; limit 15:00
+dw 0x0000 ;; base  15:00
+dw 0x0000 ;; base  23:16
+
+new_gdt:
+dw 0x0028                     ;; limit 15:00
+dw 0x7C00+new_gdt_table       ;; base  15:00
+dw 0x0000                     ;; base  23:16
+
+protected_gdt:
+dw 0x0018                     ;; limit 15:00
+dw 0x7C00+pmode_gdt_table     ;; base  15:00
+dw 0x0000                     ;; base  23:16
+
+;-----------------------------------------------------
+
+new_gdt_table:
+;//  1 2 3 4 
+;//0
+dd 0x00000000
+dd 0x00000000
+
+;//8
+dd 0x0000ffff
+dd 0x00409E00
+
+;//10
+dd 0x0000ffff
+dd 0x00409200
+
+;//18
+dd 0x0000ffff
+dd 0x00009a00
+
+;//20
+dd 0x0000ffff
+dd 0x00009200
+
+;-------------------------
+
+pmode_gdt_table:
+;//  1 2 3 4 
+;//0
+dd 0x00000000
+dd 0x00000000
+
+;//8
+dd 0x0000ffff
+dd 0x00CF9E00
+
+;//10
+dd 0x0000ffff
+dd 0x00CF9200
+
+;*****************************************************
+;*****************************************************
+; the file size must be 1024 bytes.
+
+
+.org 0x400-1
+; dd 0xdeadbeef			
+db 0x0
+
+;*****************************************************
Index: util/ADLO/COPYING
===================================================================
--- util/ADLO/COPYING	(revision 0)
+++ util/ADLO/COPYING	(revision 0)
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Index: util/ADLO/CAST
===================================================================
--- util/ADLO/CAST	(revision 0)
+++ util/ADLO/CAST	(revision 0)
@@ -0,0 +1,9 @@
+Most of the analysis, design and implementation of the project was done by
+me, Adam Sulmicki. I have received invaluable help from Adam Agnew. Bill
+Arbaugh was the person who made it all possible.
+
+I also received help from Christophe Bothamy, Bochs' developer, on all
+Boch's bios related issues. Thanks!
+
+I also got tips from Eric W. Biederman, as well as other members of the
+LinuxBIOS mailing list.
Index: util/ADLO/NEWS
===================================================================
--- util/ADLO/NEWS	(revision 0)
+++ util/ADLO/NEWS	(revision 0)
@@ -0,0 +1,38 @@
+initial release
+	
+	boots LILO
+
+"linux_alpha" release
+
+	fixed keyboard in lilo
+
+	boots linux
+		(must use "linux mem=100mb ro init=/bin/sh ")
+
+"win2k" release
+
+	lilo works
+		no restrictions
+	grub works
+		no restrictions
+	linux works
+		no restrictions
+	openbsd works
+		no restrictions
+	win2k
+		no restrictions
+
+	freebsd 
+		hangs at /sbin/init
+	win98
+		sometimes boots up to desktop
+	winXP
+		finish text mode bar, 
+		switch to gfx mode 
+		stop 7b 
+
+"public" release
+	
+	just bit of cleanups, other than that same as
+	"win2k" release.
+
Index: util/ADLO/README
===================================================================
--- util/ADLO/README	(revision 0)
+++ util/ADLO/README	(revision 0)
@@ -0,0 +1,231 @@
+==========================================
+$Id: README,v 1.1 2002/11/25 02:07:53 rminnich Exp $
+==========================================
+
+ADLO
+
+	A project to combine LinuxBIOS and BOCHS BIOS to add support 
+	for booting legacy applications, such as Microsoft Windows.
+
+==========================================
+
+Boot Overvew:
+	
+	ADLO requires an boot loader with ELF support.
+	In our case it is either LinuxBIOS or EtherBOOT.
+
+	Sample execution paths:
+
+LinuxBIOS -> ADLO -> LILO -> LINUX
+
+LinuxBIOS -> EtherBOOT -> ADLO -> LILO -> LINUX
+
+	Then it can start any real mode application.
+	In our case it could be LILO or GRUB, but
+	choices are not limited to this.
+
+==========================================
+
+ADLO overview:
+
+	-Elf header
+	-mainteance routines
+	-bochs bios
+	-vga bios
+
+------------------------------------------
+
+Elf header
+
+	hand crafted binary. Currently there are 3 diff binaries
+	for three different sizes of payload (65kb,113kb,129kb).
+
+	they are catted at beginning of file
+
+------------------------------------------
+
+mainteance routines 
+
+	this is the loader.s file which gets compiled into loader.o binary
+
+	it is catted 2nd right after elf header and it is where
+	control is being transfered when the elf is executed.
+
+	it consist of :
+		-tuneups:
+			select device to boot
+			set memory for Int15/EAX=E820
+			enable LBA
+			copy LinuxBIOS table [TODO]
+		-shadow : 
+			enable/write/read
+		-copy:
+			bochs @ 0xF00000
+			video bios (binary only) @ 0xC00000
+		-switch
+			from Real to Protected Mode
+		-jump 
+			jmp to the bios at 0xFFFF:0x0000
+
+	we must have bios payload always at 1kb
+	offset from start of ELF payload so loader.o
+	must be always 1024 bytes.
+		
+------------------------------------------
+
+BOCHS BIOS
+
+	we have taken the boch's project and just extracted
+	its bios from it only. it took us (so far) only
+	minor modifications in order to make it work.
+
+	basically it is the following directory in the
+	bochs sources
+
+		bochs-cvs/bochs/bios
+
+	of all files there the rombios.c is the key file.
+	Most of the times our patches are against current 
+	version of the rombios.c file. We try to feed back
+	the patches to bochs maintainers so that's possible
+	to use the bochs bios as is.
+
+	the boch's related stuff are in bochs subdirectory.
+
+	building boch's bios is as simple as 
+
+		cd bochs/bochs-cvs/bochs/bios
+		make
+
+	the resulting file rombios.bin is our bios image.
+
+			-----
+	At present time, it is recommended to use 
+	dev86-0.16.3 + my patch in order to compile 
+	bochs' bios. The dev86 package can be found
+	at:
+
+	http://www.cix.co.uk/~mayday/
+
+	prepatched sources at:
+
+	http://www.missl.cs.umd.edu/~adam/dev86-0.16.3-MSR.tgz
+
+------------------------------------------
+
+Video BIOS
+
+	Video BIOS we use is binary only, so far.
+
+	At the present time we are using the original
+	Video BIOS as supplied from the system.
+
+	To get the image simply type:
+
+		make makevideobios
+
+	The right thing will happen. For refernece the
+	magic line in the Makefile is:
+
+	videobios:
+        	dd if=/proc/kcore \
+		of=./video.bios.bin \
+		bs=1 count=65536 skip=790528
+
+		---------------
+
+	We assume it is located at 0xC00000-0xE00000
+	and we use 'dd and /proc/kcore to extract it
+	(obviously it works as root only).
+
+	Obviously it needs to be done with original 
+	(as shipped by manufactuer bios) so it should 
+	be done as the first step of the ADLO setup.
+
+		---------------
+
+	The /proc/kcore is in ELF format (linux kernel 
+	2.4.x and later) so if we want to get accurate 
+	image of ram we must skip the ELF header. 
+	The magic command is :
+
+		 readelf -a /proc/kcore 
+
+Program Headers:
+  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz 
+  NOTE           0x0007d4 0x00000000 0x00000000 0x005b0 0x00000
+  LOAD           0x001000 0xc0000000 0x00000000 0xff60000 0xff60000
+                 ^^^^^^^^
+
+	There we look for the first LOAD section, and see what
+	is its offset (for us it is 0x1000)
+
+		---------------
+
+	64KiB = 65536
+
+	0x0C 00 00 + 0x10 00 = 790528
+
+		---------------
+
+	On technical note in our case video bios
+	is just 48kb, but for paranoia sake we copy
+	64kb.
+
+==========================================
+
+Environment overview
+
+ADLO is an ELF file and thus can be loaded either directly from 1)
+LinuxBIOS, or 2) via EtherBOOT, or 3) via EtherBOOT+ AA patch for FS
+support.
+
+1)
+Both LinuxBIOS and ADLO are on the same EEPROM chip. From end-user 
+viewpoint it is probably the most similar to the bios classic.
+(computer boots up and just loads whatever is in MBR).
+
+2)
+Set it up in /tftpboot directory on dhcp/tftp server and have it served
+from there. (this is how we did the development)
+
+3)
+Use AA patch and put it as /kernel file and it will be then loaded by
+etherboot from local hard disk.
+
+
+------------------------------------------
+
+Before ADLO starts.
+
+	ADLO requires an boot loader with ELF support.
+	In our case it is:
+
+	-LinuxBIOS
+	-LinuxBIOS and EtherBOOT
+	-LinuxBIOS and EtherBOOT + AA polled I/O patch (w/ FS support).
+
+	As little as LinuxBIOS only is required to get ADLO
+	up and running.
+
+	For development purposes it is recommended full set of
+	LinuxBIOS + EtherBOOT and boot via DHCP/TFTP.
+	
+------------------------------------------
+
+After ADLO finish.
+
+	ADLO will attempt to locate the first hard disk
+	and read MBR from it and execute.
+
+	Then it can start any real mode application.
+	In our case it could be LILO or GRUB 
+	but choices are not limited to this. 
+
+	In our case it would cause LILO to load 
+	(fully usable) and from there it was able to
+	execute simple programs. so far the list
+	of the program is limited to linux which 
+	runs somewhat. More to come in future.
+
+==========================================
Index: util/ADLO/Makefile
===================================================================
--- util/ADLO/Makefile	(revision 0)
+++ util/ADLO/Makefile	(revision 0)
@@ -0,0 +1,112 @@
+#-------------------------------------------------
+# $Id: Makefile,v 1.2 2003/05/22 12:52:38 aip Exp $
+#-------------------------------------------------
+
+ELF064=elf/elf-header-065kb.payload
+ELF113=elf/elf-header-113kb.payload
+ELF129=elf/elf-header-129kb.payload
+
+LOADER=loader.o
+
+BOCHS_R=bochs
+BOCHS_B=bochs/bios
+BOCHS_C=bochs/bochs-cvs
+BOCHS_X=bochs/bochs-cvs/bochs/bios
+
+BIOS_R=${BOCHS_R}/rombios.bin
+BIOS_B=${BOCHS_B}/rombios.bin
+BIOS_X=${BOCHS_X}/rombios.bin
+
+PAYLOAD=payload
+
+BOCHSCVS=-d:pserver:anonymous@cvs.bochs.sourceforge.net:/cvsroot/bochs
+
+#-------------------------------------------------
+
+all: clean build move #install
+
+#-------------------------------------------------
+
+epia: loader.o bios
+	cat ${ELF064} ${LOADER} ${BIOS_B} > ${PAYLOAD}
+build: loader.o bios
+	cat ${ELF064} ${LOADER} ${BIOS_B} > ${PAYLOAD}
+
+#-------------------------------------------------
+
+loader.o: loader.s
+	as86 loader.s -b ${LOADER}
+
+#-------------------------------------------------
+
+bios:
+	( cd ${BOCHS_B} ; make )
+	sync
+
+#-------------------------------------------------
+
+#64kb -- sive of Video BIOS
+#
+#C=`echo 64*1024 | bc`
+C=65536
+
+#0xC0000 -- start of Video BIOS in ram.
+#S=`echo -e "ibase=16 \n C0000" | bc`
+#
+# ELF header offset -- 0x1000 == 4096
+#
+# 786432+4096 = 790528
+S=790528
+
+videobios:
+	 dd if=/proc/kcore of=./video.bios.bin bs=1 count=${C} skip=${S}
+
+# 0xF000+0x1000
+S2=987136
+
+pcbios: 
+	 dd if=/proc/kcore of=./pc.bios.bin bs=1 count=${C} skip=${S2}
+
+pirq: pcbios
+	#grep -a "\$PIR" -B0 -A127 pc.bios.bin > pirq.bin
+	grep -a "\$PIR" -B0 -A0 pc.bios.bin > pirq.bin
+
+#now you have to hand edit the pirq.bin file
+
+#-------------------------------------------------
+
+bochslogin:
+	mkdir -p ${BOCHS_C}
+	echo "When prompted for a password, simply press the Enter key."
+	( cd ${BOCHS_C} ; cvs ${BOCHSCVS} login )
+
+bochsco:
+	mkdir -p ${BOCHS_C}
+	( cd ${BOCHS_C} ; cvs ${BOCHSCVS} co bochs/bios )
+
+bochsstat:
+	( cd ${BOCHS_C} ; cvs ${BOCHSCVS} status bochs/bios/rombios.c )
+
+bochsupdate:
+	( cd ${BOCHS_C} ; cvs ${BOCHSCVS} update bochs/bios/rombios.c )
+
+
+#-------------------------------------------------
+move:
+	cp ${PAYLOAD} ../../
+
+install:
+	cp -abf ${PAYLOAD} /tftpboot/kernel
+	sync
+
+#-------------------------------------------------
+
+clean:
+	rm -rf *.o ${PAYLOAD}
+	(cd ${BOCHS_B} ; make clean ) 
+
+distclean: clean
+	rm -rf ${BOCHS_C}
+
+#-------------------------------------------------
+
Index: util/ADLO/bochs/bios/rombios.c
===================================================================
--- util/ADLO/bochs/bios/rombios.c	(revision 0)
+++ util/ADLO/bochs/bios/rombios.c	(revision 0)
@@ -0,0 +1,10551 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: rombios.c,v 1.163 2006/07/07 16:10:37 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+//  Copyright (C) 2002  MandrakeSoft S.A.
+//
+//    MandrakeSoft S.A.
+//    43, rue d'Aboukir
+//    75002 Paris - France
+//    http://www.linux-mandrake.com/
+//    http://www.mandrakesoft.com/
+//
+//  This library is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU Lesser General Public
+//  License as published by the Free Software Foundation; either
+//  version 2 of the License, or (at your option) any later version.
+//
+//  This library is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+//  Lesser General Public License for more details.
+//
+//  You should have received a copy of the GNU Lesser General Public
+//  License along with this library; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
+
+// ROM BIOS for use with Bochs/Plex x86 emulation environment
+
+
+// ROM BIOS compatability entry points:
+// ===================================
+// $e05b ; POST Entry Point
+// $e2c3 ; NMI Handler Entry Point
+// $e3fe ; INT 13h Fixed Disk Services Entry Point
+// $e401 ; Fixed Disk Parameter Table
+// $e6f2 ; INT 19h Boot Load Service Entry Point
+// $e6f5 ; Configuration Data Table
+// $e729 ; Baud Rate Generator Table
+// $e739 ; INT 14h Serial Communications Service Entry Point
+// $e82e ; INT 16h Keyboard Service Entry Point
+// $e987 ; INT 09h Keyboard Service Entry Point
+// $ec59 ; INT 13h Diskette Service Entry Point
+// $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
+// $efc7 ; Diskette Controller Parameter Table
+// $efd2 ; INT 17h Printer Service Entry Point
+// $f045 ; INT 10 Functions 0-Fh Entry Point
+// $f065 ; INT 10h Video Support Service Entry Point
+// $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
+// $f841 ; INT 12h Memory Size Service Entry Point
+// $f84d ; INT 11h Equipment List Service Entry Point
+// $f859 ; INT 15h System Services Entry Point
+// $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
+// $fe6e ; INT 1Ah Time-of-day Service Entry Point
+// $fea5 ; INT 08h System Timer ISR Entry Point
+// $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
+// $ff53 ; IRET Instruction for Dummy Interrupt Handler
+// $ff54 ; INT 05h Print Screen Service Entry Point
+// $fff0 ; Power-up Entry Point
+// $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
+// $fffe ; System Model ID
+
+// NOTES for ATA/ATAPI driver (cbbochs@free.fr)
+//   Features
+//     - supports up to 4 ATA interfaces
+//     - device/geometry detection
+//     - 16bits/32bits device access
+//     - pchs/lba access
+//     - datain/dataout/packet command support
+//
+// NOTES for El-Torito Boot (cbbochs@free.fr)
+//   - CD-ROM booting is only available if ATA/ATAPI Driver is available
+//   - Current code is only able to boot mono-session cds 
+//   - Current code can not boot and emulate a hard-disk
+//     the bios will panic otherwise
+//   - Current code also use memory in EBDA segement. 
+//   - I used cmos byte 0x3D to store extended information on boot-device
+//   - Code has to be modified modified to handle multiple cdrom drives
+//   - Here are the cdrom boot failure codes:
+//       1 : no atapi device found
+//       2 : no atapi cdrom found
+//       3 : can not read cd - BRVD
+//       4 : cd is not eltorito (BRVD)
+//       5 : cd is not eltorito (ISO TAG)
+//       6 : cd is not eltorito (ELTORITO TAG)
+//       7 : can not read cd - boot catalog
+//       8 : boot catalog : bad header
+//       9 : boot catalog : bad platform
+//      10 : boot catalog : bad signature
+//      11 : boot catalog : bootable flag not set
+//      12 : can not read cd - boot image
+//
+//   ATA driver
+//   - EBDA segment. 
+//     I used memory starting at 0x121 in the segment
+//   - the translation policy is defined in cmos regs 0x39 & 0x3a
+//
+// TODO :
+//
+//   int74 
+//     - needs to be reworked.  Uses direct [bp] offsets. (?)
+//
+//   int13:
+//     - f04 (verify sectors) isn't complete  (?)
+//     - f02/03/04 should set current cyl,etc in BDA  (?)
+//     - rewrite int13_relocated & clean up int13 entry code
+//
+//   NOTES:
+//   - NMI access (bit7 of addr written to 70h)
+//
+//   ATA driver
+//   - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
+//   - could send the multiple-sector read/write commands
+//
+//   El-Torito
+//   - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
+//   - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
+//   - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
+//   - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
+//     This is ok. But DL should be reincremented afterwards. 
+//   - Fix all "FIXME ElTorito Various"
+//   - should be able to boot any cdrom instead of the first one
+//
+//   BCC Bug: find a generic way to handle the bug of #asm after an "if"  (fixed in 0.16.7)
+
+#define DEBUG_ROMBIOS      1
+
+#define DEBUG_ATA          0
+#define DEBUG_INT13_HD     0
+#define DEBUG_INT13_CD     0
+#define DEBUG_INT13_ET     0
+#define DEBUG_INT13_FL     0
+#define DEBUG_INT15        0
+#define DEBUG_INT16        0
+#define DEBUG_INT1A        0
+#define DEBUG_INT74        0
+#define DEBUG_APM          0
+
+#define BX_CPU           3
+#define BX_USE_PS2_MOUSE 1
+#define BX_CALL_INT15_4F 1
+#define BX_USE_EBDA      1
+#define BX_SUPPORT_FLOPPY 1
+#define BX_FLOPPY_ON_CNT 37   /* 2 seconds */
+//#define BX_PCIBIOS       1
+#define BX_APM           0
+
+#define LINUXBIOS	1
+
+#define BX_USE_ATADRV    1
+#define BX_ELTORITO_BOOT 1
+
+#define BX_MAX_ATA_INTERFACES   4
+#define BX_MAX_ATA_DEVICES      (BX_MAX_ATA_INTERFACES*2)
+
+#define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
+#define BX_DEBUG_SERIAL  1 /* output to COM1 */
+
+   /* model byte 0xFC = AT */
+#define SYS_MODEL_ID     0xFC
+#define SYS_SUBMODEL_ID  0x00
+#define BIOS_REVISION    1
+#define BIOS_CONFIG_TABLE 0xe6f5
+
+#ifndef BIOS_BUILD_DATE
+#  define BIOS_BUILD_DATE "06/23/99"
+#endif
+
+  // 1K of base memory used for Extended Bios Data Area (EBDA)
+  // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
+#define EBDA_SEG           0x9FC0
+#define EBDA_SIZE          1              // In KiB
+#define BASE_MEM_IN_K   (640 - EBDA_SIZE)
+
+  // Define the application NAME
+#ifdef PLEX86
+#  define BX_APPNAME "Plex86"
+#else
+#  define BX_APPNAME "Bochs"
+#endif
+
+  // Sanity Checks
+#if BX_USE_ATADRV && BX_CPU<3
+#    error The ATA/ATAPI Driver can only to be used with a 386+ cpu
+#endif
+#if BX_USE_ATADRV && !BX_USE_EBDA
+#    error ATA/ATAPI Driver can only be used if EBDA is available
+#endif
+#if BX_ELTORITO_BOOT && !BX_USE_ATADRV
+#    error El-Torito Boot can only be use if ATA/ATAPI Driver is available
+#endif
+#if BX_PCIBIOS && BX_CPU<3
+#    error PCI BIOS can only be used with 386+ cpu
+#endif
+#if BX_APM && BX_CPU<3
+#    error APM BIOS can only be used with 386+ cpu
+#endif
+
+#define PANIC_PORT  0x400
+#define PANIC_PORT2 0x401
+#define INFO_PORT   0x402
+#define DEBUG_PORT  0x403
+
+// define this if you want to make PCIBIOS working on a specific bridge only
+// undef enables PCIBIOS when at least one PCI device is found
+// i440FX is emulated by Bochs and QEMU
+#define PCI_FIXED_HOST_BRIDGE 0x12378086 ;; i440FX PCI bridge
+
+// #20  is dec 20
+// #$20 is hex 20 = 32
+// #0x20 is hex 20 = 32
+// LDA  #$20
+// JSR  $E820
+// LDD  .i,S
+// JSR  $C682
+// mov al, #$20
+
+// all hex literals should be prefixed with '0x'
+//   grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
+// no mov SEG-REG, #value, must mov register into seg-reg
+//   grep -i "mov[ ]*.s" rombios.c
+
+// This is for compiling with gcc2 and gcc3
+#define ASM_START #asm
+#define ASM_END #endasm
+
+ASM_START
+.rom
+
+.org 0x0000
+
+#if BX_CPU >= 3
+use16 386
+#else
+use16 286
+#endif
+
+MACRO HALT
+  ;; the HALT macro is called with the line number of the HALT call.
+  ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex 
+  ;; to print a BX_PANIC message.  This will normally halt the simulation
+  ;; with a message such as "BIOS panic at rombios.c, line 4091".
+  ;; However, users can choose to make panics non-fatal and continue.
+#if BX_VIRTUAL_PORTS
+  mov dx,#PANIC_PORT
+  mov ax,#?1
+  out dx,ax
+#else
+  mov dx,#0x80
+  mov ax,#?1
+  out dx,al
+#endif
+MEND
+
+MACRO JMP_AP
+  db 0xea
+  dw ?2
+  dw ?1
+MEND
+
+MACRO SET_INT_VECTOR
+  mov ax, ?3
+  mov ?1*4, ax
+  mov ax, ?2
+  mov ?1*4+2, ax
+MEND
+
+ASM_END
+
+typedef unsigned char  Bit8u;
+typedef unsigned short Bit16u;
+typedef unsigned short bx_bool;
+typedef unsigned long  Bit32u;
+
+#if BX_USE_ATADRV
+
+  void memsetb(seg,offset,value,count);
+  void memcpyb(dseg,doffset,sseg,soffset,count);
+  void memcpyd(dseg,doffset,sseg,soffset,count);
+  
+  // memset of count bytes
+    void 
+  memsetb(seg,offset,value,count)
+    Bit16u seg;
+    Bit16u offset;
+    Bit16u value;
+    Bit16u count;
+  {
+  ASM_START
+    push bp
+    mov  bp, sp
+  
+      push ax
+      push cx
+      push es
+      push di
+  
+      mov  cx, 10[bp] ; count
+      cmp  cx, #0x00
+      je   memsetb_end
+      mov  ax, 4[bp] ; segment
+      mov  es, ax
+      mov  ax, 6[bp] ; offset
+      mov  di, ax
+      mov  al, 8[bp] ; value
+      cld
+      rep
+       stosb
+  
+  memsetb_end:
+      pop di
+      pop es
+      pop cx
+      pop ax
+  
+    pop bp
+  ASM_END
+  }
+  
+#if 0 
+  // memcpy of count bytes
+    void 
+  memcpyb(dseg,doffset,sseg,soffset,count)
+    Bit16u dseg;
+    Bit16u doffset;
+    Bit16u sseg;
+    Bit16u soffset;
+    Bit16u count;
+  {
+  ASM_START
+    push bp
+    mov  bp, sp
+  
+      push ax
+      push cx
+      push es
+      push di
+      push ds
+      push si
+  
+      mov  cx, 12[bp] ; count
+      cmp  cx, #0x0000
+      je   memcpyb_end
+      mov  ax, 4[bp] ; dsegment
+      mov  es, ax
+      mov  ax, 6[bp] ; doffset
+      mov  di, ax
+      mov  ax, 8[bp] ; ssegment
+      mov  ds, ax
+      mov  ax, 10[bp] ; soffset
+      mov  si, ax
+      cld
+      rep
+       movsb
+  
+  memcpyb_end:
+      pop si
+      pop ds
+      pop di
+      pop es
+      pop cx
+      pop ax
+  
+    pop bp
+  ASM_END
+  }
+
+  // memcpy of count dword
+    void 
+  memcpyd(dseg,doffset,sseg,soffset,count)
+    Bit16u dseg;
+    Bit16u doffset;
+    Bit16u sseg;
+    Bit16u soffset;
+    Bit16u count;
+  {
+  ASM_START
+    push bp
+    mov  bp, sp
+  
+      push ax
+      push cx
+      push es
+      push di
+      push ds
+      push si
+  
+      mov  cx, 12[bp] ; count
+      cmp  cx, #0x0000
+      je   memcpyd_end
+      mov  ax, 4[bp] ; dsegment
+      mov  es, ax
+      mov  ax, 6[bp] ; doffset
+      mov  di, ax
+      mov  ax, 8[bp] ; ssegment
+      mov  ds, ax
+      mov  ax, 10[bp] ; soffset
+      mov  si, ax
+      cld
+      rep
+       movsd
+  
+  memcpyd_end:
+      pop si
+      pop ds
+      pop di
+      pop es
+      pop cx
+      pop ax
+  
+    pop bp
+  ASM_END
+  }
+#endif
+#endif //BX_USE_ATADRV
+
+  // read_dword and write_dword functions
+  static Bit32u         read_dword();
+  static void           write_dword();
+  
+    Bit32u
+  read_dword(seg, offset)
+    Bit16u seg;
+    Bit16u offset;
+  {
+  ASM_START
+    push bp
+    mov  bp, sp
+  
+      push bx
+      push ds
+      mov  ax, 4[bp] ; segment
+      mov  ds, ax
+      mov  bx, 6[bp] ; offset
+      mov  ax, [bx]
+      inc  bx
+      inc  bx
+      mov  dx, [bx]
+      ;; ax = return value (word)
+      ;; dx = return value (word)
+      pop  ds
+      pop  bx
+  
+    pop  bp
+  ASM_END
+  }
+  
+    void
+  write_dword(seg, offset, data)
+    Bit16u seg;
+    Bit16u offset;
+    Bit32u data;
+  {
+  ASM_START
+    push bp
+    mov  bp, sp
+  
+      push ax
+      push bx
+      push ds
+      mov  ax, 4[bp] ; segment
+      mov  ds, ax
+      mov  bx, 6[bp] ; offset
+      mov  ax, 8[bp] ; data word
+      mov  [bx], ax  ; write data word
+      inc  bx
+      inc  bx
+      mov  ax, 10[bp] ; data word
+      mov  [bx], ax  ; write data word
+      pop  ds
+      pop  bx
+      pop  ax
+  
+    pop  bp
+  ASM_END
+  }
+  
+  // Bit32u (unsigned long) and long helper functions
+  ASM_START
+  
+  ;; and function
+  landl:
+  landul:
+    SEG SS 
+      and ax,[di]
+    SEG SS 
+      and bx,2[di]
+    ret
+  
+  ;; add function
+  laddl:
+  laddul:
+    SEG SS 
+      add ax,[di]
+    SEG SS 
+      adc bx,2[di]
+    ret
+  
+  ;; cmp function
+  lcmpl:
+  lcmpul:
+    and eax, #0x0000FFFF
+    shl ebx, #16
+    add eax, ebx
+    shr ebx, #16
+    SEG SS
+      cmp eax, dword ptr [di]
+    ret
+  
+  ;; sub function
+  lsubl:
+  lsubul:
+    SEG SS
+    sub ax,[di]
+    SEG SS
+    sbb bx,2[di]
+    ret
+  
+  ;; mul function
+  lmull:
+  lmulul:
+    and eax, #0x0000FFFF
+    shl ebx, #16
+    add eax, ebx
+    SEG SS
+    mul eax, dword ptr [di]
+    mov ebx, eax
+    shr ebx, #16
+    ret
+  
+  ;; dec function
+  ldecl:
+  ldecul:
+    SEG SS
+    dec dword ptr [bx]
+    ret
+  
+  ;; or function
+  lorl:
+  lorul:
+    SEG SS
+    or  ax,[di]
+    SEG SS
+    or  bx,2[di]
+    ret
+  
+  ;; inc function
+  lincl:
+  lincul:
+    SEG SS
+    inc dword ptr [bx]
+    ret
+  
+  ;; tst function
+  ltstl:
+  ltstul:
+    and eax, #0x0000FFFF
+    shl ebx, #16
+    add eax, ebx
+    shr ebx, #16
+    test eax, eax
+    ret
+  
+  ;; sr function
+  lsrul:
+    mov  cx,di
+    jcxz lsr_exit
+    and  eax, #0x0000FFFF
+    shl  ebx, #16
+    add  eax, ebx
+  lsr_loop:
+    shr  eax, #1
+    loop lsr_loop
+    mov  ebx, eax
+    shr  ebx, #16
+  lsr_exit:
+    ret
+  
+  ;; sl function
+  lsll:
+  lslul:
+    mov  cx,di
+    jcxz lsl_exit
+    and  eax, #0x0000FFFF
+    shl  ebx, #16
+    add  eax, ebx
+  lsl_loop: 
+    shl  eax, #1
+    loop lsl_loop
+    mov  ebx, eax
+    shr  ebx, #16
+  lsl_exit:
+    ret
+  
+  idiv_:
+    cwd
+    idiv bx
+    ret
+
+  idiv_u:
+    xor dx,dx
+    div bx
+    ret
+
+  ldivul:
+    and  eax, #0x0000FFFF
+    shl  ebx, #16
+    add  eax, ebx
+    xor  edx, edx
+    SEG SS
+    mov  bx,  2[di]
+    shl  ebx, #16
+    SEG SS
+    mov  bx,  [di]
+    div  ebx
+    mov  ebx, eax
+    shr  ebx, #16
+    ret
+
+  ASM_END
+
+// for access to RAM area which is used by interrupt vectors
+// and BIOS Data Area
+
+typedef struct {
+  unsigned char filler1[0x400];
+  unsigned char filler2[0x6c];
+  Bit16u ticks_low;
+  Bit16u ticks_high;
+  Bit8u  midnight_flag;
+  } bios_data_t;
+
+#define BiosData ((bios_data_t  *) 0)
+
+#if BX_USE_ATADRV
+  typedef struct {
+    Bit16u heads;      // # heads
+    Bit16u cylinders;  // # cylinders
+    Bit16u spt;        // # sectors / track
+    } chs_t;
+
+  // DPTE definition
+  typedef struct {
+    Bit16u iobase1;
+    Bit16u iobase2;
+    Bit8u  prefix;
+    Bit8u  unused;
+    Bit8u  irq;
+    Bit8u  blkcount;
+    Bit8u  dma;
+    Bit8u  pio;
+    Bit16u options;
+    Bit16u reserved;
+    Bit8u  revision;
+    Bit8u  checksum;
+    } dpte_t;
+ 
+  typedef struct {
+    Bit8u  iface;        // ISA or PCI
+    Bit16u iobase1;      // IO Base 1
+    Bit16u iobase2;      // IO Base 2
+    Bit8u  irq;          // IRQ
+    } ata_channel_t;
+
+  typedef struct {
+    Bit8u  type;         // Detected type of ata (ata/atapi/none/unknown)
+    Bit8u  device;       // Detected type of attached devices (hd/cd/none)
+    Bit8u  removable;    // Removable device flag
+    Bit8u  lock;         // Locks for removable devices
+    // Bit8u  lba_capable;  // LBA capable flag - always yes for bochs devices
+    Bit8u  mode;         // transfert mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
+    Bit16u blksize;      // block size
+
+    Bit8u  translation;  // type of translation
+    chs_t  lchs;         // Logical CHS
+    chs_t  pchs;         // Physical CHS
+
+    Bit32u sectors;      // Total sectors count
+    } ata_device_t;
+
+  typedef struct {
+    // ATA channels info
+    ata_channel_t channels[BX_MAX_ATA_INTERFACES];
+
+    // ATA devices info
+    ata_device_t  devices[BX_MAX_ATA_DEVICES];
+    //
+    // map between (bios hd id - 0x80) and ata channels
+    Bit8u  hdcount, hdidmap[BX_MAX_ATA_DEVICES];                
+
+    // map between (bios cd id - 0xE0) and ata channels
+    Bit8u  cdcount, cdidmap[BX_MAX_ATA_DEVICES];                
+
+    // Buffer for DPTE table
+    dpte_t dpte;
+
+    // Count of transferred sectors and bytes
+    Bit16u trsfsectors;
+    Bit32u trsfbytes;
+
+    } ata_t;
+  
+#if BX_ELTORITO_BOOT
+  // ElTorito Device Emulation data 
+  typedef struct {
+    Bit8u  active;
+    Bit8u  media;
+    Bit8u  emulated_drive;
+    Bit8u  controller_index;
+    Bit16u device_spec;
+    Bit32u ilba;
+    Bit16u buffer_segment;
+    Bit16u load_segment;
+    Bit16u sector_count;
+    
+    // Virtual device
+    chs_t  vdevice;
+    } cdemu_t;
+#endif // BX_ELTORITO_BOOT
+  
+  // for access to EBDA area
+  //     The EBDA structure should conform to 
+  //     http://www.frontiernet.net/~fys/rombios.htm document
+  //     I made the ata and cdemu structs begin at 0x121 in the EBDA seg
+  typedef struct {
+    unsigned char filler1[0x3D];
+
+    // FDPT - Can be splitted in data members if needed
+    unsigned char fdpt0[0x10];
+    unsigned char fdpt1[0x10];
+
+    unsigned char filler2[0xC4];
+
+    // ATA Driver data
+    ata_t   ata;
+
+#if BX_ELTORITO_BOOT
+    // El Torito Emulation data
+    cdemu_t cdemu;
+#endif // BX_ELTORITO_BOOT
+
+    } ebda_data_t;
+  
+  #define EbdaData ((ebda_data_t *) 0)
+
+  // for access to the int13ext structure
+  typedef struct {
+    Bit8u  size;
+    Bit8u  reserved;
+    Bit16u count;
+    Bit16u offset;
+    Bit16u segment;
+    Bit32u lba1;
+    Bit32u lba2;
+    } int13ext_t;
+ 
+  #define Int13Ext ((int13ext_t *) 0)
+
+  // Disk Physical Table definition
+  typedef struct {
+    Bit16u  size;
+    Bit16u  infos;
+    Bit32u  cylinders;
+    Bit32u  heads;
+    Bit32u  spt;
+    Bit32u  sector_count1;
+    Bit32u  sector_count2;
+    Bit16u  blksize;
+    Bit16u  dpte_segment;
+    Bit16u  dpte_offset;
+    Bit16u  key;
+    Bit8u   dpi_length;
+    Bit8u   reserved1;
+    Bit16u  reserved2;
+    Bit8u   host_bus[4];
+    Bit8u   iface_type[8];
+    Bit8u   iface_path[8];
+    Bit8u   device_path[8];
+    Bit8u   reserved3;
+    Bit8u   checksum;
+    } dpt_t;
+ 
+  #define Int13DPT ((dpt_t *) 0)
+
+#endif // BX_USE_ATADRV
+
+typedef struct {
+  union {
+    struct {
+      Bit16u di, si, bp, sp;
+      Bit16u bx, dx, cx, ax;
+      } r16;
+    struct {
+      Bit16u filler[4];
+      Bit8u  bl, bh, dl, dh, cl, ch, al, ah;
+      } r8;
+    } u;
+  } pusha_regs_t;
+
+typedef struct {
+ union {
+  struct {
+    Bit32u edi, esi, ebp, esp;
+    Bit32u ebx, edx, ecx, eax;
+    } r32;
+  struct {
+    Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
+    Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
+    } r16;
+  struct {
+    Bit32u filler[4];
+    Bit8u  bl, bh; 
+    Bit16u filler1;
+    Bit8u  dl, dh; 
+    Bit16u filler2;
+    Bit8u  cl, ch;
+    Bit16u filler3;
+    Bit8u  al, ah;
+    Bit16u filler4;
+    } r8;
+  } u;
+} pushad_regs_t;
+
+typedef struct {
+  union {
+    struct {
+      Bit16u flags;
+      } r16;
+    struct {
+      Bit8u  flagsl;
+      Bit8u  flagsh;
+      } r8;
+    } u;
+  } flags_t;
+
+#define SetCF(x)   x.u.r8.flagsl |= 0x01
+#define SetZF(x)   x.u.r8.flagsl |= 0x40
+#define ClearCF(x) x.u.r8.flagsl &= 0xfe
+#define ClearZF(x) x.u.r8.flagsl &= 0xbf
+#define GetCF(x)   (x.u.r8.flagsl & 0x01)
+
+typedef struct {
+  Bit16u ip;
+  Bit16u cs;
+  flags_t flags;
+  } iret_addr_t;
+
+
+
+static Bit8u          inb();
+static Bit8u          inb_cmos();
+static void           outb();
+static void           outb_cmos();
+static Bit16u         inw();
+static void           outw();
+static void           init_rtc();
+static bx_bool        rtc_updating();
+
+static Bit8u          read_byte();
+static Bit16u         read_word();
+static void           write_byte();
+static void           write_word();
+static void           bios_printf();
+
+static Bit8u          inhibit_mouse_int_and_events();
+static void           enable_mouse_int_and_events();
+static Bit8u          send_to_mouse_ctrl();
+static Bit8u          get_mouse_data();
+static void           set_kbd_command_byte();
+
+static void           int09_function();
+static void           int13_harddisk();
+static void           int13_cdrom();
+static void           int13_cdemu();
+static void           int13_eltorito();
+static void           int13_diskette_function();
+static void           int14_function();
+static void           int15_function();
+static void           int16_function();
+static void           int17_function();
+static Bit32u         int19_function();
+static void           int1a_function();
+static void           int70_function();
+static void           int74_function();
+static Bit16u         get_CS();
+static Bit16u         get_SS();
+static unsigned int   enqueue_key();
+static unsigned int   dequeue_key();
+static void           get_hd_geometry();
+static void           set_diskette_ret_status();
+static void           set_diskette_current_cyl();
+static void           determine_floppy_media();
+static bx_bool        floppy_drive_exists();
+static bx_bool        floppy_drive_recal();
+static bx_bool        floppy_media_known();
+static bx_bool        floppy_media_sense();
+static bx_bool        set_enable_a20();
+static void           debugger_on();
+static void           debugger_off();
+static void           keyboard_init();
+static void           keyboard_panic();
+static void           shutdown_status_panic();
+static void           nmi_handler_msg();
+
+static void           print_bios_banner();
+static void           print_boot_device();
+static void           print_boot_failure();
+static void           print_cdromboot_failure();
+
+# if BX_USE_ATADRV
+
+// ATA / ATAPI driver
+void   ata_init();
+void   ata_detect();
+void   ata_reset();
+
+Bit16u ata_cmd_non_data();
+Bit16u ata_cmd_data_in();
+Bit16u ata_cmd_data_out();
+Bit16u ata_cmd_packet();
+
+Bit16u atapi_get_sense();
+Bit16u atapi_is_ready();
+Bit16u atapi_is_cdrom();
+
+#endif // BX_USE_ATADRV
+
+#if BX_ELTORITO_BOOT
+
+void   cdemu_init();
+Bit8u  cdemu_isactive();
+Bit8u  cdemu_emulated_drive();
+
+Bit16u cdrom_boot();
+
+#endif // BX_ELTORITO_BOOT
+
+static char bios_cvs_version_string[] = "$Revision: 1.163 $ $Date: 2006/07/07 16:10:37 $";
+
+#define BIOS_COPYRIGHT_STRING "(c) 2002 MandrakeSoft S.A. Written by Kevin Lawton & the Bochs team."
+
+#define BIOS_PRINTF_HALT     1
+#define BIOS_PRINTF_SCREEN   2
+#define BIOS_PRINTF_INFO     4
+#define BIOS_PRINTF_DEBUG    8
+#define BIOS_PRINTF_ALL      (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO)
+#define BIOS_PRINTF_DEBHALT  (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT)
+
+#define printf(format, p...)  bios_printf(BIOS_PRINTF_SCREEN, format, ##p)
+
+// Defines the output macros. 
+// BX_DEBUG goes to INFO port until we can easily choose debug info on a 
+// per-device basis. Debug info are sent only in debug mode
+#if DEBUG_ROMBIOS
+#  define BX_DEBUG(format, p...)  bios_printf(BIOS_PRINTF_INFO, format, ##p)    
+#else
+#  define BX_DEBUG(format, p...) 
+#endif
+#define BX_INFO(format, p...)   bios_printf(BIOS_PRINTF_INFO, format, ##p)
+#define BX_PANIC(format, p...)  bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
+
+#if DEBUG_ATA
+#  define BX_DEBUG_ATA(a...) BX_DEBUG(a)
+#else
+#  define BX_DEBUG_ATA(a...)
+#endif
+#if DEBUG_INT13_HD
+#  define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
+#else
+#  define BX_DEBUG_INT13_HD(a...)
+#endif
+#if DEBUG_INT13_CD
+#  define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
+#else
+#  define BX_DEBUG_INT13_CD(a...)
+#endif
+#if DEBUG_INT13_ET
+#  define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
+#else
+#  define BX_DEBUG_INT13_ET(a...)
+#endif
+#if DEBUG_INT13_FL
+#  define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
+#else
+#  define BX_DEBUG_INT13_FL(a...)
+#endif
+#if DEBUG_INT15
+#  define BX_DEBUG_INT15(a...) BX_DEBUG(a)
+#else
+#  define BX_DEBUG_INT15(a...)
+#endif
+#if DEBUG_INT16
+#  define BX_DEBUG_INT16(a...) BX_DEBUG(a)
+#else
+#  define BX_DEBUG_INT16(a...)
+#endif
+#if DEBUG_INT1A
+#  define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
+#else
+#  define BX_DEBUG_INT1A(a...)
+#endif
+#if DEBUG_INT74
+#  define BX_DEBUG_INT74(a...) BX_DEBUG(a)
+#else
+#  define BX_DEBUG_INT74(a...)
+#endif
+
+#define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
+#define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
+#define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
+#define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
+#define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
+#define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
+#define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
+#define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
+
+#define GET_AL() ( AX & 0x00ff )
+#define GET_BL() ( BX & 0x00ff )
+#define GET_CL() ( CX & 0x00ff )
+#define GET_DL() ( DX & 0x00ff )
+#define GET_AH() ( AX >> 8 )
+#define GET_BH() ( BX >> 8 )
+#define GET_CH() ( CX >> 8 )
+#define GET_DH() ( DX >> 8 )
+
+#define GET_ELDL() ( ELDX & 0x00ff )
+#define GET_ELDH() ( ELDX >> 8 )
+
+#define SET_CF()     FLAGS |= 0x0001
+#define CLEAR_CF()   FLAGS &= 0xfffe
+#define GET_CF()     (FLAGS & 0x0001)
+
+#define SET_ZF()     FLAGS |= 0x0040
+#define CLEAR_ZF()   FLAGS &= 0xffbf
+#define GET_ZF()     (FLAGS & 0x0040)
+
+#define UNSUPPORTED_FUNCTION 0x86
+
+#define none 0
+#define MAX_SCAN_CODE 0x58
+
+static struct {
+  Bit16u normal;
+  Bit16u shift;
+  Bit16u control;
+  Bit16u alt;
+  Bit8u lock_flags;
+  } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
+      {   none,   none,   none,   none, none },
+      { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
+      { 0x0231, 0x0221,   none, 0x7800, none }, /* 1! */
+      { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
+      { 0x0433, 0x0423,   none, 0x7a00, none }, /* 3# */
+      { 0x0534, 0x0524,   none, 0x7b00, none }, /* 4$ */
+      { 0x0635, 0x0625,   none, 0x7c00, none }, /* 5% */
+      { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
+      { 0x0837, 0x0826,   none, 0x7e00, none }, /* 7& */
+      { 0x0938, 0x092a,   none, 0x7f00, none }, /* 8* */
+      { 0x0a39, 0x0a28,   none, 0x8000, none }, /* 9( */
+      { 0x0b30, 0x0b29,   none, 0x8100, none }, /* 0) */
+      { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
+      { 0x0d3d, 0x0d2b,   none, 0x8300, none }, /* =+ */
+      { 0x0e08, 0x0e08, 0x0e7f,   none, none }, /* backspace */
+      { 0x0f09, 0x0f00,   none,   none, none }, /* tab */
+      { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
+      { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
+      { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
+      { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
+      { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
+      { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
+      { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
+      { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
+      { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
+      { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
+      { 0x1a5b, 0x1a7b, 0x1a1b,   none, none }, /* [{ */
+      { 0x1b5d, 0x1b7d, 0x1b1d,   none, none }, /* ]} */
+      { 0x1c0d, 0x1c0d, 0x1c0a,   none, none }, /* Enter */
+      {   none,   none,   none,   none, none }, /* L Ctrl */
+      { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
+      { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
+      { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
+      { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
+      { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
+      { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
+      { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
+      { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
+      { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
+      { 0x273b, 0x273a,   none,   none, none }, /* ;: */
+      { 0x2827, 0x2822,   none,   none, none }, /* '" */
+      { 0x2960, 0x297e,   none,   none, none }, /* `~ */
+      {   none,   none,   none,   none, none }, /* L shift */
+      { 0x2b5c, 0x2b7c, 0x2b1c,   none, none }, /* |\ */
+      { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
+      { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
+      { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
+      { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
+      { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
+      { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
+      { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
+      { 0x332c, 0x333c,   none,   none, none }, /* ,< */
+      { 0x342e, 0x343e,   none,   none, none }, /* .> */
+      { 0x352f, 0x353f,   none,   none, none }, /* /? */
+      {   none,   none,   none,   none, none }, /* R Shift */
+      { 0x372a, 0x372a,   none,   none, none }, /* * */
+      {   none,   none,   none,   none, none }, /* L Alt */
+      { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
+      {   none,   none,   none,   none, none }, /* caps lock */
+      { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
+      { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
+      { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
+      { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
+      { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
+      { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
+      { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
+      { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
+      { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
+      { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
+      {   none,   none,   none,   none, none }, /* Num Lock */
+      {   none,   none,   none,   none, none }, /* Scroll Lock */
+      { 0x4700, 0x4737, 0x7700,   none, 0x20 }, /* 7 Home */
+      { 0x4800, 0x4838,   none,   none, 0x20 }, /* 8 UP */
+      { 0x4900, 0x4939, 0x8400,   none, 0x20 }, /* 9 PgUp */
+      { 0x4a2d, 0x4a2d,   none,   none, none }, /* - */
+      { 0x4b00, 0x4b34, 0x7300,   none, 0x20 }, /* 4 Left */
+      { 0x4c00, 0x4c35,   none,   none, 0x20 }, /* 5 */
+      { 0x4d00, 0x4d36, 0x7400,   none, 0x20 }, /* 6 Right */
+      { 0x4e2b, 0x4e2b,   none,   none, none }, /* + */
+      { 0x4f00, 0x4f31, 0x7500,   none, 0x20 }, /* 1 End */
+      { 0x5000, 0x5032,   none,   none, 0x20 }, /* 2 Down */
+      { 0x5100, 0x5133, 0x7600,   none, 0x20 }, /* 3 PgDn */
+      { 0x5200, 0x5230,   none,   none, 0x20 }, /* 0 Ins */
+      { 0x5300, 0x532e,   none,   none, 0x20 }, /* Del */
+      {   none,   none,   none,   none, none },
+      {   none,   none,   none,   none, none },
+      {   none,   none,   none,   none, none },
+      { 0x5700, 0x5700,   none,   none, none }, /* F11 */
+      { 0x5800, 0x5800,   none,   none, none }  /* F12 */
+      };
+
+  Bit8u
+inb(port)
+  Bit16u port;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push dx
+    mov  dx, 4[bp]
+    in   al, dx
+    pop  dx
+
+  pop  bp
+ASM_END
+}
+
+#if BX_USE_ATADRV
+  Bit16u
+inw(port)
+  Bit16u port;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push dx
+    mov  dx, 4[bp]
+    in   ax, dx
+    pop  dx
+
+  pop  bp
+ASM_END
+}
+#endif
+
+  void
+outb(port, val)
+  Bit16u port;
+  Bit8u  val;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push dx
+    mov  dx, 4[bp]
+    mov  al, 6[bp]
+    out  dx, al
+    pop  dx
+    pop  ax
+
+  pop  bp
+ASM_END
+}
+
+#if BX_USE_ATADRV
+  void
+outw(port, val)
+  Bit16u port;
+  Bit16u  val;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push dx
+    mov  dx, 4[bp]
+    mov  ax, 6[bp]
+    out  dx, ax
+    pop  dx
+    pop  ax
+
+  pop  bp
+ASM_END
+}
+#endif
+
+  void
+outb_cmos(cmos_reg, val)
+  Bit8u cmos_reg;
+  Bit8u val;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    mov  al, 4[bp] ;; cmos_reg
+    out  0x70, al
+    mov  al, 6[bp] ;; val
+    out  0x71, al
+
+  pop  bp
+ASM_END
+}
+
+  Bit8u
+inb_cmos(cmos_reg)
+  Bit8u cmos_reg;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    mov  al, 4[bp] ;; cmos_reg
+    out 0x70, al
+    in  al, 0x71
+
+  pop  bp
+ASM_END
+}
+
+  void
+init_rtc()
+{
+  outb_cmos(0x0a, 0x26);
+  outb_cmos(0x0b, 0x02);
+  inb_cmos(0x0c);
+  inb_cmos(0x0d);
+}
+
+  bx_bool
+rtc_updating()
+{
+  // This function checks to see if the update-in-progress bit
+  // is set in CMOS Status Register A.  If not, it returns 0.
+  // If it is set, it tries to wait until there is a transition
+  // to 0, and will return 0 if such a transition occurs.  A 1
+  // is returned only after timing out.  The maximum period
+  // that this bit should be set is constrained to 244useconds.
+  // The count I use below guarantees coverage or more than
+  // this time, with any reasonable IPS setting.
+
+  Bit16u count;
+
+  count = 25000;
+  while (--count != 0) {
+    if ( (inb_cmos(0x0a) & 0x80) == 0 )
+      return(0);
+    }
+  return(1); // update-in-progress never transitioned to 0
+}
+
+
+  Bit8u
+read_byte(seg, offset)
+  Bit16u seg;
+  Bit16u offset;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push bx
+    push ds
+    mov  ax, 4[bp] ; segment
+    mov  ds, ax
+    mov  bx, 6[bp] ; offset
+    mov  al, [bx]
+    ;; al = return value (byte)
+    pop  ds
+    pop  bx
+
+  pop  bp
+ASM_END
+}
+
+  Bit16u
+read_word(seg, offset)
+  Bit16u seg;
+  Bit16u offset;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push bx
+    push ds
+    mov  ax, 4[bp] ; segment
+    mov  ds, ax
+    mov  bx, 6[bp] ; offset
+    mov  ax, [bx]
+    ;; ax = return value (word)
+    pop  ds
+    pop  bx
+
+  pop  bp
+ASM_END
+}
+
+  void
+write_byte(seg, offset, data)
+  Bit16u seg;
+  Bit16u offset;
+  Bit8u data;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push bx
+    push ds
+    mov  ax, 4[bp] ; segment
+    mov  ds, ax
+    mov  bx, 6[bp] ; offset
+    mov  al, 8[bp] ; data byte
+    mov  [bx], al  ; write data byte
+    pop  ds
+    pop  bx
+    pop  ax
+
+  pop  bp
+ASM_END
+}
+
+  void
+write_word(seg, offset, data)
+  Bit16u seg;
+  Bit16u offset;
+  Bit16u data;
+{
+ASM_START
+  push bp
+  mov  bp, sp
+
+    push ax
+    push bx
+    push ds
+    mov  ax, 4[bp] ; segment
+    mov  ds, ax
+    mov  bx, 6[bp] ; offset
+    mov  ax, 8[bp] ; data word
+    mov  [bx], ax  ; write data word
+    pop  ds
+    pop  bx
+    pop  ax
+
+  pop  bp
+ASM_END
+}
+
+  Bit16u
+get_CS()
+{
+ASM_START
+  mov  ax, cs
+ASM_END
+}
+
+  Bit16u
+get_SS()
+{
+ASM_START
+  mov  ax, ss
+ASM_END
+}
+
+#if BX_DEBUG_SERIAL
+/* serial debug port*/
+#define BX_DEBUG_PORT 0x03f8
+
+/* data */
+#define UART_RBR 0x00
+#define UART_THR 0x00
+
+/* control */
+#define UART_IER 0x01
+#define UART_IIR 0x02
+#define UART_FCR 0x02
+#define UART_LCR 0x03
+#define UART_MCR 0x04
+#define UART_DLL 0x00
+#define UART_DLM 0x01
+
+/* status */
+#define UART_LSR 0x05
+#define UART_MSR 0x06
+#define UART_SCR 0x07
+
+int uart_can_tx_byte(base_port)
+    Bit16u base_port;
+{
+    return inb(base_port + UART_LSR) & 0x20;
+}
+
+void uart_wait_to_tx_byte(base_port)
+    Bit16u base_port;
+{
+    while (!uart_can_tx_byte(base_port));
+}
+
+void uart_wait_until_sent(base_port)
+    Bit16u base_port;
+{
+    while (!(inb(base_port + UART_LSR) & 0x40));
+}
+
+void uart_tx_byte(base_port, data)
+    Bit16u base_port;
+    Bit8u data;
+{
+    uart_wait_to_tx_byte(base_port);
+    outb(base_port + UART_THR, data);
+    uart_wait_until_sent(base_port);
+}
+#endif
+
+  void
+wrch(c)
+  Bit8u  c;
+{
+  ASM_START
+  push bp
+  mov  bp, sp
+
+  push bx
+  mov  ah, #0x0e
+  mov  al, 4[bp]
+  xor  bx,bx
+  int  #0x10
+  pop  bx
+
+  pop  bp
+  ASM_END
+}
+ 
+  void
+send(action, c)
+  Bit16u action;
+  Bit8u  c;
+{
+#if BX_DEBUG_SERIAL
+  if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
+  uart_tx_byte(BX_DEBUG_PORT, c);
+#endif
+#if BX_VIRTUAL_PORTS
+  if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
+  if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
+#endif
+  if (action & BIOS_PRINTF_SCREEN) {
+    if (c == '\n') wrch('\r');
+    wrch(c);
+  }
+}
+
+  void
+put_int(action, val, width, neg)
+  Bit16u action;
+  short val, width;
+  bx_bool neg;
+{
+  short nval = val / 10;
+  if (nval)
+    put_int(action, nval, width - 1, neg);
+  else {
+    while (--width > 0) send(action, ' ');
+    if (neg) send(action, '-');
+  }
+  send(action, val - (nval * 10) + '0');
+}
+
+  void
+put_uint(action, val, width, neg)
+  Bit16u action;
+  unsigned short val;
+  short width;
+  bx_bool neg;
+{
+  unsigned short nval = val / 10;
+  if (nval)
+    put_uint(action, nval, width - 1, neg);
+  else {
+    while (--width > 0) send(action, ' ');
+    if (neg) send(action, '-');
+  }
+  send(action, val - (nval * 10) + '0');
+}
+
+  void
+put_luint(action, val, width, neg)
+  Bit16u action;
+  unsigned long val;
+  short width;
+  bx_bool neg;
+{
+  unsigned long nval = val / 10;
+  if (nval)
+    put_luint(action, nval, width - 1, neg);
+  else {
+    while (--width > 0) send(action, ' ');
+    if (neg) send(action, '-');
+  }
+  send(action, val - (nval * 10) + '0');
+}
+
+//--------------------------------------------------------------------------
+// bios_printf()
+//   A compact variable argument printf function which prints its output via
+//   an I/O port so that it can be logged by Bochs/Plex.  
+//   Currently, only %x is supported (or %02x, %04x, etc).
+//
+//   Supports %[format_width][format]
+//   where format can be d,x,c,s
+//--------------------------------------------------------------------------
+  void
+bios_printf(action, s)
+  Bit16u action;
+  Bit8u *s;
+{
+  Bit8u c, format_char;
+  bx_bool  in_format;
+  short i;
+  Bit16u  *arg_ptr;
+  Bit16u   arg_seg, arg, nibble, hibyte, shift_count, format_width;
+
+  arg_ptr = &s;
+  arg_seg = get_SS();
+
+  in_format = 0;
+  format_width = 0;
+
+  if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
+#if BX_VIRTUAL_PORTS
+    outb(PANIC_PORT2, 0x00);
+#endif
+    bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
+  }
+
+  while (c = read_byte(get_CS(), s)) {
+    if ( c == '%' ) {
+      in_format = 1;
+      format_width = 0;
+      }
+    else if (in_format) {
+      if ( (c>='0') && (c<='9') ) {
+        format_width = (format_width * 10) + (c - '0');
+        }
+      else {
+        arg_ptr++; // increment to next arg
+        arg = read_word(arg_seg, arg_ptr);
+        if (c == 'x') {
+          if (format_width == 0)
+            format_width = 4;
+          for (i=format_width-1; i>=0; i--) {
+            nibble = (arg >> (4 * i)) & 0x000f;
+            send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
+            }
+          }
+        else if (c == 'u') {
+          put_uint(action, arg, format_width, 0);
+          }
+        else if (c == 'l') {
+          s++;
+          arg_ptr++; /* increment to next arg */
+          hibyte = read_word(arg_seg, arg_ptr);
+          put_luint(action, ((Bit32u) hibyte << 16) | arg, format_width, 0);
+          }
+        else if (c == 'd') {
+          if (arg & 0x8000)
+            put_int(action, -arg, format_width - 1, 1);
+          else
+            put_int(action, arg, format_width, 0);
+          }
+        else if (c == 's') {
+          bios_printf(action & (~BIOS_PRINTF_HALT), arg);
+          }
+        else if (c == 'c') {
+          send(action, arg);
+          }
+        else
+          BX_PANIC("bios_printf: unknown format\n");
+          in_format = 0;
+        }
+      }
+    else {
+      send(action, c);
+      }
+    s ++;
+    }
+
+  if (action & BIOS_PRINTF_HALT) {
+    // freeze in a busy loop.  
+ASM_START
+    cli
+ halt2_loop:
+    hlt
+    jmp halt2_loop
+ASM_END
+    }
+}
+
+//--------------------------------------------------------------------------
+// keyboard_init
+//--------------------------------------------------------------------------
+// this file is based on LinuxBIOS implementation of keyboard.c
+// could convert to #asm to gain space
+  void
+keyboard_init()
+{
+#ifndef LINUXBIOS 
+    Bit16u max;
+
+    /* ------------------- Flush buffers ------------------------*/
+    /* Wait until buffer is empty */
+    max=0xffff;
+    while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
+
+    /* flush incoming keys */
+    max=0x2000;
+    while (--max > 0) {
+        outb(0x80, 0x00);
+        if (inb(0x64) & 0x01) {
+            inb(0x60);
+            max = 0x2000;
+            }
+        }
+  
+    // Due to timer issues, and if the IPS setting is > 15000000, 
+    // the incoming keys might not be flushed here. That will
+    // cause a panic a few lines below.  See sourceforge bug report :
+    // [ 642031 ] FATAL: Keyboard RESET error:993
+
+    /* ------------------- controller side ----------------------*/
+    /* send cmd = 0xAA, self test 8042 */
+    outb(0x64, 0xaa);
+
+    /* Wait until buffer is empty */
+    max=0xffff;
+    while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
+    if (max==0x0) keyboard_panic(00);
+
+    /* Wait for data */
+    max=0xffff;
+    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
+    if (max==0x0) keyboard_panic(01);
+
+    /* read self-test result, 0x55 should be returned from 0x60 */
+    if ((inb(0x60) != 0x55)){
+        keyboard_panic(991);
+    }
+
+    /* send cmd = 0xAB, keyboard interface test */
+    outb(0x64,0xab);
+
+    /* Wait until buffer is empty */
+    max=0xffff;
+    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
+    if (max==0x0) keyboard_panic(10);
+
+    /* Wait for data */
+    max=0xffff;
+    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
+    if (max==0x0) keyboard_panic(11);
+
+    /* read keyboard interface test result, */
+    /* 0x00 should be returned form 0x60 */
+    if ((inb(0x60) != 0x00)) {
+        keyboard_panic(992);
+    }
+
+    /* Enable Keyboard clock */
+    outb(0x64,0xae);
+    outb(0x64,0xa8);
+
+    /* ------------------- keyboard side ------------------------*/
+    /* reset kerboard and self test  (keyboard side) */
+    outb(0x60, 0xff);
+
+    /* Wait until buffer is empty */
+    max=0xffff;
+    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
+    if (max==0x0) keyboard_panic(20);
+
+    /* Wait for data */
+    max=0xffff;
+    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
+    if (max==0x0) keyboard_panic(21);
+
+    /* keyboard should return ACK */
+    if ((inb(0x60) != 0xfa)) {
+        keyboard_panic(993);
+    }
+
+    /* Wait for data */
+    max=0xffff;
+    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
+    if (max==0x0) keyboard_panic(31);
+
+    if ((inb(0x60) != 0xaa)) {
+        keyboard_panic(994);
+    }
+
+    /* Disable keyboard */
+    outb(0x60, 0xf5);
+
+    /* Wait until buffer is empty */
+    max=0xffff;
+    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
+    if (max==0x0) keyboard_panic(40);
+
+    /* Wait for data */
+    max=0xffff;
+    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
+    if (max==0x0) keyboard_panic(41);
+
+    /* keyboard should return ACK */
+    if ((inb(0x60) != 0xfa)) {
+        keyboard_panic(995);
+    }
+
+    /* Write Keyboard Mode */
+    outb(0x64, 0x60);
+
+    /* Wait until buffer is empty */
+    max=0xffff;
+    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
+    if (max==0x0) keyboard_panic(50);
+
+    /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
+    outb(0x60, 0x61);
+
+    /* Wait until buffer is empty */
+    max=0xffff;
+    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
+    if (max==0x0) keyboard_panic(60);
+
+    /* Enable keyboard */
+    outb(0x60, 0xf4);
+
+    /* Wait until buffer is empty */
+    max=0xffff;
+    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
+    if (max==0x0) keyboard_panic(70);
+
+    /* Wait for data */
+    max=0xffff;
+    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
+    if (max==0x0) keyboard_panic(70);
+
+    /* keyboard should return ACK */
+    if ((inb(0x60) != 0xfa)) {
+        keyboard_panic(996);
+    }
+
+    outb(0x80, 0x77);
+#endif
+}
+
+//--------------------------------------------------------------------------
+// keyboard_panic
+//--------------------------------------------------------------------------
+  void
+keyboard_panic(status)
+  Bit16u status;
+{
+  // If you're getting a 993 keyboard panic here, 
+  // please see the comment in keyboard_init
+  
+  BX_PANIC("Keyboard error:%u\n",status);
+}
+
+//--------------------------------------------------------------------------
+// shutdown_status_panic
+//   called when the shutdown statsu is not implemented, displays the status
+//--------------------------------------------------------------------------
+  void
+shutdown_status_panic(status)
+  Bit16u status;
+{
+  BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
+}
+
+//--------------------------------------------------------------------------
+// print_bios_banner
+//   displays a the bios version
+//--------------------------------------------------------------------------
+void
+print_bios_banner()
+{
+  printf(BX_APPNAME" BIOS - build: %s\n%s\nOptions: ",
+    BIOS_BUILD_DATE, bios_cvs_version_string);
+  printf(
+#ifdef BX_PCIBIOS
+  "pcibios "
+#endif
+#ifdef BX_ELTORITO_BOOT
+  "eltorito "
+#endif
+  "\n\n");
+}
+
+//--------------------------------------------------------------------------
+// print_boot_device
+//   displays the boot device
+//--------------------------------------------------------------------------
+
+static char drivetypes[][10]={"Floppy","Hard Disk","CD-Rom"};
+
+void
+print_boot_device(cdboot, drive)
+  Bit8u cdboot; Bit16u drive;
+{
+  Bit8u i;
+
+  // cdboot contains 0 if floppy/harddisk, 1 otherwise
+  // drive contains real/emulated boot drive
+
+  if(cdboot)i=2;                    // CD-Rom
+  else if((drive&0x0080)==0x00)i=0; // Floppy
+  else if((drive&0x0080)==0x80)i=1; // Hard drive
+  else return;
+  
+  printf("Booting from %s...\n",drivetypes[i]);
+}
+
+//--------------------------------------------------------------------------
+// print_boot_failure
+//   displays the reason why boot failed
+//--------------------------------------------------------------------------
+  void
+print_boot_failure(cdboot, drive, reason, lastdrive)
+  Bit8u cdboot; Bit8u drive; Bit8u lastdrive;
+{
+  Bit16u drivenum = drive&0x7f;
+
+  // cdboot: 1 if boot from cd, 0 otherwise
+  // drive : drive number
+  // reason: 0 signature check failed, 1 read error
+  // lastdrive: 1 boot drive is the last one in boot sequence
+ 
+  if (cdboot)
+    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s failed\n",drivetypes[2]);
+  else if (drive & 0x80)
+    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[1],drivenum);
+  else
+    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[0],drivenum);
+
+  if (lastdrive==1) {
+    if (reason==0)
+      BX_PANIC("Not a bootable disk\n");
+    else
+      BX_PANIC("Could not read the boot disk\n");
+  }
+}
+
+//--------------------------------------------------------------------------
+// print_cdromboot_failure
+//   displays the reason why boot failed
+//--------------------------------------------------------------------------
+  void
+print_cdromboot_failure( code )
+  Bit16u code;
+{
+  bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
+  
+  return;
+}
+
+void
+nmi_handler_msg()
+{
+  BX_PANIC("NMI Handler called\n");
+}
+
+void
+int18_panic_msg()
+{
+  BX_PANIC("INT18: BOOT FAILURE\n");
+}
+
+void
+log_bios_start()
+{
+#if BX_DEBUG_SERIAL
+  outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
+#endif
+  BX_INFO("%s\n", bios_cvs_version_string);
+}
+
+  bx_bool
+set_enable_a20(val)
+  bx_bool val;
+{
+  Bit8u  oldval;
+
+  // Use PS2 System Control port A to set A20 enable
+
+  // get current setting first
+  oldval = inb(0x92);
+
+  // change A20 status
+  if (val)
+    outb(0x92, oldval | 0x02);
+  else
+    outb(0x92, oldval & 0xfd);
+
+  return((oldval & 0x02) != 0);
+}
+
+  void
+debugger_on()
+{
+  outb(0xfedc, 0x01);
+}
+
+  void
+debugger_off()
+{
+  outb(0xfedc, 0x00);
+}
+
+#if BX_USE_ATADRV
+
+// ---------------------------------------------------------------------------
+// Start of ATA/ATAPI Driver
+// ---------------------------------------------------------------------------
+
+// Global defines -- ATA register and register bits.
+// command block & control block regs
+#define ATA_CB_DATA  0   // data reg         in/out pio_base_addr1+0
+#define ATA_CB_ERR   1   // error            in     pio_base_addr1+1
+#define ATA_CB_FR    1   // feature reg         out pio_base_addr1+1
+#define ATA_CB_SC    2   // sector count     in/out pio_base_addr1+2
+#define ATA_CB_SN    3   // sector number    in/out pio_base_addr1+3
+#define ATA_CB_CL    4   // cylinder low     in/out pio_base_addr1+4
+#define ATA_CB_CH    5   // cylinder high    in/out pio_base_addr1+5
+#define ATA_CB_DH    6   // device head      in/out pio_base_addr1+6
+#define ATA_CB_STAT  7   // primary status   in     pio_base_addr1+7
+#define ATA_CB_CMD   7   // command             out pio_base_addr1+7
+#define ATA_CB_ASTAT 6   // alternate status in     pio_base_addr2+6
+#define ATA_CB_DC    6   // device control      out pio_base_addr2+6
+#define ATA_CB_DA    7   // device address   in     pio_base_addr2+7
+
+#define ATA_CB_ER_ICRC 0x80    // ATA Ultra DMA bad CRC
+#define ATA_CB_ER_BBK  0x80    // ATA bad block
+#define ATA_CB_ER_UNC  0x40    // ATA uncorrected error
+#define ATA_CB_ER_MC   0x20    // ATA media change
+#define ATA_CB_ER_IDNF 0x10    // ATA id not found
+#define ATA_CB_ER_MCR  0x08    // ATA media change request
+#define ATA_CB_ER_ABRT 0x04    // ATA command aborted
+#define ATA_CB_ER_NTK0 0x02    // ATA track 0 not found
+#define ATA_CB_ER_NDAM 0x01    // ATA address mark not found
+
+#define ATA_CB_ER_P_SNSKEY 0xf0   // ATAPI sense key (mask)
+#define ATA_CB_ER_P_MCR    0x08   // ATAPI Media Change Request
+#define ATA_CB_ER_P_ABRT   0x04   // ATAPI command abort
+#define ATA_CB_ER_P_EOM    0x02   // ATAPI End of Media
+#define ATA_CB_ER_P_ILI    0x01   // ATAPI Illegal Length Indication
+
+// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
+#define ATA_CB_SC_P_TAG    0xf8   // ATAPI tag (mask)
+#define ATA_CB_SC_P_REL    0x04   // ATAPI release
+#define ATA_CB_SC_P_IO     0x02   // ATAPI I/O
+#define ATA_CB_SC_P_CD     0x01   // ATAPI C/D
+
+// bits 7-4 of the device/head (CB_DH) reg
+#define ATA_CB_DH_DEV0 0xa0    // select device 0
+#define ATA_CB_DH_DEV1 0xb0    // select device 1
+
+// status reg (CB_STAT and CB_ASTAT) bits
+#define ATA_CB_STAT_BSY  0x80  // busy
+#define ATA_CB_STAT_RDY  0x40  // ready
+#define ATA_CB_STAT_DF   0x20  // device fault
+#define ATA_CB_STAT_WFT  0x20  // write fault (old name)
+#define ATA_CB_STAT_SKC  0x10  // seek complete
+#define ATA_CB_STAT_SERV 0x10  // service
+#define ATA_CB_STAT_DRQ  0x08  // data request
+#define ATA_CB_STAT_CORR 0x04  // corrected
+#define ATA_CB_STAT_IDX  0x02  // index
+#define ATA_CB_STAT_ERR  0x01  // error (ATA)
+#define ATA_CB_STAT_CHK  0x01  // check (ATAPI)
+
+// device control reg (CB_DC) bits
+#define ATA_CB_DC_HD15   0x08  // bit should always be set to one
+#define ATA_CB_DC_SRST   0x04  // soft reset
+#define ATA_CB_DC_NIEN   0x02  // disable interrupts
+
+// Most mandtory and optional ATA commands (from ATA-3),
+#define ATA_CMD_CFA_ERASE_SECTORS            0xC0
+#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE     0x03
+#define ATA_CMD_CFA_TRANSLATE_SECTOR         0x87
+#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE  0xCD
+#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE   0x38
+#define ATA_CMD_CHECK_POWER_MODE1            0xE5
+#define ATA_CMD_CHECK_POWER_MODE2            0x98
+#define ATA_CMD_DEVICE_RESET                 0x08
+#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC    0x90
+#define ATA_CMD_FLUSH_CACHE                  0xE7
+#define ATA_CMD_FORMAT_TRACK                 0x50
+#define ATA_CMD_IDENTIFY_DEVICE              0xEC
+#define ATA_CMD_IDENTIFY_DEVICE_PACKET       0xA1
+#define ATA_CMD_IDENTIFY_PACKET_DEVICE       0xA1
+#define ATA_CMD_IDLE1                        0xE3
+#define ATA_CMD_IDLE2                        0x97
+#define ATA_CMD_IDLE_IMMEDIATE1              0xE1
+#define ATA_CMD_IDLE_IMMEDIATE2              0x95
+#define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS  0x91
+#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
+#define ATA_CMD_NOP                          0x00
+#define ATA_CMD_PACKET                       0xA0
+#define ATA_CMD_READ_BUFFER                  0xE4
+#define ATA_CMD_READ_DMA                     0xC8
+#define ATA_CMD_READ_DMA_QUEUED              0xC7
+#define ATA_CMD_READ_MULTIPLE                0xC4
+#define ATA_CMD_READ_SECTORS                 0x20
+#define ATA_CMD_READ_VERIFY_SECTORS          0x40
+#define ATA_CMD_RECALIBRATE                  0x10
+#define ATA_CMD_SEEK                         0x70
+#define ATA_CMD_SET_FEATURES                 0xEF
+#define ATA_CMD_SET_MULTIPLE_MODE            0xC6
+#define ATA_CMD_SLEEP1                       0xE6
+#define ATA_CMD_SLEEP2                       0x99
+#define ATA_CMD_STANDBY1                     0xE2
+#define ATA_CMD_STANDBY2                     0x96
+#define ATA_CMD_STANDBY_IMMEDIATE1           0xE0
+#define ATA_CMD_STANDBY_IMMEDIATE2           0x94
+#define ATA_CMD_WRITE_BUFFER                 0xE8
+#define ATA_CMD_WRITE_DMA                    0xCA
+#define ATA_CMD_WRITE_DMA_QUEUED             0xCC
+#define ATA_CMD_WRITE_MULTIPLE               0xC5
+#define ATA_CMD_WRITE_SECTORS                0x30
+#define ATA_CMD_WRITE_VERIFY                 0x3C
+
+#define ATA_IFACE_NONE    0x00
+#define ATA_IFACE_ISA     0x00
+#define ATA_IFACE_PCI     0x01
+
+#define ATA_TYPE_NONE     0x00
+#define ATA_TYPE_UNKNOWN  0x01
+#define ATA_TYPE_ATA      0x02
+#define ATA_TYPE_ATAPI    0x03
+
+#define ATA_DEVICE_NONE  0x00
+#define ATA_DEVICE_HD    0xFF
+#define ATA_DEVICE_CDROM 0x05
+
+#define ATA_MODE_NONE    0x00
+#define ATA_MODE_PIO16   0x00
+#define ATA_MODE_PIO32   0x01
+#define ATA_MODE_ISADMA  0x02
+#define ATA_MODE_PCIDMA  0x03
+#define ATA_MODE_USEIRQ  0x10
+
+#define ATA_TRANSLATION_NONE  0
+#define ATA_TRANSLATION_LBA   1
+#define ATA_TRANSLATION_LARGE 2
+#define ATA_TRANSLATION_RECHS 3
+
+#define ATA_DATA_NO      0x00
+#define ATA_DATA_IN      0x01
+#define ATA_DATA_OUT     0x02
+  
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : initialization
+// ---------------------------------------------------------------------------
+void ata_init( )
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit8u  channel, device;
+
+  // Channels info init. 
+  for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
+    write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
+    write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
+    write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
+    write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
+    }
+
+  // Devices info init. 
+  for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
+    write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
+    write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
+    write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
+    write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
+    write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
+    write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
+    write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
+    write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
+    write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
+    write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
+    write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
+    write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
+    write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
+    
+    write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
+    }
+
+  // hdidmap  and cdidmap init. 
+  for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
+    write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
+    write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
+    }
+
+  write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
+  write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : device detection
+// ---------------------------------------------------------------------------
+
+void ata_detect( )
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit8u  hdcount, cdcount, device, type;
+  Bit8u  buffer[0x0200];
+  Bit16u i;
+
+#if BX_MAX_ATA_INTERFACES > 0
+  write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
+  write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
+  write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
+  write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
+#endif
+#if BX_MAX_ATA_INTERFACES > 1
+  write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
+  write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
+  write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
+  write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
+#endif
+#if BX_MAX_ATA_INTERFACES > 2
+  write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
+  write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
+  write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
+  write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
+#endif
+#if BX_MAX_ATA_INTERFACES > 3
+  write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
+  write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
+  write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
+  write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
+#endif
+#if BX_MAX_ATA_INTERFACES > 4
+#error Please fill the ATA interface informations
+#endif
+
+  // Device detection
+  hdcount=cdcount=0;
+  
+  for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
+    Bit16u iobase1, iobase2;
+    Bit8u  channel, slave, shift;
+    Bit8u  sc, sn, cl, ch, st;
+
+    channel = device / 2;
+    slave = device % 2;
+
+    iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
+    iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
+
+    // Disable interrupts
+    outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+
+    // Look for device
+    outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
+    outb(iobase1+ATA_CB_SC, 0x55);
+    outb(iobase1+ATA_CB_SN, 0xaa);
+    outb(iobase1+ATA_CB_SC, 0xaa);
+    outb(iobase1+ATA_CB_SN, 0x55);
+    outb(iobase1+ATA_CB_SC, 0x55);
+    outb(iobase1+ATA_CB_SN, 0xaa);
+
+    // If we found something
+    sc = inb(iobase1+ATA_CB_SC);
+    sn = inb(iobase1+ATA_CB_SN);
+
+    if ( (sc == 0x55) && (sn == 0xaa) ) {
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
+    
+      // reset the channel
+      ata_reset(device);
+      
+      // check for ATA or ATAPI
+      outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
+      sc = inb(iobase1+ATA_CB_SC);
+      sn = inb(iobase1+ATA_CB_SN);
+      if ((sc==0x01) && (sn==0x01)) {
+        cl = inb(iobase1+ATA_CB_CL);
+        ch = inb(iobase1+ATA_CB_CH);
+        st = inb(iobase1+ATA_CB_STAT);
+
+        if ((cl==0x14) && (ch==0xeb)) {
+          write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
+        } else if ((cl==0x00) && (ch==0x00) && (st!=0x00)) {
+          write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
+        } else if ((cl==0xff) && (ch==0xff)) {
+          write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
+        }
+      }
+    }
+
+    type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
+    
+    // Now we send a IDENTIFY command to ATA device 
+    if(type == ATA_TYPE_ATA) {
+      Bit32u sectors;
+      Bit16u cylinders, heads, spt, blksize;
+      Bit8u  translation, removable, mode;
+
+      //Temporary values to do the transfer
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
+
+      if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
+        BX_PANIC("ata-detect: Failed to detect ATA device\n");
+
+      removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
+      mode      = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
+      blksize   = read_word(get_SS(),buffer+10);
+      
+      cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
+      heads     = read_word(get_SS(),buffer+(3*2)); // word 3
+      spt       = read_word(get_SS(),buffer+(6*2)); // word 6
+
+      sectors   = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
+
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
+      write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
+      write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
+      write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
+      write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
+      write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
+      BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
+
+      translation = inb_cmos(0x39 + channel/2);
+      for (shift=device%4; shift>0; shift--) translation >>= 2;
+      translation &= 0x03;
+
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
+
+      switch (translation) {
+        case ATA_TRANSLATION_NONE:
+          BX_INFO("none");
+          break;
+        case ATA_TRANSLATION_LBA:
+          BX_INFO("lba");
+          break;
+        case ATA_TRANSLATION_LARGE:
+          BX_INFO("large");
+          break;
+        case ATA_TRANSLATION_RECHS:
+          BX_INFO("r-echs");
+          break;
+        }
+      switch (translation) {
+        case ATA_TRANSLATION_NONE:
+          break;
+        case ATA_TRANSLATION_LBA:
+          spt = 63;
+          sectors /= 63;
+          heads = sectors / 1024;
+          if (heads>128) heads = 255;
+          else if (heads>64) heads = 128;
+          else if (heads>32) heads = 64;
+          else if (heads>16) heads = 32;
+          else heads=16;
+          cylinders = sectors / heads;
+          break;
+        case ATA_TRANSLATION_RECHS:
+          // Take care not to overflow
+          if (heads==16) {
+            if(cylinders>61439) cylinders=61439;
+            heads=15;
+            cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
+            }
+          // then go through the large bitshift process
+        case ATA_TRANSLATION_LARGE:
+          while(cylinders > 1024) {
+            cylinders >>= 1;
+            heads <<= 1;
+
+            // If we max out the head count
+            if (heads > 127) break;
+          }
+          break;
+        }
+      // clip to 1024 cylinders in lchs
+      if (cylinders > 1024) cylinders=1024;
+      BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
+
+      write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
+      write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
+      write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
+ 
+      // fill hdidmap 
+      write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
+      hdcount++;
+      }
+    
+    // Now we send a IDENTIFY command to ATAPI device
+    if(type == ATA_TYPE_ATAPI) {
+ 
+      Bit8u  type, removable, mode;
+      Bit16u blksize;
+
+      //Temporary values to do the transfer
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
+
+      if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
+        BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
+
+      type      = read_byte(get_SS(),buffer+1) & 0x1f;
+      removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
+      mode      = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
+      blksize   = 2048;
+
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
+      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
+      write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
+
+      // fill cdidmap 
+      write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
+      cdcount++;
+      }
+  
+      {
+      Bit32u sizeinmb;
+      Bit16u ataversion;
+      Bit8u  c, i, version, model[41];
+      
+      switch (type) {
+        case ATA_TYPE_ATA:
+          sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
+          sizeinmb >>= 11;
+        case ATA_TYPE_ATAPI:
+          // Read ATA/ATAPI version
+          ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
+          for(version=15;version>0;version--) { 
+            if((ataversion&(1<<version))!=0)
+            break;
+            }
+
+          // Read model name
+          for(i=0;i<20;i++){
+            write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
+            write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
+            }
+
+          // Reformat
+          write_byte(get_SS(),model+40,0x00);
+          for(i=39;i>0;i--){
+            if(read_byte(get_SS(),model+i)==0x20)
+              write_byte(get_SS(),model+i,0x00);
+            else break;
+            }
+          break;
+        }
+
+      switch (type) {
+        case ATA_TYPE_ATA:
+          printf("ata%d %s: ",channel,slave?" slave":"master");
+          i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
+          printf(" ATA-%d Hard-Disk (%lu MBytes)\n", version, sizeinmb);
+          break;
+        case ATA_TYPE_ATAPI:
+          printf("ata%d %s: ",channel,slave?" slave":"master");
+          i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
+          if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
+            printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
+          else
+            printf(" ATAPI-%d Device\n",version);
+          break;
+        case ATA_TYPE_UNKNOWN:
+          printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
+          break;
+        }
+      }
+    }
+
+  // Store the devices counts
+  write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
+  write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
+  write_byte(0x40,0x75, hdcount);
+ 
+  printf("\n");
+
+  // FIXME : should use bios=cmos|auto|disable bits
+  // FIXME : should know about translation bits
+  // FIXME : move hard_drive_post here 
+  
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : software reset 
+// ---------------------------------------------------------------------------
+// ATA-3
+// 8.2.1 Software reset - Device 0
+
+void   ata_reset(device)
+Bit16u device;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit16u iobase1, iobase2;
+  Bit8u  channel, slave, sn, sc; 
+  Bit16u max;
+
+  channel = device / 2;
+  slave = device % 2;
+
+  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+
+  // Reset
+
+// 8.2.1 (a) -- set SRST in DC
+  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
+
+// 8.2.1 (b) -- wait for BSY
+  max=0xff;
+  while(--max>0) {
+    Bit8u status = inb(iobase1+ATA_CB_STAT);
+    if ((status & ATA_CB_STAT_BSY) != 0) break;
+  }
+
+// 8.2.1 (f) -- clear SRST
+  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+
+  if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
+
+// 8.2.1 (g) -- check for sc==sn==0x01
+    // select device
+    outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
+    sc = inb(iobase1+ATA_CB_SC);
+    sn = inb(iobase1+ATA_CB_SN);
+
+    if ( (sc==0x01) && (sn==0x01) ) {
+
+// 8.2.1 (h) -- wait for not BSY
+      max=0xff;
+      while(--max>0) {
+        Bit8u status = inb(iobase1+ATA_CB_STAT);
+        if ((status & ATA_CB_STAT_BSY) == 0) break;
+        }
+      }
+    }
+
+// 8.2.1 (i) -- wait for DRDY
+  max=0xfff;
+  while(--max>0) {
+    Bit8u status = inb(iobase1+ATA_CB_STAT);
+      if ((status & ATA_CB_STAT_RDY) != 0) break;
+  }
+
+  // Enable interrupts
+  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a non data command 
+// ---------------------------------------------------------------------------
+
+Bit16u ata_cmd_non_data()
+{return 0;}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a data-in command
+// ---------------------------------------------------------------------------
+      // returns
+      // 0 : no error
+      // 1 : BUSY bit set
+      // 2 : read error
+      // 3 : expected DRQ=1
+      // 4 : no sectors left to read/verify
+      // 5 : more sectors to read/verify
+      // 6 : no sectors left to write
+      // 7 : more sectors to write
+Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
+Bit16u device, command, count, cylinder, head, sector, segment, offset;
+Bit32u lba;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit16u iobase1, iobase2, blksize;
+  Bit8u  channel, slave;
+  Bit8u  status, current, mode;
+
+  channel = device / 2;
+  slave   = device % 2;
+
+  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+  mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+  blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
+  if (mode == ATA_MODE_PIO32) blksize>>=2;
+  else blksize>>=1;
+
+  // sector will be 0 only on lba access. Convert to lba-chs
+  if (sector == 0) {
+    sector = (Bit16u) (lba & 0x000000ffL);
+    lba >>= 8;
+    cylinder = (Bit16u) (lba & 0x0000ffffL);
+    lba >>= 16;
+    head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
+    }
+
+  // Reset count of transferred data
+  write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
+  write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
+  current = 0;
+
+  status = inb(iobase1 + ATA_CB_STAT);
+  if (status & ATA_CB_STAT_BSY) return 1;
+
+  outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+  outb(iobase1 + ATA_CB_FR, 0x00);
+  outb(iobase1 + ATA_CB_SC, count);
+  outb(iobase1 + ATA_CB_SN, sector);
+  outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
+  outb(iobase1 + ATA_CB_CH, cylinder >> 8);
+  outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
+  outb(iobase1 + ATA_CB_CMD, command);
+
+  while (1) {
+    status = inb(iobase1 + ATA_CB_STAT);
+    if ( !(status & ATA_CB_STAT_BSY) ) break;
+    }
+
+  if (status & ATA_CB_STAT_ERR) {
+    BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
+    return 2;
+    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
+    BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
+    return 3;
+  }
+
+  // FIXME : move seg/off translation here
+
+ASM_START
+        sti  ;; enable higher priority interrupts
+ASM_END
+
+  while (1) {
+
+ASM_START
+        push bp
+        mov  bp, sp
+        mov  di, _ata_cmd_data_in.offset + 2[bp]  
+        mov  ax, _ata_cmd_data_in.segment + 2[bp] 
+        mov  cx, _ata_cmd_data_in.blksize + 2[bp] 
+
+        ;; adjust if there will be an overrun. 2K max sector size
+        cmp   di, #0xf800 ;; 
+        jbe   ata_in_no_adjust
+
+ata_in_adjust:
+        sub   di, #0x0800 ;; sub 2 kbytes from offset
+        add   ax, #0x0080 ;; add 2 Kbytes to segment
+
+ata_in_no_adjust:
+        mov   es, ax      ;; segment in es
+
+        mov   dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
+
+        mov  ah, _ata_cmd_data_in.mode + 2[bp] 
+        cmp  ah, #ATA_MODE_PIO32
+        je   ata_in_32
+
+ata_in_16:
+        rep
+          insw ;; CX words transfered from port(DX) to ES:[DI]
+        jmp ata_in_done
+
+ata_in_32:
+        rep
+          insd ;; CX dwords transfered from port(DX) to ES:[DI]
+
+ata_in_done:
+        mov  _ata_cmd_data_in.offset + 2[bp], di
+        mov  _ata_cmd_data_in.segment + 2[bp], es
+        pop  bp
+ASM_END
+
+    current++;
+    write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
+    count--;
+    status = inb(iobase1 + ATA_CB_STAT);
+    if (count == 0) {
+      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
+          != ATA_CB_STAT_RDY ) {
+        BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
+        return 4;
+        }
+      break;
+      }
+    else {
+      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
+          != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
+        BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
+        return 5;
+      }
+      continue;
+    }
+  }
+  // Enable interrupts
+  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
+  return 0;
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a data-out command
+// ---------------------------------------------------------------------------
+      // returns
+      // 0 : no error
+      // 1 : BUSY bit set
+      // 2 : read error
+      // 3 : expected DRQ=1
+      // 4 : no sectors left to read/verify
+      // 5 : more sectors to read/verify
+      // 6 : no sectors left to write
+      // 7 : more sectors to write
+Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
+Bit16u device, command, count, cylinder, head, sector, segment, offset;
+Bit32u lba;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit16u iobase1, iobase2, blksize;
+  Bit8u  channel, slave;
+  Bit8u  status, current, mode;
+
+  channel = device / 2;
+  slave   = device % 2;
+
+  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+  mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+  blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
+  if (mode == ATA_MODE_PIO32) blksize>>=2;
+  else blksize>>=1;
+
+  // sector will be 0 only on lba access. Convert to lba-chs
+  if (sector == 0) {
+    sector = (Bit16u) (lba & 0x000000ffL);
+    lba >>= 8;
+    cylinder = (Bit16u) (lba & 0x0000ffffL);
+    lba >>= 16;
+    head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
+    }
+
+  // Reset count of transferred data
+  write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
+  write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
+  current = 0;
+
+  status = inb(iobase1 + ATA_CB_STAT);
+  if (status & ATA_CB_STAT_BSY) return 1;
+
+  outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+  outb(iobase1 + ATA_CB_FR, 0x00);
+  outb(iobase1 + ATA_CB_SC, count);
+  outb(iobase1 + ATA_CB_SN, sector);
+  outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
+  outb(iobase1 + ATA_CB_CH, cylinder >> 8);
+  outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
+  outb(iobase1 + ATA_CB_CMD, command);
+
+  while (1) {
+    status = inb(iobase1 + ATA_CB_STAT);
+    if ( !(status & ATA_CB_STAT_BSY) ) break;
+    }
+
+  if (status & ATA_CB_STAT_ERR) {
+    BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
+    return 2;
+    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
+    BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
+    return 3;
+    }
+
+  // FIXME : move seg/off translation here
+
+ASM_START
+        sti  ;; enable higher priority interrupts
+ASM_END
+
+  while (1) {
+
+ASM_START
+        push bp
+        mov  bp, sp
+        mov  si, _ata_cmd_data_out.offset + 2[bp]  
+        mov  ax, _ata_cmd_data_out.segment + 2[bp] 
+        mov  cx, _ata_cmd_data_out.blksize + 2[bp] 
+
+        ;; adjust if there will be an overrun. 2K max sector size
+        cmp   si, #0xf800 ;; 
+        jbe   ata_out_no_adjust
+
+ata_out_adjust:
+        sub   si, #0x0800 ;; sub 2 kbytes from offset
+        add   ax, #0x0080 ;; add 2 Kbytes to segment
+
+ata_out_no_adjust:
+        mov   es, ax      ;; segment in es
+
+        mov   dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
+
+        mov  ah, _ata_cmd_data_out.mode + 2[bp] 
+        cmp  ah, #ATA_MODE_PIO32
+        je   ata_out_32
+
+ata_out_16:
+        seg ES
+        rep
+          outsw ;; CX words transfered from port(DX) to ES:[SI]
+        jmp ata_out_done
+
+ata_out_32:
+        seg ES
+        rep
+          outsd ;; CX dwords transfered from port(DX) to ES:[SI]
+
+ata_out_done:
+        mov  _ata_cmd_data_out.offset + 2[bp], si
+        mov  _ata_cmd_data_out.segment + 2[bp], es
+        pop  bp
+ASM_END
+
+    current++;
+    write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
+    count--;
+    status = inb(iobase1 + ATA_CB_STAT);
+    if (count == 0) {
+      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
+          != ATA_CB_STAT_RDY ) {
+        BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
+        return 6;
+        }
+      break;
+      }
+    else {
+      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
+          != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
+        BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
+        return 7;
+      }
+      continue;
+    }
+  }
+  // Enable interrupts
+  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
+  return 0;
+}
+
+// ---------------------------------------------------------------------------
+// ATA/ATAPI driver : execute a packet command
+// ---------------------------------------------------------------------------
+      // returns
+      // 0 : no error
+      // 1 : error in parameters
+      // 2 : BUSY bit set
+      // 3 : error
+      // 4 : not ready
+Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
+Bit8u  cmdlen,inout;
+Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
+Bit16u header;
+Bit32u length;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit16u iobase1, iobase2;
+  Bit16u lcount, lbefore, lafter, count;
+  Bit8u  channel, slave;
+  Bit8u  status, mode, lmode;
+  Bit32u total, transfer;
+
+  channel = device / 2;
+  slave = device % 2;
+
+  // Data out is not supported yet
+  if (inout == ATA_DATA_OUT) {
+    BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
+    return 1;
+    }
+
+  // The header length must be even
+  if (header & 1) {
+    BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
+    return 1;
+    }
+
+  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+  mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+  transfer= 0L;
+
+  if (cmdlen < 12) cmdlen=12;
+  if (cmdlen > 12) cmdlen=16;
+  cmdlen>>=1;
+
+  // Reset count of transferred data
+  write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
+  write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
+
+  status = inb(iobase1 + ATA_CB_STAT);
+  if (status & ATA_CB_STAT_BSY) return 2;
+
+  outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
+  // outb(iobase1 + ATA_CB_FR, 0x00);
+  // outb(iobase1 + ATA_CB_SC, 0x00);
+  // outb(iobase1 + ATA_CB_SN, 0x00);
+  outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
+  outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
+  outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
+  outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
+
+  // Device should ok to receive command
+  while (1) {
+    status = inb(iobase1 + ATA_CB_STAT);
+    if ( !(status & ATA_CB_STAT_BSY) ) break;
+    }
+
+  if (status & ATA_CB_STAT_ERR) {
+    BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
+    return 3;
+    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
+    BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
+    return 4;
+    }
+
+  // Normalize address
+  cmdseg += (cmdoff / 16);
+  cmdoff %= 16;
+
+  // Send command to device
+ASM_START
+      sti  ;; enable higher priority interrupts
+ 
+      push bp
+      mov  bp, sp
+    
+      mov  si, _ata_cmd_packet.cmdoff + 2[bp]  
+      mov  ax, _ata_cmd_packet.cmdseg + 2[bp] 
+      mov  cx, _ata_cmd_packet.cmdlen + 2[bp] 
+      mov  es, ax      ;; segment in es
+
+      mov  dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
+
+      seg ES
+      rep
+        outsw ;; CX words transfered from port(DX) to ES:[SI]
+
+      pop  bp
+ASM_END
+
+  if (inout == ATA_DATA_NO) {
+    status = inb(iobase1 + ATA_CB_STAT);
+    }
+  else {
+  while (1) {
+
+      status = inb(iobase1 + ATA_CB_STAT);
+
+      // Check if command completed
+      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
+
+      if (status & ATA_CB_STAT_ERR) {
+        BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
+        return 3;
+      }
+
+      // Device must be ready to send data
+      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
+            != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
+        BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
+        return 4;
+        }
+
+      // Normalize address
+      bufseg += (bufoff / 16);
+      bufoff %= 16;
+    
+      // Get the byte count
+      lcount =  ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
+
+      // adjust to read what we want
+      if(header>lcount) {
+         lbefore=lcount;
+         header-=lcount;
+         lcount=0;
+         }
+      else {
+        lbefore=header;
+        header=0;
+        lcount-=lbefore;
+        }
+
+      if(lcount>length) {
+        lafter=lcount-length;
+        lcount=length;
+        length=0;
+        }
+      else {
+        lafter=0;
+        length-=lcount;
+        }
+
+      // Save byte count
+      count = lcount;
+
+      BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
+      BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
+
+      // If counts not dividable by 4, use 16bits mode
+      lmode = mode;
+      if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
+      if (lcount  & 0x03) lmode=ATA_MODE_PIO16;
+      if (lafter  & 0x03) lmode=ATA_MODE_PIO16;
+
+      // adds an extra byte if count are odd. before is always even
+      if (lcount & 0x01) {
+        lcount+=1;
+        if ((lafter > 0) && (lafter & 0x01)) {
+          lafter-=1;
+          }
+        }
+
+      if (lmode == ATA_MODE_PIO32) {
+        lcount>>=2; lbefore>>=2; lafter>>=2;
+        }
+      else {
+        lcount>>=1; lbefore>>=1; lafter>>=1;
+        }
+
+       ;  // FIXME bcc bug
+
+ASM_START
+        push bp
+        mov  bp, sp
+
+        mov  dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
+
+        mov  cx, _ata_cmd_packet.lbefore + 2[bp] 
+        jcxz ata_packet_no_before
+
+        mov  ah, _ata_cmd_packet.lmode + 2[bp] 
+        cmp  ah, #ATA_MODE_PIO32
+        je   ata_packet_in_before_32
+
+ata_packet_in_before_16:
+        in   ax, dx
+        loop ata_packet_in_before_16
+        jmp  ata_packet_no_before
+
+ata_packet_in_before_32:
+        push eax
+ata_packet_in_before_32_loop:
+        in   eax, dx
+        loop ata_packet_in_before_32_loop
+        pop  eax
+
+ata_packet_no_before:
+        mov  cx, _ata_cmd_packet.lcount + 2[bp] 
+        jcxz ata_packet_after
+
+        mov  di, _ata_cmd_packet.bufoff + 2[bp]  
+        mov  ax, _ata_cmd_packet.bufseg + 2[bp] 
+        mov  es, ax
+
+        mov  ah, _ata_cmd_packet.lmode + 2[bp] 
+        cmp  ah, #ATA_MODE_PIO32
+        je   ata_packet_in_32
+
+ata_packet_in_16:
+        rep
+          insw ;; CX words transfered tp port(DX) to ES:[DI]
+        jmp ata_packet_after
+
+ata_packet_in_32:
+        rep
+          insd ;; CX dwords transfered to port(DX) to ES:[DI]
+
+ata_packet_after:
+        mov  cx, _ata_cmd_packet.lafter + 2[bp] 
+        jcxz ata_packet_done
+
+        mov  ah, _ata_cmd_packet.lmode + 2[bp] 
+        cmp  ah, #ATA_MODE_PIO32
+        je   ata_packet_in_after_32
+
+ata_packet_in_after_16:
+        in   ax, dx
+        loop ata_packet_in_after_16
+        jmp  ata_packet_done
+
+ata_packet_in_after_32:
+        push eax
+ata_packet_in_after_32_loop:
+        in   eax, dx
+        loop ata_packet_in_after_32_loop
+        pop  eax
+
+ata_packet_done:
+        pop  bp
+ASM_END
+
+      // Compute new buffer address
+      bufoff += count;
+
+      // Save transferred bytes count
+      transfer += count;
+      write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
+      }
+    }
+
+  // Final check, device must be ready
+  if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
+         != ATA_CB_STAT_RDY ) {
+    BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
+    return 4;
+    }
+
+  // Enable interrupts
+  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
+  return 0;
+}
+
+// ---------------------------------------------------------------------------
+// End of ATA/ATAPI Driver
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// Start of ATA/ATAPI generic functions
+// ---------------------------------------------------------------------------
+
+  Bit16u 
+atapi_get_sense(device)
+  Bit16u device;
+{
+  Bit8u  atacmd[12];
+  Bit8u  buffer[16];
+  Bit8u i;
+
+  memsetb(get_SS(),atacmd,0,12);
+
+  // Request SENSE 
+  atacmd[0]=0x03;    
+  atacmd[4]=0x20;    
+  if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
+    return 0x0002;
+
+  if ((buffer[0] & 0x7e) == 0x70) {
+    return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
+    }
+
+  return 0;
+}
+
+  Bit16u 
+atapi_is_ready(device)
+  Bit16u device;
+{
+  Bit8u  atacmd[12];
+  Bit8u  buffer[];
+
+  memsetb(get_SS(),atacmd,0,12);
+ 
+  // Test Unit Ready
+  if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
+    return 0x000f;
+
+  if (atapi_get_sense(device) !=0 ) {
+    memsetb(get_SS(),atacmd,0,12);
+
+    // try to send Test Unit Ready again
+    if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
+      return 0x000f;
+
+    return atapi_get_sense(device);
+    }
+  return 0;
+}
+
+  Bit16u 
+atapi_is_cdrom(device)
+  Bit8u device;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+  if (device >= BX_MAX_ATA_DEVICES)
+    return 0;
+
+  if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
+    return 0;
+
+  if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
+    return 0;
+
+  return 1;
+}
+
+// ---------------------------------------------------------------------------
+// End of ATA/ATAPI generic functions
+// ---------------------------------------------------------------------------
+
+#endif // BX_USE_ATADRV
+
+#if BX_ELTORITO_BOOT
+
+// ---------------------------------------------------------------------------
+// Start of El-Torito boot functions
+// ---------------------------------------------------------------------------
+
+  void
+cdemu_init()
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+  // the only important data is this one for now
+  write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
+}
+
+  Bit8u
+cdemu_isactive()
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+  return(read_byte(ebda_seg,&EbdaData->cdemu.active));
+}
+
+  Bit8u
+cdemu_emulated_drive()
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+  return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
+}
+
+static char isotag[6]="CD001";
+static char eltorito[24]="EL TORITO SPECIFICATION";
+//
+// Returns ah: emulated drive, al: error code
+//
+  Bit16u 
+cdrom_boot()
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit8u  atacmd[12], buffer[2048];
+  Bit32u lba;
+  Bit16u boot_segment, nbsectors, i, error;
+  Bit8u  device;
+
+  // Find out the first cdrom
+  for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
+    if (atapi_is_cdrom(device)) break;
+    }
+  
+  // if not found
+  if(device >= BX_MAX_ATA_DEVICES) return 2;
+
+  // Read the Boot Record Volume Descriptor
+  memsetb(get_SS(),atacmd,0,12);
+  atacmd[0]=0x28;                      // READ command
+  atacmd[7]=(0x01 & 0xff00) >> 8;      // Sectors
+  atacmd[8]=(0x01 & 0x00ff);           // Sectors
+  atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
+  atacmd[3]=(0x11 & 0x00ff0000) >> 16;
+  atacmd[4]=(0x11 & 0x0000ff00) >> 8;
+  atacmd[5]=(0x11 & 0x000000ff);
+  if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
+    return 3;
+
+  // Validity checks
+  if(buffer[0]!=0)return 4;
+  for(i=0;i<5;i++){
+    if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
+   }
+  for(i=0;i<23;i++)
+    if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
+  
+  // ok, now we calculate the Boot catalog address
+  lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
+
+  // And we read the Boot Catalog
+  memsetb(get_SS(),atacmd,0,12);
+  atacmd[0]=0x28;                      // READ command
+  atacmd[7]=(0x01 & 0xff00) >> 8;      // Sectors
+  atacmd[8]=(0x01 & 0x00ff);           // Sectors
+  atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
+  atacmd[3]=(lba & 0x00ff0000) >> 16;
+  atacmd[4]=(lba & 0x0000ff00) >> 8;
+  atacmd[5]=(lba & 0x000000ff);
+  if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
+    return 7;
+ 
+  // Validation entry
+  if(buffer[0x00]!=0x01)return 8;   // Header
+  if(buffer[0x01]!=0x00)return 9;   // Platform
+  if(buffer[0x1E]!=0x55)return 10;  // key 1
+  if(buffer[0x1F]!=0xAA)return 10;  // key 2
+
+  // Initial/Default Entry
+  if(buffer[0x20]!=0x88)return 11; // Bootable
+
+  write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
+  if(buffer[0x21]==0){
+    // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0. 
+    // Win2000 cd boot needs to know it booted from cd
+    write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
+    } 
+  else if(buffer[0x21]<4)
+    write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
+  else
+    write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
+
+  write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
+  write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
+
+  boot_segment=buffer[0x23]*0x100+buffer[0x22];
+  if(boot_segment==0x0000)boot_segment=0x07C0;
+
+  write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
+  write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
+  
+  nbsectors=buffer[0x27]*0x100+buffer[0x26];
+  write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
+
+  lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
+  write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
+
+  // And we read the image in memory
+  memsetb(get_SS(),atacmd,0,12);
+  atacmd[0]=0x28;                      // READ command
+  atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8;      // Sectors
+  atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff);           // Sectors
+  atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
+  atacmd[3]=(lba & 0x00ff0000) >> 16;
+  atacmd[4]=(lba & 0x0000ff00) >> 8;
+  atacmd[5]=(lba & 0x000000ff);
+  if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
+    return 12;
+
+  // Remember the media type
+  switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
+    case 0x01:  // 1.2M floppy
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
+      break;
+    case 0x02:  // 1.44M floppy
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
+      break;
+    case 0x03:  // 2.88M floppy
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
+      break;
+    case 0x04:  // Harddrive
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
+	      (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
+      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
+      break;
+   }
+
+  if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
+    // Increase bios installed hardware number of devices
+    if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
+      write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
+    else
+      write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
+   }
+
+  
+  // everything is ok, so from now on, the emulation is active
+  if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
+    write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
+
+  // return the boot drive + no error
+  return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
+}
+
+// ---------------------------------------------------------------------------
+// End of El-Torito boot functions
+// ---------------------------------------------------------------------------
+#endif // BX_ELTORITO_BOOT
+
+  void
+int14_function(regs, ds, iret_addr)
+  pusha_regs_t regs; // regs pushed from PUSHA instruction
+  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
+  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
+{
+  Bit16u addr,timer,val16;
+  Bit8u timeout;
+
+  ASM_START
+  sti
+  ASM_END
+
+  addr = read_word(0x0040, (regs.u.r16.dx << 1));
+  timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
+  if ((regs.u.r16.dx < 4) && (addr > 0)) {
+    switch (regs.u.r8.ah) {
+      case 0:
+        outb(addr+3, inb(addr+3) | 0x80);
+        if (regs.u.r8.al & 0xE0 == 0) {
+          outb(addr, 0x17);
+          outb(addr+1, 0x04);
+        } else {
+          val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
+          outb(addr, val16 & 0xFF);
+          outb(addr+1, val16 >> 8);
+        }
+        outb(addr+3, regs.u.r8.al & 0x1F);
+        regs.u.r8.ah = inb(addr+5);
+        regs.u.r8.al = inb(addr+6);
+        ClearCF(iret_addr.flags);
+        break;
+      case 1:
+        timer = read_word(0x0040, 0x006C);
+        while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
+          val16 = read_word(0x0040, 0x006C);
+          if (val16 != timer) {
+            timer = val16;
+            timeout--;
+            }
+          }
+        if (timeout) outb(addr, regs.u.r8.al);
+        regs.u.r8.ah = inb(addr+5);
+        if (!timeout) regs.u.r8.ah |= 0x80;
+        ClearCF(iret_addr.flags);
+        break;
+      case 2:
+        timer = read_word(0x0040, 0x006C);
+        while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
+          val16 = read_word(0x0040, 0x006C);
+          if (val16 != timer) {
+            timer = val16;
+            timeout--;
+            }
+          }
+        if (timeout) {
+          regs.u.r8.ah = 0;
+          regs.u.r8.al = inb(addr);
+        } else {
+          regs.u.r8.ah = inb(addr+5);
+          }
+        ClearCF(iret_addr.flags);
+        break;
+      case 3:
+        regs.u.r8.ah = inb(addr+5);
+        regs.u.r8.al = inb(addr+6);
+        ClearCF(iret_addr.flags);
+        break;
+      default:
+        SetCF(iret_addr.flags); // Unsupported
+      }
+  } else {
+    SetCF(iret_addr.flags); // Unsupported
+    }
+}
+
+  void
+int15_function(regs, ES, DS, FLAGS)
+  pusha_regs_t regs; // REGS pushed via pusha
+  Bit16u ES, DS, FLAGS;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  bx_bool prev_a20_enable;
+  Bit16u  base15_00;
+  Bit8u   base23_16;
+  Bit16u  ss;
+  Bit16u  CX,DX;
+
+  Bit16u bRegister;
+  Bit8u irqDisable;
+
+BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
+
+  switch (regs.u.r8.ah) {
+    case 0x24: /* A20 Control */
+      switch (regs.u.r8.al) {
+        case 0x00:
+          set_enable_a20(0);
+          CLEAR_CF();
+          regs.u.r8.ah = 0;
+          break;
+        case 0x01:
+          set_enable_a20(1);
+          CLEAR_CF();
+          regs.u.r8.ah = 0;
+          break;
+        case 0x02:
+          regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
+          CLEAR_CF();
+          regs.u.r8.ah = 0;
+          break;
+        case 0x03:
+          CLEAR_CF();
+          regs.u.r8.ah = 0;
+          regs.u.r16.bx = 3;
+          break;
+        default:
+          BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
+          SET_CF();
+          regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+      }
+      break;
+
+    case 0x41:
+      SET_CF();
+      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+      break;
+
+    case 0x4f:
+      /* keyboard intercept */
+#if BX_CPU < 2
+      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+#else
+      // nop
+#endif
+      SET_CF();
+      break;
+
+    case 0x52:    // removable media eject
+      CLEAR_CF();
+      regs.u.r8.ah = 0;  // "ok ejection may proceed"
+      break;
+
+    case 0x83: {
+      if( regs.u.r8.al == 0 ) {
+        // Set Interval requested.
+        if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
+          // Interval not already set.
+          write_byte( 0x40, 0xA0, 1 );  // Set status byte.
+          write_word( 0x40, 0x98, ES ); // Byte location, segment
+          write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
+          write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
+          write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
+          CLEAR_CF( );
+          irqDisable = inb( 0xA1 );
+          outb( 0xA1, irqDisable & 0xFE );
+          bRegister = inb_cmos( 0xB );  // Unmask IRQ8 so INT70 will get through.
+          outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
+        } else {
+          // Interval already set.
+          BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
+          SET_CF();
+          regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+        }
+      } else if( regs.u.r8.al == 1 ) {
+        // Clear Interval requested
+        write_byte( 0x40, 0xA0, 0 );  // Clear status byte
+        CLEAR_CF( );
+        bRegister = inb_cmos( 0xB );
+        outb_cmos( 0xB, bRegister & ~0x40 );  // Turn off the Periodic Interrupt timer
+      } else {
+        BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
+        SET_CF();
+        regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+        regs.u.r8.al--;
+      }
+
+      break;
+    }
+
+    case 0x87:
+#if BX_CPU < 3
+#  error "Int15 function 87h not supported on < 80386"
+#endif
+      // +++ should probably have descriptor checks
+      // +++ should have exception handlers
+
+ // turn off interrupts
+ASM_START
+  cli
+ASM_END
+
+      prev_a20_enable = set_enable_a20(1); // enable A20 line
+
+      // 128K max of transfer on 386+ ???
+      // source == destination ???
+
+      // ES:SI points to descriptor table
+      // offset   use     initially  comments
+      // ==============================================
+      // 00..07   Unused  zeros      Null descriptor
+      // 08..0f   GDT     zeros      filled in by BIOS
+      // 10..17   source  ssssssss   source of data
+      // 18..1f   dest    dddddddd   destination of data
+      // 20..27   CS      zeros      filled in by BIOS
+      // 28..2f   SS      zeros      filled in by BIOS
+
+      //es:si
+      //eeee0
+      //0ssss
+      //-----
+
+// check for access rights of source & dest here
+
+      // Initialize GDT descriptor
+      base15_00 = (ES << 4) + regs.u.r16.si;
+      base23_16 = ES >> 12;
+      if (base15_00 < (ES<<4))
+        base23_16++;
+      write_word(ES, regs.u.r16.si+0x08+0, 47);       // limit 15:00 = 6 * 8bytes/descriptor
+      write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
+      write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
+      write_byte(ES, regs.u.r16.si+0x08+5, 0x93);     // access
+      write_word(ES, regs.u.r16.si+0x08+6, 0x0000);   // base 31:24/reserved/limit 19:16
+
+      // Initialize CS descriptor
+      write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
+      write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
+      write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
+      write_byte(ES, regs.u.r16.si+0x20+5, 0x9b);  // access
+      write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
+
+      // Initialize SS descriptor
+      ss = get_SS();
+      base15_00 = ss << 4;
+      base23_16 = ss >> 12;
+      write_word(ES, regs.u.r16.si+0x28+0, 0xffff);   // limit 15:00 = normal 64K limit
+      write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
+      write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
+      write_byte(ES, regs.u.r16.si+0x28+5, 0x93);     // access
+      write_word(ES, regs.u.r16.si+0x28+6, 0x0000);   // base 31:24/reserved/limit 19:16
+
+      CX = regs.u.r16.cx;
+ASM_START
+      // Compile generates locals offset info relative to SP.
+      // Get CX (word count) from stack.
+      mov  bx, sp
+      SEG SS
+        mov  cx, _int15_function.CX [bx]
+
+      // since we need to set SS:SP, save them to the BDA
+      // for future restore
+      push eax
+      xor eax, eax
+      mov ds, ax
+      mov 0x0469, ss
+      mov 0x0467, sp
+
+      SEG ES
+        lgdt [si + 0x08]
+      SEG CS
+        lidt [pmode_IDT_info]
+      ;;  perhaps do something with IDT here
+
+      ;; set PE bit in CR0
+      mov  eax, cr0
+      or   al, #0x01
+      mov  cr0, eax
+      ;; far jump to flush CPU queue after transition to protected mode
+      JMP_AP(0x0020, protected_mode)
+
+protected_mode:
+      ;; GDT points to valid descriptor table, now load SS, DS, ES
+      mov  ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
+      mov  ss, ax
+      mov  ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
+      mov  ds, ax
+      mov  ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
+      mov  es, ax
+      xor  si, si
+      xor  di, di
+      cld
+      rep
+        movsw  ;; move CX words from DS:SI to ES:DI
+
+      ;; make sure DS and ES limits are 64KB
+      mov ax, #0x28
+      mov ds, ax
+      mov es, ax
+
+      ;; reset PG bit in CR0 ???
+      mov  eax, cr0
+      and  al, #0xFE
+      mov  cr0, eax
+
+      ;; far jump to flush CPU queue after transition to real mode
+      JMP_AP(0xf000, real_mode)
+
+real_mode:
+      ;; restore IDT to normal real-mode defaults
+      SEG CS
+        lidt [rmode_IDT_info]
+
+      // restore SS:SP from the BDA
+      xor ax, ax
+      mov ds, ax
+      mov ss, 0x0469
+      mov sp, 0x0467
+      pop eax
+ASM_END
+
+      set_enable_a20(prev_a20_enable);
+
+ // turn back on interrupts
+ASM_START
+  sti
+ASM_END
+
+      regs.u.r8.ah = 0;
+      CLEAR_CF();
+      break;
+
+
+    case 0x88:
+      // Get the amount of extended memory (above 1M)
+#if BX_CPU < 2
+      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+      SET_CF();
+#else
+      regs.u.r8.al = inb_cmos(0x30);
+      regs.u.r8.ah = inb_cmos(0x31);
+
+      // limit to 15M
+      if(regs.u.r16.ax > 0x3c00)
+        regs.u.r16.ax = 0x3c00;
+
+      CLEAR_CF();
+#endif
+      break;
+
+    case 0x90:
+      /* Device busy interrupt.  Called by Int 16h when no key available */
+      break;
+
+    case 0x91:
+      /* Interrupt complete.  Called by Int 16h when key becomes available */
+      break;
+
+    case 0xbf:
+      BX_INFO("*** int 15h function AH=bf not yet supported!\n");
+      SET_CF();
+      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+      break;
+
+    case 0xC0:
+#if 0
+      SET_CF();
+      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+      break;
+#endif
+      CLEAR_CF();
+      regs.u.r8.ah = 0;
+      regs.u.r16.bx =  BIOS_CONFIG_TABLE;
+      ES = 0xF000;
+      break;
+
+    case 0xc1:
+      ES = ebda_seg;
+      CLEAR_CF();
+      break;
+
+    case 0xd8:
+      bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
+      SET_CF();
+      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+      break;
+
+    default:
+      BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
+        (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
+      SET_CF();
+      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+      break;
+    }
+}
+
+#if BX_USE_PS2_MOUSE
+  void
+int15_function_mouse(regs, ES, DS, FLAGS)
+  pusha_regs_t regs; // REGS pushed via pusha
+  Bit16u ES, DS, FLAGS;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit8u  mouse_flags_1, mouse_flags_2;
+  Bit16u mouse_driver_seg;
+  Bit16u mouse_driver_offset;
+  Bit8u  comm_byte, prev_command_byte;
+  Bit8u  ret, mouse_data1, mouse_data2, mouse_data3;
+
+BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
+
+  switch (regs.u.r8.ah) {
+    case 0xC2:
+      // Return Codes status in AH
+      // =========================
+      // 00: success
+      // 01: invalid subfunction (AL > 7)
+      // 02: invalid input value (out of allowable range)
+      // 03: interface error
+      // 04: resend command received from mouse controller,
+      //     device driver should attempt command again
+      // 05: cannot enable mouse, since no far call has been installed
+      // 80/86: mouse service not implemented
+
+      switch (regs.u.r8.al) {
+        case 0: // Disable/Enable Mouse
+BX_DEBUG_INT15("case 0:\n");
+          switch (regs.u.r8.bh) {
+            case 0: // Disable Mouse
+BX_DEBUG_INT15("case 0: disable mouse\n");
+              inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+              ret = send_to_mouse_ctrl(0xF5); // disable mouse command
+              if (ret == 0) {
+                ret = get_mouse_data(&mouse_data1);
+                if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
+                  CLEAR_CF();
+                  regs.u.r8.ah = 0;
+                  return;
+                  }
+                }
+
+              // error
+              SET_CF();
+              regs.u.r8.ah = ret;
+              return;
+              break;
+
+            case 1: // Enable Mouse
+BX_DEBUG_INT15("case 1: enable mouse\n");
+              mouse_flags_2 = read_byte(ebda_seg, 0x0027);
+              if ( (mouse_flags_2 & 0x80) == 0 ) {
+                BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
+                SET_CF();  // error
+                regs.u.r8.ah = 5; // no far call installed
+                return;
+                }
+              inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+              ret = send_to_mouse_ctrl(0xF4); // enable mouse command
+              if (ret == 0) {
+                ret = get_mouse_data(&mouse_data1);
+                if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
+                  enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
+                  CLEAR_CF();
+                  regs.u.r8.ah = 0;
+                  return;
+                  }
+                }
+              SET_CF();
+              regs.u.r8.ah = ret;
+              return;
+
+            default: // invalid subfunction
+              BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
+              SET_CF();  // error
+              regs.u.r8.ah = 1; // invalid subfunction
+              return;
+            }
+          break;
+
+        case 1: // Reset Mouse
+        case 5: // Initialize Mouse
+BX_DEBUG_INT15("case 1 or 5:\n");
+          if (regs.u.r8.al == 5) {
+            if (regs.u.r8.bh != 3) {
+              SET_CF();
+              regs.u.r8.ah = 0x02; // invalid input
+              return;
+            }
+            mouse_flags_2 = read_byte(ebda_seg, 0x0027);
+            mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
+            mouse_flags_1 = 0x00;
+            write_byte(ebda_seg, 0x0026, mouse_flags_1);
+            write_byte(ebda_seg, 0x0027, mouse_flags_2);
+          }
+
+          inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+          ret = send_to_mouse_ctrl(0xFF); // reset mouse command
+          if (ret == 0) {
+            ret = get_mouse_data(&mouse_data3);
+            // if no mouse attached, it will return RESEND
+            if (mouse_data3 == 0xfe) {
+              SET_CF();
+              return;
+            }
+            if (mouse_data3 != 0xfa)
+              BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
+            if ( ret == 0 ) {
+              ret = get_mouse_data(&mouse_data1);
+              if ( ret == 0 ) {
+                ret = get_mouse_data(&mouse_data2);
+                if ( ret == 0 ) {
+                  // turn IRQ12 and packet generation on
+                  enable_mouse_int_and_events();
+                  CLEAR_CF();
+                  regs.u.r8.ah = 0;
+                  regs.u.r8.bl = mouse_data1;
+                  regs.u.r8.bh = mouse_data2;
+                  return;
+                  }
+                }
+              }
+            }
+
+          // error
+          SET_CF();
+          regs.u.r8.ah = ret;
+          return;
+
+        case 2: // Set Sample Rate
+BX_DEBUG_INT15("case 2:\n");
+          switch (regs.u.r8.bh) {
+            case 0: mouse_data1 = 10; break; //  10 reports/sec
+            case 1: mouse_data1 = 20; break; //  20 reports/sec
+            case 2: mouse_data1 = 40; break; //  40 reports/sec
+            case 3: mouse_data1 = 60; break; //  60 reports/sec
+            case 4: mouse_data1 = 80; break; //  80 reports/sec
+            case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
+            case 6: mouse_data1 = 200; break; // 200 reports/sec
+            default: mouse_data1 = 0;
+          }
+          if (mouse_data1 > 0) {
+            ret = send_to_mouse_ctrl(0xF3); // set sample rate command
+            if (ret == 0) {
+              ret = get_mouse_data(&mouse_data2);
+              ret = send_to_mouse_ctrl(mouse_data1);
+              ret = get_mouse_data(&mouse_data2);
+              CLEAR_CF();
+              regs.u.r8.ah = 0;
+            } else {
+              // error
+              SET_CF();
+              regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+            }
+          } else {
+            // error
+            SET_CF();
+            regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+          }
+          break;
+
+        case 3: // Set Resolution
+BX_DEBUG_INT15("case 3:\n");
+          // BX:
+          //      0 =  25 dpi, 1 count  per millimeter
+          //      1 =  50 dpi, 2 counts per millimeter
+          //      2 = 100 dpi, 4 counts per millimeter
+          //      3 = 200 dpi, 8 counts per millimeter
+          CLEAR_CF();
+          regs.u.r8.ah = 0;
+          break;
+
+        case 4: // Get Device ID
+BX_DEBUG_INT15("case 4:\n");
+          inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+          ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
+          if (ret == 0) {
+            ret = get_mouse_data(&mouse_data1);
+            ret = get_mouse_data(&mouse_data2);
+            CLEAR_CF();
+            regs.u.r8.ah = 0;
+            regs.u.r8.bh = mouse_data2;
+          } else {
+            // error
+            SET_CF();
+            regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+          }
+          break;
+
+        case 6: // Return Status & Set Scaling Factor...
+BX_DEBUG_INT15("case 6:\n");
+          switch (regs.u.r8.bh) {
+            case 0: // Return Status
+              comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+              ret = send_to_mouse_ctrl(0xE9); // get mouse info command
+              if (ret == 0) {
+                ret = get_mouse_data(&mouse_data1);
+                if (mouse_data1 != 0xfa)
+                  BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
+                if (ret == 0) {
+                  ret = get_mouse_data(&mouse_data1);
+                  if ( ret == 0 ) {
+                    ret = get_mouse_data(&mouse_data2);
+                    if ( ret == 0 ) {
+                      ret = get_mouse_data(&mouse_data3);
+                      if ( ret == 0 ) {
+                        CLEAR_CF();
+                        regs.u.r8.ah = 0;
+                        regs.u.r8.bl = mouse_data1;
+                        regs.u.r8.cl = mouse_data2;
+                        regs.u.r8.dl = mouse_data3;
+                        set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
+                        return;
+                        }
+                      }
+                    }
+                  }
+                }
+
+              // error
+              SET_CF();
+              regs.u.r8.ah = ret;
+              set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
+              return;
+
+            case 1: // Set Scaling Factor to 1:1
+            case 2: // Set Scaling Factor to 2:1
+              comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+              if (regs.u.r8.bh == 1) {
+                ret = send_to_mouse_ctrl(0xE6);
+              } else {
+                ret = send_to_mouse_ctrl(0xE7);
+              }
+              if (ret == 0) {
+                get_mouse_data(&mouse_data1);
+                ret = (mouse_data1 != 0xFA);
+              }
+              if (ret == 0) {
+                CLEAR_CF();
+                regs.u.r8.ah = 0;
+              } else {
+                // error
+                SET_CF();
+                regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+              }
+              set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
+              break;
+
+            default:
+              BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
+            }
+          break;
+
+        case 7: // Set Mouse Handler Address
+BX_DEBUG_INT15("case 7:\n");
+          mouse_driver_seg = ES;
+          mouse_driver_offset = regs.u.r16.bx;
+          write_word(ebda_seg, 0x0022, mouse_driver_offset);
+          write_word(ebda_seg, 0x0024, mouse_driver_seg);
+          mouse_flags_2 = read_byte(ebda_seg, 0x0027);
+          if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
+            /* remove handler */
+            if ( (mouse_flags_2 & 0x80) != 0 ) {
+              mouse_flags_2 &= ~0x80;
+              inhibit_mouse_int_and_events(); // disable IRQ12 and packets
+              }
+            }
+          else {
+            /* install handler */
+            mouse_flags_2 |= 0x80;
+            }
+          write_byte(ebda_seg, 0x0027, mouse_flags_2);
+          CLEAR_CF();
+          regs.u.r8.ah = 0;
+          break;
+
+        default:
+BX_DEBUG_INT15("case default:\n");
+          regs.u.r8.ah = 1; // invalid function
+          SET_CF();
+        }
+      break;
+
+    default:
+      BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
+        (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
+      SET_CF();
+      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+      break;
+    }
+}
+#endif
+
+
+void set_e820_range(ES, DI, start, end, type)
+     Bit16u ES; 
+     Bit16u DI;
+     Bit32u start;
+     Bit32u end; 
+     Bit16u type;
+{
+    write_word(ES, DI, start);
+    write_word(ES, DI+2, start >> 16);
+    write_word(ES, DI+4, 0x00);
+    write_word(ES, DI+6, 0x00);
+    
+    end -= start;
+    write_word(ES, DI+8, end);
+    write_word(ES, DI+10, end >> 16);
+    write_word(ES, DI+12, 0x0000);
+    write_word(ES, DI+14, 0x0000);
+    
+    write_word(ES, DI+16, type);
+    write_word(ES, DI+18, 0x0);
+}
+
+  void
+int15_function32(regs, ES, DS, FLAGS)
+  pushad_regs_t regs; // REGS pushed via pushad
+  Bit16u ES, DS, FLAGS;
+{
+  Bit32u  extended_memory_size=0; // 64bits long
+  Bit16u  CX,DX;
+
+BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
+
+  switch (regs.u.r8.ah) {
+    case 0x86:
+      // Wait for CX:DX microseconds. currently using the 
+      // refresh request port 0x61 bit4, toggling every 15usec 
+
+      CX = regs.u.r16.cx;
+      DX = regs.u.r16.dx;
+
+ASM_START
+      sti
+
+      ;; Get the count in eax
+      mov  bx, sp
+      SEG SS
+        mov  ax, _int15_function.CX [bx]
+      shl  eax, #16
+      SEG SS
+        mov  ax, _int15_function.DX [bx]
+
+      ;; convert to numbers of 15usec ticks
+      mov ebx, #15
+      xor edx, edx
+      div eax, ebx
+      mov ecx, eax
+
+      ;; wait for ecx number of refresh requests
+      in al, #0x61
+      and al,#0x10
+      mov ah, al
+
+      or ecx, ecx
+      je int1586_tick_end
+int1586_tick:
+      in al, #0x61
+      and al,#0x10
+      cmp al, ah
+      je  int1586_tick
+      mov ah, al
+      dec ecx
+      jnz int1586_tick
+int1586_tick_end:
+ASM_END
+
+      break;
+
+    case 0xe8:
+        switch(regs.u.r8.al)
+        {
+         case 0x20: // coded by osmaker aka K.J.
+            if(regs.u.r32.edx == 0x534D4150)
+            {
+                switch(regs.u.r16.bx)
+                {
+                    case 0:
+                        set_e820_range(ES, regs.u.r16.di, 
+                                       0x0000000L, 0x0009fc00L, 1);
+                        regs.u.r32.ebx = 1;
+                        regs.u.r32.eax = 0x534D4150;
+                        regs.u.r32.ecx = 0x14;
+                        CLEAR_CF();
+                        return;
+                        break;
+                    case 1:
+                        set_e820_range(ES, regs.u.r16.di, 
+                                       0x0009fc00L, 0x000a0000L, 2);
+                        regs.u.r32.ebx = 2;
+                        regs.u.r32.eax = 0x534D4150;
+                        regs.u.r32.ecx = 0x14;
+                        CLEAR_CF();
+                        return;
+                        break;
+                    case 2:
+                        set_e820_range(ES, regs.u.r16.di, 
+                                       0x000e8000L, 0x00100000L, 2);
+                        regs.u.r32.ebx = 3;
+                        regs.u.r32.eax = 0x534D4150;
+                        regs.u.r32.ecx = 0x14;
+                        CLEAR_CF();
+                        return;
+                        break;
+                    case 3:
+                        extended_memory_size = inb_cmos(0x35);
+                        extended_memory_size <<= 8;
+                        extended_memory_size |= inb_cmos(0x34);
+                        extended_memory_size *= 64;
+                        if(extended_memory_size > 0x3bc000) // greater than EFF00000???
+                        {
+                            extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
+                        }
+                        extended_memory_size *= 1024;
+                        extended_memory_size += (16L * 1024 * 1024);
+
+                        if(extended_memory_size <= (16L * 1024 * 1024))
+                        {
+                            extended_memory_size = inb_cmos(0x31);
+                            extended_memory_size <<= 8;
+                            extended_memory_size |= inb_cmos(0x30);
+                            extended_memory_size *= 1024;
+                        }
+
+                        set_e820_range(ES, regs.u.r16.di, 
+                                       0x00100000L, extended_memory_size, 1);
+                        regs.u.r32.ebx = 4;
+                        regs.u.r32.eax = 0x534D4150;
+                        regs.u.r32.ecx = 0x14;
+                        CLEAR_CF();
+                        return;
+                        break;
+                    case 4:
+                        /* 256KB BIOS area at the end of 4 GB */
+                        set_e820_range(ES, regs.u.r16.di, 
+                                       0xfffc0000L, 0x00000000L, 2);
+                        regs.u.r32.ebx = 0;
+                        regs.u.r32.eax = 0x534D4150;
+                        regs.u.r32.ecx = 0x14;
+                        CLEAR_CF();
+                        return;
+                    default:  /* AX=E820, DX=534D4150, BX unrecognized */
+                        goto int15_unimplemented;
+                        break;
+                }
+	    } else {
+	      // if DX != 0x534D4150)
+	      goto int15_unimplemented;
+	    }
+            break;
+
+        case 0x01: 
+          // do we have any reason to fail here ?
+          CLEAR_CF();
+
+          // my real system sets ax and bx to 0
+          // this is confirmed by Ralph Brown list
+          // but syslinux v1.48 is known to behave 
+          // strangely if ax is set to 0
+          // regs.u.r16.ax = 0;
+          // regs.u.r16.bx = 0;
+
+          // Get the amount of extended memory (above 1M)
+          regs.u.r8.cl = inb_cmos(0x30);
+          regs.u.r8.ch = inb_cmos(0x31);
+          
+          // limit to 15M
+          if(regs.u.r16.cx > 0x3c00)
+          {
+            regs.u.r16.cx = 0x3c00;
+          }
+
+          // Get the amount of extended memory above 16M in 64k blocs
+          regs.u.r8.dl = inb_cmos(0x34);
+          regs.u.r8.dh = inb_cmos(0x35);
+
+          // Set configured memory equal to extended memory
+          regs.u.r16.ax = regs.u.r16.cx;
+          regs.u.r16.bx = regs.u.r16.dx;
+          break;
+	default:  /* AH=0xE8?? but not implemented */
+	  goto int15_unimplemented;
+       }
+       break;
+    int15_unimplemented:
+       // fall into the default
+    default:
+      BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
+        (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
+      SET_CF();
+      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
+      break;
+    }
+}
+
+  void
+int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
+  Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
+{
+  Bit8u scan_code, ascii_code, shift_flags, count;
+  Bit16u kbd_code, max;
+
+  BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
+
+  switch (GET_AH()) {
+    case 0x00: /* read keyboard input */
+
+      if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
+        BX_PANIC("KBD: int16h: out of keyboard input\n");
+        }
+      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
+      else if (ascii_code == 0xE0) ascii_code = 0;
+      AX = (scan_code << 8) | ascii_code;
+      break;
+
+    case 0x01: /* check keyboard status */
+      if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
+        SET_ZF();
+        return;
+        }
+      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
+      else if (ascii_code == 0xE0) ascii_code = 0;
+      AX = (scan_code << 8) | ascii_code;
+      CLEAR_ZF();
+      break;
+
+    case 0x02: /* get shift flag status */
+      shift_flags = read_byte(0x0040, 0x17);
+      SET_AL(shift_flags);
+      break;
+
+    case 0x05: /* store key-stroke into buffer */
+      if ( !enqueue_key(GET_CH(), GET_CL()) ) {
+        SET_AL(1);
+        }
+      else {
+        SET_AL(0);
+        }
+      break;
+
+    case 0x09: /* GET KEYBOARD FUNCTIONALITY */
+      // bit Bochs Description     
+      //  7    0   reserved
+      //  6    0   INT 16/AH=20h-22h supported (122-key keyboard support)
+      //  5    1   INT 16/AH=10h-12h supported (enhanced keyboard support)
+      //  4    1   INT 16/AH=0Ah supported
+      //  3    0   INT 16/AX=0306h supported
+      //  2    0   INT 16/AX=0305h supported
+      //  1    0   INT 16/AX=0304h supported
+      //  0    0   INT 16/AX=0300h supported
+      //
+      SET_AL(0x30);
+      break;
+
+    case 0x0A: /* GET KEYBOARD ID */
+      count = 2;
+      kbd_code = 0x0;
+      outb(0x60, 0xf2);
+      /* Wait for data */
+      max=0xffff;
+      while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
+      if (max>0x0) {
+        if ((inb(0x60) == 0xfa)) {
+          do {
+            max=0xffff;
+            while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
+            if (max>0x0) {
+              kbd_code >>= 8;
+              kbd_code |= (inb(0x60) << 8);
+            }
+          } while (--count>0);
+	}
+      }
+      BX=kbd_code;
+      break;
+
+    case 0x10: /* read MF-II keyboard input */
+
+      if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
+        BX_PANIC("KBD: int16h: out of keyboard input\n");
+        }
+      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
+      AX = (scan_code << 8) | ascii_code;
+      break;
+
+    case 0x11: /* check MF-II keyboard status */
+      if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
+        SET_ZF();
+        return;
+        }
+      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
+      AX = (scan_code << 8) | ascii_code;
+      CLEAR_ZF();
+      break;
+
+    case 0x12: /* get extended keyboard status */
+      shift_flags = read_byte(0x0040, 0x17);
+      SET_AL(shift_flags);
+      shift_flags = read_byte(0x0040, 0x18) & 0x73;
+      shift_flags |= read_byte(0x0040, 0x96) & 0x0c;
+      SET_AH(shift_flags);
+      BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
+      break;
+
+    case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
+      SET_AH(0x80); // function int16 ah=0x10-0x12 supported
+      break;
+
+    case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
+      // don't change AH : function int16 ah=0x20-0x22 NOT supported
+      break;
+
+    case 0x6F:
+      if (GET_AL() == 0x08)
+	SET_AH(0x02); // unsupported, aka normal keyboard
+
+    default:
+      BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
+    }
+}
+
+  unsigned int
+dequeue_key(scan_code, ascii_code, incr)
+  Bit8u *scan_code;
+  Bit8u *ascii_code;
+  unsigned int incr;
+{
+  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
+  Bit16u ss;
+  Bit8u  acode, scode;
+
+#if BX_CPU < 2
+  buffer_start = 0x001E;
+  buffer_end   = 0x003E;
+#else
+  buffer_start = read_word(0x0040, 0x0080);
+  buffer_end   = read_word(0x0040, 0x0082);
+#endif
+
+  buffer_head = read_word(0x0040, 0x001a);
+  buffer_tail = read_word(0x0040, 0x001c);
+
+  if (buffer_head != buffer_tail) {
+    ss = get_SS();
+    acode = read_byte(0x0040, buffer_head);
+    scode = read_byte(0x0040, buffer_head+1);
+    write_byte(ss, ascii_code, acode);
+    write_byte(ss, scan_code, scode);
+
+    if (incr) {
+      buffer_head += 2;
+      if (buffer_head >= buffer_end)
+        buffer_head = buffer_start;
+      write_word(0x0040, 0x001a, buffer_head);
+      }
+    return(1);
+    }
+  else {
+    return(0);
+    }
+}
+
+static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
+
+  Bit8u
+inhibit_mouse_int_and_events()
+{
+  Bit8u command_byte, prev_command_byte;
+
+  // Turn off IRQ generation and aux data line
+  if ( inb(0x64) & 0x02 )
+    BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
+  outb(0x64, 0x20); // get command byte
+  while ( (inb(0x64) & 0x01) != 0x01 );
+  prev_command_byte = inb(0x60);
+  command_byte = prev_command_byte;
+  //while ( (inb(0x64) & 0x02) );
+  if ( inb(0x64) & 0x02 )
+    BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
+  command_byte &= 0xfd; // turn off IRQ 12 generation
+  command_byte |= 0x20; // disable mouse serial clock line
+  outb(0x64, 0x60); // write command byte
+  outb(0x60, command_byte);
+  return(prev_command_byte);
+}
+
+  void
+enable_mouse_int_and_events()
+{
+  Bit8u command_byte;
+
+  // Turn on IRQ generation and aux data line
+  if ( inb(0x64) & 0x02 )
+    BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
+  outb(0x64, 0x20); // get command byte
+  while ( (inb(0x64) & 0x01) != 0x01 );
+  command_byte = inb(0x60);
+  //while ( (inb(0x64) & 0x02) );
+  if ( inb(0x64) & 0x02 )
+    BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
+  command_byte |= 0x02; // turn on IRQ 12 generation
+  command_byte &= 0xdf; // enable mouse serial clock line
+  outb(0x64, 0x60); // write command byte
+  outb(0x60, command_byte);
+}
+
+  Bit8u
+send_to_mouse_ctrl(sendbyte)
+  Bit8u sendbyte;
+{
+  Bit8u response;
+
+  // wait for chance to write to ctrl
+  if ( inb(0x64) & 0x02 )
+    BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
+  outb(0x64, 0xD4);
+  outb(0x60, sendbyte);
+  return(0);
+}
+
+
+  Bit8u
+get_mouse_data(data)
+  Bit8u *data;
+{
+  Bit8u response;
+  Bit16u ss;
+
+  while ( (inb(0x64) & 0x21) != 0x21 ) {
+    }
+
+  response = inb(0x60);
+
+  ss = get_SS();
+  write_byte(ss, data, response);
+  return(0);
+}
+
+  void
+set_kbd_command_byte(command_byte)
+  Bit8u command_byte;
+{
+  if ( inb(0x64) & 0x02 )
+    BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
+  outb(0x64, 0xD4);
+
+  outb(0x64, 0x60); // write command byte
+  outb(0x60, command_byte);
+}
+
+  void
+int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
+  Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
+{
+  Bit8u scancode, asciicode, shift_flags;
+  Bit8u mf2_flags, mf2_state, led_flags;
+
+  //
+  // DS has been set to F000 before call
+  //
+
+
+  scancode = GET_AL();
+
+  if (scancode == 0) {
+    BX_INFO("KBD: int09 handler: AL=0\n");
+    return;
+    }
+
+
+  shift_flags = read_byte(0x0040, 0x17);
+  mf2_flags = read_byte(0x0040, 0x18);
+  mf2_state = read_byte(0x0040, 0x96);
+  led_flags = read_byte(0x0040, 0x97);
+  asciicode = 0;
+
+  switch (scancode) {
+    case 0x3a: /* Caps Lock press */
+      shift_flags ^= 0x40;
+      write_byte(0x0040, 0x17, shift_flags);
+      mf2_flags |= 0x40;
+      led_flags ^= 0x04;
+      write_byte(0x0040, 0x18, mf2_flags);
+      write_byte(0x0040, 0x97, led_flags);
+      break;
+    case 0xba: /* Caps Lock release */
+      mf2_flags &= ~0x40;
+      write_byte(0x0040, 0x18, mf2_flags);
+      break;
+
+    case 0x2a: /* L Shift press */
+      shift_flags |= 0x02;
+      write_byte(0x0040, 0x17, shift_flags);
+      break;
+    case 0xaa: /* L Shift release */
+      shift_flags &= ~0x02;
+      write_byte(0x0040, 0x17, shift_flags);
+      break;
+
+    case 0x36: /* R Shift press */
+      shift_flags |= 0x01;
+      write_byte(0x0040, 0x17, shift_flags);
+      break;
+    case 0xb6: /* R Shift release */
+      shift_flags &= ~0x01;
+      write_byte(0x0040, 0x17, shift_flags);
+      break;
+
+    case 0x1d: /* Ctrl press */
+      if ((mf2_state & 0x01) == 0) {
+        shift_flags |= 0x04;
+        write_byte(0x0040, 0x17, shift_flags);
+        if (mf2_state & 0x02) {
+          mf2_state |= 0x04;
+          write_byte(0x0040, 0x96, mf2_state);
+        } else {
+          mf2_flags |= 0x01;
+          write_byte(0x0040, 0x18, mf2_flags);
+        }
+      }
+      break;
+    case 0x9d: /* Ctrl release */
+      if ((mf2_state & 0x01) == 0) {
+        shift_flags &= ~0x04;
+        write_byte(0x0040, 0x17, shift_flags);
+        if (mf2_state & 0x02) {
+          mf2_state &= ~0x04;
+          write_byte(0x0040, 0x96, mf2_state);
+        } else {
+          mf2_flags &= ~0x01;
+          write_byte(0x0040, 0x18, mf2_flags);
+        }
+      }
+      break;
+
+    case 0x38: /* Alt press */
+      shift_flags |= 0x08;
+      write_byte(0x0040, 0x17, shift_flags);
+      if (mf2_state & 0x02) {
+        mf2_state |= 0x08;
+        write_byte(0x0040, 0x96, mf2_state);
+      } else {
+        mf2_flags |= 0x02;
+        write_byte(0x0040, 0x18, mf2_flags);
+      }
+      break;
+    case 0xb8: /* Alt release */
+      shift_flags &= ~0x08;
+      write_byte(0x0040, 0x17, shift_flags);
+      if (mf2_state & 0x02) {
+        mf2_state &= ~0x08;
+        write_byte(0x0040, 0x96, mf2_state);
+      } else {
+        mf2_flags &= ~0x02;
+        write_byte(0x0040, 0x18, mf2_flags);
+      }
+      break;
+
+    case 0x45: /* Num Lock press */
+      if ((mf2_state & 0x03) == 0) {
+        mf2_flags |= 0x20;
+        write_byte(0x0040, 0x18, mf2_flags);
+        shift_flags ^= 0x20;
+        led_flags ^= 0x02;
+        write_byte(0x0040, 0x17, shift_flags);
+        write_byte(0x0040, 0x97, led_flags);
+      }
+      break;
+    case 0xc5: /* Num Lock release */
+      if ((mf2_state & 0x03) == 0) {
+        mf2_flags &= ~0x20;
+        write_byte(0x0040, 0x18, mf2_flags);
+      }
+      break;
+
+    case 0x46: /* Scroll Lock press */
+      mf2_flags |= 0x10;
+      write_byte(0x0040, 0x18, mf2_flags);
+      shift_flags ^= 0x10;
+      led_flags ^= 0x01;
+      write_byte(0x0040, 0x17, shift_flags);
+      write_byte(0x0040, 0x97, led_flags);
+      break;
+
+    case 0xc6: /* Scroll Lock release */
+      mf2_flags &= ~0x10;
+      write_byte(0x0040, 0x18, mf2_flags);
+      break;
+
+    default:
+      if (scancode & 0x80) {
+        break; /* toss key releases ... */
+      }
+      if (scancode > MAX_SCAN_CODE) {
+        BX_INFO("KBD: int09h_handler(): unknown scancode read: 0x%02x!\n", scancode);
+        return;
+      }
+      if (shift_flags & 0x08) { /* ALT */
+        asciicode = scan_to_scanascii[scancode].alt;
+        scancode = scan_to_scanascii[scancode].alt >> 8;
+      } else if (shift_flags & 0x04) { /* CONTROL */
+        asciicode = scan_to_scanascii[scancode].control;
+        scancode = scan_to_scanascii[scancode].control >> 8;
+      } else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
+        /* check if lock state should be ignored 
+         * because a SHIFT key are pressed */
+
+        if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
+          asciicode = scan_to_scanascii[scancode].normal;
+          scancode = scan_to_scanascii[scancode].normal >> 8;
+        } else {
+          asciicode = scan_to_scanascii[scancode].shift;
+          scancode = scan_to_scanascii[scancode].shift >> 8;
+        }
+      } else {
+        /* check if lock is on */
+        if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
+          asciicode = scan_to_scanascii[scancode].shift;
+          scancode = scan_to_scanascii[scancode].shift >> 8;
+        } else {
+          asciicode = scan_to_scanascii[scancode].normal;
+          scancode = scan_to_scanascii[scancode].normal >> 8;
+        }
+      }
+      if (scancode==0 && asciicode==0) {
+        BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
+      }
+      enqueue_key(scancode, asciicode);
+      break;
+  }
+  if ((scancode & 0x7f) != 0x1d) {
+    mf2_state &= ~0x01;
+  }
+  mf2_state &= ~0x02;
+  write_byte(0x0040, 0x96, mf2_state);
+}
+
+  unsigned int
+enqueue_key(scan_code, ascii_code)
+  Bit8u scan_code, ascii_code;
+{
+  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
+
+#if BX_CPU < 2
+  buffer_start = 0x001E;
+  buffer_end   = 0x003E;
+#else
+  buffer_start = read_word(0x0040, 0x0080);
+  buffer_end   = read_word(0x0040, 0x0082);
+#endif
+
+  buffer_head = read_word(0x0040, 0x001A);
+  buffer_tail = read_word(0x0040, 0x001C);
+
+  temp_tail = buffer_tail;
+  buffer_tail += 2;
+  if (buffer_tail >= buffer_end)
+    buffer_tail = buffer_start;
+
+  if (buffer_tail == buffer_head) {
+    return(0);
+    }
+
+   write_byte(0x0040, temp_tail, ascii_code);
+   write_byte(0x0040, temp_tail+1, scan_code);
+   write_word(0x0040, 0x001C, buffer_tail);
+   return(1);
+}
+
+
+  void
+int74_function(make_farcall, Z, Y, X, status)
+  Bit16u make_farcall, Z, Y, X, status;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit8u  in_byte, index, package_count;
+  Bit8u  mouse_flags_1, mouse_flags_2;
+
+BX_DEBUG_INT74("entering int74_function\n");
+  make_farcall = 0;
+
+  in_byte = inb(0x64);
+  if ( (in_byte & 0x21) != 0x21 ) {
+    return;
+    }
+  in_byte = inb(0x60);
+BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
+
+  mouse_flags_1 = read_byte(ebda_seg, 0x0026);
+  mouse_flags_2 = read_byte(ebda_seg, 0x0027);
+
+  if ( (mouse_flags_2 & 0x80) != 0x80 ) {
+      return;
+  }
+
+  package_count = mouse_flags_2 & 0x07;
+  index = mouse_flags_1 & 0x07;
+  write_byte(ebda_seg, 0x28 + index, in_byte);
+
+  if ( (index+1) >= package_count ) {
+BX_DEBUG_INT74("int74_function: make_farcall=1\n");
+    status = read_byte(ebda_seg, 0x0028 + 0);
+    X      = read_byte(ebda_seg, 0x0028 + 1);
+    Y      = read_byte(ebda_seg, 0x0028 + 2);
+    Z      = 0;
+    mouse_flags_1 = 0;
+    // check if far call handler installed
+    if (mouse_flags_2 & 0x80)
+      make_farcall = 1;
+    }
+  else {
+    mouse_flags_1++;
+    }
+  write_byte(ebda_seg, 0x0026, mouse_flags_1);
+}
+
+#define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
+
+#if BX_USE_ATADRV
+
+  void
+int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
+  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+  Bit32u lba;
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit16u cylinder, head, sector;
+  Bit16u segment, offset;
+  Bit16u npc, nph, npspt, nlc, nlh, nlspt;
+  Bit16u size, count;
+  Bit8u  device, status;
+
+  BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+
+  write_byte(0x0040, 0x008e, 0);  // clear completion flag
+
+  // basic check : device has to be defined
+  if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
+    BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
+    goto int13_fail;
+    }
+
+  // Get the ata channel
+  device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
+
+  // basic check : device has to be valid 
+  if (device >= BX_MAX_ATA_DEVICES) {
+    BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
+    goto int13_fail;
+    }
+  
+  switch (GET_AH()) {
+
+    case 0x00: /* disk controller reset */
+      ata_reset (device);
+      goto int13_success;
+      break;
+
+    case 0x01: /* read disk status */
+      status = read_byte(0x0040, 0x0074);
+      SET_AH(status);
+      SET_DISK_RET_STATUS(0);
+      /* set CF if error status read */
+      if (status) goto int13_fail_nostatus;
+      else        goto int13_success_noah;
+      break;
+
+    case 0x02: // read disk sectors
+    case 0x03: // write disk sectors 
+    case 0x04: // verify disk sectors
+
+      count       = GET_AL();
+      cylinder    = GET_CH();
+      cylinder   |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
+      sector      = (GET_CL() & 0x3f);
+      head        = GET_DH();
+
+      segment = ES;
+      offset  = BX;
+
+      if ( (count > 128) || (count == 0) ) {
+        BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
+        goto int13_fail;
+        }
+
+      nlc   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
+      nlh   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
+      nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
+
+      // sanity check on cyl heads, sec
+      if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
+        BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
+        goto int13_fail;
+        }
+      
+      // FIXME verify
+      if ( GET_AH() == 0x04 ) goto int13_success;
+
+      nph   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
+      npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
+
+      // if needed, translate lchs to lba, and execute command
+      if ( (nph != nlh) || (npspt != nlspt)) {
+        lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
+        sector = 0; // this forces the command to be lba
+        }
+
+      if ( GET_AH() == 0x02 )
+        status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
+      else
+        status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
+
+      // Set nb of sector transferred
+      SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
+
+      if (status != 0) {
+        BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
+        SET_AH(0x0c);
+        goto int13_fail_noah;
+        }
+
+      goto int13_success;
+      break;
+
+    case 0x05: /* format disk track */
+      BX_INFO("format disk track called\n");
+      goto int13_success;
+      return;
+      break;
+
+    case 0x08: /* read disk drive parameters */
+      
+      // Get logical geometry from table
+      nlc   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
+      nlh   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
+      nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
+      count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
+
+      nlc = nlc - 2; /* 0 based , last sector not used */
+      SET_AL(0);
+      SET_CH(nlc & 0xff);
+      SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
+      SET_DH(nlh - 1);
+      SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
+
+      // FIXME should set ES & DI
+      
+      goto int13_success;
+      break;
+
+    case 0x10: /* check drive ready */
+      // should look at 40:8E also???
+      
+      // Read the status from controller
+      status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
+      if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
+        goto int13_success;
+        }
+      else {
+        SET_AH(0xAA);
+        goto int13_fail_noah;
+        }
+      break;
+
+    case 0x15: /* read disk drive size */
+
+      // Get physical geometry from table
+      npc   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
+      nph   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
+      npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
+
+      // Compute sector count seen by int13
+      lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
+      CX = lba >> 16;
+      DX = lba & 0xffff;
+
+      SET_AH(3);  // hard disk accessible
+      goto int13_success_noah;
+      break;
+
+    case 0x41: // IBM/MS installation check
+      BX=0xaa55;     // install check
+      SET_AH(0x30);  // EDD 3.0
+      CX=0x0007;     // ext disk access and edd, removable supported
+      goto int13_success_noah;
+      break;
+
+    case 0x42: // IBM/MS extended read
+    case 0x43: // IBM/MS extended write
+    case 0x44: // IBM/MS verify
+    case 0x47: // IBM/MS extended seek
+
+      count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
+      segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
+      offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
+ 
+      // Can't use 64 bits lba
+      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
+      if (lba != 0L) {
+        BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
+        goto int13_fail;
+        }
+
+      // Get 32 bits lba and check
+      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
+      if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
+        BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
+        goto int13_fail;
+        }
+
+      // If verify or seek
+      if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
+        goto int13_success;
+      
+      // Execute the command
+      if ( GET_AH() == 0x42 )
+        status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
+      else
+        status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
+
+      count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
+      write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
+
+      if (status != 0) {
+        BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
+        SET_AH(0x0c);
+        goto int13_fail_noah;
+        }
+
+      goto int13_success;
+      break;
+
+    case 0x45: // IBM/MS lock/unlock drive
+    case 0x49: // IBM/MS extended media change
+      goto int13_success;    // Always success for HD
+      break;
+      
+    case 0x46: // IBM/MS eject media
+      SET_AH(0xb2);          // Volume Not Removable
+      goto int13_fail_noah;  // Always fail for HD
+      break;
+
+    case 0x48: // IBM/MS get drive parameters
+      size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
+
+      // Buffer is too small
+      if(size < 0x1a) 
+        goto int13_fail;
+
+      // EDD 1.x
+      if(size >= 0x1a) {
+        Bit16u   blksize;
+
+        npc     = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
+        nph     = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
+        npspt   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
+        lba     = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
+        blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
+
+        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
+        write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
+        write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
+        write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
+        write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
+        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba);  // FIXME should be Bit64
+        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);  
+        write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);  
+        }
+
+      // EDD 2.x
+      if(size >= 0x1e) {
+        Bit8u  channel, dev, irq, mode, checksum, i, translation;
+        Bit16u iobase1, iobase2, options;
+
+        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
+
+        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);  
+        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);  
+
+        // Fill in dpte
+        channel = device / 2;
+        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+        iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+        irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
+        mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+        translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
+
+        options  = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
+        options |= (1<<4); // lba translation
+        options |= (mode==ATA_MODE_PIO32?1:0<<7);
+        options |= (translation==ATA_TRANSLATION_LBA?1:0<<9); 
+        options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9); 
+
+        write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
+        write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
+        write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
+        write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
+        write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
+        write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
+ 
+        checksum=0;
+        for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
+        checksum = ~checksum;
+        write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
+        }
+
+      // EDD 3.x
+      if(size >= 0x42) {
+        Bit8u channel, iface, checksum, i;
+        Bit16u iobase1;
+
+        channel = device / 2;
+        iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
+        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+
+        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
+        write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
+        write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
+        write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
+        write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
+
+        if (iface==ATA_IFACE_ISA) {
+          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
+          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
+          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
+          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
+          }
+        else { 
+          // FIXME PCI
+          }
+        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
+        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
+        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
+        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
+
+        if (iface==ATA_IFACE_ISA) {
+          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
+          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
+          write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
+          }
+        else { 
+          // FIXME PCI
+          }
+        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
+        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
+        write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
+        write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
+
+        checksum=0;
+        for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
+        checksum = ~checksum;
+        write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
+        }
+
+      goto int13_success;
+      break;
+
+    case 0x4e: // // IBM/MS set hardware configuration
+      // DMA, prefetch, PIO maximum not supported
+      switch (GET_AL()) {
+        case 0x01:
+        case 0x03:
+        case 0x04:
+        case 0x06:
+          goto int13_success;
+          break;
+        default :
+          goto int13_fail;
+        }
+      break;
+
+    case 0x09: /* initialize drive parameters */
+    case 0x0c: /* seek to specified cylinder */
+    case 0x0d: /* alternate disk reset */
+    case 0x11: /* recalibrate */
+    case 0x14: /* controller internal diagnostic */
+      BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
+      goto int13_success;
+      break;
+
+    case 0x0a: /* read disk sectors with ECC */
+    case 0x0b: /* write disk sectors with ECC */
+    case 0x18: // set media type for format
+    case 0x50: // IBM/MS send packet command
+    default:
+      BX_INFO("int13_harddisk function %02xh unsupported, returns fail\n", GET_AH());
+      goto int13_fail;
+      break;
+    }
+
+int13_fail:
+    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
+int13_fail_noah:
+    SET_DISK_RET_STATUS(GET_AH());
+int13_fail_nostatus:
+    SET_CF();     // error occurred
+    return;
+
+int13_success:
+    SET_AH(0x00); // no error
+int13_success_noah:
+    SET_DISK_RET_STATUS(0x00);
+    CLEAR_CF();   // no error
+    return;
+}
+
+// ---------------------------------------------------------------------------
+// Start of int13 for cdrom
+// ---------------------------------------------------------------------------
+
+  void
+int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
+  Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit8u  device, status, locks;
+  Bit8u  atacmd[12];
+  Bit32u lba;
+  Bit16u count, segment, offset, i, size;
+
+  BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+  
+  SET_DISK_RET_STATUS(0x00);
+
+  /* basic check : device should be 0xE0+ */
+  if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
+    BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
+    goto int13_fail;
+    }
+
+  // Get the ata channel
+  device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
+
+  /* basic check : device has to be valid  */
+  if (device >= BX_MAX_ATA_DEVICES) {
+    BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
+    goto int13_fail;
+    }
+  
+  switch (GET_AH()) {
+
+    // all those functions return SUCCESS
+    case 0x00: /* disk controller reset */
+    case 0x09: /* initialize drive parameters */
+    case 0x0c: /* seek to specified cylinder */
+    case 0x0d: /* alternate disk reset */  
+    case 0x10: /* check drive ready */    
+    case 0x11: /* recalibrate */      
+    case 0x14: /* controller internal diagnostic */
+    case 0x16: /* detect disk change */
+      goto int13_success;
+      break;
+
+    // all those functions return disk write-protected
+    case 0x03: /* write disk sectors */
+    case 0x05: /* format disk track */
+    case 0x43: // IBM/MS extended write
+      SET_AH(0x03);
+      goto int13_fail_noah;
+      break;
+
+    case 0x01: /* read disk status */
+      status = read_byte(0x0040, 0x0074);
+      SET_AH(status);
+      SET_DISK_RET_STATUS(0);
+
+      /* set CF if error status read */
+      if (status) goto int13_fail_nostatus;
+      else        goto int13_success_noah;
+      break;      
+
+    case 0x15: /* read disk drive size */
+      SET_AH(0x02);
+      goto int13_fail_noah;
+      break;
+
+    case 0x41: // IBM/MS installation check
+      BX=0xaa55;     // install check
+      SET_AH(0x30);  // EDD 2.1
+      CX=0x0007;     // ext disk access, removable and edd
+      goto int13_success_noah;
+      break;
+
+    case 0x42: // IBM/MS extended read
+    case 0x44: // IBM/MS verify sectors
+    case 0x47: // IBM/MS extended seek
+       
+      count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
+      segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
+      offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
+ 
+      // Can't use 64 bits lba
+      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
+      if (lba != 0L) {
+        BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
+        goto int13_fail;
+        }
+
+      // Get 32 bits lba 
+      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
+
+      // If verify or seek
+      if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
+        goto int13_success;
+      
+      memsetb(get_SS(),atacmd,0,12);
+      atacmd[0]=0x28;                      // READ command
+      atacmd[7]=(count & 0xff00) >> 8;     // Sectors
+      atacmd[8]=(count & 0x00ff);          // Sectors
+      atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
+      atacmd[3]=(lba & 0x00ff0000) >> 16;
+      atacmd[4]=(lba & 0x0000ff00) >> 8;
+      atacmd[5]=(lba & 0x000000ff);
+      status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset); 
+
+      count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
+      write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
+
+      if (status != 0) {
+        BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
+        SET_AH(0x0c);
+        goto int13_fail_noah;
+        }
+
+      goto int13_success;
+      break;
+
+    case 0x45: // IBM/MS lock/unlock drive
+      if (GET_AL() > 2) goto int13_fail;
+
+      locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
+
+      switch (GET_AL()) {
+        case 0 :  // lock
+          if (locks == 0xff) {
+            SET_AH(0xb4);
+            SET_AL(1);
+            goto int13_fail_noah;
+            }
+          write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
+          SET_AL(1);
+          break;
+        case 1 :  // unlock
+          if (locks == 0x00) {
+            SET_AH(0xb0);
+            SET_AL(0);
+            goto int13_fail_noah;
+            }
+          write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
+          SET_AL(locks==0?0:1);
+          break;
+        case 2 :  // status
+          SET_AL(locks==0?0:1);
+          break;
+        }
+      goto int13_success;
+      break;
+
+    case 0x46: // IBM/MS eject media
+      locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
+      
+      if (locks != 0) {
+        SET_AH(0xb1); // media locked
+        goto int13_fail_noah;
+        }
+      // FIXME should handle 0x31 no media in device
+      // FIXME should handle 0xb5 valid request failed
+    
+      // Call removable media eject
+      ASM_START
+        push bp
+        mov  bp, sp
+
+        mov ah, #0x52
+        int 15
+        mov _int13_cdrom.status + 2[bp], ah
+        jnc int13_cdrom_rme_end
+        mov _int13_cdrom.status, #1
+int13_cdrom_rme_end:
+        pop bp
+      ASM_END
+
+      if (status != 0) {
+        SET_AH(0xb1); // media locked
+        goto int13_fail_noah;
+      }
+
+      goto int13_success;
+      break;
+
+    case 0x48: // IBM/MS get drive parameters
+      size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
+
+      // Buffer is too small
+      if(size < 0x1a) 
+        goto int13_fail;
+
+      // EDD 1.x
+      if(size >= 0x1a) {
+        Bit16u   cylinders, heads, spt, blksize;
+
+        blksize   = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
+
+        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
+        write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
+        write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
+        write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
+        write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
+        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff);  // FIXME should be Bit64
+        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);  
+        write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);  
+        }
+
+      // EDD 2.x
+      if(size >= 0x1e) {
+        Bit8u  channel, dev, irq, mode, checksum, i;
+        Bit16u iobase1, iobase2, options;
+
+        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
+
+        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);  
+        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);  
+
+        // Fill in dpte
+        channel = device / 2;
+        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+        iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
+        irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
+        mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
+
+        // FIXME atapi device
+        options  = (1<<4); // lba translation
+        options |= (1<<5); // removable device
+        options |= (1<<6); // atapi device
+        options |= (mode==ATA_MODE_PIO32?1:0<<7);
+
+        write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
+        write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
+        write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
+        write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
+        write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
+        write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
+        write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
+
+        checksum=0;
+        for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
+        checksum = ~checksum;
+        write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
+        }
+
+      // EDD 3.x
+      if(size >= 0x42) {
+        Bit8u channel, iface, checksum, i;
+        Bit16u iobase1;
+
+        channel = device / 2;
+        iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
+        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
+
+        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
+        write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
+        write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
+        write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
+        write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
+
+        if (iface==ATA_IFACE_ISA) {
+          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
+          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
+          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
+          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
+          }
+        else { 
+          // FIXME PCI
+          }
+        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
+        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
+        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
+        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
+
+        if (iface==ATA_IFACE_ISA) {
+          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
+          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
+          write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
+          }
+        else { 
+          // FIXME PCI
+          }
+        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
+        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
+        write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
+        write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
+
+        checksum=0;
+        for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
+        checksum = ~checksum;
+        write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
+        }
+
+      goto int13_success;
+      break;
+
+    case 0x49: // IBM/MS extended media change
+      // always send changed ??
+      SET_AH(06);
+      goto int13_fail_nostatus;
+      break;
+      
+    case 0x4e: // // IBM/MS set hardware configuration
+      // DMA, prefetch, PIO maximum not supported
+      switch (GET_AL()) {
+        case 0x01:
+        case 0x03:
+        case 0x04:
+        case 0x06:
+          goto int13_success;
+          break;
+        default :
+          goto int13_fail;
+        }
+      break;
+
+    // all those functions return unimplemented
+    case 0x02: /* read sectors */
+    case 0x04: /* verify sectors */
+    case 0x08: /* read disk drive parameters */
+    case 0x0a: /* read disk sectors with ECC */
+    case 0x0b: /* write disk sectors with ECC */
+    case 0x18: /* set media type for format */
+    case 0x50: // ? - send packet command
+    default:
+      BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
+      goto int13_fail;
+      break;
+    }
+
+int13_fail:
+    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
+int13_fail_noah:
+    SET_DISK_RET_STATUS(GET_AH());
+int13_fail_nostatus:
+    SET_CF();     // error occurred
+    return;
+
+int13_success:
+    SET_AH(0x00); // no error
+int13_success_noah:
+    SET_DISK_RET_STATUS(0x00);
+    CLEAR_CF();   // no error
+    return;
+}
+
+// ---------------------------------------------------------------------------
+// End of int13 for cdrom
+// ---------------------------------------------------------------------------
+
+#if BX_ELTORITO_BOOT
+// ---------------------------------------------------------------------------
+// Start of int13 for eltorito functions
+// ---------------------------------------------------------------------------
+
+  void
+int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
+  Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+
+  BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+  // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
+  
+  switch (GET_AH()) {
+
+    // FIXME ElTorito Various. Should be implemented
+    case 0x4a: // ElTorito - Initiate disk emu
+    case 0x4c: // ElTorito - Initiate disk emu and boot
+    case 0x4d: // ElTorito - Return Boot catalog
+      BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
+      goto int13_fail;
+      break;
+
+    case 0x4b: // ElTorito - Terminate disk emu
+      // FIXME ElTorito Hardcoded
+      write_byte(DS,SI+0x00,0x13);
+      write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
+      write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
+      write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
+      write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
+      write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
+      write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
+      write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
+      write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
+      write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
+      write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
+      write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
+
+      // If we have to terminate emulation
+      if(GET_AL() == 0x00) {
+        // FIXME ElTorito Various. Should be handled accordingly to spec
+        write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
+        }
+
+      goto int13_success;
+      break;
+
+    default:
+      BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
+      goto int13_fail;
+      break;
+    }
+
+int13_fail:
+    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
+    SET_DISK_RET_STATUS(GET_AH());
+    SET_CF();     // error occurred
+    return;
+
+int13_success:
+    SET_AH(0x00); // no error
+    SET_DISK_RET_STATUS(0x00);
+    CLEAR_CF();   // no error
+    return;
+}
+
+// ---------------------------------------------------------------------------
+// End of int13 for eltorito functions
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// Start of int13 when emulating a device from the cd
+// ---------------------------------------------------------------------------
+
+  void
+int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
+  Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit8u  device, status;
+  Bit16u vheads, vspt, vcylinders;
+  Bit16u head, sector, cylinder, nbsectors;
+  Bit32u vlba, ilba, slba, elba;
+  Bit16u before, segment, offset;
+  Bit8u  atacmd[12];
+
+  BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+  
+  /* at this point, we are emulating a floppy/harddisk */
+  
+  // Recompute the device number 
+  device  = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
+  device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
+
+  SET_DISK_RET_STATUS(0x00);
+
+  /* basic checks : emulation should be active, dl should equal the emulated drive */
+  if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
+   || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
+    BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
+    goto int13_fail;
+    }
+  
+  switch (GET_AH()) {
+
+    // all those functions return SUCCESS
+    case 0x00: /* disk controller reset */
+    case 0x09: /* initialize drive parameters */
+    case 0x0c: /* seek to specified cylinder */
+    case 0x0d: /* alternate disk reset */  // FIXME ElTorito Various. should really reset ?
+    case 0x10: /* check drive ready */     // FIXME ElTorito Various. should check if ready ?
+    case 0x11: /* recalibrate */      
+    case 0x14: /* controller internal diagnostic */
+    case 0x16: /* detect disk change */
+      goto int13_success;
+      break;
+
+    // all those functions return disk write-protected
+    case 0x03: /* write disk sectors */
+    case 0x05: /* format disk track */
+      SET_AH(0x03);
+      goto int13_fail_noah;
+      break;
+
+    case 0x01: /* read disk status */
+      status=read_byte(0x0040, 0x0074);
+      SET_AH(status);
+      SET_DISK_RET_STATUS(0);
+
+      /* set CF if error status read */
+      if (status) goto int13_fail_nostatus;
+      else        goto int13_success_noah;
+      break;
+
+    case 0x02: // read disk sectors
+    case 0x04: // verify disk sectors
+      vspt       = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); 
+      vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders); 
+      vheads     = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads); 
+
+      ilba       = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
+
+      sector    = GET_CL() & 0x003f;
+      cylinder  = (GET_CL() & 0x00c0) << 2 | GET_CH();
+      head      = GET_DH();
+      nbsectors = GET_AL();
+      segment   = ES;
+      offset    = BX;
+
+      // no sector to read ?
+      if(nbsectors==0) goto int13_success;
+
+      // sanity checks sco openserver needs this!
+      if ((sector   >  vspt)
+       || (cylinder >= vcylinders)
+       || (head     >= vheads)) {
+        goto int13_fail;
+        }
+
+      // After controls, verify do nothing
+      if (GET_AH() == 0x04) goto int13_success;
+
+      segment = ES+(BX / 16);
+      offset  = BX % 16;
+
+      // calculate the virtual lba inside the image
+      vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
+ 
+      // In advance so we don't loose the count
+      SET_AL(nbsectors);
+
+      // start lba on cd
+      slba  = (Bit32u)vlba/4; 
+      before= (Bit16u)vlba%4;
+
+      // end lba on cd
+      elba = (Bit32u)(vlba+nbsectors-1)/4;
+      
+      memsetb(get_SS(),atacmd,0,12);
+      atacmd[0]=0x28;                      // READ command
+      atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
+      atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff);      // Sectors
+      atacmd[2]=(ilba+slba & 0xff000000) >> 24;  // LBA
+      atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
+      atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
+      atacmd[5]=(ilba+slba & 0x000000ff);
+      if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
+        BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
+        SET_AH(0x02);
+        SET_AL(0);
+        goto int13_fail_noah;
+        }
+
+      goto int13_success;
+      break;
+
+    case 0x08: /* read disk drive parameters */
+      vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); 
+      vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1; 
+      vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1; 
+ 
+      SET_AL( 0x00 );
+      SET_BL( 0x00 );
+      SET_CH( vcylinders & 0xff );
+      SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt  & 0x3f ));
+      SET_DH( vheads );
+      SET_DL( 0x02 );   // FIXME ElTorito Various. should send the real count of drives 1 or 2
+                        // FIXME ElTorito Harddisk. should send the HD count
+ 
+      switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
+        case 0x01: SET_BL( 0x02 ); break;
+        case 0x02: SET_BL( 0x04 ); break;
+        case 0x03: SET_BL( 0x06 ); break;
+        }
+
+ASM_START
+      push bp
+      mov  bp, sp
+      mov ax, #diskette_param_table2
+      mov _int13_cdemu.DI+2[bp], ax
+      mov _int13_cdemu.ES+2[bp], cs
+      pop  bp
+ASM_END
+      goto int13_success;
+      break;
+
+    case 0x15: /* read disk drive size */
+      // FIXME ElTorito Harddisk. What geometry to send ?
+      SET_AH(0x03);
+      goto int13_success_noah;
+      break;
+
+    // all those functions return unimplemented
+    case 0x0a: /* read disk sectors with ECC */
+    case 0x0b: /* write disk sectors with ECC */
+    case 0x18: /* set media type for format */
+    case 0x41: // IBM/MS installation check
+      // FIXME ElTorito Harddisk. Darwin would like to use EDD
+    case 0x42: // IBM/MS extended read
+    case 0x43: // IBM/MS extended write
+    case 0x44: // IBM/MS verify sectors
+    case 0x45: // IBM/MS lock/unlock drive
+    case 0x46: // IBM/MS eject media
+    case 0x47: // IBM/MS extended seek
+    case 0x48: // IBM/MS get drive parameters 
+    case 0x49: // IBM/MS extended media change
+    case 0x4e: // ? - set hardware configuration
+    case 0x50: // ? - send packet command
+    default:
+      BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
+      goto int13_fail;
+      break;
+    }
+
+int13_fail:
+    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
+int13_fail_noah:
+    SET_DISK_RET_STATUS(GET_AH());
+int13_fail_nostatus:
+    SET_CF();     // error occurred
+    return;
+
+int13_success:
+    SET_AH(0x00); // no error
+int13_success_noah:
+    SET_DISK_RET_STATUS(0x00);
+    CLEAR_CF();   // no error
+    return;
+}
+
+// ---------------------------------------------------------------------------
+// End of int13 when emulating a device from the cd
+// ---------------------------------------------------------------------------
+
+#endif // BX_ELTORITO_BOOT
+
+#else //BX_USE_ATADRV
+
+  void
+outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
+  Bit16u cylinder;
+  Bit16u hd_heads;
+  Bit16u head;
+  Bit16u hd_sectors;
+  Bit16u sector;
+  Bit16u dl;
+{
+ASM_START
+        push   bp
+        mov    bp, sp
+        push   eax
+        push   ebx
+        push   edx
+        xor    eax,eax
+        mov    ax,4[bp]  // cylinder
+        xor    ebx,ebx
+        mov    bl,6[bp]  // hd_heads
+        imul   ebx
+
+        mov    bl,8[bp]  // head
+        add    eax,ebx
+        mov    bl,10[bp] // hd_sectors
+        imul   ebx
+        mov    bl,12[bp] // sector
+        add    eax,ebx
+
+        dec    eax
+        mov    dx,#0x1f3
+        out    dx,al
+        mov    dx,#0x1f4
+        mov    al,ah
+        out    dx,al
+        shr    eax,#16
+        mov    dx,#0x1f5
+        out    dx,al
+        and    ah,#0xf
+        mov    bl,14[bp] // dl
+        and    bl,#1
+        shl    bl,#4
+        or     ah,bl
+        or     ah,#0xe0
+        mov    al,ah
+        mov    dx,#0x01f6
+        out    dx,al
+        pop    edx
+        pop    ebx
+        pop    eax
+        pop    bp
+ASM_END
+}
+
+  void
+int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
+  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+  Bit8u    drive, num_sectors, sector, head, status, mod;
+  Bit8u    drive_map;
+  Bit8u    n_drives;
+  Bit16u   cyl_mod, ax;
+  Bit16u   max_cylinder, cylinder, total_sectors;
+  Bit16u   hd_cylinders;
+  Bit8u    hd_heads, hd_sectors;
+  Bit16u   val16;
+  Bit8u    sector_count;
+  unsigned int i;
+  Bit16u   tempbx;
+  Bit16u   dpsize;
+
+  Bit16u   count, segment, offset;
+  Bit32u   lba;
+  Bit16u   error;
+
+  BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+
+  write_byte(0x0040, 0x008e, 0);  // clear completion flag
+
+  /* at this point, DL is >= 0x80 to be passed from the floppy int13h
+     handler code */
+  /* check how many disks first (cmos reg 0x12), return an error if
+     drive not present */
+  drive_map = inb_cmos(0x12);
+  drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
+              (((drive_map & 0x0f)==0) ? 0 : 2);
+  n_drives = (drive_map==0) ? 0 :
+    ((drive_map==3) ? 2 : 1);
+
+  if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
+    SET_AH(0x01);
+    SET_DISK_RET_STATUS(0x01);
+    SET_CF(); /* error occurred */
+    return;
+    }
+
+  switch (GET_AH()) {
+
+    case 0x00: /* disk controller reset */
+BX_DEBUG_INT13_HD("int13_f00\n");
+
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      set_diskette_ret_status(0);
+      set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
+      set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
+      CLEAR_CF(); /* successful */
+      return;
+      break;
+
+    case 0x01: /* read disk status */
+BX_DEBUG_INT13_HD("int13_f01\n");
+      status = read_byte(0x0040, 0x0074);
+      SET_AH(status);
+      SET_DISK_RET_STATUS(0);
+      /* set CF if error status read */
+      if (status) SET_CF();
+      else        CLEAR_CF();
+      return;
+      break;
+
+    case 0x04: // verify disk sectors
+    case 0x02: // read disk sectors
+      drive = GET_ELDL();
+      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
+
+      num_sectors = GET_AL();
+      cylinder    = (GET_CL() & 0x00c0) << 2 | GET_CH();
+      sector      = (GET_CL() & 0x3f);
+      head        = GET_DH();
+
+
+      if (hd_cylinders > 1024) {
+        if (hd_cylinders <= 2048) {
+          cylinder <<= 1;
+          }
+        else if (hd_cylinders <= 4096) {
+          cylinder <<= 2;
+          }
+        else if (hd_cylinders <= 8192) {
+          cylinder <<= 3;
+          }
+        else { // hd_cylinders <= 16384
+          cylinder <<= 4;
+          }
+
+        ax = head / hd_heads;
+        cyl_mod = ax & 0xff;
+        head    = ax >> 8;
+        cylinder |= cyl_mod;
+        }
+
+      if ( (cylinder >= hd_cylinders) ||
+           (sector > hd_sectors) ||
+           (head >= hd_heads) ) {
+        SET_AH(1);
+        SET_DISK_RET_STATUS(1);
+        SET_CF(); /* error occurred */
+        return;
+        }
+
+      if ( (num_sectors > 128) || (num_sectors == 0) )
+        BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
+
+      if (head > 15)
+        BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
+
+      if ( GET_AH() == 0x04 ) {
+        SET_AH(0);
+        SET_DISK_RET_STATUS(0);
+        CLEAR_CF();
+        return;
+        }
+
+      status = inb(0x1f7);
+      if (status & 0x80) {
+        BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
+        }
+      outb(0x01f2, num_sectors);
+      /* activate LBA? (tomv) */
+      if (hd_heads > 16) {
+BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
+        outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
+        }
+      else {
+        outb(0x01f3, sector);
+        outb(0x01f4, cylinder & 0x00ff);
+        outb(0x01f5, cylinder >> 8);
+        outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
+        }
+      outb(0x01f7, 0x20);
+
+      while (1) {
+        status = inb(0x1f7);
+        if ( !(status & 0x80) ) break;
+        }
+
+      if (status & 0x01) {
+        BX_PANIC("hard drive BIOS:(read/verify) read error\n");
+      } else if ( !(status & 0x08) ) {
+        BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
+        BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
+      }
+
+      sector_count = 0;
+      tempbx = BX;
+
+ASM_START
+  sti  ;; enable higher priority interrupts
+ASM_END
+
+      while (1) {
+ASM_START
+        ;; store temp bx in real DI register
+        push bp
+        mov  bp, sp
+        mov  di, _int13_harddisk.tempbx + 2 [bp]
+        pop  bp
+
+        ;; adjust if there will be an overrun
+        cmp   di, #0xfe00
+        jbe   i13_f02_no_adjust
+i13_f02_adjust:
+        sub   di, #0x0200 ; sub 512 bytes from offset
+        mov   ax, es
+        add   ax, #0x0020 ; add 512 to segment
+        mov   es, ax
+
+i13_f02_no_adjust:
+        mov  cx, #0x0100   ;; counter (256 words = 512b)
+        mov  dx, #0x01f0  ;; AT data read port
+
+        rep
+          insw ;; CX words transfered from port(DX) to ES:[DI]
+
+i13_f02_done:
+        ;; store real DI register back to temp bx
+        push bp
+        mov  bp, sp
+        mov  _int13_harddisk.tempbx + 2 [bp], di
+        pop  bp
+ASM_END
+
+        sector_count++;
+        num_sectors--;
+        if (num_sectors == 0) {
+          status = inb(0x1f7);
+          if ( (status & 0xc9) != 0x40 )
+            BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
+          break;
+          }
+        else {
+          status = inb(0x1f7);
+          if ( (status & 0xc9) != 0x48 )
+            BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
+          continue;
+          }
+        }
+
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      SET_AL(sector_count);
+      CLEAR_CF(); /* successful */
+      return;
+      break;
+
+
+    case 0x03: /* write disk sectors */
+BX_DEBUG_INT13_HD("int13_f03\n");
+      drive = GET_ELDL ();
+      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
+
+      num_sectors = GET_AL();
+      cylinder    = GET_CH();
+      cylinder    |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
+      sector      = (GET_CL() & 0x3f);
+      head        = GET_DH();
+
+      if (hd_cylinders > 1024) {
+        if (hd_cylinders <= 2048) {
+          cylinder <<= 1;
+          }
+        else if (hd_cylinders <= 4096) {
+          cylinder <<= 2;
+          }
+        else if (hd_cylinders <= 8192) {
+          cylinder <<= 3;
+          }
+        else { // hd_cylinders <= 16384
+          cylinder <<= 4;
+          }
+
+        ax = head / hd_heads;
+        cyl_mod = ax & 0xff;
+        head    = ax >> 8;
+        cylinder |= cyl_mod;
+        }
+
+      if ( (cylinder >= hd_cylinders) ||
+           (sector > hd_sectors) ||
+           (head >= hd_heads) ) {
+        SET_AH( 1);
+        SET_DISK_RET_STATUS(1);
+        SET_CF(); /* error occurred */
+        return;
+        }
+
+      if ( (num_sectors > 128) || (num_sectors == 0) )
+        BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
+
+      if (head > 15)
+        BX_PANIC("hard drive BIOS:(read) head > 15\n");
+
+      status = inb(0x1f7);
+      if (status & 0x80) {
+        BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
+        }
+// should check for Drive Ready Bit also in status reg
+      outb(0x01f2, num_sectors);
+
+      /* activate LBA? (tomv) */
+      if (hd_heads > 16) {
+BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
+        outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
+        }
+      else {
+        outb(0x01f3, sector);
+        outb(0x01f4, cylinder & 0x00ff);
+        outb(0x01f5, cylinder >> 8);
+        outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
+        }
+      outb(0x01f7, 0x30);
+
+      // wait for busy bit to turn off after seeking
+      while (1) {
+        status = inb(0x1f7);
+        if ( !(status & 0x80) ) break;
+        }
+
+      if ( !(status & 0x08) ) {
+        BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
+        BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
+        }
+
+      sector_count = 0;
+      tempbx = BX;
+
+ASM_START
+  sti  ;; enable higher priority interrupts
+ASM_END
+
+      while (1) {
+ASM_START
+        ;; store temp bx in real SI register
+        push bp
+        mov  bp, sp
+        mov  si, _int13_harddisk.tempbx + 2 [bp]
+        pop  bp
+
+        ;; adjust if there will be an overrun
+        cmp   si, #0xfe00
+        jbe   i13_f03_no_adjust
+i13_f03_adjust:
+        sub   si, #0x0200 ; sub 512 bytes from offset
+        mov   ax, es
+        add   ax, #0x0020 ; add 512 to segment
+        mov   es, ax
+
+i13_f03_no_adjust:
+        mov  cx, #0x0100   ;; counter (256 words = 512b)
+        mov  dx, #0x01f0  ;; AT data read port
+
+        seg ES
+        rep
+          outsw ;; CX words tranfered from ES:[SI] to port(DX)
+
+        ;; store real SI register back to temp bx
+        push bp
+        mov  bp, sp
+        mov  _int13_harddisk.tempbx + 2 [bp], si
+        pop  bp
+ASM_END
+
+        sector_count++;
+        num_sectors--;
+        if (num_sectors == 0) {
+          status = inb(0x1f7);
+          if ( (status & 0xe9) != 0x40 )
+            BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
+          break;
+          }
+        else {
+          status = inb(0x1f7);
+          if ( (status & 0xc9) != 0x48 )
+            BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
+          continue;
+          }
+        }
+
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      SET_AL(sector_count);
+      CLEAR_CF(); /* successful */
+      return;
+      break;
+
+    case 0x05: /* format disk track */
+BX_DEBUG_INT13_HD("int13_f05\n");
+      BX_PANIC("format disk track called\n");
+      /* nop */
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      CLEAR_CF(); /* successful */
+      return;
+      break;
+
+    case 0x08: /* read disk drive parameters */
+BX_DEBUG_INT13_HD("int13_f08\n");
+      
+      drive = GET_ELDL ();
+      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
+
+      // translate CHS
+      //
+      if (hd_cylinders <= 1024) {
+        // hd_cylinders >>= 0;
+        // hd_heads <<= 0;
+        }
+      else if (hd_cylinders <= 2048) {
+        hd_cylinders >>= 1;
+        hd_heads <<= 1;
+        }
+      else if (hd_cylinders <= 4096) {
+        hd_cylinders >>= 2;
+        hd_heads <<= 2;
+        }
+      else if (hd_cylinders <= 8192) {
+        hd_cylinders >>= 3;
+        hd_heads <<= 3;
+        }
+      else { // hd_cylinders <= 16384
+        hd_cylinders >>= 4;
+        hd_heads <<= 4;
+        }
+
+      max_cylinder = hd_cylinders - 2; /* 0 based */
+      SET_AL(0);
+      SET_CH(max_cylinder & 0xff);
+      SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
+      SET_DH(hd_heads - 1);
+      SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      CLEAR_CF(); /* successful */
+
+      return;
+      break;
+
+    case 0x09: /* initialize drive parameters */
+BX_DEBUG_INT13_HD("int13_f09\n");
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      CLEAR_CF(); /* successful */
+      return;
+      break;
+
+    case 0x0a: /* read disk sectors with ECC */
+BX_DEBUG_INT13_HD("int13_f0a\n");
+    case 0x0b: /* write disk sectors with ECC */
+BX_DEBUG_INT13_HD("int13_f0b\n");
+      BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
+      return;
+      break;
+
+    case 0x0c: /* seek to specified cylinder */
+BX_DEBUG_INT13_HD("int13_f0c\n");
+      BX_INFO("int13h function 0ch (seek) not implemented!\n");
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      CLEAR_CF(); /* successful */
+      return;
+      break;
+
+    case 0x0d: /* alternate disk reset */
+BX_DEBUG_INT13_HD("int13_f0d\n");
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      CLEAR_CF(); /* successful */
+      return;
+      break;
+
+    case 0x10: /* check drive ready */
+BX_DEBUG_INT13_HD("int13_f10\n");
+      //SET_AH(0);
+      //SET_DISK_RET_STATUS(0);
+      //CLEAR_CF(); /* successful */
+      //return;
+      //break;
+
+      // should look at 40:8E also???
+      status = inb(0x01f7);
+      if ( (status & 0xc0) == 0x40 ) {
+        SET_AH(0);
+        SET_DISK_RET_STATUS(0);
+        CLEAR_CF(); // drive ready
+        return;
+        }
+      else {
+        SET_AH(0xAA);
+        SET_DISK_RET_STATUS(0xAA);
+        SET_CF(); // not ready
+        return;
+        }
+      break;
+
+    case 0x11: /* recalibrate */
+BX_DEBUG_INT13_HD("int13_f11\n");
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      CLEAR_CF(); /* successful */
+      return;
+      break;
+
+    case 0x14: /* controller internal diagnostic */
+BX_DEBUG_INT13_HD("int13_f14\n");
+      SET_AH(0);
+      SET_DISK_RET_STATUS(0);
+      CLEAR_CF(); /* successful */
+      SET_AL(0);
+      return;
+      break;
+
+    case 0x15: /* read disk drive size */
+      drive = GET_ELDL();
+      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
+ASM_START
+      push bp
+      mov  bp, sp
+      mov  al, _int13_harddisk.hd_heads + 2 [bp]
+      mov  ah, _int13_harddisk.hd_sectors + 2 [bp]
+      mul  al, ah ;; ax = heads * sectors
+      mov  bx, _int13_harddisk.hd_cylinders + 2 [bp]
+      dec  bx     ;; use (cylinders - 1) ???
+      mul  ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
+      ;; now we need to move the 32bit result dx:ax to what the
+      ;; BIOS wants which is cx:dx.
+      ;; and then into CX:DX on the stack
+      mov  _int13_harddisk.CX + 2 [bp], dx
+      mov  _int13_harddisk.DX + 2 [bp], ax
+      pop  bp
+ASM_END
+      SET_AH(3);  // hard disk accessible
+      SET_DISK_RET_STATUS(0); // ??? should this be 0
+      CLEAR_CF(); // successful
+      return;
+      break;
+
+    case 0x18: // set media type for format
+    case 0x41: // IBM/MS 
+    case 0x42: // IBM/MS 
+    case 0x43: // IBM/MS 
+    case 0x44: // IBM/MS 
+    case 0x45: // IBM/MS lock/unlock drive
+    case 0x46: // IBM/MS eject media
+    case 0x47: // IBM/MS extended seek
+    case 0x49: // IBM/MS extended media change
+    case 0x50: // IBM/MS send packet command
+    default:
+      BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
+
+      SET_AH(1);  // code=invalid function in AH or invalid parameter
+      SET_DISK_RET_STATUS(1);
+      SET_CF(); /* unsuccessful */
+      return;
+      break;
+    }
+}
+
+static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
+static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
+
+  void
+get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
+  Bit8u drive;
+  Bit16u *hd_cylinders;
+  Bit8u  *hd_heads;
+  Bit8u  *hd_sectors;
+{
+  Bit8u hd_type;
+  Bit16u ss;
+  Bit16u cylinders;
+  Bit8u iobase;
+
+  ss = get_SS();
+  if (drive == 0x80) {
+    hd_type = inb_cmos(0x12) & 0xf0;
+    if (hd_type != 0xf0)
+      BX_INFO(panic_msg_reg12h,0);
+    hd_type = inb_cmos(0x19); // HD0: extended type
+    if (hd_type != 47)
+      BX_INFO(panic_msg_reg19h,0,0x19);
+    iobase = 0x1b;
+  } else {
+    hd_type = inb_cmos(0x12) & 0x0f;
+    if (hd_type != 0x0f)
+      BX_INFO(panic_msg_reg12h,1);
+    hd_type = inb_cmos(0x1a); // HD0: extended type
+    if (hd_type != 47)
+      BX_INFO(panic_msg_reg19h,0,0x1a);
+    iobase = 0x24;
+  }
+
+  // cylinders
+  cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
+  write_word(ss, hd_cylinders, cylinders);
+
+  // heads
+  write_byte(ss, hd_heads, inb_cmos(iobase+2));
+
+  // sectors per track
+  write_byte(ss, hd_sectors, inb_cmos(iobase+8));
+}
+
+#endif //else BX_USE_ATADRV
+
+
+//////////////////////
+// FLOPPY functions //
+//////////////////////
+
+void floppy_reset_controller()
+{
+  Bit8u val8;
+
+  // Reset controller
+  val8 = inb(0x03f2);
+  outb(0x03f2, val8 & ~0x04);
+  outb(0x03f2, val8 | 0x04);
+
+  // Wait for controller to come out of reset  
+  do {
+    val8 = inb(0x3f4);
+  } while ( (val8 & 0xc0) != 0x80 );
+}
+
+void floppy_prepare_controller(drive)
+  Bit16u drive;
+{
+  Bit8u  val8, dor, prev_reset;
+
+  // set 40:3e bit 7 to 0
+  val8 = read_byte(0x0040, 0x003e);
+  val8 &= 0x7f;
+  write_byte(0x0040, 0x003e, val8);
+
+  // turn on motor of selected drive, DMA & int enabled, normal operation
+  prev_reset = inb(0x03f2) & 0x04;
+  if (drive)
+    dor = 0x20;
+  else
+    dor = 0x10;
+  dor |= 0x0c;
+  dor |= drive;
+  outb(0x03f2, dor);
+
+  // reset the disk motor timeout value of INT 08
+  write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
+
+  // wait for drive readiness
+  do {
+    val8 = inb(0x3f4);
+  } while ( (val8 & 0xc0) != 0x80 );
+
+  if (prev_reset == 0) {
+    // turn on interrupts
+ASM_START
+    sti
+ASM_END
+    // wait on 40:3e bit 7 to become 1
+    do {
+      val8 = read_byte(0x0040, 0x003e);
+    } while ( (val8 & 0x80) == 0 );
+    val8 &= 0x7f;
+ASM_START
+    cli
+ASM_END
+    write_byte(0x0040, 0x003e, val8);
+  }
+}
+
+  bx_bool
+floppy_media_known(drive)
+  Bit16u drive;
+{
+  Bit8u  val8;
+  Bit16u media_state_offset;
+
+  val8 = read_byte(0x0040, 0x003e); // diskette recal status
+  if (drive)
+    val8 >>= 1;
+  val8 &= 0x01;
+  if (val8 == 0)
+    return(0);
+
+  media_state_offset = 0x0090;
+  if (drive)
+    media_state_offset += 1;
+
+  val8 = read_byte(0x0040, media_state_offset);
+  val8 = (val8 >> 4) & 0x01;
+  if (val8 == 0)
+    return(0);
+
+  // check pass, return KNOWN
+  return(1);
+}
+
+  bx_bool
+floppy_media_sense(drive)
+  Bit16u drive;
+{
+  bx_bool retval;
+  Bit16u  media_state_offset;
+  Bit8u   drive_type, config_data, media_state;
+
+  if (floppy_drive_recal(drive) == 0) {
+    return(0);
+    }
+
+  // for now cheat and get drive type from CMOS,
+  // assume media is same as drive type
+
+  // ** config_data **
+  // Bitfields for diskette media control:
+  // Bit(s)  Description (Table M0028)
+  //  7-6  last data rate set by controller
+  //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+  //  5-4  last diskette drive step rate selected
+  //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
+  //  3-2  {data rate at start of operation}
+  //  1-0  reserved
+
+  // ** media_state **
+  // Bitfields for diskette drive media state:
+  // Bit(s)  Description (Table M0030)
+  //  7-6  data rate
+  //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
+  //  5  double stepping required (e.g. 360kB in 1.2MB)
+  //  4  media type established
+  //  3  drive capable of supporting 4MB media
+  //  2-0  on exit from BIOS, contains
+  //    000 trying 360kB in 360kB
+  //    001 trying 360kB in 1.2MB
+  //    010 trying 1.2MB in 1.2MB
+  //    011 360kB in 360kB established
+  //    100 360kB in 1.2MB established
+  //    101 1.2MB in 1.2MB established
+  //    110 reserved
+  //    111 all other formats/drives
+
+  drive_type = inb_cmos(0x10);
+  if (drive == 0)
+    drive_type >>= 4;
+  else
+    drive_type &= 0x0f;
+  if ( drive_type == 1 ) {
+    // 360K 5.25" drive
+    config_data = 0x00; // 0000 0000
+    media_state = 0x25; // 0010 0101
+    retval = 1;
+    }
+  else if ( drive_type == 2 ) {
+    // 1.2 MB 5.25" drive
+    config_data = 0x00; // 0000 0000
+    media_state = 0x25; // 0010 0101   // need double stepping??? (bit 5)
+    retval = 1;
+    }
+  else if ( drive_type == 3 ) {
+    // 720K 3.5" drive
+    config_data = 0x00; // 0000 0000 ???
+    media_state = 0x17; // 0001 0111
+    retval = 1;
+    }
+  else if ( drive_type == 4 ) {
+    // 1.44 MB 3.5" drive
+    config_data = 0x00; // 0000 0000
+    media_state = 0x17; // 0001 0111
+    retval = 1;
+    }
+  else if ( drive_type == 5 ) {
+    // 2.88 MB 3.5" drive
+    config_data = 0xCC; // 1100 1100
+    media_state = 0xD7; // 1101 0111
+    retval = 1;
+    }
+  //
+  // Extended floppy size uses special cmos setting 
+  else if ( drive_type == 6 ) {
+    // 160k 5.25" drive
+    config_data = 0x00; // 0000 0000
+    media_state = 0x27; // 0010 0111
+    retval = 1;
+    }
+  else if ( drive_type == 7 ) {
+    // 180k 5.25" drive
+    config_data = 0x00; // 0000 0000
+    media_state = 0x27; // 0010 0111
+    retval = 1;
+    }
+  else if ( drive_type == 8 ) {
+    // 320k 5.25" drive
+    config_data = 0x00; // 0000 0000
+    media_state = 0x27; // 0010 0111
+    retval = 1;
+    }
+
+  else {
+    // not recognized
+    config_data = 0x00; // 0000 0000
+    media_state = 0x00; // 0000 0000
+    retval = 0;
+    }
+
+  if (drive == 0)
+    media_state_offset = 0x90;
+  else
+    media_state_offset = 0x91;
+  write_byte(0x0040, 0x008B, config_data);
+  write_byte(0x0040, media_state_offset, media_state);
+
+  return(retval);
+}
+
+  bx_bool
+floppy_drive_recal(drive)
+  Bit16u drive;
+{
+  Bit8u  val8;
+  Bit16u curr_cyl_offset;
+
+  floppy_prepare_controller(drive);
+
+  // send Recalibrate command (2 bytes) to controller
+  outb(0x03f5, 0x07);  // 07: Recalibrate
+  outb(0x03f5, drive); // 0=drive0, 1=drive1
+
+  // turn on interrupts
+ASM_START
+  sti
+ASM_END
+
+  // wait on 40:3e bit 7 to become 1
+  do {
+    val8 = (read_byte(0x0040, 0x003e) & 0x80);
+  } while ( val8 == 0 );
+
+  val8 = 0; // separate asm from while() loop
+  // turn off interrupts
+ASM_START
+  cli
+ASM_END
+
+  // set 40:3e bit 7 to 0, and calibrated bit
+  val8 = read_byte(0x0040, 0x003e);
+  val8 &= 0x7f;
+  if (drive) {
+    val8 |= 0x02; // Drive 1 calibrated
+    curr_cyl_offset = 0x0095;
+  } else {
+    val8 |= 0x01; // Drive 0 calibrated
+    curr_cyl_offset = 0x0094;
+  }
+  write_byte(0x0040, 0x003e, val8);
+  write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
+
+  return(1);
+}
+
+
+
+  bx_bool
+floppy_drive_exists(drive)
+  Bit16u drive;
+{
+  Bit8u  drive_type;
+
+  // check CMOS to see if drive exists
+  drive_type = inb_cmos(0x10);
+  if (drive == 0)
+    drive_type >>= 4;
+  else
+    drive_type &= 0x0f;
+  if ( drive_type == 0 )
+    return(0);
+  else
+    return(1);
+}
+
+#if BX_SUPPORT_FLOPPY
+  void
+int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
+  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+  Bit8u  drive, num_sectors, track, sector, head, status;
+  Bit16u base_address, base_count, base_es;
+  Bit8u  page, mode_register, val8, dor;
+  Bit8u  return_status[7];
+  Bit8u  drive_type, num_floppies, ah;
+  Bit16u es, last_addr;
+
+  BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
+
+  ah = GET_AH();
+
+  switch ( ah ) {
+    case 0x00: // diskette controller reset
+BX_DEBUG_INT13_FL("floppy f00\n");
+      drive = GET_ELDL();
+      if (drive > 1) {
+        SET_AH(1); // invalid param
+        set_diskette_ret_status(1);
+        SET_CF();
+        return;
+      }
+      drive_type = inb_cmos(0x10);
+
+      if (drive == 0)
+        drive_type >>= 4;
+      else
+        drive_type &= 0x0f;
+      if (drive_type == 0) {
+        SET_AH(0x80); // drive not responding
+        set_diskette_ret_status(0x80);
+        SET_CF();
+        return;
+      }
+      SET_AH(0);
+      set_diskette_ret_status(0);
+      CLEAR_CF(); // successful
+      set_diskette_current_cyl(drive, 0); // current cylinder
+      return;
+
+    case 0x01: // Read Diskette Status
+      CLEAR_CF();
+      val8 = read_byte(0x0000, 0x0441);
+      SET_AH(val8);
+      if (val8) {
+        SET_CF();
+      }
+      return;
+
+    case 0x02: // Read Diskette Sectors
+    case 0x03: // Write Diskette Sectors
+    case 0x04: // Verify Diskette Sectors
+      num_sectors = GET_AL();
+      track       = GET_CH();
+      sector      = GET_CL();
+      head        = GET_DH();
+      drive       = GET_ELDL();
+
+      if ( (drive > 1) || (head > 1) ||
+           (num_sectors == 0) || (num_sectors > 72) ) {
+BX_INFO("floppy: drive>1 || head>1 ...\n");
+        SET_AH(1);
+        set_diskette_ret_status(1);
+        SET_AL(0); // no sectors read
+        SET_CF(); // error occurred
+        return;
+      }
+
+      // see if drive exists
+      if (floppy_drive_exists(drive) == 0) {
+        SET_AH(0x80); // not responding
+        set_diskette_ret_status(0x80);
+        SET_AL(0); // no sectors read
+        SET_CF(); // error occurred
+        return;
+      }
+
+      // see if media in drive, and type is known
+      if (floppy_media_known(drive) == 0) {
+        if (floppy_media_sense(drive) == 0) {
+          SET_AH(0x0C); // Media type not found
+          set_diskette_ret_status(0x0C);
+          SET_AL(0); // no sectors read
+          SET_CF(); // error occurred
+          return;
+        }
+      }
+
+      if (ah == 0x02) {
+        // Read Diskette Sectors
+
+        //-----------------------------------
+        // set up DMA controller for transfer
+        //-----------------------------------
+
+        // es:bx = pointer to where to place information from diskette
+        // port 04: DMA-1 base and current address, channel 2
+        // port 05: DMA-1 base and current count, channel 2
+        page = (ES >> 12);   // upper 4 bits
+        base_es = (ES << 4); // lower 16bits contributed by ES
+        base_address = base_es + BX; // lower 16 bits of address
+                                     // contributed by ES:BX
+        if ( base_address < base_es ) {
+          // in case of carry, adjust page by 1
+          page++;
+        }
+        base_count = (num_sectors * 512) - 1;
+
+        // check for 64K boundary overrun
+        last_addr = base_address + base_count;
+        if (last_addr < base_address) {
+          SET_AH(0x09);
+          set_diskette_ret_status(0x09);
+          SET_AL(0); // no sectors read
+          SET_CF(); // error occurred
+          return;
+        }
+
+        BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
+        outb(0x000a, 0x06);
+
+  BX_DEBUG_INT13_FL("clear flip-flop\n");
+        outb(0x000c, 0x00); // clear flip-flop
+        outb(0x0004, base_address);
+        outb(0x0004, base_address>>8);
+  BX_DEBUG_INT13_FL("clear flip-flop\n");
+        outb(0x000c, 0x00); // clear flip-flop
+        outb(0x0005, base_count);
+        outb(0x0005, base_count>>8);
+
+        // port 0b: DMA-1 Mode Register
+        mode_register = 0x46; // single mode, increment, autoinit disable,
+                              // transfer type=write, channel 2
+  BX_DEBUG_INT13_FL("setting mode register\n");
+        outb(0x000b, mode_register);
+
+  BX_DEBUG_INT13_FL("setting page register\n");
+        // port 81: DMA-1 Page Register, channel 2
+        outb(0x0081, page);
+
+  BX_DEBUG_INT13_FL("unmask chan 2\n");
+        outb(0x000a, 0x02); // unmask channel 2
+
+        BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
+        outb(0x000a, 0x02);
+
+        //--------------------------------------
+        // set up floppy controller for transfer
+        //--------------------------------------
+        floppy_prepare_controller(drive);
+
+        // send read-normal-data command (9 bytes) to controller
+        outb(0x03f5, 0xe6); // e6: read normal data
+        outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
+        outb(0x03f5, track);
+        outb(0x03f5, head);
+        outb(0x03f5, sector);
+        outb(0x03f5, 2); // 512 byte sector size
+        outb(0x03f5, sector + num_sectors - 1); // last sector to read on track
+        outb(0x03f5, 0); // Gap length
+        outb(0x03f5, 0xff); // Gap length
+
+        // turn on interrupts
+  ASM_START
+        sti
+  ASM_END
+
+        // wait on 40:3e bit 7 to become 1
+        do {
+          val8 = read_byte(0x0040, 0x0040);
+          if (val8 == 0) {
+            floppy_reset_controller();
+            SET_AH(0x80); // drive not ready (timeout)
+            set_diskette_ret_status(0x80);
+            SET_AL(0); // no sectors read
+            SET_CF(); // error occurred
+            return;
+          }
+          val8 = (read_byte(0x0040, 0x003e) & 0x80);
+        } while ( val8 == 0 );
+
+        val8 = 0; // separate asm from while() loop
+        // turn off interrupts
+  ASM_START
+        cli
+  ASM_END
+
+        // set 40:3e bit 7 to 0
+        val8 = read_byte(0x0040, 0x003e);
+        val8 &= 0x7f;
+        write_byte(0x0040, 0x003e, val8);
+
+        // check port 3f4 for accessibility to status bytes
+        val8 = inb(0x3f4);
+        if ( (val8 & 0xc0) != 0xc0 )
+          BX_PANIC("int13_diskette: ctrl not ready\n");
+
+        // read 7 return status bytes from controller
+        // using loop index broken, have to unroll...
+        return_status[0] = inb(0x3f5);
+        return_status[1] = inb(0x3f5);
+        return_status[2] = inb(0x3f5);
+        return_status[3] = inb(0x3f5);
+        return_status[4] = inb(0x3f5);
+        return_status[5] = inb(0x3f5);
+        return_status[6] = inb(0x3f5);
+        // record in BIOS Data Area
+        write_byte(0x0040, 0x0042, return_status[0]);
+        write_byte(0x0040, 0x0043, return_status[1]);
+        write_byte(0x0040, 0x0044, return_status[2]);
+        write_byte(0x0040, 0x0045, return_status[3]);
+        write_byte(0x0040, 0x0046, return_status[4]);
+        write_byte(0x0040, 0x0047, return_status[5]);
+        write_byte(0x0040, 0x0048, return_status[6]);
+
+        if ( (return_status[0] & 0xc0) != 0 ) {
+          SET_AH(0x20);
+          set_diskette_ret_status(0x20);
+          SET_AL(0); // no sectors read
+          SET_CF(); // error occurred
+          return;
+        }
+
+        // ??? should track be new val from return_status[3] ?
+        set_diskette_current_cyl(drive, track);
+        // AL = number of sectors read (same value as passed)
+        SET_AH(0x00); // success
+        CLEAR_CF();   // success
+        return;
+      } else if (ah == 0x03) {
+        // Write Diskette Sectors
+
+        //-----------------------------------
+        // set up DMA controller for transfer
+        //-----------------------------------
+
+        // es:bx = pointer to where to place information from diskette
+        // port 04: DMA-1 base and current address, channel 2
+        // port 05: DMA-1 base and current count, channel 2
+        page = (ES >> 12);   // upper 4 bits
+        base_es = (ES << 4); // lower 16bits contributed by ES
+        base_address = base_es + BX; // lower 16 bits of address
+                                     // contributed by ES:BX
+        if ( base_address < base_es ) {
+          // in case of carry, adjust page by 1
+          page++;
+        }
+        base_count = (num_sectors * 512) - 1;
+
+        // check for 64K boundary overrun
+        last_addr = base_address + base_count;
+        if (last_addr < base_address) {
+          SET_AH(0x09);
+          set_diskette_ret_status(0x09);
+          SET_AL(0); // no sectors read
+          SET_CF(); // error occurred
+          return;
+        }
+
+        BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
+        outb(0x000a, 0x06);
+
+        outb(0x000c, 0x00); // clear flip-flop
+        outb(0x0004, base_address);
+        outb(0x0004, base_address>>8);
+        outb(0x000c, 0x00); // clear flip-flop
+        outb(0x0005, base_count);
+        outb(0x0005, base_count>>8);
+
+        // port 0b: DMA-1 Mode Register
+        mode_register = 0x4a; // single mode, increment, autoinit disable,
+                              // transfer type=read, channel 2
+        outb(0x000b, mode_register);
+
+        // port 81: DMA-1 Page Register, channel 2
+        outb(0x0081, page);
+
+        BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
+        outb(0x000a, 0x02);
+
+        //--------------------------------------
+        // set up floppy controller for transfer
+        //--------------------------------------
+        floppy_prepare_controller(drive);
+
+        // send write-normal-data command (9 bytes) to controller
+        outb(0x03f5, 0xc5); // c5: write normal data
+        outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
+        outb(0x03f5, track);
+        outb(0x03f5, head);
+        outb(0x03f5, sector);
+        outb(0x03f5, 2); // 512 byte sector size
+        outb(0x03f5, sector + num_sectors - 1); // last sector to write on track
+        outb(0x03f5, 0); // Gap length
+        outb(0x03f5, 0xff); // Gap length
+
+        // turn on interrupts
+  ASM_START
+        sti
+  ASM_END
+
+        // wait on 40:3e bit 7 to become 1
+        do {
+          val8 = read_byte(0x0040, 0x0040);
+          if (val8 == 0) {
+            floppy_reset_controller();
+            SET_AH(0x80); // drive not ready (timeout)
+            set_diskette_ret_status(0x80);
+            SET_AL(0); // no sectors written
+            SET_CF(); // error occurred
+            return;
+          }
+          val8 = (read_byte(0x0040, 0x003e) & 0x80);
+        } while ( val8 == 0 );
+
+        val8 = 0; // separate asm from while() loop
+        // turn off interrupts
+  ASM_START
+        cli
+  ASM_END
+
+        // set 40:3e bit 7 to 0
+        val8 = read_byte(0x0040, 0x003e);
+        val8 &= 0x7f;
+        write_byte(0x0040, 0x003e, val8);
+
+        // check port 3f4 for accessibility to status bytes
+        val8 = inb(0x3f4);
+        if ( (val8 & 0xc0) != 0xc0 )
+          BX_PANIC("int13_diskette: ctrl not ready\n");
+
+        // read 7 return status bytes from controller
+        // using loop index broken, have to unroll...
+        return_status[0] = inb(0x3f5);
+        return_status[1] = inb(0x3f5);
+        return_status[2] = inb(0x3f5);
+        return_status[3] = inb(0x3f5);
+        return_status[4] = inb(0x3f5);
+        return_status[5] = inb(0x3f5);
+        return_status[6] = inb(0x3f5);
+        // record in BIOS Data Area
+        write_byte(0x0040, 0x0042, return_status[0]);
+        write_byte(0x0040, 0x0043, return_status[1]);
+        write_byte(0x0040, 0x0044, return_status[2]);
+        write_byte(0x0040, 0x0045, return_status[3]);
+        write_byte(0x0040, 0x0046, return_status[4]);
+        write_byte(0x0040, 0x0047, return_status[5]);
+        write_byte(0x0040, 0x0048, return_status[6]);
+
+        if ( (return_status[0] & 0xc0) != 0 ) {
+          if ( (return_status[1] & 0x02) != 0 ) {
+            // diskette not writable.
+            // AH=status code=0x03 (tried to write on write-protected disk)
+            // AL=number of sectors written=0
+            AX = 0x0300;
+            SET_CF();
+            return;
+          } else {
+            BX_PANIC("int13_diskette_function: read error\n");
+          }
+        }
+
+        // ??? should track be new val from return_status[3] ?
+        set_diskette_current_cyl(drive, track);
+        // AL = number of sectors read (same value as passed)
+        SET_AH(0x00); // success
+        CLEAR_CF();   // success
+        return;
+      } else {  // if (ah == 0x04)
+        // Verify Diskette Sectors
+
+        // ??? should track be new val from return_status[3] ?
+        set_diskette_current_cyl(drive, track);
+        // AL = number of sectors verified (same value as passed)
+        CLEAR_CF();   // success
+        SET_AH(0x00); // success
+        return;
+      }
+      break;
+
+    case 0x05: // format diskette track
+BX_DEBUG_INT13_FL("floppy f05\n");
+
+      num_sectors = GET_AL();
+      track       = GET_CH();
+      head        = GET_DH();
+      drive       = GET_ELDL();
+
+      if ((drive > 1) || (head > 1) || (track > 79) ||
+          (num_sectors == 0) || (num_sectors > 18)) {
+        SET_AH(1);
+        set_diskette_ret_status(1);
+        SET_CF(); // error occurred
+      }
+
+      // see if drive exists
+      if (floppy_drive_exists(drive) == 0) {
+        SET_AH(0x80); // drive not responding
+        set_diskette_ret_status(0x80);
+        SET_CF(); // error occurred
+        return;
+      }
+
+      // see if media in drive, and type is known
+      if (floppy_media_known(drive) == 0) {
+        if (floppy_media_sense(drive) == 0) {
+          SET_AH(0x0C); // Media type not found
+          set_diskette_ret_status(0x0C);
+          SET_AL(0); // no sectors read
+          SET_CF(); // error occurred
+          return;
+        }
+      }
+
+      // set up DMA controller for transfer
+      page = (ES >> 12);   // upper 4 bits
+      base_es = (ES << 4); // lower 16bits contributed by ES
+      base_address = base_es + BX; // lower 16 bits of address
+                                   // contributed by ES:BX
+      if ( base_address < base_es ) {
+        // in case of carry, adjust page by 1
+        page++;
+      }
+      base_count = (num_sectors * 4) - 1;
+
+      // check for 64K boundary overrun
+      last_addr = base_address + base_count;
+      if (last_addr < base_address) {
+        SET_AH(0x09);
+        set_diskette_ret_status(0x09);
+        SET_AL(0); // no sectors read
+        SET_CF(); // error occurred
+        return;
+      }
+
+      outb(0x000a, 0x06);
+      outb(0x000c, 0x00); // clear flip-flop
+      outb(0x0004, base_address);
+      outb(0x0004, base_address>>8);
+      outb(0x000c, 0x00); // clear flip-flop
+      outb(0x0005, base_count);
+      outb(0x0005, base_count>>8);
+      mode_register = 0x4a; // single mode, increment, autoinit disable,
+                            // transfer type=read, channel 2
+      outb(0x000b, mode_register);
+      // port 81: DMA-1 Page Register, channel 2
+      outb(0x0081, page);
+      outb(0x000a, 0x02);
+
+      // set up floppy controller for transfer
+      floppy_prepare_controller(drive);
+
+      // send format-track command (6 bytes) to controller
+      outb(0x03f5, 0x4d); // 4d: format track
+      outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
+      outb(0x03f5, 2); // 512 byte sector size
+      outb(0x03f5, num_sectors); // number of sectors per track
+      outb(0x03f5, 0); // Gap length
+      outb(0x03f5, 0xf6); // Fill byte
+      // turn on interrupts
+  ASM_START
+      sti
+  ASM_END
+
+      // wait on 40:3e bit 7 to become 1
+      do {
+        val8 = read_byte(0x0040, 0x0040);
+        if (val8 == 0) {
+          floppy_reset_controller();
+          SET_AH(0x80); // drive not ready (timeout)
+          set_diskette_ret_status(0x80);
+          SET_CF(); // error occurred
+          return;
+        }
+        val8 = (read_byte(0x0040, 0x003e) & 0x80);
+      } while ( val8 == 0 );
+
+      val8 = 0; // separate asm from while() loop
+      // turn off interrupts
+  ASM_START
+      cli
+  ASM_END
+      // set 40:3e bit 7 to 0
+      val8 = read_byte(0x0040, 0x003e);
+      val8 &= 0x7f;
+      write_byte(0x0040, 0x003e, val8);
+      // check port 3f4 for accessibility to status bytes
+      val8 = inb(0x3f4);
+      if ( (val8 & 0xc0) != 0xc0 )
+        BX_PANIC("int13_diskette: ctrl not ready\n");
+
+      // read 7 return status bytes from controller
+      // using loop index broken, have to unroll...
+      return_status[0] = inb(0x3f5);
+      return_status[1] = inb(0x3f5);
+      return_status[2] = inb(0x3f5);
+      return_status[3] = inb(0x3f5);
+      return_status[4] = inb(0x3f5);
+      return_status[5] = inb(0x3f5);
+      return_status[6] = inb(0x3f5);
+      // record in BIOS Data Area
+      write_byte(0x0040, 0x0042, return_status[0]);
+      write_byte(0x0040, 0x0043, return_status[1]);
+      write_byte(0x0040, 0x0044, return_status[2]);
+      write_byte(0x0040, 0x0045, return_status[3]);
+      write_byte(0x0040, 0x0046, return_status[4]);
+      write_byte(0x0040, 0x0047, return_status[5]);
+      write_byte(0x0040, 0x0048, return_status[6]);
+
+      if ( (return_status[0] & 0xc0) != 0 ) {
+        if ( (return_status[1] & 0x02) != 0 ) {
+          // diskette not writable.
+          // AH=status code=0x03 (tried to write on write-protected disk)
+          // AL=number of sectors written=0
+          AX = 0x0300;
+          SET_CF();
+          return;
+        } else {
+          BX_PANIC("int13_diskette_function: write error\n");
+        }
+      }
+
+      SET_AH(0);
+      set_diskette_ret_status(0);
+      set_diskette_current_cyl(drive, 0);
+      CLEAR_CF(); // successful
+      return;
+
+
+    case 0x08: // read diskette drive parameters
+BX_DEBUG_INT13_FL("floppy f08\n");
+      drive = GET_ELDL();
+
+      if (drive > 1) {
+        AX = 0;
+        BX = 0;
+        CX = 0;
+        DX = 0;
+        ES = 0;
+        DI = 0;
+        SET_DL(num_floppies);
+        SET_CF();
+        return;
+        }
+
+      drive_type = inb_cmos(0x10);
+      num_floppies = 0;
+      if (drive_type & 0xf0)
+        num_floppies++;
+      if (drive_type & 0x0f)
+        num_floppies++;
+
+      if (drive == 0)
+        drive_type >>= 4;
+      else
+        drive_type &= 0x0f;
+
+      SET_BH(0);
+      SET_BL(drive_type);
+      SET_AH(0);
+      SET_AL(0);
+      SET_DL(num_floppies);
+
+      switch (drive_type) {
+        case 0: // none
+          CX = 0;
+          SET_DH(0); // max head #
+          break;
+
+        case 1: // 360KB, 5.25"
+          CX = 0x2709; // 40 tracks, 9 sectors
+          SET_DH(1); // max head #
+          break;
+
+        case 2: // 1.2MB, 5.25"
+          CX = 0x4f0f; // 80 tracks, 15 sectors
+          SET_DH(1); // max head #
+          break;
+
+        case 3: // 720KB, 3.5"
+          CX = 0x4f09; // 80 tracks, 9 sectors
+          SET_DH(1); // max head #
+          break;
+
+        case 4: // 1.44MB, 3.5"
+          CX = 0x4f12; // 80 tracks, 18 sectors
+          SET_DH(1); // max head #
+          break;
+
+        case 5: // 2.88MB, 3.5"
+          CX = 0x4f24; // 80 tracks, 36 sectors
+          SET_DH(1); // max head #
+          break;
+
+        case 6: // 160k, 5.25"
+          CX = 0x2708; // 40 tracks, 8 sectors
+          SET_DH(0); // max head #
+          break;
+
+        case 7: // 180k, 5.25"
+          CX = 0x2709; // 40 tracks, 9 sectors
+          SET_DH(0); // max head #
+          break;
+
+        case 8: // 320k, 5.25"
+          CX = 0x2708; // 40 tracks, 8 sectors
+          SET_DH(1); // max head #
+          break;
+
+        default: // ?
+          BX_PANIC("floppy: int13: bad floppy type\n");
+        }
+
+      /* set es & di to point to 11 byte diskette param table in ROM */
+ASM_START
+      push bp
+      mov  bp, sp
+      mov ax, #diskette_param_table2
+      mov _int13_diskette_function.DI+2[bp], ax
+      mov _int13_diskette_function.ES+2[bp], cs
+      pop  bp
+ASM_END
+      CLEAR_CF(); // success
+      /* disk status not changed upon success */
+      return;
+
+
+    case 0x15: // read diskette drive type
+BX_DEBUG_INT13_FL("floppy f15\n");
+      drive = GET_ELDL();
+      if (drive > 1) {
+        SET_AH(0); // only 2 drives supported
+        // set_diskette_ret_status here ???
+        SET_CF();
+        return;
+        }
+      drive_type = inb_cmos(0x10);
+
+      if (drive == 0)
+        drive_type >>= 4;
+      else
+        drive_type &= 0x0f;
+      CLEAR_CF(); // successful, not present
+      if (drive_type==0) {
+        SET_AH(0); // drive not present
+        }
+      else {
+        SET_AH(1); // drive present, does not support change line
+        }
+
+      return;
+
+    case 0x16: // get diskette change line status
+BX_DEBUG_INT13_FL("floppy f16\n");
+      drive = GET_ELDL();
+      if (drive > 1) {
+        SET_AH(0x01); // invalid drive
+        set_diskette_ret_status(0x01);
+        SET_CF();
+        return;
+        }
+
+      SET_AH(0x06); // change line not supported
+      set_diskette_ret_status(0x06);
+      SET_CF();
+      return;
+
+    case 0x17: // set diskette type for format(old)
+BX_DEBUG_INT13_FL("floppy f17\n");
+      /* not used for 1.44M floppies */
+      SET_AH(0x01); // not supported
+      set_diskette_ret_status(1); /* not supported */
+      SET_CF();
+      return;
+
+    case 0x18: // set diskette type for format(new)
+BX_DEBUG_INT13_FL("floppy f18\n");
+      SET_AH(0x01); // do later
+      set_diskette_ret_status(1);
+      SET_CF();
+      return;
+
+    default:
+        BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
+
+      // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
+        SET_AH(0x01); // ???
+        set_diskette_ret_status(1);
+        SET_CF();
+        return;
+      //   }
+    }
+}
+#else  // #if BX_SUPPORT_FLOPPY
+  void
+int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
+  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
+{
+  Bit8u  val8;
+
+  switch ( GET_AH() ) {
+
+    case 0x01: // Read Diskette Status
+      CLEAR_CF();
+      val8 = read_byte(0x0000, 0x0441);
+      SET_AH(val8);
+      if (val8) {
+        SET_CF();
+        }
+      return;
+
+    default:
+      SET_CF();
+      write_byte(0x0000, 0x0441, 0x01);
+      SET_AH(0x01);
+    }
+}
+#endif  // #if BX_SUPPORT_FLOPPY
+
+ void
+set_diskette_ret_status(value)
+  Bit8u value;
+{
+  write_byte(0x0040, 0x0041, value);
+}
+
+  void
+set_diskette_current_cyl(drive, cyl)
+  Bit8u drive;
+  Bit8u cyl;
+{
+  if (drive > 1)
+    BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
+  write_byte(0x0040, 0x0094+drive, cyl);
+}
+
+  void
+determine_floppy_media(drive)
+  Bit16u drive;
+{
+#if 0
+  Bit8u  val8, DOR, ctrl_info;
+
+  ctrl_info = read_byte(0x0040, 0x008F);
+  if (drive==1)
+    ctrl_info >>= 4;
+  else
+    ctrl_info &= 0x0f;
+
+#if 0
+  if (drive == 0) {
+    DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
+    }
+  else {
+    DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
+    }
+#endif
+
+  if ( (ctrl_info & 0x04) != 0x04 ) {
+    // Drive not determined means no drive exists, done.
+    return;
+    }
+
+#if 0
+  // check Main Status Register for readiness
+  val8 = inb(0x03f4) & 0x80; // Main Status Register
+  if (val8 != 0x80)
+    BX_PANIC("d_f_m: MRQ bit not set\n");
+
+  // change line
+
+  // existing BDA values
+
+  // turn on drive motor
+  outb(0x03f2, DOR); // Digital Output Register
+  //
+#endif
+  BX_PANIC("d_f_m: OK so far\n");
+#endif
+}
+
+  void
+int17_function(regs, ds, iret_addr)
+  pusha_regs_t regs; // regs pushed from PUSHA instruction
+  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
+  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
+{
+  Bit16u addr,timeout;
+  Bit8u val8;
+
+  ASM_START
+  sti
+  ASM_END
+
+  addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
+  if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
+    timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
+    if (regs.u.r8.ah == 0) {
+      outb(addr, regs.u.r8.al);
+      val8 = inb(addr+2);
+      outb(addr+2, val8 | 0x01); // send strobe
+      ASM_START
+      nop
+      ASM_END
+      outb(addr+2, val8 & ~0x01);
+      while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
+        timeout--;
+      }
+    }
+    if (regs.u.r8.ah == 1) {
+      val8 = inb(addr+2);
+      outb(addr+2, val8 & ~0x04); // send init
+      ASM_START
+      nop
+      ASM_END
+      outb(addr+2, val8 | 0x04);
+    }
+    val8 = inb(addr+1);
+    regs.u.r8.ah = (val8 ^ 0x48);
+    if (!timeout) regs.u.r8.ah |= 0x01;
+    ClearCF(iret_addr.flags);
+  } else {
+    SetCF(iret_addr.flags); // Unsupported
+  }
+}
+
+// returns bootsegment in ax, drive in bl
+  Bit32u 
+int19_function(bseqnr)
+Bit8u bseqnr;
+{
+  Bit16u ebda_seg=read_word(0x0040,0x000E);
+  Bit16u bootseq;
+  Bit8u  bootdrv;
+  Bit8u  bootcd;
+  Bit8u  bootchk;
+  Bit16u bootseg;
+  Bit16u status;
+  Bit8u  lastdrive=0;
+
+  // if BX_ELTORITO_BOOT is not defined, old behavior
+  //   check bit 5 in CMOS reg 0x2d.  load either 0x00 or 0x80 into DL
+  //   in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
+  //     0: system boot sequence, first drive C: then A:
+  //     1: system boot sequence, first drive A: then C:
+  // else BX_ELTORITO_BOOT is defined
+  //   CMOS regs 0x3D and 0x38 contain the boot sequence:
+  //     CMOS reg 0x3D & 0x0f : 1st boot device
+  //     CMOS reg 0x3D & 0xf0 : 2nd boot device
+  //     CMOS reg 0x38 & 0xf0 : 3rd boot device
+  //   boot device codes:
+  //     0x00 : not defined
+  //     0x01 : first floppy 
+  //     0x02 : first harddrive
+  //     0x03 : first cdrom
+  //     else : boot failure
+
+  // Get the boot sequence
+#if BX_ELTORITO_BOOT
+  bootseq=inb_cmos(0x3d);
+  bootseq|=((inb_cmos(0x38) & 0xf0) << 4);
+
+  if (bseqnr==2) bootseq >>= 4;
+  if (bseqnr==3) bootseq >>= 8;
+  if (bootseq<0x10) lastdrive = 1;
+  bootdrv=0x00; bootcd=0;
+  switch(bootseq & 0x0f) {
+    case 0x01: bootdrv=0x00; bootcd=0; break;
+    case 0x02: bootdrv=0x80; bootcd=0; break;
+    case 0x03: bootdrv=0x00; bootcd=1; break;
+    default:   return 0x00000000;
+    }
+#else
+  bootseq=inb_cmos(0x2d);
+
+  if (bseqnr==2) {
+    bootseq ^= 0x20;
+    lastdrive = 1;
+  }
+  bootdrv=0x00; bootcd=0;
+  if((bootseq&0x20)==0) bootdrv=0x80;
+#endif // BX_ELTORITO_BOOT
+
+#if BX_ELTORITO_BOOT
+  // We have to boot from cd
+  if (bootcd != 0) {
+    status = cdrom_boot();
+
+    // If failure
+    if ( (status & 0x00ff) !=0 ) {
+      print_cdromboot_failure(status);
+      print_boot_failure(bootcd, bootdrv, 1, lastdrive);
+      return 0x00000000;
+      }
+
+    bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
+    bootdrv = (Bit8u)(status>>8);
+    }
+
+#endif // BX_ELTORITO_BOOT
+
+  // We have to boot from harddisk or floppy
+  if (bootcd == 0) {
+    bootseg=0x07c0;
+
+ASM_START
+    push bp
+    mov  bp, sp
+
+    mov  ax, #0x0000
+    mov  _int19_function.status + 2[bp], ax
+    mov  dl, _int19_function.bootdrv + 2[bp]
+    mov  ax, _int19_function.bootseg + 2[bp]
+    mov  es, ax         ;; segment
+    mov  bx, #0x0000    ;; offset
+    mov  ah, #0x02      ;; function 2, read diskette sector
+    mov  al, #0x01      ;; read 1 sector
+    mov  ch, #0x00      ;; track 0
+    mov  cl, #0x01      ;; sector 1
+    mov  dh, #0x00      ;; head 0
+    int  #0x13          ;; read sector
+    jnc  int19_load_done
+    mov  ax, #0x0001
+    mov  _int19_function.status + 2[bp], ax
+
+int19_load_done:
+    pop  bp
+ASM_END
+    
+    if (status != 0) {
+      print_boot_failure(bootcd, bootdrv, 1, lastdrive);
+      return 0x00000000;
+      }
+    }
+
+  // check signature if instructed by cmos reg 0x38, only for floppy
+  // bootchk = 1 : signature check disabled
+  // bootchk = 0 : signature check enabled
+  if (bootdrv != 0) bootchk = 0;
+  else bootchk = inb_cmos(0x38) & 0x01;
+
+#if BX_ELTORITO_BOOT
+  // if boot from cd, no signature check
+  if (bootcd != 0)
+    bootchk = 1;
+#endif // BX_ELTORITO_BOOT
+
+  if (bootchk == 0) {
+    if (read_word(bootseg,0x1fe) != 0xaa55) {
+      print_boot_failure(bootcd, bootdrv, 0, lastdrive);
+      return 0x00000000;
+      }
+    }
+  
+#if BX_ELTORITO_BOOT
+  // Print out the boot string
+  print_boot_device(bootcd, bootdrv);
+#else // BX_ELTORITO_BOOT
+  print_boot_device(0, bootdrv);
+#endif // BX_ELTORITO_BOOT
+
+  // return the boot segment
+  return (((Bit32u)bootdrv) << 16) + bootseg;
+}
+
+  void
+int1a_function(regs, ds, iret_addr)
+  pusha_regs_t regs; // regs pushed from PUSHA instruction
+  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
+  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
+{
+  Bit8u val8;
+
+  BX_DEBUG_INT1A("int1a: AX=%04x BX=%04x CX=%04x DX=%04x DS=%04x\n", regs.u.r16.ax, regs.u.r16.bx, regs.u.r16.cx, regs.u.r16.dx, ds);
+
+  ASM_START
+  sti
+  ASM_END
+
+  switch (regs.u.r8.ah) {
+    case 0: // get current clock count
+      ASM_START
+      cli
+      ASM_END
+      regs.u.r16.cx = BiosData->ticks_high;
+      regs.u.r16.dx = BiosData->ticks_low;
+      regs.u.r8.al  = BiosData->midnight_flag;
+      BiosData->midnight_flag = 0; // reset flag
+      ASM_START
+      sti
+      ASM_END
+      // AH already 0
+      ClearCF(iret_addr.flags); // OK
+      break;
+
+    case 1: // Set Current Clock Count
+      ASM_START
+      cli
+      ASM_END
+      BiosData->ticks_high = regs.u.r16.cx;
+      BiosData->ticks_low  = regs.u.r16.dx;
+      BiosData->midnight_flag = 0; // reset flag
+      ASM_START
+      sti
+      ASM_END
+      regs.u.r8.ah = 0;
+      ClearCF(iret_addr.flags); // OK
+      break;
+
+
+    case 2: // Read CMOS Time
+      if (rtc_updating()) {
+        SetCF(iret_addr.flags);
+        break;
+        }
+
+      regs.u.r8.dh = inb_cmos(0x00); // Seconds
+      regs.u.r8.cl = inb_cmos(0x02); // Minutes
+      regs.u.r8.ch = inb_cmos(0x04); // Hours
+      regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
+      regs.u.r8.ah = 0;
+      regs.u.r8.al = regs.u.r8.ch;
+      ClearCF(iret_addr.flags); // OK
+      break;
+
+    case 3: // Set CMOS Time
+      // Using a debugger, I notice the following masking/setting
+      // of bits in Status Register B, by setting Reg B to
+      // a few values and getting its value after INT 1A was called.
+      //
+      //        try#1       try#2       try#3
+      // before 1111 1101   0111 1101   0000 0000
+      // after  0110 0010   0110 0010   0000 0010
+      //
+      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
+      // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
+      if (rtc_updating()) {
+        init_rtc();
+        // fall through as if an update were not in progress
+        }
+      outb_cmos(0x00, regs.u.r8.dh); // Seconds
+      outb_cmos(0x02, regs.u.r8.cl); // Minutes
+      outb_cmos(0x04, regs.u.r8.ch); // Hours
+      // Set Daylight Savings time enabled bit to requested value
+      val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
+      // (reg B already selected)
+      outb_cmos(0x0b, val8);
+      regs.u.r8.ah = 0;
+      regs.u.r8.al = val8; // val last written to Reg B
+      ClearCF(iret_addr.flags); // OK
+      break;
+
+    case 4: // Read CMOS Date
+      regs.u.r8.ah = 0;
+      if (rtc_updating()) {
+        SetCF(iret_addr.flags);
+        break;
+        }
+      regs.u.r8.cl = inb_cmos(0x09); // Year
+      regs.u.r8.dh = inb_cmos(0x08); // Month
+      regs.u.r8.dl = inb_cmos(0x07); // Day of Month
+      regs.u.r8.ch = inb_cmos(0x32); // Century
+      regs.u.r8.al = regs.u.r8.ch;
+      ClearCF(iret_addr.flags); // OK
+      break;
+
+    case 5: // Set CMOS Date
+      // Using a debugger, I notice the following masking/setting
+      // of bits in Status Register B, by setting Reg B to
+      // a few values and getting its value after INT 1A was called.
+      //
+      //        try#1       try#2       try#3       try#4
+      // before 1111 1101   0111 1101   0000 0010   0000 0000
+      // after  0110 1101   0111 1101   0000 0010   0000 0000
+      //
+      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
+      // My assumption: RegB = (RegB & 01111111b)
+      if (rtc_updating()) {
+        init_rtc();
+        SetCF(iret_addr.flags);
+        break;
+        }
+      outb_cmos(0x09, regs.u.r8.cl); // Year
+      outb_cmos(0x08, regs.u.r8.dh); // Month
+      outb_cmos(0x07, regs.u.r8.dl); // Day of Month
+      outb_cmos(0x32, regs.u.r8.ch); // Century
+      val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
+      outb_cmos(0x0b, val8);
+      regs.u.r8.ah = 0;
+      regs.u.r8.al = val8; // AL = val last written to Reg B
+      ClearCF(iret_addr.flags); // OK
+      break;
+
+    case 6: // Set Alarm Time in CMOS
+      // Using a debugger, I notice the following masking/setting
+      // of bits in Status Register B, by setting Reg B to
+      // a few values and getting its value after INT 1A was called.
+      //
+      //        try#1       try#2       try#3
+      // before 1101 1111   0101 1111   0000 0000
+      // after  0110 1111   0111 1111   0010 0000
+      //
+      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
+      // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
+      val8 = inb_cmos(0x0b); // Get Status Reg B
+      regs.u.r16.ax = 0;
+      if (val8 & 0x20) {
+        // Alarm interrupt enabled already
+        SetCF(iret_addr.flags); // Error: alarm in use
+        break;
+        }
+      if (rtc_updating()) {
+        init_rtc();
+        // fall through as if an update were not in progress
+        }
+      outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
+      outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
+      outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
+      outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
+      // enable Status Reg B alarm bit, clear halt clock bit
+      outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
+      ClearCF(iret_addr.flags); // OK
+      break;
+
+    case 7: // Turn off Alarm
+      // Using a debugger, I notice the following masking/setting
+      // of bits in Status Register B, by setting Reg B to
+      // a few values and getting its value after INT 1A was called.
+      //
+      //        try#1       try#2       try#3       try#4
+      // before 1111 1101   0111 1101   0010 0000   0010 0010
+      // after  0100 0101   0101 0101   0000 0000   0000 0010
+      //
+      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
+      // My assumption: RegB = (RegB & 01010111b)
+      val8 = inb_cmos(0x0b); // Get Status Reg B
+      // clear clock-halt bit, disable alarm bit
+      outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
+      regs.u.r8.ah = 0;
+      regs.u.r8.al = val8; // val last written to Reg B
+      ClearCF(iret_addr.flags); // OK
+      break;
+#if BX_PCIBIOS
+    case 0xb1:
+      // real mode PCI BIOS functions now handled in assembler code
+      // this C code handles the error code for information only
+      if (regs.u.r8.bl == 0xff) {
+        BX_INFO("PCI BIOS: PCI not present\n");
+      } else if (regs.u.r8.bl == 0x81) {
+        BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
+      } else if (regs.u.r8.bl == 0x83) {
+        BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
+      } else if (regs.u.r8.bl == 0x86) {
+        BX_INFO("PCI device %04x:%04x not found at index %d\n", regs.u.r16.dx, regs.u.r16.cx, regs.u.r16.si);
+      }
+      regs.u.r8.ah = regs.u.r8.bl;
+      SetCF(iret_addr.flags);
+      break;
+#endif
+
+    default:
+      SetCF(iret_addr.flags); // Unsupported
+    }
+}
+
+  void
+int70_function(regs, ds, iret_addr)
+  pusha_regs_t regs; // regs pushed from PUSHA instruction
+  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
+  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
+{
+  // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
+  Bit8u registerB = 0, registerC = 0;
+
+  // Check which modes are enabled and have occurred.
+  registerB = inb_cmos( 0xB );
+  registerC = inb_cmos( 0xC );
+
+  if( ( registerB & 0x60 ) != 0 ) {
+    if( ( registerC & 0x20 ) != 0 ) {
+      // Handle Alarm Interrupt.
+ASM_START
+      sti
+      int #0x4a
+      cli
+ASM_END
+    }
+    if( ( registerC & 0x40 ) != 0 ) {
+      // Handle Periodic Interrupt.
+
+      if( read_byte( 0x40, 0xA0 ) != 0 ) {
+        // Wait Interval (Int 15, AH=83) active.
+        Bit32u time, toggle;
+
+        time = read_dword( 0x40, 0x9C );  // Time left in microseconds.
+        if( time < 0x3D1 ) {
+          // Done waiting.
+          Bit16u segment, offset;
+
+          segment = read_word( 0x40, 0x98 );
+          offset = read_word( 0x40, 0x9A );
+          write_byte( 0x40, 0xA0, 0 );  // Turn of status byte.
+          outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
+          write_byte(segment, offset, read_byte(segment, offset) | 0x80 );  // Write to specified flag byte.
+        } else {
+          // Continue waiting.
+          time -= 0x3D1;
+          write_dword( 0x40, 0x9C, time );
+        }
+      }
+    }
+  }
+
+ASM_START
+  call eoi_both_pics
+ASM_END
+}
+
+
+ASM_START
+;------------------------------------------
+;- INT74h : PS/2 mouse hardware interrupt -
+;------------------------------------------
+int74_handler:
+  sti
+  pusha
+  push ds         ;; save DS
+  push #0x00 ;; placeholder for status
+  push #0x00 ;; placeholder for X
+  push #0x00 ;; placeholder for Y
+  push #0x00 ;; placeholder for Z
+  push #0x00 ;; placeholder for make_far_call boolean
+  call _int74_function
+  pop  cx      ;; remove make_far_call from stack
+  jcxz int74_done
+
+  ;; make far call to EBDA:0022
+  push #0x00
+  pop ds
+  push 0x040E     ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
+  pop ds
+  //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
+  call far ptr[0x22]
+int74_done:
+  cli
+  call eoi_both_pics
+  add sp, #8     ;; pop status, x, y, z
+
+  pop ds          ;; restore DS
+  popa
+  iret
+
+
+;; This will perform an IRET, but will retain value of current CF
+;; by altering flags on stack.  Better than RETF #02.
+iret_modify_cf:
+  jc   carry_set
+  push bp
+  mov  bp, sp
+  and  BYTE [bp + 0x06], #0xfe
+  pop  bp
+  iret
+carry_set:
+  push bp
+  mov  bp, sp
+  or   BYTE [bp + 0x06], #0x01
+  pop  bp
+  iret
+
+
+;----------------------
+;- INT13h (relocated) -
+;----------------------
+;
+; int13_relocated is a little bit messed up since I played with it
+; I have to rewrite it:
+;   - call a function that detect which function to call
+;   - make all called C function get the same parameters list
+;
+int13_relocated:
+
+#if BX_ELTORITO_BOOT
+  ;; check for an eltorito function
+  cmp   ah,#0x4a
+  jb    int13_not_eltorito
+  cmp   ah,#0x4d
+  ja    int13_not_eltorito
+
+  pusha
+  push  es
+  push  ds
+  push  ss
+  pop   ds
+
+  push  #int13_out
+  jmp   _int13_eltorito      ;; ELDX not used
+
+int13_not_eltorito:
+  push  ax
+  push  bx
+  push  cx
+  push  dx
+
+  ;; check if emulation active
+  call  _cdemu_isactive
+  cmp   al,#0x00
+  je    int13_cdemu_inactive
+
+  ;; check if access to the emulated drive
+  call  _cdemu_emulated_drive
+  pop   dx
+  push  dx
+  cmp   al,dl                ;; int13 on emulated drive
+  jne   int13_nocdemu
+
+  pop   dx
+  pop   cx
+  pop   bx
+  pop   ax
+
+  pusha
+  push  es
+  push  ds
+  push  ss
+  pop   ds
+
+  push  #int13_out
+  jmp   _int13_cdemu         ;; ELDX not used
+
+int13_nocdemu:
+  and   dl,#0xE0             ;; mask to get device class, including cdroms
+  cmp   al,dl                ;; al is 0x00 or 0x80
+  jne   int13_cdemu_inactive ;; inactive for device class
+
+  pop   dx
+  pop   cx
+  pop   bx
+  pop   ax
+
+  push  ax
+  push  cx
+  push  dx
+  push  bx
+
+  dec   dl                   ;; real drive is dl - 1
+  jmp   int13_legacy
+
+int13_cdemu_inactive:
+  pop   dx
+  pop   cx
+  pop   bx
+  pop   ax
+
+#endif // BX_ELTORITO_BOOT
+
+int13_noeltorito:
+
+  push  ax
+  push  cx
+  push  dx
+  push  bx
+
+int13_legacy:
+
+  push  dx                   ;; push eltorito value of dx instead of sp
+
+  push  bp
+  push  si
+  push  di
+
+  push  es
+  push  ds
+  push  ss
+  pop   ds
+
+  ;; now the 16-bit registers can be restored with:
+  ;; pop ds; pop es; popa; iret
+  ;; arguments passed to functions should be
+  ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
+
+  test  dl, #0x80
+  jnz   int13_notfloppy
+
+  push #int13_out
+  jmp _int13_diskette_function
+
+int13_notfloppy:
+
+#if BX_USE_ATADRV
+
+  cmp   dl, #0xE0
+  jb    int13_notcdrom
+
+  // ebx is modified: BSD 5.2.1 boot loader problem
+  // someone should figure out which 32 bit register that actually are used
+
+  shr   ebx, #16
+  push  bx
+
+  call  _int13_cdrom
+
+  pop   bx
+  shl   ebx, #16
+
+  jmp int13_out
+
+int13_notcdrom:
+
+#endif
+
+int13_disk:
+  call  _int13_harddisk
+
+int13_out:
+  pop ds
+  pop es
+  popa
+  iret 
+
+
+;----------
+;- INT18h -
+;----------
+int18_handler: ;; Boot Failure routing
+  call _int18_panic_msg
+  hlt
+  iret
+
+;----------
+;- INT19h -
+;----------
+int19_relocated: ;; Boot function, relocated
+
+  ;; int19 was beginning to be really complex, so now it
+  ;; just calls an C function, that does the work
+  ;; it returns in BL the boot drive, and in AX the boot segment
+  ;; the boot segment will be 0x0000 if something has failed
+
+  push bp
+  mov  bp, sp
+
+  ;; drop ds
+  xor  ax, ax
+  mov  ds, ax
+
+  ;; 1st boot device
+  mov  ax, #0x0001
+  push ax
+  call _int19_function
+  inc  sp
+  inc  sp
+  ;; bl contains the boot drive
+  ;; ax contains the boot segment or 0 if failure
+
+  test       ax, ax  ;; if ax is 0 try next boot device
+  jnz        boot_setup
+
+  ;; 2nd boot device
+  mov  ax, #0x0002
+  push ax
+  call _int19_function
+  inc  sp
+  inc  sp
+  test       ax, ax  ;; if ax is 0 try next boot device
+  jnz        boot_setup
+
+  ;; 3rd boot device
+  mov  ax, #0x0003
+  push ax
+  call _int19_function
+  inc  sp
+  inc  sp
+  test       ax, ax  ;; if ax is 0 call int18
+  jz         int18_handler
+
+boot_setup:
+  mov dl,    bl      ;; set drive so guest os find it
+  shl eax,   #0x04   ;; convert seg to ip
+  mov 2[bp], ax      ;; set ip
+
+  shr eax,   #0x04   ;; get cs back
+  and ax,    #0xF000 ;; remove what went in ip
+  mov 4[bp], ax      ;; set cs
+  xor ax,    ax
+  mov es,    ax      ;; set es to zero fixes [ 549815 ]
+  mov [bp],  ax      ;; set bp to zero
+  mov ax,    #0xaa55 ;; set ok flag
+
+  pop bp
+  iret               ;; Beam me up Scotty
+
+;----------
+;- INT1Ch -
+;----------
+int1c_handler: ;; User Timer Tick
+  iret
+
+
+;----------------------
+;- POST: Floppy Drive -
+;----------------------
+floppy_drive_post:
+  mov  ax, #0x0000
+  mov  ds, ax
+
+  mov  al, #0x00
+  mov  0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
+
+  mov  0x043f, al  ;; diskette motor status: read op, drive0, motors off
+
+  mov  0x0440, al  ;; diskette motor timeout counter: not active
+  mov  0x0441, al  ;; diskette controller status return code
+
+  mov  0x0442, al  ;; disk & diskette controller status register 0
+  mov  0x0443, al  ;; diskette controller status register 1
+  mov  0x0444, al  ;; diskette controller status register 2
+  mov  0x0445, al  ;; diskette controller cylinder number
+  mov  0x0446, al  ;; diskette controller head number
+  mov  0x0447, al  ;; diskette controller sector number
+  mov  0x0448, al  ;; diskette controller bytes written
+
+  mov  0x048b, al  ;; diskette configuration data
+
+  ;; -----------------------------------------------------------------
+  ;; (048F) diskette controller information
+  ;;
+  mov  al, #0x10   ;; get CMOS diskette drive type
+  out  0x70, AL
+  in   AL, 0x71
+  mov  ah, al      ;; save byte to AH
+
+look_drive0:
+  shr  al, #4      ;; look at top 4 bits for drive 0
+  jz   f0_missing  ;; jump if no drive0
+  mov  bl, #0x07   ;; drive0 determined, multi-rate, has changed line
+  jmp  look_drive1
+f0_missing:
+  mov  bl, #0x00   ;; no drive0
+
+look_drive1:
+  mov  al, ah      ;; restore from AH
+  and  al, #0x0f   ;; look at bottom 4 bits for drive 1
+  jz   f1_missing  ;; jump if no drive1
+  or   bl, #0x70   ;; drive1 determined, multi-rate, has changed line
+f1_missing:
+                   ;; leave high bits in BL zerod
+  mov  0x048f, bl  ;; put new val in BDA (diskette controller information)
+  ;; -----------------------------------------------------------------
+
+  mov  al, #0x00
+  mov  0x0490, al  ;; diskette 0 media state
+  mov  0x0491, al  ;; diskette 1 media state
+
+                   ;; diskette 0,1 operational starting state
+                   ;; drive type has not been determined,
+                   ;; has no changed detection line
+  mov  0x0492, al
+  mov  0x0493, al
+
+  mov  0x0494, al  ;; diskette 0 current cylinder
+  mov  0x0495, al  ;; diskette 1 current cylinder
+
+  mov  al, #0x02
+  out  #0x0a, al   ;; clear DMA-1 channel 2 mask bit
+
+  SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
+  SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
+  SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
+
+  ret
+
+
+;--------------------
+;- POST: HARD DRIVE -
+;--------------------
+; relocated here because the primary POST area isnt big enough.
+hard_drive_post:
+  // IRQ 14 = INT 76h
+  // INT 76h calls INT 15h function ax=9100
+
+  mov  al, #0x0a   ; 0000 1010 = reserved, disable IRQ 14
+  mov  dx, #0x03f6
+  out  dx, al
+
+  mov  ax, #0x0000
+  mov  ds, ax
+  mov  0x0474, al /* hard disk status of last operation */
+  mov  0x0477, al /* hard disk port offset (XT only ???) */
+  mov  0x048c, al /* hard disk status register */
+  mov  0x048d, al /* hard disk error register */
+  mov  0x048e, al /* hard disk task complete flag */
+  mov  al, #0x01
+  mov  0x0475, al /* hard disk number attached */
+  mov  al, #0xc0
+  mov  0x0476, al /* hard disk control byte */
+  SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
+  SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
+  ;; INT 41h: hard disk 0 configuration pointer
+  ;; INT 46h: hard disk 1 configuration pointer
+  SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
+  SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
+
+  ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
+  mov  al, #0x12
+  out  #0x70, al
+  in   al, #0x71
+  and  al, #0xf0
+  cmp  al, #0xf0
+  je   post_d0_extended
+  jmp check_for_hd1
+post_d0_extended:
+  mov  al, #0x19
+  out  #0x70, al
+  in   al, #0x71
+  cmp  al, #47  ;; decimal 47 - user definable
+  je   post_d0_type47
+  HALT(__LINE__)
+post_d0_type47:
+  ;; CMOS  purpose                  param table offset
+  ;; 1b    cylinders low            0
+  ;; 1c    cylinders high           1
+  ;; 1d    heads                    2
+  ;; 1e    write pre-comp low       5
+  ;; 1f    write pre-comp high      6
+  ;; 20    retries/bad map/heads>8  8
+  ;; 21    landing zone low         C
+  ;; 22    landing zone high        D
+  ;; 23    sectors/track            E
+
+  mov  ax, #EBDA_SEG
+  mov  ds, ax
+
+  ;;; Filling EBDA table for hard disk 0.
+  mov  al, #0x1f
+  out  #0x70, al
+  in   al, #0x71
+  mov  ah, al
+  mov  al, #0x1e
+  out  #0x70, al
+  in   al, #0x71
+  mov   (0x003d + 0x05), ax ;; write precomp word
+
+  mov  al, #0x20
+  out  #0x70, al
+  in   al, #0x71
+  mov   (0x003d + 0x08), al ;; drive control byte
+
+  mov  al, #0x22
+  out  #0x70, al
+  in   al, #0x71
+  mov  ah, al
+  mov  al, #0x21
+  out  #0x70, al
+  in   al, #0x71
+  mov   (0x003d + 0x0C), ax ;; landing zone word
+
+  mov  al, #0x1c   ;; get cylinders word in AX
+  out  #0x70, al
+  in   al, #0x71   ;; high byte
+  mov  ah, al
+  mov  al, #0x1b
+  out  #0x70, al
+  in   al, #0x71   ;; low byte
+  mov  bx, ax      ;; BX = cylinders
+
+  mov  al, #0x1d
+  out  #0x70, al
+  in   al, #0x71
+  mov  cl, al      ;; CL = heads
+
+  mov  al, #0x23
+  out  #0x70, al
+  in   al, #0x71
+  mov  dl, al      ;; DL = sectors
+
+  cmp  bx, #1024
+  jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
+
+hd0_post_physical_chs:
+  ;; no logical CHS mapping used, just physical CHS
+  ;; use Standard Fixed Disk Parameter Table (FDPT)
+  mov   (0x003d + 0x00), bx ;; number of physical cylinders
+  mov   (0x003d + 0x02), cl ;; number of physical heads
+  mov   (0x003d + 0x0E), dl ;; number of physical sectors
+  jmp check_for_hd1
+
+hd0_post_logical_chs:
+  ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
+  mov   (0x003d + 0x09), bx ;; number of physical cylinders
+  mov   (0x003d + 0x0b), cl ;; number of physical heads
+  mov   (0x003d + 0x04), dl ;; number of physical sectors
+  mov   (0x003d + 0x0e), dl ;; number of logical sectors (same)
+  mov al, #0xa0
+  mov   (0x003d + 0x03), al ;; A0h signature, indicates translated table
+
+  cmp bx, #2048
+  jnbe hd0_post_above_2048
+  ;; 1024 < c <= 2048 cylinders
+  shr bx, #0x01
+  shl cl, #0x01
+  jmp hd0_post_store_logical
+
+hd0_post_above_2048:
+  cmp bx, #4096
+  jnbe hd0_post_above_4096
+  ;; 2048 < c <= 4096 cylinders
+  shr bx, #0x02
+  shl cl, #0x02
+  jmp hd0_post_store_logical
+
+hd0_post_above_4096:
+  cmp bx, #8192
+  jnbe hd0_post_above_8192
+  ;; 4096 < c <= 8192 cylinders
+  shr bx, #0x03
+  shl cl, #0x03
+  jmp hd0_post_store_logical
+
+hd0_post_above_8192:
+  ;; 8192 < c <= 16384 cylinders
+  shr bx, #0x04
+  shl cl, #0x04
+
+hd0_post_store_logical:
+  mov   (0x003d + 0x00), bx ;; number of physical cylinders
+  mov   (0x003d + 0x02), cl ;; number of physical heads
+  ;; checksum
+  mov   cl, #0x0f     ;; repeat count
+  mov   si, #0x003d   ;; offset to disk0 FDPT
+  mov   al, #0x00     ;; sum
+hd0_post_checksum_loop:
+  add   al, [si]
+  inc   si
+  dec   cl
+  jnz hd0_post_checksum_loop
+  not   al  ;; now take 2s complement
+  inc   al
+  mov   [si], al
+;;; Done filling EBDA table for hard disk 0.
+
+
+check_for_hd1:
+  ;; is there really a second hard disk?  if not, return now
+  mov  al, #0x12
+  out  #0x70, al
+  in   al, #0x71
+  and  al, #0x0f
+  jnz   post_d1_exists
+  ret
+post_d1_exists:
+  ;; check that the hd type is really 0x0f.
+  cmp al, #0x0f
+  jz post_d1_extended
+  HALT(__LINE__)
+post_d1_extended:
+  ;; check that the extended type is 47 - user definable
+  mov  al, #0x1a
+  out  #0x70, al
+  in   al, #0x71
+  cmp  al, #47  ;; decimal 47 - user definable
+  je   post_d1_type47
+  HALT(__LINE__)
+post_d1_type47:
+  ;; Table for disk1.
+  ;; CMOS  purpose                  param table offset
+  ;; 0x24    cylinders low            0
+  ;; 0x25    cylinders high           1
+  ;; 0x26    heads                    2
+  ;; 0x27    write pre-comp low       5
+  ;; 0x28    write pre-comp high      6
+  ;; 0x29    heads>8                  8
+  ;; 0x2a    landing zone low         C
+  ;; 0x2b    landing zone high        D
+  ;; 0x2c    sectors/track            E
+;;; Fill EBDA table for hard disk 1.
+  mov  ax, #EBDA_SEG
+  mov  ds, ax
+  mov  al, #0x28
+  out  #0x70, al
+  in   al, #0x71
+  mov  ah, al
+  mov  al, #0x27
+  out  #0x70, al
+  in   al, #0x71
+  mov   (0x004d + 0x05), ax ;; write precomp word
+
+  mov  al, #0x29
+  out  #0x70, al
+  in   al, #0x71
+  mov   (0x004d + 0x08), al ;; drive control byte
+
+  mov  al, #0x2b
+  out  #0x70, al
+  in   al, #0x71
+  mov  ah, al
+  mov  al, #0x2a
+  out  #0x70, al
+  in   al, #0x71
+  mov   (0x004d + 0x0C), ax ;; landing zone word
+
+  mov  al, #0x25   ;; get cylinders word in AX
+  out  #0x70, al
+  in   al, #0x71   ;; high byte
+  mov  ah, al
+  mov  al, #0x24
+  out  #0x70, al
+  in   al, #0x71   ;; low byte
+  mov  bx, ax      ;; BX = cylinders
+
+  mov  al, #0x26
+  out  #0x70, al
+  in   al, #0x71
+  mov  cl, al      ;; CL = heads
+
+  mov  al, #0x2c
+  out  #0x70, al
+  in   al, #0x71
+  mov  dl, al      ;; DL = sectors
+
+  cmp  bx, #1024
+  jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
+
+hd1_post_physical_chs:
+  ;; no logical CHS mapping used, just physical CHS
+  ;; use Standard Fixed Disk Parameter Table (FDPT)
+  mov   (0x004d + 0x00), bx ;; number of physical cylinders
+  mov   (0x004d + 0x02), cl ;; number of physical heads
+  mov   (0x004d + 0x0E), dl ;; number of physical sectors
+  ret
+
+hd1_post_logical_chs:
+  ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
+  mov   (0x004d + 0x09), bx ;; number of physical cylinders
+  mov   (0x004d + 0x0b), cl ;; number of physical heads
+  mov   (0x004d + 0x04), dl ;; number of physical sectors
+  mov   (0x004d + 0x0e), dl ;; number of logical sectors (same)
+  mov al, #0xa0
+  mov   (0x004d + 0x03), al ;; A0h signature, indicates translated table
+
+  cmp bx, #2048
+  jnbe hd1_post_above_2048
+  ;; 1024 < c <= 2048 cylinders
+  shr bx, #0x01
+  shl cl, #0x01
+  jmp hd1_post_store_logical
+
+hd1_post_above_2048:
+  cmp bx, #4096
+  jnbe hd1_post_above_4096
+  ;; 2048 < c <= 4096 cylinders
+  shr bx, #0x02
+  shl cl, #0x02
+  jmp hd1_post_store_logical
+
+hd1_post_above_4096:
+  cmp bx, #8192
+  jnbe hd1_post_above_8192
+  ;; 4096 < c <= 8192 cylinders
+  shr bx, #0x03
+  shl cl, #0x03
+  jmp hd1_post_store_logical
+
+hd1_post_above_8192:
+  ;; 8192 < c <= 16384 cylinders
+  shr bx, #0x04
+  shl cl, #0x04
+
+hd1_post_store_logical:
+  mov   (0x004d + 0x00), bx ;; number of physical cylinders
+  mov   (0x004d + 0x02), cl ;; number of physical heads
+  ;; checksum
+  mov   cl, #0x0f     ;; repeat count
+  mov   si, #0x004d   ;; offset to disk0 FDPT
+  mov   al, #0x00     ;; sum
+hd1_post_checksum_loop:
+  add   al, [si]
+  inc   si
+  dec   cl
+  jnz hd1_post_checksum_loop
+  not   al  ;; now take 2s complement
+  inc   al
+  mov   [si], al
+;;; Done filling EBDA table for hard disk 1.
+
+  ret
+
+;--------------------
+;- POST: EBDA segment
+;--------------------
+; relocated here because the primary POST area isnt big enough.
+ebda_post:
+#if BX_USE_EBDA
+  mov ax, #EBDA_SEG
+  mov ds, ax
+  mov byte ptr [0x0], #EBDA_SIZE
+#endif
+  xor ax, ax            ; mov EBDA seg into 40E
+  mov ds, ax
+  mov word ptr [0x40E], #EBDA_SEG
+  ret;;
+
+;--------------------
+;- POST: EOI + jmp via [0x40:67)
+;--------------------
+; relocated here because the primary POST area isnt big enough.
+eoi_jmp_post:
+  call eoi_both_pics
+
+  xor ax, ax
+  mov ds, ax
+
+  jmp far ptr [0x467]
+
+
+;--------------------
+eoi_both_pics:
+  mov   al, #0x20
+  out   #0xA0, al ;; slave  PIC EOI
+eoi_master_pic:
+  mov   al, #0x20
+  out   #0x20, al ;; master PIC EOI
+  ret
+
+;--------------------
+BcdToBin:
+  ;; in:  AL in BCD format
+  ;; out: AL in binary format, AH will always be 0
+  ;; trashes BX
+  mov  bl, al
+  and  bl, #0x0f ;; bl has low digit
+  shr  al, #4    ;; al has high digit
+  mov  bh, #10
+  mul  al, bh    ;; multiply high digit by 10 (result in AX)
+  add  al, bl    ;;   then add low digit
+  ret
+
+;--------------------
+timer_tick_post:
+  ;; Setup the Timer Ticks Count (0x46C:dword) and
+  ;;   Timer Ticks Roller Flag (0x470:byte)
+  ;; The Timer Ticks Count needs to be set according to
+  ;; the current CMOS time, as if ticks have been occurring
+  ;; at 18.2hz since midnight up to this point.  Calculating
+  ;; this is a little complicated.  Here are the factors I gather
+  ;; regarding this.  14,318,180 hz was the original clock speed,
+  ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
+  ;; at the time, or 4 to drive the CGA video adapter.  The div3
+  ;; source was divided again by 4 to feed a 1.193Mhz signal to
+  ;; the timer.  With a maximum 16bit timer count, this is again
+  ;; divided down by 65536 to 18.2hz.
+  ;;
+  ;; 14,318,180 Hz clock
+  ;;   /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
+  ;;   /4 = 1,193,181 Hz fed to timer
+  ;;   /65536 (maximum timer count) = 18.20650736 ticks/second
+  ;; 1 second = 18.20650736 ticks
+  ;; 1 minute = 1092.390442 ticks
+  ;; 1 hour   = 65543.42651 ticks
+  ;;
+  ;; Given the values in the CMOS clock, one could calculate
+  ;; the number of ticks by the following:
+  ;;   ticks = (BcdToBin(seconds) * 18.206507) +
+  ;;           (BcdToBin(minutes) * 1092.3904)
+  ;;           (BcdToBin(hours)   * 65543.427)
+  ;; To get a little more accuracy, since Im using integer
+  ;; arithmatic, I use:
+  ;;   ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
+  ;;           (BcdToBin(minutes) * 10923904) / 10000 +
+  ;;           (BcdToBin(hours)   * 65543427) / 1000
+
+  ;; assuming DS=0000
+
+  ;; get CMOS seconds
+  xor  eax, eax ;; clear EAX
+  mov  al, #0x00
+  out  #0x70, al
+  in   al, #0x71 ;; AL has CMOS seconds in BCD
+  call BcdToBin  ;; EAX now has seconds in binary
+  mov  edx, #18206507
+  mul  eax, edx
+  mov  ebx, #1000000
+  xor  edx, edx
+  div  eax, ebx
+  mov  ecx, eax  ;; ECX will accumulate total ticks
+
+  ;; get CMOS minutes
+  xor  eax, eax ;; clear EAX
+  mov  al, #0x02
+  out  #0x70, al
+  in   al, #0x71 ;; AL has CMOS minutes in BCD
+  call BcdToBin  ;; EAX now has minutes in binary
+  mov  edx, #10923904
+  mul  eax, edx
+  mov  ebx, #10000
+  xor  edx, edx
+  div  eax, ebx
+  add  ecx, eax  ;; add to total ticks
+
+  ;; get CMOS hours
+  xor  eax, eax ;; clear EAX
+  mov  al, #0x04
+  out  #0x70, al
+  in   al, #0x71 ;; AL has CMOS hours in BCD
+  call BcdToBin  ;; EAX now has hours in binary
+  mov  edx, #65543427
+  mul  eax, edx
+  mov  ebx, #1000
+  xor  edx, edx
+  div  eax, ebx
+  add  ecx, eax  ;; add to total ticks
+
+  mov  0x46C, ecx ;; Timer Ticks Count
+  xor  al, al
+  mov  0x470, al  ;; Timer Ticks Rollover Flag
+  ret
+
+;--------------------
+int76_handler:
+  ;; record completion in BIOS task complete flag
+  push  ax
+  push  ds
+  mov   ax, #0x0040
+  mov   ds, ax
+  mov   0x008E, #0xff
+  call  eoi_both_pics
+  pop   ds
+  pop   ax
+  iret
+
+
+;--------------------
+
+;--------------------
+#if BX_PCIBIOS
+use32 386
+.align 16
+bios32_structure:
+  db 0x5f, 0x33, 0x32, 0x5f  ;; "_32_" signature
+  dw bios32_entry_point, 0xf ;; 32 bit physical address
+  db 0             ;; revision level
+  ;; length in paragraphs and checksum stored in a word to prevent errors
+  dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
+        & 0xff) << 8) + 0x01
+  db 0,0,0,0,0     ;; reserved
+
+.align 16
+bios32_entry_point:
+  pushf
+  cmp eax, #0x49435024 ;; "$PCI"
+  jne unknown_service
+  mov eax, #0x80000000
+  mov dx, #0x0cf8
+  out dx, eax
+  mov dx, #0x0cfc
+  in  eax, dx
+#ifdef PCI_FIXED_HOST_BRIDGE
+  cmp eax, #PCI_FIXED_HOST_BRIDGE
+  jne unknown_service
+#else
+  ;; say ok if a device is present
+  cmp eax, #0xffffffff
+  je unknown_service
+#endif
+  mov ebx, #0x000f0000
+  mov ecx, #0
+  mov edx, #pcibios_protected
+  xor al, al
+  jmp bios32_end
+unknown_service:
+  mov al, #0x80
+bios32_end:
+  popf
+  retf
+
+.align 16
+pcibios_protected:
+  pushf
+  cli
+  push esi
+  push edi
+  cmp al, #0x01 ;; installation check
+  jne pci_pro_f02
+  mov bx, #0x0210
+  mov cx, #0
+  mov edx, #0x20494350 ;; "PCI "
+  mov al, #0x01
+  jmp pci_pro_ok
+pci_pro_f02: ;; find pci device
+  cmp al, #0x02
+  jne pci_pro_f08
+  shl ecx, #16
+  mov cx, dx
+  mov bx, #0x0000
+  mov di, #0x00
+pci_pro_devloop:
+  call pci_pro_select_reg
+  mov dx, #0x0cfc
+  in  eax, dx
+  cmp eax, ecx
+  jne pci_pro_nextdev
+  cmp si, #0
+  je  pci_pro_ok
+  dec si
+pci_pro_nextdev:
+  inc bx
+  cmp bx, #0x0100
+  jne pci_pro_devloop
+  mov ah, #0x86
+  jmp pci_pro_fail
+pci_pro_f08: ;; read configuration byte
+  cmp al, #0x08
+  jne pci_pro_f09
+  call pci_pro_select_reg
+  push edx
+  mov dx, di
+  and dx, #0x03
+  add dx, #0x0cfc
+  in  al, dx
+  pop edx
+  mov cl, al
+  jmp pci_pro_ok
+pci_pro_f09: ;; read configuration word
+  cmp al, #0x09
+  jne pci_pro_f0a
+  call pci_pro_select_reg
+  push edx
+  mov dx, di
+  and dx, #0x02
+  add dx, #0x0cfc
+  in  ax, dx
+  pop edx
+  mov cx, ax
+  jmp pci_pro_ok
+pci_pro_f0a: ;; read configuration dword
+  cmp al, #0x0a
+  jne pci_pro_f0b
+  call pci_pro_select_reg
+  push edx
+  mov dx, #0x0cfc
+  in  eax, dx
+  pop edx
+  mov ecx, eax
+  jmp pci_pro_ok
+pci_pro_f0b: ;; write configuration byte
+  cmp al, #0x0b
+  jne pci_pro_f0c
+  call pci_pro_select_reg
+  push edx
+  mov dx, di
+  and dx, #0x03
+  add dx, #0x0cfc
+  mov al, cl
+  out dx, al
+  pop edx
+  jmp pci_pro_ok
+pci_pro_f0c: ;; write configuration word
+  cmp al, #0x0c
+  jne pci_pro_f0d
+  call pci_pro_select_reg
+  push edx
+  mov dx, di
+  and dx, #0x02
+  add dx, #0x0cfc
+  mov ax, cx
+  out dx, ax
+  pop edx
+  jmp pci_pro_ok
+pci_pro_f0d: ;; write configuration dword
+  cmp al, #0x0d
+  jne pci_pro_unknown
+  call pci_pro_select_reg
+  push edx
+  mov dx, #0x0cfc
+  mov eax, ecx
+  out dx, eax
+  pop edx
+  jmp pci_pro_ok
+pci_pro_unknown:
+  mov ah, #0x81
+pci_pro_fail:
+  pop edi
+  pop esi
+  popf
+  stc
+  retf
+pci_pro_ok:
+  xor ah, ah
+  pop edi
+  pop esi
+  popf
+  clc
+  retf
+
+pci_pro_select_reg:
+  push edx
+  mov eax, #0x800000
+  mov ax,  bx
+  shl eax, #8
+  and di,  #0xff
+  or  ax,  di
+  and al,  #0xfc
+  mov dx, #0x0cf8
+  out dx,  eax
+  pop edx
+  ret
+
+use16 386
+
+pcibios_real:
+  push eax
+  push dx
+  mov eax, #0x80000000
+  mov dx, #0x0cf8
+  out dx, eax
+  mov dx, #0x0cfc
+  in  eax, dx
+#ifdef PCI_FIXED_HOST_BRIDGE
+  cmp eax, #PCI_FIXED_HOST_BRIDGE
+  je  pci_present
+#else
+  ;; say ok if a device is present
+  cmp eax, #0xffffffff
+  jne  pci_present
+#endif
+  pop dx
+  pop eax
+  mov ah, #0xff
+  stc
+  ret
+pci_present:
+  pop dx
+  pop eax
+  cmp al, #0x01 ;; installation check
+  jne pci_real_f02
+  mov ax, #0x0001
+  mov bx, #0x0210
+  mov cx, #0
+  mov edx, #0x20494350 ;; "PCI "
+  mov edi, #0xf0000
+  mov di, #pcibios_protected
+  clc
+  ret
+pci_real_f02: ;; find pci device
+  push esi
+  push edi
+  cmp al, #0x02
+  jne pci_real_f08
+  shl ecx, #16
+  mov cx, dx
+  mov bx, #0x0000
+  mov di, #0x00
+pci_real_devloop:
+  call pci_real_select_reg
+  mov dx, #0x0cfc
+  in  eax, dx
+  cmp eax, ecx
+  jne pci_real_nextdev
+  cmp si, #0
+  je  pci_real_ok
+  dec si
+pci_real_nextdev:
+  inc bx
+  cmp bx, #0x0100
+  jne pci_real_devloop
+  mov dx, cx
+  shr ecx, #16
+  mov ah, #0x86
+  jmp pci_real_fail
+pci_real_f08: ;; read configuration byte
+  cmp al, #0x08
+  jne pci_real_f09
+  call pci_real_select_reg
+  push dx
+  mov dx, di
+  and dx, #0x03
+  add dx, #0x0cfc
+  in  al, dx
+  pop dx
+  mov cl, al
+  jmp pci_real_ok
+pci_real_f09: ;; read configuration word
+  cmp al, #0x09
+  jne pci_real_f0a
+  call pci_real_select_reg
+  push dx
+  mov dx, di
+  and dx, #0x02
+  add dx, #0x0cfc
+  in  ax, dx
+  pop dx
+  mov cx, ax
+  jmp pci_real_ok
+pci_real_f0a: ;; read configuration dword
+  cmp al, #0x0a
+  jne pci_real_f0b
+  call pci_real_select_reg
+  push dx
+  mov dx, #0x0cfc
+  in  eax, dx
+  pop dx
+  mov ecx, eax
+  jmp pci_real_ok
+pci_real_f0b: ;; write configuration byte
+  cmp al, #0x0b
+  jne pci_real_f0c
+  call pci_real_select_reg
+  push dx
+  mov dx, di
+  and dx, #0x03
+  add dx, #0x0cfc
+  mov al, cl
+  out dx, al
+  pop dx
+  jmp pci_real_ok
+pci_real_f0c: ;; write configuration word
+  cmp al, #0x0c
+  jne pci_real_f0d
+  call pci_real_select_reg
+  push dx
+  mov dx, di
+  and dx, #0x02
+  add dx, #0x0cfc
+  mov ax, cx
+  out dx, ax
+  pop dx
+  jmp pci_real_ok
+pci_real_f0d: ;; write configuration dword
+  cmp al, #0x0d
+  jne pci_real_f0e
+  call pci_real_select_reg
+  push dx
+  mov dx, #0x0cfc
+  mov eax, ecx
+  out dx, eax
+  pop dx
+  jmp pci_real_ok
+pci_real_f0e: ;; get irq routing options
+  cmp al, #0x0e
+  jne pci_real_unknown
+  SEG ES
+  cmp word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start
+  jb pci_real_too_small    
+  SEG ES
+  mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start        
+  pushf
+  push ds
+  push es
+  push cx
+  push si
+  push di
+  cld
+  mov si, #pci_routing_table_structure_start
+  push cs
+  pop ds
+  SEG ES
+  mov cx, [di+2]
+  SEG ES
+  mov es, [di+4]
+  mov di, cx
+  mov cx, #pci_routing_table_structure_end - pci_routing_table_structure_start
+  rep 
+      movsb
+  pop di
+  pop si
+  pop cx
+  pop es
+  pop ds
+  popf
+  mov bx, #(1 << 9) | (1 << 11)   ;; irq 9 and 11 are used
+  jmp pci_real_ok
+pci_real_too_small:
+  SEG ES
+  mov word ptr [di], #pci_routing_table_structure_end - pci_routing_table_structure_start        
+  mov ah, #0x89
+  jmp pci_real_fail
+
+pci_real_unknown:
+  mov ah, #0x81
+pci_real_fail:
+  pop edi
+  pop esi
+  stc
+  ret
+pci_real_ok:
+  xor ah, ah
+  pop edi
+  pop esi
+  clc
+  ret
+
+pci_real_select_reg:
+  push dx
+  mov eax, #0x800000
+  mov ax,  bx
+  shl eax, #8
+  and di,  #0xff
+  or  ax,  di
+  and al,  #0xfc
+  mov dx,  #0x0cf8
+  out dx,  eax
+  pop dx
+  ret
+  
+.align 16
+pci_routing_table_structure:
+  db 0x24, 0x50, 0x49, 0x52  ;; "$PIR" signature
+  db 0, 1 ;; version
+  dw 32 + (6 * 16) ;; table size
+  db 0 ;; PCI interrupt router bus
+  db 0x08 ;; PCI interrupt router DevFunc
+  dw 0x0000 ;; PCI exclusive IRQs 
+  dw 0x8086 ;; compatible PCI interrupt router vendor ID
+  dw 0x7000 ;; compatible PCI interrupt router device ID
+  dw 0,0 ;; Miniport data
+  db 0,0,0,0,0,0,0,0,0,0,0 ;; reserved
+  db 0x07 ;; checksum
+pci_routing_table_structure_start:
+  ;; first slot entry PCI-to-ISA (embedded)
+  db 0 ;; pci bus number
+  db 0x08 ;; pci device number (bit 7-3)
+  db 0x60 ;; link value INTA#: pointer into PCI2ISA config space
+  dw 0xdef8 ;; IRQ bitmap INTA# 
+  db 0x61 ;; link value INTB#
+  dw 0xdef8 ;; IRQ bitmap INTB# 
+  db 0x62 ;; link value INTC#
+  dw 0xdef8 ;; IRQ bitmap INTC# 
+  db 0x63 ;; link value INTD#
+  dw 0xdef8 ;; IRQ bitmap INTD#
+  db 0 ;; physical slot (0 = embedded)
+  db 0 ;; reserved
+  ;; second slot entry: 1st PCI slot
+  db 0 ;; pci bus number
+  db 0x10 ;; pci device number (bit 7-3)
+  db 0x61 ;; link value INTA#
+  dw 0xdef8 ;; IRQ bitmap INTA# 
+  db 0x62 ;; link value INTB#
+  dw 0xdef8 ;; IRQ bitmap INTB# 
+  db 0x63 ;; link value INTC#
+  dw 0xdef8 ;; IRQ bitmap INTC# 
+  db 0x60 ;; link value INTD#
+  dw 0xdef8 ;; IRQ bitmap INTD#
+  db 1 ;; physical slot (0 = embedded)
+  db 0 ;; reserved
+  ;; third slot entry: 2nd PCI slot
+  db 0 ;; pci bus number
+  db 0x18 ;; pci device number (bit 7-3)
+  db 0x62 ;; link value INTA#
+  dw 0xdef8 ;; IRQ bitmap INTA# 
+  db 0x63 ;; link value INTB#
+  dw 0xdef8 ;; IRQ bitmap INTB# 
+  db 0x60 ;; link value INTC#
+  dw 0xdef8 ;; IRQ bitmap INTC# 
+  db 0x61 ;; link value INTD#
+  dw 0xdef8 ;; IRQ bitmap INTD#
+  db 2 ;; physical slot (0 = embedded)
+  db 0 ;; reserved
+  ;; 4th slot entry: 3rd PCI slot
+  db 0 ;; pci bus number
+  db 0x20 ;; pci device number (bit 7-3)
+  db 0x63 ;; link value INTA#
+  dw 0xdef8 ;; IRQ bitmap INTA# 
+  db 0x60 ;; link value INTB#
+  dw 0xdef8 ;; IRQ bitmap INTB# 
+  db 0x61 ;; link value INTC#
+  dw 0xdef8 ;; IRQ bitmap INTC# 
+  db 0x62 ;; link value INTD#
+  dw 0xdef8 ;; IRQ bitmap INTD#
+  db 3 ;; physical slot (0 = embedded)
+  db 0 ;; reserved
+  ;; 5th slot entry: 4rd PCI slot
+  db 0 ;; pci bus number
+  db 0x28 ;; pci device number (bit 7-3)
+  db 0x60 ;; link value INTA#
+  dw 0xdef8 ;; IRQ bitmap INTA# 
+  db 0x61 ;; link value INTB#
+  dw 0xdef8 ;; IRQ bitmap INTB# 
+  db 0x62 ;; link value INTC#
+  dw 0xdef8 ;; IRQ bitmap INTC# 
+  db 0x63 ;; link value INTD#
+  dw 0xdef8 ;; IRQ bitmap INTD#
+  db 4 ;; physical slot (0 = embedded)
+  db 0 ;; reserved
+  ;; 6th slot entry: 5rd PCI slot
+  db 0 ;; pci bus number
+  db 0x30 ;; pci device number (bit 7-3)
+  db 0x61 ;; link value INTA#
+  dw 0xdef8 ;; IRQ bitmap INTA# 
+  db 0x62 ;; link value INTB#
+  dw 0xdef8 ;; IRQ bitmap INTB# 
+  db 0x63 ;; link value INTC#
+  dw 0xdef8 ;; IRQ bitmap INTC# 
+  db 0x60 ;; link value INTD#
+  dw 0xdef8 ;; IRQ bitmap INTD#
+  db 5 ;; physical slot (0 = embedded)
+  db 0 ;; reserved
+pci_routing_table_structure_end:
+
+pci_irq_list:
+  db 11, 10, 9, 5;
+
+pcibios_init_sel_reg:
+  push eax
+  mov eax, #0x800000
+  mov ax,  bx
+  shl eax, #8
+  and dl,  #0xfc
+  or  al,  dl
+  mov dx,  #0x0cf8
+  out dx,  eax
+  pop eax
+  ret
+  
+pcibios_init_iomem_bases:
+  push bp
+  mov  bp, sp
+  mov  eax, #0xe0000000 ;; base for memory init
+  push eax
+  mov  ax, #0xc000 ;; base for i/o init
+  push ax
+  mov  ax, #0x0010 ;; start at base address #0
+  push ax
+  mov  bx, #0x0008
+pci_init_io_loop1:
+  mov  dl, #0x00
+  call pcibios_init_sel_reg
+  mov  dx, #0x0cfc
+  in   ax, dx
+  cmp  ax, #0xffff
+  jz   next_pci_dev
+  mov  dl, #0x04 ;; disable i/o and memory space access
+  call pcibios_init_sel_reg
+  mov  dx, #0x0cfc
+  in   al, dx
+  and  al, #0xfc
+  out  dx, al
+pci_init_io_loop2:
+  mov  dl, [bp-8]
+  call pcibios_init_sel_reg
+  mov  dx, #0x0cfc
+  in   eax, dx
+  test al, #0x01
+  jnz  init_io_base
+  mov  ecx, eax
+  mov  eax, #0xffffffff
+  out  dx, eax
+  in   eax, dx
+  cmp  eax, ecx
+  je   next_pci_base
+  xor  eax, #0xffffffff
+  mov  ecx, eax
+  mov  eax, [bp-4]
+  out  dx, eax
+  add  eax, ecx ;; calculate next free mem base
+  add  eax, #0x01000000
+  and  eax, #0xff000000
+  mov  [bp-4], eax
+  jmp  next_pci_base
+init_io_base:
+  mov  cx, ax
+  mov  ax, #0xffff
+  out  dx, ax
+  in   ax, dx
+  cmp  ax, cx
+  je   next_pci_base
+  xor  ax, #0xfffe
+  mov  cx, ax
+  mov  ax, [bp-6]
+  out  dx, ax
+  add  ax, cx ;; calculate next free i/o base
+  add  ax, #0x0100
+  and  ax, #0xff00
+  mov  [bp-6], ax
+next_pci_base:
+  mov  al, [bp-8]
+  add  al, #0x04
+  cmp  al, #0x28
+  je   enable_iomem_space
+  mov  byte ptr[bp-8], al
+  jmp  pci_init_io_loop2
+enable_iomem_space:
+  mov  dl, #0x04 ;; enable i/o and memory space access if available
+  call pcibios_init_sel_reg
+  mov  dx, #0x0cfc
+  in   al, dx
+  or   al, #0x07
+  out  dx, al
+next_pci_dev:
+  mov  byte ptr[bp-8], #0x10
+  inc  bx
+  cmp  bx, #0x0100
+  jne  pci_init_io_loop1
+  mov  sp, bp
+  pop  bp
+  ret
+
+pcibios_init_set_elcr:
+  push ax
+  push cx
+  mov  dx, #0x04d0
+  test al, #0x08
+  jz   is_master_pic
+  inc  dx
+  and  al, #0x07
+is_master_pic:
+  mov  cl, al
+  mov  bl, #0x01
+  shl  bl, cl
+  in   al, dx
+  or   al, bl
+  out  dx, al
+  pop  cx
+  pop  ax
+  ret
+
+pcibios_init_irqs:
+  push ds
+  push bp
+  mov  ax, #0xf000
+  mov  ds, ax
+  mov  dx, #0x04d0 ;; reset ELCR1 + ELCR2
+  mov  al, #0x00
+  out  dx, al
+  inc  dx
+  out  dx, al
+  mov  si, #pci_routing_table_structure
+  mov  bh, [si+8]
+  mov  bl, [si+9]
+  mov  dl, #0x00
+  call pcibios_init_sel_reg
+  mov  dx, #0x0cfc
+  in   eax, dx
+  cmp  eax, [si+12] ;; check irq router
+  jne  pci_init_end
+  mov  dl, [si+34]
+  call pcibios_init_sel_reg
+  push bx ;; save irq router bus + devfunc
+  mov  dx, #0x0cfc
+  mov  ax, #0x8080
+  out  dx, ax ;; reset PIRQ route control
+  inc  dx
+  inc  dx
+  out  dx, ax
+  mov  ax, [si+6]
+  sub  ax, #0x20
+  shr  ax, #4
+  mov  cx, ax
+  add  si, #0x20 ;; set pointer to 1st entry
+  mov  bp, sp
+  mov  ax, #pci_irq_list
+  push ax
+  xor  ax, ax
+  push ax
+pci_init_irq_loop1:
+  mov  bh, [si]
+  mov  bl, [si+1]
+pci_init_irq_loop2:
+  mov  dl, #0x00
+  call pcibios_init_sel_reg
+  mov  dx, #0x0cfc
+  in   ax, dx
+  cmp  ax, #0xffff
+  jnz  pci_test_int_pin
+  test bl, #0x07
+  jz   next_pir_entry
+  jmp  next_pci_func
+pci_test_int_pin:
+  mov  dl, #0x3c
+  call pcibios_init_sel_reg
+  mov  dx, #0x0cfd
+  in   al, dx
+  and  al, #0x07
+  jz   next_pci_func
+  dec  al ;; determine pirq reg
+  mov  dl, #0x03
+  mul  al, dl
+  add  al, #0x02
+  xor  ah, ah
+  mov  bx, ax
+  mov  al, [si+bx]
+  mov  dl, al
+  mov  bx, [bp]
+  call pcibios_init_sel_reg
+  mov  dx, #0x0cfc
+  and  al, #0x03
+  add  dl, al
+  in   al, dx
+  cmp  al, #0x80
+  jb   pirq_found
+  mov  bx, [bp-2] ;; pci irq list pointer
+  mov  al, [bx]
+  out  dx, al
+  inc  bx
+  mov  [bp-2], bx
+  call pcibios_init_set_elcr
+pirq_found:
+  mov  bh, [si]
+  mov  bl, [si+1]
+  add  bl, [bp-3] ;; pci function number
+  mov  dl, #0x3c
+  call pcibios_init_sel_reg
+  mov  dx, #0x0cfc
+  out  dx, al
+next_pci_func:
+  inc  byte ptr[bp-3]
+  inc  bl
+  test bl, #0x07
+  jnz  pci_init_irq_loop2
+next_pir_entry:
+  add  si, #0x10
+  mov  byte ptr[bp-3], #0x00
+  loop pci_init_irq_loop1
+  mov  sp, bp
+  pop  bx
+pci_init_end:
+  pop  bp
+  pop  ds
+  ret
+#endif // BX_PCIBIOS
+
+; parallel port detection: base address in DX, index in BX, timeout in CL
+detect_parport:
+  push dx
+  add  dx, #2
+  in   al, dx
+  and  al, #0xdf ; clear input mode
+  out  dx, al
+  pop  dx
+  mov  al, #0xaa
+  out  dx, al
+  in   al, dx
+  cmp  al, #0xaa
+  jne  no_parport
+  push bx
+  shl  bx, #1
+  mov  [bx+0x408], dx ; Parallel I/O address
+  pop  bx
+  mov  [bx+0x478], cl ; Parallel printer timeout
+  inc  bx
+no_parport:
+  ret
+
+; serial port detection: base address in DX, index in BX, timeout in CL
+detect_serial:
+  push dx
+  inc  dx
+  mov  al, #0x02
+  out  dx, al
+  in   al, dx
+  cmp  al, #0x02
+  jne  no_serial
+  inc  dx
+  in   al, dx
+  cmp  al, #0x02
+  jne  no_serial
+  dec  dx
+  xor  al, al
+  out  dx, al
+  pop  dx
+  push bx
+  shl  bx, #1
+  mov  [bx+0x400], dx ; Serial I/O address
+  pop  bx
+  mov  [bx+0x47c], cl ; Serial timeout
+  inc  bx
+  ret
+no_serial:
+  pop  dx
+  ret
+
+rom_checksum:
+  push ax
+  push bx
+  push cx
+  xor  ax, ax
+  xor  bx, bx
+  xor  cx, cx
+  mov  ch, [2]
+  shl  cx, #1
+checksum_loop:
+  add  al, [bx]
+  inc  bx
+  loop checksum_loop
+  and  al, #0xff
+  pop  cx
+  pop  bx
+  pop  ax
+  ret
+
+rom_scan:
+  ;; Scan for existence of valid expansion ROMS.
+  ;;   Video ROM:   from 0xC0000..0xC7FFF in 2k increments
+  ;;   General ROM: from 0xC8000..0xDFFFF in 2k increments
+  ;;   System  ROM: only 0xE0000
+  ;;
+  ;; Header:
+  ;;   Offset    Value
+  ;;   0         0x55
+  ;;   1         0xAA
+  ;;   2         ROM length in 512-byte blocks
+  ;;   3         ROM initialization entry point (FAR CALL)
+
+  mov  cx, #0xc000
+rom_scan_loop:
+  mov  ds, cx
+  mov  ax, #0x0004 ;; start with increment of 4 (512-byte) blocks = 2k
+  cmp [0], #0xAA55 ;; look for signature
+  jne  rom_scan_increment
+  call rom_checksum
+  jnz  rom_scan_increment
+  mov  al, [2]  ;; change increment to ROM length in 512-byte blocks
+
+  ;; We want our increment in 512-byte quantities, rounded to
+  ;; the nearest 2k quantity, since we only scan at 2k intervals.
+  test al, #0x03
+  jz   block_count_rounded
+  and  al, #0xfc ;; needs rounding up
+  add  al, #0x04
+block_count_rounded:
+
+  xor  bx, bx   ;; Restore DS back to 0000:
+  mov  ds, bx
+  push ax       ;; Save AX
+  ;; Push addr of ROM entry point
+  push cx       ;; Push seg
+  push #0x0003  ;; Push offset
+  mov  bp, sp   ;; Call ROM init routine using seg:off on stack
+  db   0xff     ;; call_far ss:[bp+0]
+  db   0x5e
+  db   0
+  cli           ;; In case expansion ROM BIOS turns IF on
+  add  sp, #2   ;; Pop offset value
+  pop  cx       ;; Pop seg value (restore CX)
+  pop  ax       ;; Restore AX
+rom_scan_increment:
+  shl  ax, #5   ;; convert 512-bytes blocks to 16-byte increments
+                ;; because the segment selector is shifted left 4 bits.
+  add  cx, ax
+  cmp  cx, #0xe000
+  jbe  rom_scan_loop
+
+  xor  ax, ax   ;; Restore DS back to 0000:
+  mov  ds, ax
+  ret
+
+;; for 'C' strings and other data, insert them here with
+;; a the following hack:
+;; DATA_SEG_DEFS_HERE
+
+
+;--------
+;- POST -
+;--------
+.org 0xe05b ; POST Entry Point
+post:
+
+  xor ax, ax
+
+  ;; first reset the DMA controllers
+  out 0x0d,al
+  out 0xda,al
+
+  ;; then initialize the DMA controllers
+  mov al, #0xC0
+  out 0xD6, al ; cascade mode of channel 4 enabled
+  mov al, #0x00
+  out 0xD4, al ; unmask channel 4
+
+  ;; Examine CMOS shutdown status.
+  mov AL, #0x0f
+  out 0x70, AL
+  in  AL, 0x71
+
+  ;; backup status
+  mov bl, al
+
+  ;; Reset CMOS shutdown status.
+  mov AL, #0x0f
+  out 0x70, AL          ; select CMOS register Fh
+  mov AL, #0x00
+  out 0x71, AL          ; set shutdown action to normal
+
+  ;; Examine CMOS shutdown status.
+  mov al, bl
+
+  ;; 0x00, 0x09, 0x0D+ = normal startup
+  cmp AL, #0x00
+  jz normal_post
+  cmp AL, #0x0d
+  jae normal_post
+  cmp AL, #0x09
+  je normal_post
+
+  ;; 0x05 = eoi + jmp via [0x40:0x67] jump
+  cmp al, #0x05
+  je  eoi_jmp_post
+
+  ;; Examine CMOS shutdown status.
+  ;;  0x01,0x02,0x03,0x04,0x06,0x07,0x08, 0x0a, 0x0b, 0x0c = Unimplemented shutdown status.
+  push bx
+  call _shutdown_status_panic
+
+#if 0 
+  HALT(__LINE__)
+  ;
+  ;#if 0
+  ;  0xb0, 0x20,       /* mov al, #0x20 */
+  ;  0xe6, 0x20,       /* out 0x20, al    ;send EOI to PIC */
+  ;#endif
+  ;
+  pop es
+  pop ds
+  popa
+  iret
+#endif
+
+normal_post:
+  ; case 0: normal startup
+
+  cli
+  mov  ax, #0xfffe
+  mov  sp, ax
+  mov  ax, #0x0000
+  mov  ds, ax
+  mov  ss, ax
+
+  ;; zero out BIOS data area (40:00..40:ff)
+  mov  es, ax
+  mov  cx, #0x0080 ;; 128 words
+  mov  di, #0x0400
+  cld
+  rep
+    stosw
+
+  call _log_bios_start
+
+  ;; set all interrupts to default handler
+  mov  bx, #0x0000    ;; offset index
+  mov  cx, #0x0100    ;; counter (256 interrupts)
+  mov  ax, #dummy_iret_handler
+  mov  dx, #0xF000
+
+post_default_ints:
+  mov  [bx], ax
+  inc  bx
+  inc  bx
+  mov  [bx], dx
+  inc  bx
+  inc  bx
+  loop post_default_ints
+
+  ;; set vector 0x79 to zero
+  ;; this is used by 'gardian angel' protection system
+  SET_INT_VECTOR(0x79, #0, #0)
+
+  ;; base memory in K 40:13 (word)
+  mov  ax, #BASE_MEM_IN_K
+  mov  0x0413, ax
+
+
+  ;; Manufacturing Test 40:12
+  ;;   zerod out above
+
+  ;; Warm Boot Flag 0040:0072
+  ;;   value of 1234h = skip memory checks
+  ;;   zerod out above
+
+
+  ;; Printer Services vector
+  SET_INT_VECTOR(0x17, #0xF000, #int17_handler)
+
+  ;; Bootstrap failure vector
+  SET_INT_VECTOR(0x18, #0xF000, #int18_handler)
+
+  ;; Bootstrap Loader vector
+  SET_INT_VECTOR(0x19, #0xF000, #int19_handler)
+
+  ;; User Timer Tick vector
+  SET_INT_VECTOR(0x1c, #0xF000, #int1c_handler)
+
+  ;; Memory Size Check vector
+  SET_INT_VECTOR(0x12, #0xF000, #int12_handler)
+
+  ;; Equipment Configuration Check vector
+  SET_INT_VECTOR(0x11, #0xF000, #int11_handler)
+
+  ;; System Services
+  SET_INT_VECTOR(0x15, #0xF000, #int15_handler)
+
+  ;; EBDA setup
+  call ebda_post
+
+  ;; PIT setup
+  SET_INT_VECTOR(0x08, #0xF000, #int08_handler)
+  ;; int 1C already points at dummy_iret_handler (above)
+  mov al, #0x34 ; timer0: binary count, 16bit count, mode 2
+  out 0x43, al
+  mov al, #0x00 ; maximum count of 0000H = 18.2Hz
+  out 0x40, al
+  out 0x40, al
+
+  ;; Keyboard
+  SET_INT_VECTOR(0x09, #0xF000, #int09_handler)
+  SET_INT_VECTOR(0x16, #0xF000, #int16_handler)
+
+  xor  ax, ax
+  mov  ds, ax
+  mov  0x0417, al /* keyboard shift flags, set 1 */
+  mov  0x0418, al /* keyboard shift flags, set 2 */
+  mov  0x0419, al /* keyboard alt-numpad work area */
+  mov  0x0471, al /* keyboard ctrl-break flag */
+  mov  0x0497, al /* keyboard status flags 4 */
+  mov  al, #0x10
+  mov  0x0496, al /* keyboard status flags 3 */
+
+
+  /* keyboard head of buffer pointer */
+  mov  bx, #0x001E
+  mov  0x041A, bx
+
+  /* keyboard end of buffer pointer */
+  mov  0x041C, bx
+
+  /* keyboard pointer to start of buffer */
+  mov  bx, #0x001E
+  mov  0x0480, bx
+
+  /* keyboard pointer to end of buffer */
+  mov  bx, #0x003E
+  mov  0x0482, bx
+
+  /* init the keyboard */
+  call _keyboard_init
+
+  ;; mov CMOS Equipment Byte to BDA Equipment Word
+  mov  ax, 0x0410
+  mov  al, #0x14
+  out  0x70, al
+  in   al, 0x71
+  mov  0x0410, ax
+
+
+  ;; Parallel setup
+  SET_INT_VECTOR(0x0F, #0xF000, #dummy_iret_handler)
+  xor ax, ax
+  mov ds, ax
+  xor bx, bx
+  mov cl, #0x14 ; timeout value
+  mov dx, #0x378 ; Parallel I/O address, port 1
+  call detect_parport
+  mov dx, #0x278 ; Parallel I/O address, port 2
+  call detect_parport
+  shl bx, #0x0e
+  mov ax, 0x410   ; Equipment word bits 14..15 determing # parallel ports
+  and ax, #0x3fff
+  or  ax, bx ; set number of parallel ports
+  mov 0x410, ax
+
+  ;; Serial setup
+  SET_INT_VECTOR(0x0C, #0xF000, #dummy_iret_handler)
+  SET_INT_VECTOR(0x14, #0xF000, #int14_handler)
+  xor bx, bx
+  mov cl, #0x0a ; timeout value
+  mov dx, #0x03f8 ; Serial I/O address, port 1
+  call detect_serial
+  mov dx, #0x02f8 ; Serial I/O address, port 2
+  call detect_serial
+  mov dx, #0x03e8 ; Serial I/O address, port 3
+  call detect_serial
+  mov dx, #0x02e8 ; Serial I/O address, port 4
+  call detect_serial
+  shl bx, #0x09
+  mov ax, 0x410   ; Equipment word bits 9..11 determing # serial ports
+  and ax, #0xf1ff
+  or  ax, bx ; set number of serial port
+  mov 0x410, ax
+
+  ;; CMOS RTC
+  SET_INT_VECTOR(0x1A, #0xF000, #int1a_handler)
+  SET_INT_VECTOR(0x4A, #0xF000, #dummy_iret_handler)
+  SET_INT_VECTOR(0x70, #0xF000, #int70_handler)
+  ;; BIOS DATA AREA 0x4CE ???
+  call timer_tick_post
+
+  ;; PS/2 mouse setup
+  SET_INT_VECTOR(0x74, #0xF000, #int74_handler)
+
+  ;; IRQ13 (FPU exception) setup
+  SET_INT_VECTOR(0x75, #0xF000, #int75_handler)
+
+  ;; Video setup
+  SET_INT_VECTOR(0x10, #0xF000, #int10_handler)
+
+  ;; PIC
+  mov al, #0x11 ; send initialisation commands
+  out 0x20, al
+  out 0xa0, al
+  mov al, #0x08
+  out 0x21, al
+  mov al, #0x70
+  out 0xa1, al
+  mov al, #0x04
+  out 0x21, al
+  mov al, #0x02
+  out 0xa1, al
+  mov al, #0x01
+  out 0x21, al
+  out 0xa1, al
+  mov  al, #0xb8
+  out  0x21, AL ;master pic: unmask IRQ 0, 1, 2, 6
+#if BX_USE_PS2_MOUSE
+  mov  al, #0x8f
+#else
+  mov  al, #0x9f
+#endif
+  out  0xa1, AL ;slave  pic: unmask IRQ 12, 13, 14
+
+#if BX_PCIBIOS
+  call pcibios_init_iomem_bases
+  call pcibios_init_irqs
+#endif
+
+  call rom_scan
+
+  call _print_bios_banner 
+
+  ;;
+  ;; Floppy setup
+  ;;
+  call floppy_drive_post
+
+#if BX_USE_ATADRV
+
+  ;;
+  ;; Hard Drive setup
+  ;;
+  call hard_drive_post
+
+  ;;
+  ;; ATA/ATAPI driver setup
+  ;;
+  call _ata_init
+  call _ata_detect
+  ;;
+#else // BX_USE_ATADRV
+
+  ;;
+  ;; Hard Drive setup
+  ;;
+  call hard_drive_post
+
+#endif // BX_USE_ATADRV
+
+#if BX_ELTORITO_BOOT
+  ;;
+  ;; eltorito floppy/harddisk emulation from cd
+  ;;
+  call _cdemu_init
+  ;;
+#endif // BX_ELTORITO_BOOT
+ 
+  sti        ;; enable interrupts
+  int  #0x19
+
+
+.org 0xe2c3 ; NMI Handler Entry Point
+nmi:
+  ;; FIXME the NMI handler should not panic
+  ;; but iret when called from int75 (fpu exception)
+  call _nmi_handler_msg
+  iret
+
+int75_handler:
+  out  0xf0, al         // clear irq13 
+  call eoi_both_pics    // clear interrupt
+  int  2                // legacy nmi call
+  iret
+
+;-------------------------------------------
+;- INT 13h Fixed Disk Services Entry Point -
+;-------------------------------------------
+.org 0xe3fe ; INT 13h Fixed Disk Services Entry Point
+int13_handler:
+  //JMPL(int13_relocated)
+  jmp int13_relocated
+
+.org 0xe401 ; Fixed Disk Parameter Table
+
+;----------
+;- INT19h -
+;----------
+.org 0xe6f2 ; INT 19h Boot Load Service Entry Point
+int19_handler:
+
+  jmp int19_relocated
+;-------------------------------------------
+;- System BIOS Configuration Data Table
+;-------------------------------------------
+.org BIOS_CONFIG_TABLE
+db 0x08                  ; Table size (bytes) -Lo
+db 0x00                  ; Table size (bytes) -Hi
+db SYS_MODEL_ID
+db SYS_SUBMODEL_ID
+db BIOS_REVISION
+; Feature byte 1
+; b7: 1=DMA channel 3 used by hard disk
+; b6: 1=2 interrupt controllers present
+; b5: 1=RTC present
+; b4: 1=BIOS calls int 15h/4Fh every key
+; b3: 1=wait for extern event supported (Int 15h/41h)
+; b2: 1=extended BIOS data area used
+; b1: 0=AT or ESDI bus, 1=MicroChannel
+; b0: 1=Dual bus (MicroChannel + ISA)
+db (0 << 7) | \
+   (1 << 6) | \
+   (1 << 5) | \
+   (BX_CALL_INT15_4F << 4) | \
+   (0 << 3) | \
+   (BX_USE_EBDA << 2) | \
+   (0 << 1) | \
+   (0 << 0)
+; Feature byte 2
+; b7: 1=32-bit DMA supported
+; b6: 1=int16h, function 9 supported
+; b5: 1=int15h/C6h (get POS data) supported
+; b4: 1=int15h/C7h (get mem map info) supported
+; b3: 1=int15h/C8h (en/dis CPU) supported
+; b2: 1=non-8042 kb controller
+; b1: 1=data streaming supported
+; b0: reserved
+db (0 << 7) | \
+   (1 << 6) | \
+   (0 << 5) | \
+   (0 << 4) | \
+   (0 << 3) | \
+   (0 << 2) | \
+   (0 << 1) | \
+   (0 << 0)
+; Feature byte 3
+; b7: not used
+; b6: reserved
+; b5: reserved
+; b4: POST supports ROM-to-RAM enable/disable
+; b3: SCSI on system board
+; b2: info panel installed
+; b1: Initial Machine Load (IML) system - BIOS on disk
+; b0: SCSI supported in IML
+db 0x00
+; Feature byte 4
+; b7: IBM private
+; b6: EEPROM present
+; b5-3: ABIOS presence (011 = not supported)
+; b2: private
+; b1: memory split above 16Mb supported
+; b0: POSTEXT directly supported by POST
+db 0x00
+; Feature byte 5 (IBM)
+; b1: enhanced mouse
+; b0: flash EPROM
+db 0x00
+
+
+
+.org 0xe729 ; Baud Rate Generator Table
+
+;----------
+;- INT14h -
+;----------
+.org 0xe739 ; INT 14h Serial Communications Service Entry Point
+int14_handler:
+  push ds
+  pusha
+  mov  ax, #0x0000
+  mov  ds, ax
+  call _int14_function
+  popa
+  pop  ds
+  iret
+
+
+;----------------------------------------
+;- INT 16h Keyboard Service Entry Point -
+;----------------------------------------
+.org 0xe82e
+int16_handler:
+
+  sti
+  push  ds
+  pushf
+  pusha
+
+  cmp   ah, #0x00
+  je    int16_F00
+  cmp   ah, #0x10
+  je    int16_F00
+
+  mov  bx, #0xf000
+  mov  ds, bx
+  call _int16_function
+  popa
+  popf
+  pop  ds
+  jz   int16_zero_set
+
+int16_zero_clear:
+  push bp
+  mov  bp, sp
+  //SEG SS
+  and  BYTE [bp + 0x06], #0xbf
+  pop  bp
+  iret
+
+int16_zero_set:
+  push bp
+  mov  bp, sp
+  //SEG SS
+  or   BYTE [bp + 0x06], #0x40
+  pop  bp
+  iret
+
+int16_F00:
+  mov  bx, #0x0040
+  mov  ds, bx
+
+int16_wait_for_key:
+  cli
+  mov  bx, 0x001a
+  cmp  bx, 0x001c
+  jne  int16_key_found
+  sti
+  nop
+#if 0
+                           /* no key yet, call int 15h, function AX=9002 */
+  0x50,                    /* push AX */
+  0xb8, 0x02, 0x90,        /* mov AX, #0x9002 */
+  0xcd, 0x15,              /* int 15h */
+  0x58,                    /* pop  AX */
+  0xeb, 0xea,              /* jmp   WAIT_FOR_KEY */
+#endif
+  jmp  int16_wait_for_key
+
+int16_key_found:
+  mov  bx, #0xf000
+  mov  ds, bx
+  call _int16_function
+  popa
+  popf
+  pop  ds
+#if 0
+                           /* notify int16 complete w/ int 15h, function AX=9102 */
+  0x50,                    /* push AX */
+  0xb8, 0x02, 0x91,        /* mov AX, #0x9102 */
+  0xcd, 0x15,              /* int 15h */
+  0x58,                    /* pop  AX */
+#endif
+  iret
+
+
+
+;-------------------------------------------------
+;- INT09h : Keyboard Hardware Service Entry Point -
+;-------------------------------------------------
+.org 0xe987
+int09_handler:
+  cli
+  push ax
+
+  mov al, #0xAD      ;;disable keyboard
+  out #0x64, al
+
+  mov al, #0x0B
+  out #0x20, al
+  in  al, #0x20
+  and al, #0x02
+  jz  int09_finish
+
+  in  al, #0x60             ;;read key from keyboard controller
+  sti
+  push  ds
+  pusha
+#ifdef BX_CALL_INT15_4F
+  mov  ah, #0x4f     ;; allow for keyboard intercept
+  stc
+  int  #0x15
+  jnc  int09_done
+#endif
+
+  ;; check for extended key
+  cmp  al, #0xe0
+  jne int09_check_pause
+  xor  ax, ax
+  mov  ds, ax
+  mov  al, BYTE [0x496]     ;; mf2_state |= 0x02
+  or   al, #0x02
+  mov  BYTE [0x496], al
+  jmp int09_done
+
+int09_check_pause:  ;; check for pause key
+  cmp  al, #0xe1
+  jne int09_process_key
+  xor  ax, ax
+  mov  ds, ax
+  mov  al, BYTE [0x496]     ;; mf2_state |= 0x01
+  or   al, #0x01
+  mov  BYTE [0x496], al
+  jmp int09_done
+
+int09_process_key:
+  mov   bx, #0xf000
+  mov   ds, bx
+  call  _int09_function
+
+int09_done:
+  popa
+  pop   ds
+  cli
+  call eoi_master_pic
+
+int09_finish:
+  mov al, #0xAE      ;;enable keyboard
+  out #0x64, al
+  pop ax
+  iret
+
+
+;----------------------------------------
+;- INT 13h Diskette Service Entry Point -
+;----------------------------------------
+.org 0xec59
+int13_diskette:
+  jmp int13_noeltorito
+
+;---------------------------------------------
+;- INT 0Eh Diskette Hardware ISR Entry Point -
+;---------------------------------------------
+.org 0xef57 ; INT 0Eh Diskette Hardware ISR Entry Point
+int0e_handler:
+  push ax
+  push dx
+  mov  dx, #0x03f4
+  in   al, dx
+  and  al, #0xc0
+  cmp  al, #0xc0
+  je   int0e_normal
+  mov  dx, #0x03f5
+  mov  al, #0x08 ; sense interrupt status
+  out  dx, al
+int0e_loop1:
+  mov  dx, #0x03f4
+  in   al, dx
+  and  al, #0xc0
+  cmp  al, #0xc0
+  jne  int0e_loop1
+int0e_loop2:
+  mov  dx, #0x03f5
+  in   al, dx
+  mov  dx, #0x03f4
+  in   al, dx
+  and  al, #0xc0
+  cmp  al, #0xc0
+  je int0e_loop2
+int0e_normal:
+  push ds
+  mov  ax, #0x0000 ;; segment 0000
+  mov  ds, ax
+  call eoi_master_pic
+  mov  al, 0x043e
+  or   al, #0x80 ;; diskette interrupt has occurred
+  mov  0x043e, al
+  pop  ds
+  pop  dx
+  pop  ax
+  iret
+
+
+.org 0xefc7 ; Diskette Controller Parameter Table
+diskette_param_table:
+;;  Since no provisions are made for multiple drive types, most
+;;  values in this table are ignored.  I set parameters for 1.44M
+;;  floppy here
+db  0xAF
+db  0x02 ;; head load time 0000001, DMA used
+db  0x25
+db  0x02
+db    18
+db  0x1B
+db  0xFF
+db  0x6C
+db  0xF6
+db  0x0F
+db  0x08
+
+
+;----------------------------------------
+;- INT17h : Printer Service Entry Point -
+;----------------------------------------
+.org 0xefd2
+int17_handler:
+  push ds
+  pusha
+  mov  ax, #0x0000
+  mov  ds, ax
+  call _int17_function
+  popa
+  pop  ds
+  iret
+
+diskette_param_table2:
+;;  New diskette parameter table adding 3 parameters from IBM
+;;  Since no provisions are made for multiple drive types, most
+;;  values in this table are ignored.  I set parameters for 1.44M
+;;  floppy here
+db  0xAF
+db  0x02 ;; head load time 0000001, DMA used
+db  0x25
+db  0x02
+db    18
+db  0x1B
+db  0xFF
+db  0x6C
+db  0xF6
+db  0x0F
+db  0x08
+db    79 ;; maximum track
+db     0 ;; data transfer rate
+db     4 ;; drive type in cmos
+
+.org 0xf045 ; INT 10 Functions 0-Fh Entry Point
+  HALT(__LINE__)
+  iret
+
+;----------
+;- INT10h -
+;----------
+.org 0xf065 ; INT 10h Video Support Service Entry Point
+int10_handler:
+  ;; dont do anything, since the VGA BIOS handles int10h requests
+  iret
+
+.org 0xf0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
+
+;----------
+;- INT12h -
+;----------
+.org 0xf841 ; INT 12h Memory Size Service Entry Point
+; ??? different for Pentium (machine check)?
+int12_handler:
+  push ds
+  mov  ax, #0x0040
+  mov  ds, ax
+  mov  ax, 0x0013
+  pop  ds
+  iret
+
+;----------
+;- INT11h -
+;----------
+.org 0xf84d ; INT 11h Equipment List Service Entry Point
+int11_handler:
+  push ds
+  mov  ax, #0x0040
+  mov  ds, ax
+  mov  ax, 0x0010
+  pop  ds
+  iret
+
+;----------
+;- INT15h -
+;----------
+.org 0xf859 ; INT 15h System Services Entry Point
+int15_handler:
+  pushf
+  push  ds
+  push  es
+  cmp  ah, #0x86
+  je int15_handler32
+  cmp  ah, #0xE8
+  je int15_handler32
+  pusha
+#if BX_USE_PS2_MOUSE
+  cmp  ah, #0xC2
+  je int15_handler_mouse
+#endif
+  call _int15_function
+int15_handler_mouse_ret:
+  popa
+int15_handler32_ret:
+  pop   es
+  pop   ds
+  popf
+  jmp iret_modify_cf
+
+#if BX_USE_PS2_MOUSE
+int15_handler_mouse:
+  call _int15_function_mouse
+  jmp int15_handler_mouse_ret
+#endif
+
+int15_handler32:
+  pushad
+  call _int15_function32
+  popad
+  jmp int15_handler32_ret
+
+;; Protected mode IDT descriptor
+;;
+;; I just make the limit 0, so the machine will shutdown
+;; if an exception occurs during protected mode memory
+;; transfers.
+;;
+;; Set base to f0000 to correspond to beginning of BIOS,
+;; in case I actually define an IDT later
+;; Set limit to 0
+
+pmode_IDT_info:
+dw 0x0000  ;; limit 15:00
+dw 0x0000  ;; base  15:00
+db 0x0f    ;; base  23:16
+
+;; Real mode IDT descriptor
+;;
+;; Set to typical real-mode values.
+;; base  = 000000
+;; limit =   03ff
+
+rmode_IDT_info:
+dw 0x03ff  ;; limit 15:00
+dw 0x0000  ;; base  15:00
+db 0x00    ;; base  23:16
+
+
+;----------
+;- INT1Ah -
+;----------
+.org 0xfe6e ; INT 1Ah Time-of-day Service Entry Point
+int1a_handler:
+#if BX_PCIBIOS
+  cmp  ah, #0xb1
+  jne  int1a_normal
+  call pcibios_real
+  jc   pcibios_error
+  retf 2
+pcibios_error:
+  mov  bl, ah
+  mov  ah, #0xb1
+  push ds
+  pusha
+  mov ax, ss  ; set readable descriptor to ds, for calling pcibios
+  mov ds, ax  ;  on 16bit protected mode.
+  jmp int1a_callfunction
+int1a_normal:
+#endif
+  push ds
+  pusha
+  xor  ax, ax
+  mov  ds, ax
+int1a_callfunction:
+  call _int1a_function
+  popa
+  pop  ds
+  iret
+
+;;
+;; int70h: IRQ8 - CMOS RTC
+;;
+int70_handler:
+  push ds
+  pushad
+  xor  ax, ax
+  mov  ds, ax
+  call _int70_function
+  popad
+  pop  ds
+  iret
+
+;---------
+;- INT08 -
+;---------
+.org 0xfea5 ; INT 08h System Timer ISR Entry Point
+int08_handler:
+  sti
+  push eax
+  push ds
+  xor ax, ax
+  mov ds, ax
+
+  ;; time to turn off drive(s)?
+  mov  al,0x0440
+  or   al,al
+  jz   int08_floppy_off
+  dec  al
+  mov  0x0440,al
+  jnz  int08_floppy_off
+  ;; turn motor(s) off
+  push dx
+  mov  dx,#0x03f2
+  in   al,dx
+  and  al,#0xcf
+  out  dx,al
+  pop  dx
+int08_floppy_off:
+
+  mov eax, 0x046c ;; get ticks dword
+  inc eax
+
+  ;; compare eax to one days worth of timer ticks at 18.2 hz
+  cmp eax, #0x001800B0
+  jb  int08_store_ticks
+  ;; there has been a midnight rollover at this point
+  xor eax, eax    ;; zero out counter
+  inc BYTE 0x0470 ;; increment rollover flag
+
+int08_store_ticks:
+  mov 0x046c, eax ;; store new ticks dword
+  ;; chain to user timer tick INT #0x1c
+  //pushf
+  //;; call_ep [ds:loc]
+  //CALL_EP( 0x1c << 2 )
+  int #0x1c
+  cli
+  call eoi_master_pic
+  pop ds
+  pop eax
+  iret
+
+.org 0xfef3 ; Initial Interrupt Vector Offsets Loaded by POST
+
+
+.org 0xff00
+.ascii BIOS_COPYRIGHT_STRING
+
+;------------------------------------------------
+;- IRET Instruction for Dummy Interrupt Handler -
+;------------------------------------------------
+.org 0xff53 ; IRET Instruction for Dummy Interrupt Handler
+dummy_iret_handler:
+  iret
+
+.org 0xff54 ; INT 05h Print Screen Service Entry Point
+  HALT(__LINE__)
+  iret
+
+.org 0xfff0 ; Power-up Entry Point
+  jmp 0xf000:post 
+
+.org 0xfff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
+.ascii BIOS_BUILD_DATE
+
+.org 0xfffe ; System Model ID
+db SYS_MODEL_ID
+db 0x00   ; filler
+
+.org 0xfa6e ;; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
+ASM_END
+/*
+ * This font comes from the fntcol16.zip package (c) by  Joseph Gil 
+ * found at ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
+ * This font is public domain
+ */ 
+static Bit8u vgafont8[128*8]=
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e,
+ 0x7e, 0xff, 0xdb, 0xff, 0xc3, 0xe7, 0xff, 0x7e,
+ 0x6c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00,
+ 0x38, 0x7c, 0x38, 0xfe, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x10, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x7c,
+ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00,
+ 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff,
+ 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+ 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff,
+ 0x0f, 0x07, 0x0f, 0x7d, 0xcc, 0xcc, 0xcc, 0x78,
+ 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18,
+ 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x70, 0xf0, 0xe0,
+ 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x67, 0xe6, 0xc0,
+ 0x99, 0x5a, 0x3c, 0xe7, 0xe7, 0x3c, 0x5a, 0x99,
+ 0x80, 0xe0, 0xf8, 0xfe, 0xf8, 0xe0, 0x80, 0x00,
+ 0x02, 0x0e, 0x3e, 0xfe, 0x3e, 0x0e, 0x02, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x7e, 0x3c, 0x18,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00,
+ 0x7f, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x00,
+ 0x3e, 0x63, 0x38, 0x6c, 0x6c, 0x38, 0xcc, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x7e, 0x7e, 0x7e, 0x00,
+ 0x18, 0x3c, 0x7e, 0x18, 0x7e, 0x3c, 0x18, 0xff,
+ 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x00,
+ 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+ 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00,
+ 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00,
+ 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00,
+ 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0xff, 0xff, 0x7e, 0x3c, 0x18, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x78, 0x78, 0x30, 0x30, 0x00, 0x30, 0x00,
+ 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x6c, 0x6c, 0xfe, 0x6c, 0xfe, 0x6c, 0x6c, 0x00,
+ 0x30, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x30, 0x00,
+ 0x00, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xc6, 0x00,
+ 0x38, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0x76, 0x00,
+ 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00,
+ 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00,
+ 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+ 0x00, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00,
+ 0x7c, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0x7c, 0x00,
+ 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x60, 0xcc, 0xfc, 0x00,
+ 0x78, 0xcc, 0x0c, 0x38, 0x0c, 0xcc, 0x78, 0x00,
+ 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x1e, 0x00,
+ 0xfc, 0xc0, 0xf8, 0x0c, 0x0c, 0xcc, 0x78, 0x00,
+ 0x38, 0x60, 0xc0, 0xf8, 0xcc, 0xcc, 0x78, 0x00,
+ 0xfc, 0xcc, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x00,
+ 0x78, 0xcc, 0xcc, 0x78, 0xcc, 0xcc, 0x78, 0x00,
+ 0x78, 0xcc, 0xcc, 0x7c, 0x0c, 0x18, 0x70, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x00,
+ 0x00, 0x30, 0x30, 0x00, 0x00, 0x30, 0x30, 0x60,
+ 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x00,
+ 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfc, 0x00, 0x00,
+ 0x60, 0x30, 0x18, 0x0c, 0x18, 0x30, 0x60, 0x00,
+ 0x78, 0xcc, 0x0c, 0x18, 0x30, 0x00, 0x30, 0x00,
+ 0x7c, 0xc6, 0xde, 0xde, 0xde, 0xc0, 0x78, 0x00,
+ 0x30, 0x78, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x66, 0x66, 0xfc, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xc0, 0x66, 0x3c, 0x00,
+ 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x62, 0xfe, 0x00,
+ 0xfe, 0x62, 0x68, 0x78, 0x68, 0x60, 0xf0, 0x00,
+ 0x3c, 0x66, 0xc0, 0xc0, 0xce, 0x66, 0x3e, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xfc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x1e, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78, 0x00,
+ 0xe6, 0x66, 0x6c, 0x78, 0x6c, 0x66, 0xe6, 0x00,
+ 0xf0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00,
+ 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, 0xc6, 0x00,
+ 0xc6, 0xe6, 0xf6, 0xde, 0xce, 0xc6, 0xc6, 0x00,
+ 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x38, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00,
+ 0x78, 0xcc, 0xcc, 0xcc, 0xdc, 0x78, 0x1c, 0x00,
+ 0xfc, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0xe6, 0x00,
+ 0x78, 0xcc, 0xe0, 0x70, 0x1c, 0xcc, 0x78, 0x00,
+ 0xfc, 0xb4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xfc, 0x00,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0xc6, 0xc6, 0xc6, 0xd6, 0xfe, 0xee, 0xc6, 0x00,
+ 0xc6, 0xc6, 0x6c, 0x38, 0x38, 0x6c, 0xc6, 0x00,
+ 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x30, 0x78, 0x00,
+ 0xfe, 0xc6, 0x8c, 0x18, 0x32, 0x66, 0xfe, 0x00,
+ 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00,
+ 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x02, 0x00,
+ 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00,
+ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0x76, 0x00,
+ 0xe0, 0x60, 0x60, 0x7c, 0x66, 0x66, 0xdc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xc0, 0xcc, 0x78, 0x00,
+ 0x1c, 0x0c, 0x0c, 0x7c, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xfc, 0xc0, 0x78, 0x00,
+ 0x38, 0x6c, 0x60, 0xf0, 0x60, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0xe0, 0x60, 0x6c, 0x76, 0x66, 0x66, 0xe6, 0x00,
+ 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x0c, 0x00, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0x78,
+ 0xe0, 0x60, 0x66, 0x6c, 0x78, 0x6c, 0xe6, 0x00,
+ 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00,
+ 0x00, 0x00, 0xcc, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
+ 0x00, 0x00, 0xf8, 0xcc, 0xcc, 0xcc, 0xcc, 0x00,
+ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0x78, 0x00,
+ 0x00, 0x00, 0xdc, 0x66, 0x66, 0x7c, 0x60, 0xf0,
+ 0x00, 0x00, 0x76, 0xcc, 0xcc, 0x7c, 0x0c, 0x1e,
+ 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0xf0, 0x00,
+ 0x00, 0x00, 0x7c, 0xc0, 0x78, 0x0c, 0xf8, 0x00,
+ 0x10, 0x30, 0x7c, 0x30, 0x30, 0x34, 0x18, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x78, 0x30, 0x00,
+ 0x00, 0x00, 0xc6, 0xd6, 0xfe, 0xfe, 0x6c, 0x00,
+ 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x6c, 0xc6, 0x00,
+ 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xf8,
+ 0x00, 0x00, 0xfc, 0x98, 0x30, 0x64, 0xfc, 0x00,
+ 0x1c, 0x30, 0x30, 0xe0, 0x30, 0x30, 0x1c, 0x00,
+ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00,
+ 0xe0, 0x30, 0x30, 0x1c, 0x30, 0x30, 0xe0, 0x00,
+ 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0x00,
+};
+
+ASM_START
+.org 0xcc00
+// bcc-generated data will be placed here
+ASM_END
Index: util/ADLO/bochs/bios/README
===================================================================
--- util/ADLO/bochs/bios/README	(revision 0)
+++ util/ADLO/bochs/bios/README	(revision 0)
@@ -0,0 +1 @@
+The bochs bios.
Index: util/ADLO/bochs/bios/Makefile
===================================================================
--- util/ADLO/bochs/bios/Makefile	(revision 0)
+++ util/ADLO/bochs/bios/Makefile	(revision 0)
@@ -0,0 +1,50 @@
+# Copyright (C) 2001  MandrakeSoft S.A.
+#
+#   MandrakeSoft S.A.
+#   43, rue d'Aboukir
+#   75002 Paris - France
+#   http://www.linux-mandrake.com/
+#   http://www.mandrakesoft.com/
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+
+# Makefile for the BIOS component of bochs
+
+.SUFFIXES: .cc
+
+# -------- end configurable options --------------------------
+
+.cc.o:
+	$(CXX) -c $(CXXFLAGS) $(LOCAL_CXXFLAGS) $(BX_INCDIRS) $< -o $@
+
+bios: rombios.bin
+
+rombios.bin: rombios.c
+	gcc -E -P $< > _rombios_.c
+	bcc -o rombios.s -C-c -D__i86__ -0 -S _rombios_.c
+	sed -e 's/^\.text//' -e 's/^\.data//' rombios.s > _rombios_.s
+	as86 _rombios_.s -b rombios.bin -u- -w- -g -0 -j -O -l rombios.txt
+	ls -l rombios.bin
+
+# -----------------------------------------------------------------
+
+clean:
+	rm -f  *.s
+	rm -f  _rombios_.c
+	rm -f  rombios.txt
+	rm -f  rombios.bin
+
+
+# -----------------------------------------------------------------
Index: arch/x86/Makefile
===================================================================
--- arch/x86/Makefile	(revision 466)
+++ arch/x86/Makefile	(working copy)
@@ -37,10 +37,18 @@
 ROM_SIZE := $(shell expr $(CONFIG_LINUXBIOS_ROMSIZE_KB) \* 1024)
 
 LARFILES := nocompress:normal/initram normal/stage2 nocompress:normal/option_table
+
+ifeq ($(CONFIG_PAYLOAD_ELF_ADLO),y)
+LARFILES += normal/payload
+CONFIG_PAYLOAD_FILE = payload
+else
+
 ifneq ($(CONFIG_PAYLOAD_NONE),y)
 LARFILES += normal/payload
 endif
+endif
 
+	
 DECOMPRESSORS :=
 ifeq ($(CONFIG_COMPRESSION_LZMA),y)
 DECOMPRESSORS += lzma.o
@@ -64,6 +72,19 @@
 	$(Q)cp $(obj)/linuxbios.initram $(obj)/lar.tmp/normal/initram
 	$(Q)cp $(obj)/linuxbios.stage2 $(obj)/lar.tmp/normal/stage2
 	$(Q)cp $(obj)/option_table $(obj)/lar.tmp/normal/option_table
+ifeq ($(CONFIG_PAYLOAD_ELF_ADLO),y)
+	$(Q)make -C util/ADLO
+	$(Q)# TODO: Print sth. other than $(CONFIG_PAYLOAD_FILE) if compressed.
+	$(Q)if [ -r $(CONFIG_PAYLOAD_FILE) ]; then \
+		printf "  PAYLOAD $(CONFIG_PAYLOAD_FILE)\n"; \
+		cp $(CONFIG_PAYLOAD_FILE) $(obj)/lar.tmp/normal/payload; \
+	else \
+		printf "Error: ADLO payload file '$(CONFIG_PAYLOAD_FILE)' not found.\n"; \
+		exit 1; \
+	fi
+
+else
+
 ifeq ($(CONFIG_PAYLOAD_NONE),y)
 	$(Q)printf "  PAYLOAD none (as specified by user)\n"
 else
@@ -76,6 +97,7 @@
 		exit 1; \
 	fi
 endif
+endif
 	$(Q)printf "  LAR     $(subst $(shell pwd)/,,$(@))\n"
 	$(Q)cd $(obj)/lar.tmp && ../util/lar/lar $(COMPRESSFLAG) -c \
 		../linuxbios.rom \
Index: Makefile
===================================================================
--- Makefile	(revision 466)
+++ Makefile	(working copy)
@@ -127,6 +127,7 @@
 prepare:
 	$(Q)mkdir -p $(obj)
 
+
 # FIXME: If .config changed, a rebuild is required.
 
 prepare2:
@@ -152,6 +153,12 @@
 	$(Q)rm -rf $(DOXYGEN_OUTPUT_DIR)
 	$(Q)printf "  CLEAN   doc/design/newboot.pdf\n"
 	$(Q)rm -f doc/design/newboot.pdf
+	$(Q)printf "  CLEAN   ADLO\n"
+	$(Q)make -C util/ADLO clean
+	$(Q)if [ -r payload ]; then \
+		rm -rf payload \
+		rm -rf $(obj)/lar.tmp/normal/payload; \
+	fi
 
 distclean: clean
 	$(Q)printf "  CLEAN   .kconfig.d .config .tmpconfig.h .config.old .xcompile\n"
