ia64/xen-unstable

changeset 5373:5cc07785693b

bitkeeper revision 1.1693 (42a6e907gUWgpcjzXD2dFMNiFLL5Rw)

Merge arcadians.cl.cam.ac.uk:/auto/groups/xeno-xenod/BK/xeno.bk
into arcadians.cl.cam.ac.uk:/auto/anfs/nos1/akw27/xeno-clone/xeno.bk
author akw27@arcadians.cl.cam.ac.uk
date Wed Jun 08 12:48:07 2005 +0000 (2005-06-08)
parents ab753acf69d1 bf014cae2dad
children 949970efef98
files .rootkeys BitKeeper/etc/ignore linux-2.4.30-xen-sparse/arch/xen/kernel/setup.c linux-2.6.11-xen-sparse/arch/xen/i386/kernel/setup.c linux-2.6.11-xen-sparse/arch/xen/x86_64/kernel/setup.c linux-2.6.11-xen-sparse/include/asm-xen/asm-i386/system.h linux-2.6.11-xen-sparse/include/asm-xen/asm-x86_64/system.h tools/Makefile tools/firmware/Makefile tools/firmware/README tools/firmware/rombios/Makefile tools/firmware/rombios/apmbios.S tools/firmware/rombios/biossums.c tools/firmware/rombios/makesym.perl tools/firmware/rombios/rombios.c tools/firmware/rombios/rombios.diffs tools/firmware/vgabios/BUGS tools/firmware/vgabios/COPYING tools/firmware/vgabios/ChangeLog tools/firmware/vgabios/Makefile tools/firmware/vgabios/Notes tools/firmware/vgabios/README tools/firmware/vgabios/TODO tools/firmware/vgabios/biossums.c tools/firmware/vgabios/clext.c tools/firmware/vgabios/dataseghack tools/firmware/vgabios/vbe.c tools/firmware/vgabios/vbe.h tools/firmware/vgabios/vbe_display_api.txt tools/firmware/vgabios/vbetables.h tools/firmware/vgabios/vgabios.c tools/firmware/vgabios/vgabios.h tools/firmware/vgabios/vgafonts.h tools/firmware/vgabios/vgatables.h tools/firmware/vmxassist/Makefile tools/firmware/vmxassist/TODO tools/firmware/vmxassist/gen.c tools/firmware/vmxassist/head.S tools/firmware/vmxassist/machine.h tools/firmware/vmxassist/mkhex tools/firmware/vmxassist/setup.c tools/firmware/vmxassist/trap.S tools/firmware/vmxassist/util.c tools/firmware/vmxassist/util.h tools/firmware/vmxassist/vm86.c tools/firmware/vmxassist/vm86.h tools/firmware/vmxassist/vmxassist.ld tools/firmware/vmxassist/vmxloader.c tools/libxc/xc_linux_build.c tools/libxc/xc_plan9_build.c xen/arch/ia64/domain.c xen/arch/x86/domain_build.c xen/arch/x86/setup.c xen/arch/x86/vmx_io.c xen/common/elf.c xen/include/asm-x86/string.h xen/include/public/xen.h xen/include/xen/sched.h
line diff
     1.1 --- a/.rootkeys	Wed Jun 08 08:16:41 2005 +0000
     1.2 +++ b/.rootkeys	Wed Jun 08 12:48:07 2005 +0000
     1.3 @@ -567,6 +567,46 @@ 41dde8af6M2Pm1Rrv_f5jEFC_BIOIA tools/exa
     1.4  41090ec8Pj_bkgCBpg2W7WfmNkumEA tools/examples/xmexample1
     1.5  40cf2937oKlROYOJTN8GWwWM5AmjBg tools/examples/xmexample2
     1.6  41fc0c18_k4iL81hu4pMIWQu9dKpKA tools/examples/xmexample3
     1.7 +42a6b4b7KssGzTDVN-XG2FM1gCEnnw tools/firmware/Makefile
     1.8 +42a6b4b7qP95OSsEL8XWKKZ1p1myjQ tools/firmware/README
     1.9 +42a6b4b78PWdYzKYvLt_EHhvQCl9ig tools/firmware/rombios/Makefile
    1.10 +42a6b4b75sz5KF9Lry2EGnPMhOdnUA tools/firmware/rombios/apmbios.S
    1.11 +42a6b4b7YwP9rl3AJRTmZbBoal_c6Q tools/firmware/rombios/biossums.c
    1.12 +42a6b4b83gANosDYd43YaK7ATQvBEg tools/firmware/rombios/makesym.perl
    1.13 +42a6b4b8qcIQIBXDeOY3JRwsLM6lhw tools/firmware/rombios/rombios.c
    1.14 +42a6b4b8K7yqnU3-QxndYNZUgHpniw tools/firmware/rombios/rombios.diffs
    1.15 +42a6b4b86GMM969Y82nK3HuUi6eP9g tools/firmware/vgabios/BUGS
    1.16 +42a6b4b8J_MHMVmmF_igI7zeDxSiwA tools/firmware/vgabios/COPYING
    1.17 +42a6b4b8SYW5q21pPPuQt88Bkpqc2Q tools/firmware/vgabios/ChangeLog
    1.18 +42a6b4b8INe7qe20YYlwATaAADEMQA tools/firmware/vgabios/Makefile
    1.19 +42a6b4b8AYFCsoAeqqQ8dibmgxkfLA tools/firmware/vgabios/Notes
    1.20 +42a6b4b8NUXHh1hudvvNCuqgo9cB-Q tools/firmware/vgabios/README
    1.21 +42a6b4b8MM0Pj6uDwdJ4Eyg6hB-oEA tools/firmware/vgabios/TODO
    1.22 +42a6b4b8AL0YrgudjmQr7QvJ3we1Cg tools/firmware/vgabios/biossums.c
    1.23 +42a6b4b8Zce-r8OtpctwvqHBS8cHEw tools/firmware/vgabios/clext.c
    1.24 +42a6b4b8fIyMd0d8tIPV4JDAvB5l1A tools/firmware/vgabios/dataseghack
    1.25 +42a6b4b8M4BsNDRAJMHpY8H2iRu0qA tools/firmware/vgabios/vbe.c
    1.26 +42a6b4b8Z2pSU4e5qrUR5r1vEKNbKQ tools/firmware/vgabios/vbe.h
    1.27 +42a6b4b8EyiklW2C9eD9_t0OmRfmFQ tools/firmware/vgabios/vbe_display_api.txt
    1.28 +42a6b4b8oXcw5CgLj-mBVT4dUc-Umw tools/firmware/vgabios/vbetables.h
    1.29 +42a6b4b85jkZnCar41YreYVUAY7IDQ tools/firmware/vgabios/vgabios.c
    1.30 +42a6b4b8xxpRYh1BesaSgW3gpgMsaQ tools/firmware/vgabios/vgabios.h
    1.31 +42a6b4b8WSA5xHF-R5F8iBcB6BC5wA tools/firmware/vgabios/vgafonts.h
    1.32 +42a6b4b9C66bPuUTaLjCnJ0I-kGz9w tools/firmware/vgabios/vgatables.h
    1.33 +42a6b4b969QLJRt3TU_v3yYhZI45Gg tools/firmware/vmxassist/Makefile
    1.34 +42a6b4b95iuk7M2s-edoSFrWcdoYcw tools/firmware/vmxassist/TODO
    1.35 +42a6b4b9Q6VB27GxRNCARsDN2ZuKNw tools/firmware/vmxassist/gen.c
    1.36 +42a6b4b9NmLjb36-sXiiWzcGHjTOJA tools/firmware/vmxassist/head.S
    1.37 +42a6b4b9jmF9m22iiwu8XwEm1j5fnQ tools/firmware/vmxassist/machine.h
    1.38 +42a6b4b9ABmGHA1LzYjpq63FBs4hcw tools/firmware/vmxassist/mkhex
    1.39 +42a6b4b9xmj4TLHJtV-DhnwT9mMpfw tools/firmware/vmxassist/setup.c
    1.40 +42a6b4b9PjgANTP8Y8JFTToBrV9ssg tools/firmware/vmxassist/trap.S
    1.41 +42a6b4b9GlymU0VmQyan23pagDaRTQ tools/firmware/vmxassist/util.c
    1.42 +42a6b4b9mmqUyFn487gP4spU_R6xtg tools/firmware/vmxassist/util.h
    1.43 +42a6b4b9JssxvlpcV_-QcGRMDGgL_w tools/firmware/vmxassist/vm86.c
    1.44 +42a6b4b92oUAJMzCE-YcVlA2Z-2zyg tools/firmware/vmxassist/vm86.h
    1.45 +42a6b4b9TlkVUYTkLd_Bvq9vlrEx6g tools/firmware/vmxassist/vmxassist.ld
    1.46 +42a6b4b92L-2zFg-Qal6YweeE-pMiA tools/firmware/vmxassist/vmxloader.c
    1.47  428d0d82yOaUzYQuYQxH7VzQytKo-g tools/ioemu/COPYING
    1.48  428d0d82EdPp1TqJBembLgyB1y413w tools/ioemu/COPYING.LIB
    1.49  428d0d82fd6-QydvFfHmeQBGrKnrrA tools/ioemu/Changelog
     2.1 --- a/BitKeeper/etc/ignore	Wed Jun 08 08:16:41 2005 +0000
     2.2 +++ b/BitKeeper/etc/ignore	Wed Jun 08 12:48:07 2005 +0000
     2.3 @@ -92,6 +92,18 @@ tools/blktap/xen/*
     2.4  tools/check/.*
     2.5  tools/cmdline/*
     2.6  tools/cmdline/xen/*
     2.7 +tools/firmware/*.bin
     2.8 +tools/firmware/*.sym
     2.9 +tools/firmware/*bios/*bios*.txt
    2.10 +tools/firmware/*/biossums
    2.11 +tools/firmware/rombios/BIOS-bochs-latest
    2.12 +tools/firmware/rombios/_rombios_.c
    2.13 +tools/firmware/rombios/rombios.s
    2.14 +tools/firmware/vmxassist/gen
    2.15 +tools/firmware/vmxassist/offsets.h
    2.16 +tools/firmware/vmxassist/roms.h
    2.17 +tools/firmware/vmxassist/vmxassist
    2.18 +tools/firmware/vmxassist/vmxloader
    2.19  tools/gdb/gdb-6.2.1-linux-i386-xen/*
    2.20  tools/gdb/gdb-6.2.1/*
    2.21  tools/ioemu/config-host.*
     3.1 --- a/linux-2.4.30-xen-sparse/arch/xen/kernel/setup.c	Wed Jun 08 08:16:41 2005 +0000
     3.2 +++ b/linux-2.4.30-xen-sparse/arch/xen/kernel/setup.c	Wed Jun 08 12:48:07 2005 +0000
     3.3 @@ -113,7 +113,7 @@ int enable_acpi_smp_table;
     3.4  /* Raw start-of-day parameters from the hypervisor. */
     3.5  union xen_start_info_union xen_start_info_union;
     3.6  
     3.7 -#define COMMAND_LINE_SIZE 256
     3.8 +#define COMMAND_LINE_SIZE MAX_GUEST_CMDLINE
     3.9  static char command_line[COMMAND_LINE_SIZE];
    3.10  char saved_command_line[COMMAND_LINE_SIZE];
    3.11  
     4.1 --- a/linux-2.6.11-xen-sparse/arch/xen/i386/kernel/setup.c	Wed Jun 08 08:16:41 2005 +0000
     4.2 +++ b/linux-2.6.11-xen-sparse/arch/xen/i386/kernel/setup.c	Wed Jun 08 12:48:07 2005 +0000
     4.3 @@ -699,12 +699,14 @@ static inline void copy_edd(void)
     4.4  static void __init parse_cmdline_early (char ** cmdline_p)
     4.5  {
     4.6  	char c = ' ', *to = command_line, *from = saved_command_line;
     4.7 -	int len = 0;
     4.8 +	int len = 0, max_cmdline;
     4.9  	int userdef = 0;
    4.10  
    4.11 -	memcpy(saved_command_line, xen_start_info.cmd_line, MAX_CMDLINE);
    4.12 +	if ((max_cmdline = MAX_GUEST_CMDLINE) > COMMAND_LINE_SIZE)
    4.13 +		max_cmdline = COMMAND_LINE_SIZE;
    4.14 +	memcpy(saved_command_line, xen_start_info.cmd_line, max_cmdline);
    4.15  	/* Save unparsed command line copy for /proc/cmdline */
    4.16 -	saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
    4.17 +	saved_command_line[max_cmdline-1] = '\0';
    4.18  
    4.19  	for (;;) {
    4.20  		if (c != ' ')
     5.1 --- a/linux-2.6.11-xen-sparse/arch/xen/x86_64/kernel/setup.c	Wed Jun 08 08:16:41 2005 +0000
     5.2 +++ b/linux-2.6.11-xen-sparse/arch/xen/x86_64/kernel/setup.c	Wed Jun 08 12:48:07 2005 +0000
     5.3 @@ -318,12 +318,13 @@ union xen_start_info_union xen_start_inf
     5.4  static __init void parse_cmdline_early (char ** cmdline_p)
     5.5  {
     5.6  	char c = ' ', *to = command_line, *from = COMMAND_LINE;
     5.7 -	int len = 0;
     5.8 +	int len = 0, max_cmdline;
     5.9  
    5.10 -	memcpy(saved_command_line, xen_start_info.cmd_line, MAX_CMDLINE);
    5.11 +	if ((max_cmdline = MAX_GUEST_CMDLINE) > COMMAND_LINE_SIZE)
    5.12 +		max_cmdline = COMMAND_LINE_SIZE;
    5.13 +	memcpy(saved_command_line, xen_start_info.cmd_line, max_cmdline);
    5.14  	/* Save unparsed command line copy for /proc/cmdline */
    5.15 -	memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
    5.16 -	saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
    5.17 +	saved_command_line[max_cmdline-1] = '\0';
    5.18  
    5.19  	for (;;) {
    5.20  		if (c != ' ') 
     6.1 --- a/linux-2.6.11-xen-sparse/include/asm-xen/asm-i386/system.h	Wed Jun 08 08:16:41 2005 +0000
     6.2 +++ b/linux-2.6.11-xen-sparse/include/asm-xen/asm-i386/system.h	Wed Jun 08 12:48:07 2005 +0000
     6.3 @@ -107,14 +107,25 @@ static inline unsigned long _get_base(ch
     6.4   * Clear and set 'TS' bit respectively
     6.5   */
     6.6  #define clts() (HYPERVISOR_fpu_taskswitch(0))
     6.7 -#define read_cr0() \
     6.8 -	BUG();
     6.9 +#define read_cr0() ({ \
    6.10 +	unsigned int __dummy; \
    6.11 +	__asm__( \
    6.12 +		"movl %%cr0,%0\n\t" \
    6.13 +		:"=r" (__dummy)); \
    6.14 +	__dummy; \
    6.15 +})
    6.16  #define write_cr0(x) \
    6.17 -	BUG();
    6.18 -#define read_cr4() \
    6.19 -	BUG();
    6.20 +	__asm__("movl %0,%%cr0": :"r" (x));
    6.21 +
    6.22 +#define read_cr4() ({ \
    6.23 +	unsigned int __dummy; \
    6.24 +	__asm__( \
    6.25 +		"movl %%cr4,%0\n\t" \
    6.26 +		:"=r" (__dummy)); \
    6.27 +	__dummy; \
    6.28 +})
    6.29  #define write_cr4(x) \
    6.30 -	BUG();
    6.31 +	__asm__("movl %0,%%cr4": :"r" (x));
    6.32  #define stts() (HYPERVISOR_fpu_taskswitch(1))
    6.33  
    6.34  #endif	/* __KERNEL__ */
     7.1 --- a/linux-2.6.11-xen-sparse/include/asm-xen/asm-x86_64/system.h	Wed Jun 08 08:16:41 2005 +0000
     7.2 +++ b/linux-2.6.11-xen-sparse/include/asm-xen/asm-x86_64/system.h	Wed Jun 08 12:48:07 2005 +0000
     7.3 @@ -145,30 +145,38 @@ struct alt_instr {
     7.4   * Clear and set 'TS' bit respectively
     7.5   */
     7.6  #define clts() (HYPERVISOR_fpu_taskswitch(0))
     7.7 +
     7.8  static inline unsigned long read_cr0(void)
     7.9  { 
    7.10 -	return 0;
    7.11 +	unsigned long cr0;
    7.12 +	asm volatile("movq %%cr0,%0" : "=r" (cr0));
    7.13 +	return cr0;
    7.14  } 
    7.15  
    7.16  static inline void write_cr0(unsigned long val) 
    7.17  { 
    7.18 -	/* Ignore, Linux tries to clear TS and EM */
    7.19 +	asm volatile("movq %0,%%cr0" :: "r" (val));
    7.20  } 
    7.21  
    7.22  static inline unsigned long read_cr3(void)
    7.23  { 
    7.24 -        BUG();
    7.25 +	unsigned long cr3;
    7.26 +	asm("movq %%cr3,%0" : "=r" (cr3));
    7.27 +	return cr3;
    7.28  } 
    7.29  
    7.30  static inline unsigned long read_cr4(void)
    7.31  { 
    7.32 -        BUG();
    7.33 +	unsigned long cr4;
    7.34 +	asm("movq %%cr4,%0" : "=r" (cr4));
    7.35 +	return cr4;
    7.36  } 
    7.37  
    7.38  static inline void write_cr4(unsigned long val)
    7.39  { 
    7.40 -        BUG();
    7.41 +	asm volatile("movq %0,%%cr4" :: "r" (val));
    7.42  } 
    7.43 +
    7.44  #define stts() (HYPERVISOR_fpu_taskswitch(1))
    7.45  
    7.46  #define wbinvd() \
     8.1 --- a/tools/Makefile	Wed Jun 08 08:16:41 2005 +0000
     8.2 +++ b/tools/Makefile	Wed Jun 08 12:48:07 2005 +0000
     8.3 @@ -10,6 +10,7 @@ SUBDIRS += python
     8.4  SUBDIRS += xcs
     8.5  SUBDIRS += xcutils
     8.6  SUBDIRS += pygrub
     8.7 +SUBDIRS += firmware
     8.8  
     8.9  .PHONY: all install clean check check_clean ioemu eioemuinstall ioemuclean
    8.10  
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/tools/firmware/Makefile	Wed Jun 08 12:48:07 2005 +0000
     9.3 @@ -0,0 +1,34 @@
     9.4 +XEN_ROOT = ../..
     9.5 +include $(XEN_ROOT)/tools/Rules.mk
     9.6 +
     9.7 +TARGET      := vmxassist/vmxloader
     9.8 +INSTALL_DIR := $(DESTDIR)/usr/share/xen
     9.9 +
    9.10 +SUBDIRS :=
    9.11 +SUBDIRS += rombios
    9.12 +SUBDIRS += vgabios
    9.13 +SUBDIRS += vmxassist
    9.14 +
    9.15 +.PHONY: all install clean
    9.16 +
    9.17 +all:
    9.18 +	@set -e; if ! `which bcc 1>/dev/null 2>/dev/null`; then \
    9.19 +	echo "***********************************************************"; \
    9.20 +	echo "WARNING: Install dev86 package to build firmware!"; \
    9.21 +	echo "         (http://www.cix.co.uk/~mayday)"; \
    9.22 +	echo "***********************************************************"; \
    9.23 +	else \
    9.24 +	for subdir in $(SUBDIRS); do \
    9.25 +		$(MAKE) -C $$subdir $@; \
    9.26 +	done; \
    9.27 +	fi
    9.28 +
    9.29 +
    9.30 +install: all
    9.31 +	[ -d $(INSTALL_DIR) ] || install -d -m0755 $(INSTALL_DIR)
    9.32 +	[ ! -e $(TARGET) ] || install -m0644 $(TARGET) $(INSTALL_DIR)
    9.33 +
    9.34 +clean: 
    9.35 +	@set -e; for subdir in $(SUBDIRS); do \
    9.36 +		$(MAKE) -C $$subdir $@; \
    9.37 +	done
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/tools/firmware/README	Wed Jun 08 12:48:07 2005 +0000
    10.3 @@ -0,0 +1,88 @@
    10.4 +Domain FirmWare support
    10.5 +-----------------------
    10.6 +
    10.7 +One of the key advantages of full virtualization hardware support (such
    10.8 +as Intel's VT or AMD's Pacifica) is the ability to run unmodified guest
    10.9 +operating systems.  However, since most OSes rely on BIOS support during
   10.10 +their early bringup, we need to provide a surrogate ROMBIOS and VGABIOS
   10.11 +firmware layer.
   10.12 +
   10.13 +What's more, we need to support real-mode which is required by
   10.14 +the firmware and bootstrap loaders. Real-mode support is especially
   10.15 +challenging for Intel's VMX (VT) enabled CPUs where there is no real-mode
   10.16 +support for VMX guest partitions. In this case you either have to do full
   10.17 +emulation (full real-mode emulator; more complete but potentially slower)
   10.18 +or partial emulation (use the VM8086 extensions, emulate only those
   10.19 +instructions that are missing; faster, but potentially incomplete). The
   10.20 +vmxassist code in this subdirectory uses the later approach because it
   10.21 +is smaller and faster.
   10.22 +
   10.23 +The approach is relatively straight forward. Vmxloader contains three
   10.24 +payloads (rombios, vgabios and vmxassist) and it is bootstrapped as any
   10.25 +other 32-bit OS. Vmxloader copies its payloads to the addresses below
   10.26 +and transfers control to vmxassist.
   10.27 +
   10.28 +	vgabios		VGABIOS (standard and Cirrus).
   10.29 +			Resides at C000:0000.
   10.30 +
   10.31 +	vmxassist	VMXAssist VM86 realmode emulator for VMX.
   10.32 +			Resides at D000:0000.
   10.33 +
   10.34 +	rombios		ROMBIOS code. Derived from Bochs.
   10.35 +			Resides at F000:0000
   10.36 +
   10.37 +Vmxassist first sets up it own world (GDT, IDT, TR, etc), enables
   10.38 +VM8086 and then transfers control to F000:FFF0 and executes 16-bit
   10.39 +code. Unsupported instructions cause a general protection failure at
   10.40 +which point vmxassist kicks in and emulates the offending instruction.
   10.41 +Whever the emulated code transitions to 32-bit protected mode, vmxassist
   10.42 +will go away. Whenever 32-bit protected code transitions to real-mode,
   10.43 +Xen/VMX will detect this and transfer control to vmxassist.
   10.44 +
   10.45 +Most of the vmxassist complexity comes from properly handling the
   10.46 +real to protected mode and protected to real mode transitions and
   10.47 +the proper emulation of the segment registers. Even though the Intel
   10.48 +manual clearly states that you should immediately perform a jmp far
   10.49 +after a mode transition, many operating systems execute additional
   10.50 +instructions and some even refer to segment selectors and pop data
   10.51 +from the stack. Vmxassist contains a number of work arounds for these
   10.52 +OSes.
   10.53 +
   10.54 +
   10.55 +Acknowledgements
   10.56 +----------------
   10.57 +
   10.58 +The rombios was taken (largely unmodified) from Bochs, which was written
   10.59 +by Kevin Lawton. The VGABIOS was written by Christophe Bothamy. Arun Sharma,
   10.60 +Asit Mallick and Nitin Kamble (Intel) provided the E820 patches and lots
   10.61 +of useful feedback.
   10.62 +
   10.63 +
   10.64 +Contact
   10.65 +-------
   10.66 +
   10.67 +Leendert van Doorn
   10.68 +IBM T.J. Watson Research Center
   10.69 +19 Skyline Drive
   10.70 +Hawthorne, NY 10532
   10.71 +leendert@watson.ibm.com
   10.72 +
   10.73 +
   10.74 +Tested Operating Systems
   10.75 +------------------------
   10.76 +
   10.77 +Since vmxassist uses partial emulation, it may always miss opcodes
   10.78 +that are required by a particular OS. The table below lists the OSes
   10.79 +I have tried.  The Install column indicates a full CD/DVD install into
   10.80 +a VMX partition. The Disk column indicates booting from prefabricated
   10.81 +disk image.
   10.82 +
   10.83 +Operating System			Install		Disk
   10.84 +------------------------------------------------------------
   10.85 +RedHat Enterprise Linux (RHEL3_U5)	Yes		Yes
   10.86 +Fedora Code (FC3)			(-)		Yes
   10.87 +FreeBSD 5.3				(-)		Yes
   10.88 +MS-DOS 5.0				(-)		Yes
   10.89 +
   10.90 +(-) not tried yet
   10.91 +
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/tools/firmware/rombios/Makefile	Wed Jun 08 12:48:07 2005 +0000
    11.3 @@ -0,0 +1,58 @@
    11.4 +BIOS_BUILDS = BIOS-bochs-latest
    11.5 +#BIOS_BUILDS += BIOS-bochs-2-processors
    11.6 +#BIOS_BUILDS += BIOS-bochs-4-processors
    11.7 +#BIOS_BUILDS += BIOS-bochs-8-processors
    11.8 +
    11.9 +all: bios
   11.10 +
   11.11 +bios: biossums ${BIOS_BUILDS}
   11.12 +
   11.13 +clean:
   11.14 +	rm -f  *.o *.a *.s rombios.bin _rombios*_.c
   11.15 +	rm -f  as86-sym.txt ld86-sym.txt 
   11.16 +	rm -f  rombios*.txt rombios*.sym usage biossums
   11.17 +	rm -f  BIOS-bochs-*
   11.18 +
   11.19 +BIOS-bochs-latest: rombios.c biossums
   11.20 +	gcc -DBX_SMP_PROCESSORS=1 -E -P $< > _rombios_.c
   11.21 +	bcc -o rombios.s -C-c -D__i86__ -0 -S _rombios_.c
   11.22 +	sed -e 's/^\.text//' -e 's/^\.data//' rombios.s > _rombios_.s
   11.23 +	as86 _rombios_.s -b tmp.bin -u- -w- -g -0 -j -O -l rombios.txt
   11.24 +	-perl makesym.perl < rombios.txt > rombios.sym
   11.25 +	mv tmp.bin BIOS-bochs-latest
   11.26 +	./biossums BIOS-bochs-latest
   11.27 +	rm -f _rombios_.s
   11.28 +
   11.29 +BIOS-bochs-2-processors: rombios.c biossums
   11.30 +	gcc -DBX_SMP_PROCESSORS=2 -E -P $< > _rombios2_.c
   11.31 +	bcc -o rombios2.s -C-c -D__i86__ -0 -S _rombios2_.c
   11.32 +	sed -e 's/^\.text//' -e 's/^\.data//' rombios2.s > _rombios2_.s
   11.33 +	as86 _rombios2_.s -b tmp2.bin -u- -w- -g -0 -j -O -l rombios2.txt
   11.34 +	-perl makesym.perl < rombios2.txt > rombios2.sym
   11.35 +	mv tmp2.bin BIOS-bochs-2-processors
   11.36 +	./biossums BIOS-bochs-2-processors
   11.37 +	rm -f _rombios2_.s
   11.38 +
   11.39 +BIOS-bochs-4-processors: rombios.c biossums
   11.40 +	gcc -DBX_SMP_PROCESSORS=4 -E -P $< > _rombios4_.c
   11.41 +	bcc -o rombios4.s -C-c -D__i86__ -0 -S _rombios4_.c
   11.42 +	sed -e 's/^\.text//' -e 's/^\.data//' rombios4.s > _rombios4_.s
   11.43 +	as86 _rombios4_.s -b tmp4.bin -u- -w- -g -0 -j -O -l rombios4.txt
   11.44 +	-perl makesym.perl < rombios4.txt > rombios4.sym
   11.45 +	mv tmp4.bin BIOS-bochs-4-processors
   11.46 +	./biossums BIOS-bochs-4-processors
   11.47 +	rm -f _rombios4_.s
   11.48 +
   11.49 +BIOS-bochs-8-processors: rombios.c biossums
   11.50 +	gcc -DBX_SMP_PROCESSORS=8 -E -P $< > _rombios8_.c
   11.51 +	bcc -o rombios8.s -C-c -D__i86__ -0 -S _rombios8_.c
   11.52 +	sed -e 's/^\.text//' -e 's/^\.data//' rombios8.s > _rombios8_.s
   11.53 +	as86 _rombios8_.s -b tmp8.bin -u- -w- -g -0 -j -O -l rombios8.txt
   11.54 +	-perl makesym.perl < rombios8.txt > rombios8.sym
   11.55 +	mv tmp8.bin BIOS-bochs-8-processors
   11.56 +	./biossums BIOS-bochs-8-processors
   11.57 +	rm -f _rombios8_.s
   11.58 +
   11.59 +biossums: biossums.c
   11.60 +	gcc -o biossums biossums.c
   11.61 +
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/tools/firmware/rombios/apmbios.S	Wed Jun 08 12:48:07 2005 +0000
    12.3 @@ -0,0 +1,329 @@
    12.4 +//  APM BIOS support for the Bochs BIOS
    12.5 +//  Copyright (C) 2004 Fabrice Bellard
    12.6 +//
    12.7 +//  Debugging extensions, 16-bit interface and extended power options
    12.8 +//  Copyright (C) 2005 Struan Bartlett
    12.9 +//
   12.10 +//  This library is free software; you can redistribute it and/or
   12.11 +//  modify it under the terms of the GNU Lesser General Public
   12.12 +//  License as published by the Free Software Foundation; either
   12.13 +//  version 2 of the License, or (at your option) any later version.
   12.14 +//
   12.15 +//  This library is distributed in the hope that it will be useful,
   12.16 +//  but WITHOUT ANY WARRANTY; without even the implied warranty of
   12.17 +//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   12.18 +//  Lesser General Public License for more details.
   12.19 +//
   12.20 +//  You should have received a copy of the GNU Lesser General Public
   12.21 +//  License along with this library; if not, write to the Free Software
   12.22 +//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   12.23 +
   12.24 +#if defined(APM_REAL)
   12.25 +#define APMSYM(s) apmreal_ ## s
   12.26 +#elif defined(APM_PROT16)
   12.27 +#define APMSYM(s) apm16_ ## s
   12.28 +#elif defined(APM_PROT32)
   12.29 +#define APMSYM(s) apm32_ ## s
   12.30 +#else
   12.31 +#error unsupported APM mode
   12.32 +#endif
   12.33 +
   12.34 +APMSYM(out_str):      
   12.35 +  push eax
   12.36 +  push ebx
   12.37 +  mov ebx, eax
   12.38 +APMSYM(out_str1):
   12.39 +  SEG CS
   12.40 +  mov al, byte ptr [bx]
   12.41 +  cmp al, #0
   12.42 +  je APMSYM(out_str2)
   12.43 +  outb dx, al
   12.44 +  inc ebx
   12.45 +  jmp APMSYM(out_str1)
   12.46 +APMSYM(out_str2):
   12.47 +  pop ebx
   12.48 +  pop eax
   12.49 +  ret
   12.50 +  
   12.51 +APMSYM(07_poweroff_str):
   12.52 +  .ascii "Shutdown"
   12.53 +  db 0
   12.54 +APMSYM(07_suspend_str):
   12.55 +  .ascii "Suspend"
   12.56 +  db 0
   12.57 +APMSYM(07_standby_str):
   12.58 +  .ascii "Standby"
   12.59 +  db 0
   12.60 +  
   12.61 +#if DEBUG_APM
   12.62 +APMSYM(put_str):      
   12.63 +  push edx
   12.64 +  mov dx, #INFO_PORT
   12.65 +  call APMSYM(out_str)
   12.66 +  pop edx
   12.67 +  ret
   12.68 +  
   12.69 +; print the hex number in eax
   12.70 +APMSYM(put_num):      
   12.71 +  push eax
   12.72 +  push ebx
   12.73 +  push ecx
   12.74 +  push edx
   12.75 +  mov ecx, eax
   12.76 +  mov bx, #8
   12.77 +  mov dx, #INFO_PORT
   12.78 +APMSYM(put_num1):
   12.79 +  mov eax, ecx
   12.80 +  shr eax, #28
   12.81 +  add al, #0x30
   12.82 +  cmp al, #0x39
   12.83 +  jbe APMSYM(put_num2)
   12.84 +  add al, #0x27
   12.85 +APMSYM(put_num2):
   12.86 +  outb dx, al
   12.87 +  shl ecx, #4
   12.88 +  dec bx
   12.89 +  jne APMSYM(put_num1)
   12.90 +  pop edx
   12.91 +  pop ecx
   12.92 +  pop ebx
   12.93 +  pop eax
   12.94 +  ret
   12.95 +
   12.96 +APMSYM(put_reg):
   12.97 +  outb dx, al
   12.98 +  shr eax, #8
   12.99 +  outb dx, al
  12.100 +  shr eax, #8
  12.101 +  outb dx, al
  12.102 +  shr eax, #8
  12.103 +  outb dx, al
  12.104 +  
  12.105 +  mov eax,ebx
  12.106 +  call APMSYM(put_num)
  12.107 +  
  12.108 +  mov al, #0x3b
  12.109 +  outb dx,al
  12.110 +  mov al, #0x20
  12.111 +  outb dx,al
  12.112 +  ret  
  12.113 +
  12.114 +APMSYM(put_regs):
  12.115 +  push eax
  12.116 +  push edx
  12.117 +  push ebx
  12.118 +  mov dx, #INFO_PORT
  12.119 +  
  12.120 +  mov ebx, eax
  12.121 +  mov eax, #0x3d584145 // 'EAX='
  12.122 +  call APMSYM(put_reg)
  12.123 +  pop ebx
  12.124 +  push ebx
  12.125 +  mov eax, #0x3d584245 // 'EBX='
  12.126 +  call APMSYM(put_reg)
  12.127 +  mov ebx, ecx
  12.128 +  mov eax, #0x3d584345 // 'ECX='
  12.129 +  call APMSYM(put_reg)
  12.130 +  mov ebx, edx
  12.131 +  mov eax, #0x3d584445 // 'EDX='
  12.132 +  call APMSYM(put_reg)
  12.133 +  mov ebx, esi
  12.134 +  mov eax, #0x3d495345 // 'ESI='
  12.135 +  call APMSYM(put_reg)
  12.136 +  mov ebx, edi
  12.137 +  mov eax, #0x3d494445 // 'EDI='
  12.138 +  call APMSYM(put_reg)
  12.139 +  
  12.140 +  mov al, #0x0a
  12.141 +  outb dx, al
  12.142 +  pop ebx
  12.143 +  pop edx
  12.144 +  pop eax
  12.145 +  ret
  12.146 +#endif
  12.147 +
  12.148 +#if defined(APM_PROT32)
  12.149 +_apm32_entry:
  12.150 +#endif
  12.151 +#if defined(APM_PROT16)
  12.152 +_apm16_entry:
  12.153 +#endif
  12.154 +  pushf
  12.155 +  
  12.156 +#if defined(APM_REAL)
  12.157 +_apmreal_entry:
  12.158 +#endif
  12.159 +
  12.160 +#if DEBUG_APM
  12.161 +  call APMSYM(put_regs)
  12.162 +#endif
  12.163 +
  12.164 +#if defined(APM_REAL)
  12.165 +;-----------------
  12.166 +; APM installation check
  12.167 +APMSYM(00):
  12.168 +  cmp al, #0x00
  12.169 +  jne APMSYM(01)
  12.170 +
  12.171 +  mov ah, #1 // APM major version
  12.172 +  mov al, #2 // APM minor version
  12.173 +  
  12.174 +  mov bh, #0x50 // 'P'
  12.175 +  mov bl, #0x4d // 'M'
  12.176 +  
  12.177 +  // bit 0 : 16 bit interface supported
  12.178 +  // bit 1 : 32 bit interface supported
  12.179 +  mov cx, #0x3
  12.180 +  jmp APMSYM(ok)
  12.181 +  
  12.182 +;-----------------
  12.183 +; APM real mode interface connect
  12.184 +APMSYM(01):
  12.185 +  cmp al, #0x01
  12.186 +  jne APMSYM(02)
  12.187 +  jmp APMSYM(ok)
  12.188 +
  12.189 +;-----------------
  12.190 +; APM 16 bit protected mode interface connect
  12.191 +APMSYM(02):
  12.192 +  cmp al, #0x02
  12.193 +  jne APMSYM(03)
  12.194 +
  12.195 +  mov bx, #_apm16_entry
  12.196 +  
  12.197 +  mov ax, #0xf000 // 16 bit code segment base
  12.198 +  mov si, #0xfff0 // 16 bit code segment size
  12.199 +  mov cx, #0xf000 // data segment address
  12.200 +  mov di, #0xfff0 // data segment length
  12.201 +  jmp APMSYM(ok)
  12.202 +
  12.203 +;-----------------
  12.204 +; APM 32 bit protected mode interface connect
  12.205 +APMSYM(03):
  12.206 +  cmp al, #0x03
  12.207 +  jne APMSYM(04)
  12.208 +  mov ax, #0xf000 // 32 bit code segment base
  12.209 +  mov ebx, #_apm32_entry
  12.210 +  mov cx, #0xf000 // 16 bit code segment base
  12.211 +  // 32 bit code segment size (low 16 bits)
  12.212 +  // 16 bit code segment size (high 16 bits)
  12.213 +  mov esi, #0xfff0fff0
  12.214 +  mov dx, #0xf000 // data segment address
  12.215 +  mov di, #0xfff0 // data segment length
  12.216 +  jmp APMSYM(ok)
  12.217 +#endif
  12.218 +
  12.219 +;-----------------
  12.220 +; APM interface disconnect
  12.221 +APMSYM(04):
  12.222 +  cmp al, #0x04
  12.223 +  jne APMSYM(07)
  12.224 +  jmp APMSYM(ok)
  12.225 +
  12.226 +;-----------------
  12.227 +; APM Set Power State
  12.228 +APMSYM(07):
  12.229 +  cmp al, #0x07
  12.230 +  jne APMSYM(0a)
  12.231 +  
  12.232 +  cmp bx, #1
  12.233 +  jne APMSYM(ok)
  12.234 +  
  12.235 +  cmp cx, #3
  12.236 +  je APMSYM(07_poweroff)
  12.237 +  
  12.238 +  cmp cx, #2
  12.239 +  je APMSYM(07_suspend)
  12.240 +  
  12.241 +  cmp cx, #1
  12.242 +  je APMSYM(07_standby)
  12.243 +  
  12.244 +  jne APMSYM(ok)
  12.245 +  
  12.246 +APMSYM(07_poweroff):  
  12.247 +  // send power off event to emulator
  12.248 +  cli
  12.249 +  mov dx, #0x8900
  12.250 +  mov ax, #APMSYM(07_poweroff_str)
  12.251 +  call APMSYM(out_str)
  12.252 +
  12.253 +APMSYM(07_1):
  12.254 +  hlt
  12.255 +  jmp APMSYM(07_1)
  12.256 +
  12.257 +APMSYM(07_suspend):
  12.258 +  push edx
  12.259 +  mov dx, #0x8900
  12.260 +  mov ax, #APMSYM(07_suspend_str)
  12.261 +  call APMSYM(out_str)
  12.262 +  pop edx
  12.263 +  jmp APMSYM(ok)
  12.264 +
  12.265 +APMSYM(07_standby):
  12.266 +  push edx
  12.267 +  mov dx, #0x8900
  12.268 +  mov ax, #APMSYM(07_standby_str)
  12.269 +  call APMSYM(out_str)
  12.270 +  pop edx
  12.271 +  jmp APMSYM(ok)
  12.272 +
  12.273 +;-----------------
  12.274 +; Get Power Status
  12.275 +APMSYM(0a):
  12.276 +  cmp al, #0x0a
  12.277 +  jne APMSYM(0b)
  12.278 +  mov bh, #0x01 // on line
  12.279 +  // mov bh, #0x02 // battery
  12.280 +  mov bl, #0xff // unknown battery status
  12.281 +  // mov bl, #0x03 // charging
  12.282 +  mov ch, #0x80 // no system battery
  12.283 +  // mov ch, #0x8 // charging
  12.284 +  mov cl, #0xff // unknown remaining time
  12.285 +  // mov cl, #50
  12.286 +  mov dx, #0xffff // unknown remaining time 
  12.287 +  mov si, #0      // zero battery
  12.288 +  // mov si, #1      // one battery
  12.289 +  jmp APMSYM(ok)
  12.290 +
  12.291 +;-----------------
  12.292 +; Get PM Event
  12.293 +APMSYM(0b):
  12.294 +  cmp al, #0x0b
  12.295 +  jne APMSYM(0e)
  12.296 +  mov ah, #0x80 // no event pending
  12.297 +  jmp APMSYM(error)
  12.298 +   
  12.299 +;-----------------
  12.300 +; APM Driver Version
  12.301 +APMSYM(0e):
  12.302 +  cmp al, #0x0e
  12.303 +  jne APMSYM(unimplemented)
  12.304 +  
  12.305 +  mov ah, #1
  12.306 +  mov al, #2
  12.307 +  
  12.308 +  jmp APMSYM(ok)
  12.309 +
  12.310 +;-----------------
  12.311 +APMSYM(ok):
  12.312 +  popf
  12.313 +  clc
  12.314 +#if defined(APM_REAL)
  12.315 +  jmp iret_modify_cf
  12.316 +#else
  12.317 +  retf  
  12.318 +#endif
  12.319 +APMSYM(unimplemented):
  12.320 +APMSYM(error):
  12.321 +  popf
  12.322 +  stc
  12.323 +#if defined(APM_REAL)
  12.324 +  jmp iret_modify_cf
  12.325 +#else
  12.326 +  retf
  12.327 +#endif
  12.328 +
  12.329 +#undef APM_PROT32
  12.330 +#undef APM_PROT16
  12.331 +#undef APM_REAL
  12.332 +#undef APMSYM
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/tools/firmware/rombios/biossums.c	Wed Jun 08 12:48:07 2005 +0000
    13.3 @@ -0,0 +1,478 @@
    13.4 +/* biossums.c  --- written by Eike W. */
    13.5 +
    13.6 +#include <stdlib.h>
    13.7 +#include <stdio.h>
    13.8 +
    13.9 +typedef unsigned char byte;
   13.10 +
   13.11 +void check( int value, char* message );
   13.12 +
   13.13 +#define LEN_BIOS_DATA 0x10000
   13.14 +#define MAX_OFFSET    (LEN_BIOS_DATA - 1)
   13.15 +
   13.16 +
   13.17 +#define BIOS_OFFSET 0xFFFF
   13.18 +
   13.19 +long chksum_bios_get_offset( byte* data, long offset );
   13.20 +byte chksum_bios_calc_value( byte* data, long offset );
   13.21 +byte chksum_bios_get_value(  byte* data, long offset );
   13.22 +void chksum_bios_set_value(  byte* data, long offset, byte value );
   13.23 +
   13.24 +
   13.25 +#define _32__LEN         9
   13.26 +#define _32__CHKSUM     10
   13.27 +
   13.28 +#define _32__MINHDR     16
   13.29 +
   13.30 +long chksum__32__get_offset( byte* data, long offset );
   13.31 +byte chksum__32__calc_value( byte* data, long offset );
   13.32 +byte chksum__32__get_value(  byte* data, long offset );
   13.33 +void chksum__32__set_value(  byte* data, long offset, byte value );
   13.34 +
   13.35 +
   13.36 +#define _MP__LEN         8
   13.37 +#define _MP__CHKSUM     10
   13.38 +
   13.39 +#define _MP__MINHDR     16
   13.40 +
   13.41 +long chksum__mp__get_offset( byte* data, long offset );
   13.42 +byte chksum__mp__calc_value( byte* data, long offset );
   13.43 +byte chksum__mp__get_value(  byte* data, long offset );
   13.44 +void chksum__mp__set_value(  byte* data, long offset, byte value );
   13.45 +
   13.46 +
   13.47 +#define PCMP_BASELEN     4
   13.48 +#define PCMP_CHKSUM      7
   13.49 +#define PCMP_EXT_LEN    40
   13.50 +#define PCMP_EXT_CHKSUM 42
   13.51 +
   13.52 +#define PCMP_MINHDR     42
   13.53 +
   13.54 +long chksum_pcmp_get_offset( byte* data, long offset );
   13.55 +byte chksum_pcmp_calc_value( byte* data, long offset );
   13.56 +byte chksum_pcmp_get_value(  byte* data, long offset );
   13.57 +void chksum_pcmp_set_value(  byte* data, long offset, byte value );
   13.58 +
   13.59 +
   13.60 +#define _PIR_LEN         6
   13.61 +#define _PIR_CHKSUM     31
   13.62 +
   13.63 +#define _PIR_MINHDR     32
   13.64 +
   13.65 +long chksum__pir_get_offset( byte *data, long offset );
   13.66 +byte chksum__pir_calc_value( byte* data, long offset );
   13.67 +byte chksum__pir_get_value(  byte* data, long offset );
   13.68 +void chksum__pir_set_value(  byte* data, long offset, byte value );
   13.69 +
   13.70 +
   13.71 +byte bios_data[LEN_BIOS_DATA];
   13.72 +
   13.73 +
   13.74 +int main( int argc, char* argv[] ) {
   13.75 +
   13.76 +  FILE* stream;
   13.77 +  long  offset, tmp_offset;
   13.78 +  byte  cur_val = 0, new_val = 0;
   13.79 +  int   hits;
   13.80 +
   13.81 +
   13.82 +  if( argc != 2 ) {
   13.83 +    printf( "Error. Need a file-name as an argument.\n" );
   13.84 +    exit( EXIT_FAILURE );
   13.85 +  }
   13.86 +
   13.87 +  if(( stream = fopen( argv[1], "rb" )) == NULL ) {
   13.88 +    printf( "Error opening %s for reading.\n", argv[1] );
   13.89 +    exit( EXIT_FAILURE );
   13.90 +  }
   13.91 +  if( fread( bios_data, 1, LEN_BIOS_DATA, stream ) < LEN_BIOS_DATA ) {
   13.92 +    printf( "Error reading 64KBytes from %s.\n", argv[1] );
   13.93 +    fclose( stream );
   13.94 +    exit( EXIT_FAILURE );
   13.95 +  }
   13.96 +  fclose( stream );
   13.97 +
   13.98 +  hits   = 0;
   13.99 +  offset = 0L;
  13.100 +  while( (tmp_offset = chksum__32__get_offset( bios_data, offset )) != -1L ) {
  13.101 +    offset  = tmp_offset;
  13.102 +    cur_val = chksum__32__get_value(  bios_data, offset );
  13.103 +    new_val = chksum__32__calc_value( bios_data, offset );
  13.104 +    printf( "\n\nPCI-Bios header at: 0x%4lX\n", offset  );
  13.105 +    printf( "Current checksum:     0x%02X\n",   cur_val );
  13.106 +    printf( "Calculated checksum:  0x%02X  ",   new_val );
  13.107 +    hits++;
  13.108 +  }
  13.109 +  if( hits == 1 && cur_val != new_val ) {
  13.110 +    printf( "Setting checksum." );
  13.111 +    chksum__32__set_value( bios_data, offset, new_val );
  13.112 +  }
  13.113 +  if( hits >= 2 ) {
  13.114 +    printf( "Multiple PCI headers! No checksum set." );
  13.115 +  }
  13.116 +  if( hits ) {
  13.117 +    printf( "\n" );
  13.118 +  }
  13.119 +
  13.120 +
  13.121 +  hits   = 0;
  13.122 +  offset = 0L;
  13.123 +  while( (tmp_offset = chksum__mp__get_offset( bios_data, offset )) != -1L ) {
  13.124 +    offset  = tmp_offset;
  13.125 +    cur_val = chksum__mp__get_value(  bios_data, offset );
  13.126 +    new_val = chksum__mp__calc_value( bios_data, offset );
  13.127 +    printf( "\n\nMP header at:       0x%4lX\n", offset  );
  13.128 +    printf( "Current checksum:     0x%02X\n",   cur_val );
  13.129 +    printf( "Calculated checksum:  0x%02X  ",   new_val );
  13.130 +    hits++;
  13.131 +  }
  13.132 +  if( hits == 1 && cur_val != new_val ) {
  13.133 +    printf( "Setting checksum." );
  13.134 +    chksum__mp__set_value( bios_data, offset, new_val );
  13.135 +  }
  13.136 +  if( hits >= 2 ) {
  13.137 +    printf( "Warning! Multiple MP headers. No checksum set." );
  13.138 +  }
  13.139 +  if( hits ) {
  13.140 +    printf( "\n" );
  13.141 +  }
  13.142 +
  13.143 +
  13.144 +  hits   = 0;
  13.145 +  offset = 0L;
  13.146 +  while( (tmp_offset = chksum_pcmp_get_offset( bios_data, offset )) != -1L ) {
  13.147 +    offset  = tmp_offset;
  13.148 +    cur_val = chksum_pcmp_get_value(  bios_data, offset );
  13.149 +    new_val = chksum_pcmp_calc_value( bios_data, offset );
  13.150 +    printf( "\n\nPCMP header at:     0x%4lX\n", offset  );
  13.151 +    printf( "Current checksum:     0x%02X\n",   cur_val );
  13.152 +    printf( "Calculated checksum:  0x%02X  ",   new_val );
  13.153 +    hits++;
  13.154 +  }
  13.155 +  if( hits == 1 && cur_val != new_val ) {
  13.156 +    printf( "Setting checksum." );
  13.157 +    chksum_pcmp_set_value( bios_data, offset, new_val );
  13.158 +  }
  13.159 +  if( hits >= 2 ) {
  13.160 +    printf( "Warning! Multiple PCMP headers. No checksum set." );
  13.161 +  }
  13.162 +  if( hits ) {
  13.163 +    printf( "\n" );
  13.164 +  }
  13.165 +
  13.166 +
  13.167 +  hits   = 0;
  13.168 +  offset = 0L;
  13.169 +  while( (tmp_offset = chksum__pir_get_offset( bios_data, offset )) != -1L ) {
  13.170 +    offset  = tmp_offset;
  13.171 +    cur_val = chksum__pir_get_value(  bios_data, offset );
  13.172 +    new_val = chksum__pir_calc_value( bios_data, offset );
  13.173 +    printf( "\n\n$PIR header at:     0x%4lX\n", offset  );
  13.174 +    printf( "Current checksum:     0x%02X\n",   cur_val );
  13.175 +    printf( "Calculated checksum:  0x%02X\n  ",  new_val );
  13.176 +    hits++;
  13.177 +  }
  13.178 +  if( hits == 1 && cur_val != new_val ) {
  13.179 +    printf( "Setting checksum." );
  13.180 +    chksum__pir_set_value( bios_data, offset, new_val );
  13.181 +  }
  13.182 +  if( hits >= 2 ) {
  13.183 +    printf( "Warning! Multiple $PIR headers. No checksum set." );
  13.184 +  }
  13.185 +  if( hits ) {
  13.186 +    printf( "\n" );
  13.187 +  }
  13.188 +
  13.189 +
  13.190 +  offset  = 0L;
  13.191 +  offset  = chksum_bios_get_offset( bios_data, offset );
  13.192 +  cur_val = chksum_bios_get_value(  bios_data, offset );
  13.193 +  new_val = chksum_bios_calc_value( bios_data, offset );
  13.194 +  printf( "\n\nBios checksum at:   0x%4lX\n", offset  );
  13.195 +  printf( "Current checksum:     0x%02X\n",   cur_val );
  13.196 +  printf( "Calculated checksum:  0x%02X  ",   new_val );
  13.197 +  if( cur_val != new_val ) {
  13.198 +    printf( "Setting checksum." );
  13.199 +    chksum_bios_set_value( bios_data, offset, new_val );
  13.200 +  }
  13.201 +  printf( "\n" );
  13.202 +
  13.203 +
  13.204 +  if(( stream = fopen( argv[1], "wb" )) == NULL ) {
  13.205 +    printf( "Error opening %s for writing.\n", argv[1] );
  13.206 +    exit( EXIT_FAILURE );
  13.207 +  }
  13.208 +  if( fwrite( bios_data, 1, LEN_BIOS_DATA, stream ) < LEN_BIOS_DATA ) {
  13.209 +    printf( "Error writing 64KBytes to %s.\n", argv[1] );
  13.210 +    fclose( stream );
  13.211 +    exit( EXIT_FAILURE );
  13.212 +  }
  13.213 +  fclose( stream );
  13.214 +
  13.215 +  return( EXIT_SUCCESS );
  13.216 +}
  13.217 +
  13.218 +
  13.219 +void check( int okay, char* message ) {
  13.220 +
  13.221 +  if( !okay ) {
  13.222 +    printf( "\n\nError. %s.\n", message );
  13.223 +    exit( EXIT_FAILURE );
  13.224 +  }
  13.225 +}
  13.226 +
  13.227 +
  13.228 +long chksum_bios_get_offset( byte* data, long offset ) {
  13.229 +
  13.230 +  return( BIOS_OFFSET );
  13.231 +}
  13.232 +
  13.233 +
  13.234 +byte chksum_bios_calc_value( byte* data, long offset ) {
  13.235 +
  13.236 +  int   i;
  13.237 +  byte  sum;
  13.238 +
  13.239 +  sum = 0;
  13.240 +  for( i = 0; i < MAX_OFFSET; i++ ) {
  13.241 +    sum = sum + *( data + i );
  13.242 +  }
  13.243 +  sum = -sum;          /* iso ensures -s + s == 0 on unsigned types */
  13.244 +  return( sum );
  13.245 +}
  13.246 +
  13.247 +
  13.248 +byte chksum_bios_get_value( byte* data, long offset ) {
  13.249 +
  13.250 +  return( *( data + BIOS_OFFSET ) );
  13.251 +}
  13.252 +
  13.253 +
  13.254 +void chksum_bios_set_value( byte* data, long offset, byte value ) {
  13.255 +
  13.256 +  *( data + BIOS_OFFSET ) = value;
  13.257 +}
  13.258 +
  13.259 +
  13.260 +byte chksum__32__calc_value( byte* data, long offset ) {
  13.261 +
  13.262 +  int           i;
  13.263 +  int           len;
  13.264 +  byte sum;
  13.265 +
  13.266 +  check( offset + _32__MINHDR <= MAX_OFFSET, "_32_ header out of bounds" );
  13.267 +  len = *( data + offset + _32__LEN ) << 4;
  13.268 +  check( offset + len <= MAX_OFFSET, "_32_ header-length out of bounds" );
  13.269 +  sum = 0;
  13.270 +  for( i = 0; i < len; i++ ) {
  13.271 +    if( i != _32__CHKSUM ) {
  13.272 +      sum = sum + *( data + offset + i );
  13.273 +    }
  13.274 +  }
  13.275 +  sum = -sum;
  13.276 +  return( sum );
  13.277 +}
  13.278 +
  13.279 +
  13.280 +long chksum__32__get_offset( byte* data, long offset ) {
  13.281 +
  13.282 +  long result = -1L;
  13.283 +
  13.284 +  offset = offset + 0x0F;
  13.285 +  offset = offset & ~( 0x0F );
  13.286 +  while( offset + 16 < MAX_OFFSET ) {
  13.287 +    offset = offset + 16;
  13.288 +    if( *( data + offset + 0 ) == '_' && \
  13.289 +        *( data + offset + 1 ) == '3' && \
  13.290 +        *( data + offset + 2 ) == '2' && \
  13.291 +        *( data + offset + 3 ) == '_' ) {
  13.292 +      result = offset;
  13.293 +      break;
  13.294 +    }
  13.295 +  }
  13.296 +  return( result );
  13.297 +}
  13.298 +
  13.299 +
  13.300 +byte chksum__32__get_value( byte* data, long offset ) {
  13.301 +
  13.302 +  check( offset + _32__CHKSUM <= MAX_OFFSET, "PCI-Bios checksum out of bounds" );
  13.303 +  return(  *( data + offset + _32__CHKSUM ) );
  13.304 +}
  13.305 +
  13.306 +
  13.307 +void chksum__32__set_value( byte* data, long offset, byte value ) {
  13.308 +
  13.309 +  check( offset + _32__CHKSUM <= MAX_OFFSET, "PCI-Bios checksum out of bounds" );
  13.310 +  *( data + offset + _32__CHKSUM ) = value;
  13.311 +}
  13.312 +
  13.313 +
  13.314 +byte chksum__mp__calc_value( byte* data, long offset ) {
  13.315 +
  13.316 +  int   i;
  13.317 +  int   len;
  13.318 +  byte  sum;
  13.319 +
  13.320 +  check( offset + _MP__MINHDR <= MAX_OFFSET, "_MP_ header out of bounds" );
  13.321 +  len = *( data + offset + _MP__LEN ) << 4;
  13.322 +  check( offset + len <= MAX_OFFSET, "_MP_ header-length out of bounds" );
  13.323 +  sum = 0;
  13.324 +  for( i = 0; i < len; i++ ) {
  13.325 +    if( i != _MP__CHKSUM ) {
  13.326 +      sum = sum + *( data + offset + i );
  13.327 +    }
  13.328 +  }
  13.329 +  sum = -sum;
  13.330 +  return( sum );
  13.331 +}
  13.332 +
  13.333 +
  13.334 +long chksum__mp__get_offset( byte* data, long offset ) {
  13.335 +
  13.336 +  long result = -1L;
  13.337 +
  13.338 +  offset = offset + 0x0F;
  13.339 +  offset = offset & ~( 0x0F );
  13.340 +  while( offset + 16 < MAX_OFFSET ) {
  13.341 +    offset = offset + 16;
  13.342 +    if( *( data + offset + 0 ) == '_' && \
  13.343 +        *( data + offset + 1 ) == 'M' && \
  13.344 +        *( data + offset + 2 ) == 'P' && \
  13.345 +        *( data + offset + 3 ) == '_' ) {
  13.346 +      result = offset;
  13.347 +      break;
  13.348 +    }
  13.349 +  }
  13.350 +  return( result );
  13.351 +}
  13.352 +
  13.353 +
  13.354 +byte chksum__mp__get_value( byte* data, long offset ) {
  13.355 +
  13.356 +  check( offset + _MP__CHKSUM <= MAX_OFFSET, "MP checksum out of bounds" );
  13.357 +  return( *( data + offset + _MP__CHKSUM ) );
  13.358 +}
  13.359 +
  13.360 +
  13.361 +void chksum__mp__set_value( byte* data, long offset, byte value ) {
  13.362 +
  13.363 +  check( offset + _MP__CHKSUM <= MAX_OFFSET, "MP checksum out of bounds" );
  13.364 +  *( data + offset + _MP__CHKSUM ) = value;
  13.365 +}
  13.366 +
  13.367 +
  13.368 +byte chksum_pcmp_calc_value( byte* data, long offset ) {
  13.369 +
  13.370 +  int   i;
  13.371 +  int   len;
  13.372 +  byte  sum;
  13.373 +
  13.374 +  check( offset + PCMP_MINHDR <= MAX_OFFSET, "PCMP header out of bounds" );
  13.375 +  len  =   *( data + offset + PCMP_BASELEN )      + \
  13.376 +         ( *( data + offset + PCMP_BASELEN + 1 ) << 8 );
  13.377 +  check( offset + len <= MAX_OFFSET, "PCMP header-length out of bounds" );
  13.378 +  if( *( data + offset + PCMP_EXT_LEN )     | \
  13.379 +      *( data + offset + PCMP_EXT_LEN + 1 ) | \
  13.380 +      *( data + offset + PCMP_EXT_CHKSUM ) ) {
  13.381 +    check( 0, "PCMP header indicates extended tables (unsupported)" );
  13.382 +  }
  13.383 +  sum = 0;
  13.384 +  for( i = 0; i < len; i++ ) {
  13.385 +    if( i != PCMP_CHKSUM ) {
  13.386 +      sum = sum + *( data + offset + i );
  13.387 +    }
  13.388 +  }
  13.389 +  sum = -sum;
  13.390 +  return( sum );
  13.391 +}
  13.392 +
  13.393 +
  13.394 +long chksum_pcmp_get_offset( byte* data, long offset ) {
  13.395 +
  13.396 +  long result = -1L;
  13.397 +
  13.398 +  offset = offset + 0x0F;
  13.399 +  offset = offset & ~( 0x0F );
  13.400 +  while( offset + 16 < MAX_OFFSET ) {
  13.401 +    offset = offset + 16;
  13.402 +    if( *( data + offset + 0 ) == 'P' && \
  13.403 +        *( data + offset + 1 ) == 'C' && \
  13.404 +        *( data + offset + 2 ) == 'M' && \
  13.405 +        *( data + offset + 3 ) == 'P' ) {
  13.406 +      result = offset;
  13.407 +      break;
  13.408 +    }
  13.409 +  }
  13.410 +  return( result );
  13.411 +}
  13.412 +
  13.413 +
  13.414 +byte chksum_pcmp_get_value( byte* data, long offset ) {
  13.415 +
  13.416 +  check( offset + PCMP_CHKSUM <= MAX_OFFSET, "PCMP checksum out of bounds" );
  13.417 +  return( *( data + offset + PCMP_CHKSUM ) );
  13.418 +}
  13.419 +
  13.420 +
  13.421 +void chksum_pcmp_set_value( byte* data, long offset, byte value ) {
  13.422 +
  13.423 +  check( offset + PCMP_CHKSUM <= MAX_OFFSET, "PCMP checksum out of bounds" );
  13.424 +  *( data + offset + PCMP_CHKSUM ) = value;
  13.425 +}
  13.426 +
  13.427 +
  13.428 +byte chksum__pir_calc_value( byte* data, long offset ) {
  13.429 +
  13.430 +  int   i;
  13.431 +  int   len;
  13.432 +  byte  sum;
  13.433 +
  13.434 +  check( offset + _PIR_MINHDR <= MAX_OFFSET, "$PIR header out of bounds" );
  13.435 +  len  =   *( data + offset + _PIR_LEN )      + \
  13.436 +         ( *( data + offset + _PIR_LEN + 1 ) << 8 );
  13.437 +  check( offset + len <= MAX_OFFSET, "$PIR header-length out of bounds" );
  13.438 +  sum = 0;
  13.439 +  for( i = 0; i < len; i++ ) {
  13.440 +    if( i != _PIR_CHKSUM ) {
  13.441 +      sum = sum + *( data + offset + i );
  13.442 +    }
  13.443 +  }
  13.444 +  sum = -sum;
  13.445 +  return( sum );
  13.446 +}
  13.447 +
  13.448 +
  13.449 +long chksum__pir_get_offset( byte* data, long offset ) {
  13.450 +
  13.451 +  long result = -1L;
  13.452 +
  13.453 +  offset = offset + 0x0F;
  13.454 +  offset = offset & ~( 0x0F );
  13.455 +  while( offset + 16 < MAX_OFFSET ) {
  13.456 +    offset = offset + 16;
  13.457 +    if( *( data + offset + 0 ) == '$' && \
  13.458 +        *( data + offset + 1 ) == 'P' && \
  13.459 +        *( data + offset + 2 ) == 'I' && \
  13.460 +        *( data + offset + 3 ) == 'R' ) {
  13.461 +      result = offset;
  13.462 +      break;
  13.463 +    }
  13.464 +  }
  13.465 +  return( result );
  13.466 +}
  13.467 +
  13.468 +
  13.469 +byte chksum__pir_get_value( byte* data, long offset ) {
  13.470 +
  13.471 +  check( offset + _PIR_CHKSUM <= MAX_OFFSET, "$PIR checksum out of bounds" );
  13.472 +  return(  *( data + offset + _PIR_CHKSUM ) );
  13.473 +}
  13.474 +
  13.475 +
  13.476 +void chksum__pir_set_value( byte* data, long offset, byte value ) {
  13.477 +
  13.478 +  check( offset + _PIR_CHKSUM <= MAX_OFFSET, "$PIR checksum out of bounds" );
  13.479 +  *( data + offset + _PIR_CHKSUM ) = value;
  13.480 +}
  13.481 +
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/tools/firmware/rombios/makesym.perl	Wed Jun 08 12:48:07 2005 +0000
    14.3 @@ -0,0 +1,31 @@
    14.4 +#!/usr/bin/perl
    14.5 +#
    14.6 +# $Id: makesym.perl,v 1.1 2002/11/24 22:45:40 bdenney Exp $
    14.7 +#
    14.8 +# Read output file from as86 (e.g. rombios.txt) and write out a symbol 
    14.9 +# table suitable for the Bochs debugger.
   14.10 +#
   14.11 +
   14.12 +$WHERE_BEFORE_SYM_TABLE = 0;
   14.13 +$WHERE_IN_SYM_TABLE = 1;
   14.14 +$WHERE_AFTER_SYM_TABLE = 2;
   14.15 +
   14.16 +$where = $WHERE_BEFORE_SYM_TABLE;
   14.17 +while (<STDIN>) {
   14.18 +  chop;
   14.19 +  if ($where == WHERE_BEFORE_SYM_TABLE && /^Symbols:/) {
   14.20 +    $where = $WHERE_IN_SYM_TABLE;
   14.21 +  } elsif ($where == $WHERE_IN_SYM_TABLE && /^$/) {
   14.22 +    $where = $WHERE_AFTER_SYM_TABLE;
   14.23 +  }
   14.24 +  if ($where == $WHERE_IN_SYM_TABLE) {
   14.25 +    @F = split (/\s+/);
   14.26 +    ($name[0], $junk, $addr[0], $junk, $name[1], $junk, $addr[1]) = @F;
   14.27 +    foreach $col (0,1) {
   14.28 +      next if length $addr[$col] < 1;
   14.29 +      $addr[$col] =~ tr/A-Z/a-z/;
   14.30 +      $addr[$col] = "000f" . $addr[$col];
   14.31 +      print "$addr[$col] $name[$col]\n";
   14.32 +    }
   14.33 +  }
   14.34 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/tools/firmware/rombios/rombios.c	Wed Jun 08 12:48:07 2005 +0000
    15.3 @@ -0,0 +1,10825 @@
    15.4 +/////////////////////////////////////////////////////////////////////////
    15.5 +// $Id: rombios.c,v 1.138 2005/05/07 15:55:26 vruppert Exp $
    15.6 +/////////////////////////////////////////////////////////////////////////
    15.7 +//
    15.8 +//  Copyright (C) 2002  MandrakeSoft S.A.
    15.9 +//
   15.10 +//    MandrakeSoft S.A.
   15.11 +//    43, rue d'Aboukir
   15.12 +//    75002 Paris - France
   15.13 +//    http://www.linux-mandrake.com/
   15.14 +//    http://www.mandrakesoft.com/
   15.15 +//
   15.16 +//  This library is free software; you can redistribute it and/or
   15.17 +//  modify it under the terms of the GNU Lesser General Public
   15.18 +//  License as published by the Free Software Foundation; either
   15.19 +//  version 2 of the License, or (at your option) any later version.
   15.20 +//
   15.21 +//  This library is distributed in the hope that it will be useful,
   15.22 +//  but WITHOUT ANY WARRANTY; without even the implied warranty of
   15.23 +//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   15.24 +//  Lesser General Public License for more details.
   15.25 +//
   15.26 +//  You should have received a copy of the GNU Lesser General Public
   15.27 +//  License along with this library; if not, write to the Free Software
   15.28 +//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   15.29 +
   15.30 +// ROM BIOS for use with Bochs/Plex x86 emulation environment
   15.31 +
   15.32 +#define VMXASSIST
   15.33 +
   15.34 +// ROM BIOS compatability entry points:
   15.35 +// ===================================
   15.36 +// $e05b ; POST Entry Point
   15.37 +// $e2c3 ; NMI Handler Entry Point
   15.38 +// $e3fe ; INT 13h Fixed Disk Services Entry Point
   15.39 +// $e401 ; Fixed Disk Parameter Table
   15.40 +// $e6f2 ; INT 19h Boot Load Service Entry Point
   15.41 +// $e6f5 ; Configuration Data Table
   15.42 +// $e729 ; Baud Rate Generator Table
   15.43 +// $e739 ; INT 14h Serial Communications Service Entry Point
   15.44 +// $e82e ; INT 16h Keyboard Service Entry Point
   15.45 +// $e987 ; INT 09h Keyboard Service Entry Point
   15.46 +// $ec59 ; INT 13h Diskette Service Entry Point
   15.47 +// $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
   15.48 +// $efc7 ; Diskette Controller Parameter Table
   15.49 +// $efd2 ; INT 17h Printer Service Entry Point
   15.50 +// $f045 ; INT 10 Functions 0-Fh Entry Point
   15.51 +// $f065 ; INT 10h Video Support Service Entry Point
   15.52 +// $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
   15.53 +// $f841 ; INT 12h Memory Size Service Entry Point
   15.54 +// $f84d ; INT 11h Equipment List Service Entry Point
   15.55 +// $f859 ; INT 15h System Services Entry Point
   15.56 +// $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
   15.57 +// $fe6e ; INT 1Ah Time-of-day Service Entry Point
   15.58 +// $fea5 ; INT 08h System Timer ISR Entry Point
   15.59 +// $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
   15.60 +// $ff53 ; IRET Instruction for Dummy Interrupt Handler
   15.61 +// $ff54 ; INT 05h Print Screen Service Entry Point
   15.62 +// $fff0 ; Power-up Entry Point
   15.63 +// $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
   15.64 +// $fffe ; System Model ID
   15.65 +
   15.66 +// NOTES for ATA/ATAPI driver (cbbochs@free.fr)
   15.67 +//   Features
   15.68 +//     - supports up to 4 ATA interfaces
   15.69 +//     - device/geometry detection
   15.70 +//     - 16bits/32bits device access
   15.71 +//     - pchs/lba access
   15.72 +//     - datain/dataout/packet command support
   15.73 +//
   15.74 +// NOTES for El-Torito Boot (cbbochs@free.fr)
   15.75 +//   - CD-ROM booting is only available if ATA/ATAPI Driver is available
   15.76 +//   - Current code is only able to boot mono-session cds 
   15.77 +//   - Current code can not boot and emulate a hard-disk
   15.78 +//     the bios will panic otherwise
   15.79 +//   - Current code also use memory in EBDA segement. 
   15.80 +//   - I used cmos byte 0x3D to store extended information on boot-device
   15.81 +//   - Code has to be modified modified to handle multiple cdrom drives
   15.82 +//   - Here are the cdrom boot failure codes:
   15.83 +//       1 : no atapi device found
   15.84 +//       2 : no atapi cdrom found
   15.85 +//       3 : can not read cd - BRVD
   15.86 +//       4 : cd is not eltorito (BRVD)
   15.87 +//       5 : cd is not eltorito (ISO TAG)
   15.88 +//       6 : cd is not eltorito (ELTORITO TAG)
   15.89 +//       7 : can not read cd - boot catalog
   15.90 +//       8 : boot catalog : bad header
   15.91 +//       9 : boot catalog : bad platform
   15.92 +//      10 : boot catalog : bad signature
   15.93 +//      11 : boot catalog : bootable flag not set
   15.94 +//      12 : can not read cd - boot image
   15.95 +//
   15.96 +//   ATA driver
   15.97 +//   - EBDA segment. 
   15.98 +//     I used memory starting at 0x121 in the segment
   15.99 +//   - the translation policy is defined in cmos regs 0x39 & 0x3a
  15.100 +//
  15.101 +// TODO :
  15.102 +//
  15.103 +//   int74 
  15.104 +//     - needs to be reworked.  Uses direct [bp] offsets. (?)
  15.105 +//
  15.106 +//   int13:
  15.107 +//     - f04 (verify sectors) isn't complete  (?)
  15.108 +//     - f02/03/04 should set current cyl,etc in BDA  (?)
  15.109 +//     - rewrite int13_relocated & clean up int13 entry code
  15.110 +//
  15.111 +//   NOTES:
  15.112 +//   - NMI access (bit7 of addr written to 70h)
  15.113 +//
  15.114 +//   ATA driver
  15.115 +//   - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
  15.116 +//   - could send the multiple-sector read/write commands
  15.117 +//
  15.118 +//   El-Torito
  15.119 +//   - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
  15.120 +//   - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
  15.121 +//   - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
  15.122 +//   - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
  15.123 +//     This is ok. But DL should be reincremented afterwards. 
  15.124 +//   - Fix all "FIXME ElTorito Various"
  15.125 +//   - should be able to boot any cdrom instead of the first one
  15.126 +//
  15.127 +//   BCC Bug: find a generic way to handle the bug of #asm after an "if"  (fixed in 0.16.7)
  15.128 +
  15.129 +#define DEBUG_ROMBIOS      0
  15.130 +
  15.131 +#define DEBUG_ATA          0
  15.132 +#define DEBUG_INT13_HD     0
  15.133 +#define DEBUG_INT13_CD     0
  15.134 +#define DEBUG_INT13_ET     0
  15.135 +#define DEBUG_INT13_FL     0
  15.136 +#define DEBUG_INT15        0
  15.137 +#define DEBUG_INT16        0
  15.138 +#define DEBUG_INT1A        0
  15.139 +#define DEBUG_INT74        0
  15.140 +#define DEBUG_APM          0
  15.141 +
  15.142 +#define BX_CPU           3
  15.143 +#define BX_USE_PS2_MOUSE 1
  15.144 +#define BX_CALL_INT15_4F 1
  15.145 +#define BX_USE_EBDA      1
  15.146 +#define BX_SUPPORT_FLOPPY 1
  15.147 +#define BX_FLOPPY_ON_CNT 37   /* 2 seconds */
  15.148 +#define BX_PCIBIOS       1
  15.149 +#define BX_APM           1
  15.150 +
  15.151 +#define BX_USE_ATADRV    1
  15.152 +#define BX_ELTORITO_BOOT 1
  15.153 +
  15.154 +#define BX_MAX_ATA_INTERFACES   4
  15.155 +#define BX_MAX_ATA_DEVICES      (BX_MAX_ATA_INTERFACES*2)
  15.156 +
  15.157 +#define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
  15.158 +#define BX_DEBUG_SERIAL  0 /* output to COM1 */
  15.159 +
  15.160 +   /* model byte 0xFC = AT */
  15.161 +#define SYS_MODEL_ID     0xFC
  15.162 +#define SYS_SUBMODEL_ID  0x00
  15.163 +#define BIOS_REVISION    1
  15.164 +#define BIOS_CONFIG_TABLE 0xe6f5
  15.165 +
  15.166 +#ifndef BIOS_BUILD_DATE
  15.167 +#  define BIOS_BUILD_DATE "06/23/99"
  15.168 +#endif
  15.169 +
  15.170 +  // 1K of base memory used for Extended Bios Data Area (EBDA)
  15.171 +  // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
  15.172 +#define EBDA_SEG           0x9FC0
  15.173 +#define EBDA_SIZE          1              // In KiB
  15.174 +#define BASE_MEM_IN_K   (640 - EBDA_SIZE)
  15.175 +
  15.176 +  // Define the application NAME
  15.177 +#ifdef VMXASSIST
  15.178 +#  define BX_APPNAME "VMXAssist"
  15.179 +#elif PLEX86
  15.180 +#  define BX_APPNAME "Plex86"
  15.181 +#else
  15.182 +#  define BX_APPNAME "Bochs"
  15.183 +#endif
  15.184 +
  15.185 +  // Sanity Checks
  15.186 +#if BX_USE_ATADRV && BX_CPU<3
  15.187 +#    error The ATA/ATAPI Driver can only to be used with a 386+ cpu
  15.188 +#endif
  15.189 +#if BX_USE_ATADRV && !BX_USE_EBDA
  15.190 +#    error ATA/ATAPI Driver can only be used if EBDA is available
  15.191 +#endif
  15.192 +#if BX_ELTORITO_BOOT && !BX_USE_ATADRV
  15.193 +#    error El-Torito Boot can only be use if ATA/ATAPI Driver is available
  15.194 +#endif
  15.195 +#if BX_PCIBIOS && BX_CPU<3
  15.196 +#    error PCI BIOS can only be used with 386+ cpu
  15.197 +#endif
  15.198 +#if BX_APM && BX_CPU<3
  15.199 +#    error APM BIOS can only be used with 386+ cpu
  15.200 +#endif
  15.201 +
  15.202 +#ifndef BX_SMP_PROCESSORS
  15.203 +#define BX_SMP_PROCESSORS 1
  15.204 +#    warning BX_SMP_PROCESSORS not defined, defaulting to 1
  15.205 +#endif
  15.206 +  
  15.207 +#define PANIC_PORT  0x400
  15.208 +#define PANIC_PORT2 0x401
  15.209 +#define INFO_PORT   0x402
  15.210 +#define DEBUG_PORT  0x403
  15.211 +
  15.212 +// #20  is dec 20
  15.213 +// #$20 is hex 20 = 32
  15.214 +// #0x20 is hex 20 = 32
  15.215 +// LDA  #$20
  15.216 +// JSR  $E820
  15.217 +// LDD  .i,S
  15.218 +// JSR  $C682
  15.219 +// mov al, #$20
  15.220 +
  15.221 +// all hex literals should be prefixed with '0x'
  15.222 +//   grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
  15.223 +// no mov SEG-REG, #value, must mov register into seg-reg
  15.224 +//   grep -i "mov[ ]*.s" rombios.c
  15.225 +
  15.226 +// This is for compiling with gcc2 and gcc3
  15.227 +#define ASM_START #asm
  15.228 +#define ASM_END #endasm
  15.229 +
  15.230 +ASM_START
  15.231 +.rom
  15.232 +
  15.233 +.org 0x0000
  15.234 +
  15.235 +#if BX_CPU >= 3
  15.236 +use16 386
  15.237 +#else
  15.238 +use16 286
  15.239 +#endif
  15.240 +
  15.241 +MACRO HALT
  15.242 +  ;; the HALT macro is called with the line number of the HALT call.
  15.243 +  ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex 
  15.244 +  ;; to print a BX_PANIC message.  This will normally halt the simulation
  15.245 +  ;; with a message such as "BIOS panic at rombios.c, line 4091".
  15.246 +  ;; However, users can choose to make panics non-fatal and continue.
  15.247 +#if BX_VIRTUAL_PORTS
  15.248 +  mov dx,#PANIC_PORT
  15.249 +  mov ax,#?1
  15.250 +  out dx,ax
  15.251 +#else
  15.252 +  mov dx,#0x80
  15.253 +  mov ax,#?1
  15.254 +  out dx,al
  15.255 +#endif
  15.256 +MEND
  15.257 +
  15.258 +MACRO JMP_AP
  15.259 +  db 0xea
  15.260 +  dw ?2
  15.261 +  dw ?1
  15.262 +MEND
  15.263 +
  15.264 +MACRO SET_INT_VECTOR
  15.265 +  mov ax, ?3
  15.266 +  mov ?1*4, ax
  15.267 +  mov ax, ?2
  15.268 +  mov ?1*4+2, ax
  15.269 +MEND
  15.270 +
  15.271 +ASM_END
  15.272 +
  15.273 +typedef unsigned char  Bit8u;
  15.274 +typedef unsigned short Bit16u;
  15.275 +typedef unsigned short bx_bool;
  15.276 +typedef unsigned long  Bit32u;
  15.277 +
  15.278 +#if BX_USE_ATADRV
  15.279 +
  15.280 +  void memsetb(seg,offset,value,count);
  15.281 +  void memcpyb(dseg,doffset,sseg,soffset,count);
  15.282 +  void memcpyd(dseg,doffset,sseg,soffset,count);
  15.283 +  
  15.284 +  // memset of count bytes
  15.285 +    void 
  15.286 +  memsetb(seg,offset,value,count)
  15.287 +    Bit16u seg;
  15.288 +    Bit16u offset;
  15.289 +    Bit16u value;
  15.290 +    Bit16u count;
  15.291 +  {
  15.292 +  ASM_START
  15.293 +    push bp
  15.294 +    mov  bp, sp
  15.295 +  
  15.296 +      push ax
  15.297 +      push cx
  15.298 +      push es
  15.299 +      push di
  15.300 +  
  15.301 +      mov  cx, 10[bp] ; count
  15.302 +      cmp  cx, #0x00
  15.303 +      je   memsetb_end
  15.304 +      mov  ax, 4[bp] ; segment
  15.305 +      mov  es, ax
  15.306 +      mov  ax, 6[bp] ; offset
  15.307 +      mov  di, ax
  15.308 +      mov  al, 8[bp] ; value
  15.309 +      cld
  15.310 +      rep
  15.311 +       stosb
  15.312 +  
  15.313 +  memsetb_end:
  15.314 +      pop di
  15.315 +      pop es
  15.316 +      pop cx
  15.317 +      pop ax
  15.318 +  
  15.319 +    pop bp
  15.320 +  ASM_END
  15.321 +  }
  15.322 +  
  15.323 +  // memcpy of count bytes
  15.324 +    void 
  15.325 +  memcpyb(dseg,doffset,sseg,soffset,count)
  15.326 +    Bit16u dseg;
  15.327 +    Bit16u doffset;
  15.328 +    Bit16u sseg;
  15.329 +    Bit16u soffset;
  15.330 +    Bit16u count;
  15.331 +  {
  15.332 +  ASM_START
  15.333 +    push bp
  15.334 +    mov  bp, sp
  15.335 +  
  15.336 +      push ax
  15.337 +      push cx
  15.338 +      push es
  15.339 +      push di
  15.340 +      push ds
  15.341 +      push si
  15.342 +  
  15.343 +      mov  cx, 12[bp] ; count
  15.344 +      cmp  cx, #0x0000
  15.345 +      je   memcpyb_end
  15.346 +      mov  ax, 4[bp] ; dsegment
  15.347 +      mov  es, ax
  15.348 +      mov  ax, 6[bp] ; doffset
  15.349 +      mov  di, ax
  15.350 +      mov  ax, 8[bp] ; ssegment
  15.351 +      mov  ds, ax
  15.352 +      mov  ax, 10[bp] ; soffset
  15.353 +      mov  si, ax
  15.354 +      cld
  15.355 +      rep
  15.356 +       movsb
  15.357 +  
  15.358 +  memcpyb_end:
  15.359 +      pop si
  15.360 +      pop ds
  15.361 +      pop di
  15.362 +      pop es
  15.363 +      pop cx
  15.364 +      pop ax
  15.365 +  
  15.366 +    pop bp
  15.367 +  ASM_END
  15.368 +  }
  15.369 +
  15.370 +#if 0 
  15.371 +  // memcpy of count dword
  15.372 +    void 
  15.373 +  memcpyd(dseg,doffset,sseg,soffset,count)
  15.374 +    Bit16u dseg;
  15.375 +    Bit16u doffset;
  15.376 +    Bit16u sseg;
  15.377 +    Bit16u soffset;
  15.378 +    Bit16u count;
  15.379 +  {
  15.380 +  ASM_START
  15.381 +    push bp
  15.382 +    mov  bp, sp
  15.383 +  
  15.384 +      push ax
  15.385 +      push cx
  15.386 +      push es
  15.387 +      push di
  15.388 +      push ds
  15.389 +      push si
  15.390 +  
  15.391 +      mov  cx, 12[bp] ; count
  15.392 +      cmp  cx, #0x0000
  15.393 +      je   memcpyd_end
  15.394 +      mov  ax, 4[bp] ; dsegment
  15.395 +      mov  es, ax
  15.396 +      mov  ax, 6[bp] ; doffset
  15.397 +      mov  di, ax
  15.398 +      mov  ax, 8[bp] ; ssegment
  15.399 +      mov  ds, ax
  15.400 +      mov  ax, 10[bp] ; soffset
  15.401 +      mov  si, ax
  15.402 +      cld
  15.403 +      rep
  15.404 +       movsd
  15.405 +  
  15.406 +  memcpyd_end:
  15.407 +      pop si
  15.408 +      pop ds
  15.409 +      pop di
  15.410 +      pop es
  15.411 +      pop cx
  15.412 +      pop ax
  15.413 +  
  15.414 +    pop bp
  15.415 +  ASM_END
  15.416 +  }
  15.417 +#endif
  15.418 +#endif //BX_USE_ATADRV
  15.419 +
  15.420 +  // read_dword and write_dword functions
  15.421 +  static Bit32u         read_dword();
  15.422 +  static void           write_dword();
  15.423 +  
  15.424 +    Bit32u
  15.425 +  read_dword(seg, offset)
  15.426 +    Bit16u seg;
  15.427 +    Bit16u offset;
  15.428 +  {
  15.429 +  ASM_START
  15.430 +    push bp
  15.431 +    mov  bp, sp
  15.432 +  
  15.433 +      push bx
  15.434 +      push ds
  15.435 +      mov  ax, 4[bp] ; segment
  15.436 +      mov  ds, ax
  15.437 +      mov  bx, 6[bp] ; offset
  15.438 +      mov  ax, [bx]
  15.439 +      inc  bx
  15.440 +      inc  bx
  15.441 +      mov  dx, [bx]
  15.442 +      ;; ax = return value (word)
  15.443 +      ;; dx = return value (word)
  15.444 +      pop  ds
  15.445 +      pop  bx
  15.446 +  
  15.447 +    pop  bp
  15.448 +  ASM_END
  15.449 +  }
  15.450 +  
  15.451 +    void
  15.452 +  write_dword(seg, offset, data)
  15.453 +    Bit16u seg;
  15.454 +    Bit16u offset;
  15.455 +    Bit32u data;
  15.456 +  {
  15.457 +  ASM_START
  15.458 +    push bp
  15.459 +    mov  bp, sp
  15.460 +  
  15.461 +      push ax
  15.462 +      push bx
  15.463 +      push ds
  15.464 +      mov  ax, 4[bp] ; segment
  15.465 +      mov  ds, ax
  15.466 +      mov  bx, 6[bp] ; offset
  15.467 +      mov  ax, 8[bp] ; data word
  15.468 +      mov  [bx], ax  ; write data word
  15.469 +      inc  bx
  15.470 +      inc  bx
  15.471 +      mov  ax, 10[bp] ; data word
  15.472 +      mov  [bx], ax  ; write data word
  15.473 +      pop  ds
  15.474 +      pop  bx
  15.475 +      pop  ax
  15.476 +  
  15.477 +    pop  bp
  15.478 +  ASM_END
  15.479 +  }
  15.480 +  
  15.481 +  // Bit32u (unsigned long) and long helper functions
  15.482 +  ASM_START
  15.483 +  
  15.484 +  ;; and function
  15.485 +  landl:
  15.486 +  landul:
  15.487 +    SEG SS 
  15.488 +      and ax,[di]
  15.489 +    SEG SS 
  15.490 +      and bx,2[di]
  15.491 +    ret
  15.492 +  
  15.493 +  ;; add function
  15.494 +  laddl:
  15.495 +  laddul:
  15.496 +    SEG SS 
  15.497 +      add ax,[di]
  15.498 +    SEG SS 
  15.499 +      adc bx,2[di]
  15.500 +    ret
  15.501 +  
  15.502 +  ;; cmp function
  15.503 +  lcmpl:
  15.504 +  lcmpul:
  15.505 +    and eax, #0x0000FFFF
  15.506 +    shl ebx, #16
  15.507 +    add eax, ebx
  15.508 +    shr ebx, #16
  15.509 +    SEG SS
  15.510 +      cmp eax, dword ptr [di]
  15.511 +    ret
  15.512 +  
  15.513 +  ;; sub function
  15.514 +  lsubl:
  15.515 +  lsubul:
  15.516 +    SEG SS
  15.517 +    sub ax,[di]
  15.518 +    SEG SS
  15.519 +    sbb bx,2[di]
  15.520 +    ret
  15.521 +  
  15.522 +  ;; mul function
  15.523 +  lmull:
  15.524 +  lmulul:
  15.525 +    and eax, #0x0000FFFF
  15.526 +    shl ebx, #16
  15.527 +    add eax, ebx
  15.528 +    SEG SS
  15.529 +    mul eax, dword ptr [di]
  15.530 +    mov ebx, eax
  15.531 +    shr ebx, #16
  15.532 +    ret
  15.533 +  
  15.534 +  ;; dec function
  15.535 +  ldecl:
  15.536 +  ldecul:
  15.537 +    SEG SS
  15.538 +    dec dword ptr [bx]
  15.539 +    ret
  15.540 +  
  15.541 +  ;; or function
  15.542 +  lorl:
  15.543 +  lorul:
  15.544 +    SEG SS
  15.545 +    or  ax,[di]
  15.546 +    SEG SS
  15.547 +    or  bx,2[di]
  15.548 +    ret
  15.549 +  
  15.550 +  ;; inc function
  15.551 +  lincl:
  15.552 +  lincul:
  15.553 +    SEG SS
  15.554 +    inc dword ptr [bx]
  15.555 +    ret
  15.556 +  
  15.557 +  ;; tst function
  15.558 +  ltstl:
  15.559 +  ltstul:
  15.560 +    and eax, #0x0000FFFF
  15.561 +    shl ebx, #16
  15.562 +    add eax, ebx
  15.563 +    shr ebx, #16
  15.564 +    test eax, eax
  15.565 +    ret
  15.566 +  
  15.567 +  ;; sr function
  15.568 +  lsrul:
  15.569 +    mov  cx,di
  15.570 +    jcxz lsr_exit
  15.571 +    and  eax, #0x0000FFFF
  15.572 +    shl  ebx, #16
  15.573 +    add  eax, ebx
  15.574 +  lsr_loop:
  15.575 +    shr  eax, #1
  15.576 +    loop lsr_loop
  15.577 +    mov  ebx, eax
  15.578 +    shr  ebx, #16
  15.579 +  lsr_exit:
  15.580 +    ret
  15.581 +  
  15.582 +  ;; sl function
  15.583 +  lsll:
  15.584 +  lslul:
  15.585 +    mov  cx,di
  15.586 +    jcxz lsl_exit
  15.587 +    and  eax, #0x0000FFFF
  15.588 +    shl  ebx, #16
  15.589 +    add  eax, ebx
  15.590 +  lsl_loop: 
  15.591 +    shl  eax, #1
  15.592 +    loop lsl_loop
  15.593 +    mov  ebx, eax
  15.594 +    shr  ebx, #16
  15.595 +  lsl_exit:
  15.596 +    ret
  15.597 +  
  15.598 +  idiv_:
  15.599 +    cwd
  15.600 +    idiv bx
  15.601 +    ret
  15.602 +
  15.603 +  idiv_u:
  15.604 +    xor dx,dx
  15.605 +    div bx
  15.606 +    ret
  15.607 +
  15.608 +  ldivul:
  15.609 +    and  eax, #0x0000FFFF
  15.610 +    shl  ebx, #16
  15.611 +    add  eax, ebx
  15.612 +    xor  edx, edx
  15.613 +    SEG SS
  15.614 +    mov  bx,  2[di]
  15.615 +    shl  ebx, #16
  15.616 +    SEG SS
  15.617 +    mov  bx,  [di]
  15.618 +    div  ebx
  15.619 +    mov  ebx, eax
  15.620 +    shr  ebx, #16
  15.621 +    ret
  15.622 +
  15.623 +  ASM_END
  15.624 +
  15.625 +// for access to RAM area which is used by interrupt vectors
  15.626 +// and BIOS Data Area
  15.627 +
  15.628 +typedef struct {
  15.629 +  unsigned char filler1[0x400];
  15.630 +  unsigned char filler2[0x6c];
  15.631 +  Bit16u ticks_low;
  15.632 +  Bit16u ticks_high;
  15.633 +  Bit8u  midnight_flag;
  15.634 +  } bios_data_t;
  15.635 +
  15.636 +#define BiosData ((bios_data_t  *) 0)
  15.637 +
  15.638 +#if BX_USE_ATADRV
  15.639 +  typedef struct {
  15.640 +    Bit16u heads;      // # heads
  15.641 +    Bit16u cylinders;  // # cylinders
  15.642 +    Bit16u spt;        // # sectors / track
  15.643 +    } chs_t;
  15.644 +
  15.645 +  // DPTE definition
  15.646 +  typedef struct {
  15.647 +    Bit16u iobase1;
  15.648 +    Bit16u iobase2;
  15.649 +    Bit8u  prefix;
  15.650 +    Bit8u  unused;
  15.651 +    Bit8u  irq;
  15.652 +    Bit8u  blkcount;
  15.653 +    Bit8u  dma;
  15.654 +    Bit8u  pio;
  15.655 +    Bit16u options;
  15.656 +    Bit16u reserved;
  15.657 +    Bit8u  revision;
  15.658 +    Bit8u  checksum;
  15.659 +    } dpte_t;
  15.660 + 
  15.661 +  typedef struct {
  15.662 +    Bit8u  iface;        // ISA or PCI
  15.663 +    Bit16u iobase1;      // IO Base 1
  15.664 +    Bit16u iobase2;      // IO Base 2
  15.665 +    Bit8u  irq;          // IRQ
  15.666 +    } ata_channel_t;
  15.667 +
  15.668 +  typedef struct {
  15.669 +    Bit8u  type;         // Detected type of ata (ata/atapi/none/unknown)
  15.670 +    Bit8u  device;       // Detected type of attached devices (hd/cd/none)
  15.671 +    Bit8u  removable;    // Removable device flag
  15.672 +    Bit8u  lock;         // Locks for removable devices
  15.673 +    // Bit8u  lba_capable;  // LBA capable flag - always yes for bochs devices
  15.674 +    Bit8u  mode;         // transfert mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
  15.675 +    Bit16u blksize;      // block size
  15.676 +
  15.677 +    Bit8u  translation;  // type of translation
  15.678 +    chs_t  lchs;         // Logical CHS
  15.679 +    chs_t  pchs;         // Physical CHS
  15.680 +
  15.681 +    Bit32u sectors;      // Total sectors count
  15.682 +    } ata_device_t;
  15.683 +
  15.684 +  typedef struct {
  15.685 +    // ATA channels info
  15.686 +    ata_channel_t channels[BX_MAX_ATA_INTERFACES];
  15.687 +
  15.688 +    // ATA devices info
  15.689 +    ata_device_t  devices[BX_MAX_ATA_DEVICES];
  15.690 +    //
  15.691 +    // map between (bios hd id - 0x80) and ata channels
  15.692 +    Bit8u  hdcount, hdidmap[BX_MAX_ATA_DEVICES];                
  15.693 +
  15.694 +    // map between (bios cd id - 0xE0) and ata channels
  15.695 +    Bit8u  cdcount, cdidmap[BX_MAX_ATA_DEVICES];                
  15.696 +
  15.697 +    // Buffer for DPTE table
  15.698 +    dpte_t dpte;
  15.699 +
  15.700 +    // Count of transferred sectors and bytes
  15.701 +    Bit16u trsfsectors;
  15.702 +    Bit32u trsfbytes;
  15.703 +
  15.704 +    } ata_t;
  15.705 +  
  15.706 +#if BX_ELTORITO_BOOT
  15.707 +  // ElTorito Device Emulation data 
  15.708 +  typedef struct {
  15.709 +    Bit8u  active;
  15.710 +    Bit8u  media;
  15.711 +    Bit8u  emulated_drive;
  15.712 +    Bit8u  controller_index;
  15.713 +    Bit16u device_spec;
  15.714 +    Bit32u ilba;
  15.715 +    Bit16u buffer_segment;
  15.716 +    Bit16u load_segment;
  15.717 +    Bit16u sector_count;
  15.718 +    
  15.719 +    // Virtual device
  15.720 +    chs_t  vdevice;
  15.721 +    } cdemu_t;
  15.722 +#endif // BX_ELTORITO_BOOT
  15.723 +  
  15.724 +  // for access to EBDA area
  15.725 +  //     The EBDA structure should conform to 
  15.726 +  //     http://www.cybertrails.com/~fys/rombios.htm document
  15.727 +  //     I made the ata and cdemu structs begin at 0x121 in the EBDA seg
  15.728 +  typedef struct {
  15.729 +    unsigned char filler1[0x3D];
  15.730 +
  15.731 +    // FDPT - Can be splitted in data members if needed
  15.732 +    unsigned char fdpt0[0x10];
  15.733 +    unsigned char fdpt1[0x10];
  15.734 +
  15.735 +    unsigned char filler2[0xC4];
  15.736 +
  15.737 +    // ATA Driver data
  15.738 +    ata_t   ata;
  15.739 +
  15.740 +#if BX_ELTORITO_BOOT
  15.741 +    // El Torito Emulation data
  15.742 +    cdemu_t cdemu;
  15.743 +#endif // BX_ELTORITO_BOOT
  15.744 +
  15.745 +    } ebda_data_t;
  15.746 +  
  15.747 +  #define EbdaData ((ebda_data_t *) 0)
  15.748 +
  15.749 +  // for access to the int13ext structure
  15.750 +  typedef struct {
  15.751 +    Bit8u  size;
  15.752 +    Bit8u  reserved;
  15.753 +    Bit16u count;
  15.754 +    Bit16u offset;
  15.755 +    Bit16u segment;
  15.756 +    Bit32u lba1;
  15.757 +    Bit32u lba2;
  15.758 +    } int13ext_t;
  15.759 + 
  15.760 +  #define Int13Ext ((int13ext_t *) 0)
  15.761 +
  15.762 +  // Disk Physical Table definition
  15.763 +  typedef struct {
  15.764 +    Bit16u  size;
  15.765 +    Bit16u  infos;
  15.766 +    Bit32u  cylinders;
  15.767 +    Bit32u  heads;
  15.768 +    Bit32u  spt;
  15.769 +    Bit32u  sector_count1;
  15.770 +    Bit32u  sector_count2;
  15.771 +    Bit16u  blksize;
  15.772 +    Bit16u  dpte_segment;
  15.773 +    Bit16u  dpte_offset;
  15.774 +    Bit16u  key;
  15.775 +    Bit8u   dpi_length;
  15.776 +    Bit8u   reserved1;
  15.777 +    Bit16u  reserved2;
  15.778 +    Bit8u   host_bus[4];
  15.779 +    Bit8u   iface_type[8];
  15.780 +    Bit8u   iface_path[8];
  15.781 +    Bit8u   device_path[8];
  15.782 +    Bit8u   reserved3;
  15.783 +    Bit8u   checksum;
  15.784 +    } dpt_t;
  15.785 + 
  15.786 +  #define Int13DPT ((dpt_t *) 0)
  15.787 +
  15.788 +#endif // BX_USE_ATADRV
  15.789 +
  15.790 +typedef struct {
  15.791 +  union {
  15.792 +    struct {
  15.793 +      Bit16u di, si, bp, sp;
  15.794 +      Bit16u bx, dx, cx, ax;
  15.795 +      } r16;
  15.796 +    struct {
  15.797 +      Bit16u filler[4];
  15.798 +      Bit8u  bl, bh, dl, dh, cl, ch, al, ah;
  15.799 +      } r8;
  15.800 +    } u;
  15.801 +  } pusha_regs_t;
  15.802 +
  15.803 +typedef struct {
  15.804 + union {
  15.805 +  struct {
  15.806 +    Bit32u edi, esi, ebp, esp;
  15.807 +    Bit32u ebx, edx, ecx, eax;
  15.808 +    } r32;
  15.809 +  struct {
  15.810 +    Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
  15.811 +    Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
  15.812 +    } r16;
  15.813 +  struct {
  15.814 +    Bit32u filler[4];
  15.815 +    Bit8u  bl, bh; 
  15.816 +    Bit16u filler1;
  15.817 +    Bit8u  dl, dh; 
  15.818 +    Bit16u filler2;
  15.819 +    Bit8u  cl, ch;
  15.820 +    Bit16u filler3;
  15.821 +    Bit8u  al, ah;
  15.822 +    Bit16u filler4;
  15.823 +    } r8;
  15.824 +  } u;
  15.825 +} pushad_regs_t;
  15.826 +
  15.827 +typedef struct {
  15.828 +  union {
  15.829 +    struct {
  15.830 +      Bit16u flags;
  15.831 +      } r16;
  15.832 +    struct {
  15.833 +      Bit8u  flagsl;
  15.834 +      Bit8u  flagsh;
  15.835 +      } r8;
  15.836 +    } u;
  15.837 +  } flags_t;
  15.838 +
  15.839 +#define SetCF(x)   x.u.r8.flagsl |= 0x01
  15.840 +#define SetZF(x)   x.u.r8.flagsl |= 0x40
  15.841 +#define ClearCF(x) x.u.r8.flagsl &= 0xfe
  15.842 +#define ClearZF(x) x.u.r8.flagsl &= 0xbf
  15.843 +#define GetCF(x)   (x.u.r8.flagsl & 0x01)
  15.844 +
  15.845 +typedef struct {
  15.846 +  Bit16u ip;
  15.847 +  Bit16u cs;
  15.848 +  flags_t flags;
  15.849 +  } iret_addr_t;
  15.850 +
  15.851 +
  15.852 +
  15.853 +static Bit8u          inb();
  15.854 +static Bit8u          inb_cmos();
  15.855 +static void           outb();
  15.856 +static void           outb_cmos();
  15.857 +static Bit16u         inw();
  15.858 +static void           outw();
  15.859 +static void           init_rtc();
  15.860 +static bx_bool        rtc_updating();
  15.861 +
  15.862 +static Bit8u          read_byte();
  15.863 +static Bit16u         read_word();
  15.864 +static void           write_byte();
  15.865 +static void           write_word();
  15.866 +static void           bios_printf();
  15.867 +static void           copy_e820_table();
  15.868 +
  15.869 +static Bit8u          inhibit_mouse_int_and_events();
  15.870 +static void           enable_mouse_int_and_events();
  15.871 +static Bit8u          send_to_mouse_ctrl();
  15.872 +static Bit8u          get_mouse_data();
  15.873 +static void           set_kbd_command_byte();
  15.874 +
  15.875 +static void           int09_function();
  15.876 +static void           int13_harddisk();
  15.877 +static void           int13_cdrom();
  15.878 +static void           int13_cdemu();
  15.879 +static void           int13_eltorito();
  15.880 +static void           int13_diskette_function();
  15.881 +static void           int14_function();
  15.882 +static void           int15_function();
  15.883 +static void           int16_function();
  15.884 +static void           int17_function();
  15.885 +static Bit32u         int19_function();
  15.886 +static void           int1a_function();
  15.887 +static void           int70_function();
  15.888 +static void           int74_function();
  15.889 +static Bit16u         get_CS();
  15.890 +//static Bit16u         get_DS();
  15.891 +//static void           set_DS();
  15.892 +static Bit16u         get_SS();
  15.893 +static unsigned int   enqueue_key();
  15.894 +static unsigned int   dequeue_key();
  15.895 +static void           get_hd_geometry();
  15.896 +static void           set_diskette_ret_status();
  15.897 +static void           set_diskette_current_cyl();
  15.898 +static void           determine_floppy_media();
  15.899 +static bx_bool        floppy_drive_exists();
  15.900 +static bx_bool        floppy_drive_recal();
  15.901 +static bx_bool        floppy_media_known();
  15.902 +static bx_bool        floppy_media_sense();
  15.903 +static bx_bool        set_enable_a20();
  15.904 +static void           debugger_on();
  15.905 +static void           debugger_off();
  15.906 +static void           keyboard_init();
  15.907 +static void           keyboard_panic();
  15.908 +static void           shutdown_status_panic();
  15.909 +static void           nmi_handler_msg();
  15.910 +
  15.911 +static void           print_bios_banner();
  15.912 +static void           print_boot_device();
  15.913 +static void           print_boot_failure();
  15.914 +static void           print_cdromboot_failure();
  15.915 +
  15.916 +# if BX_USE_ATADRV
  15.917 +
  15.918 +// ATA / ATAPI driver
  15.919 +void   ata_init();
  15.920 +void   ata_detect();
  15.921 +void   ata_reset();
  15.922 +
  15.923 +Bit16u ata_cmd_non_data();
  15.924 +Bit16u ata_cmd_data_in();
  15.925 +Bit16u ata_cmd_data_out();
  15.926 +Bit16u ata_cmd_packet();
  15.927 +
  15.928 +Bit16u atapi_get_sense();
  15.929 +Bit16u atapi_is_ready();
  15.930 +Bit16u atapi_is_cdrom();
  15.931 +
  15.932 +#endif // BX_USE_ATADRV
  15.933 +
  15.934 +#if BX_ELTORITO_BOOT
  15.935 +
  15.936 +void   cdemu_init();
  15.937 +Bit8u  cdemu_isactive();
  15.938 +Bit8u  cdemu_emulated_drive();
  15.939 +
  15.940 +Bit16u cdrom_boot();
  15.941 +
  15.942 +#endif // BX_ELTORITO_BOOT
  15.943 +
  15.944 +static char bios_cvs_version_string[] = "$Revision: 1.138 $";
  15.945 +static char bios_date_string[] = "$Date: 2005/05/07 15:55:26 $";
  15.946 +
  15.947 +static char CVSID[] = "$Id: rombios.c,v 1.138 2005/05/07 15:55:26 vruppert Exp $";
  15.948 +
  15.949 +/* Offset to skip the CVS $Id: prefix */ 
  15.950 +#define bios_version_string  (CVSID + 4)
  15.951 +
  15.952 +#define BIOS_PRINTF_HALT     1
  15.953 +#define BIOS_PRINTF_SCREEN   2
  15.954 +#define BIOS_PRINTF_INFO     4
  15.955 +#define BIOS_PRINTF_DEBUG    8
  15.956 +#define BIOS_PRINTF_ALL      (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO)
  15.957 +#define BIOS_PRINTF_DEBHALT  (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT)
  15.958 +
  15.959 +#define printf(format, p...)  bios_printf(BIOS_PRINTF_SCREEN, format, ##p)
  15.960 +
  15.961 +// Defines the output macros. 
  15.962 +// BX_DEBUG goes to INFO port until we can easily choose debug info on a 
  15.963 +// per-device basis. Debug info are sent only in debug mode
  15.964 +#if DEBUG_ROMBIOS
  15.965 +#  define BX_DEBUG(format, p...)  bios_printf(BIOS_PRINTF_INFO, format, ##p)    
  15.966 +#else
  15.967 +#  define BX_DEBUG(format, p...) 
  15.968 +#endif
  15.969 +#define BX_INFO(format, p...)   bios_printf(BIOS_PRINTF_INFO, format, ##p)
  15.970 +#define BX_PANIC(format, p...)  bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
  15.971 +
  15.972 +#if DEBUG_ATA
  15.973 +#  define BX_DEBUG_ATA(a...) BX_DEBUG(a)
  15.974 +#else
  15.975 +#  define BX_DEBUG_ATA(a...)
  15.976 +#endif
  15.977 +#if DEBUG_INT13_HD
  15.978 +#  define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
  15.979 +#else
  15.980 +#  define BX_DEBUG_INT13_HD(a...)
  15.981 +#endif
  15.982 +#if DEBUG_INT13_CD
  15.983 +#  define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
  15.984 +#else
  15.985 +#  define BX_DEBUG_INT13_CD(a...)
  15.986 +#endif
  15.987 +#if DEBUG_INT13_ET
  15.988 +#  define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
  15.989 +#else
  15.990 +#  define BX_DEBUG_INT13_ET(a...)
  15.991 +#endif
  15.992 +#if DEBUG_INT13_FL
  15.993 +#  define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
  15.994 +#else
  15.995 +#  define BX_DEBUG_INT13_FL(a...)
  15.996 +#endif
  15.997 +#if DEBUG_INT15
  15.998 +#  define BX_DEBUG_INT15(a...) BX_DEBUG(a)
  15.999 +#else
 15.1000 +#  define BX_DEBUG_INT15(a...)
 15.1001 +#endif
 15.1002 +#if DEBUG_INT16
 15.1003 +#  define BX_DEBUG_INT16(a...) BX_DEBUG(a)
 15.1004 +#else
 15.1005 +#  define BX_DEBUG_INT16(a...)
 15.1006 +#endif
 15.1007 +#if DEBUG_INT1A
 15.1008 +#  define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
 15.1009 +#else
 15.1010 +#  define BX_DEBUG_INT1A(a...)
 15.1011 +#endif
 15.1012 +#if DEBUG_INT74
 15.1013 +#  define BX_DEBUG_INT74(a...) BX_DEBUG(a)
 15.1014 +#else
 15.1015 +#  define BX_DEBUG_INT74(a...)
 15.1016 +#endif
 15.1017 +
 15.1018 +#define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
 15.1019 +#define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
 15.1020 +#define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
 15.1021 +#define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
 15.1022 +#define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
 15.1023 +#define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
 15.1024 +#define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
 15.1025 +#define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
 15.1026 +
 15.1027 +#define GET_AL() ( AX & 0x00ff )
 15.1028 +#define GET_BL() ( BX & 0x00ff )
 15.1029 +#define GET_CL() ( CX & 0x00ff )
 15.1030 +#define GET_DL() ( DX & 0x00ff )
 15.1031 +#define GET_AH() ( AX >> 8 )
 15.1032 +#define GET_BH() ( BX >> 8 )
 15.1033 +#define GET_CH() ( CX >> 8 )
 15.1034 +#define GET_DH() ( DX >> 8 )
 15.1035 +
 15.1036 +#define GET_ELDL() ( ELDX & 0x00ff )
 15.1037 +#define GET_ELDH() ( ELDX >> 8 )
 15.1038 +
 15.1039 +#define SET_CF()     FLAGS |= 0x0001
 15.1040 +#define CLEAR_CF()   FLAGS &= 0xfffe
 15.1041 +#define GET_CF()     (FLAGS & 0x0001)
 15.1042 +
 15.1043 +#define SET_ZF()     FLAGS |= 0x0040
 15.1044 +#define CLEAR_ZF()   FLAGS &= 0xffbf
 15.1045 +#define GET_ZF()     (FLAGS & 0x0040)
 15.1046 +
 15.1047 +#define UNSUPPORTED_FUNCTION 0x86
 15.1048 +
 15.1049 +#define none 0
 15.1050 +#define MAX_SCAN_CODE 0x53
 15.1051 +
 15.1052 +static struct {
 15.1053 +  Bit16u normal;
 15.1054 +  Bit16u shift;
 15.1055 +  Bit16u control;
 15.1056 +  Bit16u alt;
 15.1057 +  Bit8u lock_flags;
 15.1058 +  } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
 15.1059 +      {   none,   none,   none,   none, none },
 15.1060 +      { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
 15.1061 +      { 0x0231, 0x0221,   none, 0x7800, none }, /* 1! */
 15.1062 +      { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
 15.1063 +      { 0x0433, 0x0423,   none, 0x7a00, none }, /* 3# */
 15.1064 +      { 0x0534, 0x0524,   none, 0x7b00, none }, /* 4$ */
 15.1065 +      { 0x0635, 0x0625,   none, 0x7c00, none }, /* 5% */
 15.1066 +      { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
 15.1067 +      { 0x0837, 0x0826,   none, 0x7e00, none }, /* 7& */
 15.1068 +      { 0x0938, 0x092a,   none, 0x7f00, none }, /* 8* */
 15.1069 +      { 0x0a39, 0x0a28,   none, 0x8000, none }, /* 9( */
 15.1070 +      { 0x0b30, 0x0b29,   none, 0x8100, none }, /* 0) */
 15.1071 +      { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
 15.1072 +      { 0x0d3d, 0x0d2b,   none, 0x8300, none }, /* =+ */
 15.1073 +      { 0x0e08, 0x0e08, 0x0e7f,   none, none }, /* backspace */
 15.1074 +      { 0x0f09, 0x0f00,   none,   none, none }, /* tab */
 15.1075 +      { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
 15.1076 +      { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
 15.1077 +      { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
 15.1078 +      { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
 15.1079 +      { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
 15.1080 +      { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
 15.1081 +      { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
 15.1082 +      { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
 15.1083 +      { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
 15.1084 +      { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
 15.1085 +      { 0x1a5b, 0x1a7b, 0x1a1b,   none, none }, /* [{ */
 15.1086 +      { 0x1b5d, 0x1b7d, 0x1b1d,   none, none }, /* ]} */
 15.1087 +      { 0x1c0d, 0x1c0d, 0x1c0a,   none, none }, /* Enter */
 15.1088 +      {   none,   none,   none,   none, none }, /* L Ctrl */
 15.1089 +      { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
 15.1090 +      { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
 15.1091 +      { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
 15.1092 +      { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
 15.1093 +      { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
 15.1094 +      { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
 15.1095 +      { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
 15.1096 +      { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
 15.1097 +      { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
 15.1098 +      { 0x273b, 0x273a,   none,   none, none }, /* ;: */
 15.1099 +      { 0x2827, 0x2822,   none,   none, none }, /* '" */
 15.1100 +      { 0x2960, 0x297e,   none,   none, none }, /* `~ */
 15.1101 +      {   none,   none,   none,   none, none }, /* L shift */
 15.1102 +      { 0x2b5c, 0x2b7c, 0x2b1c,   none, none }, /* |\ */
 15.1103 +      { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
 15.1104 +      { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
 15.1105 +      { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
 15.1106 +      { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
 15.1107 +      { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
 15.1108 +      { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
 15.1109 +      { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
 15.1110 +      { 0x332c, 0x333c,   none,   none, none }, /* ,< */
 15.1111 +      { 0x342e, 0x343e,   none,   none, none }, /* .> */
 15.1112 +      { 0x352f, 0x353f,   none,   none, none }, /* /? */
 15.1113 +      {   none,   none,   none,   none, none }, /* R Shift */
 15.1114 +      { 0x372a, 0x372a,   none,   none, none }, /* * */
 15.1115 +      {   none,   none,   none,   none, none }, /* L Alt */
 15.1116 +      { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
 15.1117 +      {   none,   none,   none,   none, none }, /* caps lock */
 15.1118 +      { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
 15.1119 +      { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
 15.1120 +      { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
 15.1121 +      { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
 15.1122 +      { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
 15.1123 +      { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
 15.1124 +      { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
 15.1125 +      { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
 15.1126 +      { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
 15.1127 +      { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
 15.1128 +      {   none,   none,   none,   none, none }, /* Num Lock */
 15.1129 +      {   none,   none,   none,   none, none }, /* Scroll Lock */
 15.1130 +      { 0x4700, 0x4737, 0x7700,   none, 0x20 }, /* 7 Home */
 15.1131 +      { 0x4800, 0x4838,   none,   none, 0x20 }, /* 8 UP */
 15.1132 +      { 0x4900, 0x4939, 0x8400,   none, 0x20 }, /* 9 PgUp */
 15.1133 +      { 0x4a2d, 0x4a2d,   none,   none, none }, /* - */
 15.1134 +      { 0x4b00, 0x4b34, 0x7300,   none, 0x20 }, /* 4 Left */
 15.1135 +      { 0x4c00, 0x4c35,   none,   none, 0x20 }, /* 5 */
 15.1136 +      { 0x4d00, 0x4d36, 0x7400,   none, 0x20 }, /* 6 Right */
 15.1137 +      { 0x4e2b, 0x4e2b,   none,   none, none }, /* + */
 15.1138 +      { 0x4f00, 0x4f31, 0x7500,   none, 0x20 }, /* 1 End */
 15.1139 +      { 0x5000, 0x5032,   none,   none, 0x20 }, /* 2 Down */
 15.1140 +      { 0x5100, 0x5133, 0x7600,   none, 0x20 }, /* 3 PgDn */
 15.1141 +      { 0x5200, 0x5230,   none,   none, 0x20 }, /* 0 Ins */
 15.1142 +      { 0x5300, 0x532e,   none,   none, 0x20 }  /* Del */
 15.1143 +      };
 15.1144 +
 15.1145 +  Bit8u
 15.1146 +inb(port)
 15.1147 +  Bit16u port;
 15.1148 +{
 15.1149 +ASM_START
 15.1150 +  push bp
 15.1151 +  mov  bp, sp
 15.1152 +
 15.1153 +    push dx
 15.1154 +    mov  dx, 4[bp]
 15.1155 +    in   al, dx
 15.1156 +    pop  dx
 15.1157 +
 15.1158 +  pop  bp
 15.1159 +ASM_END
 15.1160 +}
 15.1161 +
 15.1162 +#if BX_USE_ATADRV
 15.1163 +  Bit16u
 15.1164 +inw(port)
 15.1165 +  Bit16u port;
 15.1166 +{
 15.1167 +ASM_START
 15.1168 +  push bp
 15.1169 +  mov  bp, sp
 15.1170 +
 15.1171 +    push dx
 15.1172 +    mov  dx, 4[bp]
 15.1173 +    in   ax, dx
 15.1174 +    pop  dx
 15.1175 +
 15.1176 +  pop  bp
 15.1177 +ASM_END
 15.1178 +}
 15.1179 +#endif
 15.1180 +
 15.1181 +  void
 15.1182 +outb(port, val)
 15.1183 +  Bit16u port;
 15.1184 +  Bit8u  val;
 15.1185 +{
 15.1186 +ASM_START
 15.1187 +  push bp
 15.1188 +  mov  bp, sp
 15.1189 +
 15.1190 +    push ax
 15.1191 +    push dx
 15.1192 +    mov  dx, 4[bp]
 15.1193 +    mov  al, 6[bp]
 15.1194 +    out  dx, al
 15.1195 +    pop  dx
 15.1196 +    pop  ax
 15.1197 +
 15.1198 +  pop  bp
 15.1199 +ASM_END
 15.1200 +}
 15.1201 +
 15.1202 +#if BX_USE_ATADRV
 15.1203 +  void
 15.1204 +outw(port, val)
 15.1205 +  Bit16u port;
 15.1206 +  Bit16u  val;
 15.1207 +{
 15.1208 +ASM_START
 15.1209 +  push bp
 15.1210 +  mov  bp, sp
 15.1211 +
 15.1212 +    push ax
 15.1213 +    push dx
 15.1214 +    mov  dx, 4[bp]
 15.1215 +    mov  ax, 6[bp]
 15.1216 +    out  dx, ax
 15.1217 +    pop  dx
 15.1218 +    pop  ax
 15.1219 +
 15.1220 +  pop  bp
 15.1221 +ASM_END
 15.1222 +}
 15.1223 +#endif
 15.1224 +
 15.1225 +  void
 15.1226 +outb_cmos(cmos_reg, val)
 15.1227 +  Bit8u cmos_reg;
 15.1228 +  Bit8u val;
 15.1229 +{
 15.1230 +ASM_START
 15.1231 +  push bp
 15.1232 +  mov  bp, sp
 15.1233 +
 15.1234 +    mov  al, 4[bp] ;; cmos_reg
 15.1235 +    out  0x70, al
 15.1236 +    mov  al, 6[bp] ;; val
 15.1237 +    out  0x71, al
 15.1238 +
 15.1239 +  pop  bp
 15.1240 +ASM_END
 15.1241 +}
 15.1242 +
 15.1243 +  Bit8u
 15.1244 +inb_cmos(cmos_reg)
 15.1245 +  Bit8u cmos_reg;
 15.1246 +{
 15.1247 +ASM_START
 15.1248 +  push bp
 15.1249 +  mov  bp, sp
 15.1250 +
 15.1251 +    mov  al, 4[bp] ;; cmos_reg
 15.1252 +    out 0x70, al
 15.1253 +    in  al, 0x71
 15.1254 +
 15.1255 +  pop  bp
 15.1256 +ASM_END
 15.1257 +}
 15.1258 +
 15.1259 +  void
 15.1260 +init_rtc()
 15.1261 +{
 15.1262 +  outb_cmos(0x0a, 0x26);
 15.1263 +  outb_cmos(0x0b, 0x02);
 15.1264 +  inb_cmos(0x0c);
 15.1265 +  inb_cmos(0x0d);
 15.1266 +}
 15.1267 +
 15.1268 +  bx_bool
 15.1269 +rtc_updating()
 15.1270 +{
 15.1271 +  // This function checks to see if the update-in-progress bit
 15.1272 +  // is set in CMOS Status Register A.  If not, it returns 0.
 15.1273 +  // If it is set, it tries to wait until there is a transition
 15.1274 +  // to 0, and will return 0 if such a transition occurs.  A 1
 15.1275 +  // is returned only after timing out.  The maximum period
 15.1276 +  // that this bit should be set is constrained to 244useconds.
 15.1277 +  // The count I use below guarantees coverage or more than
 15.1278 +  // this time, with any reasonable IPS setting.
 15.1279 +
 15.1280 +  Bit16u count;
 15.1281 +
 15.1282 +  count = 25000;
 15.1283 +  while (--count != 0) {
 15.1284 +    if ( (inb_cmos(0x0a) & 0x80) == 0 )
 15.1285 +      return(0);
 15.1286 +    }
 15.1287 +  return(1); // update-in-progress never transitioned to 0
 15.1288 +}
 15.1289 +
 15.1290 +
 15.1291 +  Bit8u
 15.1292 +read_byte(seg, offset)
 15.1293 +  Bit16u seg;
 15.1294 +  Bit16u offset;
 15.1295 +{
 15.1296 +ASM_START
 15.1297 +  push bp
 15.1298 +  mov  bp, sp
 15.1299 +
 15.1300 +    push bx
 15.1301 +    push ds
 15.1302 +    mov  ax, 4[bp] ; segment
 15.1303 +    mov  ds, ax
 15.1304 +    mov  bx, 6[bp] ; offset
 15.1305 +    mov  al, [bx]
 15.1306 +    ;; al = return value (byte)
 15.1307 +    pop  ds
 15.1308 +    pop  bx
 15.1309 +
 15.1310 +  pop  bp
 15.1311 +ASM_END
 15.1312 +}
 15.1313 +
 15.1314 +  Bit16u
 15.1315 +read_word(seg, offset)
 15.1316 +  Bit16u seg;
 15.1317 +  Bit16u offset;
 15.1318 +{
 15.1319 +ASM_START
 15.1320 +  push bp
 15.1321 +  mov  bp, sp
 15.1322 +
 15.1323 +    push bx
 15.1324 +    push ds
 15.1325 +    mov  ax, 4[bp] ; segment
 15.1326 +    mov  ds, ax
 15.1327 +    mov  bx, 6[bp] ; offset
 15.1328 +    mov  ax, [bx]
 15.1329 +    ;; ax = return value (word)
 15.1330 +    pop  ds
 15.1331 +    pop  bx
 15.1332 +
 15.1333 +  pop  bp
 15.1334 +ASM_END
 15.1335 +}
 15.1336 +
 15.1337 +  void
 15.1338 +write_byte(seg, offset, data)
 15.1339 +  Bit16u seg;
 15.1340 +  Bit16u offset;
 15.1341 +  Bit8u data;
 15.1342 +{
 15.1343 +ASM_START
 15.1344 +  push bp
 15.1345 +  mov  bp, sp
 15.1346 +
 15.1347 +    push ax
 15.1348 +    push bx
 15.1349 +    push ds
 15.1350 +    mov  ax, 4[bp] ; segment
 15.1351 +    mov  ds, ax
 15.1352 +    mov  bx, 6[bp] ; offset
 15.1353 +    mov  al, 8[bp] ; data byte
 15.1354 +    mov  [bx], al  ; write data byte
 15.1355 +    pop  ds
 15.1356 +    pop  bx
 15.1357 +    pop  ax
 15.1358 +
 15.1359 +  pop  bp
 15.1360 +ASM_END
 15.1361 +}
 15.1362 +
 15.1363 +  void
 15.1364 +write_word(seg, offset, data)
 15.1365 +  Bit16u seg;
 15.1366 +  Bit16u offset;
 15.1367 +  Bit16u data;
 15.1368 +{
 15.1369 +ASM_START
 15.1370 +  push bp
 15.1371 +  mov  bp, sp
 15.1372 +
 15.1373 +    push ax
 15.1374 +    push bx
 15.1375 +    push ds
 15.1376 +    mov  ax, 4[bp] ; segment
 15.1377 +    mov  ds, ax
 15.1378 +    mov  bx, 6[bp] ; offset
 15.1379 +    mov  ax, 8[bp] ; data word
 15.1380 +    mov  [bx], ax  ; write data word
 15.1381 +    pop  ds
 15.1382 +    pop  bx
 15.1383 +    pop  ax
 15.1384 +
 15.1385 +  pop  bp
 15.1386 +ASM_END
 15.1387 +}
 15.1388 +
 15.1389 +  Bit16u
 15.1390 +get_CS()
 15.1391 +{
 15.1392 +ASM_START
 15.1393 +  mov  ax, cs
 15.1394 +ASM_END
 15.1395 +}
 15.1396 +
 15.1397 +//  Bit16u
 15.1398 +//get_DS()
 15.1399 +//{
 15.1400 +//ASM_START
 15.1401 +//  mov  ax, ds
 15.1402 +//ASM_END
 15.1403 +//}
 15.1404 +//
 15.1405 +//  void
 15.1406 +//set_DS(ds_selector)
 15.1407 +//  Bit16u ds_selector;
 15.1408 +//{
 15.1409 +//ASM_START
 15.1410 +//  push bp
 15.1411 +//  mov  bp, sp
 15.1412 +//
 15.1413 +//    push ax
 15.1414 +//    mov  ax, 4[bp] ; ds_selector
 15.1415 +//    mov  ds, ax
 15.1416 +//    pop  ax
 15.1417 +//
 15.1418 +//  pop  bp
 15.1419 +//ASM_END
 15.1420 +//}
 15.1421 +
 15.1422 +  Bit16u
 15.1423 +get_SS()
 15.1424 +{
 15.1425 +ASM_START
 15.1426 +  mov  ax, ss
 15.1427 +ASM_END
 15.1428 +}
 15.1429 +
 15.1430 +#ifdef VMXASSIST
 15.1431 +void
 15.1432 +copy_e820_table()
 15.1433 +{
 15.1434 +  Bit8u nr_entries = read_byte(0x9000, 0x1e8);
 15.1435 +  if (nr_entries > 32)
 15.1436 +  	nr_entries = 32;
 15.1437 +  write_word(0xe000, 0x8, nr_entries);
 15.1438 +  memcpyb(0xe000, 0x10, 0x9000, 0x2d0, nr_entries * 0x14);
 15.1439 +}
 15.1440 +#endif /* VMXASSIST */
 15.1441 +
 15.1442 +#if BX_DEBUG_SERIAL
 15.1443 +/* serial debug port*/
 15.1444 +#define BX_DEBUG_PORT 0x03f8
 15.1445 +
 15.1446 +/* data */
 15.1447 +#define UART_RBR 0x00
 15.1448 +#define UART_THR 0x00
 15.1449 +
 15.1450 +/* control */
 15.1451 +#define UART_IER 0x01
 15.1452 +#define UART_IIR 0x02
 15.1453 +#define UART_FCR 0x02
 15.1454 +#define UART_LCR 0x03
 15.1455 +#define UART_MCR 0x04
 15.1456 +#define UART_DLL 0x00
 15.1457 +#define UART_DLM 0x01
 15.1458 +
 15.1459 +/* status */
 15.1460 +#define UART_LSR 0x05
 15.1461 +#define UART_MSR 0x06
 15.1462 +#define UART_SCR 0x07
 15.1463 +
 15.1464 +int uart_can_tx_byte(base_port)
 15.1465 +    Bit16u base_port;
 15.1466 +{
 15.1467 +    return inb(base_port + UART_LSR) & 0x20;
 15.1468 +}
 15.1469 +
 15.1470 +void uart_wait_to_tx_byte(base_port)
 15.1471 +    Bit16u base_port;
 15.1472 +{
 15.1473 +    while (!uart_can_tx_byte(base_port));
 15.1474 +}
 15.1475 +
 15.1476 +void uart_wait_until_sent(base_port)
 15.1477 +    Bit16u base_port;
 15.1478 +{
 15.1479 +    while (!(inb(base_port + UART_LSR) & 0x40));
 15.1480 +}
 15.1481 +
 15.1482 +void uart_tx_byte(base_port, data)
 15.1483 +    Bit16u base_port;
 15.1484 +    Bit8u data;
 15.1485 +{
 15.1486 +    uart_wait_to_tx_byte(base_port);
 15.1487 +    outb(base_port + UART_THR, data);
 15.1488 +    uart_wait_until_sent(base_port);
 15.1489 +}
 15.1490 +#endif
 15.1491 +
 15.1492 +  void
 15.1493 +wrch(c)
 15.1494 +  Bit8u  c;
 15.1495 +{
 15.1496 +  ASM_START
 15.1497 +  push bp
 15.1498 +  mov  bp, sp
 15.1499 +
 15.1500 +  push bx
 15.1501 +  mov  ah, #0x0e
 15.1502 +  mov  al, 4[bp]
 15.1503 +  xor  bx,bx
 15.1504 +  int  #0x10
 15.1505 +  pop  bx
 15.1506 +
 15.1507 +  pop  bp
 15.1508 +  ASM_END
 15.1509 +}
 15.1510 + 
 15.1511 +  void
 15.1512 +send(action, c)
 15.1513 +  Bit16u action;
 15.1514 +  Bit8u  c;
 15.1515 +{
 15.1516 +#if BX_DEBUG_SERIAL
 15.1517 +  if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
 15.1518 +  uart_tx_byte(BX_DEBUG_PORT, c);
 15.1519 +#endif
 15.1520 +#ifdef VMXASSIST
 15.1521 +  outb(0xE9, c);
 15.1522 +#endif
 15.1523 +#if BX_VIRTUAL_PORTS
 15.1524 +  if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
 15.1525 +  if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
 15.1526 +#endif
 15.1527 +  if (action & BIOS_PRINTF_SCREEN) {
 15.1528 +    if (c == '\n') wrch('\r');
 15.1529 +    wrch(c);
 15.1530 +  }
 15.1531 +}
 15.1532 +
 15.1533 +  void
 15.1534 +put_int(action, val, width, neg)
 15.1535 +  Bit16u action;
 15.1536 +  short val, width;
 15.1537 +  bx_bool neg;
 15.1538 +{
 15.1539 +  short nval = val / 10;
 15.1540 +  if (nval)
 15.1541 +    put_int(action, nval, width - 1, neg);
 15.1542 +  else {
 15.1543 +    while (--width > 0) send(action, ' ');
 15.1544 +    if (neg) send(action, '-');
 15.1545 +  }
 15.1546 +  send(action, val - (nval * 10) + '0');
 15.1547 +}
 15.1548 +
 15.1549 +  void
 15.1550 +put_uint(action, val, width, neg)
 15.1551 +  Bit16u action;
 15.1552 +  unsigned short val;
 15.1553 +  short width;
 15.1554 +  bx_bool neg;
 15.1555 +{
 15.1556 +  unsigned short nval = val / 10;
 15.1557 +  if (nval)
 15.1558 +    put_uint(action, nval, width - 1, neg);
 15.1559 +  else {
 15.1560 +    while (--width > 0) send(action, ' ');
 15.1561 +    if (neg) send(action, '-');
 15.1562 +  }
 15.1563 +  send(action, val - (nval * 10) + '0');
 15.1564 +}
 15.1565 +
 15.1566 +//--------------------------------------------------------------------------
 15.1567 +// bios_printf()
 15.1568 +//   A compact variable argument printf function which prints its output via
 15.1569 +//   an I/O port so that it can be logged by Bochs/Plex.  
 15.1570 +//   Currently, only %x is supported (or %02x, %04x, etc).
 15.1571 +//
 15.1572 +//   Supports %[format_width][format]
 15.1573 +//   where format can be d,x,c,s
 15.1574 +//--------------------------------------------------------------------------
 15.1575 +  void
 15.1576 +bios_printf(action, s)
 15.1577 +  Bit16u action;
 15.1578 +  Bit8u *s;
 15.1579 +{
 15.1580 +  Bit8u c, format_char;
 15.1581 +  bx_bool  in_format;
 15.1582 +  short i;
 15.1583 +  Bit16u  *arg_ptr;
 15.1584 +  Bit16u   arg_seg, arg, nibble, shift_count, format_width;
 15.1585 +
 15.1586 +  arg_ptr = &s;
 15.1587 +  arg_seg = get_SS();
 15.1588 +
 15.1589 +  in_format = 0;
 15.1590 +  format_width = 0;
 15.1591 +
 15.1592 +  if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
 15.1593 +#if BX_VIRTUAL_PORTS
 15.1594 +    outb(PANIC_PORT2, 0x00);
 15.1595 +#endif
 15.1596 +    bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
 15.1597 +  }
 15.1598 +
 15.1599 +  while (c = read_byte(get_CS(), s)) {
 15.1600 +    if ( c == '%' ) {
 15.1601 +      in_format = 1;
 15.1602 +      format_width = 0;
 15.1603 +      }
 15.1604 +    else if (in_format) {
 15.1605 +      if ( (c>='0') && (c<='9') ) {
 15.1606 +        format_width = (format_width * 10) + (c - '0');
 15.1607 +        }
 15.1608 +      else {
 15.1609 +        arg_ptr++; // increment to next arg
 15.1610 +        arg = read_word(arg_seg, arg_ptr);
 15.1611 +        if (c == 'x') {
 15.1612 +          if (format_width == 0)
 15.1613 +            format_width = 4;
 15.1614 +          for (i=format_width-1; i>=0; i--) {
 15.1615 +            nibble = (arg >> (4 * i)) & 0x000f;
 15.1616 +            send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
 15.1617 +            }
 15.1618 +          }
 15.1619 +        else if (c == 'u') {
 15.1620 +          put_uint(action, arg, format_width, 0);
 15.1621 +          }
 15.1622 +        else if (c == 'd') {
 15.1623 +          if (arg & 0x8000)
 15.1624 +            put_int(action, -arg, format_width - 1, 1);
 15.1625 +          else
 15.1626 +            put_int(action, arg, format_width, 0);
 15.1627 +          }
 15.1628 +        else if (c == 's') {
 15.1629 +          bios_printf(action & (~BIOS_PRINTF_HALT), arg);
 15.1630 +          }
 15.1631 +        else if (c == 'c') {
 15.1632 +          send(action, arg);
 15.1633 +          }
 15.1634 +        else
 15.1635 +          BX_PANIC("bios_printf: unknown format\n");
 15.1636 +          in_format = 0;
 15.1637 +        }
 15.1638 +      }
 15.1639 +    else {
 15.1640 +      send(action, c);
 15.1641 +      }
 15.1642 +    s ++;
 15.1643 +    }
 15.1644 +
 15.1645 +  if (action & BIOS_PRINTF_HALT) {
 15.1646 +    // freeze in a busy loop.  
 15.1647 +ASM_START
 15.1648 +    cli
 15.1649 + halt2_loop:
 15.1650 +    hlt
 15.1651 +    jmp halt2_loop
 15.1652 +ASM_END
 15.1653 +    }
 15.1654 +}
 15.1655 +
 15.1656 +//--------------------------------------------------------------------------
 15.1657 +// keyboard_init
 15.1658 +//--------------------------------------------------------------------------
 15.1659 +// this file is based on LinuxBIOS implementation of keyboard.c
 15.1660 +// could convert to #asm to gain space
 15.1661 +  void
 15.1662 +keyboard_init()
 15.1663 +{
 15.1664 +    Bit16u max;
 15.1665 +
 15.1666 +    /* ------------------- Flush buffers ------------------------*/
 15.1667 +    /* Wait until buffer is empty */
 15.1668 +    max=0xffff;
 15.1669 +    while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
 15.1670 +
 15.1671 +    /* flush incoming keys */
 15.1672 +    max=0x2000;
 15.1673 +    while (--max > 0) {
 15.1674 +        outb(0x80, 0x00);
 15.1675 +        if (inb(0x64) & 0x01) {
 15.1676 +            inb(0x60);
 15.1677 +            max = 0x2000;
 15.1678 +            }
 15.1679 +        }
 15.1680 +  
 15.1681 +    // Due to timer issues, and if the IPS setting is > 15000000, 
 15.1682 +    // the incoming keys might not be flushed here. That will
 15.1683 +    // cause a panic a few lines below.  See sourceforge bug report :
 15.1684 +    // [ 642031 ] FATAL: Keyboard RESET error:993
 15.1685 +
 15.1686 +    /* ------------------- controller side ----------------------*/
 15.1687 +    /* send cmd = 0xAA, self test 8042 */
 15.1688 +    outb(0x64, 0xaa);
 15.1689 +
 15.1690 +    /* Wait until buffer is empty */
 15.1691 +    max=0xffff;
 15.1692 +    while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
 15.1693 +    if (max==0x0) keyboard_panic(00);
 15.1694 +
 15.1695 +    /* Wait for data */
 15.1696 +    max=0xffff;
 15.1697 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
 15.1698 +    if (max==0x0) keyboard_panic(01);
 15.1699 +
 15.1700 +    /* read self-test result, 0x55 should be returned from 0x60 */
 15.1701 +    if ((inb(0x60) != 0x55)){
 15.1702 +        keyboard_panic(991);
 15.1703 +    }
 15.1704 +
 15.1705 +    /* send cmd = 0xAB, keyboard interface test */
 15.1706 +    outb(0x64,0xab);
 15.1707 +
 15.1708 +    /* Wait until buffer is empty */
 15.1709 +    max=0xffff;
 15.1710 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
 15.1711 +    if (max==0x0) keyboard_panic(10);
 15.1712 +
 15.1713 +    /* Wait for data */
 15.1714 +    max=0xffff;
 15.1715 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
 15.1716 +    if (max==0x0) keyboard_panic(11);
 15.1717 +
 15.1718 +    /* read keyboard interface test result, */
 15.1719 +    /* 0x00 should be returned form 0x60 */
 15.1720 +    if ((inb(0x60) != 0x00)) {
 15.1721 +        keyboard_panic(992);
 15.1722 +    }
 15.1723 +
 15.1724 +    /* Enable Keyboard clock */
 15.1725 +    outb(0x64,0xae);
 15.1726 +    outb(0x64,0xa8);
 15.1727 +
 15.1728 +    /* ------------------- keyboard side ------------------------*/
 15.1729 +    /* reset kerboard and self test  (keyboard side) */
 15.1730 +    outb(0x60, 0xff);
 15.1731 +
 15.1732 +    /* Wait until buffer is empty */
 15.1733 +    max=0xffff;
 15.1734 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
 15.1735 +    if (max==0x0) keyboard_panic(20);
 15.1736 +
 15.1737 +    /* Wait for data */
 15.1738 +    max=0xffff;
 15.1739 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
 15.1740 +    if (max==0x0) keyboard_panic(21);
 15.1741 +
 15.1742 +    /* keyboard should return ACK */
 15.1743 +    if ((inb(0x60) != 0xfa)) {
 15.1744 +        keyboard_panic(993);
 15.1745 +    }
 15.1746 +
 15.1747 +    /* Wait for data */
 15.1748 +    max=0xffff;
 15.1749 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
 15.1750 +    if (max==0x0) keyboard_panic(31);
 15.1751 +
 15.1752 +    if ((inb(0x60) != 0xaa)) {
 15.1753 +        keyboard_panic(994);
 15.1754 +    }
 15.1755 +
 15.1756 +    /* Disable keyboard */
 15.1757 +    outb(0x60, 0xf5);
 15.1758 +
 15.1759 +    /* Wait until buffer is empty */
 15.1760 +    max=0xffff;
 15.1761 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
 15.1762 +    if (max==0x0) keyboard_panic(40);
 15.1763 +
 15.1764 +    /* Wait for data */
 15.1765 +    max=0xffff;
 15.1766 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
 15.1767 +    if (max==0x0) keyboard_panic(41);
 15.1768 +
 15.1769 +    /* keyboard should return ACK */
 15.1770 +    if ((inb(0x60) != 0xfa)) {
 15.1771 +        keyboard_panic(995);
 15.1772 +    }
 15.1773 +
 15.1774 +    /* Write Keyboard Mode */
 15.1775 +    outb(0x64, 0x60);
 15.1776 +
 15.1777 +    /* Wait until buffer is empty */
 15.1778 +    max=0xffff;
 15.1779 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
 15.1780 +    if (max==0x0) keyboard_panic(50);
 15.1781 +
 15.1782 +    /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
 15.1783 +    outb(0x60, 0x61);
 15.1784 +
 15.1785 +    /* Wait until buffer is empty */
 15.1786 +    max=0xffff;
 15.1787 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
 15.1788 +    if (max==0x0) keyboard_panic(60);
 15.1789 +
 15.1790 +    /* Enable keyboard */
 15.1791 +    outb(0x60, 0xf4);
 15.1792 +
 15.1793 +    /* Wait until buffer is empty */
 15.1794 +    max=0xffff;
 15.1795 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
 15.1796 +    if (max==0x0) keyboard_panic(70);
 15.1797 +
 15.1798 +    /* Wait for data */
 15.1799 +    max=0xffff;
 15.1800 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
 15.1801 +    if (max==0x0) keyboard_panic(70);
 15.1802 +
 15.1803 +    /* keyboard should return ACK */
 15.1804 +    if ((inb(0x60) != 0xfa)) {
 15.1805 +        keyboard_panic(996);
 15.1806 +    }
 15.1807 +
 15.1808 +    outb(0x80, 0x77);
 15.1809 +}
 15.1810 +
 15.1811 +//--------------------------------------------------------------------------
 15.1812 +// keyboard_panic
 15.1813 +//--------------------------------------------------------------------------
 15.1814 +  void
 15.1815 +keyboard_panic(status)
 15.1816 +  Bit16u status;
 15.1817 +{
 15.1818 +  // If you're getting a 993 keyboard panic here, 
 15.1819 +  // please see the comment in keyboard_init
 15.1820 +  
 15.1821 +  BX_PANIC("Keyboard error:%u\n",status);
 15.1822 +}
 15.1823 +
 15.1824 +//--------------------------------------------------------------------------
 15.1825 +// shutdown_status_panic
 15.1826 +//   called when the shutdown statsu is not implemented, displays the status
 15.1827 +//--------------------------------------------------------------------------
 15.1828 +  void
 15.1829 +shutdown_status_panic(status)
 15.1830 +  Bit16u status;
 15.1831 +{
 15.1832 +  BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
 15.1833 +}
 15.1834 +
 15.1835 +//--------------------------------------------------------------------------
 15.1836 +// print_bios_banner
 15.1837 +//   displays a the bios version
 15.1838 +//--------------------------------------------------------------------------
 15.1839 +void
 15.1840 +print_bios_banner()
 15.1841 +{
 15.1842 +  printf(BX_APPNAME" BIOS, %d cpu%s, ", BX_SMP_PROCESSORS, BX_SMP_PROCESSORS>1?"s":"");
 15.1843 +  printf("%s %s\n", bios_cvs_version_string, bios_date_string);
 15.1844 +  printf("\n");
 15.1845 +}
 15.1846 +
 15.1847 +//--------------------------------------------------------------------------
 15.1848 +// print_boot_device
 15.1849 +//   displays the boot device
 15.1850 +//--------------------------------------------------------------------------
 15.1851 +
 15.1852 +static char drivetypes[][10]={"Floppy","Hard Disk","CD-Rom"};
 15.1853 +
 15.1854 +void
 15.1855 +print_boot_device(cdboot, drive)
 15.1856 +  Bit8u cdboot; Bit16u drive;
 15.1857 +{
 15.1858 +  Bit8u i;
 15.1859 +
 15.1860 +  // cdboot contains 0 if floppy/harddisk, 1 otherwise
 15.1861 +  // drive contains real/emulated boot drive
 15.1862 +
 15.1863 +  if(cdboot)i=2;                    // CD-Rom
 15.1864 +  else if((drive&0x0080)==0x00)i=0; // Floppy
 15.1865 +  else if((drive&0x0080)==0x80)i=1; // Hard drive
 15.1866 +  else return;
 15.1867 +  
 15.1868 +  printf("Booting from %s...\n",drivetypes[i]);
 15.1869 +}
 15.1870 +
 15.1871 +//--------------------------------------------------------------------------
 15.1872 +// print_boot_failure
 15.1873 +//   displays the reason why boot failed
 15.1874 +//--------------------------------------------------------------------------
 15.1875 +  void
 15.1876 +print_boot_failure(cdboot, drive, reason, lastdrive)
 15.1877 +  Bit8u cdboot; Bit8u drive; Bit8u lastdrive;
 15.1878 +{
 15.1879 +  Bit16u drivenum = drive&0x7f;
 15.1880 +
 15.1881 +  // cdboot: 1 if boot from cd, 0 otherwise
 15.1882 +  // drive : drive number
 15.1883 +  // reason: 0 signature check failed, 1 read error
 15.1884 +  // lastdrive: 1 boot drive is the last one in boot sequence
 15.1885 + 
 15.1886 +  if (cdboot)
 15.1887 +    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s failed\n",drivetypes[2]);
 15.1888 +  else if (drive & 0x80)
 15.1889 +    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[1],drivenum);
 15.1890 +  else
 15.1891 +    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[0],drivenum);
 15.1892 +
 15.1893 +  if (lastdrive==1) {
 15.1894 +    if (reason==0)
 15.1895 +      BX_PANIC("Not a bootable disk\n");
 15.1896 +    else
 15.1897 +      BX_PANIC("Could not read the boot disk\n");
 15.1898 +  }
 15.1899 +}
 15.1900 +
 15.1901 +//--------------------------------------------------------------------------
 15.1902 +// print_cdromboot_failure
 15.1903 +//   displays the reason why boot failed
 15.1904 +//--------------------------------------------------------------------------
 15.1905 +  void
 15.1906 +print_cdromboot_failure( code )
 15.1907 +  Bit16u code;
 15.1908 +{
 15.1909 +  bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
 15.1910 +  
 15.1911 +  return;
 15.1912 +}
 15.1913 +
 15.1914 +void
 15.1915 +nmi_handler_msg()
 15.1916 +{
 15.1917 +  BX_PANIC("NMI Handler called\n");
 15.1918 +}
 15.1919 +
 15.1920 +void
 15.1921 +int18_panic_msg()
 15.1922 +{
 15.1923 +  BX_PANIC("INT18: BOOT FAILURE\n");
 15.1924 +}
 15.1925 +
 15.1926 +void
 15.1927 +log_bios_start()
 15.1928 +{
 15.1929 +#if BX_DEBUG_SERIAL
 15.1930 +  outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
 15.1931 +#endif
 15.1932 +  BX_INFO("%s\n", bios_version_string);
 15.1933 +}
 15.1934 +
 15.1935 +  bx_bool
 15.1936 +set_enable_a20(val)
 15.1937 +  bx_bool val;
 15.1938 +{
 15.1939 +  Bit8u  oldval;
 15.1940 +
 15.1941 +  // Use PS2 System Control port A to set A20 enable
 15.1942 +
 15.1943 +  // get current setting first
 15.1944 +  oldval = inb(0x92);
 15.1945 +
 15.1946 +  // change A20 status
 15.1947 +  if (val)
 15.1948 +    outb(0x92, oldval | 0x02);
 15.1949 +  else
 15.1950 +    outb(0x92, oldval & 0xfd);
 15.1951 +
 15.1952 +  return((oldval & 0x02) != 0);
 15.1953 +}
 15.1954 +
 15.1955 +  void
 15.1956 +debugger_on()
 15.1957 +{
 15.1958 +  outb(0xfedc, 0x01);
 15.1959 +}
 15.1960 +
 15.1961 +  void
 15.1962 +debugger_off()
 15.1963 +{
 15.1964 +  outb(0xfedc, 0x00);
 15.1965 +}
 15.1966 +
 15.1967 +#if BX_USE_ATADRV
 15.1968 +
 15.1969 +// ---------------------------------------------------------------------------
 15.1970 +// Start of ATA/ATAPI Driver
 15.1971 +// ---------------------------------------------------------------------------
 15.1972 +
 15.1973 +// Global defines -- ATA register and register bits.
 15.1974 +// command block & control block regs
 15.1975 +#define ATA_CB_DATA  0   // data reg         in/out pio_base_addr1+0
 15.1976 +#define ATA_CB_ERR   1   // error            in     pio_base_addr1+1
 15.1977 +#define ATA_CB_FR    1   // feature reg         out pio_base_addr1+1
 15.1978 +#define ATA_CB_SC    2   // sector count     in/out pio_base_addr1+2
 15.1979 +#define ATA_CB_SN    3   // sector number    in/out pio_base_addr1+3
 15.1980 +#define ATA_CB_CL    4   // cylinder low     in/out pio_base_addr1+4
 15.1981 +#define ATA_CB_CH    5   // cylinder high    in/out pio_base_addr1+5
 15.1982 +#define ATA_CB_DH    6   // device head      in/out pio_base_addr1+6
 15.1983 +#define ATA_CB_STAT  7   // primary status   in     pio_base_addr1+7
 15.1984 +#define ATA_CB_CMD   7   // command             out pio_base_addr1+7
 15.1985 +#define ATA_CB_ASTAT 6   // alternate status in     pio_base_addr2+6
 15.1986 +#define ATA_CB_DC    6   // device control      out pio_base_addr2+6
 15.1987 +#define ATA_CB_DA    7   // device address   in     pio_base_addr2+7
 15.1988 +
 15.1989 +#define ATA_CB_ER_ICRC 0x80    // ATA Ultra DMA bad CRC
 15.1990 +#define ATA_CB_ER_BBK  0x80    // ATA bad block
 15.1991 +#define ATA_CB_ER_UNC  0x40    // ATA uncorrected error
 15.1992 +#define ATA_CB_ER_MC   0x20    // ATA media change
 15.1993 +#define ATA_CB_ER_IDNF 0x10    // ATA id not found
 15.1994 +#define ATA_CB_ER_MCR  0x08    // ATA media change request
 15.1995 +#define ATA_CB_ER_ABRT 0x04    // ATA command aborted
 15.1996 +#define ATA_CB_ER_NTK0 0x02    // ATA track 0 not found
 15.1997 +#define ATA_CB_ER_NDAM 0x01    // ATA address mark not found
 15.1998 +
 15.1999 +#define ATA_CB_ER_P_SNSKEY 0xf0   // ATAPI sense key (mask)
 15.2000 +#define ATA_CB_ER_P_MCR    0x08   // ATAPI Media Change Request
 15.2001 +#define ATA_CB_ER_P_ABRT   0x04   // ATAPI command abort
 15.2002 +#define ATA_CB_ER_P_EOM    0x02   // ATAPI End of Media
 15.2003 +#define ATA_CB_ER_P_ILI    0x01   // ATAPI Illegal Length Indication
 15.2004 +
 15.2005 +// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
 15.2006 +#define ATA_CB_SC_P_TAG    0xf8   // ATAPI tag (mask)
 15.2007 +#define ATA_CB_SC_P_REL    0x04   // ATAPI release
 15.2008 +#define ATA_CB_SC_P_IO     0x02   // ATAPI I/O
 15.2009 +#define ATA_CB_SC_P_CD     0x01   // ATAPI C/D
 15.2010 +
 15.2011 +// bits 7-4 of the device/head (CB_DH) reg
 15.2012 +#define ATA_CB_DH_DEV0 0xa0    // select device 0
 15.2013 +#define ATA_CB_DH_DEV1 0xb0    // select device 1
 15.2014 +
 15.2015 +// status reg (CB_STAT and CB_ASTAT) bits
 15.2016 +#define ATA_CB_STAT_BSY  0x80  // busy
 15.2017 +#define ATA_CB_STAT_RDY  0x40  // ready
 15.2018 +#define ATA_CB_STAT_DF   0x20  // device fault
 15.2019 +#define ATA_CB_STAT_WFT  0x20  // write fault (old name)
 15.2020 +#define ATA_CB_STAT_SKC  0x10  // seek complete
 15.2021 +#define ATA_CB_STAT_SERV 0x10  // service
 15.2022 +#define ATA_CB_STAT_DRQ  0x08  // data request
 15.2023 +#define ATA_CB_STAT_CORR 0x04  // corrected
 15.2024 +#define ATA_CB_STAT_IDX  0x02  // index
 15.2025 +#define ATA_CB_STAT_ERR  0x01  // error (ATA)
 15.2026 +#define ATA_CB_STAT_CHK  0x01  // check (ATAPI)
 15.2027 +
 15.2028 +// device control reg (CB_DC) bits
 15.2029 +#define ATA_CB_DC_HD15   0x08  // bit should always be set to one
 15.2030 +#define ATA_CB_DC_SRST   0x04  // soft reset
 15.2031 +#define ATA_CB_DC_NIEN   0x02  // disable interrupts
 15.2032 +
 15.2033 +// Most mandtory and optional ATA commands (from ATA-3),
 15.2034 +#define ATA_CMD_CFA_ERASE_SECTORS            0xC0
 15.2035 +#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE     0x03
 15.2036 +#define ATA_CMD_CFA_TRANSLATE_SECTOR         0x87
 15.2037 +#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE  0xCD
 15.2038 +#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE   0x38
 15.2039 +#define ATA_CMD_CHECK_POWER_MODE1            0xE5
 15.2040 +#define ATA_CMD_CHECK_POWER_MODE2            0x98
 15.2041 +#define ATA_CMD_DEVICE_RESET                 0x08
 15.2042 +#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC    0x90
 15.2043 +#define ATA_CMD_FLUSH_CACHE                  0xE7
 15.2044 +#define ATA_CMD_FORMAT_TRACK                 0x50
 15.2045 +#define ATA_CMD_IDENTIFY_DEVICE              0xEC
 15.2046 +#define ATA_CMD_IDENTIFY_DEVICE_PACKET       0xA1
 15.2047 +#define ATA_CMD_IDENTIFY_PACKET_DEVICE       0xA1
 15.2048 +#define ATA_CMD_IDLE1                        0xE3
 15.2049 +#define ATA_CMD_IDLE2                        0x97
 15.2050 +#define ATA_CMD_IDLE_IMMEDIATE1              0xE1
 15.2051 +#define ATA_CMD_IDLE_IMMEDIATE2              0x95
 15.2052 +#define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS  0x91
 15.2053 +#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
 15.2054 +#define ATA_CMD_NOP                          0x00
 15.2055 +#define ATA_CMD_PACKET                       0xA0
 15.2056 +#define ATA_CMD_READ_BUFFER                  0xE4
 15.2057 +#define ATA_CMD_READ_DMA                     0xC8
 15.2058 +#define ATA_CMD_READ_DMA_QUEUED              0xC7
 15.2059 +#define ATA_CMD_READ_MULTIPLE                0xC4
 15.2060 +#define ATA_CMD_READ_SECTORS                 0x20
 15.2061 +#define ATA_CMD_READ_VERIFY_SECTORS          0x40
 15.2062 +#define ATA_CMD_RECALIBRATE                  0x10
 15.2063 +#define ATA_CMD_SEEK                         0x70
 15.2064 +#define ATA_CMD_SET_FEATURES                 0xEF
 15.2065 +#define ATA_CMD_SET_MULTIPLE_MODE            0xC6
 15.2066 +#define ATA_CMD_SLEEP1                       0xE6
 15.2067 +#define ATA_CMD_SLEEP2                       0x99
 15.2068 +#define ATA_CMD_STANDBY1                     0xE2
 15.2069 +#define ATA_CMD_STANDBY2                     0x96
 15.2070 +#define ATA_CMD_STANDBY_IMMEDIATE1           0xE0
 15.2071 +#define ATA_CMD_STANDBY_IMMEDIATE2           0x94
 15.2072 +#define ATA_CMD_WRITE_BUFFER                 0xE8
 15.2073 +#define ATA_CMD_WRITE_DMA                    0xCA
 15.2074 +#define ATA_CMD_WRITE_DMA_QUEUED             0xCC
 15.2075 +#define ATA_CMD_WRITE_MULTIPLE               0xC5
 15.2076 +#define ATA_CMD_WRITE_SECTORS                0x30
 15.2077 +#define ATA_CMD_WRITE_VERIFY                 0x3C
 15.2078 +
 15.2079 +#define ATA_IFACE_NONE    0x00
 15.2080 +#define ATA_IFACE_ISA     0x00
 15.2081 +#define ATA_IFACE_PCI     0x01
 15.2082 +
 15.2083 +#define ATA_TYPE_NONE     0x00
 15.2084 +#define ATA_TYPE_UNKNOWN  0x01
 15.2085 +#define ATA_TYPE_ATA      0x02
 15.2086 +#define ATA_TYPE_ATAPI    0x03
 15.2087 +
 15.2088 +#define ATA_DEVICE_NONE  0x00
 15.2089 +#define ATA_DEVICE_HD    0xFF
 15.2090 +#define ATA_DEVICE_CDROM 0x05
 15.2091 +
 15.2092 +#define ATA_MODE_NONE    0x00
 15.2093 +#define ATA_MODE_PIO16   0x00
 15.2094 +#define ATA_MODE_PIO32   0x01
 15.2095 +#define ATA_MODE_ISADMA  0x02
 15.2096 +#define ATA_MODE_PCIDMA  0x03
 15.2097 +#define ATA_MODE_USEIRQ  0x10
 15.2098 +
 15.2099 +#define ATA_TRANSLATION_NONE  0
 15.2100 +#define ATA_TRANSLATION_LBA   1
 15.2101 +#define ATA_TRANSLATION_LARGE 2
 15.2102 +#define ATA_TRANSLATION_RECHS 3
 15.2103 +
 15.2104 +#define ATA_DATA_NO      0x00
 15.2105 +#define ATA_DATA_IN      0x01
 15.2106 +#define ATA_DATA_OUT     0x02
 15.2107 +  
 15.2108 +// ---------------------------------------------------------------------------
 15.2109 +// ATA/ATAPI driver : initialization
 15.2110 +// ---------------------------------------------------------------------------
 15.2111 +void ata_init( )
 15.2112 +{
 15.2113 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.2114 +  Bit8u  channel, device;
 15.2115 +
 15.2116 +  // Channels info init. 
 15.2117 +  for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
 15.2118 +    write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
 15.2119 +    write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
 15.2120 +    write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
 15.2121 +    write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
 15.2122 +    }
 15.2123 +
 15.2124 +  // Devices info init. 
 15.2125 +  for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
 15.2126 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
 15.2127 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
 15.2128 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
 15.2129 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
 15.2130 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
 15.2131 +    write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
 15.2132 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
 15.2133 +    write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
 15.2134 +    write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
 15.2135 +    write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
 15.2136 +    write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
 15.2137 +    write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
 15.2138 +    write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
 15.2139 +    
 15.2140 +    write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
 15.2141 +    }
 15.2142 +
 15.2143 +  // hdidmap  and cdidmap init. 
 15.2144 +  for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
 15.2145 +    write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
 15.2146 +    write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
 15.2147 +    }
 15.2148 +
 15.2149 +  write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
 15.2150 +  write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
 15.2151 +}
 15.2152 +
 15.2153 +// ---------------------------------------------------------------------------
 15.2154 +// ATA/ATAPI driver : device detection
 15.2155 +// ---------------------------------------------------------------------------
 15.2156 +
 15.2157 +void ata_detect( )
 15.2158 +{
 15.2159 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.2160 +  Bit8u  hdcount, cdcount, device, type;
 15.2161 +  Bit8u  buffer[0x0200];
 15.2162 +
 15.2163 +#if BX_MAX_ATA_INTERFACES > 0
 15.2164 +  write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
 15.2165 +  write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
 15.2166 +  write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
 15.2167 +  write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
 15.2168 +#endif
 15.2169 +#if BX_MAX_ATA_INTERFACES > 1
 15.2170 +  write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
 15.2171 +  write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
 15.2172 +  write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
 15.2173 +  write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
 15.2174 +#endif
 15.2175 +#if BX_MAX_ATA_INTERFACES > 2
 15.2176 +  write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
 15.2177 +  write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
 15.2178 +  write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
 15.2179 +  write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
 15.2180 +#endif
 15.2181 +#if BX_MAX_ATA_INTERFACES > 3
 15.2182 +  write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
 15.2183 +  write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
 15.2184 +  write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
 15.2185 +  write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
 15.2186 +#endif
 15.2187 +#if BX_MAX_ATA_INTERFACES > 4
 15.2188 +#error Please fill the ATA interface informations
 15.2189 +#endif
 15.2190 +
 15.2191 +  // Device detection
 15.2192 +  hdcount=cdcount=0;
 15.2193 +  
 15.2194 +  for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
 15.2195 +    Bit16u iobase1, iobase2;
 15.2196 +    Bit8u  channel, slave, shift;
 15.2197 +    Bit8u  sc, sn, cl, ch, st;
 15.2198 +
 15.2199 +    channel = device / 2;
 15.2200 +    slave = device % 2;
 15.2201 +
 15.2202 +    iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
 15.2203 +    iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
 15.2204 +
 15.2205 +    // Disable interrupts
 15.2206 +    outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
 15.2207 +
 15.2208 +    // Look for device
 15.2209 +    outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
 15.2210 +    outb(iobase1+ATA_CB_SC, 0x55);
 15.2211 +    outb(iobase1+ATA_CB_SN, 0xaa);
 15.2212 +    outb(iobase1+ATA_CB_SC, 0xaa);
 15.2213 +    outb(iobase1+ATA_CB_SN, 0x55);
 15.2214 +    outb(iobase1+ATA_CB_SC, 0x55);
 15.2215 +    outb(iobase1+ATA_CB_SN, 0xaa);
 15.2216 +
 15.2217 +    // If we found something
 15.2218 +    sc = inb(iobase1+ATA_CB_SC);
 15.2219 +    sn = inb(iobase1+ATA_CB_SN);
 15.2220 +
 15.2221 +    if ( (sc == 0x55) && (sn == 0xaa) ) {
 15.2222 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
 15.2223 +    
 15.2224 +      // reset the channel
 15.2225 +      ata_reset (device);
 15.2226 +      
 15.2227 +      // check for ATA or ATAPI
 15.2228 +      outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
 15.2229 +      sc = inb(iobase1+ATA_CB_SC);
 15.2230 +      sn = inb(iobase1+ATA_CB_SN);
 15.2231 +      if ( (sc==0x01) && (sn==0x01) ) {
 15.2232 +        cl = inb(iobase1+ATA_CB_CL);
 15.2233 +        ch = inb(iobase1+ATA_CB_CH);
 15.2234 +        st = inb(iobase1+ATA_CB_STAT);
 15.2235 +
 15.2236 +        if ( (cl==0x14) && (ch==0xeb) ) {
 15.2237 +          write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
 15.2238 +          }
 15.2239 +        else if ( (cl==0x00) && (ch==0x00) && (st!=0x00) ) {
 15.2240 +          write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
 15.2241 +          }
 15.2242 +        }
 15.2243 +      }
 15.2244 +
 15.2245 +    type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
 15.2246 +    
 15.2247 +    // Now we send a IDENTIFY command to ATA device 
 15.2248 +    if(type == ATA_TYPE_ATA) {
 15.2249 +      Bit32u sectors;
 15.2250 +      Bit16u cylinders, heads, spt, blksize;
 15.2251 +      Bit8u  translation, removable, mode;
 15.2252 +
 15.2253 +      //Temporary values to do the transfer
 15.2254 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
 15.2255 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
 15.2256 +
 15.2257 +      if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
 15.2258 +        BX_PANIC("ata-detect: Failed to detect ATA device\n");
 15.2259 +
 15.2260 +      removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
 15.2261 +      mode      = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
 15.2262 +      blksize   = read_word(get_SS(),buffer+10);
 15.2263 +      
 15.2264 +      cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
 15.2265 +      heads     = read_word(get_SS(),buffer+(3*2)); // word 3
 15.2266 +      spt       = read_word(get_SS(),buffer+(6*2)); // word 6
 15.2267 +
 15.2268 +      sectors   = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
 15.2269 +
 15.2270 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
 15.2271 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
 15.2272 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
 15.2273 +      write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
 15.2274 +      write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
 15.2275 +      write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
 15.2276 +      write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
 15.2277 +      write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
 15.2278 +      BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
 15.2279 +
 15.2280 +      translation = inb_cmos(0x39 + channel/2);
 15.2281 +      for (shift=device%4; shift>0; shift--) translation >>= 2;
 15.2282 +      translation &= 0x03;
 15.2283 +
 15.2284 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
 15.2285 +
 15.2286 +      switch (translation) {
 15.2287 +        case ATA_TRANSLATION_NONE:
 15.2288 +          BX_INFO("none");
 15.2289 +          break;
 15.2290 +        case ATA_TRANSLATION_LBA:
 15.2291 +          BX_INFO("lba");
 15.2292 +          break;
 15.2293 +        case ATA_TRANSLATION_LARGE:
 15.2294 +          BX_INFO("large");
 15.2295 +          break;
 15.2296 +        case ATA_TRANSLATION_RECHS:
 15.2297 +          BX_INFO("r-echs");
 15.2298 +          break;
 15.2299 +        }
 15.2300 +      switch (translation) {
 15.2301 +        case ATA_TRANSLATION_NONE:
 15.2302 +          break;
 15.2303 +        case ATA_TRANSLATION_LBA:
 15.2304 +          spt = 63;
 15.2305 +          sectors /= 63;
 15.2306 +          heads = sectors / 1024;
 15.2307 +          if (heads>128) heads = 255;
 15.2308 +          else if (heads>64) heads = 128;
 15.2309 +          else if (heads>32) heads = 64;
 15.2310 +          else if (heads>16) heads = 32;
 15.2311 +          else heads=16;
 15.2312 +          cylinders = sectors / heads;
 15.2313 +          break;
 15.2314 +        case ATA_TRANSLATION_RECHS:
 15.2315 +          // Take care not to overflow
 15.2316 +          if (heads==16) {
 15.2317 +            if(cylinders>61439) cylinders=61439;
 15.2318 +            heads=15;
 15.2319 +            cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
 15.2320 +            }
 15.2321 +          // then go through the large bitshift process
 15.2322 +        case ATA_TRANSLATION_LARGE:
 15.2323 +          while(cylinders > 1024) {
 15.2324 +            cylinders >>= 1;
 15.2325 +            heads <<= 1;
 15.2326 +
 15.2327 +            // If we max out the head count
 15.2328 +            if (heads > 127) break;
 15.2329 +          }
 15.2330 +          break;
 15.2331 +        }
 15.2332 +      // clip to 1024 cylinders in lchs
 15.2333 +      if (cylinders > 1024) cylinders=1024;
 15.2334 +      BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
 15.2335 +
 15.2336 +      write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
 15.2337 +      write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
 15.2338 +      write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
 15.2339 + 
 15.2340 +      // fill hdidmap 
 15.2341 +      write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
 15.2342 +      hdcount++;
 15.2343 +      }
 15.2344 +    
 15.2345 +    // Now we send a IDENTIFY command to ATAPI device
 15.2346 +    if(type == ATA_TYPE_ATAPI) {
 15.2347 + 
 15.2348 +      Bit8u  type, removable, mode;
 15.2349 +      Bit16u blksize;
 15.2350 +
 15.2351 +      //Temporary values to do the transfer
 15.2352 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
 15.2353 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
 15.2354 +
 15.2355 +      if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
 15.2356 +        BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
 15.2357 +
 15.2358 +      type      = read_byte(get_SS(),buffer+1) & 0x1f;
 15.2359 +      removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
 15.2360 +      mode      = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
 15.2361 +      blksize   = 2048;
 15.2362 +
 15.2363 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
 15.2364 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
 15.2365 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
 15.2366 +      write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
 15.2367 +
 15.2368 +      // fill cdidmap 
 15.2369 +      write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
 15.2370 +      cdcount++;
 15.2371 +      }
 15.2372 +  
 15.2373 +      {
 15.2374 +      Bit32u sizeinmb;
 15.2375 +      Bit16u ataversion;
 15.2376 +      Bit8u  c, i, version, model[41];
 15.2377 +      
 15.2378 +      switch (type) {
 15.2379 +        case ATA_TYPE_ATA:
 15.2380 +          sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
 15.2381 +          sizeinmb >>= 11;
 15.2382 +        case ATA_TYPE_ATAPI:
 15.2383 +          // Read ATA/ATAPI version
 15.2384 +          ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
 15.2385 +          for(version=15;version>0;version--) { 
 15.2386 +            if((ataversion&(1<<version))!=0)
 15.2387 +            break;
 15.2388 +            }
 15.2389 +
 15.2390 +          // Read model name
 15.2391 +          for(i=0;i<20;i++){
 15.2392 +            write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
 15.2393 +            write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
 15.2394 +            }
 15.2395 +
 15.2396 +          // Reformat
 15.2397 +          write_byte(get_SS(),model+40,0x00);
 15.2398 +          for(i=39;i>0;i--){
 15.2399 +            if(read_byte(get_SS(),model+i)==0x20)
 15.2400 +              write_byte(get_SS(),model+i,0x00);
 15.2401 +            else break;
 15.2402 +            }
 15.2403 +          break;
 15.2404 +        }
 15.2405 +
 15.2406 +      switch (type) {
 15.2407 +        case ATA_TYPE_ATA:
 15.2408 +          printf("ata%d %s: ",channel,slave?" slave":"master");
 15.2409 +          i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
 15.2410 +          printf(" ATA-%d Hard-Disk (%d MBytes)\n",version,(Bit16u)sizeinmb);
 15.2411 +          break;
 15.2412 +        case ATA_TYPE_ATAPI:
 15.2413 +          printf("ata%d %s: ",channel,slave?" slave":"master");
 15.2414 +          i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
 15.2415 +          if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
 15.2416 +            printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
 15.2417 +          else
 15.2418 +            printf(" ATAPI-%d Device\n",version);
 15.2419 +          break;
 15.2420 +        case ATA_TYPE_UNKNOWN:
 15.2421 +          printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
 15.2422 +          break;
 15.2423 +        }
 15.2424 +      }
 15.2425 +    }
 15.2426 +
 15.2427 +  // Store the devices counts
 15.2428 +  write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
 15.2429 +  write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
 15.2430 +  write_byte(0x40,0x75, hdcount);
 15.2431 + 
 15.2432 +  printf("\n");
 15.2433 +
 15.2434 +  // FIXME : should use bios=cmos|auto|disable bits
 15.2435 +  // FIXME : should know about translation bits
 15.2436 +  // FIXME : move hard_drive_post here 
 15.2437 +  
 15.2438 +}
 15.2439 +
 15.2440 +// ---------------------------------------------------------------------------
 15.2441 +// ATA/ATAPI driver : software reset 
 15.2442 +// ---------------------------------------------------------------------------
 15.2443 +// ATA-3
 15.2444 +// 8.2.1 Software reset - Device 0
 15.2445 +
 15.2446 +void   ata_reset(device)
 15.2447 +Bit16u device;
 15.2448 +{
 15.2449 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.2450 +  Bit16u iobase1, iobase2;
 15.2451 +  Bit8u  channel, slave, sn, sc; 
 15.2452 +  Bit16u max;
 15.2453 +
 15.2454 +  channel = device / 2;
 15.2455 +  slave = device % 2;
 15.2456 +
 15.2457 +  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
 15.2458 +  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
 15.2459 +
 15.2460 +  // Reset
 15.2461 +
 15.2462 +// 8.2.1 (a) -- set SRST in DC
 15.2463 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
 15.2464 +
 15.2465 +// 8.2.1 (b) -- wait for BSY
 15.2466 +  max=0xff;
 15.2467 +  while(--max>0) {
 15.2468 +    Bit8u status = inb(iobase1+ATA_CB_STAT);
 15.2469 +    if ((status & ATA_CB_STAT_BSY) != 0) break;
 15.2470 +  }
 15.2471 +
 15.2472 +// 8.2.1 (f) -- clear SRST
 15.2473 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
 15.2474 +
 15.2475 +  if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
 15.2476 +
 15.2477 +// 8.2.1 (g) -- check for sc==sn==0x01
 15.2478 +    // select device
 15.2479 +    outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
 15.2480 +    sc = inb(iobase1+ATA_CB_SC);
 15.2481 +    sn = inb(iobase1+ATA_CB_SN);
 15.2482 +
 15.2483 +    if ( (sc==0x01) && (sn==0x01) ) {
 15.2484 +
 15.2485 +// 8.2.1 (h) -- wait for not BSY
 15.2486 +      max=0xff;
 15.2487 +      while(--max>0) {
 15.2488 +        Bit8u status = inb(iobase1+ATA_CB_STAT);
 15.2489 +        if ((status & ATA_CB_STAT_BSY) == 0) break;
 15.2490 +        }
 15.2491 +      }
 15.2492 +    }
 15.2493 +
 15.2494 +// 8.2.1 (i) -- wait for DRDY
 15.2495 +  max=0xfff;
 15.2496 +  while(--max>0) {
 15.2497 +    Bit8u status = inb(iobase1+ATA_CB_STAT);
 15.2498 +      if ((status & ATA_CB_STAT_RDY) != 0) break;
 15.2499 +  }
 15.2500 +
 15.2501 +  // Enable interrupts
 15.2502 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
 15.2503 +}
 15.2504 +
 15.2505 +// ---------------------------------------------------------------------------
 15.2506 +// ATA/ATAPI driver : execute a non data command 
 15.2507 +// ---------------------------------------------------------------------------
 15.2508 +
 15.2509 +Bit16u ata_cmd_non_data()
 15.2510 +{return 0;}
 15.2511 +
 15.2512 +// ---------------------------------------------------------------------------
 15.2513 +// ATA/ATAPI driver : execute a data-in command
 15.2514 +// ---------------------------------------------------------------------------
 15.2515 +      // returns
 15.2516 +      // 0 : no error
 15.2517 +      // 1 : BUSY bit set
 15.2518 +      // 2 : read error
 15.2519 +      // 3 : expected DRQ=1
 15.2520 +      // 4 : no sectors left to read/verify
 15.2521 +      // 5 : more sectors to read/verify
 15.2522 +      // 6 : no sectors left to write
 15.2523 +      // 7 : more sectors to write
 15.2524 +Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
 15.2525 +Bit16u device, command, count, cylinder, head, sector, segment, offset;
 15.2526 +Bit32u lba;
 15.2527 +{
 15.2528 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.2529 +  Bit16u iobase1, iobase2, blksize;
 15.2530 +  Bit8u  channel, slave;
 15.2531 +  Bit8u  status, current, mode;
 15.2532 +
 15.2533 +  channel = device / 2;
 15.2534 +  slave   = device % 2;
 15.2535 +
 15.2536 +  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
 15.2537 +  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
 15.2538 +  mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
 15.2539 +  blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
 15.2540 +  if (mode == ATA_MODE_PIO32) blksize>>=2;
 15.2541 +  else blksize>>=1;
 15.2542 +
 15.2543 +  // sector will be 0 only on lba access. Convert to lba-chs
 15.2544 +  if (sector == 0) {
 15.2545 +    sector = (Bit16u) (lba & 0x000000ffL);
 15.2546 +    lba >>= 8;
 15.2547 +    cylinder = (Bit16u) (lba & 0x0000ffffL);
 15.2548 +    lba >>= 16;
 15.2549 +    head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
 15.2550 +    }
 15.2551 +
 15.2552 +  // Reset count of transferred data
 15.2553 +  write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
 15.2554 +  write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
 15.2555 +  current = 0;
 15.2556 +
 15.2557 +  status = inb(iobase1 + ATA_CB_STAT);
 15.2558 +  if (status & ATA_CB_STAT_BSY) return 1;
 15.2559 +
 15.2560 +  outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
 15.2561 +  outb(iobase1 + ATA_CB_FR, 0x00);
 15.2562 +  outb(iobase1 + ATA_CB_SC, count);
 15.2563 +  outb(iobase1 + ATA_CB_SN, sector);
 15.2564 +  outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
 15.2565 +  outb(iobase1 + ATA_CB_CH, cylinder >> 8);
 15.2566 +  outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
 15.2567 +  outb(iobase1 + ATA_CB_CMD, command);
 15.2568 +
 15.2569 +  while (1) {
 15.2570 +    status = inb(iobase1 + ATA_CB_STAT);
 15.2571 +    if ( !(status & ATA_CB_STAT_BSY) ) break;
 15.2572 +    }
 15.2573 +
 15.2574 +  if (status & ATA_CB_STAT_ERR) {
 15.2575 +    BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
 15.2576 +    return 2;
 15.2577 +    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
 15.2578 +    BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
 15.2579 +    return 3;
 15.2580 +  }
 15.2581 +
 15.2582 +  // FIXME : move seg/off translation here
 15.2583 +
 15.2584 +ASM_START
 15.2585 +        sti  ;; enable higher priority interrupts
 15.2586 +ASM_END
 15.2587 +
 15.2588 +  while (1) {
 15.2589 +
 15.2590 +ASM_START
 15.2591 +        push bp
 15.2592 +        mov  bp, sp
 15.2593 +        mov  di, _ata_cmd_data_in.offset + 2[bp]  
 15.2594 +        mov  ax, _ata_cmd_data_in.segment + 2[bp] 
 15.2595 +        mov  cx, _ata_cmd_data_in.blksize + 2[bp] 
 15.2596 +
 15.2597 +        ;; adjust if there will be an overrun. 2K max sector size
 15.2598 +        cmp   di, #0xf800 ;; 
 15.2599 +        jbe   ata_in_no_adjust
 15.2600 +
 15.2601 +ata_in_adjust:
 15.2602 +        sub   di, #0x0800 ;; sub 2 kbytes from offset
 15.2603 +        add   ax, #0x0080 ;; add 2 Kbytes to segment
 15.2604 +
 15.2605 +ata_in_no_adjust:
 15.2606 +        mov   es, ax      ;; segment in es
 15.2607 +
 15.2608 +        mov   dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
 15.2609 +
 15.2610 +        mov  ah, _ata_cmd_data_in.mode + 2[bp] 
 15.2611 +        cmp  ah, #ATA_MODE_PIO32
 15.2612 +        je   ata_in_32
 15.2613 +
 15.2614 +ata_in_16:
 15.2615 +        rep
 15.2616 +          insw ;; CX words transfered from port(DX) to ES:[DI]
 15.2617 +        jmp ata_in_done
 15.2618 +
 15.2619 +ata_in_32:
 15.2620 +        rep
 15.2621 +          insd ;; CX dwords transfered from port(DX) to ES:[DI]
 15.2622 +
 15.2623 +ata_in_done:
 15.2624 +        mov  _ata_cmd_data_in.offset + 2[bp], di
 15.2625 +        mov  _ata_cmd_data_in.segment + 2[bp], es
 15.2626 +        pop  bp
 15.2627 +ASM_END
 15.2628 +
 15.2629 +    current++;
 15.2630 +    write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
 15.2631 +    count--;
 15.2632 +    status = inb(iobase1 + ATA_CB_STAT);
 15.2633 +    if (count == 0) {
 15.2634 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
 15.2635 +          != ATA_CB_STAT_RDY ) {
 15.2636 +        BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
 15.2637 +        return 4;
 15.2638 +        }
 15.2639 +      break;
 15.2640 +      }
 15.2641 +    else {
 15.2642 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
 15.2643 +          != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
 15.2644 +        BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
 15.2645 +        return 5;
 15.2646 +      }
 15.2647 +      continue;
 15.2648 +    }
 15.2649 +  }
 15.2650 +  // Enable interrupts
 15.2651 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
 15.2652 +  return 0;
 15.2653 +}
 15.2654 +
 15.2655 +// ---------------------------------------------------------------------------
 15.2656 +// ATA/ATAPI driver : execute a data-out command
 15.2657 +// ---------------------------------------------------------------------------
 15.2658 +      // returns
 15.2659 +      // 0 : no error
 15.2660 +      // 1 : BUSY bit set
 15.2661 +      // 2 : read error
 15.2662 +      // 3 : expected DRQ=1
 15.2663 +      // 4 : no sectors left to read/verify
 15.2664 +      // 5 : more sectors to read/verify
 15.2665 +      // 6 : no sectors left to write
 15.2666 +      // 7 : more sectors to write
 15.2667 +Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
 15.2668 +Bit16u device, command, count, cylinder, head, sector, segment, offset;
 15.2669 +Bit32u lba;
 15.2670 +{
 15.2671 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.2672 +  Bit16u iobase1, iobase2, blksize;
 15.2673 +  Bit8u  channel, slave;
 15.2674 +  Bit8u  status, current, mode;
 15.2675 +
 15.2676 +  channel = device / 2;
 15.2677 +  slave   = device % 2;
 15.2678 +
 15.2679 +  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
 15.2680 +  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
 15.2681 +  mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
 15.2682 +  blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
 15.2683 +  if (mode == ATA_MODE_PIO32) blksize>>=2;
 15.2684 +  else blksize>>=1;
 15.2685 +
 15.2686 +  // sector will be 0 only on lba access. Convert to lba-chs
 15.2687 +  if (sector == 0) {
 15.2688 +    sector = (Bit16u) (lba & 0x000000ffL);
 15.2689 +    lba >>= 8;
 15.2690 +    cylinder = (Bit16u) (lba & 0x0000ffffL);
 15.2691 +    lba >>= 16;
 15.2692 +    head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
 15.2693 +    }
 15.2694 +
 15.2695 +  // Reset count of transferred data
 15.2696 +  write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
 15.2697 +  write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
 15.2698 +  current = 0;
 15.2699 +
 15.2700 +  status = inb(iobase1 + ATA_CB_STAT);
 15.2701 +  if (status & ATA_CB_STAT_BSY) return 1;
 15.2702 +
 15.2703 +  outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
 15.2704 +  outb(iobase1 + ATA_CB_FR, 0x00);
 15.2705 +  outb(iobase1 + ATA_CB_SC, count);
 15.2706 +  outb(iobase1 + ATA_CB_SN, sector);
 15.2707 +  outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
 15.2708 +  outb(iobase1 + ATA_CB_CH, cylinder >> 8);
 15.2709 +  outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
 15.2710 +  outb(iobase1 + ATA_CB_CMD, command);
 15.2711 +
 15.2712 +  while (1) {
 15.2713 +    status = inb(iobase1 + ATA_CB_STAT);
 15.2714 +    if ( !(status & ATA_CB_STAT_BSY) ) break;
 15.2715 +    }
 15.2716 +
 15.2717 +  if (status & ATA_CB_STAT_ERR) {
 15.2718 +    BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
 15.2719 +    return 2;
 15.2720 +    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
 15.2721 +    BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
 15.2722 +    return 3;
 15.2723 +    }
 15.2724 +
 15.2725 +  // FIXME : move seg/off translation here
 15.2726 +
 15.2727 +ASM_START
 15.2728 +        sti  ;; enable higher priority interrupts
 15.2729 +ASM_END
 15.2730 +
 15.2731 +  while (1) {
 15.2732 +
 15.2733 +ASM_START
 15.2734 +        push bp
 15.2735 +        mov  bp, sp
 15.2736 +        mov  si, _ata_cmd_data_out.offset + 2[bp]  
 15.2737 +        mov  ax, _ata_cmd_data_out.segment + 2[bp] 
 15.2738 +        mov  cx, _ata_cmd_data_out.blksize + 2[bp] 
 15.2739 +
 15.2740 +        ;; adjust if there will be an overrun. 2K max sector size
 15.2741 +        cmp   si, #0xf800 ;; 
 15.2742 +        jbe   ata_out_no_adjust
 15.2743 +
 15.2744 +ata_out_adjust:
 15.2745 +        sub   si, #0x0800 ;; sub 2 kbytes from offset
 15.2746 +        add   ax, #0x0080 ;; add 2 Kbytes to segment
 15.2747 +
 15.2748 +ata_out_no_adjust:
 15.2749 +        mov   es, ax      ;; segment in es
 15.2750 +
 15.2751 +        mov   dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
 15.2752 +
 15.2753 +        mov  ah, _ata_cmd_data_out.mode + 2[bp] 
 15.2754 +        cmp  ah, #ATA_MODE_PIO32
 15.2755 +        je   ata_out_32
 15.2756 +
 15.2757 +ata_out_16:
 15.2758 +        seg ES
 15.2759 +        rep
 15.2760 +          outsw ;; CX words transfered from port(DX) to ES:[SI]
 15.2761 +        jmp ata_out_done
 15.2762 +
 15.2763 +ata_out_32:
 15.2764 +        seg ES
 15.2765 +        rep
 15.2766 +          outsd ;; CX dwords transfered from port(DX) to ES:[SI]
 15.2767 +
 15.2768 +ata_out_done:
 15.2769 +        mov  _ata_cmd_data_out.offset + 2[bp], si
 15.2770 +        mov  _ata_cmd_data_out.segment + 2[bp], es
 15.2771 +        pop  bp
 15.2772 +ASM_END
 15.2773 +
 15.2774 +    current++;
 15.2775 +    write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
 15.2776 +    count--;
 15.2777 +    status = inb(iobase1 + ATA_CB_STAT);
 15.2778 +    if (count == 0) {
 15.2779 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
 15.2780 +          != ATA_CB_STAT_RDY ) {
 15.2781 +        BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
 15.2782 +        return 6;
 15.2783 +        }
 15.2784 +      break;
 15.2785 +      }
 15.2786 +    else {
 15.2787 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
 15.2788 +          != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
 15.2789 +        BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
 15.2790 +        return 7;
 15.2791 +      }
 15.2792 +      continue;
 15.2793 +    }
 15.2794 +  }
 15.2795 +  // Enable interrupts
 15.2796 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
 15.2797 +  return 0;
 15.2798 +}
 15.2799 +
 15.2800 +// ---------------------------------------------------------------------------
 15.2801 +// ATA/ATAPI driver : execute a packet command
 15.2802 +// ---------------------------------------------------------------------------
 15.2803 +      // returns
 15.2804 +      // 0 : no error
 15.2805 +      // 1 : error in parameters
 15.2806 +      // 2 : BUSY bit set
 15.2807 +      // 3 : error
 15.2808 +      // 4 : not ready
 15.2809 +Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
 15.2810 +Bit8u  cmdlen,inout;
 15.2811 +Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
 15.2812 +Bit16u header;
 15.2813 +Bit32u length;
 15.2814 +{
 15.2815 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.2816 +  Bit16u iobase1, iobase2;
 15.2817 +  Bit16u lcount, lbefore, lafter, count;
 15.2818 +  Bit8u  channel, slave;
 15.2819 +  Bit8u  status, mode, lmode;
 15.2820 +  Bit32u total, transfer;
 15.2821 +
 15.2822 +  channel = device / 2;
 15.2823 +  slave = device % 2;
 15.2824 +
 15.2825 +  // Data out is not supported yet
 15.2826 +  if (inout == ATA_DATA_OUT) {
 15.2827 +    BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
 15.2828 +    return 1;
 15.2829 +    }
 15.2830 +
 15.2831 +  // The header length must be even
 15.2832 +  if (header & 1) {
 15.2833 +    BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
 15.2834 +    return 1;
 15.2835 +    }
 15.2836 +
 15.2837 +  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
 15.2838 +  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
 15.2839 +  mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
 15.2840 +  transfer= 0L;
 15.2841 +
 15.2842 +  if (cmdlen < 12) cmdlen=12;
 15.2843 +  if (cmdlen > 12) cmdlen=16;
 15.2844 +  cmdlen>>=1;
 15.2845 +
 15.2846 +  // Reset count of transferred data
 15.2847 +  write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
 15.2848 +  write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
 15.2849 +
 15.2850 +  status = inb(iobase1 + ATA_CB_STAT);
 15.2851 +  if (status & ATA_CB_STAT_BSY) return 2;
 15.2852 +
 15.2853 +  outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
 15.2854 +  // outb(iobase1 + ATA_CB_FR, 0x00);
 15.2855 +  // outb(iobase1 + ATA_CB_SC, 0x00);
 15.2856 +  // outb(iobase1 + ATA_CB_SN, 0x00);
 15.2857 +  outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
 15.2858 +  outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
 15.2859 +  outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
 15.2860 +  outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
 15.2861 +
 15.2862 +  // Device should ok to receive command
 15.2863 +  while (1) {
 15.2864 +    status = inb(iobase1 + ATA_CB_STAT);
 15.2865 +    if ( !(status & ATA_CB_STAT_BSY) ) break;
 15.2866 +    }
 15.2867 +
 15.2868 +  if (status & ATA_CB_STAT_ERR) {
 15.2869 +    BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
 15.2870 +    return 3;
 15.2871 +    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
 15.2872 +    BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
 15.2873 +    return 4;
 15.2874 +    }
 15.2875 +
 15.2876 +  // Normalize address
 15.2877 +  cmdseg += (cmdoff / 16);
 15.2878 +  cmdoff %= 16;
 15.2879 +
 15.2880 +  // Send command to device
 15.2881 +ASM_START
 15.2882 +      sti  ;; enable higher priority interrupts
 15.2883 + 
 15.2884 +      push bp
 15.2885 +      mov  bp, sp
 15.2886 +    
 15.2887 +      mov  si, _ata_cmd_packet.cmdoff + 2[bp]  
 15.2888 +      mov  ax, _ata_cmd_packet.cmdseg + 2[bp] 
 15.2889 +      mov  cx, _ata_cmd_packet.cmdlen + 2[bp] 
 15.2890 +      mov  es, ax      ;; segment in es
 15.2891 +
 15.2892 +      mov  dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
 15.2893 +
 15.2894 +      seg ES
 15.2895 +      rep
 15.2896 +        outsw ;; CX words transfered from port(DX) to ES:[SI]
 15.2897 +
 15.2898 +      pop  bp
 15.2899 +ASM_END
 15.2900 +
 15.2901 +  if (inout == ATA_DATA_NO) {
 15.2902 +    status = inb(iobase1 + ATA_CB_STAT);
 15.2903 +    }
 15.2904 +  else {
 15.2905 +  while (1) {
 15.2906 +
 15.2907 +      status = inb(iobase1 + ATA_CB_STAT);
 15.2908 +
 15.2909 +      // Check if command completed
 15.2910 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
 15.2911 +
 15.2912 +      if (status & ATA_CB_STAT_ERR) {
 15.2913 +        BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
 15.2914 +        return 3;
 15.2915 +      }
 15.2916 +
 15.2917 +      // Device must be ready to send data
 15.2918 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
 15.2919 +            != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
 15.2920 +        BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
 15.2921 +        return 4;
 15.2922 +        }
 15.2923 +
 15.2924 +      // Normalize address
 15.2925 +      bufseg += (bufoff / 16);
 15.2926 +      bufoff %= 16;
 15.2927 +    
 15.2928 +      // Get the byte count
 15.2929 +      lcount =  ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
 15.2930 +
 15.2931 +      // adjust to read what we want
 15.2932 +      if(header>lcount) {
 15.2933 +         lbefore=lcount;
 15.2934 +         header-=lcount;
 15.2935 +         lcount=0;
 15.2936 +         }
 15.2937 +      else {
 15.2938 +        lbefore=header;
 15.2939 +        header=0;
 15.2940 +        lcount-=lbefore;
 15.2941 +        }
 15.2942 +
 15.2943 +      if(lcount>length) {
 15.2944 +        lafter=lcount-length;
 15.2945 +        lcount=length;
 15.2946 +        length=0;
 15.2947 +        }
 15.2948 +      else {
 15.2949 +        lafter=0;
 15.2950 +        length-=lcount;
 15.2951 +        }
 15.2952 +
 15.2953 +      // Save byte count
 15.2954 +      count = lcount;
 15.2955 +
 15.2956 +      BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
 15.2957 +      BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
 15.2958 +
 15.2959 +      // If counts not dividable by 4, use 16bits mode
 15.2960 +      lmode = mode;
 15.2961 +      if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
 15.2962 +      if (lcount  & 0x03) lmode=ATA_MODE_PIO16;
 15.2963 +      if (lafter  & 0x03) lmode=ATA_MODE_PIO16;
 15.2964 +
 15.2965 +      // adds an extra byte if count are odd. before is always even
 15.2966 +      if (lcount & 0x01) {
 15.2967 +        lcount+=1;
 15.2968 +        if ((lafter > 0) && (lafter & 0x01)) {
 15.2969 +          lafter-=1;
 15.2970 +          }
 15.2971 +        }
 15.2972 +
 15.2973 +      if (lmode == ATA_MODE_PIO32) {
 15.2974 +        lcount>>=2; lbefore>>=2; lafter>>=2;
 15.2975 +        }
 15.2976 +      else {
 15.2977 +        lcount>>=1; lbefore>>=1; lafter>>=1;
 15.2978 +        }
 15.2979 +
 15.2980 +       ;  // FIXME bcc bug
 15.2981 +
 15.2982 +ASM_START
 15.2983 +        push bp
 15.2984 +        mov  bp, sp
 15.2985 +
 15.2986 +        mov  dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
 15.2987 +
 15.2988 +        mov  cx, _ata_cmd_packet.lbefore + 2[bp] 
 15.2989 +        jcxz ata_packet_no_before
 15.2990 +
 15.2991 +        mov  ah, _ata_cmd_packet.lmode + 2[bp] 
 15.2992 +        cmp  ah, #ATA_MODE_PIO32
 15.2993 +        je   ata_packet_in_before_32
 15.2994 +
 15.2995 +ata_packet_in_before_16:
 15.2996 +        in   ax, dx
 15.2997 +        loop ata_packet_in_before_16
 15.2998 +        jmp  ata_packet_no_before
 15.2999 +
 15.3000 +ata_packet_in_before_32:
 15.3001 +        push eax
 15.3002 +ata_packet_in_before_32_loop:
 15.3003 +        in   eax, dx
 15.3004 +        loop ata_packet_in_before_32_loop
 15.3005 +        pop  eax
 15.3006 +
 15.3007 +ata_packet_no_before:
 15.3008 +        mov  cx, _ata_cmd_packet.lcount + 2[bp] 
 15.3009 +        jcxz ata_packet_after
 15.3010 +
 15.3011 +        mov  di, _ata_cmd_packet.bufoff + 2[bp]  
 15.3012 +        mov  ax, _ata_cmd_packet.bufseg + 2[bp] 
 15.3013 +        mov  es, ax
 15.3014 +
 15.3015 +        mov  ah, _ata_cmd_packet.lmode + 2[bp] 
 15.3016 +        cmp  ah, #ATA_MODE_PIO32
 15.3017 +        je   ata_packet_in_32
 15.3018 +
 15.3019 +ata_packet_in_16:
 15.3020 +        rep
 15.3021 +          insw ;; CX words transfered tp port(DX) to ES:[DI]
 15.3022 +        jmp ata_packet_after
 15.3023 +
 15.3024 +ata_packet_in_32:
 15.3025 +        rep
 15.3026 +          insd ;; CX dwords transfered to port(DX) to ES:[DI]
 15.3027 +
 15.3028 +ata_packet_after:
 15.3029 +        mov  cx, _ata_cmd_packet.lafter + 2[bp] 
 15.3030 +        jcxz ata_packet_done
 15.3031 +
 15.3032 +        mov  ah, _ata_cmd_packet.lmode + 2[bp] 
 15.3033 +        cmp  ah, #ATA_MODE_PIO32
 15.3034 +        je   ata_packet_in_after_32
 15.3035 +
 15.3036 +ata_packet_in_after_16:
 15.3037 +        in   ax, dx
 15.3038 +        loop ata_packet_in_after_16
 15.3039 +        jmp  ata_packet_done
 15.3040 +
 15.3041 +ata_packet_in_after_32:
 15.3042 +        push eax
 15.3043 +ata_packet_in_after_32_loop:
 15.3044 +        in   eax, dx
 15.3045 +        loop ata_packet_in_after_32_loop
 15.3046 +        pop  eax
 15.3047 +
 15.3048 +ata_packet_done:
 15.3049 +        pop  bp
 15.3050 +ASM_END
 15.3051 +
 15.3052 +      // Compute new buffer address
 15.3053 +      bufoff += count;
 15.3054 +
 15.3055 +      // Save transferred bytes count
 15.3056 +      transfer += count;
 15.3057 +      write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
 15.3058 +      }
 15.3059 +    }
 15.3060 +
 15.3061 +  // Final check, device must be ready
 15.3062 +  if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
 15.3063 +         != ATA_CB_STAT_RDY ) {
 15.3064 +    BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
 15.3065 +    return 4;
 15.3066 +    }
 15.3067 +
 15.3068 +  // Enable interrupts
 15.3069 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
 15.3070 +  return 0;
 15.3071 +}
 15.3072 +
 15.3073 +// ---------------------------------------------------------------------------
 15.3074 +// End of ATA/ATAPI Driver
 15.3075 +// ---------------------------------------------------------------------------
 15.3076 +
 15.3077 +// ---------------------------------------------------------------------------
 15.3078 +// Start of ATA/ATAPI generic functions
 15.3079 +// ---------------------------------------------------------------------------
 15.3080 +
 15.3081 +  Bit16u 
 15.3082 +atapi_get_sense(device)
 15.3083 +  Bit16u device;
 15.3084 +{
 15.3085 +  Bit8u  atacmd[12];
 15.3086 +  Bit8u  buffer[16];
 15.3087 +  Bit8u i;
 15.3088 +
 15.3089 +  memsetb(get_SS(),atacmd,0,12);
 15.3090 +
 15.3091 +  // Request SENSE 
 15.3092 +  atacmd[0]=0x03;    
 15.3093 +  atacmd[4]=0x20;    
 15.3094 +  if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
 15.3095 +    return 0x0002;
 15.3096 +
 15.3097 +  if ((buffer[0] & 0x7e) == 0x70) {
 15.3098 +    return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
 15.3099 +    }
 15.3100 +
 15.3101 +  return 0;
 15.3102 +}
 15.3103 +
 15.3104 +  Bit16u 
 15.3105 +atapi_is_ready(device)
 15.3106 +  Bit16u device;
 15.3107 +{
 15.3108 +  Bit8u  atacmd[12];
 15.3109 +  Bit8u  buffer[];
 15.3110 +
 15.3111 +  memsetb(get_SS(),atacmd,0,12);
 15.3112 + 
 15.3113 +  // Test Unit Ready
 15.3114 +  if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
 15.3115 +    return 0x000f;
 15.3116 +
 15.3117 +  if (atapi_get_sense(device) !=0 ) {
 15.3118 +    memsetb(get_SS(),atacmd,0,12);
 15.3119 +
 15.3120 +    // try to send Test Unit Ready again
 15.3121 +    if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
 15.3122 +      return 0x000f;
 15.3123 +
 15.3124 +    return atapi_get_sense(device);
 15.3125 +    }
 15.3126 +  return 0;
 15.3127 +}
 15.3128 +
 15.3129 +  Bit16u 
 15.3130 +atapi_is_cdrom(device)
 15.3131 +  Bit8u device;
 15.3132 +{
 15.3133 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.3134 +
 15.3135 +  if (device >= BX_MAX_ATA_DEVICES)
 15.3136 +    return 0;
 15.3137 +
 15.3138 +  if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
 15.3139 +    return 0;
 15.3140 +
 15.3141 +  if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
 15.3142 +    return 0;
 15.3143 +
 15.3144 +  return 1;
 15.3145 +}
 15.3146 +
 15.3147 +// ---------------------------------------------------------------------------
 15.3148 +// End of ATA/ATAPI generic functions
 15.3149 +// ---------------------------------------------------------------------------
 15.3150 +
 15.3151 +#endif // BX_USE_ATADRV
 15.3152 +
 15.3153 +#if BX_ELTORITO_BOOT
 15.3154 +
 15.3155 +// ---------------------------------------------------------------------------
 15.3156 +// Start of El-Torito boot functions
 15.3157 +// ---------------------------------------------------------------------------
 15.3158 +
 15.3159 +  void
 15.3160 +cdemu_init()
 15.3161 +{
 15.3162 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.3163 +
 15.3164 +  // the only important data is this one for now
 15.3165 +  write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
 15.3166 +}
 15.3167 +
 15.3168 +  Bit8u
 15.3169 +cdemu_isactive()
 15.3170 +{
 15.3171 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.3172 +
 15.3173 +  return(read_byte(ebda_seg,&EbdaData->cdemu.active));
 15.3174 +}
 15.3175 +
 15.3176 +  Bit8u
 15.3177 +cdemu_emulated_drive()
 15.3178 +{
 15.3179 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.3180 +
 15.3181 +  return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
 15.3182 +}
 15.3183 +
 15.3184 +static char isotag[6]="CD001";
 15.3185 +static char eltorito[24]="EL TORITO SPECIFICATION";
 15.3186 +//
 15.3187 +// Returns ah: emulated drive, al: error code
 15.3188 +//
 15.3189 +  Bit16u 
 15.3190 +cdrom_boot()
 15.3191 +{
 15.3192 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.3193 +  Bit8u  atacmd[12], buffer[2048];
 15.3194 +  Bit32u lba;
 15.3195 +  Bit16u boot_segment, nbsectors, i, error;
 15.3196 +  Bit8u  device;
 15.3197 +
 15.3198 +  // Find out the first cdrom
 15.3199 +  for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
 15.3200 +    if (atapi_is_cdrom(device)) break;
 15.3201 +    }
 15.3202 +  
 15.3203 +  // if not found
 15.3204 +  if(device >= BX_MAX_ATA_DEVICES) return 2;
 15.3205 +
 15.3206 +  // Read the Boot Record Volume Descriptor
 15.3207 +  memsetb(get_SS(),atacmd,0,12);
 15.3208 +  atacmd[0]=0x28;                      // READ command
 15.3209 +  atacmd[7]=(0x01 & 0xff00) >> 8;      // Sectors
 15.3210 +  atacmd[8]=(0x01 & 0x00ff);           // Sectors
 15.3211 +  atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
 15.3212 +  atacmd[3]=(0x11 & 0x00ff0000) >> 16;
 15.3213 +  atacmd[4]=(0x11 & 0x0000ff00) >> 8;
 15.3214 +  atacmd[5]=(0x11 & 0x000000ff);
 15.3215 +  if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
 15.3216 +    return 3;
 15.3217 +
 15.3218 +  // Validity checks
 15.3219 +  if(buffer[0]!=0)return 4;
 15.3220 +  for(i=0;i<5;i++){
 15.3221 +    if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
 15.3222 +   }
 15.3223 +  for(i=0;i<23;i++)
 15.3224 +    if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
 15.3225 +  
 15.3226 +  // ok, now we calculate the Boot catalog address
 15.3227 +  lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
 15.3228 +
 15.3229 +  // And we read the Boot Catalog
 15.3230 +  memsetb(get_SS(),atacmd,0,12);
 15.3231 +  atacmd[0]=0x28;                      // READ command
 15.3232 +  atacmd[7]=(0x01 & 0xff00) >> 8;      // Sectors
 15.3233 +  atacmd[8]=(0x01 & 0x00ff);           // Sectors
 15.3234 +  atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
 15.3235 +  atacmd[3]=(lba & 0x00ff0000) >> 16;
 15.3236 +  atacmd[4]=(lba & 0x0000ff00) >> 8;
 15.3237 +  atacmd[5]=(lba & 0x000000ff);
 15.3238 +  if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
 15.3239 +    return 7;
 15.3240 + 
 15.3241 +  // Validation entry
 15.3242 +  if(buffer[0x00]!=0x01)return 8;   // Header
 15.3243 +  if(buffer[0x01]!=0x00)return 9;   // Platform
 15.3244 +  if(buffer[0x1E]!=0x55)return 10;  // key 1
 15.3245 +  if(buffer[0x1F]!=0xAA)return 10;  // key 2
 15.3246 +
 15.3247 +  // Initial/Default Entry
 15.3248 +  if(buffer[0x20]!=0x88)return 11; // Bootable
 15.3249 +
 15.3250 +  write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
 15.3251 +  if(buffer[0x21]==0){
 15.3252 +    // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0. 
 15.3253 +    // Win2000 cd boot needs to know it booted from cd
 15.3254 +    write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
 15.3255 +    } 
 15.3256 +  else if(buffer[0x21]<4)
 15.3257 +    write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
 15.3258 +  else
 15.3259 +    write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
 15.3260 +
 15.3261 +  write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
 15.3262 +  write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
 15.3263 +
 15.3264 +  boot_segment=buffer[0x23]*0x100+buffer[0x22];
 15.3265 +  if(boot_segment==0x0000)boot_segment=0x07C0;
 15.3266 +
 15.3267 +  write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
 15.3268 +  write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
 15.3269 +  
 15.3270 +  nbsectors=buffer[0x27]*0x100+buffer[0x26];
 15.3271 +  write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
 15.3272 +
 15.3273 +  lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
 15.3274 +  write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
 15.3275 +
 15.3276 +  // And we read the image in memory
 15.3277 +  memsetb(get_SS(),atacmd,0,12);
 15.3278 +  atacmd[0]=0x28;                      // READ command
 15.3279 +  atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8;      // Sectors
 15.3280 +  atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff);           // Sectors
 15.3281 +  atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
 15.3282 +  atacmd[3]=(lba & 0x00ff0000) >> 16;
 15.3283 +  atacmd[4]=(lba & 0x0000ff00) >> 8;
 15.3284 +  atacmd[5]=(lba & 0x000000ff);
 15.3285 +  if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
 15.3286 +    return 12;
 15.3287 +
 15.3288 +  // Remember the media type
 15.3289 +  switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
 15.3290 +    case 0x01:  // 1.2M floppy
 15.3291 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
 15.3292 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
 15.3293 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
 15.3294 +      break;
 15.3295 +    case 0x02:  // 1.44M floppy
 15.3296 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
 15.3297 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
 15.3298 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
 15.3299 +      break;
 15.3300 +    case 0x03:  // 2.88M floppy
 15.3301 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
 15.3302 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
 15.3303 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
 15.3304 +      break;
 15.3305 +    case 0x04:  // Harddrive
 15.3306 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
 15.3307 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
 15.3308 +	      (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
 15.3309 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
 15.3310 +      break;
 15.3311 +   }
 15.3312 +
 15.3313 +  if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
 15.3314 +    // Increase bios installed hardware number of devices
 15.3315 +    if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
 15.3316 +      write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
 15.3317 +    else
 15.3318 +      write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
 15.3319 +   }
 15.3320 +
 15.3321 +  
 15.3322 +  // everything is ok, so from now on, the emulation is active
 15.3323 +  if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
 15.3324 +    write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
 15.3325 +
 15.3326 +  // return the boot drive + no error
 15.3327 +  return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
 15.3328 +}
 15.3329 +
 15.3330 +// ---------------------------------------------------------------------------
 15.3331 +// End of El-Torito boot functions
 15.3332 +// ---------------------------------------------------------------------------
 15.3333 +#endif // BX_ELTORITO_BOOT
 15.3334 +
 15.3335 +  void
 15.3336 +int14_function(regs, ds, iret_addr)
 15.3337 +  pusha_regs_t regs; // regs pushed from PUSHA instruction
 15.3338 +  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
 15.3339 +  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
 15.3340 +{
 15.3341 +  Bit16u addr,timer,val16;
 15.3342 +  Bit8u timeout;
 15.3343 +
 15.3344 +  ASM_START
 15.3345 +  sti
 15.3346 +  ASM_END
 15.3347 +
 15.3348 +  addr = read_word(0x0040, (regs.u.r16.dx << 1));
 15.3349 +  timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
 15.3350 +  if ((regs.u.r16.dx < 4) && (addr > 0)) {
 15.3351 +    switch (regs.u.r8.ah) {
 15.3352 +      case 0:
 15.3353 +        outb(addr+3, inb(addr+3) | 0x80);
 15.3354 +        if (regs.u.r8.al & 0xE0 == 0) {
 15.3355 +          outb(addr, 0x17);
 15.3356 +          outb(addr+1, 0x04);
 15.3357 +        } else {
 15.3358 +          val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
 15.3359 +          outb(addr, val16 & 0xFF);
 15.3360 +          outb(addr+1, val16 >> 8);
 15.3361 +        }
 15.3362 +        outb(addr+3, regs.u.r8.al & 0x1F);
 15.3363 +        regs.u.r8.ah = inb(addr+5);
 15.3364 +        regs.u.r8.al = inb(addr+6);
 15.3365 +        ClearCF(iret_addr.flags);
 15.3366 +        break;
 15.3367 +      case 1:
 15.3368 +        timer = read_word(0x0040, 0x006C);
 15.3369 +        while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
 15.3370 +          val16 = read_word(0x0040, 0x006C);
 15.3371 +          if (val16 != timer) {
 15.3372 +            timer = val16;
 15.3373 +            timeout--;
 15.3374 +            }
 15.3375 +          }
 15.3376 +        if (timeout) outb(addr, regs.u.r8.al);
 15.3377 +        regs.u.r8.ah = inb(addr+5);
 15.3378 +        if (!timeout) regs.u.r8.ah |= 0x80;
 15.3379 +        ClearCF(iret_addr.flags);
 15.3380 +        break;
 15.3381 +      case 2:
 15.3382 +        timer = read_word(0x0040, 0x006C);
 15.3383 +        while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
 15.3384 +          val16 = read_word(0x0040, 0x006C);
 15.3385 +          if (val16 != timer) {
 15.3386 +            timer = val16;
 15.3387 +            timeout--;
 15.3388 +            }
 15.3389 +          }
 15.3390 +        if (timeout) {
 15.3391 +          regs.u.r8.ah = 0;
 15.3392 +          regs.u.r8.al = inb(addr);
 15.3393 +        } else {
 15.3394 +          regs.u.r8.ah = inb(addr+5);
 15.3395 +          }
 15.3396 +        ClearCF(iret_addr.flags);
 15.3397 +        break;
 15.3398 +      case 3:
 15.3399 +        regs.u.r8.ah = inb(addr+5);
 15.3400 +        regs.u.r8.al = inb(addr+6);
 15.3401 +        ClearCF(iret_addr.flags);
 15.3402 +        break;
 15.3403 +      default:
 15.3404 +        SetCF(iret_addr.flags); // Unsupported
 15.3405 +      }
 15.3406 +  } else {
 15.3407 +    SetCF(iret_addr.flags); // Unsupported
 15.3408 +    }
 15.3409 +}
 15.3410 +
 15.3411 +  void
 15.3412 +int15_function(regs, ES, DS, FLAGS)
 15.3413 +  pusha_regs_t regs; // REGS pushed via pusha
 15.3414 +  Bit16u ES, DS, FLAGS;
 15.3415 +{
 15.3416 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.3417 +  bx_bool prev_a20_enable;
 15.3418 +  Bit16u  base15_00;
 15.3419 +  Bit8u   base23_16;
 15.3420 +  Bit16u  ss;
 15.3421 +  Bit16u  CX,DX;
 15.3422 +
 15.3423 +  Bit16u bRegister;
 15.3424 +  Bit8u irqDisable;
 15.3425 +
 15.3426 +BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
 15.3427 +
 15.3428 +  switch (regs.u.r8.ah) {
 15.3429 +    case 0x24: /* A20 Control */
 15.3430 +      switch (regs.u.r8.al) {
 15.3431 +        case 0x00:
 15.3432 +          set_enable_a20(0);
 15.3433 +          CLEAR_CF();
 15.3434 +          regs.u.r8.ah = 0;
 15.3435 +          break;
 15.3436 +        case 0x01:
 15.3437 +          set_enable_a20(1);
 15.3438 +          CLEAR_CF();
 15.3439 +          regs.u.r8.ah = 0;
 15.3440 +          break;
 15.3441 +        case 0x02:
 15.3442 +          regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
 15.3443 +          CLEAR_CF();
 15.3444 +          regs.u.r8.ah = 0;
 15.3445 +          break;
 15.3446 +        case 0x03:
 15.3447 +          CLEAR_CF();
 15.3448 +          regs.u.r8.ah = 0;
 15.3449 +          regs.u.r16.bx = 3;
 15.3450 +          break;
 15.3451 +        default:
 15.3452 +          BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
 15.3453 +          SET_CF();
 15.3454 +          regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3455 +      }
 15.3456 +      break;
 15.3457 +
 15.3458 +    case 0x41:
 15.3459 +      SET_CF();
 15.3460 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3461 +      break;
 15.3462 +
 15.3463 +    case 0x4f:
 15.3464 +      /* keyboard intercept */
 15.3465 +#if BX_CPU < 2
 15.3466 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3467 +#else
 15.3468 +      // nop
 15.3469 +#endif
 15.3470 +      SET_CF();
 15.3471 +      break;
 15.3472 +
 15.3473 +    case 0x52:    // removable media eject
 15.3474 +      CLEAR_CF();
 15.3475 +      regs.u.r8.ah = 0;  // "ok ejection may proceed"
 15.3476 +      break;
 15.3477 +
 15.3478 +    case 0x83: {
 15.3479 +      if( regs.u.r8.al == 0 ) {
 15.3480 +        // Set Interval requested.
 15.3481 +        if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
 15.3482 +          // Interval not already set.
 15.3483 +          write_byte( 0x40, 0xA0, 1 );  // Set status byte.
 15.3484 +          write_word( 0x40, 0x98, ES ); // Byte location, segment
 15.3485 +          write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
 15.3486 +          write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
 15.3487 +          write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
 15.3488 +          CLEAR_CF( );
 15.3489 +          irqDisable = inb( 0xA1 );
 15.3490 +          outb( 0xA1, irqDisable & 0xFE );
 15.3491 +          bRegister = inb_cmos( 0xB );  // Unmask IRQ8 so INT70 will get through.
 15.3492 +          outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
 15.3493 +        } else {
 15.3494 +          // Interval already set.
 15.3495 +          BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
 15.3496 +          SET_CF();
 15.3497 +          regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3498 +        }
 15.3499 +      } else if( regs.u.r8.al == 1 ) {
 15.3500 +        // Clear Interval requested
 15.3501 +        write_byte( 0x40, 0xA0, 0 );  // Clear status byte
 15.3502 +        CLEAR_CF( );
 15.3503 +        bRegister = inb_cmos( 0xB );
 15.3504 +        outb_cmos( 0xB, bRegister & ~0x40 );  // Turn off the Periodic Interrupt timer
 15.3505 +      } else {
 15.3506 +        BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
 15.3507 +        SET_CF();
 15.3508 +        regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3509 +        regs.u.r8.al--;
 15.3510 +      }
 15.3511 +
 15.3512 +      break;
 15.3513 +    }
 15.3514 +
 15.3515 +    case 0x87:
 15.3516 +#if BX_CPU < 3
 15.3517 +#  error "Int15 function 87h not supported on < 80386"
 15.3518 +#endif
 15.3519 +      // +++ should probably have descriptor checks
 15.3520 +      // +++ should have exception handlers
 15.3521 +
 15.3522 + // turn off interrupts
 15.3523 +ASM_START
 15.3524 +  cli
 15.3525 +ASM_END
 15.3526 +
 15.3527 +      prev_a20_enable = set_enable_a20(1); // enable A20 line
 15.3528 +
 15.3529 +      // 128K max of transfer on 386+ ???
 15.3530 +      // source == destination ???
 15.3531 +
 15.3532 +      // ES:SI points to descriptor table
 15.3533 +      // offset   use     initially  comments
 15.3534 +      // ==============================================
 15.3535 +      // 00..07   Unused  zeros      Null descriptor
 15.3536 +      // 08..0f   GDT     zeros      filled in by BIOS
 15.3537 +      // 10..17   source  ssssssss   source of data
 15.3538 +      // 18..1f   dest    dddddddd   destination of data
 15.3539 +      // 20..27   CS      zeros      filled in by BIOS
 15.3540 +      // 28..2f   SS      zeros      filled in by BIOS
 15.3541 +
 15.3542 +      //es:si
 15.3543 +      //eeee0
 15.3544 +      //0ssss
 15.3545 +      //-----
 15.3546 +
 15.3547 +// check for access rights of source & dest here
 15.3548 +
 15.3549 +      // Initialize GDT descriptor
 15.3550 +      base15_00 = (ES << 4) + regs.u.r16.si;
 15.3551 +      base23_16 = ES >> 12;
 15.3552 +      if (base15_00 < (ES<<4))
 15.3553 +        base23_16++;
 15.3554 +      write_word(ES, regs.u.r16.si+0x08+0, 47);       // limit 15:00 = 6 * 8bytes/descriptor
 15.3555 +      write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
 15.3556 +      write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
 15.3557 +      write_byte(ES, regs.u.r16.si+0x08+5, 0x93);     // access
 15.3558 +      write_word(ES, regs.u.r16.si+0x08+6, 0x0000);   // base 31:24/reserved/limit 19:16
 15.3559 +
 15.3560 +      // Initialize CS descriptor
 15.3561 +      write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
 15.3562 +      write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
 15.3563 +      write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
 15.3564 +      write_byte(ES, regs.u.r16.si+0x20+5, 0x9b);  // access
 15.3565 +      write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
 15.3566 +
 15.3567 +      // Initialize SS descriptor
 15.3568 +      ss = get_SS();
 15.3569 +      base15_00 = ss << 4;
 15.3570 +      base23_16 = ss >> 12;
 15.3571 +      write_word(ES, regs.u.r16.si+0x28+0, 0xffff);   // limit 15:00 = normal 64K limit
 15.3572 +      write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
 15.3573 +      write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
 15.3574 +      write_byte(ES, regs.u.r16.si+0x28+5, 0x93);     // access
 15.3575 +      write_word(ES, regs.u.r16.si+0x28+6, 0x0000);   // base 31:24/reserved/limit 19:16
 15.3576 +
 15.3577 +      CX = regs.u.r16.cx;
 15.3578 +ASM_START
 15.3579 +      // Compile generates locals offset info relative to SP.
 15.3580 +      // Get CX (word count) from stack.
 15.3581 +      mov  bx, sp
 15.3582 +      SEG SS
 15.3583 +        mov  cx, _int15_function.CX [bx]
 15.3584 +
 15.3585 +      // since we need to set SS:SP, save them to the BDA
 15.3586 +      // for future restore
 15.3587 +      push eax
 15.3588 +      xor eax, eax
 15.3589 +      mov ds, ax
 15.3590 +      mov 0x0469, ss
 15.3591 +      mov 0x0467, sp
 15.3592 +
 15.3593 +      SEG ES
 15.3594 +        lgdt [si + 0x08]
 15.3595 +      SEG CS
 15.3596 +        lidt [pmode_IDT_info]
 15.3597 +      ;;  perhaps do something with IDT here
 15.3598 +
 15.3599 +      ;; set PE bit in CR0
 15.3600 +      mov  eax, cr0
 15.3601 +      or   al, #0x01
 15.3602 +      mov  cr0, eax
 15.3603 +      ;; far jump to flush CPU queue after transition to protected mode
 15.3604 +      JMP_AP(0x0020, protected_mode)
 15.3605 +
 15.3606 +protected_mode:
 15.3607 +      ;; GDT points to valid descriptor table, now load SS, DS, ES
 15.3608 +      mov  ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
 15.3609 +      mov  ss, ax
 15.3610 +      mov  ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
 15.3611 +      mov  ds, ax
 15.3612 +      mov  ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
 15.3613 +      mov  es, ax
 15.3614 +      xor  si, si
 15.3615 +      xor  di, di
 15.3616 +      cld
 15.3617 +      rep
 15.3618 +        movsw  ;; move CX words from DS:SI to ES:DI
 15.3619 +
 15.3620 +      ;; make sure DS and ES limits are 64KB
 15.3621 +      mov ax, #0x28
 15.3622 +      mov ds, ax
 15.3623 +      mov es, ax
 15.3624 +
 15.3625 +      ;; reset PG bit in CR0 ???
 15.3626 +      mov  eax, cr0
 15.3627 +      and  al, #0xFE
 15.3628 +      mov  cr0, eax
 15.3629 +
 15.3630 +      ;; far jump to flush CPU queue after transition to real mode
 15.3631 +      JMP_AP(0xf000, real_mode)
 15.3632 +
 15.3633 +real_mode:
 15.3634 +      ;; restore IDT to normal real-mode defaults
 15.3635 +      SEG CS
 15.3636 +        lidt [rmode_IDT_info]
 15.3637 +
 15.3638 +      // restore SS:SP from the BDA
 15.3639 +      xor ax, ax
 15.3640 +      mov ds, ax
 15.3641 +      mov ss, 0x0469
 15.3642 +      mov sp, 0x0467
 15.3643 +      pop eax
 15.3644 +ASM_END
 15.3645 +
 15.3646 +      set_enable_a20(prev_a20_enable);
 15.3647 +
 15.3648 + // turn back on interrupts
 15.3649 +ASM_START
 15.3650 +  sti
 15.3651 +ASM_END
 15.3652 +
 15.3653 +      regs.u.r8.ah = 0;
 15.3654 +      CLEAR_CF();
 15.3655 +      break;
 15.3656 +
 15.3657 +
 15.3658 +    case 0x88:
 15.3659 +      // Get the amount of extended memory (above 1M)
 15.3660 +#if BX_CPU < 2
 15.3661 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3662 +      SET_CF();
 15.3663 +#else
 15.3664 +      regs.u.r8.al = inb_cmos(0x30);
 15.3665 +      regs.u.r8.ah = inb_cmos(0x31);
 15.3666 +
 15.3667 +      // limit to 15M
 15.3668 +      if(regs.u.r16.ax > 0x3c00)
 15.3669 +        regs.u.r16.ax = 0x3c00;
 15.3670 +
 15.3671 +      CLEAR_CF();
 15.3672 +#endif
 15.3673 +      break;
 15.3674 +
 15.3675 +    case 0x90:
 15.3676 +      /* Device busy interrupt.  Called by Int 16h when no key available */
 15.3677 +      break;
 15.3678 +
 15.3679 +    case 0x91:
 15.3680 +      /* Interrupt complete.  Called by Int 16h when key becomes available */
 15.3681 +      break;
 15.3682 +
 15.3683 +    case 0xbf:
 15.3684 +      BX_INFO("*** int 15h function AH=bf not yet supported!\n");
 15.3685 +      SET_CF();
 15.3686 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3687 +      break;
 15.3688 +
 15.3689 +    case 0xC0:
 15.3690 +#if 0
 15.3691 +      SET_CF();
 15.3692 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3693 +      break;
 15.3694 +#endif
 15.3695 +      CLEAR_CF();
 15.3696 +      regs.u.r8.ah = 0;
 15.3697 +      regs.u.r16.bx =  BIOS_CONFIG_TABLE;
 15.3698 +      ES = 0xF000;
 15.3699 +      break;
 15.3700 +
 15.3701 +    case 0xc1:
 15.3702 +      ES = ebda_seg;
 15.3703 +      CLEAR_CF();
 15.3704 +      break;
 15.3705 +
 15.3706 +    case 0xd8:
 15.3707 +      bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
 15.3708 +      SET_CF();
 15.3709 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3710 +      break;
 15.3711 +
 15.3712 +    default:
 15.3713 +      BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
 15.3714 +        (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
 15.3715 +      SET_CF();
 15.3716 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3717 +      break;
 15.3718 +    }
 15.3719 +}
 15.3720 +
 15.3721 +#if BX_USE_PS2_MOUSE
 15.3722 +  void
 15.3723 +int15_function_mouse(regs, ES, DS, FLAGS)
 15.3724 +  pusha_regs_t regs; // REGS pushed via pusha
 15.3725 +  Bit16u ES, DS, FLAGS;
 15.3726 +{
 15.3727 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.3728 +  Bit8u  mouse_flags_1, mouse_flags_2;
 15.3729 +  Bit16u mouse_driver_seg;
 15.3730 +  Bit16u mouse_driver_offset;
 15.3731 +  Bit8u  comm_byte, prev_command_byte;
 15.3732 +  Bit8u  ret, mouse_data1, mouse_data2, mouse_data3;
 15.3733 +
 15.3734 +BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
 15.3735 +
 15.3736 +  switch (regs.u.r8.ah) {
 15.3737 +    case 0xC2:
 15.3738 +      // Return Codes status in AH
 15.3739 +      // =========================
 15.3740 +      // 00: success
 15.3741 +      // 01: invalid subfunction (AL > 7)
 15.3742 +      // 02: invalid input value (out of allowable range)
 15.3743 +      // 03: interface error
 15.3744 +      // 04: resend command received from mouse controller,
 15.3745 +      //     device driver should attempt command again
 15.3746 +      // 05: cannot enable mouse, since no far call has been installed
 15.3747 +      // 80/86: mouse service not implemented
 15.3748 +
 15.3749 +      switch (regs.u.r8.al) {
 15.3750 +        case 0: // Disable/Enable Mouse
 15.3751 +BX_DEBUG_INT15("case 0:\n");
 15.3752 +          switch (regs.u.r8.bh) {
 15.3753 +            case 0: // Disable Mouse
 15.3754 +BX_DEBUG_INT15("case 0: disable mouse\n");
 15.3755 +              inhibit_mouse_int_and_events(); // disable IRQ12 and packets
 15.3756 +              ret = send_to_mouse_ctrl(0xF5); // disable mouse command
 15.3757 +              if (ret == 0) {
 15.3758 +                ret = get_mouse_data(&mouse_data1);
 15.3759 +                if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
 15.3760 +                  CLEAR_CF();
 15.3761 +                  regs.u.r8.ah = 0;
 15.3762 +                  return;
 15.3763 +                  }
 15.3764 +                }
 15.3765 +
 15.3766 +              // error
 15.3767 +              SET_CF();
 15.3768 +              regs.u.r8.ah = ret;
 15.3769 +              return;
 15.3770 +              break;
 15.3771 +
 15.3772 +            case 1: // Enable Mouse
 15.3773 +BX_DEBUG_INT15("case 1: enable mouse\n");
 15.3774 +              mouse_flags_2 = read_byte(ebda_seg, 0x0027);
 15.3775 +              if ( (mouse_flags_2 & 0x80) == 0 ) {
 15.3776 +                BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
 15.3777 +                SET_CF();  // error
 15.3778 +                regs.u.r8.ah = 5; // no far call installed
 15.3779 +                return;
 15.3780 +                }
 15.3781 +              inhibit_mouse_int_and_events(); // disable IRQ12 and packets
 15.3782 +              ret = send_to_mouse_ctrl(0xF4); // enable mouse command
 15.3783 +              if (ret == 0) {
 15.3784 +                ret = get_mouse_data(&mouse_data1);
 15.3785 +                if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
 15.3786 +                  enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
 15.3787 +                  CLEAR_CF();
 15.3788 +                  regs.u.r8.ah = 0;
 15.3789 +                  return;
 15.3790 +                  }
 15.3791 +                }
 15.3792 +              SET_CF();
 15.3793 +              regs.u.r8.ah = ret;
 15.3794 +              return;
 15.3795 +
 15.3796 +            default: // invalid subfunction
 15.3797 +              BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
 15.3798 +              SET_CF();  // error
 15.3799 +              regs.u.r8.ah = 1; // invalid subfunction
 15.3800 +              return;
 15.3801 +            }
 15.3802 +          break;
 15.3803 +
 15.3804 +        case 1: // Reset Mouse
 15.3805 +        case 5: // Initialize Mouse
 15.3806 +BX_DEBUG_INT15("case 1 or 5:\n");
 15.3807 +          if (regs.u.r8.al == 5) {
 15.3808 +            if (regs.u.r8.bh != 3) {
 15.3809 +              SET_CF();
 15.3810 +              regs.u.r8.ah = 0x02; // invalid input
 15.3811 +              return;
 15.3812 +            }
 15.3813 +            mouse_flags_2 = read_byte(ebda_seg, 0x0027);
 15.3814 +            mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
 15.3815 +            mouse_flags_1 = 0x00;
 15.3816 +            write_byte(ebda_seg, 0x0026, mouse_flags_1);
 15.3817 +            write_byte(ebda_seg, 0x0027, mouse_flags_2);
 15.3818 +          }
 15.3819 +
 15.3820 +          inhibit_mouse_int_and_events(); // disable IRQ12 and packets
 15.3821 +          ret = send_to_mouse_ctrl(0xFF); // reset mouse command
 15.3822 +          if (ret == 0) {
 15.3823 +            ret = get_mouse_data(&mouse_data3);
 15.3824 +            // if no mouse attached, it will return RESEND
 15.3825 +            if (mouse_data3 == 0xfe) {
 15.3826 +              SET_CF();
 15.3827 +              return;
 15.3828 +            }
 15.3829 +            if (mouse_data3 != 0xfa)
 15.3830 +              BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
 15.3831 +            if ( ret == 0 ) {
 15.3832 +              ret = get_mouse_data(&mouse_data1);
 15.3833 +              if ( ret == 0 ) {
 15.3834 +                ret = get_mouse_data(&mouse_data2);
 15.3835 +                if ( ret == 0 ) {
 15.3836 +                  // turn IRQ12 and packet generation on
 15.3837 +                  enable_mouse_int_and_events();
 15.3838 +                  CLEAR_CF();
 15.3839 +                  regs.u.r8.ah = 0;
 15.3840 +                  regs.u.r8.bl = mouse_data1;
 15.3841 +                  regs.u.r8.bh = mouse_data2;
 15.3842 +                  return;
 15.3843 +                  }
 15.3844 +                }
 15.3845 +              }
 15.3846 +            }
 15.3847 +
 15.3848 +          // error
 15.3849 +          SET_CF();
 15.3850 +          regs.u.r8.ah = ret;
 15.3851 +          return;
 15.3852 +
 15.3853 +        case 2: // Set Sample Rate
 15.3854 +BX_DEBUG_INT15("case 2:\n");
 15.3855 +          switch (regs.u.r8.bh) {
 15.3856 +            case 0: mouse_data1 = 10; break; //  10 reports/sec
 15.3857 +            case 1: mouse_data1 = 20; break; //  20 reports/sec
 15.3858 +            case 2: mouse_data1 = 40; break; //  40 reports/sec
 15.3859 +            case 3: mouse_data1 = 60; break; //  60 reports/sec
 15.3860 +            case 4: mouse_data1 = 80; break; //  80 reports/sec
 15.3861 +            case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
 15.3862 +            case 6: mouse_data1 = 200; break; // 200 reports/sec
 15.3863 +            default: mouse_data1 = 0;
 15.3864 +          }
 15.3865 +          if (mouse_data1 > 0) {
 15.3866 +            ret = send_to_mouse_ctrl(0xF3); // set sample rate command
 15.3867 +            if (ret == 0) {
 15.3868 +              ret = get_mouse_data(&mouse_data2);
 15.3869 +              ret = send_to_mouse_ctrl(mouse_data1);
 15.3870 +              ret = get_mouse_data(&mouse_data2);
 15.3871 +              CLEAR_CF();
 15.3872 +              regs.u.r8.ah = 0;
 15.3873 +            } else {
 15.3874 +              // error
 15.3875 +              SET_CF();
 15.3876 +              regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3877 +            }
 15.3878 +          } else {
 15.3879 +            // error
 15.3880 +            SET_CF();
 15.3881 +            regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3882 +          }
 15.3883 +          break;
 15.3884 +
 15.3885 +        case 3: // Set Resolution
 15.3886 +BX_DEBUG_INT15("case 3:\n");
 15.3887 +          // BX:
 15.3888 +          //      0 =  25 dpi, 1 count  per millimeter
 15.3889 +          //      1 =  50 dpi, 2 counts per millimeter
 15.3890 +          //      2 = 100 dpi, 4 counts per millimeter
 15.3891 +          //      3 = 200 dpi, 8 counts per millimeter
 15.3892 +          CLEAR_CF();
 15.3893 +          regs.u.r8.ah = 0;
 15.3894 +          break;
 15.3895 +
 15.3896 +        case 4: // Get Device ID
 15.3897 +BX_DEBUG_INT15("case 4:\n");
 15.3898 +          inhibit_mouse_int_and_events(); // disable IRQ12 and packets
 15.3899 +          ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
 15.3900 +          if (ret == 0) {
 15.3901 +            ret = get_mouse_data(&mouse_data1);
 15.3902 +            ret = get_mouse_data(&mouse_data2);
 15.3903 +            CLEAR_CF();
 15.3904 +            regs.u.r8.ah = 0;
 15.3905 +            regs.u.r8.bh = mouse_data2;
 15.3906 +          } else {
 15.3907 +            // error
 15.3908 +            SET_CF();
 15.3909 +            regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3910 +          }
 15.3911 +          break;
 15.3912 +
 15.3913 +        case 6: // Return Status & Set Scaling Factor...
 15.3914 +BX_DEBUG_INT15("case 6:\n");
 15.3915 +          switch (regs.u.r8.bh) {
 15.3916 +            case 0: // Return Status
 15.3917 +              comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
 15.3918 +              ret = send_to_mouse_ctrl(0xE9); // get mouse info command
 15.3919 +              if (ret == 0) {
 15.3920 +                ret = get_mouse_data(&mouse_data1);
 15.3921 +                if (mouse_data1 != 0xfa)
 15.3922 +                  BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
 15.3923 +                if (ret == 0) {
 15.3924 +                  ret = get_mouse_data(&mouse_data1);
 15.3925 +                  if ( ret == 0 ) {
 15.3926 +                    ret = get_mouse_data(&mouse_data2);
 15.3927 +                    if ( ret == 0 ) {
 15.3928 +                      ret = get_mouse_data(&mouse_data3);
 15.3929 +                      if ( ret == 0 ) {
 15.3930 +                        CLEAR_CF();
 15.3931 +                        regs.u.r8.ah = 0;
 15.3932 +                        regs.u.r8.bl = mouse_data1;
 15.3933 +                        regs.u.r8.cl = mouse_data2;
 15.3934 +                        regs.u.r8.dl = mouse_data3;
 15.3935 +                        set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
 15.3936 +                        return;
 15.3937 +                        }
 15.3938 +                      }
 15.3939 +                    }
 15.3940 +                  }
 15.3941 +                }
 15.3942 +
 15.3943 +              // error
 15.3944 +              SET_CF();
 15.3945 +              regs.u.r8.ah = ret;
 15.3946 +              set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
 15.3947 +              return;
 15.3948 +
 15.3949 +            case 1: // Set Scaling Factor to 1:1
 15.3950 +            case 2: // Set Scaling Factor to 2:1
 15.3951 +              comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
 15.3952 +              if (regs.u.r8.bh == 1) {
 15.3953 +                ret = send_to_mouse_ctrl(0xE6);
 15.3954 +              } else {
 15.3955 +                ret = send_to_mouse_ctrl(0xE7);
 15.3956 +              }
 15.3957 +              if (ret == 0) {
 15.3958 +                get_mouse_data(&mouse_data1);
 15.3959 +                ret = (mouse_data1 != 0xFA);
 15.3960 +              }
 15.3961 +              if (ret == 0) {
 15.3962 +                CLEAR_CF();
 15.3963 +                regs.u.r8.ah = 0;
 15.3964 +              } else {
 15.3965 +                // error
 15.3966 +                SET_CF();
 15.3967 +                regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.3968 +              }
 15.3969 +              set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
 15.3970 +              break;
 15.3971 +
 15.3972 +            default:
 15.3973 +              BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
 15.3974 +            }
 15.3975 +          break;
 15.3976 +
 15.3977 +        case 7: // Set Mouse Handler Address
 15.3978 +BX_DEBUG_INT15("case 7:\n");
 15.3979 +          mouse_driver_seg = ES;
 15.3980 +          mouse_driver_offset = regs.u.r16.bx;
 15.3981 +          write_word(ebda_seg, 0x0022, mouse_driver_offset);
 15.3982 +          write_word(ebda_seg, 0x0024, mouse_driver_seg);
 15.3983 +          mouse_flags_2 = read_byte(ebda_seg, 0x0027);
 15.3984 +          if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
 15.3985 +            /* remove handler */
 15.3986 +            if ( (mouse_flags_2 & 0x80) != 0 ) {
 15.3987 +              mouse_flags_2 &= ~0x80;
 15.3988 +              inhibit_mouse_int_and_events(); // disable IRQ12 and packets
 15.3989 +              }
 15.3990 +            }
 15.3991 +          else {
 15.3992 +            /* install handler */
 15.3993 +            mouse_flags_2 |= 0x80;
 15.3994 +            }
 15.3995 +          write_byte(ebda_seg, 0x0027, mouse_flags_2);
 15.3996 +          CLEAR_CF();
 15.3997 +          regs.u.r8.ah = 0;
 15.3998 +          break;
 15.3999 +
 15.4000 +        default:
 15.4001 +BX_DEBUG_INT15("case default:\n");
 15.4002 +          regs.u.r8.ah = 1; // invalid function
 15.4003 +          SET_CF();
 15.4004 +        }
 15.4005 +      break;
 15.4006 +
 15.4007 +    default:
 15.4008 +      BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
 15.4009 +        (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
 15.4010 +      SET_CF();
 15.4011 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.4012 +      break;
 15.4013 +    }
 15.4014 +}
 15.4015 +#endif
 15.4016 +
 15.4017 +  void
 15.4018 +int15_function32(regs, ES, DS, FLAGS)
 15.4019 +  pushad_regs_t regs; // REGS pushed via pushad
 15.4020 +  Bit16u ES, DS, FLAGS;
 15.4021 +{
 15.4022 +  Bit32u  extended_memory_size=0; // 64bits long
 15.4023 +  Bit16u  CX,DX;
 15.4024 +
 15.4025 +BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
 15.4026 +
 15.4027 +  switch (regs.u.r8.ah) {
 15.4028 +    case 0x86:
 15.4029 +      // Wait for CX:DX microseconds. currently using the 
 15.4030 +      // refresh request port 0x61 bit4, toggling every 15usec 
 15.4031 +
 15.4032 +      CX = regs.u.r16.cx;
 15.4033 +      DX = regs.u.r16.dx;
 15.4034 +
 15.4035 +ASM_START
 15.4036 +      sti
 15.4037 +
 15.4038 +      ;; Get the count in eax
 15.4039 +      mov  bx, sp
 15.4040 +      SEG SS
 15.4041 +        mov  ax, _int15_function.CX [bx]
 15.4042 +      shl  eax, #16
 15.4043 +      SEG SS
 15.4044 +        mov  ax, _int15_function.DX [bx]
 15.4045 +
 15.4046 +      ;; convert to numbers of 15usec ticks
 15.4047 +      mov ebx, #15
 15.4048 +      xor edx, edx
 15.4049 +      div eax, ebx
 15.4050 +      mov ecx, eax
 15.4051 +
 15.4052 +      ;; wait for ecx number of refresh requests
 15.4053 +      in al, #0x61
 15.4054 +      and al,#0x10
 15.4055 +      mov ah, al
 15.4056 +
 15.4057 +      or ecx, ecx
 15.4058 +      je int1586_tick_end
 15.4059 +int1586_tick:
 15.4060 +      in al, #0x61
 15.4061 +      and al,#0x10
 15.4062 +      cmp al, ah
 15.4063 +      je  int1586_tick
 15.4064 +      mov ah, al
 15.4065 +      dec ecx
 15.4066 +      jnz int1586_tick
 15.4067 +int1586_tick_end:
 15.4068 +ASM_END
 15.4069 +
 15.4070 +      break;
 15.4071 +
 15.4072 +    case 0xe8:
 15.4073 +        switch(regs.u.r8.al)
 15.4074 +        {
 15.4075 +         case 0x20: // coded by osmaker aka K.J.
 15.4076 +            if(regs.u.r32.edx == 0x534D4150)
 15.4077 +            {
 15.4078 +#ifdef VMXASSIST
 15.4079 +		if ((regs.u.r16.bx / 0x14)* 0x14 == regs.u.r16.bx) {
 15.4080 +		    Bit16u e820_table_size = read_word(0xe000, 0x8) * 0x14;
 15.4081 +
 15.4082 +		    if (regs.u.r16.bx + 0x14 <= e820_table_size) {
 15.4083 +			memcpyb(ES, regs.u.r16.di,
 15.4084 +				0xe000, 0x10 + regs.u.r16.bx, 0x14);
 15.4085 +		    }
 15.4086 +		    regs.u.r32.ebx += 0x14;
 15.4087 +		    if ((regs.u.r32.ebx + 0x14 - 1) > e820_table_size)
 15.4088 +			regs.u.r32.ebx = 0;
 15.4089 +		    regs.u.r32.eax = 0x534D4150;
 15.4090 +		    regs.u.r32.ecx = 0x14;
 15.4091 +		    CLEAR_CF();
 15.4092 +		    return;
 15.4093 +		} else if (regs.u.r16.bx == 1) {
 15.4094 +		    extended_memory_size = inb_cmos(0x35);
 15.4095 +		    extended_memory_size <<= 8;
 15.4096 +		    extended_memory_size |= inb_cmos(0x34);
 15.4097 +		    extended_memory_size *= 64;
 15.4098 +		    if (extended_memory_size > 0x3bc000) // greater than EFF00000???
 15.4099 +		    {
 15.4100 +			extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
 15.4101 +		    }
 15.4102 +		    extended_memory_size *= 1024;
 15.4103 +		    extended_memory_size += 15728640; // make up for the 16mb of memory that is chopped off
 15.4104 +
 15.4105 +		    if (extended_memory_size <= 15728640)
 15.4106 +		    {
 15.4107 +			extended_memory_size = inb_cmos(0x31);
 15.4108 +			extended_memory_size <<= 8;
 15.4109 +			extended_memory_size |= inb_cmos(0x30);
 15.4110 +			extended_memory_size *= 1024;
 15.4111 +		    }
 15.4112 +
 15.4113 +		    write_word(ES, regs.u.r16.di, 0x0000);
 15.4114 +		    write_word(ES, regs.u.r16.di+2, 0x0010);
 15.4115 +		    write_word(ES, regs.u.r16.di+4, 0x0000);
 15.4116 +		    write_word(ES, regs.u.r16.di+6, 0x0000);
 15.4117 +
 15.4118 +		    write_word(ES, regs.u.r16.di+8, extended_memory_size);
 15.4119 +		    extended_memory_size >>= 16;
 15.4120 +		    write_word(ES, regs.u.r16.di+10, extended_memory_size);
 15.4121 +		    extended_memory_size >>= 16;
 15.4122 +		    write_word(ES, regs.u.r16.di+12, extended_memory_size);
 15.4123 +		    extended_memory_size >>= 16;
 15.4124 +		    write_word(ES, regs.u.r16.di+14, extended_memory_size);
 15.4125 +
 15.4126 +		    write_word(ES, regs.u.r16.di+16, 0x1);
 15.4127 +		    write_word(ES, regs.u.r16.di+18, 0x0);
 15.4128 +
 15.4129 +		    regs.u.r32.ebx = 0;
 15.4130 +		    regs.u.r32.eax = 0x534D4150;
 15.4131 +		    regs.u.r32.ecx = 0x14;
 15.4132 +		    CLEAR_CF();
 15.4133 +		    return;
 15.4134 +		} else { /* AX=E820, DX=534D4150, BX unrecognized */
 15.4135 +		    goto int15_unimplemented;
 15.4136 +		}
 15.4137 +#else
 15.4138 +                switch(regs.u.r16.bx)
 15.4139 +                {
 15.4140 +                    case 0:
 15.4141 +                        write_word(ES, regs.u.r16.di, 0x00);
 15.4142 +                        write_word(ES, regs.u.r16.di+2, 0x00);
 15.4143 +                        write_word(ES, regs.u.r16.di+4, 0x00);
 15.4144 +                        write_word(ES, regs.u.r16.di+6, 0x00);
 15.4145 +
 15.4146 +                        write_word(ES, regs.u.r16.di+8, 0xFC00);
 15.4147 +                        write_word(ES, regs.u.r16.di+10, 0x0009);
 15.4148 +                        write_word(ES, regs.u.r16.di+12, 0x0000);
 15.4149 +                        write_word(ES, regs.u.r16.di+14, 0x0000);
 15.4150 +
 15.4151 +                        write_word(ES, regs.u.r16.di+16, 0x1);
 15.4152 +                        write_word(ES, regs.u.r16.di+18, 0x0);
 15.4153 +
 15.4154 +                        regs.u.r32.ebx = 1;
 15.4155 +
 15.4156 +                        regs.u.r32.eax = 0x534D4150;
 15.4157 +                        regs.u.r32.ecx = 0x14;
 15.4158 +                        CLEAR_CF();
 15.4159 +                        return;
 15.4160 +                        break;
 15.4161 +                    case 1:
 15.4162 +                        extended_memory_size = inb_cmos(0x35);
 15.4163 +                        extended_memory_size <<= 8;
 15.4164 +                        extended_memory_size |= inb_cmos(0x34);
 15.4165 +                        extended_memory_size *= 64;
 15.4166 +                        if(extended_memory_size > 0x3bc000) // greater than EFF00000???
 15.4167 +                        {
 15.4168 +                            extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
 15.4169 +                        }
 15.4170 +                        extended_memory_size *= 1024;
 15.4171 +                        extended_memory_size += 15728640; // make up for the 16mb of memory that is chopped off
 15.4172 +
 15.4173 +                        if(extended_memory_size <= 15728640)
 15.4174 +                        {
 15.4175 +                            extended_memory_size = inb_cmos(0x31);
 15.4176 +                            extended_memory_size <<= 8;
 15.4177 +                            extended_memory_size |= inb_cmos(0x30);
 15.4178 +                            extended_memory_size *= 1024;
 15.4179 +                        }
 15.4180 +
 15.4181 +                        write_word(ES, regs.u.r16.di, 0x0000);
 15.4182 +                        write_word(ES, regs.u.r16.di+2, 0x0010);
 15.4183 +                        write_word(ES, regs.u.r16.di+4, 0x0000);
 15.4184 +                        write_word(ES, regs.u.r16.di+6, 0x0000);
 15.4185 +
 15.4186 +                        write_word(ES, regs.u.r16.di+8, extended_memory_size);
 15.4187 +                        extended_memory_size >>= 16;
 15.4188 +                        write_word(ES, regs.u.r16.di+10, extended_memory_size);
 15.4189 +                        extended_memory_size >>= 16;
 15.4190 +                        write_word(ES, regs.u.r16.di+12, extended_memory_size);
 15.4191 +                        extended_memory_size >>= 16;
 15.4192 +                        write_word(ES, regs.u.r16.di+14, extended_memory_size);
 15.4193 +
 15.4194 +                        write_word(ES, regs.u.r16.di+16, 0x1);
 15.4195 +                        write_word(ES, regs.u.r16.di+18, 0x0);
 15.4196 +
 15.4197 +                        regs.u.r32.ebx = 0;
 15.4198 +                        regs.u.r32.eax = 0x534D4150;
 15.4199 +                        regs.u.r32.ecx = 0x14;
 15.4200 +                        CLEAR_CF();
 15.4201 +                        return;
 15.4202 +                        break;
 15.4203 +                    default:  /* AX=E820, DX=534D4150, BX unrecognized */
 15.4204 +                        goto int15_unimplemented;
 15.4205 +                        break;
 15.4206 +                }
 15.4207 +#endif
 15.4208 +	    } else {
 15.4209 +	      // if DX != 0x534D4150)
 15.4210 +	      goto int15_unimplemented;
 15.4211 +	    }
 15.4212 +            break;
 15.4213 +
 15.4214 +        case 0x01: 
 15.4215 +          // do we have any reason to fail here ?
 15.4216 +          CLEAR_CF();
 15.4217 +
 15.4218 +          // my real system sets ax and bx to 0
 15.4219 +          // this is confirmed by Ralph Brown list
 15.4220 +          // but syslinux v1.48 is known to behave 
 15.4221 +          // strangely if ax is set to 0
 15.4222 +          // regs.u.r16.ax = 0;
 15.4223 +          // regs.u.r16.bx = 0;
 15.4224 +
 15.4225 +          // Get the amount of extended memory (above 1M)
 15.4226 +          regs.u.r8.cl = inb_cmos(0x30);
 15.4227 +          regs.u.r8.ch = inb_cmos(0x31);
 15.4228 +          
 15.4229 +          // limit to 15M
 15.4230 +          if(regs.u.r16.cx > 0x3c00)
 15.4231 +          {
 15.4232 +            regs.u.r16.cx = 0x3c00;
 15.4233 +          }
 15.4234 +
 15.4235 +          // Get the amount of extended memory above 16M in 64k blocs
 15.4236 +          regs.u.r8.dl = inb_cmos(0x34);
 15.4237 +          regs.u.r8.dh = inb_cmos(0x35);
 15.4238 +
 15.4239 +          // Set configured memory equal to extended memory
 15.4240 +          regs.u.r16.ax = regs.u.r16.cx;
 15.4241 +          regs.u.r16.bx = regs.u.r16.dx;
 15.4242 +          break;
 15.4243 +	default:  /* AH=0xE8?? but not implemented */
 15.4244 +	  goto int15_unimplemented;
 15.4245 +       }
 15.4246 +       break;
 15.4247 +    int15_unimplemented:
 15.4248 +       // fall into the default
 15.4249 +    default:
 15.4250 +      BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
 15.4251 +        (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
 15.4252 +      SET_CF();
 15.4253 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
 15.4254 +      break;
 15.4255 +    }
 15.4256 +}
 15.4257 +
 15.4258 +  void
 15.4259 +int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
 15.4260 +  Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
 15.4261 +{
 15.4262 +  Bit8u scan_code, ascii_code, shift_flags, count;
 15.4263 +  Bit16u kbd_code, max;
 15.4264 +
 15.4265 +  BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
 15.4266 +
 15.4267 +  switch (GET_AH()) {
 15.4268 +    case 0x00: /* read keyboard input */
 15.4269 +
 15.4270 +      if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
 15.4271 +        BX_PANIC("KBD: int16h: out of keyboard input\n");
 15.4272 +        }
 15.4273 +      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
 15.4274 +      else if (ascii_code == 0xE0) ascii_code = 0;
 15.4275 +      AX = (scan_code << 8) | ascii_code;
 15.4276 +      break;
 15.4277 +
 15.4278 +    case 0x01: /* check keyboard status */
 15.4279 +      if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
 15.4280 +        SET_ZF();
 15.4281 +        return;
 15.4282 +        }
 15.4283 +      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
 15.4284 +      else if (ascii_code == 0xE0) ascii_code = 0;
 15.4285 +      AX = (scan_code << 8) | ascii_code;
 15.4286 +      CLEAR_ZF();
 15.4287 +      break;
 15.4288 +
 15.4289 +    case 0x02: /* get shift flag status */
 15.4290 +      shift_flags = read_byte(0x0040, 0x17);
 15.4291 +      SET_AL(shift_flags);
 15.4292 +      break;
 15.4293 +
 15.4294 +    case 0x05: /* store key-stroke into buffer */
 15.4295 +      if ( !enqueue_key(GET_CH(), GET_CL()) ) {
 15.4296 +        SET_AL(1);
 15.4297 +        }
 15.4298 +      else {
 15.4299 +        SET_AL(0);
 15.4300 +        }
 15.4301 +      break;
 15.4302 +
 15.4303 +    case 0x09: /* GET KEYBOARD FUNCTIONALITY */
 15.4304 +      // bit Bochs Description     
 15.4305 +      //  7    0   reserved
 15.4306 +      //  6    0   INT 16/AH=20h-22h supported (122-key keyboard support)
 15.4307 +      //  5    1   INT 16/AH=10h-12h supported (enhanced keyboard support)
 15.4308 +      //  4    1   INT 16/AH=0Ah supported
 15.4309 +      //  3    0   INT 16/AX=0306h supported
 15.4310 +      //  2    0   INT 16/AX=0305h supported
 15.4311 +      //  1    0   INT 16/AX=0304h supported
 15.4312 +      //  0    0   INT 16/AX=0300h supported
 15.4313 +      //
 15.4314 +      SET_AL(0x30);
 15.4315 +      break;
 15.4316 +
 15.4317 +    case 0x0A: /* GET KEYBOARD ID */
 15.4318 +      count = 2;
 15.4319 +      kbd_code = 0x0;
 15.4320 +      outb(0x60, 0xf2);
 15.4321 +      /* Wait for data */
 15.4322 +      max=0xffff;
 15.4323 +      while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
 15.4324 +      if (max>0x0) {
 15.4325 +        if ((inb(0x60) == 0xfa)) {
 15.4326 +          do {
 15.4327 +            max=0xffff;
 15.4328 +            while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
 15.4329 +            if (max>0x0) {
 15.4330 +              kbd_code >>= 8;
 15.4331 +              kbd_code |= (inb(0x60) << 8);
 15.4332 +            }
 15.4333 +          } while (--count>0);
 15.4334 +	}
 15.4335 +      }
 15.4336 +      BX=kbd_code;
 15.4337 +      break;
 15.4338 +
 15.4339 +    case 0x10: /* read MF-II keyboard input */
 15.4340 +
 15.4341 +      if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
 15.4342 +        BX_PANIC("KBD: int16h: out of keyboard input\n");
 15.4343 +        }
 15.4344 +      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
 15.4345 +      AX = (scan_code << 8) | ascii_code;
 15.4346 +      break;
 15.4347 +
 15.4348 +    case 0x11: /* check MF-II keyboard status */
 15.4349 +      if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
 15.4350 +        SET_ZF();
 15.4351 +        return;
 15.4352 +        }
 15.4353 +      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
 15.4354 +      AX = (scan_code << 8) | ascii_code;
 15.4355 +      CLEAR_ZF();
 15.4356 +      break;
 15.4357 +
 15.4358 +    case 0x12: /* get extended keyboard status */
 15.4359 +      shift_flags = read_byte(0x0040, 0x17);
 15.4360 +      SET_AL(shift_flags);
 15.4361 +      shift_flags = read_byte(0x0040, 0x18);
 15.4362 +      SET_AH(shift_flags);
 15.4363 +      BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
 15.4364 +      break;
 15.4365 +
 15.4366 +    case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
 15.4367 +      SET_AH(0x80); // function int16 ah=0x10-0x12 supported
 15.4368 +      break;
 15.4369 +
 15.4370 +    case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
 15.4371 +      // don't change AH : function int16 ah=0x20-0x22 NOT supported
 15.4372 +      break;
 15.4373 +
 15.4374 +    case 0x6F:
 15.4375 +      if (GET_AL() == 0x08)
 15.4376 +	SET_AH(0x02); // unsupported, aka normal keyboard
 15.4377 +
 15.4378 +    default:
 15.4379 +      BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
 15.4380 +    }
 15.4381 +}
 15.4382 +
 15.4383 +  unsigned int
 15.4384 +dequeue_key(scan_code, ascii_code, incr)
 15.4385 +  Bit8u *scan_code;
 15.4386 +  Bit8u *ascii_code;
 15.4387 +  unsigned int incr;
 15.4388 +{
 15.4389 +  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
 15.4390 +  Bit16u ss;
 15.4391 +  Bit8u  acode, scode;
 15.4392 +
 15.4393 +#if BX_CPU < 2
 15.4394 +  buffer_start = 0x001E;
 15.4395 +  buffer_end   = 0x003E;
 15.4396 +#else
 15.4397 +  buffer_start = read_word(0x0040, 0x0080);
 15.4398 +  buffer_end   = read_word(0x0040, 0x0082);
 15.4399 +#endif
 15.4400 +
 15.4401 +  buffer_head = read_word(0x0040, 0x001a);
 15.4402 +  buffer_tail = read_word(0x0040, 0x001c);
 15.4403 +
 15.4404 +  if (buffer_head != buffer_tail) {
 15.4405 +    ss = get_SS();
 15.4406 +    acode = read_byte(0x0040, buffer_head);
 15.4407 +    scode = read_byte(0x0040, buffer_head+1);
 15.4408 +    write_byte(ss, ascii_code, acode);
 15.4409 +    write_byte(ss, scan_code, scode);
 15.4410 +
 15.4411 +    if (incr) {
 15.4412 +      buffer_head += 2;
 15.4413 +      if (buffer_head >= buffer_end)
 15.4414 +        buffer_head = buffer_start;
 15.4415 +      write_word(0x0040, 0x001a, buffer_head);
 15.4416 +      }
 15.4417 +    return(1);
 15.4418 +    }
 15.4419 +  else {
 15.4420 +    return(0);
 15.4421 +    }
 15.4422 +}
 15.4423 +
 15.4424 +static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
 15.4425 +
 15.4426 +  Bit8u
 15.4427 +inhibit_mouse_int_and_events()
 15.4428 +{
 15.4429 +  Bit8u command_byte, prev_command_byte;
 15.4430 +
 15.4431 +  // Turn off IRQ generation and aux data line
 15.4432 +  if ( inb(0x64) & 0x02 )
 15.4433 +    BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
 15.4434 +  outb(0x64, 0x20); // get command byte
 15.4435 +  while ( (inb(0x64) & 0x01) != 0x01 );
 15.4436 +  prev_command_byte = inb(0x60);
 15.4437 +  command_byte = prev_command_byte;
 15.4438 +  //while ( (inb(0x64) & 0x02) );
 15.4439 +  if ( inb(0x64) & 0x02 )
 15.4440 +    BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
 15.4441 +  command_byte &= 0xfd; // turn off IRQ 12 generation
 15.4442 +  command_byte |= 0x20; // disable mouse serial clock line
 15.4443 +  outb(0x64, 0x60); // write command byte
 15.4444 +  outb(0x60, command_byte);
 15.4445 +  return(prev_command_byte);
 15.4446 +}
 15.4447 +
 15.4448 +  void
 15.4449 +enable_mouse_int_and_events()
 15.4450 +{
 15.4451 +  Bit8u command_byte;
 15.4452 +
 15.4453 +  // Turn on IRQ generation and aux data line
 15.4454 +  if ( inb(0x64) & 0x02 )
 15.4455 +    BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
 15.4456 +  outb(0x64, 0x20); // get command byte
 15.4457 +  while ( (inb(0x64) & 0x01) != 0x01 );
 15.4458 +  command_byte = inb(0x60);
 15.4459 +  //while ( (inb(0x64) & 0x02) );
 15.4460 +  if ( inb(0x64) & 0x02 )
 15.4461 +    BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
 15.4462 +  command_byte |= 0x02; // turn on IRQ 12 generation
 15.4463 +  command_byte &= 0xdf; // enable mouse serial clock line
 15.4464 +  outb(0x64, 0x60); // write command byte
 15.4465 +  outb(0x60, command_byte);
 15.4466 +}
 15.4467 +
 15.4468 +  Bit8u
 15.4469 +send_to_mouse_ctrl(sendbyte)
 15.4470 +  Bit8u sendbyte;
 15.4471 +{
 15.4472 +  Bit8u response;
 15.4473 +
 15.4474 +  // wait for chance to write to ctrl
 15.4475 +  if ( inb(0x64) & 0x02 )
 15.4476 +    BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
 15.4477 +  outb(0x64, 0xD4);
 15.4478 +  outb(0x60, sendbyte);
 15.4479 +  return(0);
 15.4480 +}
 15.4481 +
 15.4482 +
 15.4483 +  Bit8u
 15.4484 +get_mouse_data(data)
 15.4485 +  Bit8u *data;
 15.4486 +{
 15.4487 +  Bit8u response;
 15.4488 +  Bit16u ss;
 15.4489 +
 15.4490 +  while ( (inb(0x64) & 0x21) != 0x21 ) {
 15.4491 +    }
 15.4492 +
 15.4493 +  response = inb(0x60);
 15.4494 +
 15.4495 +  ss = get_SS();
 15.4496 +  write_byte(ss, data, response);
 15.4497 +  return(0);
 15.4498 +}
 15.4499 +
 15.4500 +  void
 15.4501 +set_kbd_command_byte(command_byte)
 15.4502 +  Bit8u command_byte;
 15.4503 +{
 15.4504 +  if ( inb(0x64) & 0x02 )
 15.4505 +    BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
 15.4506 +  outb(0x64, 0xD4);
 15.4507 +
 15.4508 +  outb(0x64, 0x60); // write command byte
 15.4509 +  outb(0x60, command_byte);
 15.4510 +}
 15.4511 +
 15.4512 +  void
 15.4513 +int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
 15.4514 +  Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
 15.4515 +{
 15.4516 +  Bit8u scancode, asciicode, shift_flags;
 15.4517 +  Bit8u mf2_flags, mf2_state, led_flags;
 15.4518 +
 15.4519 +  //
 15.4520 +  // DS has been set to F000 before call
 15.4521 +  //
 15.4522 +
 15.4523 +
 15.4524 +  scancode = GET_AL();
 15.4525 +
 15.4526 +  if (scancode == 0) {
 15.4527 +    BX_INFO("KBD: int09 handler: AL=0\n");
 15.4528 +    return;
 15.4529 +    }
 15.4530 +
 15.4531 +
 15.4532 +  shift_flags = read_byte(0x0040, 0x17);
 15.4533 +  mf2_flags = read_byte(0x0040, 0x18);
 15.4534 +  mf2_state = read_byte(0x0040, 0x96);
 15.4535 +  led_flags = read_byte(0x0040, 0x97);
 15.4536 +  asciicode = 0;
 15.4537 +
 15.4538 +  switch (scancode) {
 15.4539 +    case 0x3a: /* Caps Lock press */
 15.4540 +      shift_flags ^= 0x40;
 15.4541 +      write_byte(0x0040, 0x17, shift_flags);
 15.4542 +      mf2_flags |= 0x40;
 15.4543 +      write_byte(0x0040, 0x18, mf2_flags);
 15.4544 +      led_flags ^= 0x04;
 15.4545 +      write_byte(0x0040, 0x97, led_flags);
 15.4546 +      break;
 15.4547 +    case 0xba: /* Caps Lock release */
 15.4548 +      mf2_flags &= ~0x40;
 15.4549 +      write_byte(0x0040, 0x18, mf2_flags);
 15.4550 +      break;
 15.4551 +
 15.4552 +    case 0x2a: /* L Shift press */
 15.4553 +      /*shift_flags &= ~0x40;*/
 15.4554 +      shift_flags |= 0x02;
 15.4555 +      write_byte(0x0040, 0x17, shift_flags);
 15.4556 +      led_flags &= ~0x04;
 15.4557 +      write_byte(0x0040, 0x97, led_flags);
 15.4558 +      break;
 15.4559 +    case 0xaa: /* L Shift release */
 15.4560 +      shift_flags &= ~0x02;
 15.4561 +      write_byte(0x0040, 0x17, shift_flags);
 15.4562 +      break;
 15.4563 +
 15.4564 +    case 0x36: /* R Shift press */
 15.4565 +      /*shift_flags &= ~0x40;*/
 15.4566 +      shift_flags |= 0x01;
 15.4567 +      write_byte(0x0040, 0x17, shift_flags);
 15.4568 +      led_flags &= ~0x04;
 15.4569 +      write_byte(0x0040, 0x97, led_flags);
 15.4570 +      break;
 15.4571 +    case 0xb6: /* R Shift release */
 15.4572 +      shift_flags &= ~0x01;
 15.4573 +      write_byte(0x0040, 0x17, shift_flags);
 15.4574 +      break;
 15.4575 +
 15.4576 +    case 0x1d: /* Ctrl press */
 15.4577 +      shift_flags |= 0x04;
 15.4578 +      write_byte(0x0040, 0x17, shift_flags);
 15.4579 +      if (mf2_state & 0x01) {
 15.4580 +        mf2_flags |= 0x04;
 15.4581 +      } else {
 15.4582 +        mf2_flags |= 0x01;
 15.4583 +        }
 15.4584 +      write_byte(0x0040, 0x18, mf2_flags);
 15.4585 +      break;
 15.4586 +    case 0x9d: /* Ctrl release */
 15.4587 +      shift_flags &= ~0x04;
 15.4588 +      write_byte(0x0040, 0x17, shift_flags);
 15.4589 +      if (mf2_state & 0x01) {
 15.4590 +        mf2_flags &= ~0x04;
 15.4591 +      } else {
 15.4592 +        mf2_flags &= ~0x01;
 15.4593 +        }
 15.4594 +      write_byte(0x0040, 0x18, mf2_flags);
 15.4595 +      break;
 15.4596 +
 15.4597 +    case 0x38: /* Alt press */
 15.4598 +      shift_flags |= 0x08;
 15.4599 +      write_byte(0x0040, 0x17, shift_flags);
 15.4600 +      if (mf2_state & 0x01) {
 15.4601 +        mf2_flags |= 0x08;
 15.4602 +      } else {
 15.4603 +        mf2_flags |= 0x02;
 15.4604 +        }
 15.4605 +      write_byte(0x0040, 0x18, mf2_flags);
 15.4606 +      break;
 15.4607 +    case 0xb8: /* Alt release */
 15.4608 +      shift_flags &= ~0x08;
 15.4609 +      write_byte(0x0040, 0x17, shift_flags);
 15.4610 +      if (mf2_state & 0x01) {
 15.4611 +        mf2_flags &= ~0x08;
 15.4612 +      } else {
 15.4613 +        mf2_flags &= ~0x02;
 15.4614 +        }
 15.4615 +      write_byte(0x0040, 0x18, mf2_flags);
 15.4616 +      break;
 15.4617 +
 15.4618 +    case 0x45: /* Num Lock press */
 15.4619 +      if ((mf2_state & 0x01) == 0) {
 15.4620 +        mf2_flags |= 0x20;
 15.4621 +        write_byte(0x0040, 0x18, mf2_flags);
 15.4622 +        shift_flags ^= 0x20;
 15.4623 +        led_flags ^= 0x02;
 15.4624 +        write_byte(0x0040, 0x17, shift_flags);
 15.4625 +        write_byte(0x0040, 0x97, led_flags);
 15.4626 +        }
 15.4627 +      break;
 15.4628 +    case 0xc5: /* Num Lock release */
 15.4629 +      if ((mf2_state & 0x01) == 0) {
 15.4630 +        mf2_flags &= ~0x20;
 15.4631 +        write_byte(0x0040, 0x18, mf2_flags);
 15.4632 +        }
 15.4633 +      break;
 15.4634 +
 15.4635 +    case 0x46: /* Scroll Lock press */
 15.4636 +      mf2_flags |= 0x10;
 15.4637 +      write_byte(0x0040, 0x18, mf2_flags);
 15.4638 +      shift_flags ^= 0x10;
 15.4639 +      led_flags ^= 0x01;
 15.4640 +      write_byte(0x0040, 0x17, shift_flags);
 15.4641 +      write_byte(0x0040, 0x97, led_flags);
 15.4642 +      break;
 15.4643 +
 15.4644 +    case 0xc6: /* Scroll Lock release */
 15.4645 +      mf2_flags &= ~0x10;
 15.4646 +      write_byte(0x0040, 0x18, mf2_flags);
 15.4647 +      break;
 15.4648 +
 15.4649 +    default:
 15.4650 +      if (scancode & 0x80) return; /* toss key releases ... */
 15.4651 +      if (scancode > MAX_SCAN_CODE) {
 15.4652 +        BX_INFO("KBD: int09h_handler(): unknown scancode read!\n");
 15.4653 +        return;
 15.4654 +        }
 15.4655 +      if (shift_flags & 0x08) { /* ALT */
 15.4656 +        asciicode = scan_to_scanascii[scancode].alt;
 15.4657 +        scancode = scan_to_scanascii[scancode].alt >> 8;
 15.4658 +        }
 15.4659 +      else if (shift_flags & 0x04) { /* CONTROL */
 15.4660 +        asciicode = scan_to_scanascii[scancode].control;
 15.4661 +        scancode = scan_to_scanascii[scancode].control >> 8;
 15.4662 +        }
 15.4663 +      else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
 15.4664 +        /* check if lock state should be ignored 
 15.4665 +         * because a SHIFT key are pressed */
 15.4666 +         
 15.4667 +        if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
 15.4668 +          asciicode = scan_to_scanascii[scancode].normal;
 15.4669 +          scancode = scan_to_scanascii[scancode].normal >> 8;
 15.4670 +          }
 15.4671 +        else {
 15.4672 +          asciicode = scan_to_scanascii[scancode].shift;
 15.4673 +          scancode = scan_to_scanascii[scancode].shift >> 8;
 15.4674 +          }
 15.4675 +        }
 15.4676 +      else {
 15.4677 +        /* check if lock is on */
 15.4678 +        if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
 15.4679 +          asciicode = scan_to_scanascii[scancode].shift;
 15.4680 +          scancode = scan_to_scanascii[scancode].shift >> 8;
 15.4681 +          }
 15.4682 +        else {
 15.4683 +          asciicode = scan_to_scanascii[scancode].normal;
 15.4684 +          scancode = scan_to_scanascii[scancode].normal >> 8;
 15.4685 +          }
 15.4686 +        }
 15.4687 +      if (scancode==0 && asciicode==0) {
 15.4688 +        BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
 15.4689 +        }
 15.4690 +      enqueue_key(scancode, asciicode);
 15.4691 +      break;
 15.4692 +    }
 15.4693 +  mf2_state &= ~0x01;
 15.4694 +}
 15.4695 +
 15.4696 +  unsigned int
 15.4697 +enqueue_key(scan_code, ascii_code)
 15.4698 +  Bit8u scan_code, ascii_code;
 15.4699 +{
 15.4700 +  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
 15.4701 +
 15.4702 +  //BX_INFO("KBD:   enqueue_key() called scan:%02x, ascii:%02x\n",
 15.4703 +  //    scan_code, ascii_code);
 15.4704 +
 15.4705 +#if BX_CPU < 2
 15.4706 +  buffer_start = 0x001E;
 15.4707 +  buffer_end   = 0x003E;
 15.4708 +#else
 15.4709 +  buffer_start = read_word(0x0040, 0x0080);
 15.4710 +  buffer_end   = read_word(0x0040, 0x0082);
 15.4711 +#endif
 15.4712 +
 15.4713 +  buffer_head = read_word(0x0040, 0x001A);
 15.4714 +  buffer_tail = read_word(0x0040, 0x001C);
 15.4715 +
 15.4716 +  temp_tail = buffer_tail;
 15.4717 +  buffer_tail += 2;
 15.4718 +  if (buffer_tail >= buffer_end)
 15.4719 +    buffer_tail = buffer_start;
 15.4720 +
 15.4721 +  if (buffer_tail == buffer_head) {
 15.4722 +    return(0);
 15.4723 +    }
 15.4724 +
 15.4725 +   write_byte(0x0040, temp_tail, ascii_code);
 15.4726 +   write_byte(0x0040, temp_tail+1, scan_code);
 15.4727 +   write_word(0x0040, 0x001C, buffer_tail);
 15.4728 +   return(1);
 15.4729 +}
 15.4730 +
 15.4731 +
 15.4732 +  void
 15.4733 +int74_function(make_farcall, Z, Y, X, status)
 15.4734 +  Bit16u make_farcall, Z, Y, X, status;
 15.4735 +{
 15.4736 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.4737 +  Bit8u  in_byte, index, package_count;
 15.4738 +  Bit8u  mouse_flags_1, mouse_flags_2;
 15.4739 +
 15.4740 +BX_DEBUG_INT74("entering int74_function\n");
 15.4741 +  make_farcall = 0;
 15.4742 +
 15.4743 +  in_byte = inb(0x64);
 15.4744 +  if ( (in_byte & 0x21) != 0x21 ) {
 15.4745 +    return;
 15.4746 +    }
 15.4747 +  in_byte = inb(0x60);
 15.4748 +BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
 15.4749 +
 15.4750 +  mouse_flags_1 = read_byte(ebda_seg, 0x0026);
 15.4751 +  mouse_flags_2 = read_byte(ebda_seg, 0x0027);
 15.4752 +
 15.4753 +  if ( (mouse_flags_2 & 0x80) != 0x80 ) {
 15.4754 +      //    BX_PANIC("int74_function:\n");
 15.4755 +      return;
 15.4756 +    }
 15.4757 +
 15.4758 +  package_count = mouse_flags_2 & 0x07;
 15.4759 +  index = mouse_flags_1 & 0x07;
 15.4760 +  write_byte(ebda_seg, 0x28 + index, in_byte);
 15.4761 +
 15.4762 +  if ( (index+1) >= package_count ) {
 15.4763 +BX_DEBUG_INT74("int74_function: make_farcall=1\n");
 15.4764 +    status = read_byte(ebda_seg, 0x0028 + 0);
 15.4765 +    X      = read_byte(ebda_seg, 0x0028 + 1);
 15.4766 +    Y      = read_byte(ebda_seg, 0x0028 + 2);
 15.4767 +    Z      = 0;
 15.4768 +    mouse_flags_1 = 0;
 15.4769 +    // check if far call handler installed
 15.4770 +    if (mouse_flags_2 & 0x80)
 15.4771 +      make_farcall = 1;
 15.4772 +    }
 15.4773 +  else {
 15.4774 +    mouse_flags_1++;
 15.4775 +    }
 15.4776 +  write_byte(ebda_seg, 0x0026, mouse_flags_1);
 15.4777 +}
 15.4778 +
 15.4779 +#define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
 15.4780 +
 15.4781 +#if BX_USE_ATADRV
 15.4782 +
 15.4783 +  void
 15.4784 +int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
 15.4785 +  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
 15.4786 +{
 15.4787 +  Bit32u lba;
 15.4788 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.4789 +  Bit16u cylinder, head, sector;
 15.4790 +  Bit16u segment, offset;
 15.4791 +  Bit16u npc, nph, npspt, nlc, nlh, nlspt;
 15.4792 +  Bit16u size, count;
 15.4793 +  Bit8u  device, status;
 15.4794 +
 15.4795 +  BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
 15.4796 +
 15.4797 +  write_byte(0x0040, 0x008e, 0);  // clear completion flag
 15.4798 +
 15.4799 +  // basic check : device has to be defined
 15.4800 +  if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
 15.4801 +    BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
 15.4802 +    goto int13_fail;
 15.4803 +    }
 15.4804 +
 15.4805 +  // Get the ata channel
 15.4806 +  device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
 15.4807 +
 15.4808 +  // basic check : device has to be valid 
 15.4809 +  if (device >= BX_MAX_ATA_DEVICES) {
 15.4810 +    BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
 15.4811 +    goto int13_fail;
 15.4812 +    }
 15.4813 +  
 15.4814 +  switch (GET_AH()) {
 15.4815 +
 15.4816 +    case 0x00: /* disk controller reset */
 15.4817 +      ata_reset (device);
 15.4818 +      goto int13_success;
 15.4819 +      break;
 15.4820 +
 15.4821 +    case 0x01: /* read disk status */
 15.4822 +      status = read_byte(0x0040, 0x0074);
 15.4823 +      SET_AH(status);
 15.4824 +      SET_DISK_RET_STATUS(0);
 15.4825 +      /* set CF if error status read */
 15.4826 +      if (status) goto int13_fail_nostatus;
 15.4827 +      else        goto int13_success_noah;
 15.4828 +      break;
 15.4829 +
 15.4830 +    case 0x02: // read disk sectors
 15.4831 +    case 0x03: // write disk sectors 
 15.4832 +    case 0x04: // verify disk sectors
 15.4833 +
 15.4834 +      count       = GET_AL();
 15.4835 +      cylinder    = GET_CH();
 15.4836 +      cylinder   |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
 15.4837 +      sector      = (GET_CL() & 0x3f);
 15.4838 +      head        = GET_DH();
 15.4839 +
 15.4840 +      segment = ES;
 15.4841 +      offset  = BX;
 15.4842 +
 15.4843 +      if ( (count > 128) || (count == 0) ) {
 15.4844 +        BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
 15.4845 +        goto int13_fail;
 15.4846 +        }
 15.4847 +
 15.4848 +      nlc   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
 15.4849 +      nlh   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
 15.4850 +      nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
 15.4851 +
 15.4852 +      // sanity check on cyl heads, sec
 15.4853 +      if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
 15.4854 +        BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
 15.4855 +        goto int13_fail;
 15.4856 +        }
 15.4857 +      
 15.4858 +      // FIXME verify
 15.4859 +      if ( GET_AH() == 0x04 ) goto int13_success;
 15.4860 +
 15.4861 +      nph   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
 15.4862 +      npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
 15.4863 +
 15.4864 +      // if needed, translate lchs to lba, and execute command
 15.4865 +      if ( (nph != nlh) || (npspt != nlspt)) {
 15.4866 +        lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
 15.4867 +        sector = 0; // this forces the command to be lba
 15.4868 +        }
 15.4869 +
 15.4870 +      if ( GET_AH() == 0x02 )
 15.4871 +        status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
 15.4872 +      else
 15.4873 +        status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
 15.4874 +
 15.4875 +      // Set nb of sector transferred
 15.4876 +      SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
 15.4877 +
 15.4878 +      if (status != 0) {
 15.4879 +        BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
 15.4880 +        SET_AH(0x0c);
 15.4881 +        goto int13_fail_noah;
 15.4882 +        }
 15.4883 +
 15.4884 +      goto int13_success;
 15.4885 +      break;
 15.4886 +
 15.4887 +    case 0x05: /* format disk track */
 15.4888 +      BX_INFO("format disk track called\n");
 15.4889 +      goto int13_success;
 15.4890 +      return;
 15.4891 +      break;
 15.4892 +
 15.4893 +    case 0x08: /* read disk drive parameters */
 15.4894 +      
 15.4895 +      // Get logical geometry from table
 15.4896 +      nlc   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
 15.4897 +      nlh   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
 15.4898 +      nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
 15.4899 +      count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
 15.4900 +
 15.4901 +      nlc = nlc - 2; /* 0 based , last sector not used */
 15.4902 +      SET_AL(0);
 15.4903 +      SET_CH(nlc & 0xff);
 15.4904 +      SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
 15.4905 +      SET_DH(nlh - 1);
 15.4906 +      SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
 15.4907 +
 15.4908 +      // FIXME should set ES & DI
 15.4909 +      
 15.4910 +      goto int13_success;
 15.4911 +      break;
 15.4912 +
 15.4913 +    case 0x10: /* check drive ready */
 15.4914 +      // should look at 40:8E also???
 15.4915 +      
 15.4916 +      // Read the status from controller
 15.4917 +      status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
 15.4918 +      if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
 15.4919 +        goto int13_success;
 15.4920 +        }
 15.4921 +      else {
 15.4922 +        SET_AH(0xAA);
 15.4923 +        goto int13_fail_noah;
 15.4924 +        }
 15.4925 +      break;
 15.4926 +
 15.4927 +    case 0x15: /* read disk drive size */
 15.4928 +
 15.4929 +      // Get physical geometry from table
 15.4930 +      npc   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
 15.4931 +      nph   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
 15.4932 +      npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
 15.4933 +
 15.4934 +      // Compute sector count seen by int13
 15.4935 +      lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
 15.4936 +      CX = lba >> 16;
 15.4937 +      DX = lba & 0xffff;
 15.4938 +
 15.4939 +      SET_AH(3);  // hard disk accessible
 15.4940 +      goto int13_success_noah;
 15.4941 +      break;
 15.4942 +
 15.4943 +    case 0x41: // IBM/MS installation check
 15.4944 +      BX=0xaa55;     // install check
 15.4945 +      SET_AH(0x30);  // EDD 3.0
 15.4946 +      CX=0x0007;     // ext disk access and edd, removable supported
 15.4947 +      goto int13_success_noah;
 15.4948 +      break;
 15.4949 +
 15.4950 +    case 0x42: // IBM/MS extended read
 15.4951 +    case 0x43: // IBM/MS extended write
 15.4952 +    case 0x44: // IBM/MS verify
 15.4953 +    case 0x47: // IBM/MS extended seek
 15.4954 +
 15.4955 +      count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
 15.4956 +      segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
 15.4957 +      offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
 15.4958 + 
 15.4959 +      // Can't use 64 bits lba
 15.4960 +      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
 15.4961 +      if (lba != 0L) {
 15.4962 +        BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
 15.4963 +        goto int13_fail;
 15.4964 +        }
 15.4965 +
 15.4966 +      // Get 32 bits lba and check
 15.4967 +      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
 15.4968 +      if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
 15.4969 +        BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
 15.4970 +        goto int13_fail;
 15.4971 +        }
 15.4972 +
 15.4973 +      // If verify or seek
 15.4974 +      if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
 15.4975 +        goto int13_success;
 15.4976 +      
 15.4977 +      // Execute the command
 15.4978 +      if ( GET_AH() == 0x42 )
 15.4979 +        status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
 15.4980 +      else
 15.4981 +        status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
 15.4982 +
 15.4983 +      count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
 15.4984 +      write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
 15.4985 +
 15.4986 +      if (status != 0) {
 15.4987 +        BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
 15.4988 +        SET_AH(0x0c);
 15.4989 +        goto int13_fail_noah;
 15.4990 +        }
 15.4991 +
 15.4992 +      goto int13_success;
 15.4993 +      break;
 15.4994 +
 15.4995 +    case 0x45: // IBM/MS lock/unlock drive
 15.4996 +    case 0x49: // IBM/MS extended media change
 15.4997 +      goto int13_success;    // Always success for HD
 15.4998 +      break;
 15.4999 +      
 15.5000 +    case 0x46: // IBM/MS eject media
 15.5001 +      SET_AH(0xb2);          // Volume Not Removable
 15.5002 +      goto int13_fail_noah;  // Always fail for HD
 15.5003 +      break;
 15.5004 +
 15.5005 +    case 0x48: // IBM/MS get drive parameters
 15.5006 +      size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
 15.5007 +
 15.5008 +      // Buffer is too small
 15.5009 +      if(size < 0x1a) 
 15.5010 +        goto int13_fail;
 15.5011 +
 15.5012 +      // EDD 1.x
 15.5013 +      if(size >= 0x1a) {
 15.5014 +        Bit16u   blksize;
 15.5015 +
 15.5016 +        npc     = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
 15.5017 +        nph     = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
 15.5018 +        npspt   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
 15.5019 +        lba     = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
 15.5020 +        blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
 15.5021 +
 15.5022 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
 15.5023 +        write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
 15.5024 +        write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
 15.5025 +        write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
 15.5026 +        write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
 15.5027 +        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba);  // FIXME should be Bit64
 15.5028 +        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);  
 15.5029 +        write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);  
 15.5030 +        }
 15.5031 +
 15.5032 +      // EDD 2.x
 15.5033 +      if(size >= 0x1e) {
 15.5034 +        Bit8u  channel, dev, irq, mode, checksum, i, translation;
 15.5035 +        Bit16u iobase1, iobase2, options;
 15.5036 +
 15.5037 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
 15.5038 +
 15.5039 +        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);  
 15.5040 +        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);  
 15.5041 +
 15.5042 +        // Fill in dpte
 15.5043 +        channel = device / 2;
 15.5044 +        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
 15.5045 +        iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
 15.5046 +        irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
 15.5047 +        mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
 15.5048 +        translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
 15.5049 +
 15.5050 +        options  = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
 15.5051 +        options |= (1<<4); // lba translation
 15.5052 +        options |= (mode==ATA_MODE_PIO32?1:0<<7);
 15.5053 +        options |= (translation==ATA_TRANSLATION_LBA?1:0<<9); 
 15.5054 +        options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9); 
 15.5055 +
 15.5056 +        write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
 15.5057 +        write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
 15.5058 +        write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
 15.5059 +        write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
 15.5060 +        write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
 15.5061 +        write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
 15.5062 +        write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
 15.5063 +        write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
 15.5064 +        write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
 15.5065 +        write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
 15.5066 +        write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
 15.5067 + 
 15.5068 +        checksum=0;
 15.5069 +        for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
 15.5070 +        checksum = ~checksum;
 15.5071 +        write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
 15.5072 +        }
 15.5073 +
 15.5074 +      // EDD 3.x
 15.5075 +      if(size >= 0x42) {
 15.5076 +        Bit8u channel, iface, checksum, i;
 15.5077 +        Bit16u iobase1;
 15.5078 +
 15.5079 +        channel = device / 2;
 15.5080 +        iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
 15.5081 +        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
 15.5082 +
 15.5083 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
 15.5084 +        write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
 15.5085 +        write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
 15.5086 +        write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
 15.5087 +        write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
 15.5088 +
 15.5089 +        if (iface==ATA_IFACE_ISA) {
 15.5090 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
 15.5091 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
 15.5092 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
 15.5093 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
 15.5094 +          }
 15.5095 +        else { 
 15.5096 +          // FIXME PCI
 15.5097 +          }
 15.5098 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
 15.5099 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
 15.5100 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
 15.5101 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
 15.5102 +
 15.5103 +        if (iface==ATA_IFACE_ISA) {
 15.5104 +          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
 15.5105 +          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
 15.5106 +          write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
 15.5107 +          }
 15.5108 +        else { 
 15.5109 +          // FIXME PCI
 15.5110 +          }
 15.5111 +        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
 15.5112 +        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
 15.5113 +        write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
 15.5114 +        write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
 15.5115 +
 15.5116 +        checksum=0;
 15.5117 +        for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
 15.5118 +        checksum = ~checksum;
 15.5119 +        write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
 15.5120 +        }
 15.5121 +
 15.5122 +      goto int13_success;
 15.5123 +      break;
 15.5124 +
 15.5125 +    case 0x4e: // // IBM/MS set hardware configuration
 15.5126 +      // DMA, prefetch, PIO maximum not supported
 15.5127 +      switch (GET_AL()) {
 15.5128 +        case 0x01:
 15.5129 +        case 0x03:
 15.5130 +        case 0x04:
 15.5131 +        case 0x06:
 15.5132 +          goto int13_success;
 15.5133 +          break;
 15.5134 +        default :
 15.5135 +          goto int13_fail;
 15.5136 +        }
 15.5137 +      break;
 15.5138 +
 15.5139 +    case 0x09: /* initialize drive parameters */
 15.5140 +    case 0x0c: /* seek to specified cylinder */
 15.5141 +    case 0x0d: /* alternate disk reset */
 15.5142 +    case 0x11: /* recalibrate */
 15.5143 +    case 0x14: /* controller internal diagnostic */
 15.5144 +      BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
 15.5145 +      goto int13_success;
 15.5146 +      break;
 15.5147 +
 15.5148 +    case 0x0a: /* read disk sectors with ECC */
 15.5149 +    case 0x0b: /* write disk sectors with ECC */
 15.5150 +    case 0x18: // set media type for format
 15.5151 +    case 0x50: // IBM/MS send packet command
 15.5152 +    default:
 15.5153 +      BX_INFO("int13_harddisk function %02xh unsupported, returns fail\n", GET_AH());
 15.5154 +      goto int13_fail;
 15.5155 +      break;
 15.5156 +    }
 15.5157 +
 15.5158 +int13_fail:
 15.5159 +    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
 15.5160 +int13_fail_noah:
 15.5161 +    SET_DISK_RET_STATUS(GET_AH());
 15.5162 +int13_fail_nostatus:
 15.5163 +    SET_CF();     // error occurred
 15.5164 +    return;
 15.5165 +
 15.5166 +int13_success:
 15.5167 +    SET_AH(0x00); // no error
 15.5168 +int13_success_noah:
 15.5169 +    SET_DISK_RET_STATUS(0x00);
 15.5170 +    CLEAR_CF();   // no error
 15.5171 +    return;
 15.5172 +}
 15.5173 +
 15.5174 +// ---------------------------------------------------------------------------
 15.5175 +// Start of int13 for cdrom
 15.5176 +// ---------------------------------------------------------------------------
 15.5177 +
 15.5178 +  void
 15.5179 +int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
 15.5180 +  Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
 15.5181 +{
 15.5182 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.5183 +  Bit8u  device, status, locks;
 15.5184 +  Bit8u  atacmd[12];
 15.5185 +  Bit32u lba;
 15.5186 +  Bit16u count, segment, offset, i, size;
 15.5187 +
 15.5188 +  BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
 15.5189 +  // BX_DEBUG_INT13_CD("int13_cdrom: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
 15.5190 +  
 15.5191 +  SET_DISK_RET_STATUS(0x00);
 15.5192 +
 15.5193 +  /* basic check : device should be 0xE0+ */
 15.5194 +  if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
 15.5195 +    BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
 15.5196 +    goto int13_fail;
 15.5197 +    }
 15.5198 +
 15.5199 +  // Get the ata channel
 15.5200 +  device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
 15.5201 +
 15.5202 +  /* basic check : device has to be valid  */
 15.5203 +  if (device >= BX_MAX_ATA_DEVICES) {
 15.5204 +    BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
 15.5205 +    goto int13_fail;
 15.5206 +    }
 15.5207 +  
 15.5208 +  switch (GET_AH()) {
 15.5209 +
 15.5210 +    // all those functions return SUCCESS
 15.5211 +    case 0x00: /* disk controller reset */
 15.5212 +    case 0x09: /* initialize drive parameters */
 15.5213 +    case 0x0c: /* seek to specified cylinder */
 15.5214 +    case 0x0d: /* alternate disk reset */  
 15.5215 +    case 0x10: /* check drive ready */    
 15.5216 +    case 0x11: /* recalibrate */      
 15.5217 +    case 0x14: /* controller internal diagnostic */
 15.5218 +    case 0x16: /* detect disk change */
 15.5219 +      goto int13_success;
 15.5220 +      break;
 15.5221 +
 15.5222 +    // all those functions return disk write-protected
 15.5223 +    case 0x03: /* write disk sectors */
 15.5224 +    case 0x05: /* format disk track */
 15.5225 +    case 0x43: // IBM/MS extended write
 15.5226 +      SET_AH(0x03);
 15.5227 +      goto int13_fail_noah;
 15.5228 +      break;
 15.5229 +
 15.5230 +    case 0x01: /* read disk status */
 15.5231 +      status = read_byte(0x0040, 0x0074);
 15.5232 +      SET_AH(status);
 15.5233 +      SET_DISK_RET_STATUS(0);
 15.5234 +
 15.5235 +      /* set CF if error status read */
 15.5236 +      if (status) goto int13_fail_nostatus;
 15.5237 +      else        goto int13_success_noah;
 15.5238 +      break;      
 15.5239 +
 15.5240 +    case 0x15: /* read disk drive size */
 15.5241 +      SET_AH(0x02);
 15.5242 +      goto int13_fail_noah;
 15.5243 +      break;
 15.5244 +
 15.5245 +    case 0x41: // IBM/MS installation check
 15.5246 +      BX=0xaa55;     // install check
 15.5247 +      SET_AH(0x30);  // EDD 2.1
 15.5248 +      CX=0x0007;     // ext disk access, removable and edd
 15.5249 +      goto int13_success_noah;
 15.5250 +      break;
 15.5251 +
 15.5252 +    case 0x42: // IBM/MS extended read
 15.5253 +    case 0x44: // IBM/MS verify sectors
 15.5254 +    case 0x47: // IBM/MS extended seek
 15.5255 +       
 15.5256 +      count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
 15.5257 +      segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
 15.5258 +      offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
 15.5259 + 
 15.5260 +      // Can't use 64 bits lba
 15.5261 +      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
 15.5262 +      if (lba != 0L) {
 15.5263 +        BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
 15.5264 +        goto int13_fail;
 15.5265 +        }
 15.5266 +
 15.5267 +      // Get 32 bits lba 
 15.5268 +      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
 15.5269 +
 15.5270 +      // If verify or seek
 15.5271 +      if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
 15.5272 +        goto int13_success;
 15.5273 +      
 15.5274 +      memsetb(get_SS(),atacmd,0,12);
 15.5275 +      atacmd[0]=0x28;                      // READ command
 15.5276 +      atacmd[7]=(count & 0xff00) >> 8;     // Sectors
 15.5277 +      atacmd[8]=(count & 0x00ff);          // Sectors
 15.5278 +      atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
 15.5279 +      atacmd[3]=(lba & 0x00ff0000) >> 16;
 15.5280 +      atacmd[4]=(lba & 0x0000ff00) >> 8;
 15.5281 +      atacmd[5]=(lba & 0x000000ff);
 15.5282 +      status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset); 
 15.5283 +
 15.5284 +      count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
 15.5285 +      write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
 15.5286 +
 15.5287 +      if (status != 0) {
 15.5288 +        BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
 15.5289 +        SET_AH(0x0c);
 15.5290 +        goto int13_fail_noah;
 15.5291 +        }
 15.5292 +
 15.5293 +      goto int13_success;
 15.5294 +      break;
 15.5295 +
 15.5296 +    case 0x45: // IBM/MS lock/unlock drive
 15.5297 +      if (GET_AL() > 2) goto int13_fail;
 15.5298 +
 15.5299 +      locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
 15.5300 +
 15.5301 +      switch (GET_AL()) {
 15.5302 +        case 0 :  // lock
 15.5303 +          if (locks == 0xff) {
 15.5304 +            SET_AH(0xb4);
 15.5305 +            SET_AL(1);
 15.5306 +            goto int13_fail_noah;
 15.5307 +            }
 15.5308 +          write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
 15.5309 +          SET_AL(1);
 15.5310 +          break;
 15.5311 +        case 1 :  // unlock
 15.5312 +          if (locks == 0x00) {
 15.5313 +            SET_AH(0xb0);
 15.5314 +            SET_AL(0);
 15.5315 +            goto int13_fail_noah;
 15.5316 +            }
 15.5317 +          write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
 15.5318 +          SET_AL(locks==0?0:1);
 15.5319 +          break;
 15.5320 +        case 2 :  // status
 15.5321 +          SET_AL(locks==0?0:1);
 15.5322 +          break;
 15.5323 +        }
 15.5324 +      goto int13_success;
 15.5325 +      break;
 15.5326 +
 15.5327 +    case 0x46: // IBM/MS eject media
 15.5328 +      locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
 15.5329 +      
 15.5330 +      if (locks != 0) {
 15.5331 +        SET_AH(0xb1); // media locked
 15.5332 +        goto int13_fail_noah;
 15.5333 +        }
 15.5334 +      // FIXME should handle 0x31 no media in device
 15.5335 +      // FIXME should handle 0xb5 valid request failed
 15.5336 +    
 15.5337 +      // Call removable media eject
 15.5338 +      ASM_START
 15.5339 +        push bp
 15.5340 +        mov  bp, sp
 15.5341 +
 15.5342 +        mov ah, #0x52
 15.5343 +        int 15
 15.5344 +        mov _int13_cdrom.status + 2[bp], ah
 15.5345 +        jnc int13_cdrom_rme_end
 15.5346 +        mov _int13_cdrom.status, #1
 15.5347 +int13_cdrom_rme_end:
 15.5348 +        pop bp
 15.5349 +      ASM_END
 15.5350 +
 15.5351 +      if (status != 0) {
 15.5352 +        SET_AH(0xb1); // media locked
 15.5353 +        goto int13_fail_noah;
 15.5354 +      }
 15.5355 +
 15.5356 +      goto int13_success;
 15.5357 +      break;
 15.5358 +
 15.5359 +    case 0x48: // IBM/MS get drive parameters
 15.5360 +      size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
 15.5361 +
 15.5362 +      // Buffer is too small
 15.5363 +      if(size < 0x1a) 
 15.5364 +        goto int13_fail;
 15.5365 +
 15.5366 +      // EDD 1.x
 15.5367 +      if(size >= 0x1a) {
 15.5368 +        Bit16u   cylinders, heads, spt, blksize;
 15.5369 +
 15.5370 +        blksize   = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
 15.5371 +
 15.5372 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
 15.5373 +        write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
 15.5374 +        write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
 15.5375 +        write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
 15.5376 +        write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
 15.5377 +        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff);  // FIXME should be Bit64
 15.5378 +        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);  
 15.5379 +        write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);  
 15.5380 +        }
 15.5381 +
 15.5382 +      // EDD 2.x
 15.5383 +      if(size >= 0x1e) {
 15.5384 +        Bit8u  channel, dev, irq, mode, checksum, i;
 15.5385 +        Bit16u iobase1, iobase2, options;
 15.5386 +
 15.5387 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
 15.5388 +
 15.5389 +        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);  
 15.5390 +        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);  
 15.5391 +
 15.5392 +        // Fill in dpte
 15.5393 +        channel = device / 2;
 15.5394 +        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
 15.5395 +        iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
 15.5396 +        irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
 15.5397 +        mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
 15.5398 +
 15.5399 +        // FIXME atapi device
 15.5400 +        options  = (1<<4); // lba translation
 15.5401 +        options |= (1<<5); // removable device
 15.5402 +        options |= (1<<6); // atapi device
 15.5403 +        options |= (mode==ATA_MODE_PIO32?1:0<<7);
 15.5404 +
 15.5405 +        write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
 15.5406 +        write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
 15.5407 +        write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
 15.5408 +        write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
 15.5409 +        write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
 15.5410 +        write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
 15.5411 +        write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
 15.5412 +        write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
 15.5413 +        write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
 15.5414 +        write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
 15.5415 +        write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
 15.5416 +
 15.5417 +        checksum=0;
 15.5418 +        for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
 15.5419 +        checksum = ~checksum;
 15.5420 +        write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
 15.5421 +        }
 15.5422 +
 15.5423 +      // EDD 3.x
 15.5424 +      if(size >= 0x42) {
 15.5425 +        Bit8u channel, iface, checksum, i;
 15.5426 +        Bit16u iobase1;
 15.5427 +
 15.5428 +        channel = device / 2;
 15.5429 +        iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
 15.5430 +        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
 15.5431 +
 15.5432 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
 15.5433 +        write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
 15.5434 +        write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
 15.5435 +        write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
 15.5436 +        write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
 15.5437 +
 15.5438 +        if (iface==ATA_IFACE_ISA) {
 15.5439 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
 15.5440 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
 15.5441 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
 15.5442 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
 15.5443 +          }
 15.5444 +        else { 
 15.5445 +          // FIXME PCI
 15.5446 +          }
 15.5447 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
 15.5448 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
 15.5449 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
 15.5450 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
 15.5451 +
 15.5452 +        if (iface==ATA_IFACE_ISA) {
 15.5453 +          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
 15.5454 +          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
 15.5455 +          write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
 15.5456 +          }
 15.5457 +        else { 
 15.5458 +          // FIXME PCI
 15.5459 +          }
 15.5460 +        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
 15.5461 +        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
 15.5462 +        write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
 15.5463 +        write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
 15.5464 +
 15.5465 +        checksum=0;
 15.5466 +        for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
 15.5467 +        checksum = ~checksum;
 15.5468 +        write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
 15.5469 +        }
 15.5470 +
 15.5471 +      goto int13_success;
 15.5472 +      break;
 15.5473 +
 15.5474 +    case 0x49: // IBM/MS extended media change
 15.5475 +      // always send changed ??
 15.5476 +      SET_AH(06);
 15.5477 +      goto int13_fail_nostatus;
 15.5478 +      break;
 15.5479 +      
 15.5480 +    case 0x4e: // // IBM/MS set hardware configuration
 15.5481 +      // DMA, prefetch, PIO maximum not supported
 15.5482 +      switch (GET_AL()) {
 15.5483 +        case 0x01:
 15.5484 +        case 0x03:
 15.5485 +        case 0x04:
 15.5486 +        case 0x06:
 15.5487 +          goto int13_success;
 15.5488 +          break;
 15.5489 +        default :
 15.5490 +          goto int13_fail;
 15.5491 +        }
 15.5492 +      break;
 15.5493 +
 15.5494 +    // all those functions return unimplemented
 15.5495 +    case 0x02: /* read sectors */
 15.5496 +    case 0x04: /* verify sectors */
 15.5497 +    case 0x08: /* read disk drive parameters */
 15.5498 +    case 0x0a: /* read disk sectors with ECC */
 15.5499 +    case 0x0b: /* write disk sectors with ECC */
 15.5500 +    case 0x18: /* set media type for format */
 15.5501 +    case 0x50: // ? - send packet command
 15.5502 +    default:
 15.5503 +      BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
 15.5504 +      goto int13_fail;
 15.5505 +      break;
 15.5506 +    }
 15.5507 +
 15.5508 +int13_fail:
 15.5509 +    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
 15.5510 +int13_fail_noah:
 15.5511 +    SET_DISK_RET_STATUS(GET_AH());
 15.5512 +int13_fail_nostatus:
 15.5513 +    SET_CF();     // error occurred
 15.5514 +    return;
 15.5515 +
 15.5516 +int13_success:
 15.5517 +    SET_AH(0x00); // no error
 15.5518 +int13_success_noah:
 15.5519 +    SET_DISK_RET_STATUS(0x00);
 15.5520 +    CLEAR_CF();   // no error
 15.5521 +    return;
 15.5522 +}
 15.5523 +
 15.5524 +// ---------------------------------------------------------------------------
 15.5525 +// End of int13 for cdrom
 15.5526 +// ---------------------------------------------------------------------------
 15.5527 +
 15.5528 +#if BX_ELTORITO_BOOT
 15.5529 +// ---------------------------------------------------------------------------
 15.5530 +// Start of int13 for eltorito functions
 15.5531 +// ---------------------------------------------------------------------------
 15.5532 +
 15.5533 +  void
 15.5534 +int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
 15.5535 +  Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
 15.5536 +{
 15.5537 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.5538 +
 15.5539 +  BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
 15.5540 +  // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
 15.5541 +  
 15.5542 +  switch (GET_AH()) {
 15.5543 +
 15.5544 +    // FIXME ElTorito Various. Should be implemented
 15.5545 +    case 0x4a: // ElTorito - Initiate disk emu
 15.5546 +    case 0x4c: // ElTorito - Initiate disk emu and boot
 15.5547 +    case 0x4d: // ElTorito - Return Boot catalog
 15.5548 +      BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
 15.5549 +      goto int13_fail;
 15.5550 +      break;
 15.5551 +
 15.5552 +    case 0x4b: // ElTorito - Terminate disk emu
 15.5553 +      // FIXME ElTorito Hardcoded
 15.5554 +      write_byte(DS,SI+0x00,0x13);
 15.5555 +      write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
 15.5556 +      write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
 15.5557 +      write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
 15.5558 +      write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
 15.5559 +      write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
 15.5560 +      write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
 15.5561 +      write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
 15.5562 +      write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
 15.5563 +      write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
 15.5564 +      write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
 15.5565 +      write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
 15.5566 +
 15.5567 +      // If we have to terminate emulation
 15.5568 +      if(GET_AL() == 0x00) {
 15.5569 +        // FIXME ElTorito Various. Should be handled accordingly to spec
 15.5570 +        write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
 15.5571 +        }
 15.5572 +
 15.5573 +      goto int13_success;
 15.5574 +      break;
 15.5575 +
 15.5576 +    default:
 15.5577 +      BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
 15.5578 +      goto int13_fail;
 15.5579 +      break;
 15.5580 +    }
 15.5581 +
 15.5582 +int13_fail:
 15.5583 +    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
 15.5584 +    SET_DISK_RET_STATUS(GET_AH());
 15.5585 +    SET_CF();     // error occurred
 15.5586 +    return;
 15.5587 +
 15.5588 +int13_success:
 15.5589 +    SET_AH(0x00); // no error
 15.5590 +    SET_DISK_RET_STATUS(0x00);
 15.5591 +    CLEAR_CF();   // no error
 15.5592 +    return;
 15.5593 +}
 15.5594 +
 15.5595 +// ---------------------------------------------------------------------------
 15.5596 +// End of int13 for eltorito functions
 15.5597 +// ---------------------------------------------------------------------------
 15.5598 +
 15.5599 +// ---------------------------------------------------------------------------
 15.5600 +// Start of int13 when emulating a device from the cd
 15.5601 +// ---------------------------------------------------------------------------
 15.5602 +
 15.5603 +  void
 15.5604 +int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
 15.5605 +  Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
 15.5606 +{
 15.5607 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.5608 +  Bit8u  device, status;
 15.5609 +  Bit16u vheads, vspt, vcylinders;
 15.5610 +  Bit16u head, sector, cylinder, nbsectors;
 15.5611 +  Bit32u vlba, ilba, slba, elba;
 15.5612 +  Bit16u before, segment, offset;
 15.5613 +  Bit8u  atacmd[12];
 15.5614 +
 15.5615 +  BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
 15.5616 +  //BX_DEBUG_INT13_ET("int13_cdemu: SS=%04x ES=%04x DI=%04x SI=%04x\n", get_SS(), ES, DI, SI);
 15.5617 +  
 15.5618 +  /* at this point, we are emulating a floppy/harddisk */
 15.5619 +  
 15.5620 +  // Recompute the device number 
 15.5621 +  device  = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
 15.5622 +  device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
 15.5623 +
 15.5624 +  SET_DISK_RET_STATUS(0x00);
 15.5625 +
 15.5626 +  /* basic checks : emulation should be active, dl should equal the emulated drive */
 15.5627 +  if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
 15.5628 +   || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
 15.5629 +    BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
 15.5630 +    goto int13_fail;
 15.5631 +    }
 15.5632 +  
 15.5633 +  switch (GET_AH()) {
 15.5634 +
 15.5635 +    // all those functions return SUCCESS
 15.5636 +    case 0x00: /* disk controller reset */
 15.5637 +    case 0x09: /* initialize drive parameters */
 15.5638 +    case 0x0c: /* seek to specified cylinder */
 15.5639 +    case 0x0d: /* alternate disk reset */  // FIXME ElTorito Various. should really reset ?
 15.5640 +    case 0x10: /* check drive ready */     // FIXME ElTorito Various. should check if ready ?
 15.5641 +    case 0x11: /* recalibrate */      
 15.5642 +    case 0x14: /* controller internal diagnostic */
 15.5643 +    case 0x16: /* detect disk change */
 15.5644 +      goto int13_success;
 15.5645 +      break;
 15.5646 +
 15.5647 +    // all those functions return disk write-protected
 15.5648 +    case 0x03: /* write disk sectors */
 15.5649 +    case 0x05: /* format disk track */
 15.5650 +      SET_AH(0x03);
 15.5651 +      goto int13_fail_noah;
 15.5652 +      break;
 15.5653 +
 15.5654 +    case 0x01: /* read disk status */
 15.5655 +      status=read_byte(0x0040, 0x0074);
 15.5656 +      SET_AH(status);
 15.5657 +      SET_DISK_RET_STATUS(0);
 15.5658 +
 15.5659 +      /* set CF if error status read */
 15.5660 +      if (status) goto int13_fail_nostatus;
 15.5661 +      else        goto int13_success_noah;
 15.5662 +      break;
 15.5663 +
 15.5664 +    case 0x02: // read disk sectors
 15.5665 +    case 0x04: // verify disk sectors
 15.5666 +      vspt       = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); 
 15.5667 +      vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders); 
 15.5668 +      vheads     = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads); 
 15.5669 +
 15.5670 +      ilba       = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
 15.5671 +
 15.5672 +      sector    = GET_CL() & 0x003f;
 15.5673 +      cylinder  = (GET_CL() & 0x00c0) << 2 | GET_CH();
 15.5674 +      head      = GET_DH();
 15.5675 +      nbsectors = GET_AL();
 15.5676 +      segment   = ES;
 15.5677 +      offset    = BX;
 15.5678 +
 15.5679 +      // no sector to read ?
 15.5680 +      if(nbsectors==0) goto int13_success;
 15.5681 +
 15.5682 +      // sanity checks sco openserver needs this!
 15.5683 +      if ((sector   >  vspt)
 15.5684 +       || (cylinder >= vcylinders)
 15.5685 +       || (head     >= vheads)) {
 15.5686 +        goto int13_fail;
 15.5687 +        }
 15.5688 +
 15.5689 +      // After controls, verify do nothing
 15.5690 +      if (GET_AH() == 0x04) goto int13_success;
 15.5691 +
 15.5692 +      segment = ES+(BX / 16);
 15.5693 +      offset  = BX % 16;
 15.5694 +
 15.5695 +      // calculate the virtual lba inside the image
 15.5696 +      vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
 15.5697 + 
 15.5698 +      // In advance so we don't loose the count
 15.5699 +      SET_AL(nbsectors);
 15.5700 +
 15.5701 +      // start lba on cd
 15.5702 +      slba  = (Bit32u)vlba/4; 
 15.5703 +      before= (Bit16u)vlba%4;
 15.5704 +
 15.5705 +      // end lba on cd
 15.5706 +      elba = (Bit32u)(vlba+nbsectors-1)/4;
 15.5707 +      
 15.5708 +      memsetb(get_SS(),atacmd,0,12);
 15.5709 +      atacmd[0]=0x28;                      // READ command
 15.5710 +      atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
 15.5711 +      atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff);      // Sectors
 15.5712 +      atacmd[2]=(ilba+slba & 0xff000000) >> 24;  // LBA
 15.5713 +      atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
 15.5714 +      atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
 15.5715 +      atacmd[5]=(ilba+slba & 0x000000ff);
 15.5716 +      if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
 15.5717 +        BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
 15.5718 +        SET_AH(0x02);
 15.5719 +        SET_AL(0);
 15.5720 +        goto int13_fail_noah;
 15.5721 +        }
 15.5722 +
 15.5723 +      goto int13_success;
 15.5724 +      break;
 15.5725 +
 15.5726 +    case 0x08: /* read disk drive parameters */
 15.5727 +      vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); 
 15.5728 +      vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1; 
 15.5729 +      vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1; 
 15.5730 + 
 15.5731 +      SET_AL( 0x00 );
 15.5732 +      SET_BL( 0x00 );
 15.5733 +      SET_CH( vcylinders & 0xff );
 15.5734 +      SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt  & 0x3f ));
 15.5735 +      SET_DH( vheads );
 15.5736 +      SET_DL( 0x02 );   // FIXME ElTorito Various. should send the real count of drives 1 or 2
 15.5737 +                        // FIXME ElTorito Harddisk. should send the HD count
 15.5738 + 
 15.5739 +      switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
 15.5740 +        case 0x01: SET_BL( 0x02 ); break;
 15.5741 +        case 0x02: SET_BL( 0x04 ); break;
 15.5742 +        case 0x03: SET_BL( 0x06 ); break;
 15.5743 +        }
 15.5744 +
 15.5745 +ASM_START
 15.5746 +      push bp
 15.5747 +      mov  bp, sp
 15.5748 +      mov ax, #diskette_param_table2
 15.5749 +      mov _int13_cdemu.DI+2[bp], ax
 15.5750 +      mov _int13_cdemu.ES+2[bp], cs
 15.5751 +      pop  bp
 15.5752 +ASM_END
 15.5753 +      goto int13_success;
 15.5754 +      break;
 15.5755 +
 15.5756 +    case 0x15: /* read disk drive size */
 15.5757 +      // FIXME ElTorito Harddisk. What geometry to send ?
 15.5758 +      SET_AH(0x03);
 15.5759 +      goto int13_success_noah;
 15.5760 +      break;
 15.5761 +
 15.5762 +    // all those functions return unimplemented
 15.5763 +    case 0x0a: /* read disk sectors with ECC */
 15.5764 +    case 0x0b: /* write disk sectors with ECC */
 15.5765 +    case 0x18: /* set media type for format */
 15.5766 +    case 0x41: // IBM/MS installation check
 15.5767 +      // FIXME ElTorito Harddisk. Darwin would like to use EDD
 15.5768 +    case 0x42: // IBM/MS extended read
 15.5769 +    case 0x43: // IBM/MS extended write
 15.5770 +    case 0x44: // IBM/MS verify sectors
 15.5771 +    case 0x45: // IBM/MS lock/unlock drive
 15.5772 +    case 0x46: // IBM/MS eject media
 15.5773 +    case 0x47: // IBM/MS extended seek
 15.5774 +    case 0x48: // IBM/MS get drive parameters 
 15.5775 +    case 0x49: // IBM/MS extended media change
 15.5776 +    case 0x4e: // ? - set hardware configuration
 15.5777 +    case 0x50: // ? - send packet command
 15.5778 +    default:
 15.5779 +      BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
 15.5780 +      goto int13_fail;
 15.5781 +      break;
 15.5782 +    }
 15.5783 +
 15.5784 +int13_fail:
 15.5785 +    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
 15.5786 +int13_fail_noah:
 15.5787 +    SET_DISK_RET_STATUS(GET_AH());
 15.5788 +int13_fail_nostatus:
 15.5789 +    SET_CF();     // error occurred
 15.5790 +    return;
 15.5791 +
 15.5792 +int13_success:
 15.5793 +    SET_AH(0x00); // no error
 15.5794 +int13_success_noah:
 15.5795 +    SET_DISK_RET_STATUS(0x00);
 15.5796 +    CLEAR_CF();   // no error
 15.5797 +    return;
 15.5798 +}
 15.5799 +
 15.5800 +// ---------------------------------------------------------------------------
 15.5801 +// End of int13 when emulating a device from the cd
 15.5802 +// ---------------------------------------------------------------------------
 15.5803 +
 15.5804 +#endif // BX_ELTORITO_BOOT
 15.5805 +
 15.5806 +#else //BX_USE_ATADRV
 15.5807 +
 15.5808 +  void
 15.5809 +outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
 15.5810 +  Bit16u cylinder;
 15.5811 +  Bit16u hd_heads;
 15.5812 +  Bit16u head;
 15.5813 +  Bit16u hd_sectors;
 15.5814 +  Bit16u sector;
 15.5815 +  Bit16u dl;
 15.5816 +{
 15.5817 +ASM_START
 15.5818 +        push   bp
 15.5819 +        mov    bp, sp
 15.5820 +        push   eax
 15.5821 +        push   ebx
 15.5822 +        push   edx
 15.5823 +        xor    eax,eax
 15.5824 +        mov    ax,4[bp]  // cylinder
 15.5825 +        xor    ebx,ebx
 15.5826 +        mov    bl,6[bp]  // hd_heads
 15.5827 +        imul   ebx
 15.5828 +
 15.5829 +        mov    bl,8[bp]  // head
 15.5830 +        add    eax,ebx
 15.5831 +        mov    bl,10[bp] // hd_sectors
 15.5832 +        imul   ebx
 15.5833 +        mov    bl,12[bp] // sector
 15.5834 +        add    eax,ebx
 15.5835 +
 15.5836 +        dec    eax
 15.5837 +        mov    dx,#0x1f3
 15.5838 +        out    dx,al
 15.5839 +        mov    dx,#0x1f4
 15.5840 +        mov    al,ah
 15.5841 +        out    dx,al
 15.5842 +        shr    eax,#16
 15.5843 +        mov    dx,#0x1f5
 15.5844 +        out    dx,al
 15.5845 +        and    ah,#0xf
 15.5846 +        mov    bl,14[bp] // dl
 15.5847 +        and    bl,#1
 15.5848 +        shl    bl,#4
 15.5849 +        or     ah,bl
 15.5850 +        or     ah,#0xe0
 15.5851 +        mov    al,ah
 15.5852 +        mov    dx,#0x01f6
 15.5853 +        out    dx,al
 15.5854 +        pop    edx
 15.5855 +        pop    ebx
 15.5856 +        pop    eax
 15.5857 +        pop    bp
 15.5858 +ASM_END
 15.5859 +}
 15.5860 +
 15.5861 +  void
 15.5862 +int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
 15.5863 +  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
 15.5864 +{
 15.5865 +  Bit8u    drive, num_sectors, sector, head, status, mod;
 15.5866 +  Bit8u    drive_map;
 15.5867 +  Bit8u    n_drives;
 15.5868 +  Bit16u   cyl_mod, ax;
 15.5869 +  Bit16u   max_cylinder, cylinder, total_sectors;
 15.5870 +  Bit16u   hd_cylinders;
 15.5871 +  Bit8u    hd_heads, hd_sectors;
 15.5872 +  Bit16u   val16;
 15.5873 +  Bit8u    sector_count;
 15.5874 +  unsigned int i;
 15.5875 +  Bit16u   tempbx;
 15.5876 +  Bit16u   dpsize;
 15.5877 +
 15.5878 +  Bit16u   count, segment, offset;
 15.5879 +  Bit32u   lba;
 15.5880 +  Bit16u   error;
 15.5881 +
 15.5882 +  BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
 15.5883 +
 15.5884 +  write_byte(0x0040, 0x008e, 0);  // clear completion flag
 15.5885 +
 15.5886 +  /* at this point, DL is >= 0x80 to be passed from the floppy int13h
 15.5887 +     handler code */
 15.5888 +  /* check how many disks first (cmos reg 0x12), return an error if
 15.5889 +     drive not present */
 15.5890 +  drive_map = inb_cmos(0x12);
 15.5891 +  drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
 15.5892 +              (((drive_map & 0x0f)==0) ? 0 : 2);
 15.5893 +  n_drives = (drive_map==0) ? 0 :
 15.5894 +    ((drive_map==3) ? 2 : 1);
 15.5895 +
 15.5896 +  if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
 15.5897 +    SET_AH(0x01);
 15.5898 +    SET_DISK_RET_STATUS(0x01);
 15.5899 +    SET_CF(); /* error occurred */
 15.5900 +    return;
 15.5901 +    }
 15.5902 +
 15.5903 +  switch (GET_AH()) {
 15.5904 +
 15.5905 +    case 0x00: /* disk controller reset */
 15.5906 +BX_DEBUG_INT13_HD("int13_f00\n");
 15.5907 +
 15.5908 +      SET_AH(0);
 15.5909 +      SET_DISK_RET_STATUS(0);
 15.5910 +      set_diskette_ret_status(0);
 15.5911 +      set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
 15.5912 +      set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
 15.5913 +      CLEAR_CF(); /* successful */
 15.5914 +      return;
 15.5915 +      break;
 15.5916 +
 15.5917 +    case 0x01: /* read disk status */
 15.5918 +BX_DEBUG_INT13_HD("int13_f01\n");
 15.5919 +      status = read_byte(0x0040, 0x0074);
 15.5920 +      SET_AH(status);
 15.5921 +      SET_DISK_RET_STATUS(0);
 15.5922 +      /* set CF if error status read */
 15.5923 +      if (status) SET_CF();
 15.5924 +      else        CLEAR_CF();
 15.5925 +      return;
 15.5926 +      break;
 15.5927 +
 15.5928 +    case 0x04: // verify disk sectors
 15.5929 +    case 0x02: // read disk sectors
 15.5930 +      drive = GET_ELDL();
 15.5931 +      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
 15.5932 +
 15.5933 +      num_sectors = GET_AL();
 15.5934 +      cylinder    = (GET_CL() & 0x00c0) << 2 | GET_CH();
 15.5935 +      sector      = (GET_CL() & 0x3f);
 15.5936 +      head        = GET_DH();
 15.5937 +
 15.5938 +
 15.5939 +      if (hd_cylinders > 1024) {
 15.5940 +        if (hd_cylinders <= 2048) {
 15.5941 +          cylinder <<= 1;
 15.5942 +          }
 15.5943 +        else if (hd_cylinders <= 4096) {
 15.5944 +          cylinder <<= 2;
 15.5945 +          }
 15.5946 +        else if (hd_cylinders <= 8192) {
 15.5947 +          cylinder <<= 3;
 15.5948 +          }
 15.5949 +        else { // hd_cylinders <= 16384
 15.5950 +          cylinder <<= 4;
 15.5951 +          }
 15.5952 +
 15.5953 +        ax = head / hd_heads;
 15.5954 +        cyl_mod = ax & 0xff;
 15.5955 +        head    = ax >> 8;
 15.5956 +        cylinder |= cyl_mod;
 15.5957 +        }
 15.5958 +
 15.5959 +      if ( (cylinder >= hd_cylinders) ||
 15.5960 +           (sector > hd_sectors) ||
 15.5961 +           (head >= hd_heads) ) {
 15.5962 +        SET_AH(1);
 15.5963 +        SET_DISK_RET_STATUS(1);
 15.5964 +        SET_CF(); /* error occurred */
 15.5965 +        return;
 15.5966 +        }
 15.5967 +
 15.5968 +      if ( (num_sectors > 128) || (num_sectors == 0) )
 15.5969 +        BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
 15.5970 +
 15.5971 +      if (head > 15)
 15.5972 +        BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
 15.5973 +
 15.5974 +      if ( GET_AH() == 0x04 ) {
 15.5975 +        SET_AH(0);
 15.5976 +        SET_DISK_RET_STATUS(0);
 15.5977 +        CLEAR_CF();
 15.5978 +        return;
 15.5979 +        }
 15.5980 +
 15.5981 +      status = inb(0x1f7);
 15.5982 +      if (status & 0x80) {
 15.5983 +        BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
 15.5984 +        }
 15.5985 +      outb(0x01f2, num_sectors);
 15.5986 +      /* activate LBA? (tomv) */
 15.5987 +      if (hd_heads > 16) {
 15.5988 +BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
 15.5989 +        outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
 15.5990 +        }
 15.5991 +      else {
 15.5992 +        outb(0x01f3, sector);
 15.5993 +        outb(0x01f4, cylinder & 0x00ff);
 15.5994 +        outb(0x01f5, cylinder >> 8);
 15.5995 +        outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
 15.5996 +        }
 15.5997 +      outb(0x01f7, 0x20);
 15.5998 +
 15.5999 +      while (1) {
 15.6000 +        status = inb(0x1f7);
 15.6001 +        if ( !(status & 0x80) ) break;
 15.6002 +        }
 15.6003 +
 15.6004 +      if (status & 0x01) {
 15.6005 +        BX_PANIC("hard drive BIOS:(read/verify) read error\n");
 15.6006 +      } else if ( !(status & 0x08) ) {
 15.6007 +        BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
 15.6008 +        BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
 15.6009 +      }
 15.6010 +
 15.6011 +      sector_count = 0;
 15.6012 +      tempbx = BX;
 15.6013 +
 15.6014 +ASM_START
 15.6015 +  sti  ;; enable higher priority interrupts
 15.6016 +ASM_END
 15.6017 +
 15.6018 +      while (1) {
 15.6019 +ASM_START
 15.6020 +        ;; store temp bx in real DI register
 15.6021 +        push bp
 15.6022 +        mov  bp, sp
 15.6023 +        mov  di, _int13_harddisk.tempbx + 2 [bp]
 15.6024 +        pop  bp
 15.6025 +
 15.6026 +        ;; adjust if there will be an overrun
 15.6027 +        cmp   di, #0xfe00
 15.6028 +        jbe   i13_f02_no_adjust
 15.6029 +i13_f02_adjust:
 15.6030 +        sub   di, #0x0200 ; sub 512 bytes from offset
 15.6031 +        mov   ax, es
 15.6032 +        add   ax, #0x0020 ; add 512 to segment
 15.6033 +        mov   es, ax
 15.6034 +
 15.6035 +i13_f02_no_adjust:
 15.6036 +        mov  cx, #0x0100   ;; counter (256 words = 512b)
 15.6037 +        mov  dx, #0x01f0  ;; AT data read port
 15.6038 +
 15.6039 +        rep
 15.6040 +          insw ;; CX words transfered from port(DX) to ES:[DI]
 15.6041 +
 15.6042 +i13_f02_done:
 15.6043 +        ;; store real DI register back to temp bx
 15.6044 +        push bp
 15.6045 +        mov  bp, sp
 15.6046 +        mov  _int13_harddisk.tempbx + 2 [bp], di
 15.6047 +        pop  bp
 15.6048 +ASM_END
 15.6049 +
 15.6050 +        sector_count++;
 15.6051 +        num_sectors--;
 15.6052 +        if (num_sectors == 0) {
 15.6053 +          status = inb(0x1f7);
 15.6054 +          if ( (status & 0xc9) != 0x40 )
 15.6055 +            BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
 15.6056 +          break;
 15.6057 +          }
 15.6058 +        else {
 15.6059 +          status = inb(0x1f7);
 15.6060 +          if ( (status & 0xc9) != 0x48 )
 15.6061 +            BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
 15.6062 +          continue;
 15.6063 +          }
 15.6064 +        }
 15.6065 +
 15.6066 +      SET_AH(0);
 15.6067 +      SET_DISK_RET_STATUS(0);
 15.6068 +      SET_AL(sector_count);
 15.6069 +      CLEAR_CF(); /* successful */
 15.6070 +      return;
 15.6071 +      break;
 15.6072 +
 15.6073 +
 15.6074 +    case 0x03: /* write disk sectors */
 15.6075 +BX_DEBUG_INT13_HD("int13_f03\n");
 15.6076 +      drive = GET_ELDL ();
 15.6077 +      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
 15.6078 +
 15.6079 +      num_sectors = GET_AL();
 15.6080 +      cylinder    = GET_CH();
 15.6081 +      cylinder    |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
 15.6082 +      sector      = (GET_CL() & 0x3f);
 15.6083 +      head        = GET_DH();
 15.6084 +
 15.6085 +      if (hd_cylinders > 1024) {
 15.6086 +        if (hd_cylinders <= 2048) {
 15.6087 +          cylinder <<= 1;
 15.6088 +          }
 15.6089 +        else if (hd_cylinders <= 4096) {
 15.6090 +          cylinder <<= 2;
 15.6091 +          }
 15.6092 +        else if (hd_cylinders <= 8192) {
 15.6093 +          cylinder <<= 3;
 15.6094 +          }
 15.6095 +        else { // hd_cylinders <= 16384
 15.6096 +          cylinder <<= 4;
 15.6097 +          }
 15.6098 +
 15.6099 +        ax = head / hd_heads;
 15.6100 +        cyl_mod = ax & 0xff;
 15.6101 +        head    = ax >> 8;
 15.6102 +        cylinder |= cyl_mod;
 15.6103 +        }
 15.6104 +
 15.6105 +      if ( (cylinder >= hd_cylinders) ||
 15.6106 +           (sector > hd_sectors) ||
 15.6107 +           (head >= hd_heads) ) {
 15.6108 +        SET_AH( 1);
 15.6109 +        SET_DISK_RET_STATUS(1);
 15.6110 +        SET_CF(); /* error occurred */
 15.6111 +        return;
 15.6112 +        }
 15.6113 +
 15.6114 +      if ( (num_sectors > 128) || (num_sectors == 0) )
 15.6115 +        BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
 15.6116 +
 15.6117 +      if (head > 15)
 15.6118 +        BX_PANIC("hard drive BIOS:(read) head > 15\n");
 15.6119 +
 15.6120 +      status = inb(0x1f7);
 15.6121 +      if (status & 0x80) {
 15.6122 +        BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
 15.6123 +        }
 15.6124 +// should check for Drive Ready Bit also in status reg
 15.6125 +      outb(0x01f2, num_sectors);
 15.6126 +
 15.6127 +      /* activate LBA? (tomv) */
 15.6128 +      if (hd_heads > 16) {
 15.6129 +BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
 15.6130 +        outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
 15.6131 +        }
 15.6132 +      else {
 15.6133 +        outb(0x01f3, sector);
 15.6134 +        outb(0x01f4, cylinder & 0x00ff);
 15.6135 +        outb(0x01f5, cylinder >> 8);
 15.6136 +        outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
 15.6137 +        }
 15.6138 +      outb(0x01f7, 0x30);
 15.6139 +
 15.6140 +      // wait for busy bit to turn off after seeking
 15.6141 +      while (1) {
 15.6142 +        status = inb(0x1f7);
 15.6143 +        if ( !(status & 0x80) ) break;
 15.6144 +        }
 15.6145 +
 15.6146 +      if ( !(status & 0x08) ) {
 15.6147 +        BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
 15.6148 +        BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
 15.6149 +        }
 15.6150 +
 15.6151 +      sector_count = 0;
 15.6152 +      tempbx = BX;
 15.6153 +
 15.6154 +ASM_START
 15.6155 +  sti  ;; enable higher priority interrupts
 15.6156 +ASM_END
 15.6157 +
 15.6158 +      while (1) {
 15.6159 +ASM_START
 15.6160 +        ;; store temp bx in real SI register
 15.6161 +        push bp
 15.6162 +        mov  bp, sp
 15.6163 +        mov  si, _int13_harddisk.tempbx + 2 [bp]
 15.6164 +        pop  bp
 15.6165 +
 15.6166 +        ;; adjust if there will be an overrun
 15.6167 +        cmp   si, #0xfe00
 15.6168 +        jbe   i13_f03_no_adjust
 15.6169 +i13_f03_adjust:
 15.6170 +        sub   si, #0x0200 ; sub 512 bytes from offset
 15.6171 +        mov   ax, es
 15.6172 +        add   ax, #0x0020 ; add 512 to segment
 15.6173 +        mov   es, ax
 15.6174 +
 15.6175 +i13_f03_no_adjust:
 15.6176 +        mov  cx, #0x0100   ;; counter (256 words = 512b)
 15.6177 +        mov  dx, #0x01f0  ;; AT data read port
 15.6178 +
 15.6179 +        seg ES
 15.6180 +        rep
 15.6181 +          outsw ;; CX words tranfered from ES:[SI] to port(DX)
 15.6182 +
 15.6183 +        ;; store real SI register back to temp bx
 15.6184 +        push bp
 15.6185 +        mov  bp, sp
 15.6186 +        mov  _int13_harddisk.tempbx + 2 [bp], si
 15.6187 +        pop  bp
 15.6188 +ASM_END
 15.6189 +
 15.6190 +        sector_count++;
 15.6191 +        num_sectors--;
 15.6192 +        if (num_sectors == 0) {
 15.6193 +          status = inb(0x1f7);
 15.6194 +          if ( (status & 0xe9) != 0x40 )
 15.6195 +            BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
 15.6196 +          break;
 15.6197 +          }
 15.6198 +        else {
 15.6199 +          status = inb(0x1f7);
 15.6200 +          if ( (status & 0xc9) != 0x48 )
 15.6201 +            BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
 15.6202 +          continue;
 15.6203 +          }
 15.6204 +        }
 15.6205 +
 15.6206 +      SET_AH(0);
 15.6207 +      SET_DISK_RET_STATUS(0);
 15.6208 +      SET_AL(sector_count);
 15.6209 +      CLEAR_CF(); /* successful */
 15.6210 +      return;
 15.6211 +      break;
 15.6212 +
 15.6213 +    case 0x05: /* format disk track */
 15.6214 +BX_DEBUG_INT13_HD("int13_f05\n");
 15.6215 +      BX_PANIC("format disk track called\n");
 15.6216 +      /* nop */
 15.6217 +      SET_AH(0);
 15.6218 +      SET_DISK_RET_STATUS(0);
 15.6219 +      CLEAR_CF(); /* successful */
 15.6220 +      return;
 15.6221 +      break;
 15.6222 +
 15.6223 +    case 0x08: /* read disk drive parameters */
 15.6224 +BX_DEBUG_INT13_HD("int13_f08\n");
 15.6225 +      
 15.6226 +      drive = GET_ELDL ();
 15.6227 +      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
 15.6228 +
 15.6229 +      // translate CHS
 15.6230 +      //
 15.6231 +      if (hd_cylinders <= 1024) {
 15.6232 +        // hd_cylinders >>= 0;
 15.6233 +        // hd_heads <<= 0;
 15.6234 +        }
 15.6235 +      else if (hd_cylinders <= 2048) {
 15.6236 +        hd_cylinders >>= 1;
 15.6237 +        hd_heads <<= 1;
 15.6238 +        }
 15.6239 +      else if (hd_cylinders <= 4096) {
 15.6240 +        hd_cylinders >>= 2;
 15.6241 +        hd_heads <<= 2;
 15.6242 +        }
 15.6243 +      else if (hd_cylinders <= 8192) {
 15.6244 +        hd_cylinders >>= 3;
 15.6245 +        hd_heads <<= 3;
 15.6246 +        }
 15.6247 +      else { // hd_cylinders <= 16384
 15.6248 +        hd_cylinders >>= 4;
 15.6249 +        hd_heads <<= 4;
 15.6250 +        }
 15.6251 +
 15.6252 +      max_cylinder = hd_cylinders - 2; /* 0 based */
 15.6253 +      SET_AL(0);
 15.6254 +      SET_CH(max_cylinder & 0xff);
 15.6255 +      SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
 15.6256 +      SET_DH(hd_heads - 1);
 15.6257 +      SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
 15.6258 +      SET_AH(0);
 15.6259 +      SET_DISK_RET_STATUS(0);
 15.6260 +      CLEAR_CF(); /* successful */
 15.6261 +
 15.6262 +      return;
 15.6263 +      break;
 15.6264 +
 15.6265 +    case 0x09: /* initialize drive parameters */
 15.6266 +BX_DEBUG_INT13_HD("int13_f09\n");
 15.6267 +      SET_AH(0);
 15.6268 +      SET_DISK_RET_STATUS(0);
 15.6269 +      CLEAR_CF(); /* successful */
 15.6270 +      return;
 15.6271 +      break;
 15.6272 +
 15.6273 +    case 0x0a: /* read disk sectors with ECC */
 15.6274 +BX_DEBUG_INT13_HD("int13_f0a\n");
 15.6275 +    case 0x0b: /* write disk sectors with ECC */
 15.6276 +BX_DEBUG_INT13_HD("int13_f0b\n");
 15.6277 +      BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
 15.6278 +      return;
 15.6279 +      break;
 15.6280 +
 15.6281 +    case 0x0c: /* seek to specified cylinder */
 15.6282 +BX_DEBUG_INT13_HD("int13_f0c\n");
 15.6283 +      BX_INFO("int13h function 0ch (seek) not implemented!\n");
 15.6284 +      SET_AH(0);
 15.6285 +      SET_DISK_RET_STATUS(0);
 15.6286 +      CLEAR_CF(); /* successful */
 15.6287 +      return;
 15.6288 +      break;
 15.6289 +
 15.6290 +    case 0x0d: /* alternate disk reset */
 15.6291 +BX_DEBUG_INT13_HD("int13_f0d\n");
 15.6292 +      SET_AH(0);
 15.6293 +      SET_DISK_RET_STATUS(0);
 15.6294 +      CLEAR_CF(); /* successful */
 15.6295 +      return;
 15.6296 +      break;
 15.6297 +
 15.6298 +    case 0x10: /* check drive ready */
 15.6299 +BX_DEBUG_INT13_HD("int13_f10\n");
 15.6300 +      //SET_AH(0);
 15.6301 +      //SET_DISK_RET_STATUS(0);
 15.6302 +      //CLEAR_CF(); /* successful */
 15.6303 +      //return;
 15.6304 +      //break;
 15.6305 +
 15.6306 +      // should look at 40:8E also???
 15.6307 +      status = inb(0x01f7);
 15.6308 +      if ( (status & 0xc0) == 0x40 ) {
 15.6309 +        SET_AH(0);
 15.6310 +        SET_DISK_RET_STATUS(0);
 15.6311 +        CLEAR_CF(); // drive ready
 15.6312 +        return;
 15.6313 +        }
 15.6314 +      else {
 15.6315 +        SET_AH(0xAA);
 15.6316 +        SET_DISK_RET_STATUS(0xAA);
 15.6317 +        SET_CF(); // not ready
 15.6318 +        return;
 15.6319 +        }
 15.6320 +      break;
 15.6321 +
 15.6322 +    case 0x11: /* recalibrate */
 15.6323 +BX_DEBUG_INT13_HD("int13_f11\n");
 15.6324 +      SET_AH(0);
 15.6325 +      SET_DISK_RET_STATUS(0);
 15.6326 +      CLEAR_CF(); /* successful */
 15.6327 +      return;
 15.6328 +      break;
 15.6329 +
 15.6330 +    case 0x14: /* controller internal diagnostic */
 15.6331 +BX_DEBUG_INT13_HD("int13_f14\n");
 15.6332 +      SET_AH(0);
 15.6333 +      SET_DISK_RET_STATUS(0);
 15.6334 +      CLEAR_CF(); /* successful */
 15.6335 +      SET_AL(0);
 15.6336 +      return;
 15.6337 +      break;
 15.6338 +
 15.6339 +    case 0x15: /* read disk drive size */
 15.6340 +      drive = GET_ELDL();
 15.6341 +      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
 15.6342 +ASM_START
 15.6343 +      push bp
 15.6344 +      mov  bp, sp
 15.6345 +      mov  al, _int13_harddisk.hd_heads + 2 [bp]
 15.6346 +      mov  ah, _int13_harddisk.hd_sectors + 2 [bp]
 15.6347 +      mul  al, ah ;; ax = heads * sectors
 15.6348 +      mov  bx, _int13_harddisk.hd_cylinders + 2 [bp]
 15.6349 +      dec  bx     ;; use (cylinders - 1) ???
 15.6350 +      mul  ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
 15.6351 +      ;; now we need to move the 32bit result dx:ax to what the
 15.6352 +      ;; BIOS wants which is cx:dx.
 15.6353 +      ;; and then into CX:DX on the stack
 15.6354 +      mov  _int13_harddisk.CX + 2 [bp], dx
 15.6355 +      mov  _int13_harddisk.DX + 2 [bp], ax
 15.6356 +      pop  bp
 15.6357 +ASM_END
 15.6358 +      SET_AH(3);  // hard disk accessible
 15.6359 +      SET_DISK_RET_STATUS(0); // ??? should this be 0
 15.6360 +      CLEAR_CF(); // successful
 15.6361 +      return;
 15.6362 +      break;
 15.6363 +
 15.6364 +    case 0x18: // set media type for format
 15.6365 +    case 0x41: // IBM/MS 
 15.6366 +    case 0x42: // IBM/MS 
 15.6367 +    case 0x43: // IBM/MS 
 15.6368 +    case 0x44: // IBM/MS 
 15.6369 +    case 0x45: // IBM/MS lock/unlock drive
 15.6370 +    case 0x46: // IBM/MS eject media
 15.6371 +    case 0x47: // IBM/MS extended seek
 15.6372 +    case 0x49: // IBM/MS extended media change
 15.6373 +    case 0x50: // IBM/MS send packet command
 15.6374 +    default:
 15.6375 +      BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
 15.6376 +
 15.6377 +      SET_AH(1);  // code=invalid function in AH or invalid parameter
 15.6378 +      SET_DISK_RET_STATUS(1);
 15.6379 +      SET_CF(); /* unsuccessful */
 15.6380 +      return;
 15.6381 +      break;
 15.6382 +    }
 15.6383 +}
 15.6384 +
 15.6385 +static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
 15.6386 +static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
 15.6387 +
 15.6388 +  void
 15.6389 +get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
 15.6390 +  Bit8u drive;
 15.6391 +  Bit16u *hd_cylinders;
 15.6392 +  Bit8u  *hd_heads;
 15.6393 +  Bit8u  *hd_sectors;
 15.6394 +{
 15.6395 +  Bit8u hd_type;
 15.6396 +  Bit16u ss;
 15.6397 +  Bit16u cylinders;
 15.6398 +  Bit8u iobase;
 15.6399 +
 15.6400 +  ss = get_SS();
 15.6401 +  if (drive == 0x80) {
 15.6402 +    hd_type = inb_cmos(0x12) & 0xf0;
 15.6403 +    if (hd_type != 0xf0)
 15.6404 +      BX_INFO(panic_msg_reg12h,0);
 15.6405 +    hd_type = inb_cmos(0x19); // HD0: extended type
 15.6406 +    if (hd_type != 47)
 15.6407 +      BX_INFO(panic_msg_reg19h,0,0x19);
 15.6408 +    iobase = 0x1b;
 15.6409 +  } else {
 15.6410 +    hd_type = inb_cmos(0x12) & 0x0f;
 15.6411 +    if (hd_type != 0x0f)
 15.6412 +      BX_INFO(panic_msg_reg12h,1);
 15.6413 +    hd_type = inb_cmos(0x1a); // HD0: extended type
 15.6414 +    if (hd_type != 47)
 15.6415 +      BX_INFO(panic_msg_reg19h,0,0x1a);
 15.6416 +    iobase = 0x24;
 15.6417 +  }
 15.6418 +
 15.6419 +  // cylinders
 15.6420 +  cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
 15.6421 +  write_word(ss, hd_cylinders, cylinders);
 15.6422 +
 15.6423 +  // heads
 15.6424 +  write_byte(ss, hd_heads, inb_cmos(iobase+2));
 15.6425 +
 15.6426 +  // sectors per track
 15.6427 +  write_byte(ss, hd_sectors, inb_cmos(iobase+8));
 15.6428 +}
 15.6429 +
 15.6430 +#endif //else BX_USE_ATADRV
 15.6431 +
 15.6432 +
 15.6433 +//////////////////////
 15.6434 +// FLOPPY functions //
 15.6435 +//////////////////////
 15.6436 +
 15.6437 +  bx_bool
 15.6438 +floppy_media_known(drive)
 15.6439 +  Bit16u drive;
 15.6440 +{
 15.6441 +  Bit8u  val8;
 15.6442 +  Bit16u media_state_offset;
 15.6443 +
 15.6444 +  val8 = read_byte(0x0040, 0x003e); // diskette recal status
 15.6445 +  if (drive)
 15.6446 +    val8 >>= 1;
 15.6447 +  val8 &= 0x01;
 15.6448 +  if (val8 == 0)
 15.6449 +    return(0);
 15.6450 +
 15.6451 +  media_state_offset = 0x0090;
 15.6452 +  if (drive)
 15.6453 +    media_state_offset += 1;
 15.6454 +
 15.6455 +  val8 = read_byte(0x0040, media_state_offset);
 15.6456 +  val8 = (val8 >> 4) & 0x01;
 15.6457 +  if (val8 == 0)
 15.6458 +    return(0);
 15.6459 +
 15.6460 +  // check pass, return KNOWN
 15.6461 +  return(1);
 15.6462 +}
 15.6463 +
 15.6464 +  bx_bool
 15.6465 +floppy_media_sense(drive)
 15.6466 +  Bit16u drive;
 15.6467 +{
 15.6468 +  bx_bool retval;
 15.6469 +  Bit16u  media_state_offset;
 15.6470 +  Bit8u   drive_type, config_data, media_state;
 15.6471 +
 15.6472 +  if (floppy_drive_recal(drive) == 0) {
 15.6473 +    return(0);
 15.6474 +    }
 15.6475 +
 15.6476 +  // for now cheat and get drive type from CMOS,
 15.6477 +  // assume media is same as drive type
 15.6478 +
 15.6479 +  // ** config_data **
 15.6480 +  // Bitfields for diskette media control:
 15.6481 +  // Bit(s)  Description (Table M0028)
 15.6482 +  //  7-6  last data rate set by controller
 15.6483 +  //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
 15.6484 +  //  5-4  last diskette drive step rate selected
 15.6485 +  //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
 15.6486 +  //  3-2  {data rate at start of operation}
 15.6487 +  //  1-0  reserved
 15.6488 +
 15.6489 +  // ** media_state **
 15.6490 +  // Bitfields for diskette drive media state:
 15.6491 +  // Bit(s)  Description (Table M0030)
 15.6492 +  //  7-6  data rate
 15.6493 +  //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
 15.6494 +  //  5  double stepping required (e.g. 360kB in 1.2MB)
 15.6495 +  //  4  media type established
 15.6496 +  //  3  drive capable of supporting 4MB media
 15.6497 +  //  2-0  on exit from BIOS, contains
 15.6498 +  //    000 trying 360kB in 360kB
 15.6499 +  //    001 trying 360kB in 1.2MB
 15.6500 +  //    010 trying 1.2MB in 1.2MB
 15.6501 +  //    011 360kB in 360kB established
 15.6502 +  //    100 360kB in 1.2MB established
 15.6503 +  //    101 1.2MB in 1.2MB established
 15.6504 +  //    110 reserved
 15.6505 +  //    111 all other formats/drives
 15.6506 +
 15.6507 +  drive_type = inb_cmos(0x10);
 15.6508 +  if (drive == 0)
 15.6509 +    drive_type >>= 4;
 15.6510 +  else
 15.6511 +    drive_type &= 0x0f;
 15.6512 +  if ( drive_type == 1 ) {
 15.6513 +    // 360K 5.25" drive
 15.6514 +    config_data = 0x00; // 0000 0000
 15.6515 +    media_state = 0x25; // 0010 0101
 15.6516 +    retval = 1;
 15.6517 +    }
 15.6518 +  else if ( drive_type == 2 ) {
 15.6519 +    // 1.2 MB 5.25" drive
 15.6520 +    config_data = 0x00; // 0000 0000
 15.6521 +    media_state = 0x25; // 0010 0101   // need double stepping??? (bit 5)
 15.6522 +    retval = 1;
 15.6523 +    }
 15.6524 +  else if ( drive_type == 3 ) {
 15.6525 +    // 720K 3.5" drive
 15.6526 +    config_data = 0x00; // 0000 0000 ???
 15.6527 +    media_state = 0x17; // 0001 0111
 15.6528 +    retval = 1;
 15.6529 +    }
 15.6530 +  else if ( drive_type == 4 ) {
 15.6531 +    // 1.44 MB 3.5" drive
 15.6532 +    config_data = 0x00; // 0000 0000
 15.6533 +    media_state = 0x17; // 0001 0111
 15.6534 +    retval = 1;
 15.6535 +    }
 15.6536 +  else if ( drive_type == 5 ) {
 15.6537 +    // 2.88 MB 3.5" drive
 15.6538 +    config_data = 0xCC; // 1100 1100
 15.6539 +    media_state = 0xD7; // 1101 0111
 15.6540 +    retval = 1;
 15.6541 +    }
 15.6542 +  //
 15.6543 +  // Extended floppy size uses special cmos setting 
 15.6544 +  else if ( drive_type == 6 ) {
 15.6545 +    // 160k 5.25" drive
 15.6546 +    config_data = 0x00; // 0000 0000
 15.6547 +    media_state = 0x27; // 0010 0111
 15.6548 +    retval = 1;
 15.6549 +    }
 15.6550 +  else if ( drive_type == 7 ) {
 15.6551 +    // 180k 5.25" drive
 15.6552 +    config_data = 0x00; // 0000 0000
 15.6553 +    media_state = 0x27; // 0010 0111
 15.6554 +    retval = 1;
 15.6555 +    }
 15.6556 +  else if ( drive_type == 8 ) {
 15.6557 +    // 320k 5.25" drive
 15.6558 +    config_data = 0x00; // 0000 0000
 15.6559 +    media_state = 0x27; // 0010 0111
 15.6560 +    retval = 1;
 15.6561 +    }
 15.6562 +
 15.6563 +  else {
 15.6564 +    // not recognized
 15.6565 +    config_data = 0x00; // 0000 0000
 15.6566 +    media_state = 0x00; // 0000 0000
 15.6567 +    retval = 0;
 15.6568 +    }
 15.6569 +
 15.6570 +  if (drive == 0)
 15.6571 +    media_state_offset = 0x90;
 15.6572 +  else
 15.6573 +    media_state_offset = 0x91;
 15.6574 +  write_byte(0x0040, 0x008B, config_data);
 15.6575 +  write_byte(0x0040, media_state_offset, media_state);
 15.6576 +
 15.6577 +  return(retval);
 15.6578 +}
 15.6579 +
 15.6580 +  bx_bool
 15.6581 +floppy_drive_recal(drive)
 15.6582 +  Bit16u drive;
 15.6583 +{
 15.6584 +  Bit8u  val8, dor;
 15.6585 +  Bit16u curr_cyl_offset;
 15.6586 +
 15.6587 +  // set 40:3e bit 7 to 0
 15.6588 +  val8 = read_byte(0x0000, 0x043e);
 15.6589 +  val8 &= 0x7f;
 15.6590 +  write_byte(0x0000, 0x043e, val8);
 15.6591 +
 15.6592 +  // turn on motor of selected drive, DMA & int enabled, normal operation
 15.6593 +  if (drive)
 15.6594 +    dor = 0x20;
 15.6595 +  else
 15.6596 +    dor = 0x10;
 15.6597 +  dor |= 0x0c;
 15.6598 +  dor |= drive;
 15.6599 +  outb(0x03f2, dor);
 15.6600 +
 15.6601 +  // reset the disk motor timeout value of INT 08
 15.6602 +  write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
 15.6603 +
 15.6604 +  // check port 3f4 for drive readiness
 15.6605 +  val8 = inb(0x3f4);
 15.6606 +  if ( (val8 & 0xf0) != 0x80 )
 15.6607 +    BX_PANIC("floppy recal:f07: ctrl not ready\n");
 15.6608 +
 15.6609 +  // send Recalibrate command (2 bytes) to controller
 15.6610 +  outb(0x03f5, 0x07);  // 07: Recalibrate
 15.6611 +  outb(0x03f5, drive); // 0=drive0, 1=drive1
 15.6612 +
 15.6613 + // turn on interrupts
 15.6614 +ASM_START
 15.6615 +  sti
 15.6616 +ASM_END
 15.6617 +
 15.6618 +  // wait on 40:3e bit 7 to become 1
 15.6619 +  val8 = (read_byte(0x0000, 0x043e) & 0x80);
 15.6620 +  while ( val8 == 0 ) {
 15.6621 +    val8 = (read_byte(0x0000, 0x043e) & 0x80);
 15.6622 +    }
 15.6623 +
 15.6624 + val8 = 0; // separate asm from while() loop
 15.6625 + // turn off interrupts
 15.6626 +ASM_START
 15.6627 +  cli
 15.6628 +ASM_END
 15.6629 +
 15.6630 +  // set 40:3e bit 7 to 0, and calibrated bit
 15.6631 +  val8 = read_byte(0x0000, 0x043e);
 15.6632 +  val8 &= 0x7f;
 15.6633 +  if (drive) {
 15.6634 +    val8 |= 0x02; // Drive 1 calibrated
 15.6635 +    curr_cyl_offset = 0x0095;
 15.6636 +    }
 15.6637 +  else {
 15.6638 +    val8 |= 0x01; // Drive 0 calibrated
 15.6639 +    curr_cyl_offset = 0x0094;
 15.6640 +    }
 15.6641 +  write_byte(0x0040, 0x003e, val8);
 15.6642 +  write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
 15.6643 +
 15.6644 +  return(1);
 15.6645 +}
 15.6646 +
 15.6647 +
 15.6648 +
 15.6649 +  bx_bool
 15.6650 +floppy_drive_exists(drive)
 15.6651 +  Bit16u drive;
 15.6652 +{
 15.6653 +  Bit8u  drive_type;
 15.6654 +
 15.6655 +  // check CMOS to see if drive exists
 15.6656 +  drive_type = inb_cmos(0x10);
 15.6657 +  if (drive == 0)
 15.6658 +    drive_type >>= 4;
 15.6659 +  else
 15.6660 +    drive_type &= 0x0f;
 15.6661 +  if ( drive_type == 0 )
 15.6662 +    return(0);
 15.6663 +  else
 15.6664 +    return(1);
 15.6665 +}
 15.6666 +
 15.6667 +#if BX_SUPPORT_FLOPPY
 15.6668 +  void
 15.6669 +int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
 15.6670 +  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
 15.6671 +{
 15.6672 +  Bit8u  drive, num_sectors, track, sector, head, status;
 15.6673 +  Bit16u base_address, base_count, base_es;
 15.6674 +  Bit8u  page, mode_register, val8, dor;
 15.6675 +  Bit8u  return_status[7];
 15.6676 +  Bit8u  drive_type, num_floppies, ah;
 15.6677 +  Bit16u es, last_addr;
 15.6678 +
 15.6679 +  BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
 15.6680 +  // BX_DEBUG_INT13_FL("int13_diskette: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), get_DS(), ES, DI, SI);
 15.6681 +
 15.6682 +  ah = GET_AH();
 15.6683 +
 15.6684 +  switch ( ah ) {
 15.6685 +    case 0x00: // diskette controller reset
 15.6686 +BX_DEBUG_INT13_FL("floppy f00\n");
 15.6687 +      drive = GET_ELDL();
 15.6688 +      if (drive > 1) {
 15.6689 +        SET_AH(1); // invalid param
 15.6690 +        set_diskette_ret_status(1);
 15.6691 +        SET_CF();
 15.6692 +        return;
 15.6693 +        }
 15.6694 +      drive_type = inb_cmos(0x10);
 15.6695 +
 15.6696 +      if (drive == 0)
 15.6697 +        drive_type >>= 4;
 15.6698 +      else
 15.6699 +        drive_type &= 0x0f;
 15.6700 +      if (drive_type == 0) {
 15.6701 +        SET_AH(0x80); // drive not responding
 15.6702 +        set_diskette_ret_status(0x80);
 15.6703 +        SET_CF();
 15.6704 +        return;
 15.6705 +        }
 15.6706 +      SET_AH(0);
 15.6707 +      set_diskette_ret_status(0);
 15.6708 +      CLEAR_CF(); // successful
 15.6709 +      set_diskette_current_cyl(drive, 0); // current cylinder
 15.6710 +      return;
 15.6711 +
 15.6712 +    case 0x01: // Read Diskette Status
 15.6713 +      CLEAR_CF();
 15.6714 +      val8 = read_byte(0x0000, 0x0441);
 15.6715 +      SET_AH(val8);
 15.6716 +      if (val8) {
 15.6717 +        SET_CF();
 15.6718 +        }
 15.6719 +      return;
 15.6720 +
 15.6721 +    case 0x02: // Read Diskette Sectors
 15.6722 +    case 0x03: // Write Diskette Sectors
 15.6723 +    case 0x04: // Verify Diskette Sectors
 15.6724 +      num_sectors = GET_AL();
 15.6725 +      track       = GET_CH();
 15.6726 +      sector      = GET_CL();
 15.6727 +      head        = GET_DH();
 15.6728 +      drive       = GET_ELDL();
 15.6729 +
 15.6730 +      if ( (drive > 1) || (head > 1) ||
 15.6731 +           (num_sectors == 0) || (num_sectors > 72) ) {
 15.6732 +BX_INFO("floppy: drive>1 || head>1 ...\n");
 15.6733 +        SET_AH(1);
 15.6734 +        set_diskette_ret_status(1);
 15.6735 +        SET_AL(0); // no sectors read
 15.6736 +        SET_CF(); // error occurred
 15.6737 +        return;
 15.6738 +        }
 15.6739 +
 15.6740 +      // see if drive exists
 15.6741 +      if (floppy_drive_exists(drive) == 0) {
 15.6742 +        SET_AH(0x80); // not responding
 15.6743 +        set_diskette_ret_status(0x80);
 15.6744 +        SET_AL(0); // no sectors read
 15.6745 +        SET_CF(); // error occurred
 15.6746 +        return;
 15.6747 +        }
 15.6748 +
 15.6749 +      // see if media in drive, and type is known
 15.6750 +      if (floppy_media_known(drive) == 0) {
 15.6751 +        if (floppy_media_sense(drive) == 0) {
 15.6752 +          SET_AH(0x0C); // Media type not found
 15.6753 +          set_diskette_ret_status(0x0C);
 15.6754 +          SET_AL(0); // no sectors read
 15.6755 +          SET_CF(); // error occurred
 15.6756 +          return;
 15.6757 +          }
 15.6758 +        }
 15.6759 +
 15.6760 +      if (ah == 0x02) {
 15.6761 +        // Read Diskette Sectors
 15.6762 +
 15.6763 +        //-----------------------------------
 15.6764 +        // set up DMA controller for transfer
 15.6765 +        //-----------------------------------
 15.6766 +
 15.6767 +        // es:bx = pointer to where to place information from diskette
 15.6768 +        // port 04: DMA-1 base and current address, channel 2
 15.6769 +        // port 05: DMA-1 base and current count, channel 2
 15.6770 +        page = (ES >> 12);   // upper 4 bits
 15.6771 +        base_es = (ES << 4); // lower 16bits contributed by ES
 15.6772 +        base_address = base_es + BX; // lower 16 bits of address
 15.6773 +                                     // contributed by ES:BX
 15.6774 +        if ( base_address < base_es ) {
 15.6775 +          // in case of carry, adjust page by 1
 15.6776 +          page++;
 15.6777 +          }
 15.6778 +        base_count = (num_sectors * 512) - 1;
 15.6779 +
 15.6780 +        // check for 64K boundary overrun
 15.6781 +        last_addr = base_address + base_count;
 15.6782 +        if (last_addr < base_address) {
 15.6783 +          SET_AH(0x09);
 15.6784 +          set_diskette_ret_status(0x09);
 15.6785 +          SET_AL(0); // no sectors read
 15.6786 +          SET_CF(); // error occurred
 15.6787 +          return;
 15.6788 +          }
 15.6789 +
 15.6790 +        BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
 15.6791 +        outb(0x000a, 0x06);
 15.6792 +
 15.6793 +  BX_DEBUG_INT13_FL("clear flip-flop\n");
 15.6794 +        outb(0x000c, 0x00); // clear flip-flop
 15.6795 +        outb(0x0004, base_address);
 15.6796 +        outb(0x0004, base_address>>8);
 15.6797 +  BX_DEBUG_INT13_FL("clear flip-flop\n");
 15.6798 +        outb(0x000c, 0x00); // clear flip-flop
 15.6799 +        outb(0x0005, base_count);
 15.6800 +        outb(0x0005, base_count>>8);
 15.6801 +
 15.6802 +        // port 0b: DMA-1 Mode Register
 15.6803 +        mode_register = 0x46; // single mode, increment, autoinit disable,
 15.6804 +                              // transfer type=write, channel 2
 15.6805 +  BX_DEBUG_INT13_FL("setting mode register\n");
 15.6806 +        outb(0x000b, mode_register);
 15.6807 +
 15.6808 +  BX_DEBUG_INT13_FL("setting page register\n");
 15.6809 +        // port 81: DMA-1 Page Register, channel 2
 15.6810 +        outb(0x0081, page);
 15.6811 +
 15.6812 +  BX_DEBUG_INT13_FL("unmask chan 2\n");
 15.6813 +        outb(0x000a, 0x02); // unmask channel 2
 15.6814 +
 15.6815 +        BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
 15.6816 +        outb(0x000a, 0x02);
 15.6817 +
 15.6818 +        //--------------------------------------
 15.6819 +        // set up floppy controller for transfer
 15.6820 +        //--------------------------------------
 15.6821 +
 15.6822 +        // set 40:3e bit 7 to 0
 15.6823 +        val8 = read_byte(0x0000, 0x043e);
 15.6824 +        val8 &= 0x7f;
 15.6825 +        write_byte(0x0000, 0x043e, val8);
 15.6826 +
 15.6827 +        // turn on motor of selected drive, DMA & int enabled, normal operation
 15.6828 +        if (drive)
 15.6829 +          dor = 0x20;
 15.6830 +        else
 15.6831 +          dor = 0x10;
 15.6832 +        dor |= 0x0c;
 15.6833 +        dor |= drive;
 15.6834 +        outb(0x03f2, dor);
 15.6835 +
 15.6836 +        // reset the disk motor timeout value of INT 08
 15.6837 +        write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
 15.6838 +
 15.6839 +        // check port 3f4 for drive readiness
 15.6840 +        val8 = inb(0x3f4);
 15.6841 +        if ( (val8 & 0xf0) != 0x80 )
 15.6842 +          BX_PANIC("int13_diskette:f02: ctrl not ready\n");
 15.6843 +
 15.6844 +        // send read-normal-data command (9 bytes) to controller
 15.6845 +        outb(0x03f5, 0xe6); // e6: read normal data
 15.6846 +        outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
 15.6847 +        outb(0x03f5, track);
 15.6848 +        outb(0x03f5, head);
 15.6849 +        outb(0x03f5, sector);
 15.6850 +        outb(0x03f5, 2); // 512 byte sector size
 15.6851 +        outb(0x03f5, 0); // last sector number possible on track
 15.6852 +        outb(0x03f5, 0); // Gap length
 15.6853 +        outb(0x03f5, 0xff); // Gap length
 15.6854 +
 15.6855 +       // turn on interrupts
 15.6856 +  ASM_START
 15.6857 +        sti
 15.6858 +  ASM_END
 15.6859 +
 15.6860 +        // wait on 40:3e bit 7 to become 1
 15.6861 +        val8 = (read_byte(0x0000, 0x043e) & 0x80);
 15.6862 +        while ( val8 == 0 ) {
 15.6863 +          val8 = (read_byte(0x0000, 0x043e) & 0x80);
 15.6864 +          }
 15.6865 +
 15.6866 +       val8 = 0; // separate asm from while() loop
 15.6867 +       // turn off interrupts
 15.6868 +  ASM_START
 15.6869 +        cli
 15.6870 +  ASM_END
 15.6871 +
 15.6872 +        // set 40:3e bit 7 to 0
 15.6873 +        val8 = read_byte(0x0000, 0x043e);
 15.6874 +        val8 &= 0x7f;
 15.6875 +        write_byte(0x0000, 0x043e, val8);
 15.6876 +
 15.6877 +        // check port 3f4 for accessibility to status bytes
 15.6878 +        val8 = inb(0x3f4);
 15.6879 +        if ( (val8 & 0xc0) != 0xc0 )
 15.6880 +          BX_PANIC("int13_diskette: ctrl not ready\n");
 15.6881 +
 15.6882 +        // read 7 return status bytes from controller
 15.6883 +        // using loop index broken, have to unroll...
 15.6884 +        return_status[0] = inb(0x3f5);
 15.6885 +        return_status[1] = inb(0x3f5);
 15.6886 +        return_status[2] = inb(0x3f5);
 15.6887 +        return_status[3] = inb(0x3f5);
 15.6888 +        return_status[4] = inb(0x3f5);
 15.6889 +        return_status[5] = inb(0x3f5);
 15.6890 +        return_status[6] = inb(0x3f5);
 15.6891 +        // record in BIOS Data Area
 15.6892 +        write_byte(0x0040, 0x0042, return_status[0]);
 15.6893 +        write_byte(0x0040, 0x0043, return_status[1]);
 15.6894 +        write_byte(0x0040, 0x0044, return_status[2]);
 15.6895 +        write_byte(0x0040, 0x0045, return_status[3]);
 15.6896 +        write_byte(0x0040, 0x0046, return_status[4]);
 15.6897 +        write_byte(0x0040, 0x0047, return_status[5]);
 15.6898 +        write_byte(0x0040, 0x0048, return_status[6]);
 15.6899 +
 15.6900 +        if ( (return_status[0] & 0xc0) != 0 ) {
 15.6901 +          SET_AH(0x20);
 15.6902 +          set_diskette_ret_status(0x20);
 15.6903 +          SET_AL(0); // no sectors read
 15.6904 +          SET_CF(); // error occurred
 15.6905 +          return;
 15.6906 +          }
 15.6907 +
 15.6908 +        // ??? should track be new val from return_status[3] ?
 15.6909 +        set_diskette_current_cyl(drive, track);
 15.6910 +        // AL = number of sectors read (same value as passed)
 15.6911 +        SET_AH(0x00); // success
 15.6912 +        CLEAR_CF();   // success
 15.6913 +        return;
 15.6914 +        }
 15.6915 +      else if (ah == 0x03) {
 15.6916 +        // Write Diskette Sectors
 15.6917 +
 15.6918 +        //-----------------------------------
 15.6919 +        // set up DMA controller for transfer
 15.6920 +        //-----------------------------------
 15.6921 +
 15.6922 +        // es:bx = pointer to where to place information from diskette
 15.6923 +        // port 04: DMA-1 base and current address, channel 2
 15.6924 +        // port 05: DMA-1 base and current count, channel 2
 15.6925 +        page = (ES >> 12);   // upper 4 bits
 15.6926 +        base_es = (ES << 4); // lower 16bits contributed by ES
 15.6927 +        base_address = base_es + BX; // lower 16 bits of address
 15.6928 +                                     // contributed by ES:BX
 15.6929 +        if ( base_address < base_es ) {
 15.6930 +          // in case of carry, adjust page by 1
 15.6931 +          page++;
 15.6932 +          }
 15.6933 +        base_count = (num_sectors * 512) - 1;
 15.6934 +
 15.6935 +        // check for 64K boundary overrun
 15.6936 +        last_addr = base_address + base_count;
 15.6937 +        if (last_addr < base_address) {
 15.6938 +          SET_AH(0x09);
 15.6939 +          set_diskette_ret_status(0x09);
 15.6940 +          SET_AL(0); // no sectors read
 15.6941 +          SET_CF(); // error occurred
 15.6942 +          return;
 15.6943 +          }
 15.6944 +
 15.6945 +        BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
 15.6946 +        outb(0x000a, 0x06);
 15.6947 +
 15.6948 +        outb(0x000c, 0x00); // clear flip-flop
 15.6949 +        outb(0x0004, base_address);
 15.6950 +        outb(0x0004, base_address>>8);
 15.6951 +        outb(0x000c, 0x00); // clear flip-flop
 15.6952 +        outb(0x0005, base_count);
 15.6953 +        outb(0x0005, base_count>>8);
 15.6954 +
 15.6955 +        // port 0b: DMA-1 Mode Register
 15.6956 +        mode_register = 0x4a; // single mode, increment, autoinit disable,
 15.6957 +                              // transfer type=read, channel 2
 15.6958 +        outb(0x000b, mode_register);
 15.6959 +
 15.6960 +        // port 81: DMA-1 Page Register, channel 2
 15.6961 +        outb(0x0081, page);
 15.6962 +
 15.6963 +        BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
 15.6964 +        outb(0x000a, 0x02);
 15.6965 +
 15.6966 +        //--------------------------------------
 15.6967 +        // set up floppy controller for transfer
 15.6968 +        //--------------------------------------
 15.6969 +
 15.6970 +        // set 40:3e bit 7 to 0
 15.6971 +        val8 = read_byte(0x0000, 0x043e);
 15.6972 +        val8 &= 0x7f;
 15.6973 +        write_byte(0x0000, 0x043e, val8);
 15.6974 +
 15.6975 +        // turn on motor of selected drive, DMA & int enabled, normal operation
 15.6976 +        if (drive)
 15.6977 +          dor = 0x20;
 15.6978 +        else
 15.6979 +          dor = 0x10;
 15.6980 +        dor |= 0x0c;
 15.6981 +        dor |= drive;
 15.6982 +        outb(0x03f2, dor);
 15.6983 +
 15.6984 +        // reset the disk motor timeout value of INT 08
 15.6985 +        write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
 15.6986 +
 15.6987 +        // check port 3f4 for drive readiness
 15.6988 +        val8 = inb(0x3f4);
 15.6989 +        if ( (val8 & 0xf0) != 0x80 )
 15.6990 +          BX_PANIC("int13_diskette:f03: ctrl not ready\n");
 15.6991 +
 15.6992 +        // send read-normal-data command (9 bytes) to controller
 15.6993 +        outb(0x03f5, 0xc5); // c5: write normal data
 15.6994 +        outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
 15.6995 +        outb(0x03f5, track);
 15.6996 +        outb(0x03f5, head);
 15.6997 +        outb(0x03f5, sector);
 15.6998 +        outb(0x03f5, 2); // 512 byte sector size
 15.6999 +        outb(0x03f5, 0); // last sector number possible on track
 15.7000 +        outb(0x03f5, 0); // Gap length
 15.7001 +        outb(0x03f5, 0xff); // Gap length
 15.7002 +
 15.7003 +       // turn on interrupts
 15.7004 +  ASM_START
 15.7005 +        sti
 15.7006 +  ASM_END
 15.7007 +
 15.7008 +        // wait on 40:3e bit 7 to become 1
 15.7009 +        val8 = (read_byte(0x0000, 0x043e) & 0x80);
 15.7010 +        while ( val8 == 0 ) {
 15.7011 +          val8 = (read_byte(0x0000, 0x043e) & 0x80);
 15.7012 +          }
 15.7013 +
 15.7014 +       val8 = 0; // separate asm from while() loop
 15.7015 +       // turn off interrupts
 15.7016 +  ASM_START
 15.7017 +        cli
 15.7018 +  ASM_END
 15.7019 +
 15.7020 +        // set 40:3e bit 7 to 0
 15.7021 +        val8 = read_byte(0x0000, 0x043e);
 15.7022 +        val8 &= 0x7f;
 15.7023 +        write_byte(0x0000, 0x043e, val8);
 15.7024 +
 15.7025 +        // check port 3f4 for accessibility to status bytes
 15.7026 +        val8 = inb(0x3f4);
 15.7027 +        if ( (val8 & 0xc0) != 0xc0 )
 15.7028 +          BX_PANIC("int13_diskette: ctrl not ready\n");
 15.7029 +
 15.7030 +        // read 7 return status bytes from controller
 15.7031 +        // using loop index broken, have to unroll...
 15.7032 +        return_status[0] = inb(0x3f5);
 15.7033 +        return_status[1] = inb(0x3f5);
 15.7034 +        return_status[2] = inb(0x3f5);
 15.7035 +        return_status[3] = inb(0x3f5);
 15.7036 +        return_status[4] = inb(0x3f5);
 15.7037 +        return_status[5] = inb(0x3f5);
 15.7038 +        return_status[6] = inb(0x3f5);
 15.7039 +        // record in BIOS Data Area
 15.7040 +        write_byte(0x0040, 0x0042, return_status[0]);
 15.7041 +        write_byte(0x0040, 0x0043, return_status[1]);
 15.7042 +        write_byte(0x0040, 0x0044, return_status[2]);
 15.7043 +        write_byte(0x0040, 0x0045, return_status[3]);
 15.7044 +        write_byte(0x0040, 0x0046, return_status[4]);
 15.7045 +        write_byte(0x0040, 0x0047, return_status[5]);
 15.7046 +        write_byte(0x0040, 0x0048, return_status[6]);
 15.7047 +
 15.7048 +        if ( (return_status[0] & 0xc0) != 0 ) {
 15.7049 +          if ( (return_status[1] & 0x02) != 0 ) {
 15.7050 +            // diskette not writable.
 15.7051 +            // AH=status code=0x03 (tried to write on write-protected disk)
 15.7052 +            // AL=number of sectors written=0
 15.7053 +            AX = 0x0300;
 15.7054 +            SET_CF();
 15.7055 +            return;
 15.7056 +          } else {
 15.7057 +            BX_PANIC("int13_diskette_function: read error\n");
 15.7058 +          }
 15.7059 +        }
 15.7060 +
 15.7061 +        // ??? should track be new val from return_status[3] ?
 15.7062 +        set_diskette_current_cyl(drive, track);
 15.7063 +        // AL = number of sectors read (same value as passed)
 15.7064 +        SET_AH(0x00); // success
 15.7065 +        CLEAR_CF();   // success
 15.7066 +        return;
 15.7067 +        }
 15.7068 +      else {  // if (ah == 0x04)
 15.7069 +        // Verify Diskette Sectors
 15.7070 +
 15.7071 +        // ??? should track be new val from return_status[3] ?
 15.7072 +        set_diskette_current_cyl(drive, track);
 15.7073 +        // AL = number of sectors verified (same value as passed)
 15.7074 +        CLEAR_CF();   // success
 15.7075 +        SET_AH(0x00); // success
 15.7076 +        return;
 15.7077 +        }
 15.7078 +
 15.7079 +
 15.7080 +    case 0x05: // format diskette track
 15.7081 +BX_DEBUG_INT13_FL("floppy f05\n");
 15.7082 +
 15.7083 +      num_sectors = GET_AL();
 15.7084 +      track       = GET_CH();
 15.7085 +      head        = GET_DH();
 15.7086 +      drive       = GET_ELDL();
 15.7087 +
 15.7088 +      if ((drive > 1) || (head > 1) || (track > 79) ||
 15.7089 +          (num_sectors == 0) || (num_sectors > 18)) {
 15.7090 +        SET_AH(1);
 15.7091 +        set_diskette_ret_status(1);
 15.7092 +        SET_CF(); // error occurred
 15.7093 +        }
 15.7094 +
 15.7095 +      // see if drive exists
 15.7096 +      if (floppy_drive_exists(drive) == 0) {
 15.7097 +        SET_AH(0x80); // drive not responding
 15.7098 +        set_diskette_ret_status(0x80);
 15.7099 +        SET_CF(); // error occurred
 15.7100 +        return;
 15.7101 +        }
 15.7102 +
 15.7103 +      // see if media in drive, and type is known
 15.7104 +      if (floppy_media_known(drive) == 0) {
 15.7105 +        if (floppy_media_sense(drive) == 0) {
 15.7106 +          SET_AH(0x0C); // Media type not found
 15.7107 +          set_diskette_ret_status(0x0C);
 15.7108 +          SET_AL(0); // no sectors read
 15.7109 +          SET_CF(); // error occurred
 15.7110 +          return;
 15.7111 +          }
 15.7112 +        }
 15.7113 +
 15.7114 +      // set up DMA controller for transfer
 15.7115 +      page = (ES >> 12);   // upper 4 bits
 15.7116 +      base_es = (ES << 4); // lower 16bits contributed by ES
 15.7117 +      base_address = base_es + BX; // lower 16 bits of address
 15.7118 +                                   // contributed by ES:BX
 15.7119 +      if ( base_address < base_es ) {
 15.7120 +        // in case of carry, adjust page by 1
 15.7121 +        page++;
 15.7122 +        }
 15.7123 +      base_count = (num_sectors * 4) - 1;
 15.7124 +
 15.7125 +      // check for 64K boundary overrun
 15.7126 +      last_addr = base_address + base_count;
 15.7127 +      if (last_addr < base_address) {
 15.7128 +        SET_AH(0x09);
 15.7129 +        set_diskette_ret_status(0x09);
 15.7130 +        SET_AL(0); // no sectors read
 15.7131 +        SET_CF(); // error occurred
 15.7132 +        return;
 15.7133 +        }
 15.7134 +
 15.7135 +      outb(0x000a, 0x06);
 15.7136 +      outb(0x000c, 0x00); // clear flip-flop
 15.7137 +      outb(0x0004, base_address);
 15.7138 +      outb(0x0004, base_address>>8);
 15.7139 +      outb(0x000c, 0x00); // clear flip-flop
 15.7140 +      outb(0x0005, base_count);
 15.7141 +      outb(0x0005, base_count>>8);
 15.7142 +      mode_register = 0x4a; // single mode, increment, autoinit disable,
 15.7143 +                            // transfer type=read, channel 2
 15.7144 +      outb(0x000b, mode_register);
 15.7145 +      // port 81: DMA-1 Page Register, channel 2
 15.7146 +      outb(0x0081, page);
 15.7147 +      outb(0x000a, 0x02);
 15.7148 +
 15.7149 +      // set up floppy controller for transfer
 15.7150 +      val8 = read_byte(0x0000, 0x043e);
 15.7151 +      val8 &= 0x7f;
 15.7152 +      write_byte(0x0000, 0x043e, val8);
 15.7153 +      // turn on motor of selected drive, DMA & int enabled, normal operation
 15.7154 +      if (drive)
 15.7155 +        dor = 0x20;
 15.7156 +      else
 15.7157 +        dor = 0x10;
 15.7158 +      dor |= 0x0c;
 15.7159 +      dor |= drive;
 15.7160 +      outb(0x03f2, dor);
 15.7161 +
 15.7162 +      // reset the disk motor timeout value of INT 08
 15.7163 +      write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
 15.7164 +
 15.7165 +      // check port 3f4 for drive readiness
 15.7166 +      val8 = inb(0x3f4);
 15.7167 +      if ( (val8 & 0xf0) != 0x80 )
 15.7168 +        BX_PANIC("int13_diskette:f05: ctrl not ready\n");
 15.7169 +
 15.7170 +      // send read-normal-data command (6 bytes) to controller
 15.7171 +      outb(0x03f5, 0x4d); // 4d: format track
 15.7172 +      outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
 15.7173 +      outb(0x03f5, 2); // 512 byte sector size
 15.7174 +      outb(0x03f5, num_sectors); // number of sectors per track
 15.7175 +      outb(0x03f5, 0); // Gap length
 15.7176 +      outb(0x03f5, 0xf6); // Fill byte
 15.7177 +      // turn on interrupts
 15.7178 +  ASM_START
 15.7179 +      sti
 15.7180 +  ASM_END
 15.7181 +      // wait on 40:3e bit 7 to become 1
 15.7182 +      val8 = (read_byte(0x0000, 0x043e) & 0x80);
 15.7183 +      while ( val8 == 0 ) {
 15.7184 +        val8 = (read_byte(0x0000, 0x043e) & 0x80);
 15.7185 +        }
 15.7186 +     val8 = 0; // separate asm from while() loop
 15.7187 +     // turn off interrupts
 15.7188 +  ASM_START
 15.7189 +      cli
 15.7190 +  ASM_END
 15.7191 +      // set 40:3e bit 7 to 0
 15.7192 +      val8 = read_byte(0x0000, 0x043e);
 15.7193 +      val8 &= 0x7f;
 15.7194 +      write_byte(0x0000, 0x043e, val8);
 15.7195 +      // check port 3f4 for accessibility to status bytes
 15.7196 +      val8 = inb(0x3f4);
 15.7197 +      if ( (val8 & 0xc0) != 0xc0 )
 15.7198 +        BX_PANIC("int13_diskette: ctrl not ready\n");
 15.7199 +
 15.7200 +      // read 7 return status bytes from controller
 15.7201 +      // using loop index broken, have to unroll...
 15.7202 +      return_status[0] = inb(0x3f5);
 15.7203 +      return_status[1] = inb(0x3f5);
 15.7204 +      return_status[2] = inb(0x3f5);
 15.7205 +      return_status[3] = inb(0x3f5);
 15.7206 +      return_status[4] = inb(0x3f5);
 15.7207 +      return_status[5] = inb(0x3f5);
 15.7208 +      return_status[6] = inb(0x3f5);
 15.7209 +      // record in BIOS Data Area
 15.7210 +      write_byte(0x0040, 0x0042, return_status[0]);
 15.7211 +      write_byte(0x0040, 0x0043, return_status[1]);
 15.7212 +      write_byte(0x0040, 0x0044, return_status[2]);
 15.7213 +      write_byte(0x0040, 0x0045, return_status[3]);
 15.7214 +      write_byte(0x0040, 0x0046, return_status[4]);
 15.7215 +      write_byte(0x0040, 0x0047, return_status[5]);
 15.7216 +      write_byte(0x0040, 0x0048, return_status[6]);
 15.7217 +
 15.7218 +      if ( (return_status[0] & 0xc0) != 0 ) {
 15.7219 +        if ( (return_status[1] & 0x02) != 0 ) {
 15.7220 +          // diskette not writable.
 15.7221 +          // AH=status code=0x03 (tried to write on write-protected disk)
 15.7222 +          // AL=number of sectors written=0
 15.7223 +          AX = 0x0300;
 15.7224 +          SET_CF();
 15.7225 +          return;
 15.7226 +        } else {
 15.7227 +          BX_PANIC("int13_diskette_function: write error\n");
 15.7228 +        }
 15.7229 +      }
 15.7230 +
 15.7231 +      SET_AH(0);
 15.7232 +      set_diskette_ret_status(0);
 15.7233 +      set_diskette_current_cyl(drive, 0);
 15.7234 +      CLEAR_CF(); // successful
 15.7235 +      return;
 15.7236 +
 15.7237 +
 15.7238 +    case 0x08: // read diskette drive parameters
 15.7239 +BX_DEBUG_INT13_FL("floppy f08\n");
 15.7240 +      drive = GET_ELDL();
 15.7241 +
 15.7242 +      if (drive > 1) {
 15.7243 +        AX = 0;
 15.7244 +        BX = 0;
 15.7245 +        CX = 0;
 15.7246 +        DX = 0;
 15.7247 +        ES = 0;
 15.7248 +        DI = 0;
 15.7249 +        SET_DL(num_floppies);
 15.7250 +        SET_CF();
 15.7251 +        return;
 15.7252 +        }
 15.7253 +
 15.7254 +      drive_type = inb_cmos(0x10);
 15.7255 +      num_floppies = 0;
 15.7256 +      if (drive_type & 0xf0)
 15.7257 +        num_floppies++;
 15.7258 +      if (drive_type & 0x0f)
 15.7259 +        num_floppies++;
 15.7260 +
 15.7261 +      if (drive == 0)
 15.7262 +        drive_type >>= 4;
 15.7263 +      else
 15.7264 +        drive_type &= 0x0f;
 15.7265 +
 15.7266 +      SET_BH(0);
 15.7267 +      SET_BL(drive_type);
 15.7268 +      SET_AH(0);
 15.7269 +      SET_AL(0);
 15.7270 +      SET_DL(num_floppies);
 15.7271 +
 15.7272 +      switch (drive_type) {
 15.7273 +        case 0: // none
 15.7274 +          CX = 0;
 15.7275 +          SET_DH(0); // max head #
 15.7276 +          break;
 15.7277 +
 15.7278 +        case 1: // 360KB, 5.25"
 15.7279 +          CX = 0x2709; // 40 tracks, 9 sectors
 15.7280 +          SET_DH(1); // max head #
 15.7281 +          break;
 15.7282 +
 15.7283 +        case 2: // 1.2MB, 5.25"
 15.7284 +          CX = 0x4f0f; // 80 tracks, 15 sectors
 15.7285 +          SET_DH(1); // max head #
 15.7286 +          break;
 15.7287 +
 15.7288 +        case 3: // 720KB, 3.5"
 15.7289 +          CX = 0x4f09; // 80 tracks, 9 sectors
 15.7290 +          SET_DH(1); // max head #
 15.7291 +          break;
 15.7292 +
 15.7293 +        case 4: // 1.44MB, 3.5"
 15.7294 +          CX = 0x4f12; // 80 tracks, 18 sectors
 15.7295 +          SET_DH(1); // max head #
 15.7296 +          break;
 15.7297 +
 15.7298 +        case 5: // 2.88MB, 3.5"
 15.7299 +          CX = 0x4f24; // 80 tracks, 36 sectors
 15.7300 +          SET_DH(1); // max head #
 15.7301 +          break;
 15.7302 +
 15.7303 +        case 6: // 160k, 5.25"
 15.7304 +          CX = 0x2708; // 40 tracks, 8 sectors
 15.7305 +          SET_DH(0); // max head #
 15.7306 +          break;
 15.7307 +
 15.7308 +        case 7: // 180k, 5.25"
 15.7309 +          CX = 0x2709; // 40 tracks, 9 sectors
 15.7310 +          SET_DH(0); // max head #
 15.7311 +          break;
 15.7312 +
 15.7313 +        case 8: // 320k, 5.25"
 15.7314 +          CX = 0x2708; // 40 tracks, 8 sectors
 15.7315 +          SET_DH(1); // max head #
 15.7316 +          break;
 15.7317 +
 15.7318 +        default: // ?
 15.7319 +          BX_PANIC("floppy: int13: bad floppy type\n");
 15.7320 +        }
 15.7321 +
 15.7322 +      /* set es & di to point to 11 byte diskette param table in ROM */
 15.7323 +ASM_START
 15.7324 +      push bp
 15.7325 +      mov  bp, sp
 15.7326 +      mov ax, #diskette_param_table2
 15.7327 +      mov _int13_diskette_function.DI+2[bp], ax
 15.7328 +      mov _int13_diskette_function.ES+2[bp], cs
 15.7329 +      pop  bp
 15.7330 +ASM_END
 15.7331 +      CLEAR_CF(); // success
 15.7332 +      /* disk status not changed upon success */
 15.7333 +      return;
 15.7334 +
 15.7335 +
 15.7336 +    case 0x15: // read diskette drive type
 15.7337 +BX_DEBUG_INT13_FL("floppy f15\n");
 15.7338 +      drive = GET_ELDL();
 15.7339 +      if (drive > 1) {
 15.7340 +        SET_AH(0); // only 2 drives supported
 15.7341 +        // set_diskette_ret_status here ???
 15.7342 +        SET_CF();
 15.7343 +        return;
 15.7344 +        }
 15.7345 +      drive_type = inb_cmos(0x10);
 15.7346 +
 15.7347 +      if (drive == 0)
 15.7348 +        drive_type >>= 4;
 15.7349 +      else
 15.7350 +        drive_type &= 0x0f;
 15.7351 +      CLEAR_CF(); // successful, not present
 15.7352 +      if (drive_type==0) {
 15.7353 +        SET_AH(0); // drive not present
 15.7354 +        }
 15.7355 +      else {
 15.7356 +        SET_AH(1); // drive present, does not support change line
 15.7357 +        }
 15.7358 +
 15.7359 +      return;
 15.7360 +
 15.7361 +    case 0x16: // get diskette change line status
 15.7362 +BX_DEBUG_INT13_FL("floppy f16\n");
 15.7363 +      drive = GET_ELDL();
 15.7364 +      if (drive > 1) {
 15.7365 +        SET_AH(0x01); // invalid drive
 15.7366 +        set_diskette_ret_status(0x01);
 15.7367 +        SET_CF();
 15.7368 +        return;
 15.7369 +        }
 15.7370 +
 15.7371 +      SET_AH(0x06); // change line not supported
 15.7372 +      set_diskette_ret_status(0x06);
 15.7373 +      SET_CF();
 15.7374 +      return;
 15.7375 +
 15.7376 +    case 0x17: // set diskette type for format(old)
 15.7377 +BX_DEBUG_INT13_FL("floppy f17\n");
 15.7378 +      /* not used for 1.44M floppies */
 15.7379 +      SET_AH(0x01); // not supported
 15.7380 +      set_diskette_ret_status(1); /* not supported */
 15.7381 +      SET_CF();
 15.7382 +      return;
 15.7383 +
 15.7384 +    case 0x18: // set diskette type for format(new)
 15.7385 +BX_DEBUG_INT13_FL("floppy f18\n");
 15.7386 +      SET_AH(0x01); // do later
 15.7387 +      set_diskette_ret_status(1);
 15.7388 +      SET_CF();
 15.7389 +      return;
 15.7390 +
 15.7391 +    default:
 15.7392 +        BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
 15.7393 +
 15.7394 +      // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
 15.7395 +        SET_AH(0x01); // ???
 15.7396 +        set_diskette_ret_status(1);
 15.7397 +        SET_CF();
 15.7398 +        return;
 15.7399 +      //   }
 15.7400 +    }
 15.7401 +}
 15.7402 +#else  // #if BX_SUPPORT_FLOPPY
 15.7403 +  void
 15.7404 +int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
 15.7405 +  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
 15.7406 +{
 15.7407 +  Bit8u  val8;
 15.7408 +
 15.7409 +  switch ( GET_AH() ) {
 15.7410 +
 15.7411 +    case 0x01: // Read Diskette Status
 15.7412 +      CLEAR_CF();
 15.7413 +      val8 = read_byte(0x0000, 0x0441);
 15.7414 +      SET_AH(val8);
 15.7415 +      if (val8) {
 15.7416 +        SET_CF();
 15.7417 +        }
 15.7418 +      return;
 15.7419 +
 15.7420 +    default:
 15.7421 +      SET_CF();
 15.7422 +      write_byte(0x0000, 0x0441, 0x01);
 15.7423 +      SET_AH(0x01);
 15.7424 +    }
 15.7425 +}
 15.7426 +#endif  // #if BX_SUPPORT_FLOPPY
 15.7427 +
 15.7428 + void
 15.7429 +set_diskette_ret_status(value)
 15.7430 +  Bit8u value;
 15.7431 +{
 15.7432 +  write_byte(0x0040, 0x0041, value);
 15.7433 +}
 15.7434 +
 15.7435 +  void
 15.7436 +set_diskette_current_cyl(drive, cyl)
 15.7437 +  Bit8u drive;
 15.7438 +  Bit8u cyl;
 15.7439 +{
 15.7440 +  if (drive > 1)
 15.7441 +    BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
 15.7442 +  write_byte(0x0040, 0x0094+drive, cyl);
 15.7443 +}
 15.7444 +
 15.7445 +  void
 15.7446 +determine_floppy_media(drive)
 15.7447 +  Bit16u drive;
 15.7448 +{
 15.7449 +#if 0
 15.7450 +  Bit8u  val8, DOR, ctrl_info;
 15.7451 +
 15.7452 +  ctrl_info = read_byte(0x0040, 0x008F);
 15.7453 +  if (drive==1)
 15.7454 +    ctrl_info >>= 4;
 15.7455 +  else
 15.7456 +    ctrl_info &= 0x0f;
 15.7457 +
 15.7458 +#if 0
 15.7459 +  if (drive == 0) {
 15.7460 +    DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
 15.7461 +    }
 15.7462 +  else {
 15.7463 +    DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
 15.7464 +    }
 15.7465 +#endif
 15.7466 +
 15.7467 +  if ( (ctrl_info & 0x04) != 0x04 ) {
 15.7468 +    // Drive not determined means no drive exists, done.
 15.7469 +    return;
 15.7470 +    }
 15.7471 +
 15.7472 +#if 0
 15.7473 +  // check Main Status Register for readiness
 15.7474 +  val8 = inb(0x03f4) & 0x80; // Main Status Register
 15.7475 +  if (val8 != 0x80)
 15.7476 +    BX_PANIC("d_f_m: MRQ bit not set\n");
 15.7477 +
 15.7478 +  // change line
 15.7479 +
 15.7480 +  // existing BDA values
 15.7481 +
 15.7482 +  // turn on drive motor
 15.7483 +  outb(0x03f2, DOR); // Digital Output Register
 15.7484 +  //
 15.7485 +#endif
 15.7486 +  BX_PANIC("d_f_m: OK so far\n");
 15.7487 +#endif
 15.7488 +}
 15.7489 +
 15.7490 +  void
 15.7491 +int17_function(regs, ds, iret_addr)
 15.7492 +  pusha_regs_t regs; // regs pushed from PUSHA instruction
 15.7493 +  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
 15.7494 +  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
 15.7495 +{
 15.7496 +  Bit16u addr,timeout;
 15.7497 +  Bit8u val8;
 15.7498 +
 15.7499 +  ASM_START
 15.7500 +  sti
 15.7501 +  ASM_END
 15.7502 +
 15.7503 +  addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
 15.7504 +  if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
 15.7505 +    timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
 15.7506 +    if (regs.u.r8.ah == 0) {
 15.7507 +      outb(addr, regs.u.r8.al);
 15.7508 +      val8 = inb(addr+2);
 15.7509 +      outb(addr+2, val8 | 0x01); // send strobe
 15.7510 +      ASM_START
 15.7511 +      nop
 15.7512 +      ASM_END
 15.7513 +      outb(addr+2, val8 & ~0x01);
 15.7514 +      while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
 15.7515 +        timeout--;
 15.7516 +      }
 15.7517 +    }
 15.7518 +    if (regs.u.r8.ah == 1) {
 15.7519 +      val8 = inb(addr+2);
 15.7520 +      outb(addr+2, val8 & ~0x04); // send init
 15.7521 +      ASM_START
 15.7522 +      nop
 15.7523 +      ASM_END
 15.7524 +      outb(addr+2, val8 | 0x04);
 15.7525 +    }
 15.7526 +    val8 = inb(addr+1);
 15.7527 +    regs.u.r8.ah = (val8 ^ 0x48);
 15.7528 +    if (!timeout) regs.u.r8.ah |= 0x01;
 15.7529 +    ClearCF(iret_addr.flags);
 15.7530 +  } else {
 15.7531 +    SetCF(iret_addr.flags); // Unsupported
 15.7532 +  }
 15.7533 +}
 15.7534 +
 15.7535 +// returns bootsegment in ax, drive in bl
 15.7536 +  Bit32u 
 15.7537 +int19_function(bseqnr)
 15.7538 +Bit8u bseqnr;
 15.7539 +{
 15.7540 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
 15.7541 +  Bit16u bootseq;
 15.7542 +  Bit8u  bootdrv;
 15.7543 +  Bit8u  bootcd;
 15.7544 +  Bit8u  bootchk;
 15.7545 +  Bit16u bootseg;
 15.7546 +  Bit16u status;
 15.7547 +  Bit8u  lastdrive=0;
 15.7548 +
 15.7549 +  // if BX_ELTORITO_BOOT is not defined, old behavior
 15.7550 +  //   check bit 5 in CMOS reg 0x2d.  load either 0x00 or 0x80 into DL
 15.7551 +  //   in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
 15.7552 +  //     0: system boot sequence, first drive C: then A:
 15.7553 +  //     1: system boot sequence, first drive A: then C:
 15.7554 +  // else BX_ELTORITO_BOOT is defined
 15.7555 +  //   CMOS regs 0x3D and 0x38 contain the boot sequence:
 15.7556 +  //     CMOS reg 0x3D & 0x0f : 1st boot device
 15.7557 +  //     CMOS reg 0x3D & 0xf0 : 2nd boot device
 15.7558 +  //     CMOS reg 0x38 & 0xf0 : 3rd boot device
 15.7559 +  //   boot device codes:
 15.7560 +  //     0x00 : not defined
 15.7561 +  //     0x01 : first floppy 
 15.7562 +  //     0x02 : first harddrive
 15.7563 +  //     0x03 : first cdrom
 15.7564 +  //     else : boot failure
 15.7565 +
 15.7566 +  // Get the boot sequence
 15.7567 +#if BX_ELTORITO_BOOT
 15.7568 +  bootseq=inb_cmos(0x3d);
 15.7569 +  bootseq|=((inb_cmos(0x38) & 0xf0) << 4);
 15.7570 +
 15.7571 +  if (bseqnr==2) bootseq >>= 4;
 15.7572 +  if (bseqnr==3) bootseq >>= 8;
 15.7573 +  if (bootseq<0x10) lastdrive = 1;
 15.7574 +  bootdrv=0x00; bootcd=0;
 15.7575 +  switch(bootseq & 0x0f) {
 15.7576 +    case 0x01: bootdrv=0x00; bootcd=0; break;
 15.7577 +    case 0x02: bootdrv=0x80; bootcd=0; break;
 15.7578 +    case 0x03: bootdrv=0x00; bootcd=1; break;
 15.7579 +    default:   return 0x00000000;
 15.7580 +    }
 15.7581 +#else
 15.7582 +  bootseq=inb_cmos(0x2d);
 15.7583 +
 15.7584 +  if (bseqnr==2) {
 15.7585 +    bootseq ^= 0x20;
 15.7586 +    lastdrive = 1;
 15.7587 +  }
 15.7588 +  bootdrv=0x00; bootcd=0;
 15.7589 +  if((bootseq&0x20)==0) bootdrv=0x80;
 15.7590 +#endif // BX_ELTORITO_BOOT
 15.7591 +
 15.7592 +#if BX_ELTORITO_BOOT
 15.7593 +  // We have to boot from cd
 15.7594 +  if (bootcd != 0) {
 15.7595 +    status = cdrom_boot();
 15.7596 +
 15.7597 +    // If failure
 15.7598 +    if ( (status & 0x00ff) !=0 ) {
 15.7599 +      print_cdromboot_failure(status);
 15.7600 +      print_boot_failure(bootcd, bootdrv, 1, lastdrive);
 15.7601 +      return 0x00000000;
 15.7602 +      }
 15.7603 +
 15.7604 +    bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
 15.7605 +    bootdrv = (Bit8u)(status>>8);
 15.7606 +    }
 15.7607 +
 15.7608 +#endif // BX_ELTORITO_BOOT
 15.7609 +
 15.7610 +  // We have to boot from harddisk or floppy
 15.7611 +  if (bootcd == 0) {
 15.7612 +    bootseg=0x07c0;
 15.7613 +
 15.7614 +ASM_START
 15.7615 +    push bp
 15.7616 +    mov  bp, sp
 15.7617 +
 15.7618 +    mov  ax, #0x0000
 15.7619 +    mov  _int19_function.status + 2[bp], ax
 15.7620 +    mov  dl, _int19_function.bootdrv + 2[bp]
 15.7621 +    mov  ax, _int19_function.bootseg + 2[bp]
 15.7622 +    mov  es, ax         ;; segment
 15.7623 +    mov  bx, #0x0000    ;; offset
 15.7624 +    mov  ah, #0x02      ;; function 2, read diskette sector
 15.7625 +    mov  al, #0x01      ;; read 1 sector
 15.7626 +    mov  ch, #0x00      ;; track 0
 15.7627 +    mov  cl, #0x01      ;; sector 1
 15.7628 +    mov  dh, #0x00      ;; head 0
 15.7629 +    int  #0x13          ;; read sector
 15.7630 +    jnc  int19_load_done
 15.7631 +    mov  ax, #0x0001
 15.7632 +    mov  _int19_function.status + 2[bp], ax
 15.7633 +
 15.7634 +int19_load_done:
 15.7635 +    pop  bp
 15.7636 +ASM_END
 15.7637 +    
 15.7638 +    if (status != 0) {
 15.7639 +      print_boot_failure(bootcd, bootdrv, 1, lastdrive);
 15.7640 +      return 0x00000000;
 15.7641 +      }
 15.7642 +    }
 15.7643 +
 15.7644 +  // check signature if instructed by cmos reg 0x38, only for floppy
 15.7645 +  // bootchk = 1 : signature check disabled
 15.7646 +  // bootchk = 0 : signature check enabled
 15.7647 +  if (bootdrv != 0) bootchk = 0;
 15.7648 +  else bootchk = inb_cmos(0x38) & 0x01;
 15.7649 +
 15.7650 +#if BX_ELTORITO_BOOT
 15.7651 +  // if boot from cd, no signature check
 15.7652 +  if (bootcd != 0)
 15.7653 +    bootchk = 1;
 15.7654 +#endif // BX_ELTORITO_BOOT
 15.7655 +
 15.7656 +  if (bootchk == 0) {
 15.7657 +    if (read_word(bootseg,0x1fe) != 0xaa55) {
 15.7658 +      print_boot_failure(bootcd, bootdrv, 0, lastdrive);
 15.7659 +      return 0x00000000;
 15.7660 +      }
 15.7661 +    }
 15.7662 +  
 15.7663 +#if BX_ELTORITO_BOOT
 15.7664 +  // Print out the boot string
 15.7665 +  print_boot_device(bootcd, bootdrv);
 15.7666 +#else // BX_ELTORITO_BOOT
 15.7667 +  print_boot_device(0, bootdrv);
 15.7668 +#endif // BX_ELTORITO_BOOT
 15.7669 +
 15.7670 +  // return the boot segment
 15.7671 +  return (((Bit32u)bootdrv) << 16) + bootseg;
 15.7672 +}
 15.7673 +
 15.7674 +  void
 15.7675 +int1a_function(regs, ds, iret_addr)
 15.7676 +  pusha_regs_t regs; // regs pushed from PUSHA instruction
 15.7677 +  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
 15.7678 +  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
 15.7679 +{
 15.7680 +  Bit8u val8;
 15.7681 +
 15.7682 +  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);
 15.7683 +
 15.7684 +  ASM_START
 15.7685 +  sti
 15.7686 +  ASM_END
 15.7687 +
 15.7688 +  switch (regs.u.r8.ah) {
 15.7689 +    case 0: // get current clock count
 15.7690 +      ASM_START
 15.7691 +      cli
 15.7692 +      ASM_END
 15.7693 +      regs.u.r16.cx = BiosData->ticks_high;
 15.7694 +      regs.u.r16.dx = BiosData->ticks_low;
 15.7695 +      regs.u.r8.al  = BiosData->midnight_flag;
 15.7696 +      BiosData->midnight_flag = 0; // reset flag
 15.7697 +      ASM_START
 15.7698 +      sti
 15.7699 +      ASM_END
 15.7700 +      // AH already 0
 15.7701 +      ClearCF(iret_addr.flags); // OK
 15.7702 +      break;
 15.7703 +
 15.7704 +    case 1: // Set Current Clock Count
 15.7705 +      ASM_START
 15.7706 +      cli
 15.7707 +      ASM_END
 15.7708 +      BiosData->ticks_high = regs.u.r16.cx;
 15.7709 +      BiosData->ticks_low  = regs.u.r16.dx;
 15.7710 +      BiosData->midnight_flag = 0; // reset flag
 15.7711 +      ASM_START
 15.7712 +      sti
 15.7713 +      ASM_END
 15.7714 +      regs.u.r8.ah = 0;
 15.7715 +      ClearCF(iret_addr.flags); // OK
 15.7716 +      break;
 15.7717 +
 15.7718 +
 15.7719 +    case 2: // Read CMOS Time
 15.7720 +      if (rtc_updating()) {
 15.7721 +        SetCF(iret_addr.flags);
 15.7722 +        break;
 15.7723 +        }
 15.7724 +
 15.7725 +      regs.u.r8.dh = inb_cmos(0x00); // Seconds
 15.7726 +      regs.u.r8.cl = inb_cmos(0x02); // Minutes
 15.7727 +      regs.u.r8.ch = inb_cmos(0x04); // Hours
 15.7728 +      regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
 15.7729 +      regs.u.r8.ah = 0;
 15.7730 +      regs.u.r8.al = regs.u.r8.ch;
 15.7731 +      ClearCF(iret_addr.flags); // OK
 15.7732 +      break;
 15.7733 +
 15.7734 +    case 3: // Set CMOS Time
 15.7735 +      // Using a debugger, I notice the following masking/setting
 15.7736 +      // of bits in Status Register B, by setting Reg B to
 15.7737 +      // a few values and getting its value after INT 1A was called.
 15.7738 +      //
 15.7739 +      //        try#1       try#2       try#3
 15.7740 +      // before 1111 1101   0111 1101   0000 0000
 15.7741 +      // after  0110 0010   0110 0010   0000 0010
 15.7742 +      //
 15.7743 +      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
 15.7744 +      // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
 15.7745 +      if (rtc_updating()) {
 15.7746 +        init_rtc();
 15.7747 +        // fall through as if an update were not in progress
 15.7748 +        }
 15.7749 +      outb_cmos(0x00, regs.u.r8.dh); // Seconds
 15.7750 +      outb_cmos(0x02, regs.u.r8.cl); // Minutes
 15.7751 +      outb_cmos(0x04, regs.u.r8.ch); // Hours
 15.7752 +      // Set Daylight Savings time enabled bit to requested value
 15.7753 +      val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
 15.7754 +      // (reg B already selected)
 15.7755 +      outb_cmos(0x0b, val8);
 15.7756 +      regs.u.r8.ah = 0;
 15.7757 +      regs.u.r8.al = val8; // val last written to Reg B
 15.7758 +      ClearCF(iret_addr.flags); // OK
 15.7759 +      break;
 15.7760 +
 15.7761 +    case 4: // Read CMOS Date
 15.7762 +      regs.u.r8.ah = 0;
 15.7763 +      if (rtc_updating()) {
 15.7764 +        SetCF(iret_addr.flags);
 15.7765 +        break;
 15.7766 +        }
 15.7767 +      regs.u.r8.cl = inb_cmos(0x09); // Year
 15.7768 +      regs.u.r8.dh = inb_cmos(0x08); // Month
 15.7769 +      regs.u.r8.dl = inb_cmos(0x07); // Day of Month
 15.7770 +      regs.u.r8.ch = inb_cmos(0x32); // Century
 15.7771 +      regs.u.r8.al = regs.u.r8.ch;
 15.7772 +      ClearCF(iret_addr.flags); // OK
 15.7773 +      break;
 15.7774 +
 15.7775 +    case 5: // Set CMOS Date
 15.7776 +      // Using a debugger, I notice the following masking/setting
 15.7777 +      // of bits in Status Register B, by setting Reg B to
 15.7778 +      // a few values and getting its value after INT 1A was called.
 15.7779 +      //
 15.7780 +      //        try#1       try#2       try#3       try#4
 15.7781 +      // before 1111 1101   0111 1101   0000 0010   0000 0000
 15.7782 +      // after  0110 1101   0111 1101   0000 0010   0000 0000
 15.7783 +      //
 15.7784 +      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
 15.7785 +      // My assumption: RegB = (RegB & 01111111b)
 15.7786 +      if (rtc_updating()) {
 15.7787 +        init_rtc();
 15.7788 +        SetCF(iret_addr.flags);
 15.7789 +        break;
 15.7790 +        }
 15.7791 +      outb_cmos(0x09, regs.u.r8.cl); // Year
 15.7792 +      outb_cmos(0x08, regs.u.r8.dh); // Month
 15.7793 +      outb_cmos(0x07, regs.u.r8.dl); // Day of Month
 15.7794 +      outb_cmos(0x32, regs.u.r8.ch); // Century
 15.7795 +      val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
 15.7796 +      outb_cmos(0x0b, val8);
 15.7797 +      regs.u.r8.ah = 0;
 15.7798 +      regs.u.r8.al = val8; // AL = val last written to Reg B
 15.7799 +      ClearCF(iret_addr.flags); // OK
 15.7800 +      break;
 15.7801 +
 15.7802 +    case 6: // Set Alarm Time in CMOS
 15.7803 +      // Using a debugger, I notice the following masking/setting
 15.7804 +      // of bits in Status Register B, by setting Reg B to
 15.7805 +      // a few values and getting its value after INT 1A was called.
 15.7806 +      //
 15.7807 +      //        try#1       try#2       try#3
 15.7808 +      // before 1101 1111   0101 1111   0000 0000
 15.7809 +      // after  0110 1111   0111 1111   0010 0000
 15.7810 +      //
 15.7811 +      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
 15.7812 +      // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
 15.7813 +      val8 = inb_cmos(0x0b); // Get Status Reg B
 15.7814 +      regs.u.r16.ax = 0;
 15.7815 +      if (val8 & 0x20) {
 15.7816 +        // Alarm interrupt enabled already
 15.7817 +        SetCF(iret_addr.flags); // Error: alarm in use
 15.7818 +        break;
 15.7819 +        }
 15.7820 +      if (rtc_updating()) {
 15.7821 +        init_rtc();
 15.7822 +        // fall through as if an update were not in progress
 15.7823 +        }
 15.7824 +      outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
 15.7825 +      outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
 15.7826 +      outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
 15.7827 +      outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
 15.7828 +      // enable Status Reg B alarm bit, clear halt clock bit
 15.7829 +      outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
 15.7830 +      ClearCF(iret_addr.flags); // OK
 15.7831 +      break;
 15.7832 +
 15.7833 +    case 7: // Turn off Alarm
 15.7834 +      // Using a debugger, I notice the following masking/setting
 15.7835 +      // of bits in Status Register B, by setting Reg B to
 15.7836 +      // a few values and getting its value after INT 1A was called.
 15.7837 +      //
 15.7838 +      //        try#1       try#2       try#3       try#4
 15.7839 +      // before 1111 1101   0111 1101   0010 0000   0010 0010
 15.7840 +      // after  0100 0101   0101 0101   0000 0000   0000 0010
 15.7841 +      //
 15.7842 +      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
 15.7843 +      // My assumption: RegB = (RegB & 01010111b)
 15.7844 +      val8 = inb_cmos(0x0b); // Get Status Reg B
 15.7845 +      // clear clock-halt bit, disable alarm bit
 15.7846 +      outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
 15.7847 +      regs.u.r8.ah = 0;
 15.7848 +      regs.u.r8.al = val8; // val last written to Reg B
 15.7849 +      ClearCF(iret_addr.flags); // OK
 15.7850 +      break;
 15.7851 +#if BX_PCIBIOS
 15.7852 +    case 0xb1:
 15.7853 +      // real mode PCI BIOS functions now handled in assembler code
 15.7854 +      // this C code handles the error code for information only
 15.7855 +      if (regs.u.r8.bl == 0xff) {
 15.7856 +        BX_INFO("PCI BIOS: PCI not present\n");
 15.7857 +      } else if (regs.u.r8.bl == 0x81) {
 15.7858 +        BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
 15.7859 +      } else if (regs.u.r8.bl == 0x83) {
 15.7860 +        BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
 15.7861 +      } else if (regs.u.r8.bl == 0x86) {
 15.7862 +        BX_INFO("PCI device %04x:%04x not found\n", regs.u.r16.dx, regs.u.r16.cx);
 15.7863 +      }
 15.7864 +      regs.u.r8.ah = regs.u.r8.bl;
 15.7865 +      SetCF(iret_addr.flags);
 15.7866 +      break;
 15.7867 +#endif
 15.7868 +
 15.7869 +    default:
 15.7870 +      SetCF(iret_addr.flags); // Unsupported
 15.7871 +    }
 15.7872 +}
 15.7873 +
 15.7874 +  void
 15.7875 +int70_function(regs, ds, iret_addr)
 15.7876 +  pusha_regs_t regs; // regs pushed from PUSHA instruction
 15.7877 +  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
 15.7878 +  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
 15.7879 +{
 15.7880 +  // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
 15.7881 +  Bit8u registerB = 0, registerC = 0;
 15.7882 +
 15.7883 +  // Check which modes are enabled and have occurred.
 15.7884 +  registerB = inb_cmos( 0xB );
 15.7885 +  registerC = inb_cmos( 0xC );
 15.7886 +
 15.7887 +  if( ( registerB & 0x60 ) != 0 ) {
 15.7888 +    if( ( registerC & 0x20 ) != 0 ) {
 15.7889 +      // Handle Alarm Interrupt.
 15.7890 +ASM_START
 15.7891 +      sti
 15.7892 +      int #0x4a
 15.7893 +      cli
 15.7894 +ASM_END
 15.7895 +    }
 15.7896 +    if( ( registerC & 0x40 ) != 0 ) {
 15.7897 +      // Handle Periodic Interrupt.
 15.7898 +
 15.7899 +      if( read_byte( 0x40, 0xA0 ) != 0 ) {
 15.7900 +        // Wait Interval (Int 15, AH=83) active.
 15.7901 +        Bit32u time, toggle;
 15.7902 +
 15.7903 +        time = read_dword( 0x40, 0x9C );  // Time left in microseconds.
 15.7904 +        if( time < 0x3D1 ) {
 15.7905 +          // Done waiting.
 15.7906 +          Bit16u segment, offset;
 15.7907 +
 15.7908 +          offset = read_word( 0x40, 0x98 );
 15.7909 +          segment = read_word( 0x40, 0x9A );
 15.7910 +          write_byte( 0x40, 0xA0, 0 );  // Turn of status byte.
 15.7911 +          outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
 15.7912 +          write_byte( segment, offset, 0x80 );  // Write to specified flag byte.
 15.7913 +        } else {
 15.7914 +          // Continue waiting.
 15.7915 +          time -= 0x3D1;
 15.7916 +          write_dword( 0x40, 0x9C, time );
 15.7917 +        }
 15.7918 +      }
 15.7919 +    }
 15.7920 +  }
 15.7921 +
 15.7922 +ASM_START
 15.7923 +  call eoi_both_pics
 15.7924 +ASM_END
 15.7925 +}
 15.7926 +
 15.7927 +
 15.7928 +ASM_START
 15.7929 +;------------------------------------------
 15.7930 +;- INT74h : PS/2 mouse hardware interrupt -
 15.7931 +;------------------------------------------
 15.7932 +int74_handler:
 15.7933 +  sti
 15.7934 +  pusha
 15.7935 +  push ds         ;; save DS
 15.7936 +  push #0x00 ;; placeholder for status
 15.7937 +  push #0x00 ;; placeholder for X
 15.7938 +  push #0x00 ;; placeholder for Y
 15.7939 +  push #0x00 ;; placeholder for Z
 15.7940 +  push #0x00 ;; placeholder for make_far_call boolean
 15.7941 +  call _int74_function
 15.7942 +  pop  cx      ;; remove make_far_call from stack
 15.7943 +  jcxz int74_done
 15.7944 +
 15.7945 +  ;; make far call to EBDA:0022
 15.7946 +  push #0x00
 15.7947 +  pop ds
 15.7948 +  push 0x040E     ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
 15.7949 +  pop ds
 15.7950 +  //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
 15.7951 +  call far ptr[0x22]
 15.7952 +int74_done:
 15.7953 +  cli
 15.7954 +  call eoi_both_pics
 15.7955 +  add sp, #8     ;; pop status, x, y, z
 15.7956 +
 15.7957 +  pop ds          ;; restore DS
 15.7958 +  popa
 15.7959 +  iret
 15.7960 +
 15.7961 +
 15.7962 +;; This will perform an IRET, but will retain value of current CF
 15.7963 +;; by altering flags on stack.  Better than RETF #02.
 15.7964 +iret_modify_cf:
 15.7965 +  jc   carry_set
 15.7966 +  push bp
 15.7967 +  mov  bp, sp
 15.7968 +  and  BYTE [bp + 0x06], #0xfe
 15.7969 +  pop  bp
 15.7970 +  iret
 15.7971 +carry_set:
 15.7972 +  push bp
 15.7973 +  mov  bp, sp
 15.7974 +  or   BYTE [bp + 0x06], #0x01
 15.7975 +  pop  bp
 15.7976 +  iret
 15.7977 +
 15.7978 +
 15.7979 +;----------------------
 15.7980 +;- INT13h (relocated) -
 15.7981 +;----------------------
 15.7982 +;
 15.7983 +; int13_relocated is a little bit messed up since I played with it
 15.7984 +; I have to rewrite it:
 15.7985 +;   - call a function that detect which function to call
 15.7986 +;   - make all called C function get the same parameters list
 15.7987 +;
 15.7988 +int13_relocated:
 15.7989 +
 15.7990 +#if BX_ELTORITO_BOOT
 15.7991 +  ;; check for an eltorito function
 15.7992 +  cmp   ah,#0x4a
 15.7993 +  jb    int13_not_eltorito
 15.7994 +  cmp   ah,#0x4d
 15.7995 +  ja    int13_not_eltorito
 15.7996 +
 15.7997 +  pusha
 15.7998 +  push  es
 15.7999 +  push  ds
 15.8000 +  push  ss
 15.8001 +  pop   ds
 15.8002 +
 15.8003 +  push  #int13_out
 15.8004 +  jmp   _int13_eltorito      ;; ELDX not used
 15.8005 +
 15.8006 +int13_not_eltorito:
 15.8007 +  push  ax
 15.8008 +  push  bx
 15.8009 +  push  cx
 15.8010 +  push  dx
 15.8011 +
 15.8012 +  ;; check if emulation active
 15.8013 +  call  _cdemu_isactive
 15.8014 +  cmp   al,#0x00
 15.8015 +  je    int13_cdemu_inactive
 15.8016 +
 15.8017 +  ;; check if access to the emulated drive
 15.8018 +  call  _cdemu_emulated_drive
 15.8019 +  pop   dx
 15.8020 +  push  dx
 15.8021 +  cmp   al,dl                ;; int13 on emulated drive
 15.8022 +  jne   int13_nocdemu
 15.8023 +
 15.8024 +  pop   dx
 15.8025 +  pop   cx
 15.8026 +  pop   bx
 15.8027 +  pop   ax
 15.8028 +
 15.8029 +  pusha
 15.8030 +  push  es
 15.8031 +  push  ds
 15.8032 +  push  ss
 15.8033 +  pop   ds
 15.8034 +
 15.8035 +  push  #int13_out
 15.8036 +  jmp   _int13_cdemu         ;; ELDX not used
 15.8037 +
 15.8038 +int13_nocdemu:
 15.8039 +  and   dl,#0xE0             ;; mask to get device class, including cdroms
 15.8040 +  cmp   al,dl                ;; al is 0x00 or 0x80
 15.8041 +  jne   int13_cdemu_inactive ;; inactive for device class
 15.8042 +
 15.8043 +  pop   dx
 15.8044 +  pop   cx
 15.8045 +  pop   bx
 15.8046 +  pop   ax
 15.8047 +
 15.8048 +  push  ax
 15.8049 +  push  cx
 15.8050 +  push  dx
 15.8051 +  push  bx
 15.8052 +
 15.8053 +  dec   dl                   ;; real drive is dl - 1
 15.8054 +  jmp   int13_legacy
 15.8055 +
 15.8056 +int13_cdemu_inactive:
 15.8057 +  pop   dx
 15.8058 +  pop   cx
 15.8059 +  pop   bx
 15.8060 +  pop   ax
 15.8061 +
 15.8062 +#endif // BX_ELTORITO_BOOT
 15.8063 +
 15.8064 +int13_noeltorito:
 15.8065 +
 15.8066 +  push  ax
 15.8067 +  push  cx
 15.8068 +  push  dx
 15.8069 +  push  bx
 15.8070 +
 15.8071 +int13_legacy:
 15.8072 +
 15.8073 +  push  dx                   ;; push eltorito value of dx instead of sp
 15.8074 +
 15.8075 +  push  bp
 15.8076 +  push  si
 15.8077 +  push  di
 15.8078 +
 15.8079 +  push  es
 15.8080 +  push  ds
 15.8081 +  push  ss
 15.8082 +  pop   ds
 15.8083 +
 15.8084 +  ;; now the 16-bit registers can be restored with:
 15.8085 +  ;; pop ds; pop es; popa; iret
 15.8086 +  ;; arguments passed to functions should be
 15.8087 +  ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
 15.8088 +
 15.8089 +  test  dl, #0x80
 15.8090 +  jnz   int13_notfloppy
 15.8091 +
 15.8092 +  push #int13_out
 15.8093 +  jmp _int13_diskette_function
 15.8094 +
 15.8095 +int13_notfloppy:
 15.8096 +
 15.8097 +#if BX_USE_ATADRV
 15.8098 +
 15.8099 +  cmp   dl, #0xE0
 15.8100 +  jb    int13_notcdrom
 15.8101 +
 15.8102 +  // ebx is modified: BSD 5.2.1 boot loader problem
 15.8103 +  // someone should figure out which 32 bit register that actually are used
 15.8104 +
 15.8105 +  shr   ebx, #16
 15.8106 +  push  bx
 15.8107 +
 15.8108 +  call  _int13_cdrom
 15.8109 +
 15.8110 +  pop   bx
 15.8111 +  shl   ebx, #16
 15.8112 +
 15.8113 +  jmp int13_out
 15.8114 +
 15.8115 +int13_notcdrom:
 15.8116 +
 15.8117 +#endif
 15.8118 +
 15.8119 +int13_disk:
 15.8120 +  call  _int13_harddisk
 15.8121 +
 15.8122 +int13_out:
 15.8123 +  pop ds
 15.8124 +  pop es
 15.8125 +  popa
 15.8126 +  iret 
 15.8127 +
 15.8128 +
 15.8129 +;----------
 15.8130 +;- INT18h -
 15.8131 +;----------
 15.8132 +int18_handler: ;; Boot Failure routing
 15.8133 +  call _int18_panic_msg
 15.8134 +  hlt
 15.8135 +  iret
 15.8136 +
 15.8137 +;----------
 15.8138 +;- INT19h -
 15.8139 +;----------
 15.8140 +int19_relocated: ;; Boot function, relocated
 15.8141 +
 15.8142 +  ;; int19 was beginning to be really complex, so now it
 15.8143 +  ;; just calls an C function, that does the work
 15.8144 +  ;; it returns in BL the boot drive, and in AX the boot segment
 15.8145 +  ;; the boot segment will be 0x0000 if something has failed
 15.8146 +
 15.8147 +  push bp
 15.8148 +  mov  bp, sp
 15.8149 +
 15.8150 +  ;; drop ds
 15.8151 +  xor  ax, ax
 15.8152 +  mov  ds, ax
 15.8153 +
 15.8154 +  ;; 1st boot device
 15.8155 +  mov  ax, #0x0001
 15.8156 +  push ax
 15.8157 +  call _int19_function
 15.8158 +  inc  sp
 15.8159 +  inc  sp
 15.8160 +  ;; bl contains the boot drive
 15.8161 +  ;; ax contains the boot segment or 0 if failure
 15.8162 +
 15.8163 +  test       ax, ax  ;; if ax is 0 try next boot device
 15.8164 +  jnz        boot_setup
 15.8165 +
 15.8166 +  ;; 2nd boot device
 15.8167 +  mov  ax, #0x0002
 15.8168 +  push ax
 15.8169 +  call _int19_function
 15.8170 +  inc  sp
 15.8171 +  inc  sp
 15.8172 +  test       ax, ax  ;; if ax is 0 try next boot device
 15.8173 +  jnz        boot_setup
 15.8174 +
 15.8175 +  ;; 3rd boot device
 15.8176 +  mov  ax, #0x0003
 15.8177 +  push ax
 15.8178 +  call _int19_function
 15.8179 +  inc  sp
 15.8180 +  inc  sp
 15.8181 +  test       ax, ax  ;; if ax is 0 call int18
 15.8182 +  jz         int18_handler
 15.8183 +
 15.8184 +boot_setup:
 15.8185 +  mov dl,    bl      ;; set drive so guest os find it
 15.8186 +  shl eax,   #0x04   ;; convert seg to ip
 15.8187 +  mov 2[bp], ax      ;; set ip
 15.8188 +
 15.8189 +  shr eax,   #0x04   ;; get cs back
 15.8190 +  and ax,    #0xF000 ;; remove what went in ip
 15.8191 +  mov 4[bp], ax      ;; set cs
 15.8192 +  xor ax,    ax
 15.8193 +  mov es,    ax      ;; set es to zero fixes [ 549815 ]
 15.8194 +  mov [bp],  ax      ;; set bp to zero
 15.8195 +  mov ax,    #0xaa55 ;; set ok flag
 15.8196 +
 15.8197 +  pop bp
 15.8198 +  iret               ;; Beam me up Scotty
 15.8199 +
 15.8200 +;----------
 15.8201 +;- INT1Ch -
 15.8202 +;----------
 15.8203 +int1c_handler: ;; User Timer Tick
 15.8204 +  iret
 15.8205 +
 15.8206 +
 15.8207 +;----------------------
 15.8208 +;- POST: Floppy Drive -
 15.8209 +;----------------------
 15.8210 +floppy_drive_post:
 15.8211 +  mov  ax, #0x0000
 15.8212 +  mov  ds, ax
 15.8213 +
 15.8214 +  mov  al, #0x00
 15.8215 +  mov  0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
 15.8216 +
 15.8217 +  mov  0x043f, al  ;; diskette motor status: read op, drive0, motors off
 15.8218 +
 15.8219 +  mov  0x0440, al  ;; diskette motor timeout counter: not active
 15.8220 +  mov  0x0441, al  ;; diskette controller status return code
 15.8221 +
 15.8222 +  mov  0x0442, al  ;; disk & diskette controller status register 0
 15.8223 +  mov  0x0443, al  ;; diskette controller status register 1
 15.8224 +  mov  0x0444, al  ;; diskette controller status register 2
 15.8225 +  mov  0x0445, al  ;; diskette controller cylinder number
 15.8226 +  mov  0x0446, al  ;; diskette controller head number
 15.8227 +  mov  0x0447, al  ;; diskette controller sector number
 15.8228 +  mov  0x0448, al  ;; diskette controller bytes written
 15.8229 +
 15.8230 +  mov  0x048b, al  ;; diskette configuration data
 15.8231 +
 15.8232 +  ;; -----------------------------------------------------------------
 15.8233 +  ;; (048F) diskette controller information
 15.8234 +  ;;
 15.8235 +  mov  al, #0x10   ;; get CMOS diskette drive type
 15.8236 +  out  0x70, AL
 15.8237 +  in   AL, 0x71
 15.8238 +  mov  ah, al      ;; save byte to AH
 15.8239 +
 15.8240 +look_drive0:
 15.8241 +  shr  al, #4      ;; look at top 4 bits for drive 0
 15.8242 +  jz   f0_missing  ;; jump if no drive0
 15.8243 +  mov  bl, #0x07   ;; drive0 determined, multi-rate, has changed line
 15.8244 +  jmp  look_drive1
 15.8245 +f0_missing:
 15.8246 +  mov  bl, #0x00   ;; no drive0
 15.8247 +
 15.8248 +look_drive1:
 15.8249 +  mov  al, ah      ;; restore from AH
 15.8250 +  and  al, #0x0f   ;; look at bottom 4 bits for drive 1
 15.8251 +  jz   f1_missing  ;; jump if no drive1
 15.8252 +  or   bl, #0x70   ;; drive1 determined, multi-rate, has changed line
 15.8253 +f1_missing:
 15.8254 +                   ;; leave high bits in BL zerod
 15.8255 +  mov  0x048f, bl  ;; put new val in BDA (diskette controller information)
 15.8256 +  ;; -----------------------------------------------------------------
 15.8257 +
 15.8258 +  mov  al, #0x00
 15.8259 +  mov  0x0490, al  ;; diskette 0 media state
 15.8260 +  mov  0x0491, al  ;; diskette 1 media state
 15.8261 +
 15.8262 +                   ;; diskette 0,1 operational starting state
 15.8263 +                   ;; drive type has not been determined,
 15.8264 +                   ;; has no changed detection line
 15.8265 +  mov  0x0492, al
 15.8266 +  mov  0x0493, al
 15.8267 +
 15.8268 +  mov  0x0494, al  ;; diskette 0 current cylinder
 15.8269 +  mov  0x0495, al  ;; diskette 1 current cylinder
 15.8270 +
 15.8271 +  mov  al, #0x02
 15.8272 +  out  #0x0a, al   ;; clear DMA-1 channel 2 mask bit
 15.8273 +
 15.8274 +  SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
 15.8275 +  SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
 15.8276 +  SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
 15.8277 +
 15.8278 +  ret
 15.8279 +
 15.8280 +
 15.8281 +;--------------------
 15.8282 +;- POST: HARD DRIVE -
 15.8283 +;--------------------
 15.8284 +; relocated here because the primary POST area isnt big enough.
 15.8285 +hard_drive_post:
 15.8286 +  // IRQ 14 = INT 76h
 15.8287 +  // INT 76h calls INT 15h function ax=9100
 15.8288 +
 15.8289 +  mov  al, #0x0a   ; 0000 1010 = reserved, disable IRQ 14
 15.8290 +  mov  dx, #0x03f6
 15.8291 +  out  dx, al
 15.8292 +
 15.8293 +  mov  ax, #0x0000
 15.8294 +  mov  ds, ax
 15.8295 +  mov  0x0474, al /* hard disk status of last operation */
 15.8296 +  mov  0x0477, al /* hard disk port offset (XT only ???) */
 15.8297 +  mov  0x048c, al /* hard disk status register */
 15.8298 +  mov  0x048d, al /* hard disk error register */
 15.8299 +  mov  0x048e, al /* hard disk task complete flag */
 15.8300 +  mov  al, #0x01
 15.8301 +  mov  0x0475, al /* hard disk number attached */
 15.8302 +  mov  al, #0xc0
 15.8303 +  mov  0x0476, al /* hard disk control byte */
 15.8304 +  SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
 15.8305 +  SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
 15.8306 +  ;; INT 41h: hard disk 0 configuration pointer
 15.8307 +  ;; INT 46h: hard disk 1 configuration pointer
 15.8308 +  SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
 15.8309 +  SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
 15.8310 +
 15.8311 +  ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
 15.8312 +  mov  al, #0x12
 15.8313 +  out  #0x70, al
 15.8314 +  in   al, #0x71
 15.8315 +  and  al, #0xf0
 15.8316 +  cmp  al, #0xf0
 15.8317 +  je   post_d0_extended
 15.8318 +  jmp check_for_hd1
 15.8319 +post_d0_extended:
 15.8320 +  mov  al, #0x19
 15.8321 +  out  #0x70, al
 15.8322 +  in   al, #0x71
 15.8323 +  cmp  al, #47  ;; decimal 47 - user definable
 15.8324 +  je   post_d0_type47
 15.8325 +  HALT(__LINE__)
 15.8326 +post_d0_type47:
 15.8327 +  ;; CMOS  purpose                  param table offset
 15.8328 +  ;; 1b    cylinders low            0
 15.8329 +  ;; 1c    cylinders high           1
 15.8330 +  ;; 1d    heads                    2
 15.8331 +  ;; 1e    write pre-comp low       5
 15.8332 +  ;; 1f    write pre-comp high      6
 15.8333 +  ;; 20    retries/bad map/heads>8  8
 15.8334 +  ;; 21    landing zone low         C
 15.8335 +  ;; 22    landing zone high        D
 15.8336 +  ;; 23    sectors/track            E
 15.8337 +
 15.8338 +  mov  ax, #EBDA_SEG
 15.8339 +  mov  ds, ax
 15.8340 +
 15.8341 +  ;;; Filling EBDA table for hard disk 0.
 15.8342 +  mov  al, #0x1f
 15.8343 +  out  #0x70, al
 15.8344 +  in   al, #0x71
 15.8345 +  mov  ah, al
 15.8346 +  mov  al, #0x1e
 15.8347 +  out  #0x70, al
 15.8348 +  in   al, #0x71
 15.8349 +  mov   (0x003d + 0x05), ax ;; write precomp word
 15.8350 +
 15.8351 +  mov  al, #0x20
 15.8352 +  out  #0x70, al
 15.8353 +  in   al, #0x71
 15.8354 +  mov   (0x003d + 0x08), al ;; drive control byte
 15.8355 +
 15.8356 +  mov  al, #0x22
 15.8357 +  out  #0x70, al
 15.8358 +  in   al, #0x71
 15.8359 +  mov  ah, al
 15.8360 +  mov  al, #0x21
 15.8361 +  out  #0x70, al
 15.8362 +  in   al, #0x71
 15.8363 +  mov   (0x003d + 0x0C), ax ;; landing zone word
 15.8364 +
 15.8365 +  mov  al, #0x1c   ;; get cylinders word in AX
 15.8366 +  out  #0x70, al
 15.8367 +  in   al, #0x71   ;; high byte
 15.8368 +  mov  ah, al
 15.8369 +  mov  al, #0x1b
 15.8370 +  out  #0x70, al
 15.8371 +  in   al, #0x71   ;; low byte
 15.8372 +  mov  bx, ax      ;; BX = cylinders
 15.8373 +
 15.8374 +  mov  al, #0x1d
 15.8375 +  out  #0x70, al
 15.8376 +  in   al, #0x71
 15.8377 +  mov  cl, al      ;; CL = heads
 15.8378 +
 15.8379 +  mov  al, #0x23
 15.8380 +  out  #0x70, al
 15.8381 +  in   al, #0x71
 15.8382 +  mov  dl, al      ;; DL = sectors
 15.8383 +
 15.8384 +  cmp  bx, #1024
 15.8385 +  jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
 15.8386 +
 15.8387 +hd0_post_physical_chs:
 15.8388 +  ;; no logical CHS mapping used, just physical CHS
 15.8389 +  ;; use Standard Fixed Disk Parameter Table (FDPT)
 15.8390 +  mov   (0x003d + 0x00), bx ;; number of physical cylinders
 15.8391 +  mov   (0x003d + 0x02), cl ;; number of physical heads
 15.8392 +  mov   (0x003d + 0x0E), dl ;; number of physical sectors
 15.8393 +  jmp check_for_hd1
 15.8394 +
 15.8395 +hd0_post_logical_chs:
 15.8396 +  ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
 15.8397 +  mov   (0x003d + 0x09), bx ;; number of physical cylinders
 15.8398 +  mov   (0x003d + 0x0b), cl ;; number of physical heads
 15.8399 +  mov   (0x003d + 0x04), dl ;; number of physical sectors
 15.8400 +  mov   (0x003d + 0x0e), dl ;; number of logical sectors (same)
 15.8401 +  mov al, #0xa0
 15.8402 +  mov   (0x003d + 0x03), al ;; A0h signature, indicates translated table
 15.8403 +
 15.8404 +  cmp bx, #2048
 15.8405 +  jnbe hd0_post_above_2048
 15.8406 +  ;; 1024 < c <= 2048 cylinders
 15.8407 +  shr bx, #0x01
 15.8408 +  shl cl, #0x01
 15.8409 +  jmp hd0_post_store_logical
 15.8410 +
 15.8411 +hd0_post_above_2048:
 15.8412 +  cmp bx, #4096
 15.8413 +  jnbe hd0_post_above_4096
 15.8414 +  ;; 2048 < c <= 4096 cylinders
 15.8415 +  shr bx, #0x02
 15.8416 +  shl cl, #0x02
 15.8417 +  jmp hd0_post_store_logical
 15.8418 +
 15.8419 +hd0_post_above_4096:
 15.8420 +  cmp bx, #8192
 15.8421 +  jnbe hd0_post_above_8192
 15.8422 +  ;; 4096 < c <= 8192 cylinders
 15.8423 +  shr bx, #0x03
 15.8424 +  shl cl, #0x03
 15.8425 +  jmp hd0_post_store_logical
 15.8426 +
 15.8427 +hd0_post_above_8192:
 15.8428 +  ;; 8192 < c <= 16384 cylinders
 15.8429 +  shr bx, #0x04
 15.8430 +  shl cl, #0x04
 15.8431 +
 15.8432 +hd0_post_store_logical:
 15.8433 +  mov   (0x003d + 0x00), bx ;; number of physical cylinders
 15.8434 +  mov   (0x003d + 0x02), cl ;; number of physical heads
 15.8435 +  ;; checksum
 15.8436 +  mov   cl, #0x0f     ;; repeat count
 15.8437 +  mov   si, #0x003d   ;; offset to disk0 FDPT
 15.8438 +  mov   al, #0x00     ;; sum
 15.8439 +hd0_post_checksum_loop:
 15.8440 +  add   al, [si]
 15.8441 +  inc   si
 15.8442 +  dec   cl
 15.8443 +  jnz hd0_post_checksum_loop
 15.8444 +  not   al  ;; now take 2s complement
 15.8445 +  inc   al
 15.8446 +  mov   [si], al
 15.8447 +;;; Done filling EBDA table for hard disk 0.
 15.8448 +
 15.8449 +
 15.8450 +check_for_hd1:
 15.8451 +  ;; is there really a second hard disk?  if not, return now
 15.8452 +  mov  al, #0x12
 15.8453 +  out  #0x70, al
 15.8454 +  in   al, #0x71
 15.8455 +  and  al, #0x0f
 15.8456 +  jnz   post_d1_exists
 15.8457 +  ret
 15.8458 +post_d1_exists:
 15.8459 +  ;; check that the hd type is really 0x0f.
 15.8460 +  cmp al, #0x0f
 15.8461 +  jz post_d1_extended
 15.8462 +  HALT(__LINE__)
 15.8463 +post_d1_extended:
 15.8464 +  ;; check that the extended type is 47 - user definable
 15.8465 +  mov  al, #0x1a
 15.8466 +  out  #0x70, al
 15.8467 +  in   al, #0x71
 15.8468 +  cmp  al, #47  ;; decimal 47 - user definable
 15.8469 +  je   post_d1_type47
 15.8470 +  HALT(__LINE__)
 15.8471 +post_d1_type47:
 15.8472 +  ;; Table for disk1.
 15.8473 +  ;; CMOS  purpose                  param table offset
 15.8474 +  ;; 0x24    cylinders low            0
 15.8475 +  ;; 0x25    cylinders high           1
 15.8476 +  ;; 0x26    heads                    2
 15.8477 +  ;; 0x27    write pre-comp low       5
 15.8478 +  ;; 0x28    write pre-comp high      6
 15.8479 +  ;; 0x29    heads>8                  8
 15.8480 +  ;; 0x2a    landing zone low         C
 15.8481 +  ;; 0x2b    landing zone high        D
 15.8482 +  ;; 0x2c    sectors/track            E
 15.8483 +;;; Fill EBDA table for hard disk 1.
 15.8484 +  mov  a