ia64/xen-unstable

changeset 5370:5f9a0c66f74b

bitkeeper revision 1.1691.1.5 (42a6b4baIjkVZx9lVWvoA9RqgAGLMQ)

The following allows you to run unmodified guest operating systems
under Xen on VMX (VT) enabled processors. The tree lives under
<ROOT>/tools/dfw. Instead of booting a guest kernel, boot vmxloader and
specify the disk image in qemurc.

Leendert

Signed-Off-By: Leendert van Doorn <leendert@watson.ibm.com>
author kaf24@firebug.cl.cam.ac.uk
date Wed Jun 08 09:04:58 2005 +0000 (2005-06-08)
parents 6410a7fd8ff8
children d29fe2289a59
files .rootkeys 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
line diff
     1.1 --- a/.rootkeys	Wed Jun 08 08:40:39 2005 +0000
     1.2 +++ b/.rootkeys	Wed Jun 08 09:04:58 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 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/tools/firmware/Makefile	Wed Jun 08 09:04:58 2005 +0000
     2.3 @@ -0,0 +1,25 @@
     2.4 +XEN_ROOT = ../..
     2.5 +include $(XEN_ROOT)/tools/Rules.mk
     2.6 +
     2.7 +SUBDIRS :=
     2.8 +SUBDIRS += rombios
     2.9 +SUBDIRS += vgabios
    2.10 +SUBDIRS += vmxassist
    2.11 +
    2.12 +.PHONY: all install clean
    2.13 +
    2.14 +all: 
    2.15 +	@set -e; for subdir in $(SUBDIRS); do \
    2.16 +		$(MAKE) -C $$subdir $@; \
    2.17 +	done
    2.18 +
    2.19 +install: 
    2.20 +	@set -e; for subdir in $(SUBDIRS); do \
    2.21 +		$(MAKE) -C $$subdir $@; \
    2.22 +	done
    2.23 +
    2.24 +clean: 
    2.25 +	@set -e; for subdir in $(SUBDIRS); do \
    2.26 +		$(MAKE) -C $$subdir $@; \
    2.27 +	done
    2.28 +
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/tools/firmware/README	Wed Jun 08 09:04:58 2005 +0000
     3.3 @@ -0,0 +1,88 @@
     3.4 +Domain FirmWare support
     3.5 +-----------------------
     3.6 +
     3.7 +One of the key advantages of full virtualization hardware support (such
     3.8 +as Intel's VT or AMD's Pacifica) is the ability to run unmodified guest
     3.9 +operating systems.  However, since most OSes rely on BIOS support during
    3.10 +their early bringup, we need to provide a surrogate ROMBIOS and VGABIOS
    3.11 +firmware layer.
    3.12 +
    3.13 +What's more, we need to support real-mode which is required by
    3.14 +the firmware and bootstrap loaders. Real-mode support is especially
    3.15 +challenging for Intel's VMX (VT) enabled CPUs where there is no real-mode
    3.16 +support for VMX guest partitions. In this case you either have to do full
    3.17 +emulation (full real-mode emulator; more complete but potentially slower)
    3.18 +or partial emulation (use the VM8086 extensions, emulate only those
    3.19 +instructions that are missing; faster, but potentially incomplete). The
    3.20 +vmxassist code in this subdirectory uses the later approach because it
    3.21 +is smaller and faster.
    3.22 +
    3.23 +The approach is relatively straight forward. Vmxloader contains three
    3.24 +payloads (rombios, vgabios and vmxassist) and it is bootstrapped as any
    3.25 +other 32-bit OS. Vmxloader copies its payloads to the addresses below
    3.26 +and transfers control to vmxassist.
    3.27 +
    3.28 +	vgabios		VGABIOS (standard and Cirrus).
    3.29 +			Resides at C000:0000.
    3.30 +
    3.31 +	vmxassist	VMXAssist VM86 realmode emulator for VMX.
    3.32 +			Resides at D000:0000.
    3.33 +
    3.34 +	rombios		ROMBIOS code. Derived from Bochs.
    3.35 +			Resides at F000:0000
    3.36 +
    3.37 +Vmxassist first sets up it own world (GDT, IDT, TR, etc), enables
    3.38 +VM8086 and then transfers control to F000:FFF0 and executes 16-bit
    3.39 +code. Unsupported instructions cause a general protection failure at
    3.40 +which point vmxassist kicks in and emulates the offending instruction.
    3.41 +Whever the emulated code transitions to 32-bit protected mode, vmxassist
    3.42 +will go away. Whenever 32-bit protected code transitions to real-mode,
    3.43 +Xen/VMX will detect this and transfer control to vmxassist.
    3.44 +
    3.45 +Most of the vmxassist complexity comes from properly handling the
    3.46 +real to protected mode and protected to real mode transitions and
    3.47 +the proper emulation of the segment registers. Even though the Intel
    3.48 +manual clearly states that you should immediately perform a jmp far
    3.49 +after a mode transition, many operating systems execute additional
    3.50 +instructions and some even refer to segment selectors and pop data
    3.51 +from the stack. Vmxassist contains a number of work arounds for these
    3.52 +OSes.
    3.53 +
    3.54 +
    3.55 +Acknowledgements
    3.56 +----------------
    3.57 +
    3.58 +The rombios was taken (largely unmodified) from Bochs, which was written
    3.59 +by Kevin Lawton. The VGABIOS was written by Christophe Bothamy. Arun Sharma,
    3.60 +Asit Mallick and Nitin Kamble (Intel) provided the E820 patches and lots
    3.61 +of useful feedback.
    3.62 +
    3.63 +
    3.64 +Contact
    3.65 +-------
    3.66 +
    3.67 +Leendert van Doorn
    3.68 +IBM T.J. Watson Research Center
    3.69 +19 Skyline Drive
    3.70 +Hawthorne, NY 10532
    3.71 +leendert@watson.ibm.com
    3.72 +
    3.73 +
    3.74 +Tested Operating Systems
    3.75 +------------------------
    3.76 +
    3.77 +Since vmxassist uses partial emulation, it may always miss opcodes
    3.78 +that are required by a particular OS. The table below lists the OSes
    3.79 +I have tried.  The Install column indicates a full CD/DVD install into
    3.80 +a VMX partition. The Disk column indicates booting from prefabricated
    3.81 +disk image.
    3.82 +
    3.83 +Operating System			Install		Disk
    3.84 +------------------------------------------------------------
    3.85 +RedHat Enterprise Linux (RHEL3_U5)	Yes		Yes
    3.86 +Fedora Code (FC3)			(-)		Yes
    3.87 +FreeBSD 5.3				(-)		Yes
    3.88 +MS-DOS 5.0				(-)		Yes
    3.89 +
    3.90 +(-) not tried yet
    3.91 +
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/tools/firmware/rombios/Makefile	Wed Jun 08 09:04:58 2005 +0000
     4.3 @@ -0,0 +1,58 @@
     4.4 +BIOS_BUILDS = BIOS-bochs-latest
     4.5 +#BIOS_BUILDS += BIOS-bochs-2-processors
     4.6 +#BIOS_BUILDS += BIOS-bochs-4-processors
     4.7 +#BIOS_BUILDS += BIOS-bochs-8-processors
     4.8 +
     4.9 +all: bios
    4.10 +
    4.11 +bios: biossums ${BIOS_BUILDS}
    4.12 +
    4.13 +clean:
    4.14 +	rm -f  *.o *.a *.s rombios.bin _rombios*_.c
    4.15 +	rm -f  as86-sym.txt ld86-sym.txt 
    4.16 +	rm -f  rombios*.txt rombios*.sym usage biossums
    4.17 +	rm -f  BIOS-bochs-*
    4.18 +
    4.19 +BIOS-bochs-latest: rombios.c biossums
    4.20 +	gcc -DBX_SMP_PROCESSORS=1 -E -P $< > _rombios_.c
    4.21 +	bcc -o rombios.s -C-c -D__i86__ -0 -S _rombios_.c
    4.22 +	sed -e 's/^\.text//' -e 's/^\.data//' rombios.s > _rombios_.s
    4.23 +	as86 _rombios_.s -b tmp.bin -u- -w- -g -0 -j -O -l rombios.txt
    4.24 +	-perl makesym.perl < rombios.txt > rombios.sym
    4.25 +	mv tmp.bin BIOS-bochs-latest
    4.26 +	./biossums BIOS-bochs-latest
    4.27 +	rm -f _rombios_.s
    4.28 +
    4.29 +BIOS-bochs-2-processors: rombios.c biossums
    4.30 +	gcc -DBX_SMP_PROCESSORS=2 -E -P $< > _rombios2_.c
    4.31 +	bcc -o rombios2.s -C-c -D__i86__ -0 -S _rombios2_.c
    4.32 +	sed -e 's/^\.text//' -e 's/^\.data//' rombios2.s > _rombios2_.s
    4.33 +	as86 _rombios2_.s -b tmp2.bin -u- -w- -g -0 -j -O -l rombios2.txt
    4.34 +	-perl makesym.perl < rombios2.txt > rombios2.sym
    4.35 +	mv tmp2.bin BIOS-bochs-2-processors
    4.36 +	./biossums BIOS-bochs-2-processors
    4.37 +	rm -f _rombios2_.s
    4.38 +
    4.39 +BIOS-bochs-4-processors: rombios.c biossums
    4.40 +	gcc -DBX_SMP_PROCESSORS=4 -E -P $< > _rombios4_.c
    4.41 +	bcc -o rombios4.s -C-c -D__i86__ -0 -S _rombios4_.c
    4.42 +	sed -e 's/^\.text//' -e 's/^\.data//' rombios4.s > _rombios4_.s
    4.43 +	as86 _rombios4_.s -b tmp4.bin -u- -w- -g -0 -j -O -l rombios4.txt
    4.44 +	-perl makesym.perl < rombios4.txt > rombios4.sym
    4.45 +	mv tmp4.bin BIOS-bochs-4-processors
    4.46 +	./biossums BIOS-bochs-4-processors
    4.47 +	rm -f _rombios4_.s
    4.48 +
    4.49 +BIOS-bochs-8-processors: rombios.c biossums
    4.50 +	gcc -DBX_SMP_PROCESSORS=8 -E -P $< > _rombios8_.c
    4.51 +	bcc -o rombios8.s -C-c -D__i86__ -0 -S _rombios8_.c
    4.52 +	sed -e 's/^\.text//' -e 's/^\.data//' rombios8.s > _rombios8_.s
    4.53 +	as86 _rombios8_.s -b tmp8.bin -u- -w- -g -0 -j -O -l rombios8.txt
    4.54 +	-perl makesym.perl < rombios8.txt > rombios8.sym
    4.55 +	mv tmp8.bin BIOS-bochs-8-processors
    4.56 +	./biossums BIOS-bochs-8-processors
    4.57 +	rm -f _rombios8_.s
    4.58 +
    4.59 +biossums: biossums.c
    4.60 +	gcc -o biossums biossums.c
    4.61 +
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/tools/firmware/rombios/apmbios.S	Wed Jun 08 09:04:58 2005 +0000
     5.3 @@ -0,0 +1,329 @@
     5.4 +//  APM BIOS support for the Bochs BIOS
     5.5 +//  Copyright (C) 2004 Fabrice Bellard
     5.6 +//
     5.7 +//  Debugging extensions, 16-bit interface and extended power options
     5.8 +//  Copyright (C) 2005 Struan Bartlett
     5.9 +//
    5.10 +//  This library is free software; you can redistribute it and/or
    5.11 +//  modify it under the terms of the GNU Lesser General Public
    5.12 +//  License as published by the Free Software Foundation; either
    5.13 +//  version 2 of the License, or (at your option) any later version.
    5.14 +//
    5.15 +//  This library is distributed in the hope that it will be useful,
    5.16 +//  but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.17 +//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    5.18 +//  Lesser General Public License for more details.
    5.19 +//
    5.20 +//  You should have received a copy of the GNU Lesser General Public
    5.21 +//  License along with this library; if not, write to the Free Software
    5.22 +//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
    5.23 +
    5.24 +#if defined(APM_REAL)
    5.25 +#define APMSYM(s) apmreal_ ## s
    5.26 +#elif defined(APM_PROT16)
    5.27 +#define APMSYM(s) apm16_ ## s
    5.28 +#elif defined(APM_PROT32)
    5.29 +#define APMSYM(s) apm32_ ## s
    5.30 +#else
    5.31 +#error unsupported APM mode
    5.32 +#endif
    5.33 +
    5.34 +APMSYM(out_str):      
    5.35 +  push eax
    5.36 +  push ebx
    5.37 +  mov ebx, eax
    5.38 +APMSYM(out_str1):
    5.39 +  SEG CS
    5.40 +  mov al, byte ptr [bx]
    5.41 +  cmp al, #0
    5.42 +  je APMSYM(out_str2)
    5.43 +  outb dx, al
    5.44 +  inc ebx
    5.45 +  jmp APMSYM(out_str1)
    5.46 +APMSYM(out_str2):
    5.47 +  pop ebx
    5.48 +  pop eax
    5.49 +  ret
    5.50 +  
    5.51 +APMSYM(07_poweroff_str):
    5.52 +  .ascii "Shutdown"
    5.53 +  db 0
    5.54 +APMSYM(07_suspend_str):
    5.55 +  .ascii "Suspend"
    5.56 +  db 0
    5.57 +APMSYM(07_standby_str):
    5.58 +  .ascii "Standby"
    5.59 +  db 0
    5.60 +  
    5.61 +#if DEBUG_APM
    5.62 +APMSYM(put_str):      
    5.63 +  push edx
    5.64 +  mov dx, #INFO_PORT
    5.65 +  call APMSYM(out_str)
    5.66 +  pop edx
    5.67 +  ret
    5.68 +  
    5.69 +; print the hex number in eax
    5.70 +APMSYM(put_num):      
    5.71 +  push eax
    5.72 +  push ebx
    5.73 +  push ecx
    5.74 +  push edx
    5.75 +  mov ecx, eax
    5.76 +  mov bx, #8
    5.77 +  mov dx, #INFO_PORT
    5.78 +APMSYM(put_num1):
    5.79 +  mov eax, ecx
    5.80 +  shr eax, #28
    5.81 +  add al, #0x30
    5.82 +  cmp al, #0x39
    5.83 +  jbe APMSYM(put_num2)
    5.84 +  add al, #0x27
    5.85 +APMSYM(put_num2):
    5.86 +  outb dx, al
    5.87 +  shl ecx, #4
    5.88 +  dec bx
    5.89 +  jne APMSYM(put_num1)
    5.90 +  pop edx
    5.91 +  pop ecx
    5.92 +  pop ebx
    5.93 +  pop eax
    5.94 +  ret
    5.95 +
    5.96 +APMSYM(put_reg):
    5.97 +  outb dx, al
    5.98 +  shr eax, #8
    5.99 +  outb dx, al
   5.100 +  shr eax, #8
   5.101 +  outb dx, al
   5.102 +  shr eax, #8
   5.103 +  outb dx, al
   5.104 +  
   5.105 +  mov eax,ebx
   5.106 +  call APMSYM(put_num)
   5.107 +  
   5.108 +  mov al, #0x3b
   5.109 +  outb dx,al
   5.110 +  mov al, #0x20
   5.111 +  outb dx,al
   5.112 +  ret  
   5.113 +
   5.114 +APMSYM(put_regs):
   5.115 +  push eax
   5.116 +  push edx
   5.117 +  push ebx
   5.118 +  mov dx, #INFO_PORT
   5.119 +  
   5.120 +  mov ebx, eax
   5.121 +  mov eax, #0x3d584145 // 'EAX='
   5.122 +  call APMSYM(put_reg)
   5.123 +  pop ebx
   5.124 +  push ebx
   5.125 +  mov eax, #0x3d584245 // 'EBX='
   5.126 +  call APMSYM(put_reg)
   5.127 +  mov ebx, ecx
   5.128 +  mov eax, #0x3d584345 // 'ECX='
   5.129 +  call APMSYM(put_reg)
   5.130 +  mov ebx, edx
   5.131 +  mov eax, #0x3d584445 // 'EDX='
   5.132 +  call APMSYM(put_reg)
   5.133 +  mov ebx, esi
   5.134 +  mov eax, #0x3d495345 // 'ESI='
   5.135 +  call APMSYM(put_reg)
   5.136 +  mov ebx, edi
   5.137 +  mov eax, #0x3d494445 // 'EDI='
   5.138 +  call APMSYM(put_reg)
   5.139 +  
   5.140 +  mov al, #0x0a
   5.141 +  outb dx, al
   5.142 +  pop ebx
   5.143 +  pop edx
   5.144 +  pop eax
   5.145 +  ret
   5.146 +#endif
   5.147 +
   5.148 +#if defined(APM_PROT32)
   5.149 +_apm32_entry:
   5.150 +#endif
   5.151 +#if defined(APM_PROT16)
   5.152 +_apm16_entry:
   5.153 +#endif
   5.154 +  pushf
   5.155 +  
   5.156 +#if defined(APM_REAL)
   5.157 +_apmreal_entry:
   5.158 +#endif
   5.159 +
   5.160 +#if DEBUG_APM
   5.161 +  call APMSYM(put_regs)
   5.162 +#endif
   5.163 +
   5.164 +#if defined(APM_REAL)
   5.165 +;-----------------
   5.166 +; APM installation check
   5.167 +APMSYM(00):
   5.168 +  cmp al, #0x00
   5.169 +  jne APMSYM(01)
   5.170 +
   5.171 +  mov ah, #1 // APM major version
   5.172 +  mov al, #2 // APM minor version
   5.173 +  
   5.174 +  mov bh, #0x50 // 'P'
   5.175 +  mov bl, #0x4d // 'M'
   5.176 +  
   5.177 +  // bit 0 : 16 bit interface supported
   5.178 +  // bit 1 : 32 bit interface supported
   5.179 +  mov cx, #0x3
   5.180 +  jmp APMSYM(ok)
   5.181 +  
   5.182 +;-----------------
   5.183 +; APM real mode interface connect
   5.184 +APMSYM(01):
   5.185 +  cmp al, #0x01
   5.186 +  jne APMSYM(02)
   5.187 +  jmp APMSYM(ok)
   5.188 +
   5.189 +;-----------------
   5.190 +; APM 16 bit protected mode interface connect
   5.191 +APMSYM(02):
   5.192 +  cmp al, #0x02
   5.193 +  jne APMSYM(03)
   5.194 +
   5.195 +  mov bx, #_apm16_entry
   5.196 +  
   5.197 +  mov ax, #0xf000 // 16 bit code segment base
   5.198 +  mov si, #0xfff0 // 16 bit code segment size
   5.199 +  mov cx, #0xf000 // data segment address
   5.200 +  mov di, #0xfff0 // data segment length
   5.201 +  jmp APMSYM(ok)
   5.202 +
   5.203 +;-----------------
   5.204 +; APM 32 bit protected mode interface connect
   5.205 +APMSYM(03):
   5.206 +  cmp al, #0x03
   5.207 +  jne APMSYM(04)
   5.208 +  mov ax, #0xf000 // 32 bit code segment base
   5.209 +  mov ebx, #_apm32_entry
   5.210 +  mov cx, #0xf000 // 16 bit code segment base
   5.211 +  // 32 bit code segment size (low 16 bits)
   5.212 +  // 16 bit code segment size (high 16 bits)
   5.213 +  mov esi, #0xfff0fff0
   5.214 +  mov dx, #0xf000 // data segment address
   5.215 +  mov di, #0xfff0 // data segment length
   5.216 +  jmp APMSYM(ok)
   5.217 +#endif
   5.218 +
   5.219 +;-----------------
   5.220 +; APM interface disconnect
   5.221 +APMSYM(04):
   5.222 +  cmp al, #0x04
   5.223 +  jne APMSYM(07)
   5.224 +  jmp APMSYM(ok)
   5.225 +
   5.226 +;-----------------
   5.227 +; APM Set Power State
   5.228 +APMSYM(07):
   5.229 +  cmp al, #0x07
   5.230 +  jne APMSYM(0a)
   5.231 +  
   5.232 +  cmp bx, #1
   5.233 +  jne APMSYM(ok)
   5.234 +  
   5.235 +  cmp cx, #3
   5.236 +  je APMSYM(07_poweroff)
   5.237 +  
   5.238 +  cmp cx, #2
   5.239 +  je APMSYM(07_suspend)
   5.240 +  
   5.241 +  cmp cx, #1
   5.242 +  je APMSYM(07_standby)
   5.243 +  
   5.244 +  jne APMSYM(ok)
   5.245 +  
   5.246 +APMSYM(07_poweroff):  
   5.247 +  // send power off event to emulator
   5.248 +  cli
   5.249 +  mov dx, #0x8900
   5.250 +  mov ax, #APMSYM(07_poweroff_str)
   5.251 +  call APMSYM(out_str)
   5.252 +
   5.253 +APMSYM(07_1):
   5.254 +  hlt
   5.255 +  jmp APMSYM(07_1)
   5.256 +
   5.257 +APMSYM(07_suspend):
   5.258 +  push edx
   5.259 +  mov dx, #0x8900
   5.260 +  mov ax, #APMSYM(07_suspend_str)
   5.261 +  call APMSYM(out_str)
   5.262 +  pop edx
   5.263 +  jmp APMSYM(ok)
   5.264 +
   5.265 +APMSYM(07_standby):
   5.266 +  push edx
   5.267 +  mov dx, #0x8900
   5.268 +  mov ax, #APMSYM(07_standby_str)
   5.269 +  call APMSYM(out_str)
   5.270 +  pop edx
   5.271 +  jmp APMSYM(ok)
   5.272 +
   5.273 +;-----------------
   5.274 +; Get Power Status
   5.275 +APMSYM(0a):
   5.276 +  cmp al, #0x0a
   5.277 +  jne APMSYM(0b)
   5.278 +  mov bh, #0x01 // on line
   5.279 +  // mov bh, #0x02 // battery
   5.280 +  mov bl, #0xff // unknown battery status
   5.281 +  // mov bl, #0x03 // charging
   5.282 +  mov ch, #0x80 // no system battery
   5.283 +  // mov ch, #0x8 // charging
   5.284 +  mov cl, #0xff // unknown remaining time
   5.285 +  // mov cl, #50
   5.286 +  mov dx, #0xffff // unknown remaining time 
   5.287 +  mov si, #0      // zero battery
   5.288 +  // mov si, #1      // one battery
   5.289 +  jmp APMSYM(ok)
   5.290 +
   5.291 +;-----------------
   5.292 +; Get PM Event
   5.293 +APMSYM(0b):
   5.294 +  cmp al, #0x0b
   5.295 +  jne APMSYM(0e)
   5.296 +  mov ah, #0x80 // no event pending
   5.297 +  jmp APMSYM(error)
   5.298 +   
   5.299 +;-----------------
   5.300 +; APM Driver Version
   5.301 +APMSYM(0e):
   5.302 +  cmp al, #0x0e
   5.303 +  jne APMSYM(unimplemented)
   5.304 +  
   5.305 +  mov ah, #1
   5.306 +  mov al, #2
   5.307 +  
   5.308 +  jmp APMSYM(ok)
   5.309 +
   5.310 +;-----------------
   5.311 +APMSYM(ok):
   5.312 +  popf
   5.313 +  clc
   5.314 +#if defined(APM_REAL)
   5.315 +  jmp iret_modify_cf
   5.316 +#else
   5.317 +  retf  
   5.318 +#endif
   5.319 +APMSYM(unimplemented):
   5.320 +APMSYM(error):
   5.321 +  popf
   5.322 +  stc
   5.323 +#if defined(APM_REAL)
   5.324 +  jmp iret_modify_cf
   5.325 +#else
   5.326 +  retf
   5.327 +#endif
   5.328 +
   5.329 +#undef APM_PROT32
   5.330 +#undef APM_PROT16
   5.331 +#undef APM_REAL
   5.332 +#undef APMSYM
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/tools/firmware/rombios/biossums.c	Wed Jun 08 09:04:58 2005 +0000
     6.3 @@ -0,0 +1,478 @@
     6.4 +/* biossums.c  --- written by Eike W. */
     6.5 +
     6.6 +#include <stdlib.h>
     6.7 +#include <stdio.h>
     6.8 +
     6.9 +typedef unsigned char byte;
    6.10 +
    6.11 +void check( int value, char* message );
    6.12 +
    6.13 +#define LEN_BIOS_DATA 0x10000
    6.14 +#define MAX_OFFSET    (LEN_BIOS_DATA - 1)
    6.15 +
    6.16 +
    6.17 +#define BIOS_OFFSET 0xFFFF
    6.18 +
    6.19 +long chksum_bios_get_offset( byte* data, long offset );
    6.20 +byte chksum_bios_calc_value( byte* data, long offset );
    6.21 +byte chksum_bios_get_value(  byte* data, long offset );
    6.22 +void chksum_bios_set_value(  byte* data, long offset, byte value );
    6.23 +
    6.24 +
    6.25 +#define _32__LEN         9
    6.26 +#define _32__CHKSUM     10
    6.27 +
    6.28 +#define _32__MINHDR     16
    6.29 +
    6.30 +long chksum__32__get_offset( byte* data, long offset );
    6.31 +byte chksum__32__calc_value( byte* data, long offset );
    6.32 +byte chksum__32__get_value(  byte* data, long offset );
    6.33 +void chksum__32__set_value(  byte* data, long offset, byte value );
    6.34 +
    6.35 +
    6.36 +#define _MP__LEN         8
    6.37 +#define _MP__CHKSUM     10
    6.38 +
    6.39 +#define _MP__MINHDR     16
    6.40 +
    6.41 +long chksum__mp__get_offset( byte* data, long offset );
    6.42 +byte chksum__mp__calc_value( byte* data, long offset );
    6.43 +byte chksum__mp__get_value(  byte* data, long offset );
    6.44 +void chksum__mp__set_value(  byte* data, long offset, byte value );
    6.45 +
    6.46 +
    6.47 +#define PCMP_BASELEN     4
    6.48 +#define PCMP_CHKSUM      7
    6.49 +#define PCMP_EXT_LEN    40
    6.50 +#define PCMP_EXT_CHKSUM 42
    6.51 +
    6.52 +#define PCMP_MINHDR     42
    6.53 +
    6.54 +long chksum_pcmp_get_offset( byte* data, long offset );
    6.55 +byte chksum_pcmp_calc_value( byte* data, long offset );
    6.56 +byte chksum_pcmp_get_value(  byte* data, long offset );
    6.57 +void chksum_pcmp_set_value(  byte* data, long offset, byte value );
    6.58 +
    6.59 +
    6.60 +#define _PIR_LEN         6
    6.61 +#define _PIR_CHKSUM     31
    6.62 +
    6.63 +#define _PIR_MINHDR     32
    6.64 +
    6.65 +long chksum__pir_get_offset( byte *data, long offset );
    6.66 +byte chksum__pir_calc_value( byte* data, long offset );
    6.67 +byte chksum__pir_get_value(  byte* data, long offset );
    6.68 +void chksum__pir_set_value(  byte* data, long offset, byte value );
    6.69 +
    6.70 +
    6.71 +byte bios_data[LEN_BIOS_DATA];
    6.72 +
    6.73 +
    6.74 +int main( int argc, char* argv[] ) {
    6.75 +
    6.76 +  FILE* stream;
    6.77 +  long  offset, tmp_offset;
    6.78 +  byte  cur_val = 0, new_val = 0;
    6.79 +  int   hits;
    6.80 +
    6.81 +
    6.82 +  if( argc != 2 ) {
    6.83 +    printf( "Error. Need a file-name as an argument.\n" );
    6.84 +    exit( EXIT_FAILURE );
    6.85 +  }
    6.86 +
    6.87 +  if(( stream = fopen( argv[1], "rb" )) == NULL ) {
    6.88 +    printf( "Error opening %s for reading.\n", argv[1] );
    6.89 +    exit( EXIT_FAILURE );
    6.90 +  }
    6.91 +  if( fread( bios_data, 1, LEN_BIOS_DATA, stream ) < LEN_BIOS_DATA ) {
    6.92 +    printf( "Error reading 64KBytes from %s.\n", argv[1] );
    6.93 +    fclose( stream );
    6.94 +    exit( EXIT_FAILURE );
    6.95 +  }
    6.96 +  fclose( stream );
    6.97 +
    6.98 +  hits   = 0;
    6.99 +  offset = 0L;
   6.100 +  while( (tmp_offset = chksum__32__get_offset( bios_data, offset )) != -1L ) {
   6.101 +    offset  = tmp_offset;
   6.102 +    cur_val = chksum__32__get_value(  bios_data, offset );
   6.103 +    new_val = chksum__32__calc_value( bios_data, offset );
   6.104 +    printf( "\n\nPCI-Bios header at: 0x%4lX\n", offset  );
   6.105 +    printf( "Current checksum:     0x%02X\n",   cur_val );
   6.106 +    printf( "Calculated checksum:  0x%02X  ",   new_val );
   6.107 +    hits++;
   6.108 +  }
   6.109 +  if( hits == 1 && cur_val != new_val ) {
   6.110 +    printf( "Setting checksum." );
   6.111 +    chksum__32__set_value( bios_data, offset, new_val );
   6.112 +  }
   6.113 +  if( hits >= 2 ) {
   6.114 +    printf( "Multiple PCI headers! No checksum set." );
   6.115 +  }
   6.116 +  if( hits ) {
   6.117 +    printf( "\n" );
   6.118 +  }
   6.119 +
   6.120 +
   6.121 +  hits   = 0;
   6.122 +  offset = 0L;
   6.123 +  while( (tmp_offset = chksum__mp__get_offset( bios_data, offset )) != -1L ) {
   6.124 +    offset  = tmp_offset;
   6.125 +    cur_val = chksum__mp__get_value(  bios_data, offset );
   6.126 +    new_val = chksum__mp__calc_value( bios_data, offset );
   6.127 +    printf( "\n\nMP header at:       0x%4lX\n", offset  );
   6.128 +    printf( "Current checksum:     0x%02X\n",   cur_val );
   6.129 +    printf( "Calculated checksum:  0x%02X  ",   new_val );
   6.130 +    hits++;
   6.131 +  }
   6.132 +  if( hits == 1 && cur_val != new_val ) {
   6.133 +    printf( "Setting checksum." );
   6.134 +    chksum__mp__set_value( bios_data, offset, new_val );
   6.135 +  }
   6.136 +  if( hits >= 2 ) {
   6.137 +    printf( "Warning! Multiple MP headers. No checksum set." );
   6.138 +  }
   6.139 +  if( hits ) {
   6.140 +    printf( "\n" );
   6.141 +  }
   6.142 +
   6.143 +
   6.144 +  hits   = 0;
   6.145 +  offset = 0L;
   6.146 +  while( (tmp_offset = chksum_pcmp_get_offset( bios_data, offset )) != -1L ) {
   6.147 +    offset  = tmp_offset;
   6.148 +    cur_val = chksum_pcmp_get_value(  bios_data, offset );
   6.149 +    new_val = chksum_pcmp_calc_value( bios_data, offset );
   6.150 +    printf( "\n\nPCMP header at:     0x%4lX\n", offset  );
   6.151 +    printf( "Current checksum:     0x%02X\n",   cur_val );
   6.152 +    printf( "Calculated checksum:  0x%02X  ",   new_val );
   6.153 +    hits++;
   6.154 +  }
   6.155 +  if( hits == 1 && cur_val != new_val ) {
   6.156 +    printf( "Setting checksum." );
   6.157 +    chksum_pcmp_set_value( bios_data, offset, new_val );
   6.158 +  }
   6.159 +  if( hits >= 2 ) {
   6.160 +    printf( "Warning! Multiple PCMP headers. No checksum set." );
   6.161 +  }
   6.162 +  if( hits ) {
   6.163 +    printf( "\n" );
   6.164 +  }
   6.165 +
   6.166 +
   6.167 +  hits   = 0;
   6.168 +  offset = 0L;
   6.169 +  while( (tmp_offset = chksum__pir_get_offset( bios_data, offset )) != -1L ) {
   6.170 +    offset  = tmp_offset;
   6.171 +    cur_val = chksum__pir_get_value(  bios_data, offset );
   6.172 +    new_val = chksum__pir_calc_value( bios_data, offset );
   6.173 +    printf( "\n\n$PIR header at:     0x%4lX\n", offset  );
   6.174 +    printf( "Current checksum:     0x%02X\n",   cur_val );
   6.175 +    printf( "Calculated checksum:  0x%02X\n  ",  new_val );
   6.176 +    hits++;
   6.177 +  }
   6.178 +  if( hits == 1 && cur_val != new_val ) {
   6.179 +    printf( "Setting checksum." );
   6.180 +    chksum__pir_set_value( bios_data, offset, new_val );
   6.181 +  }
   6.182 +  if( hits >= 2 ) {
   6.183 +    printf( "Warning! Multiple $PIR headers. No checksum set." );
   6.184 +  }
   6.185 +  if( hits ) {
   6.186 +    printf( "\n" );
   6.187 +  }
   6.188 +
   6.189 +
   6.190 +  offset  = 0L;
   6.191 +  offset  = chksum_bios_get_offset( bios_data, offset );
   6.192 +  cur_val = chksum_bios_get_value(  bios_data, offset );
   6.193 +  new_val = chksum_bios_calc_value( bios_data, offset );
   6.194 +  printf( "\n\nBios checksum at:   0x%4lX\n", offset  );
   6.195 +  printf( "Current checksum:     0x%02X\n",   cur_val );
   6.196 +  printf( "Calculated checksum:  0x%02X  ",   new_val );
   6.197 +  if( cur_val != new_val ) {
   6.198 +    printf( "Setting checksum." );
   6.199 +    chksum_bios_set_value( bios_data, offset, new_val );
   6.200 +  }
   6.201 +  printf( "\n" );
   6.202 +
   6.203 +
   6.204 +  if(( stream = fopen( argv[1], "wb" )) == NULL ) {
   6.205 +    printf( "Error opening %s for writing.\n", argv[1] );
   6.206 +    exit( EXIT_FAILURE );
   6.207 +  }
   6.208 +  if( fwrite( bios_data, 1, LEN_BIOS_DATA, stream ) < LEN_BIOS_DATA ) {
   6.209 +    printf( "Error writing 64KBytes to %s.\n", argv[1] );
   6.210 +    fclose( stream );
   6.211 +    exit( EXIT_FAILURE );
   6.212 +  }
   6.213 +  fclose( stream );
   6.214 +
   6.215 +  return( EXIT_SUCCESS );
   6.216 +}
   6.217 +
   6.218 +
   6.219 +void check( int okay, char* message ) {
   6.220 +
   6.221 +  if( !okay ) {
   6.222 +    printf( "\n\nError. %s.\n", message );
   6.223 +    exit( EXIT_FAILURE );
   6.224 +  }
   6.225 +}
   6.226 +
   6.227 +
   6.228 +long chksum_bios_get_offset( byte* data, long offset ) {
   6.229 +
   6.230 +  return( BIOS_OFFSET );
   6.231 +}
   6.232 +
   6.233 +
   6.234 +byte chksum_bios_calc_value( byte* data, long offset ) {
   6.235 +
   6.236 +  int   i;
   6.237 +  byte  sum;
   6.238 +
   6.239 +  sum = 0;
   6.240 +  for( i = 0; i < MAX_OFFSET; i++ ) {
   6.241 +    sum = sum + *( data + i );
   6.242 +  }
   6.243 +  sum = -sum;          /* iso ensures -s + s == 0 on unsigned types */
   6.244 +  return( sum );
   6.245 +}
   6.246 +
   6.247 +
   6.248 +byte chksum_bios_get_value( byte* data, long offset ) {
   6.249 +
   6.250 +  return( *( data + BIOS_OFFSET ) );
   6.251 +}
   6.252 +
   6.253 +
   6.254 +void chksum_bios_set_value( byte* data, long offset, byte value ) {
   6.255 +
   6.256 +  *( data + BIOS_OFFSET ) = value;
   6.257 +}
   6.258 +
   6.259 +
   6.260 +byte chksum__32__calc_value( byte* data, long offset ) {
   6.261 +
   6.262 +  int           i;
   6.263 +  int           len;
   6.264 +  byte sum;
   6.265 +
   6.266 +  check( offset + _32__MINHDR <= MAX_OFFSET, "_32_ header out of bounds" );
   6.267 +  len = *( data + offset + _32__LEN ) << 4;
   6.268 +  check( offset + len <= MAX_OFFSET, "_32_ header-length out of bounds" );
   6.269 +  sum = 0;
   6.270 +  for( i = 0; i < len; i++ ) {
   6.271 +    if( i != _32__CHKSUM ) {
   6.272 +      sum = sum + *( data + offset + i );
   6.273 +    }
   6.274 +  }
   6.275 +  sum = -sum;
   6.276 +  return( sum );
   6.277 +}
   6.278 +
   6.279 +
   6.280 +long chksum__32__get_offset( byte* data, long offset ) {
   6.281 +
   6.282 +  long result = -1L;
   6.283 +
   6.284 +  offset = offset + 0x0F;
   6.285 +  offset = offset & ~( 0x0F );
   6.286 +  while( offset + 16 < MAX_OFFSET ) {
   6.287 +    offset = offset + 16;
   6.288 +    if( *( data + offset + 0 ) == '_' && \
   6.289 +        *( data + offset + 1 ) == '3' && \
   6.290 +        *( data + offset + 2 ) == '2' && \
   6.291 +        *( data + offset + 3 ) == '_' ) {
   6.292 +      result = offset;
   6.293 +      break;
   6.294 +    }
   6.295 +  }
   6.296 +  return( result );
   6.297 +}
   6.298 +
   6.299 +
   6.300 +byte chksum__32__get_value( byte* data, long offset ) {
   6.301 +
   6.302 +  check( offset + _32__CHKSUM <= MAX_OFFSET, "PCI-Bios checksum out of bounds" );
   6.303 +  return(  *( data + offset + _32__CHKSUM ) );
   6.304 +}
   6.305 +
   6.306 +
   6.307 +void chksum__32__set_value( byte* data, long offset, byte value ) {
   6.308 +
   6.309 +  check( offset + _32__CHKSUM <= MAX_OFFSET, "PCI-Bios checksum out of bounds" );
   6.310 +  *( data + offset + _32__CHKSUM ) = value;
   6.311 +}
   6.312 +
   6.313 +
   6.314 +byte chksum__mp__calc_value( byte* data, long offset ) {
   6.315 +
   6.316 +  int   i;
   6.317 +  int   len;
   6.318 +  byte  sum;
   6.319 +
   6.320 +  check( offset + _MP__MINHDR <= MAX_OFFSET, "_MP_ header out of bounds" );
   6.321 +  len = *( data + offset + _MP__LEN ) << 4;
   6.322 +  check( offset + len <= MAX_OFFSET, "_MP_ header-length out of bounds" );
   6.323 +  sum = 0;
   6.324 +  for( i = 0; i < len; i++ ) {
   6.325 +    if( i != _MP__CHKSUM ) {
   6.326 +      sum = sum + *( data + offset + i );
   6.327 +    }
   6.328 +  }
   6.329 +  sum = -sum;
   6.330 +  return( sum );
   6.331 +}
   6.332 +
   6.333 +
   6.334 +long chksum__mp__get_offset( byte* data, long offset ) {
   6.335 +
   6.336 +  long result = -1L;
   6.337 +
   6.338 +  offset = offset + 0x0F;
   6.339 +  offset = offset & ~( 0x0F );
   6.340 +  while( offset + 16 < MAX_OFFSET ) {
   6.341 +    offset = offset + 16;
   6.342 +    if( *( data + offset + 0 ) == '_' && \
   6.343 +        *( data + offset + 1 ) == 'M' && \
   6.344 +        *( data + offset + 2 ) == 'P' && \
   6.345 +        *( data + offset + 3 ) == '_' ) {
   6.346 +      result = offset;
   6.347 +      break;
   6.348 +    }
   6.349 +  }
   6.350 +  return( result );
   6.351 +}
   6.352 +
   6.353 +
   6.354 +byte chksum__mp__get_value( byte* data, long offset ) {
   6.355 +
   6.356 +  check( offset + _MP__CHKSUM <= MAX_OFFSET, "MP checksum out of bounds" );
   6.357 +  return( *( data + offset + _MP__CHKSUM ) );
   6.358 +}
   6.359 +
   6.360 +
   6.361 +void chksum__mp__set_value( byte* data, long offset, byte value ) {
   6.362 +
   6.363 +  check( offset + _MP__CHKSUM <= MAX_OFFSET, "MP checksum out of bounds" );
   6.364 +  *( data + offset + _MP__CHKSUM ) = value;
   6.365 +}
   6.366 +
   6.367 +
   6.368 +byte chksum_pcmp_calc_value( byte* data, long offset ) {
   6.369 +
   6.370 +  int   i;
   6.371 +  int   len;
   6.372 +  byte  sum;
   6.373 +
   6.374 +  check( offset + PCMP_MINHDR <= MAX_OFFSET, "PCMP header out of bounds" );
   6.375 +  len  =   *( data + offset + PCMP_BASELEN )      + \
   6.376 +         ( *( data + offset + PCMP_BASELEN + 1 ) << 8 );
   6.377 +  check( offset + len <= MAX_OFFSET, "PCMP header-length out of bounds" );
   6.378 +  if( *( data + offset + PCMP_EXT_LEN )     | \
   6.379 +      *( data + offset + PCMP_EXT_LEN + 1 ) | \
   6.380 +      *( data + offset + PCMP_EXT_CHKSUM ) ) {
   6.381 +    check( 0, "PCMP header indicates extended tables (unsupported)" );
   6.382 +  }
   6.383 +  sum = 0;
   6.384 +  for( i = 0; i < len; i++ ) {
   6.385 +    if( i != PCMP_CHKSUM ) {
   6.386 +      sum = sum + *( data + offset + i );
   6.387 +    }
   6.388 +  }
   6.389 +  sum = -sum;
   6.390 +  return( sum );
   6.391 +}
   6.392 +
   6.393 +
   6.394 +long chksum_pcmp_get_offset( byte* data, long offset ) {
   6.395 +
   6.396 +  long result = -1L;
   6.397 +
   6.398 +  offset = offset + 0x0F;
   6.399 +  offset = offset & ~( 0x0F );
   6.400 +  while( offset + 16 < MAX_OFFSET ) {
   6.401 +    offset = offset + 16;
   6.402 +    if( *( data + offset + 0 ) == 'P' && \
   6.403 +        *( data + offset + 1 ) == 'C' && \
   6.404 +        *( data + offset + 2 ) == 'M' && \
   6.405 +        *( data + offset + 3 ) == 'P' ) {
   6.406 +      result = offset;
   6.407 +      break;
   6.408 +    }
   6.409 +  }
   6.410 +  return( result );
   6.411 +}
   6.412 +
   6.413 +
   6.414 +byte chksum_pcmp_get_value( byte* data, long offset ) {
   6.415 +
   6.416 +  check( offset + PCMP_CHKSUM <= MAX_OFFSET, "PCMP checksum out of bounds" );
   6.417 +  return( *( data + offset + PCMP_CHKSUM ) );
   6.418 +}
   6.419 +
   6.420 +
   6.421 +void chksum_pcmp_set_value( byte* data, long offset, byte value ) {
   6.422 +
   6.423 +  check( offset + PCMP_CHKSUM <= MAX_OFFSET, "PCMP checksum out of bounds" );
   6.424 +  *( data + offset + PCMP_CHKSUM ) = value;
   6.425 +}
   6.426 +
   6.427 +
   6.428 +byte chksum__pir_calc_value( byte* data, long offset ) {
   6.429 +
   6.430 +  int   i;
   6.431 +  int   len;
   6.432 +  byte  sum;
   6.433 +
   6.434 +  check( offset + _PIR_MINHDR <= MAX_OFFSET, "$PIR header out of bounds" );
   6.435 +  len  =   *( data + offset + _PIR_LEN )      + \
   6.436 +         ( *( data + offset + _PIR_LEN + 1 ) << 8 );
   6.437 +  check( offset + len <= MAX_OFFSET, "$PIR header-length out of bounds" );
   6.438 +  sum = 0;
   6.439 +  for( i = 0; i < len; i++ ) {
   6.440 +    if( i != _PIR_CHKSUM ) {
   6.441 +      sum = sum + *( data + offset + i );
   6.442 +    }
   6.443 +  }
   6.444 +  sum = -sum;
   6.445 +  return( sum );
   6.446 +}
   6.447 +
   6.448 +
   6.449 +long chksum__pir_get_offset( byte* data, long offset ) {
   6.450 +
   6.451 +  long result = -1L;
   6.452 +
   6.453 +  offset = offset + 0x0F;
   6.454 +  offset = offset & ~( 0x0F );
   6.455 +  while( offset + 16 < MAX_OFFSET ) {
   6.456 +    offset = offset + 16;
   6.457 +    if( *( data + offset + 0 ) == '$' && \
   6.458 +        *( data + offset + 1 ) == 'P' && \
   6.459 +        *( data + offset + 2 ) == 'I' && \
   6.460 +        *( data + offset + 3 ) == 'R' ) {
   6.461 +      result = offset;
   6.462 +      break;
   6.463 +    }
   6.464 +  }
   6.465 +  return( result );
   6.466 +}
   6.467 +
   6.468 +
   6.469 +byte chksum__pir_get_value( byte* data, long offset ) {
   6.470 +
   6.471 +  check( offset + _PIR_CHKSUM <= MAX_OFFSET, "$PIR checksum out of bounds" );
   6.472 +  return(  *( data + offset + _PIR_CHKSUM ) );
   6.473 +}
   6.474 +
   6.475 +
   6.476 +void chksum__pir_set_value( byte* data, long offset, byte value ) {
   6.477 +
   6.478 +  check( offset + _PIR_CHKSUM <= MAX_OFFSET, "$PIR checksum out of bounds" );
   6.479 +  *( data + offset + _PIR_CHKSUM ) = value;
   6.480 +}
   6.481 +
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/tools/firmware/rombios/makesym.perl	Wed Jun 08 09:04:58 2005 +0000
     7.3 @@ -0,0 +1,31 @@
     7.4 +#!/usr/bin/perl
     7.5 +#
     7.6 +# $Id: makesym.perl,v 1.1 2002/11/24 22:45:40 bdenney Exp $
     7.7 +#
     7.8 +# Read output file from as86 (e.g. rombios.txt) and write out a symbol 
     7.9 +# table suitable for the Bochs debugger.
    7.10 +#
    7.11 +
    7.12 +$WHERE_BEFORE_SYM_TABLE = 0;
    7.13 +$WHERE_IN_SYM_TABLE = 1;
    7.14 +$WHERE_AFTER_SYM_TABLE = 2;
    7.15 +
    7.16 +$where = $WHERE_BEFORE_SYM_TABLE;
    7.17 +while (<STDIN>) {
    7.18 +  chop;
    7.19 +  if ($where == WHERE_BEFORE_SYM_TABLE && /^Symbols:/) {
    7.20 +    $where = $WHERE_IN_SYM_TABLE;
    7.21 +  } elsif ($where == $WHERE_IN_SYM_TABLE && /^$/) {
    7.22 +    $where = $WHERE_AFTER_SYM_TABLE;
    7.23 +  }
    7.24 +  if ($where == $WHERE_IN_SYM_TABLE) {
    7.25 +    @F = split (/\s+/);
    7.26 +    ($name[0], $junk, $addr[0], $junk, $name[1], $junk, $addr[1]) = @F;
    7.27 +    foreach $col (0,1) {
    7.28 +      next if length $addr[$col] < 1;
    7.29 +      $addr[$col] =~ tr/A-Z/a-z/;
    7.30 +      $addr[$col] = "000f" . $addr[$col];
    7.31 +      print "$addr[$col] $name[$col]\n";
    7.32 +    }
    7.33 +  }
    7.34 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/tools/firmware/rombios/rombios.c	Wed Jun 08 09:04:58 2005 +0000
     8.3 @@ -0,0 +1,10825 @@
     8.4 +/////////////////////////////////////////////////////////////////////////
     8.5 +// $Id: rombios.c,v 1.138 2005/05/07 15:55:26 vruppert Exp $
     8.6 +/////////////////////////////////////////////////////////////////////////
     8.7 +//
     8.8 +//  Copyright (C) 2002  MandrakeSoft S.A.
     8.9 +//
    8.10 +//    MandrakeSoft S.A.
    8.11 +//    43, rue d'Aboukir
    8.12 +//    75002 Paris - France
    8.13 +//    http://www.linux-mandrake.com/
    8.14 +//    http://www.mandrakesoft.com/
    8.15 +//
    8.16 +//  This library is free software; you can redistribute it and/or
    8.17 +//  modify it under the terms of the GNU Lesser General Public
    8.18 +//  License as published by the Free Software Foundation; either
    8.19 +//  version 2 of the License, or (at your option) any later version.
    8.20 +//
    8.21 +//  This library is distributed in the hope that it will be useful,
    8.22 +//  but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.23 +//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    8.24 +//  Lesser General Public License for more details.
    8.25 +//
    8.26 +//  You should have received a copy of the GNU Lesser General Public
    8.27 +//  License along with this library; if not, write to the Free Software
    8.28 +//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
    8.29 +
    8.30 +// ROM BIOS for use with Bochs/Plex x86 emulation environment
    8.31 +
    8.32 +#define VMXASSIST
    8.33 +
    8.34 +// ROM BIOS compatability entry points:
    8.35 +// ===================================
    8.36 +// $e05b ; POST Entry Point
    8.37 +// $e2c3 ; NMI Handler Entry Point
    8.38 +// $e3fe ; INT 13h Fixed Disk Services Entry Point
    8.39 +// $e401 ; Fixed Disk Parameter Table
    8.40 +// $e6f2 ; INT 19h Boot Load Service Entry Point
    8.41 +// $e6f5 ; Configuration Data Table
    8.42 +// $e729 ; Baud Rate Generator Table
    8.43 +// $e739 ; INT 14h Serial Communications Service Entry Point
    8.44 +// $e82e ; INT 16h Keyboard Service Entry Point
    8.45 +// $e987 ; INT 09h Keyboard Service Entry Point
    8.46 +// $ec59 ; INT 13h Diskette Service Entry Point
    8.47 +// $ef57 ; INT 0Eh Diskette Hardware ISR Entry Point
    8.48 +// $efc7 ; Diskette Controller Parameter Table
    8.49 +// $efd2 ; INT 17h Printer Service Entry Point
    8.50 +// $f045 ; INT 10 Functions 0-Fh Entry Point
    8.51 +// $f065 ; INT 10h Video Support Service Entry Point
    8.52 +// $f0a4 ; MDA/CGA Video Parameter Table (INT 1Dh)
    8.53 +// $f841 ; INT 12h Memory Size Service Entry Point
    8.54 +// $f84d ; INT 11h Equipment List Service Entry Point
    8.55 +// $f859 ; INT 15h System Services Entry Point
    8.56 +// $fa6e ; Character Font for 320x200 & 640x200 Graphics (lower 128 characters)
    8.57 +// $fe6e ; INT 1Ah Time-of-day Service Entry Point
    8.58 +// $fea5 ; INT 08h System Timer ISR Entry Point
    8.59 +// $fef3 ; Initial Interrupt Vector Offsets Loaded by POST
    8.60 +// $ff53 ; IRET Instruction for Dummy Interrupt Handler
    8.61 +// $ff54 ; INT 05h Print Screen Service Entry Point
    8.62 +// $fff0 ; Power-up Entry Point
    8.63 +// $fff5 ; ASCII Date ROM was built - 8 characters in MM/DD/YY
    8.64 +// $fffe ; System Model ID
    8.65 +
    8.66 +// NOTES for ATA/ATAPI driver (cbbochs@free.fr)
    8.67 +//   Features
    8.68 +//     - supports up to 4 ATA interfaces
    8.69 +//     - device/geometry detection
    8.70 +//     - 16bits/32bits device access
    8.71 +//     - pchs/lba access
    8.72 +//     - datain/dataout/packet command support
    8.73 +//
    8.74 +// NOTES for El-Torito Boot (cbbochs@free.fr)
    8.75 +//   - CD-ROM booting is only available if ATA/ATAPI Driver is available
    8.76 +//   - Current code is only able to boot mono-session cds 
    8.77 +//   - Current code can not boot and emulate a hard-disk
    8.78 +//     the bios will panic otherwise
    8.79 +//   - Current code also use memory in EBDA segement. 
    8.80 +//   - I used cmos byte 0x3D to store extended information on boot-device
    8.81 +//   - Code has to be modified modified to handle multiple cdrom drives
    8.82 +//   - Here are the cdrom boot failure codes:
    8.83 +//       1 : no atapi device found
    8.84 +//       2 : no atapi cdrom found
    8.85 +//       3 : can not read cd - BRVD
    8.86 +//       4 : cd is not eltorito (BRVD)
    8.87 +//       5 : cd is not eltorito (ISO TAG)
    8.88 +//       6 : cd is not eltorito (ELTORITO TAG)
    8.89 +//       7 : can not read cd - boot catalog
    8.90 +//       8 : boot catalog : bad header
    8.91 +//       9 : boot catalog : bad platform
    8.92 +//      10 : boot catalog : bad signature
    8.93 +//      11 : boot catalog : bootable flag not set
    8.94 +//      12 : can not read cd - boot image
    8.95 +//
    8.96 +//   ATA driver
    8.97 +//   - EBDA segment. 
    8.98 +//     I used memory starting at 0x121 in the segment
    8.99 +//   - the translation policy is defined in cmos regs 0x39 & 0x3a
   8.100 +//
   8.101 +// TODO :
   8.102 +//
   8.103 +//   int74 
   8.104 +//     - needs to be reworked.  Uses direct [bp] offsets. (?)
   8.105 +//
   8.106 +//   int13:
   8.107 +//     - f04 (verify sectors) isn't complete  (?)
   8.108 +//     - f02/03/04 should set current cyl,etc in BDA  (?)
   8.109 +//     - rewrite int13_relocated & clean up int13 entry code
   8.110 +//
   8.111 +//   NOTES:
   8.112 +//   - NMI access (bit7 of addr written to 70h)
   8.113 +//
   8.114 +//   ATA driver
   8.115 +//   - should handle the "don't detect" bit (cmos regs 0x3b & 0x3c)
   8.116 +//   - could send the multiple-sector read/write commands
   8.117 +//
   8.118 +//   El-Torito
   8.119 +//   - Emulate a Hard-disk (currently only diskette can be emulated) see "FIXME ElTorito Harddisk"
   8.120 +//   - Implement remaining int13_cdemu functions (as defined by El-Torito specs)
   8.121 +//   - cdrom drive is hardcoded to ide 0 device 1 in several places. see "FIXME ElTorito Hardcoded"
   8.122 +//   - int13 Fix DL when emulating a cd. In that case DL is decremented before calling real int13.
   8.123 +//     This is ok. But DL should be reincremented afterwards. 
   8.124 +//   - Fix all "FIXME ElTorito Various"
   8.125 +//   - should be able to boot any cdrom instead of the first one
   8.126 +//
   8.127 +//   BCC Bug: find a generic way to handle the bug of #asm after an "if"  (fixed in 0.16.7)
   8.128 +
   8.129 +#define DEBUG_ROMBIOS      0
   8.130 +
   8.131 +#define DEBUG_ATA          0
   8.132 +#define DEBUG_INT13_HD     0
   8.133 +#define DEBUG_INT13_CD     0
   8.134 +#define DEBUG_INT13_ET     0
   8.135 +#define DEBUG_INT13_FL     0
   8.136 +#define DEBUG_INT15        0
   8.137 +#define DEBUG_INT16        0
   8.138 +#define DEBUG_INT1A        0
   8.139 +#define DEBUG_INT74        0
   8.140 +#define DEBUG_APM          0
   8.141 +
   8.142 +#define BX_CPU           3
   8.143 +#define BX_USE_PS2_MOUSE 1
   8.144 +#define BX_CALL_INT15_4F 1
   8.145 +#define BX_USE_EBDA      1
   8.146 +#define BX_SUPPORT_FLOPPY 1
   8.147 +#define BX_FLOPPY_ON_CNT 37   /* 2 seconds */
   8.148 +#define BX_PCIBIOS       1
   8.149 +#define BX_APM           1
   8.150 +
   8.151 +#define BX_USE_ATADRV    1
   8.152 +#define BX_ELTORITO_BOOT 1
   8.153 +
   8.154 +#define BX_MAX_ATA_INTERFACES   4
   8.155 +#define BX_MAX_ATA_DEVICES      (BX_MAX_ATA_INTERFACES*2)
   8.156 +
   8.157 +#define BX_VIRTUAL_PORTS 1 /* normal output to Bochs ports */
   8.158 +#define BX_DEBUG_SERIAL  0 /* output to COM1 */
   8.159 +
   8.160 +   /* model byte 0xFC = AT */
   8.161 +#define SYS_MODEL_ID     0xFC
   8.162 +#define SYS_SUBMODEL_ID  0x00
   8.163 +#define BIOS_REVISION    1
   8.164 +#define BIOS_CONFIG_TABLE 0xe6f5
   8.165 +
   8.166 +#ifndef BIOS_BUILD_DATE
   8.167 +#  define BIOS_BUILD_DATE "06/23/99"
   8.168 +#endif
   8.169 +
   8.170 +  // 1K of base memory used for Extended Bios Data Area (EBDA)
   8.171 +  // EBDA is used for PS/2 mouse support, and IDE BIOS, etc.
   8.172 +#define EBDA_SEG           0x9FC0
   8.173 +#define EBDA_SIZE          1              // In KiB
   8.174 +#define BASE_MEM_IN_K   (640 - EBDA_SIZE)
   8.175 +
   8.176 +  // Define the application NAME
   8.177 +#ifdef VMXASSIST
   8.178 +#  define BX_APPNAME "VMXAssist"
   8.179 +#elif PLEX86
   8.180 +#  define BX_APPNAME "Plex86"
   8.181 +#else
   8.182 +#  define BX_APPNAME "Bochs"
   8.183 +#endif
   8.184 +
   8.185 +  // Sanity Checks
   8.186 +#if BX_USE_ATADRV && BX_CPU<3
   8.187 +#    error The ATA/ATAPI Driver can only to be used with a 386+ cpu
   8.188 +#endif
   8.189 +#if BX_USE_ATADRV && !BX_USE_EBDA
   8.190 +#    error ATA/ATAPI Driver can only be used if EBDA is available
   8.191 +#endif
   8.192 +#if BX_ELTORITO_BOOT && !BX_USE_ATADRV
   8.193 +#    error El-Torito Boot can only be use if ATA/ATAPI Driver is available
   8.194 +#endif
   8.195 +#if BX_PCIBIOS && BX_CPU<3
   8.196 +#    error PCI BIOS can only be used with 386+ cpu
   8.197 +#endif
   8.198 +#if BX_APM && BX_CPU<3
   8.199 +#    error APM BIOS can only be used with 386+ cpu
   8.200 +#endif
   8.201 +
   8.202 +#ifndef BX_SMP_PROCESSORS
   8.203 +#define BX_SMP_PROCESSORS 1
   8.204 +#    warning BX_SMP_PROCESSORS not defined, defaulting to 1
   8.205 +#endif
   8.206 +  
   8.207 +#define PANIC_PORT  0x400
   8.208 +#define PANIC_PORT2 0x401
   8.209 +#define INFO_PORT   0x402
   8.210 +#define DEBUG_PORT  0x403
   8.211 +
   8.212 +// #20  is dec 20
   8.213 +// #$20 is hex 20 = 32
   8.214 +// #0x20 is hex 20 = 32
   8.215 +// LDA  #$20
   8.216 +// JSR  $E820
   8.217 +// LDD  .i,S
   8.218 +// JSR  $C682
   8.219 +// mov al, #$20
   8.220 +
   8.221 +// all hex literals should be prefixed with '0x'
   8.222 +//   grep "#[0-9a-fA-F][0-9a-fA-F]" rombios.c
   8.223 +// no mov SEG-REG, #value, must mov register into seg-reg
   8.224 +//   grep -i "mov[ ]*.s" rombios.c
   8.225 +
   8.226 +// This is for compiling with gcc2 and gcc3
   8.227 +#define ASM_START #asm
   8.228 +#define ASM_END #endasm
   8.229 +
   8.230 +ASM_START
   8.231 +.rom
   8.232 +
   8.233 +.org 0x0000
   8.234 +
   8.235 +#if BX_CPU >= 3
   8.236 +use16 386
   8.237 +#else
   8.238 +use16 286
   8.239 +#endif
   8.240 +
   8.241 +MACRO HALT
   8.242 +  ;; the HALT macro is called with the line number of the HALT call.
   8.243 +  ;; The line number is then sent to the PANIC_PORT, causing Bochs/Plex 
   8.244 +  ;; to print a BX_PANIC message.  This will normally halt the simulation
   8.245 +  ;; with a message such as "BIOS panic at rombios.c, line 4091".
   8.246 +  ;; However, users can choose to make panics non-fatal and continue.
   8.247 +#if BX_VIRTUAL_PORTS
   8.248 +  mov dx,#PANIC_PORT
   8.249 +  mov ax,#?1
   8.250 +  out dx,ax
   8.251 +#else
   8.252 +  mov dx,#0x80
   8.253 +  mov ax,#?1
   8.254 +  out dx,al
   8.255 +#endif
   8.256 +MEND
   8.257 +
   8.258 +MACRO JMP_AP
   8.259 +  db 0xea
   8.260 +  dw ?2
   8.261 +  dw ?1
   8.262 +MEND
   8.263 +
   8.264 +MACRO SET_INT_VECTOR
   8.265 +  mov ax, ?3
   8.266 +  mov ?1*4, ax
   8.267 +  mov ax, ?2
   8.268 +  mov ?1*4+2, ax
   8.269 +MEND
   8.270 +
   8.271 +ASM_END
   8.272 +
   8.273 +typedef unsigned char  Bit8u;
   8.274 +typedef unsigned short Bit16u;
   8.275 +typedef unsigned short bx_bool;
   8.276 +typedef unsigned long  Bit32u;
   8.277 +
   8.278 +#if BX_USE_ATADRV
   8.279 +
   8.280 +  void memsetb(seg,offset,value,count);
   8.281 +  void memcpyb(dseg,doffset,sseg,soffset,count);
   8.282 +  void memcpyd(dseg,doffset,sseg,soffset,count);
   8.283 +  
   8.284 +  // memset of count bytes
   8.285 +    void 
   8.286 +  memsetb(seg,offset,value,count)
   8.287 +    Bit16u seg;
   8.288 +    Bit16u offset;
   8.289 +    Bit16u value;
   8.290 +    Bit16u count;
   8.291 +  {
   8.292 +  ASM_START
   8.293 +    push bp
   8.294 +    mov  bp, sp
   8.295 +  
   8.296 +      push ax
   8.297 +      push cx
   8.298 +      push es
   8.299 +      push di
   8.300 +  
   8.301 +      mov  cx, 10[bp] ; count
   8.302 +      cmp  cx, #0x00
   8.303 +      je   memsetb_end
   8.304 +      mov  ax, 4[bp] ; segment
   8.305 +      mov  es, ax
   8.306 +      mov  ax, 6[bp] ; offset
   8.307 +      mov  di, ax
   8.308 +      mov  al, 8[bp] ; value
   8.309 +      cld
   8.310 +      rep
   8.311 +       stosb
   8.312 +  
   8.313 +  memsetb_end:
   8.314 +      pop di
   8.315 +      pop es
   8.316 +      pop cx
   8.317 +      pop ax
   8.318 +  
   8.319 +    pop bp
   8.320 +  ASM_END
   8.321 +  }
   8.322 +  
   8.323 +  // memcpy of count bytes
   8.324 +    void 
   8.325 +  memcpyb(dseg,doffset,sseg,soffset,count)
   8.326 +    Bit16u dseg;
   8.327 +    Bit16u doffset;
   8.328 +    Bit16u sseg;
   8.329 +    Bit16u soffset;
   8.330 +    Bit16u count;
   8.331 +  {
   8.332 +  ASM_START
   8.333 +    push bp
   8.334 +    mov  bp, sp
   8.335 +  
   8.336 +      push ax
   8.337 +      push cx
   8.338 +      push es
   8.339 +      push di
   8.340 +      push ds
   8.341 +      push si
   8.342 +  
   8.343 +      mov  cx, 12[bp] ; count
   8.344 +      cmp  cx, #0x0000
   8.345 +      je   memcpyb_end
   8.346 +      mov  ax, 4[bp] ; dsegment
   8.347 +      mov  es, ax
   8.348 +      mov  ax, 6[bp] ; doffset
   8.349 +      mov  di, ax
   8.350 +      mov  ax, 8[bp] ; ssegment
   8.351 +      mov  ds, ax
   8.352 +      mov  ax, 10[bp] ; soffset
   8.353 +      mov  si, ax
   8.354 +      cld
   8.355 +      rep
   8.356 +       movsb
   8.357 +  
   8.358 +  memcpyb_end:
   8.359 +      pop si
   8.360 +      pop ds
   8.361 +      pop di
   8.362 +      pop es
   8.363 +      pop cx
   8.364 +      pop ax
   8.365 +  
   8.366 +    pop bp
   8.367 +  ASM_END
   8.368 +  }
   8.369 +
   8.370 +#if 0 
   8.371 +  // memcpy of count dword
   8.372 +    void 
   8.373 +  memcpyd(dseg,doffset,sseg,soffset,count)
   8.374 +    Bit16u dseg;
   8.375 +    Bit16u doffset;
   8.376 +    Bit16u sseg;
   8.377 +    Bit16u soffset;
   8.378 +    Bit16u count;
   8.379 +  {
   8.380 +  ASM_START
   8.381 +    push bp
   8.382 +    mov  bp, sp
   8.383 +  
   8.384 +      push ax
   8.385 +      push cx
   8.386 +      push es
   8.387 +      push di
   8.388 +      push ds
   8.389 +      push si
   8.390 +  
   8.391 +      mov  cx, 12[bp] ; count
   8.392 +      cmp  cx, #0x0000
   8.393 +      je   memcpyd_end
   8.394 +      mov  ax, 4[bp] ; dsegment
   8.395 +      mov  es, ax
   8.396 +      mov  ax, 6[bp] ; doffset
   8.397 +      mov  di, ax
   8.398 +      mov  ax, 8[bp] ; ssegment
   8.399 +      mov  ds, ax
   8.400 +      mov  ax, 10[bp] ; soffset
   8.401 +      mov  si, ax
   8.402 +      cld
   8.403 +      rep
   8.404 +       movsd
   8.405 +  
   8.406 +  memcpyd_end:
   8.407 +      pop si
   8.408 +      pop ds
   8.409 +      pop di
   8.410 +      pop es
   8.411 +      pop cx
   8.412 +      pop ax
   8.413 +  
   8.414 +    pop bp
   8.415 +  ASM_END
   8.416 +  }
   8.417 +#endif
   8.418 +#endif //BX_USE_ATADRV
   8.419 +
   8.420 +  // read_dword and write_dword functions
   8.421 +  static Bit32u         read_dword();
   8.422 +  static void           write_dword();
   8.423 +  
   8.424 +    Bit32u
   8.425 +  read_dword(seg, offset)
   8.426 +    Bit16u seg;
   8.427 +    Bit16u offset;
   8.428 +  {
   8.429 +  ASM_START
   8.430 +    push bp
   8.431 +    mov  bp, sp
   8.432 +  
   8.433 +      push bx
   8.434 +      push ds
   8.435 +      mov  ax, 4[bp] ; segment
   8.436 +      mov  ds, ax
   8.437 +      mov  bx, 6[bp] ; offset
   8.438 +      mov  ax, [bx]
   8.439 +      inc  bx
   8.440 +      inc  bx
   8.441 +      mov  dx, [bx]
   8.442 +      ;; ax = return value (word)
   8.443 +      ;; dx = return value (word)
   8.444 +      pop  ds
   8.445 +      pop  bx
   8.446 +  
   8.447 +    pop  bp
   8.448 +  ASM_END
   8.449 +  }
   8.450 +  
   8.451 +    void
   8.452 +  write_dword(seg, offset, data)
   8.453 +    Bit16u seg;
   8.454 +    Bit16u offset;
   8.455 +    Bit32u data;
   8.456 +  {
   8.457 +  ASM_START
   8.458 +    push bp
   8.459 +    mov  bp, sp
   8.460 +  
   8.461 +      push ax
   8.462 +      push bx
   8.463 +      push ds
   8.464 +      mov  ax, 4[bp] ; segment
   8.465 +      mov  ds, ax
   8.466 +      mov  bx, 6[bp] ; offset
   8.467 +      mov  ax, 8[bp] ; data word
   8.468 +      mov  [bx], ax  ; write data word
   8.469 +      inc  bx
   8.470 +      inc  bx
   8.471 +      mov  ax, 10[bp] ; data word
   8.472 +      mov  [bx], ax  ; write data word
   8.473 +      pop  ds
   8.474 +      pop  bx
   8.475 +      pop  ax
   8.476 +  
   8.477 +    pop  bp
   8.478 +  ASM_END
   8.479 +  }
   8.480 +  
   8.481 +  // Bit32u (unsigned long) and long helper functions
   8.482 +  ASM_START
   8.483 +  
   8.484 +  ;; and function
   8.485 +  landl:
   8.486 +  landul:
   8.487 +    SEG SS 
   8.488 +      and ax,[di]
   8.489 +    SEG SS 
   8.490 +      and bx,2[di]
   8.491 +    ret
   8.492 +  
   8.493 +  ;; add function
   8.494 +  laddl:
   8.495 +  laddul:
   8.496 +    SEG SS 
   8.497 +      add ax,[di]
   8.498 +    SEG SS 
   8.499 +      adc bx,2[di]
   8.500 +    ret
   8.501 +  
   8.502 +  ;; cmp function
   8.503 +  lcmpl:
   8.504 +  lcmpul:
   8.505 +    and eax, #0x0000FFFF
   8.506 +    shl ebx, #16
   8.507 +    add eax, ebx
   8.508 +    shr ebx, #16
   8.509 +    SEG SS
   8.510 +      cmp eax, dword ptr [di]
   8.511 +    ret
   8.512 +  
   8.513 +  ;; sub function
   8.514 +  lsubl:
   8.515 +  lsubul:
   8.516 +    SEG SS
   8.517 +    sub ax,[di]
   8.518 +    SEG SS
   8.519 +    sbb bx,2[di]
   8.520 +    ret
   8.521 +  
   8.522 +  ;; mul function
   8.523 +  lmull:
   8.524 +  lmulul:
   8.525 +    and eax, #0x0000FFFF
   8.526 +    shl ebx, #16
   8.527 +    add eax, ebx
   8.528 +    SEG SS
   8.529 +    mul eax, dword ptr [di]
   8.530 +    mov ebx, eax
   8.531 +    shr ebx, #16
   8.532 +    ret
   8.533 +  
   8.534 +  ;; dec function
   8.535 +  ldecl:
   8.536 +  ldecul:
   8.537 +    SEG SS
   8.538 +    dec dword ptr [bx]
   8.539 +    ret
   8.540 +  
   8.541 +  ;; or function
   8.542 +  lorl:
   8.543 +  lorul:
   8.544 +    SEG SS
   8.545 +    or  ax,[di]
   8.546 +    SEG SS
   8.547 +    or  bx,2[di]
   8.548 +    ret
   8.549 +  
   8.550 +  ;; inc function
   8.551 +  lincl:
   8.552 +  lincul:
   8.553 +    SEG SS
   8.554 +    inc dword ptr [bx]
   8.555 +    ret
   8.556 +  
   8.557 +  ;; tst function
   8.558 +  ltstl:
   8.559 +  ltstul:
   8.560 +    and eax, #0x0000FFFF
   8.561 +    shl ebx, #16
   8.562 +    add eax, ebx
   8.563 +    shr ebx, #16
   8.564 +    test eax, eax
   8.565 +    ret
   8.566 +  
   8.567 +  ;; sr function
   8.568 +  lsrul:
   8.569 +    mov  cx,di
   8.570 +    jcxz lsr_exit
   8.571 +    and  eax, #0x0000FFFF
   8.572 +    shl  ebx, #16
   8.573 +    add  eax, ebx
   8.574 +  lsr_loop:
   8.575 +    shr  eax, #1
   8.576 +    loop lsr_loop
   8.577 +    mov  ebx, eax
   8.578 +    shr  ebx, #16
   8.579 +  lsr_exit:
   8.580 +    ret
   8.581 +  
   8.582 +  ;; sl function
   8.583 +  lsll:
   8.584 +  lslul:
   8.585 +    mov  cx,di
   8.586 +    jcxz lsl_exit
   8.587 +    and  eax, #0x0000FFFF
   8.588 +    shl  ebx, #16
   8.589 +    add  eax, ebx
   8.590 +  lsl_loop: 
   8.591 +    shl  eax, #1
   8.592 +    loop lsl_loop
   8.593 +    mov  ebx, eax
   8.594 +    shr  ebx, #16
   8.595 +  lsl_exit:
   8.596 +    ret
   8.597 +  
   8.598 +  idiv_:
   8.599 +    cwd
   8.600 +    idiv bx
   8.601 +    ret
   8.602 +
   8.603 +  idiv_u:
   8.604 +    xor dx,dx
   8.605 +    div bx
   8.606 +    ret
   8.607 +
   8.608 +  ldivul:
   8.609 +    and  eax, #0x0000FFFF
   8.610 +    shl  ebx, #16
   8.611 +    add  eax, ebx
   8.612 +    xor  edx, edx
   8.613 +    SEG SS
   8.614 +    mov  bx,  2[di]
   8.615 +    shl  ebx, #16
   8.616 +    SEG SS
   8.617 +    mov  bx,  [di]
   8.618 +    div  ebx
   8.619 +    mov  ebx, eax
   8.620 +    shr  ebx, #16
   8.621 +    ret
   8.622 +
   8.623 +  ASM_END
   8.624 +
   8.625 +// for access to RAM area which is used by interrupt vectors
   8.626 +// and BIOS Data Area
   8.627 +
   8.628 +typedef struct {
   8.629 +  unsigned char filler1[0x400];
   8.630 +  unsigned char filler2[0x6c];
   8.631 +  Bit16u ticks_low;
   8.632 +  Bit16u ticks_high;
   8.633 +  Bit8u  midnight_flag;
   8.634 +  } bios_data_t;
   8.635 +
   8.636 +#define BiosData ((bios_data_t  *) 0)
   8.637 +
   8.638 +#if BX_USE_ATADRV
   8.639 +  typedef struct {
   8.640 +    Bit16u heads;      // # heads
   8.641 +    Bit16u cylinders;  // # cylinders
   8.642 +    Bit16u spt;        // # sectors / track
   8.643 +    } chs_t;
   8.644 +
   8.645 +  // DPTE definition
   8.646 +  typedef struct {
   8.647 +    Bit16u iobase1;
   8.648 +    Bit16u iobase2;
   8.649 +    Bit8u  prefix;
   8.650 +    Bit8u  unused;
   8.651 +    Bit8u  irq;
   8.652 +    Bit8u  blkcount;
   8.653 +    Bit8u  dma;
   8.654 +    Bit8u  pio;
   8.655 +    Bit16u options;
   8.656 +    Bit16u reserved;
   8.657 +    Bit8u  revision;
   8.658 +    Bit8u  checksum;
   8.659 +    } dpte_t;
   8.660 + 
   8.661 +  typedef struct {
   8.662 +    Bit8u  iface;        // ISA or PCI
   8.663 +    Bit16u iobase1;      // IO Base 1
   8.664 +    Bit16u iobase2;      // IO Base 2
   8.665 +    Bit8u  irq;          // IRQ
   8.666 +    } ata_channel_t;
   8.667 +
   8.668 +  typedef struct {
   8.669 +    Bit8u  type;         // Detected type of ata (ata/atapi/none/unknown)
   8.670 +    Bit8u  device;       // Detected type of attached devices (hd/cd/none)
   8.671 +    Bit8u  removable;    // Removable device flag
   8.672 +    Bit8u  lock;         // Locks for removable devices
   8.673 +    // Bit8u  lba_capable;  // LBA capable flag - always yes for bochs devices
   8.674 +    Bit8u  mode;         // transfert mode : PIO 16/32 bits - IRQ - ISADMA - PCIDMA
   8.675 +    Bit16u blksize;      // block size
   8.676 +
   8.677 +    Bit8u  translation;  // type of translation
   8.678 +    chs_t  lchs;         // Logical CHS
   8.679 +    chs_t  pchs;         // Physical CHS
   8.680 +
   8.681 +    Bit32u sectors;      // Total sectors count
   8.682 +    } ata_device_t;
   8.683 +
   8.684 +  typedef struct {
   8.685 +    // ATA channels info
   8.686 +    ata_channel_t channels[BX_MAX_ATA_INTERFACES];
   8.687 +
   8.688 +    // ATA devices info
   8.689 +    ata_device_t  devices[BX_MAX_ATA_DEVICES];
   8.690 +    //
   8.691 +    // map between (bios hd id - 0x80) and ata channels
   8.692 +    Bit8u  hdcount, hdidmap[BX_MAX_ATA_DEVICES];                
   8.693 +
   8.694 +    // map between (bios cd id - 0xE0) and ata channels
   8.695 +    Bit8u  cdcount, cdidmap[BX_MAX_ATA_DEVICES];                
   8.696 +
   8.697 +    // Buffer for DPTE table
   8.698 +    dpte_t dpte;
   8.699 +
   8.700 +    // Count of transferred sectors and bytes
   8.701 +    Bit16u trsfsectors;
   8.702 +    Bit32u trsfbytes;
   8.703 +
   8.704 +    } ata_t;
   8.705 +  
   8.706 +#if BX_ELTORITO_BOOT
   8.707 +  // ElTorito Device Emulation data 
   8.708 +  typedef struct {
   8.709 +    Bit8u  active;
   8.710 +    Bit8u  media;
   8.711 +    Bit8u  emulated_drive;
   8.712 +    Bit8u  controller_index;
   8.713 +    Bit16u device_spec;
   8.714 +    Bit32u ilba;
   8.715 +    Bit16u buffer_segment;
   8.716 +    Bit16u load_segment;
   8.717 +    Bit16u sector_count;
   8.718 +    
   8.719 +    // Virtual device
   8.720 +    chs_t  vdevice;
   8.721 +    } cdemu_t;
   8.722 +#endif // BX_ELTORITO_BOOT
   8.723 +  
   8.724 +  // for access to EBDA area
   8.725 +  //     The EBDA structure should conform to 
   8.726 +  //     http://www.cybertrails.com/~fys/rombios.htm document
   8.727 +  //     I made the ata and cdemu structs begin at 0x121 in the EBDA seg
   8.728 +  typedef struct {
   8.729 +    unsigned char filler1[0x3D];
   8.730 +
   8.731 +    // FDPT - Can be splitted in data members if needed
   8.732 +    unsigned char fdpt0[0x10];
   8.733 +    unsigned char fdpt1[0x10];
   8.734 +
   8.735 +    unsigned char filler2[0xC4];
   8.736 +
   8.737 +    // ATA Driver data
   8.738 +    ata_t   ata;
   8.739 +
   8.740 +#if BX_ELTORITO_BOOT
   8.741 +    // El Torito Emulation data
   8.742 +    cdemu_t cdemu;
   8.743 +#endif // BX_ELTORITO_BOOT
   8.744 +
   8.745 +    } ebda_data_t;
   8.746 +  
   8.747 +  #define EbdaData ((ebda_data_t *) 0)
   8.748 +
   8.749 +  // for access to the int13ext structure
   8.750 +  typedef struct {
   8.751 +    Bit8u  size;
   8.752 +    Bit8u  reserved;
   8.753 +    Bit16u count;
   8.754 +    Bit16u offset;
   8.755 +    Bit16u segment;
   8.756 +    Bit32u lba1;
   8.757 +    Bit32u lba2;
   8.758 +    } int13ext_t;
   8.759 + 
   8.760 +  #define Int13Ext ((int13ext_t *) 0)
   8.761 +
   8.762 +  // Disk Physical Table definition
   8.763 +  typedef struct {
   8.764 +    Bit16u  size;
   8.765 +    Bit16u  infos;
   8.766 +    Bit32u  cylinders;
   8.767 +    Bit32u  heads;
   8.768 +    Bit32u  spt;
   8.769 +    Bit32u  sector_count1;
   8.770 +    Bit32u  sector_count2;
   8.771 +    Bit16u  blksize;
   8.772 +    Bit16u  dpte_segment;
   8.773 +    Bit16u  dpte_offset;
   8.774 +    Bit16u  key;
   8.775 +    Bit8u   dpi_length;
   8.776 +    Bit8u   reserved1;
   8.777 +    Bit16u  reserved2;
   8.778 +    Bit8u   host_bus[4];
   8.779 +    Bit8u   iface_type[8];
   8.780 +    Bit8u   iface_path[8];
   8.781 +    Bit8u   device_path[8];
   8.782 +    Bit8u   reserved3;
   8.783 +    Bit8u   checksum;
   8.784 +    } dpt_t;
   8.785 + 
   8.786 +  #define Int13DPT ((dpt_t *) 0)
   8.787 +
   8.788 +#endif // BX_USE_ATADRV
   8.789 +
   8.790 +typedef struct {
   8.791 +  union {
   8.792 +    struct {
   8.793 +      Bit16u di, si, bp, sp;
   8.794 +      Bit16u bx, dx, cx, ax;
   8.795 +      } r16;
   8.796 +    struct {
   8.797 +      Bit16u filler[4];
   8.798 +      Bit8u  bl, bh, dl, dh, cl, ch, al, ah;
   8.799 +      } r8;
   8.800 +    } u;
   8.801 +  } pusha_regs_t;
   8.802 +
   8.803 +typedef struct {
   8.804 + union {
   8.805 +  struct {
   8.806 +    Bit32u edi, esi, ebp, esp;
   8.807 +    Bit32u ebx, edx, ecx, eax;
   8.808 +    } r32;
   8.809 +  struct {
   8.810 +    Bit16u di, filler1, si, filler2, bp, filler3, sp, filler4;
   8.811 +    Bit16u bx, filler5, dx, filler6, cx, filler7, ax, filler8;
   8.812 +    } r16;
   8.813 +  struct {
   8.814 +    Bit32u filler[4];
   8.815 +    Bit8u  bl, bh; 
   8.816 +    Bit16u filler1;
   8.817 +    Bit8u  dl, dh; 
   8.818 +    Bit16u filler2;
   8.819 +    Bit8u  cl, ch;
   8.820 +    Bit16u filler3;
   8.821 +    Bit8u  al, ah;
   8.822 +    Bit16u filler4;
   8.823 +    } r8;
   8.824 +  } u;
   8.825 +} pushad_regs_t;
   8.826 +
   8.827 +typedef struct {
   8.828 +  union {
   8.829 +    struct {
   8.830 +      Bit16u flags;
   8.831 +      } r16;
   8.832 +    struct {
   8.833 +      Bit8u  flagsl;
   8.834 +      Bit8u  flagsh;
   8.835 +      } r8;
   8.836 +    } u;
   8.837 +  } flags_t;
   8.838 +
   8.839 +#define SetCF(x)   x.u.r8.flagsl |= 0x01
   8.840 +#define SetZF(x)   x.u.r8.flagsl |= 0x40
   8.841 +#define ClearCF(x) x.u.r8.flagsl &= 0xfe
   8.842 +#define ClearZF(x) x.u.r8.flagsl &= 0xbf
   8.843 +#define GetCF(x)   (x.u.r8.flagsl & 0x01)
   8.844 +
   8.845 +typedef struct {
   8.846 +  Bit16u ip;
   8.847 +  Bit16u cs;
   8.848 +  flags_t flags;
   8.849 +  } iret_addr_t;
   8.850 +
   8.851 +
   8.852 +
   8.853 +static Bit8u          inb();
   8.854 +static Bit8u          inb_cmos();
   8.855 +static void           outb();
   8.856 +static void           outb_cmos();
   8.857 +static Bit16u         inw();
   8.858 +static void           outw();
   8.859 +static void           init_rtc();
   8.860 +static bx_bool        rtc_updating();
   8.861 +
   8.862 +static Bit8u          read_byte();
   8.863 +static Bit16u         read_word();
   8.864 +static void           write_byte();
   8.865 +static void           write_word();
   8.866 +static void           bios_printf();
   8.867 +static void           copy_e820_table();
   8.868 +
   8.869 +static Bit8u          inhibit_mouse_int_and_events();
   8.870 +static void           enable_mouse_int_and_events();
   8.871 +static Bit8u          send_to_mouse_ctrl();
   8.872 +static Bit8u          get_mouse_data();
   8.873 +static void           set_kbd_command_byte();
   8.874 +
   8.875 +static void           int09_function();
   8.876 +static void           int13_harddisk();
   8.877 +static void           int13_cdrom();
   8.878 +static void           int13_cdemu();
   8.879 +static void           int13_eltorito();
   8.880 +static void           int13_diskette_function();
   8.881 +static void           int14_function();
   8.882 +static void           int15_function();
   8.883 +static void           int16_function();
   8.884 +static void           int17_function();
   8.885 +static Bit32u         int19_function();
   8.886 +static void           int1a_function();
   8.887 +static void           int70_function();
   8.888 +static void           int74_function();
   8.889 +static Bit16u         get_CS();
   8.890 +//static Bit16u         get_DS();
   8.891 +//static void           set_DS();
   8.892 +static Bit16u         get_SS();
   8.893 +static unsigned int   enqueue_key();
   8.894 +static unsigned int   dequeue_key();
   8.895 +static void           get_hd_geometry();
   8.896 +static void           set_diskette_ret_status();
   8.897 +static void           set_diskette_current_cyl();
   8.898 +static void           determine_floppy_media();
   8.899 +static bx_bool        floppy_drive_exists();
   8.900 +static bx_bool        floppy_drive_recal();
   8.901 +static bx_bool        floppy_media_known();
   8.902 +static bx_bool        floppy_media_sense();
   8.903 +static bx_bool        set_enable_a20();
   8.904 +static void           debugger_on();
   8.905 +static void           debugger_off();
   8.906 +static void           keyboard_init();
   8.907 +static void           keyboard_panic();
   8.908 +static void           shutdown_status_panic();
   8.909 +static void           nmi_handler_msg();
   8.910 +
   8.911 +static void           print_bios_banner();
   8.912 +static void           print_boot_device();
   8.913 +static void           print_boot_failure();
   8.914 +static void           print_cdromboot_failure();
   8.915 +
   8.916 +# if BX_USE_ATADRV
   8.917 +
   8.918 +// ATA / ATAPI driver
   8.919 +void   ata_init();
   8.920 +void   ata_detect();
   8.921 +void   ata_reset();
   8.922 +
   8.923 +Bit16u ata_cmd_non_data();
   8.924 +Bit16u ata_cmd_data_in();
   8.925 +Bit16u ata_cmd_data_out();
   8.926 +Bit16u ata_cmd_packet();
   8.927 +
   8.928 +Bit16u atapi_get_sense();
   8.929 +Bit16u atapi_is_ready();
   8.930 +Bit16u atapi_is_cdrom();
   8.931 +
   8.932 +#endif // BX_USE_ATADRV
   8.933 +
   8.934 +#if BX_ELTORITO_BOOT
   8.935 +
   8.936 +void   cdemu_init();
   8.937 +Bit8u  cdemu_isactive();
   8.938 +Bit8u  cdemu_emulated_drive();
   8.939 +
   8.940 +Bit16u cdrom_boot();
   8.941 +
   8.942 +#endif // BX_ELTORITO_BOOT
   8.943 +
   8.944 +static char bios_cvs_version_string[] = "$Revision: 1.138 $";
   8.945 +static char bios_date_string[] = "$Date: 2005/05/07 15:55:26 $";
   8.946 +
   8.947 +static char CVSID[] = "$Id: rombios.c,v 1.138 2005/05/07 15:55:26 vruppert Exp $";
   8.948 +
   8.949 +/* Offset to skip the CVS $Id: prefix */ 
   8.950 +#define bios_version_string  (CVSID + 4)
   8.951 +
   8.952 +#define BIOS_PRINTF_HALT     1
   8.953 +#define BIOS_PRINTF_SCREEN   2
   8.954 +#define BIOS_PRINTF_INFO     4
   8.955 +#define BIOS_PRINTF_DEBUG    8
   8.956 +#define BIOS_PRINTF_ALL      (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO)
   8.957 +#define BIOS_PRINTF_DEBHALT  (BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO | BIOS_PRINTF_HALT)
   8.958 +
   8.959 +#define printf(format, p...)  bios_printf(BIOS_PRINTF_SCREEN, format, ##p)
   8.960 +
   8.961 +// Defines the output macros. 
   8.962 +// BX_DEBUG goes to INFO port until we can easily choose debug info on a 
   8.963 +// per-device basis. Debug info are sent only in debug mode
   8.964 +#if DEBUG_ROMBIOS
   8.965 +#  define BX_DEBUG(format, p...)  bios_printf(BIOS_PRINTF_INFO, format, ##p)    
   8.966 +#else
   8.967 +#  define BX_DEBUG(format, p...) 
   8.968 +#endif
   8.969 +#define BX_INFO(format, p...)   bios_printf(BIOS_PRINTF_INFO, format, ##p)
   8.970 +#define BX_PANIC(format, p...)  bios_printf(BIOS_PRINTF_DEBHALT, format, ##p)
   8.971 +
   8.972 +#if DEBUG_ATA
   8.973 +#  define BX_DEBUG_ATA(a...) BX_DEBUG(a)
   8.974 +#else
   8.975 +#  define BX_DEBUG_ATA(a...)
   8.976 +#endif
   8.977 +#if DEBUG_INT13_HD
   8.978 +#  define BX_DEBUG_INT13_HD(a...) BX_DEBUG(a)
   8.979 +#else
   8.980 +#  define BX_DEBUG_INT13_HD(a...)
   8.981 +#endif
   8.982 +#if DEBUG_INT13_CD
   8.983 +#  define BX_DEBUG_INT13_CD(a...) BX_DEBUG(a)
   8.984 +#else
   8.985 +#  define BX_DEBUG_INT13_CD(a...)
   8.986 +#endif
   8.987 +#if DEBUG_INT13_ET
   8.988 +#  define BX_DEBUG_INT13_ET(a...) BX_DEBUG(a)
   8.989 +#else
   8.990 +#  define BX_DEBUG_INT13_ET(a...)
   8.991 +#endif
   8.992 +#if DEBUG_INT13_FL
   8.993 +#  define BX_DEBUG_INT13_FL(a...) BX_DEBUG(a)
   8.994 +#else
   8.995 +#  define BX_DEBUG_INT13_FL(a...)
   8.996 +#endif
   8.997 +#if DEBUG_INT15
   8.998 +#  define BX_DEBUG_INT15(a...) BX_DEBUG(a)
   8.999 +#else
  8.1000 +#  define BX_DEBUG_INT15(a...)
  8.1001 +#endif
  8.1002 +#if DEBUG_INT16
  8.1003 +#  define BX_DEBUG_INT16(a...) BX_DEBUG(a)
  8.1004 +#else
  8.1005 +#  define BX_DEBUG_INT16(a...)
  8.1006 +#endif
  8.1007 +#if DEBUG_INT1A
  8.1008 +#  define BX_DEBUG_INT1A(a...) BX_DEBUG(a)
  8.1009 +#else
  8.1010 +#  define BX_DEBUG_INT1A(a...)
  8.1011 +#endif
  8.1012 +#if DEBUG_INT74
  8.1013 +#  define BX_DEBUG_INT74(a...) BX_DEBUG(a)
  8.1014 +#else
  8.1015 +#  define BX_DEBUG_INT74(a...)
  8.1016 +#endif
  8.1017 +
  8.1018 +#define SET_AL(val8) AX = ((AX & 0xff00) | (val8))
  8.1019 +#define SET_BL(val8) BX = ((BX & 0xff00) | (val8))
  8.1020 +#define SET_CL(val8) CX = ((CX & 0xff00) | (val8))
  8.1021 +#define SET_DL(val8) DX = ((DX & 0xff00) | (val8))
  8.1022 +#define SET_AH(val8) AX = ((AX & 0x00ff) | ((val8) << 8))
  8.1023 +#define SET_BH(val8) BX = ((BX & 0x00ff) | ((val8) << 8))
  8.1024 +#define SET_CH(val8) CX = ((CX & 0x00ff) | ((val8) << 8))
  8.1025 +#define SET_DH(val8) DX = ((DX & 0x00ff) | ((val8) << 8))
  8.1026 +
  8.1027 +#define GET_AL() ( AX & 0x00ff )
  8.1028 +#define GET_BL() ( BX & 0x00ff )
  8.1029 +#define GET_CL() ( CX & 0x00ff )
  8.1030 +#define GET_DL() ( DX & 0x00ff )
  8.1031 +#define GET_AH() ( AX >> 8 )
  8.1032 +#define GET_BH() ( BX >> 8 )
  8.1033 +#define GET_CH() ( CX >> 8 )
  8.1034 +#define GET_DH() ( DX >> 8 )
  8.1035 +
  8.1036 +#define GET_ELDL() ( ELDX & 0x00ff )
  8.1037 +#define GET_ELDH() ( ELDX >> 8 )
  8.1038 +
  8.1039 +#define SET_CF()     FLAGS |= 0x0001
  8.1040 +#define CLEAR_CF()   FLAGS &= 0xfffe
  8.1041 +#define GET_CF()     (FLAGS & 0x0001)
  8.1042 +
  8.1043 +#define SET_ZF()     FLAGS |= 0x0040
  8.1044 +#define CLEAR_ZF()   FLAGS &= 0xffbf
  8.1045 +#define GET_ZF()     (FLAGS & 0x0040)
  8.1046 +
  8.1047 +#define UNSUPPORTED_FUNCTION 0x86
  8.1048 +
  8.1049 +#define none 0
  8.1050 +#define MAX_SCAN_CODE 0x53
  8.1051 +
  8.1052 +static struct {
  8.1053 +  Bit16u normal;
  8.1054 +  Bit16u shift;
  8.1055 +  Bit16u control;
  8.1056 +  Bit16u alt;
  8.1057 +  Bit8u lock_flags;
  8.1058 +  } scan_to_scanascii[MAX_SCAN_CODE + 1] = {
  8.1059 +      {   none,   none,   none,   none, none },
  8.1060 +      { 0x011b, 0x011b, 0x011b, 0x0100, none }, /* escape */
  8.1061 +      { 0x0231, 0x0221,   none, 0x7800, none }, /* 1! */
  8.1062 +      { 0x0332, 0x0340, 0x0300, 0x7900, none }, /* 2@ */
  8.1063 +      { 0x0433, 0x0423,   none, 0x7a00, none }, /* 3# */
  8.1064 +      { 0x0534, 0x0524,   none, 0x7b00, none }, /* 4$ */
  8.1065 +      { 0x0635, 0x0625,   none, 0x7c00, none }, /* 5% */
  8.1066 +      { 0x0736, 0x075e, 0x071e, 0x7d00, none }, /* 6^ */
  8.1067 +      { 0x0837, 0x0826,   none, 0x7e00, none }, /* 7& */
  8.1068 +      { 0x0938, 0x092a,   none, 0x7f00, none }, /* 8* */
  8.1069 +      { 0x0a39, 0x0a28,   none, 0x8000, none }, /* 9( */
  8.1070 +      { 0x0b30, 0x0b29,   none, 0x8100, none }, /* 0) */
  8.1071 +      { 0x0c2d, 0x0c5f, 0x0c1f, 0x8200, none }, /* -_ */
  8.1072 +      { 0x0d3d, 0x0d2b,   none, 0x8300, none }, /* =+ */
  8.1073 +      { 0x0e08, 0x0e08, 0x0e7f,   none, none }, /* backspace */
  8.1074 +      { 0x0f09, 0x0f00,   none,   none, none }, /* tab */
  8.1075 +      { 0x1071, 0x1051, 0x1011, 0x1000, 0x40 }, /* Q */
  8.1076 +      { 0x1177, 0x1157, 0x1117, 0x1100, 0x40 }, /* W */
  8.1077 +      { 0x1265, 0x1245, 0x1205, 0x1200, 0x40 }, /* E */
  8.1078 +      { 0x1372, 0x1352, 0x1312, 0x1300, 0x40 }, /* R */
  8.1079 +      { 0x1474, 0x1454, 0x1414, 0x1400, 0x40 }, /* T */
  8.1080 +      { 0x1579, 0x1559, 0x1519, 0x1500, 0x40 }, /* Y */
  8.1081 +      { 0x1675, 0x1655, 0x1615, 0x1600, 0x40 }, /* U */
  8.1082 +      { 0x1769, 0x1749, 0x1709, 0x1700, 0x40 }, /* I */
  8.1083 +      { 0x186f, 0x184f, 0x180f, 0x1800, 0x40 }, /* O */
  8.1084 +      { 0x1970, 0x1950, 0x1910, 0x1900, 0x40 }, /* P */
  8.1085 +      { 0x1a5b, 0x1a7b, 0x1a1b,   none, none }, /* [{ */
  8.1086 +      { 0x1b5d, 0x1b7d, 0x1b1d,   none, none }, /* ]} */
  8.1087 +      { 0x1c0d, 0x1c0d, 0x1c0a,   none, none }, /* Enter */
  8.1088 +      {   none,   none,   none,   none, none }, /* L Ctrl */
  8.1089 +      { 0x1e61, 0x1e41, 0x1e01, 0x1e00, 0x40 }, /* A */
  8.1090 +      { 0x1f73, 0x1f53, 0x1f13, 0x1f00, 0x40 }, /* S */
  8.1091 +      { 0x2064, 0x2044, 0x2004, 0x2000, 0x40 }, /* D */
  8.1092 +      { 0x2166, 0x2146, 0x2106, 0x2100, 0x40 }, /* F */
  8.1093 +      { 0x2267, 0x2247, 0x2207, 0x2200, 0x40 }, /* G */
  8.1094 +      { 0x2368, 0x2348, 0x2308, 0x2300, 0x40 }, /* H */
  8.1095 +      { 0x246a, 0x244a, 0x240a, 0x2400, 0x40 }, /* J */
  8.1096 +      { 0x256b, 0x254b, 0x250b, 0x2500, 0x40 }, /* K */
  8.1097 +      { 0x266c, 0x264c, 0x260c, 0x2600, 0x40 }, /* L */
  8.1098 +      { 0x273b, 0x273a,   none,   none, none }, /* ;: */
  8.1099 +      { 0x2827, 0x2822,   none,   none, none }, /* '" */
  8.1100 +      { 0x2960, 0x297e,   none,   none, none }, /* `~ */
  8.1101 +      {   none,   none,   none,   none, none }, /* L shift */
  8.1102 +      { 0x2b5c, 0x2b7c, 0x2b1c,   none, none }, /* |\ */
  8.1103 +      { 0x2c7a, 0x2c5a, 0x2c1a, 0x2c00, 0x40 }, /* Z */
  8.1104 +      { 0x2d78, 0x2d58, 0x2d18, 0x2d00, 0x40 }, /* X */
  8.1105 +      { 0x2e63, 0x2e43, 0x2e03, 0x2e00, 0x40 }, /* C */
  8.1106 +      { 0x2f76, 0x2f56, 0x2f16, 0x2f00, 0x40 }, /* V */
  8.1107 +      { 0x3062, 0x3042, 0x3002, 0x3000, 0x40 }, /* B */
  8.1108 +      { 0x316e, 0x314e, 0x310e, 0x3100, 0x40 }, /* N */
  8.1109 +      { 0x326d, 0x324d, 0x320d, 0x3200, 0x40 }, /* M */
  8.1110 +      { 0x332c, 0x333c,   none,   none, none }, /* ,< */
  8.1111 +      { 0x342e, 0x343e,   none,   none, none }, /* .> */
  8.1112 +      { 0x352f, 0x353f,   none,   none, none }, /* /? */
  8.1113 +      {   none,   none,   none,   none, none }, /* R Shift */
  8.1114 +      { 0x372a, 0x372a,   none,   none, none }, /* * */
  8.1115 +      {   none,   none,   none,   none, none }, /* L Alt */
  8.1116 +      { 0x3920, 0x3920, 0x3920, 0x3920, none }, /* space */
  8.1117 +      {   none,   none,   none,   none, none }, /* caps lock */
  8.1118 +      { 0x3b00, 0x5400, 0x5e00, 0x6800, none }, /* F1 */
  8.1119 +      { 0x3c00, 0x5500, 0x5f00, 0x6900, none }, /* F2 */
  8.1120 +      { 0x3d00, 0x5600, 0x6000, 0x6a00, none }, /* F3 */
  8.1121 +      { 0x3e00, 0x5700, 0x6100, 0x6b00, none }, /* F4 */
  8.1122 +      { 0x3f00, 0x5800, 0x6200, 0x6c00, none }, /* F5 */
  8.1123 +      { 0x4000, 0x5900, 0x6300, 0x6d00, none }, /* F6 */
  8.1124 +      { 0x4100, 0x5a00, 0x6400, 0x6e00, none }, /* F7 */
  8.1125 +      { 0x4200, 0x5b00, 0x6500, 0x6f00, none }, /* F8 */
  8.1126 +      { 0x4300, 0x5c00, 0x6600, 0x7000, none }, /* F9 */
  8.1127 +      { 0x4400, 0x5d00, 0x6700, 0x7100, none }, /* F10 */
  8.1128 +      {   none,   none,   none,   none, none }, /* Num Lock */
  8.1129 +      {   none,   none,   none,   none, none }, /* Scroll Lock */
  8.1130 +      { 0x4700, 0x4737, 0x7700,   none, 0x20 }, /* 7 Home */
  8.1131 +      { 0x4800, 0x4838,   none,   none, 0x20 }, /* 8 UP */
  8.1132 +      { 0x4900, 0x4939, 0x8400,   none, 0x20 }, /* 9 PgUp */
  8.1133 +      { 0x4a2d, 0x4a2d,   none,   none, none }, /* - */
  8.1134 +      { 0x4b00, 0x4b34, 0x7300,   none, 0x20 }, /* 4 Left */
  8.1135 +      { 0x4c00, 0x4c35,   none,   none, 0x20 }, /* 5 */
  8.1136 +      { 0x4d00, 0x4d36, 0x7400,   none, 0x20 }, /* 6 Right */
  8.1137 +      { 0x4e2b, 0x4e2b,   none,   none, none }, /* + */
  8.1138 +      { 0x4f00, 0x4f31, 0x7500,   none, 0x20 }, /* 1 End */
  8.1139 +      { 0x5000, 0x5032,   none,   none, 0x20 }, /* 2 Down */
  8.1140 +      { 0x5100, 0x5133, 0x7600,   none, 0x20 }, /* 3 PgDn */
  8.1141 +      { 0x5200, 0x5230,   none,   none, 0x20 }, /* 0 Ins */
  8.1142 +      { 0x5300, 0x532e,   none,   none, 0x20 }  /* Del */
  8.1143 +      };
  8.1144 +
  8.1145 +  Bit8u
  8.1146 +inb(port)
  8.1147 +  Bit16u port;
  8.1148 +{
  8.1149 +ASM_START
  8.1150 +  push bp
  8.1151 +  mov  bp, sp
  8.1152 +
  8.1153 +    push dx
  8.1154 +    mov  dx, 4[bp]
  8.1155 +    in   al, dx
  8.1156 +    pop  dx
  8.1157 +
  8.1158 +  pop  bp
  8.1159 +ASM_END
  8.1160 +}
  8.1161 +
  8.1162 +#if BX_USE_ATADRV
  8.1163 +  Bit16u
  8.1164 +inw(port)
  8.1165 +  Bit16u port;
  8.1166 +{
  8.1167 +ASM_START
  8.1168 +  push bp
  8.1169 +  mov  bp, sp
  8.1170 +
  8.1171 +    push dx
  8.1172 +    mov  dx, 4[bp]
  8.1173 +    in   ax, dx
  8.1174 +    pop  dx
  8.1175 +
  8.1176 +  pop  bp
  8.1177 +ASM_END
  8.1178 +}
  8.1179 +#endif
  8.1180 +
  8.1181 +  void
  8.1182 +outb(port, val)
  8.1183 +  Bit16u port;
  8.1184 +  Bit8u  val;
  8.1185 +{
  8.1186 +ASM_START
  8.1187 +  push bp
  8.1188 +  mov  bp, sp
  8.1189 +
  8.1190 +    push ax
  8.1191 +    push dx
  8.1192 +    mov  dx, 4[bp]
  8.1193 +    mov  al, 6[bp]
  8.1194 +    out  dx, al
  8.1195 +    pop  dx
  8.1196 +    pop  ax
  8.1197 +
  8.1198 +  pop  bp
  8.1199 +ASM_END
  8.1200 +}
  8.1201 +
  8.1202 +#if BX_USE_ATADRV
  8.1203 +  void
  8.1204 +outw(port, val)
  8.1205 +  Bit16u port;
  8.1206 +  Bit16u  val;
  8.1207 +{
  8.1208 +ASM_START
  8.1209 +  push bp
  8.1210 +  mov  bp, sp
  8.1211 +
  8.1212 +    push ax
  8.1213 +    push dx
  8.1214 +    mov  dx, 4[bp]
  8.1215 +    mov  ax, 6[bp]
  8.1216 +    out  dx, ax
  8.1217 +    pop  dx
  8.1218 +    pop  ax
  8.1219 +
  8.1220 +  pop  bp
  8.1221 +ASM_END
  8.1222 +}
  8.1223 +#endif
  8.1224 +
  8.1225 +  void
  8.1226 +outb_cmos(cmos_reg, val)
  8.1227 +  Bit8u cmos_reg;
  8.1228 +  Bit8u val;
  8.1229 +{
  8.1230 +ASM_START
  8.1231 +  push bp
  8.1232 +  mov  bp, sp
  8.1233 +
  8.1234 +    mov  al, 4[bp] ;; cmos_reg
  8.1235 +    out  0x70, al
  8.1236 +    mov  al, 6[bp] ;; val
  8.1237 +    out  0x71, al
  8.1238 +
  8.1239 +  pop  bp
  8.1240 +ASM_END
  8.1241 +}
  8.1242 +
  8.1243 +  Bit8u
  8.1244 +inb_cmos(cmos_reg)
  8.1245 +  Bit8u cmos_reg;
  8.1246 +{
  8.1247 +ASM_START
  8.1248 +  push bp
  8.1249 +  mov  bp, sp
  8.1250 +
  8.1251 +    mov  al, 4[bp] ;; cmos_reg
  8.1252 +    out 0x70, al
  8.1253 +    in  al, 0x71
  8.1254 +
  8.1255 +  pop  bp
  8.1256 +ASM_END
  8.1257 +}
  8.1258 +
  8.1259 +  void
  8.1260 +init_rtc()
  8.1261 +{
  8.1262 +  outb_cmos(0x0a, 0x26);
  8.1263 +  outb_cmos(0x0b, 0x02);
  8.1264 +  inb_cmos(0x0c);
  8.1265 +  inb_cmos(0x0d);
  8.1266 +}
  8.1267 +
  8.1268 +  bx_bool
  8.1269 +rtc_updating()
  8.1270 +{
  8.1271 +  // This function checks to see if the update-in-progress bit
  8.1272 +  // is set in CMOS Status Register A.  If not, it returns 0.
  8.1273 +  // If it is set, it tries to wait until there is a transition
  8.1274 +  // to 0, and will return 0 if such a transition occurs.  A 1
  8.1275 +  // is returned only after timing out.  The maximum period
  8.1276 +  // that this bit should be set is constrained to 244useconds.
  8.1277 +  // The count I use below guarantees coverage or more than
  8.1278 +  // this time, with any reasonable IPS setting.
  8.1279 +
  8.1280 +  Bit16u count;
  8.1281 +
  8.1282 +  count = 25000;
  8.1283 +  while (--count != 0) {
  8.1284 +    if ( (inb_cmos(0x0a) & 0x80) == 0 )
  8.1285 +      return(0);
  8.1286 +    }
  8.1287 +  return(1); // update-in-progress never transitioned to 0
  8.1288 +}
  8.1289 +
  8.1290 +
  8.1291 +  Bit8u
  8.1292 +read_byte(seg, offset)
  8.1293 +  Bit16u seg;
  8.1294 +  Bit16u offset;
  8.1295 +{
  8.1296 +ASM_START
  8.1297 +  push bp
  8.1298 +  mov  bp, sp
  8.1299 +
  8.1300 +    push bx
  8.1301 +    push ds
  8.1302 +    mov  ax, 4[bp] ; segment
  8.1303 +    mov  ds, ax
  8.1304 +    mov  bx, 6[bp] ; offset
  8.1305 +    mov  al, [bx]
  8.1306 +    ;; al = return value (byte)
  8.1307 +    pop  ds
  8.1308 +    pop  bx
  8.1309 +
  8.1310 +  pop  bp
  8.1311 +ASM_END
  8.1312 +}
  8.1313 +
  8.1314 +  Bit16u
  8.1315 +read_word(seg, offset)
  8.1316 +  Bit16u seg;
  8.1317 +  Bit16u offset;
  8.1318 +{
  8.1319 +ASM_START
  8.1320 +  push bp
  8.1321 +  mov  bp, sp
  8.1322 +
  8.1323 +    push bx
  8.1324 +    push ds
  8.1325 +    mov  ax, 4[bp] ; segment
  8.1326 +    mov  ds, ax
  8.1327 +    mov  bx, 6[bp] ; offset
  8.1328 +    mov  ax, [bx]
  8.1329 +    ;; ax = return value (word)
  8.1330 +    pop  ds
  8.1331 +    pop  bx
  8.1332 +
  8.1333 +  pop  bp
  8.1334 +ASM_END
  8.1335 +}
  8.1336 +
  8.1337 +  void
  8.1338 +write_byte(seg, offset, data)
  8.1339 +  Bit16u seg;
  8.1340 +  Bit16u offset;
  8.1341 +  Bit8u data;
  8.1342 +{
  8.1343 +ASM_START
  8.1344 +  push bp
  8.1345 +  mov  bp, sp
  8.1346 +
  8.1347 +    push ax
  8.1348 +    push bx
  8.1349 +    push ds
  8.1350 +    mov  ax, 4[bp] ; segment
  8.1351 +    mov  ds, ax
  8.1352 +    mov  bx, 6[bp] ; offset
  8.1353 +    mov  al, 8[bp] ; data byte
  8.1354 +    mov  [bx], al  ; write data byte
  8.1355 +    pop  ds
  8.1356 +    pop  bx
  8.1357 +    pop  ax
  8.1358 +
  8.1359 +  pop  bp
  8.1360 +ASM_END
  8.1361 +}
  8.1362 +
  8.1363 +  void
  8.1364 +write_word(seg, offset, data)
  8.1365 +  Bit16u seg;
  8.1366 +  Bit16u offset;
  8.1367 +  Bit16u data;
  8.1368 +{
  8.1369 +ASM_START
  8.1370 +  push bp
  8.1371 +  mov  bp, sp
  8.1372 +
  8.1373 +    push ax
  8.1374 +    push bx
  8.1375 +    push ds
  8.1376 +    mov  ax, 4[bp] ; segment
  8.1377 +    mov  ds, ax
  8.1378 +    mov  bx, 6[bp] ; offset
  8.1379 +    mov  ax, 8[bp] ; data word
  8.1380 +    mov  [bx], ax  ; write data word
  8.1381 +    pop  ds
  8.1382 +    pop  bx
  8.1383 +    pop  ax
  8.1384 +
  8.1385 +  pop  bp
  8.1386 +ASM_END
  8.1387 +}
  8.1388 +
  8.1389 +  Bit16u
  8.1390 +get_CS()
  8.1391 +{
  8.1392 +ASM_START
  8.1393 +  mov  ax, cs
  8.1394 +ASM_END
  8.1395 +}
  8.1396 +
  8.1397 +//  Bit16u
  8.1398 +//get_DS()
  8.1399 +//{
  8.1400 +//ASM_START
  8.1401 +//  mov  ax, ds
  8.1402 +//ASM_END
  8.1403 +//}
  8.1404 +//
  8.1405 +//  void
  8.1406 +//set_DS(ds_selector)
  8.1407 +//  Bit16u ds_selector;
  8.1408 +//{
  8.1409 +//ASM_START
  8.1410 +//  push bp
  8.1411 +//  mov  bp, sp
  8.1412 +//
  8.1413 +//    push ax
  8.1414 +//    mov  ax, 4[bp] ; ds_selector
  8.1415 +//    mov  ds, ax
  8.1416 +//    pop  ax
  8.1417 +//
  8.1418 +//  pop  bp
  8.1419 +//ASM_END
  8.1420 +//}
  8.1421 +
  8.1422 +  Bit16u
  8.1423 +get_SS()
  8.1424 +{
  8.1425 +ASM_START
  8.1426 +  mov  ax, ss
  8.1427 +ASM_END
  8.1428 +}
  8.1429 +
  8.1430 +#ifdef VMXASSIST
  8.1431 +void
  8.1432 +copy_e820_table()
  8.1433 +{
  8.1434 +  Bit8u nr_entries = read_byte(0x9000, 0x1e8);
  8.1435 +  if (nr_entries > 32)
  8.1436 +  	nr_entries = 32;
  8.1437 +  write_word(0xe000, 0x8, nr_entries);
  8.1438 +  memcpyb(0xe000, 0x10, 0x9000, 0x2d0, nr_entries * 0x14);
  8.1439 +}
  8.1440 +#endif /* VMXASSIST */
  8.1441 +
  8.1442 +#if BX_DEBUG_SERIAL
  8.1443 +/* serial debug port*/
  8.1444 +#define BX_DEBUG_PORT 0x03f8
  8.1445 +
  8.1446 +/* data */
  8.1447 +#define UART_RBR 0x00
  8.1448 +#define UART_THR 0x00
  8.1449 +
  8.1450 +/* control */
  8.1451 +#define UART_IER 0x01
  8.1452 +#define UART_IIR 0x02
  8.1453 +#define UART_FCR 0x02
  8.1454 +#define UART_LCR 0x03
  8.1455 +#define UART_MCR 0x04
  8.1456 +#define UART_DLL 0x00
  8.1457 +#define UART_DLM 0x01
  8.1458 +
  8.1459 +/* status */
  8.1460 +#define UART_LSR 0x05
  8.1461 +#define UART_MSR 0x06
  8.1462 +#define UART_SCR 0x07
  8.1463 +
  8.1464 +int uart_can_tx_byte(base_port)
  8.1465 +    Bit16u base_port;
  8.1466 +{
  8.1467 +    return inb(base_port + UART_LSR) & 0x20;
  8.1468 +}
  8.1469 +
  8.1470 +void uart_wait_to_tx_byte(base_port)
  8.1471 +    Bit16u base_port;
  8.1472 +{
  8.1473 +    while (!uart_can_tx_byte(base_port));
  8.1474 +}
  8.1475 +
  8.1476 +void uart_wait_until_sent(base_port)
  8.1477 +    Bit16u base_port;
  8.1478 +{
  8.1479 +    while (!(inb(base_port + UART_LSR) & 0x40));
  8.1480 +}
  8.1481 +
  8.1482 +void uart_tx_byte(base_port, data)
  8.1483 +    Bit16u base_port;
  8.1484 +    Bit8u data;
  8.1485 +{
  8.1486 +    uart_wait_to_tx_byte(base_port);
  8.1487 +    outb(base_port + UART_THR, data);
  8.1488 +    uart_wait_until_sent(base_port);
  8.1489 +}
  8.1490 +#endif
  8.1491 +
  8.1492 +  void
  8.1493 +wrch(c)
  8.1494 +  Bit8u  c;
  8.1495 +{
  8.1496 +  ASM_START
  8.1497 +  push bp
  8.1498 +  mov  bp, sp
  8.1499 +
  8.1500 +  push bx
  8.1501 +  mov  ah, #0x0e
  8.1502 +  mov  al, 4[bp]
  8.1503 +  xor  bx,bx
  8.1504 +  int  #0x10
  8.1505 +  pop  bx
  8.1506 +
  8.1507 +  pop  bp
  8.1508 +  ASM_END
  8.1509 +}
  8.1510 + 
  8.1511 +  void
  8.1512 +send(action, c)
  8.1513 +  Bit16u action;
  8.1514 +  Bit8u  c;
  8.1515 +{
  8.1516 +#if BX_DEBUG_SERIAL
  8.1517 +  if (c == '\n') uart_tx_byte(BX_DEBUG_PORT, '\r');
  8.1518 +  uart_tx_byte(BX_DEBUG_PORT, c);
  8.1519 +#endif
  8.1520 +#ifdef VMXASSIST
  8.1521 +  outb(0xE9, c);
  8.1522 +#endif
  8.1523 +#if BX_VIRTUAL_PORTS
  8.1524 +  if (action & BIOS_PRINTF_DEBUG) outb(DEBUG_PORT, c);
  8.1525 +  if (action & BIOS_PRINTF_INFO) outb(INFO_PORT, c);
  8.1526 +#endif
  8.1527 +  if (action & BIOS_PRINTF_SCREEN) {
  8.1528 +    if (c == '\n') wrch('\r');
  8.1529 +    wrch(c);
  8.1530 +  }
  8.1531 +}
  8.1532 +
  8.1533 +  void
  8.1534 +put_int(action, val, width, neg)
  8.1535 +  Bit16u action;
  8.1536 +  short val, width;
  8.1537 +  bx_bool neg;
  8.1538 +{
  8.1539 +  short nval = val / 10;
  8.1540 +  if (nval)
  8.1541 +    put_int(action, nval, width - 1, neg);
  8.1542 +  else {
  8.1543 +    while (--width > 0) send(action, ' ');
  8.1544 +    if (neg) send(action, '-');
  8.1545 +  }
  8.1546 +  send(action, val - (nval * 10) + '0');
  8.1547 +}
  8.1548 +
  8.1549 +  void
  8.1550 +put_uint(action, val, width, neg)
  8.1551 +  Bit16u action;
  8.1552 +  unsigned short val;
  8.1553 +  short width;
  8.1554 +  bx_bool neg;
  8.1555 +{
  8.1556 +  unsigned short nval = val / 10;
  8.1557 +  if (nval)
  8.1558 +    put_uint(action, nval, width - 1, neg);
  8.1559 +  else {
  8.1560 +    while (--width > 0) send(action, ' ');
  8.1561 +    if (neg) send(action, '-');
  8.1562 +  }
  8.1563 +  send(action, val - (nval * 10) + '0');
  8.1564 +}
  8.1565 +
  8.1566 +//--------------------------------------------------------------------------
  8.1567 +// bios_printf()
  8.1568 +//   A compact variable argument printf function which prints its output via
  8.1569 +//   an I/O port so that it can be logged by Bochs/Plex.  
  8.1570 +//   Currently, only %x is supported (or %02x, %04x, etc).
  8.1571 +//
  8.1572 +//   Supports %[format_width][format]
  8.1573 +//   where format can be d,x,c,s
  8.1574 +//--------------------------------------------------------------------------
  8.1575 +  void
  8.1576 +bios_printf(action, s)
  8.1577 +  Bit16u action;
  8.1578 +  Bit8u *s;
  8.1579 +{
  8.1580 +  Bit8u c, format_char;
  8.1581 +  bx_bool  in_format;
  8.1582 +  short i;
  8.1583 +  Bit16u  *arg_ptr;
  8.1584 +  Bit16u   arg_seg, arg, nibble, shift_count, format_width;
  8.1585 +
  8.1586 +  arg_ptr = &s;
  8.1587 +  arg_seg = get_SS();
  8.1588 +
  8.1589 +  in_format = 0;
  8.1590 +  format_width = 0;
  8.1591 +
  8.1592 +  if ((action & BIOS_PRINTF_DEBHALT) == BIOS_PRINTF_DEBHALT) {
  8.1593 +#if BX_VIRTUAL_PORTS
  8.1594 +    outb(PANIC_PORT2, 0x00);
  8.1595 +#endif
  8.1596 +    bios_printf (BIOS_PRINTF_SCREEN, "FATAL: ");
  8.1597 +  }
  8.1598 +
  8.1599 +  while (c = read_byte(get_CS(), s)) {
  8.1600 +    if ( c == '%' ) {
  8.1601 +      in_format = 1;
  8.1602 +      format_width = 0;
  8.1603 +      }
  8.1604 +    else if (in_format) {
  8.1605 +      if ( (c>='0') && (c<='9') ) {
  8.1606 +        format_width = (format_width * 10) + (c - '0');
  8.1607 +        }
  8.1608 +      else {
  8.1609 +        arg_ptr++; // increment to next arg
  8.1610 +        arg = read_word(arg_seg, arg_ptr);
  8.1611 +        if (c == 'x') {
  8.1612 +          if (format_width == 0)
  8.1613 +            format_width = 4;
  8.1614 +          for (i=format_width-1; i>=0; i--) {
  8.1615 +            nibble = (arg >> (4 * i)) & 0x000f;
  8.1616 +            send (action, (nibble<=9)? (nibble+'0') : (nibble-10+'A'));
  8.1617 +            }
  8.1618 +          }
  8.1619 +        else if (c == 'u') {
  8.1620 +          put_uint(action, arg, format_width, 0);
  8.1621 +          }
  8.1622 +        else if (c == 'd') {
  8.1623 +          if (arg & 0x8000)
  8.1624 +            put_int(action, -arg, format_width - 1, 1);
  8.1625 +          else
  8.1626 +            put_int(action, arg, format_width, 0);
  8.1627 +          }
  8.1628 +        else if (c == 's') {
  8.1629 +          bios_printf(action & (~BIOS_PRINTF_HALT), arg);
  8.1630 +          }
  8.1631 +        else if (c == 'c') {
  8.1632 +          send(action, arg);
  8.1633 +          }
  8.1634 +        else
  8.1635 +          BX_PANIC("bios_printf: unknown format\n");
  8.1636 +          in_format = 0;
  8.1637 +        }
  8.1638 +      }
  8.1639 +    else {
  8.1640 +      send(action, c);
  8.1641 +      }
  8.1642 +    s ++;
  8.1643 +    }
  8.1644 +
  8.1645 +  if (action & BIOS_PRINTF_HALT) {
  8.1646 +    // freeze in a busy loop.  
  8.1647 +ASM_START
  8.1648 +    cli
  8.1649 + halt2_loop:
  8.1650 +    hlt
  8.1651 +    jmp halt2_loop
  8.1652 +ASM_END
  8.1653 +    }
  8.1654 +}
  8.1655 +
  8.1656 +//--------------------------------------------------------------------------
  8.1657 +// keyboard_init
  8.1658 +//--------------------------------------------------------------------------
  8.1659 +// this file is based on LinuxBIOS implementation of keyboard.c
  8.1660 +// could convert to #asm to gain space
  8.1661 +  void
  8.1662 +keyboard_init()
  8.1663 +{
  8.1664 +    Bit16u max;
  8.1665 +
  8.1666 +    /* ------------------- Flush buffers ------------------------*/
  8.1667 +    /* Wait until buffer is empty */
  8.1668 +    max=0xffff;
  8.1669 +    while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
  8.1670 +
  8.1671 +    /* flush incoming keys */
  8.1672 +    max=0x2000;
  8.1673 +    while (--max > 0) {
  8.1674 +        outb(0x80, 0x00);
  8.1675 +        if (inb(0x64) & 0x01) {
  8.1676 +            inb(0x60);
  8.1677 +            max = 0x2000;
  8.1678 +            }
  8.1679 +        }
  8.1680 +  
  8.1681 +    // Due to timer issues, and if the IPS setting is > 15000000, 
  8.1682 +    // the incoming keys might not be flushed here. That will
  8.1683 +    // cause a panic a few lines below.  See sourceforge bug report :
  8.1684 +    // [ 642031 ] FATAL: Keyboard RESET error:993
  8.1685 +
  8.1686 +    /* ------------------- controller side ----------------------*/
  8.1687 +    /* send cmd = 0xAA, self test 8042 */
  8.1688 +    outb(0x64, 0xaa);
  8.1689 +
  8.1690 +    /* Wait until buffer is empty */
  8.1691 +    max=0xffff;
  8.1692 +    while ( (inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x00);
  8.1693 +    if (max==0x0) keyboard_panic(00);
  8.1694 +
  8.1695 +    /* Wait for data */
  8.1696 +    max=0xffff;
  8.1697 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x01);
  8.1698 +    if (max==0x0) keyboard_panic(01);
  8.1699 +
  8.1700 +    /* read self-test result, 0x55 should be returned from 0x60 */
  8.1701 +    if ((inb(0x60) != 0x55)){
  8.1702 +        keyboard_panic(991);
  8.1703 +    }
  8.1704 +
  8.1705 +    /* send cmd = 0xAB, keyboard interface test */
  8.1706 +    outb(0x64,0xab);
  8.1707 +
  8.1708 +    /* Wait until buffer is empty */
  8.1709 +    max=0xffff;
  8.1710 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x10);
  8.1711 +    if (max==0x0) keyboard_panic(10);
  8.1712 +
  8.1713 +    /* Wait for data */
  8.1714 +    max=0xffff;
  8.1715 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x11);
  8.1716 +    if (max==0x0) keyboard_panic(11);
  8.1717 +
  8.1718 +    /* read keyboard interface test result, */
  8.1719 +    /* 0x00 should be returned form 0x60 */
  8.1720 +    if ((inb(0x60) != 0x00)) {
  8.1721 +        keyboard_panic(992);
  8.1722 +    }
  8.1723 +
  8.1724 +    /* Enable Keyboard clock */
  8.1725 +    outb(0x64,0xae);
  8.1726 +    outb(0x64,0xa8);
  8.1727 +
  8.1728 +    /* ------------------- keyboard side ------------------------*/
  8.1729 +    /* reset kerboard and self test  (keyboard side) */
  8.1730 +    outb(0x60, 0xff);
  8.1731 +
  8.1732 +    /* Wait until buffer is empty */
  8.1733 +    max=0xffff;
  8.1734 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x20);
  8.1735 +    if (max==0x0) keyboard_panic(20);
  8.1736 +
  8.1737 +    /* Wait for data */
  8.1738 +    max=0xffff;
  8.1739 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x21);
  8.1740 +    if (max==0x0) keyboard_panic(21);
  8.1741 +
  8.1742 +    /* keyboard should return ACK */
  8.1743 +    if ((inb(0x60) != 0xfa)) {
  8.1744 +        keyboard_panic(993);
  8.1745 +    }
  8.1746 +
  8.1747 +    /* Wait for data */
  8.1748 +    max=0xffff;
  8.1749 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x31);
  8.1750 +    if (max==0x0) keyboard_panic(31);
  8.1751 +
  8.1752 +    if ((inb(0x60) != 0xaa)) {
  8.1753 +        keyboard_panic(994);
  8.1754 +    }
  8.1755 +
  8.1756 +    /* Disable keyboard */
  8.1757 +    outb(0x60, 0xf5);
  8.1758 +
  8.1759 +    /* Wait until buffer is empty */
  8.1760 +    max=0xffff;
  8.1761 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x40);
  8.1762 +    if (max==0x0) keyboard_panic(40);
  8.1763 +
  8.1764 +    /* Wait for data */
  8.1765 +    max=0xffff;
  8.1766 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x41);
  8.1767 +    if (max==0x0) keyboard_panic(41);
  8.1768 +
  8.1769 +    /* keyboard should return ACK */
  8.1770 +    if ((inb(0x60) != 0xfa)) {
  8.1771 +        keyboard_panic(995);
  8.1772 +    }
  8.1773 +
  8.1774 +    /* Write Keyboard Mode */
  8.1775 +    outb(0x64, 0x60);
  8.1776 +
  8.1777 +    /* Wait until buffer is empty */
  8.1778 +    max=0xffff;
  8.1779 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x50);
  8.1780 +    if (max==0x0) keyboard_panic(50);
  8.1781 +
  8.1782 +    /* send cmd: scan code convert, disable mouse, enable IRQ 1 */
  8.1783 +    outb(0x60, 0x61);
  8.1784 +
  8.1785 +    /* Wait until buffer is empty */
  8.1786 +    max=0xffff;
  8.1787 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x60);
  8.1788 +    if (max==0x0) keyboard_panic(60);
  8.1789 +
  8.1790 +    /* Enable keyboard */
  8.1791 +    outb(0x60, 0xf4);
  8.1792 +
  8.1793 +    /* Wait until buffer is empty */
  8.1794 +    max=0xffff;
  8.1795 +    while ((inb(0x64) & 0x02) && (--max>0)) outb(0x80, 0x70);
  8.1796 +    if (max==0x0) keyboard_panic(70);
  8.1797 +
  8.1798 +    /* Wait for data */
  8.1799 +    max=0xffff;
  8.1800 +    while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x71);
  8.1801 +    if (max==0x0) keyboard_panic(70);
  8.1802 +
  8.1803 +    /* keyboard should return ACK */
  8.1804 +    if ((inb(0x60) != 0xfa)) {
  8.1805 +        keyboard_panic(996);
  8.1806 +    }
  8.1807 +
  8.1808 +    outb(0x80, 0x77);
  8.1809 +}
  8.1810 +
  8.1811 +//--------------------------------------------------------------------------
  8.1812 +// keyboard_panic
  8.1813 +//--------------------------------------------------------------------------
  8.1814 +  void
  8.1815 +keyboard_panic(status)
  8.1816 +  Bit16u status;
  8.1817 +{
  8.1818 +  // If you're getting a 993 keyboard panic here, 
  8.1819 +  // please see the comment in keyboard_init
  8.1820 +  
  8.1821 +  BX_PANIC("Keyboard error:%u\n",status);
  8.1822 +}
  8.1823 +
  8.1824 +//--------------------------------------------------------------------------
  8.1825 +// shutdown_status_panic
  8.1826 +//   called when the shutdown statsu is not implemented, displays the status
  8.1827 +//--------------------------------------------------------------------------
  8.1828 +  void
  8.1829 +shutdown_status_panic(status)
  8.1830 +  Bit16u status;
  8.1831 +{
  8.1832 +  BX_PANIC("Unimplemented shutdown status: %02x\n",(Bit8u)status);
  8.1833 +}
  8.1834 +
  8.1835 +//--------------------------------------------------------------------------
  8.1836 +// print_bios_banner
  8.1837 +//   displays a the bios version
  8.1838 +//--------------------------------------------------------------------------
  8.1839 +void
  8.1840 +print_bios_banner()
  8.1841 +{
  8.1842 +  printf(BX_APPNAME" BIOS, %d cpu%s, ", BX_SMP_PROCESSORS, BX_SMP_PROCESSORS>1?"s":"");
  8.1843 +  printf("%s %s\n", bios_cvs_version_string, bios_date_string);
  8.1844 +  printf("\n");
  8.1845 +}
  8.1846 +
  8.1847 +//--------------------------------------------------------------------------
  8.1848 +// print_boot_device
  8.1849 +//   displays the boot device
  8.1850 +//--------------------------------------------------------------------------
  8.1851 +
  8.1852 +static char drivetypes[][10]={"Floppy","Hard Disk","CD-Rom"};
  8.1853 +
  8.1854 +void
  8.1855 +print_boot_device(cdboot, drive)
  8.1856 +  Bit8u cdboot; Bit16u drive;
  8.1857 +{
  8.1858 +  Bit8u i;
  8.1859 +
  8.1860 +  // cdboot contains 0 if floppy/harddisk, 1 otherwise
  8.1861 +  // drive contains real/emulated boot drive
  8.1862 +
  8.1863 +  if(cdboot)i=2;                    // CD-Rom
  8.1864 +  else if((drive&0x0080)==0x00)i=0; // Floppy
  8.1865 +  else if((drive&0x0080)==0x80)i=1; // Hard drive
  8.1866 +  else return;
  8.1867 +  
  8.1868 +  printf("Booting from %s...\n",drivetypes[i]);
  8.1869 +}
  8.1870 +
  8.1871 +//--------------------------------------------------------------------------
  8.1872 +// print_boot_failure
  8.1873 +//   displays the reason why boot failed
  8.1874 +//--------------------------------------------------------------------------
  8.1875 +  void
  8.1876 +print_boot_failure(cdboot, drive, reason, lastdrive)
  8.1877 +  Bit8u cdboot; Bit8u drive; Bit8u lastdrive;
  8.1878 +{
  8.1879 +  Bit16u drivenum = drive&0x7f;
  8.1880 +
  8.1881 +  // cdboot: 1 if boot from cd, 0 otherwise
  8.1882 +  // drive : drive number
  8.1883 +  // reason: 0 signature check failed, 1 read error
  8.1884 +  // lastdrive: 1 boot drive is the last one in boot sequence
  8.1885 + 
  8.1886 +  if (cdboot)
  8.1887 +    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s failed\n",drivetypes[2]);
  8.1888 +  else if (drive & 0x80)
  8.1889 +    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[1],drivenum);
  8.1890 +  else
  8.1891 +    bios_printf(BIOS_PRINTF_INFO | BIOS_PRINTF_SCREEN, "Boot from %s %d failed\n", drivetypes[0],drivenum);
  8.1892 +
  8.1893 +  if (lastdrive==1) {
  8.1894 +    if (reason==0)
  8.1895 +      BX_PANIC("Not a bootable disk\n");
  8.1896 +    else
  8.1897 +      BX_PANIC("Could not read the boot disk\n");
  8.1898 +  }
  8.1899 +}
  8.1900 +
  8.1901 +//--------------------------------------------------------------------------
  8.1902 +// print_cdromboot_failure
  8.1903 +//   displays the reason why boot failed
  8.1904 +//--------------------------------------------------------------------------
  8.1905 +  void
  8.1906 +print_cdromboot_failure( code )
  8.1907 +  Bit16u code;
  8.1908 +{
  8.1909 +  bios_printf(BIOS_PRINTF_SCREEN | BIOS_PRINTF_INFO, "CDROM boot failure code : %04x\n",code);
  8.1910 +  
  8.1911 +  return;
  8.1912 +}
  8.1913 +
  8.1914 +void
  8.1915 +nmi_handler_msg()
  8.1916 +{
  8.1917 +  BX_PANIC("NMI Handler called\n");
  8.1918 +}
  8.1919 +
  8.1920 +void
  8.1921 +int18_panic_msg()
  8.1922 +{
  8.1923 +  BX_PANIC("INT18: BOOT FAILURE\n");
  8.1924 +}
  8.1925 +
  8.1926 +void
  8.1927 +log_bios_start()
  8.1928 +{
  8.1929 +#if BX_DEBUG_SERIAL
  8.1930 +  outb(BX_DEBUG_PORT+UART_LCR, 0x03); /* setup for serial logging: 8N1 */
  8.1931 +#endif
  8.1932 +  BX_INFO("%s\n", bios_version_string);
  8.1933 +}
  8.1934 +
  8.1935 +  bx_bool
  8.1936 +set_enable_a20(val)
  8.1937 +  bx_bool val;
  8.1938 +{
  8.1939 +  Bit8u  oldval;
  8.1940 +
  8.1941 +  // Use PS2 System Control port A to set A20 enable
  8.1942 +
  8.1943 +  // get current setting first
  8.1944 +  oldval = inb(0x92);
  8.1945 +
  8.1946 +  // change A20 status
  8.1947 +  if (val)
  8.1948 +    outb(0x92, oldval | 0x02);
  8.1949 +  else
  8.1950 +    outb(0x92, oldval & 0xfd);
  8.1951 +
  8.1952 +  return((oldval & 0x02) != 0);
  8.1953 +}
  8.1954 +
  8.1955 +  void
  8.1956 +debugger_on()
  8.1957 +{
  8.1958 +  outb(0xfedc, 0x01);
  8.1959 +}
  8.1960 +
  8.1961 +  void
  8.1962 +debugger_off()
  8.1963 +{
  8.1964 +  outb(0xfedc, 0x00);
  8.1965 +}
  8.1966 +
  8.1967 +#if BX_USE_ATADRV
  8.1968 +
  8.1969 +// ---------------------------------------------------------------------------
  8.1970 +// Start of ATA/ATAPI Driver
  8.1971 +// ---------------------------------------------------------------------------
  8.1972 +
  8.1973 +// Global defines -- ATA register and register bits.
  8.1974 +// command block & control block regs
  8.1975 +#define ATA_CB_DATA  0   // data reg         in/out pio_base_addr1+0
  8.1976 +#define ATA_CB_ERR   1   // error            in     pio_base_addr1+1
  8.1977 +#define ATA_CB_FR    1   // feature reg         out pio_base_addr1+1
  8.1978 +#define ATA_CB_SC    2   // sector count     in/out pio_base_addr1+2
  8.1979 +#define ATA_CB_SN    3   // sector number    in/out pio_base_addr1+3
  8.1980 +#define ATA_CB_CL    4   // cylinder low     in/out pio_base_addr1+4
  8.1981 +#define ATA_CB_CH    5   // cylinder high    in/out pio_base_addr1+5
  8.1982 +#define ATA_CB_DH    6   // device head      in/out pio_base_addr1+6
  8.1983 +#define ATA_CB_STAT  7   // primary status   in     pio_base_addr1+7
  8.1984 +#define ATA_CB_CMD   7   // command             out pio_base_addr1+7
  8.1985 +#define ATA_CB_ASTAT 6   // alternate status in     pio_base_addr2+6
  8.1986 +#define ATA_CB_DC    6   // device control      out pio_base_addr2+6
  8.1987 +#define ATA_CB_DA    7   // device address   in     pio_base_addr2+7
  8.1988 +
  8.1989 +#define ATA_CB_ER_ICRC 0x80    // ATA Ultra DMA bad CRC
  8.1990 +#define ATA_CB_ER_BBK  0x80    // ATA bad block
  8.1991 +#define ATA_CB_ER_UNC  0x40    // ATA uncorrected error
  8.1992 +#define ATA_CB_ER_MC   0x20    // ATA media change
  8.1993 +#define ATA_CB_ER_IDNF 0x10    // ATA id not found
  8.1994 +#define ATA_CB_ER_MCR  0x08    // ATA media change request
  8.1995 +#define ATA_CB_ER_ABRT 0x04    // ATA command aborted
  8.1996 +#define ATA_CB_ER_NTK0 0x02    // ATA track 0 not found
  8.1997 +#define ATA_CB_ER_NDAM 0x01    // ATA address mark not found
  8.1998 +
  8.1999 +#define ATA_CB_ER_P_SNSKEY 0xf0   // ATAPI sense key (mask)
  8.2000 +#define ATA_CB_ER_P_MCR    0x08   // ATAPI Media Change Request
  8.2001 +#define ATA_CB_ER_P_ABRT   0x04   // ATAPI command abort
  8.2002 +#define ATA_CB_ER_P_EOM    0x02   // ATAPI End of Media
  8.2003 +#define ATA_CB_ER_P_ILI    0x01   // ATAPI Illegal Length Indication
  8.2004 +
  8.2005 +// ATAPI Interrupt Reason bits in the Sector Count reg (CB_SC)
  8.2006 +#define ATA_CB_SC_P_TAG    0xf8   // ATAPI tag (mask)
  8.2007 +#define ATA_CB_SC_P_REL    0x04   // ATAPI release
  8.2008 +#define ATA_CB_SC_P_IO     0x02   // ATAPI I/O
  8.2009 +#define ATA_CB_SC_P_CD     0x01   // ATAPI C/D
  8.2010 +
  8.2011 +// bits 7-4 of the device/head (CB_DH) reg
  8.2012 +#define ATA_CB_DH_DEV0 0xa0    // select device 0
  8.2013 +#define ATA_CB_DH_DEV1 0xb0    // select device 1
  8.2014 +
  8.2015 +// status reg (CB_STAT and CB_ASTAT) bits
  8.2016 +#define ATA_CB_STAT_BSY  0x80  // busy
  8.2017 +#define ATA_CB_STAT_RDY  0x40  // ready
  8.2018 +#define ATA_CB_STAT_DF   0x20  // device fault
  8.2019 +#define ATA_CB_STAT_WFT  0x20  // write fault (old name)
  8.2020 +#define ATA_CB_STAT_SKC  0x10  // seek complete
  8.2021 +#define ATA_CB_STAT_SERV 0x10  // service
  8.2022 +#define ATA_CB_STAT_DRQ  0x08  // data request
  8.2023 +#define ATA_CB_STAT_CORR 0x04  // corrected
  8.2024 +#define ATA_CB_STAT_IDX  0x02  // index
  8.2025 +#define ATA_CB_STAT_ERR  0x01  // error (ATA)
  8.2026 +#define ATA_CB_STAT_CHK  0x01  // check (ATAPI)
  8.2027 +
  8.2028 +// device control reg (CB_DC) bits
  8.2029 +#define ATA_CB_DC_HD15   0x08  // bit should always be set to one
  8.2030 +#define ATA_CB_DC_SRST   0x04  // soft reset
  8.2031 +#define ATA_CB_DC_NIEN   0x02  // disable interrupts
  8.2032 +
  8.2033 +// Most mandtory and optional ATA commands (from ATA-3),
  8.2034 +#define ATA_CMD_CFA_ERASE_SECTORS            0xC0
  8.2035 +#define ATA_CMD_CFA_REQUEST_EXT_ERR_CODE     0x03
  8.2036 +#define ATA_CMD_CFA_TRANSLATE_SECTOR         0x87
  8.2037 +#define ATA_CMD_CFA_WRITE_MULTIPLE_WO_ERASE  0xCD
  8.2038 +#define ATA_CMD_CFA_WRITE_SECTORS_WO_ERASE   0x38
  8.2039 +#define ATA_CMD_CHECK_POWER_MODE1            0xE5
  8.2040 +#define ATA_CMD_CHECK_POWER_MODE2            0x98
  8.2041 +#define ATA_CMD_DEVICE_RESET                 0x08
  8.2042 +#define ATA_CMD_EXECUTE_DEVICE_DIAGNOSTIC    0x90
  8.2043 +#define ATA_CMD_FLUSH_CACHE                  0xE7
  8.2044 +#define ATA_CMD_FORMAT_TRACK                 0x50
  8.2045 +#define ATA_CMD_IDENTIFY_DEVICE              0xEC
  8.2046 +#define ATA_CMD_IDENTIFY_DEVICE_PACKET       0xA1
  8.2047 +#define ATA_CMD_IDENTIFY_PACKET_DEVICE       0xA1
  8.2048 +#define ATA_CMD_IDLE1                        0xE3
  8.2049 +#define ATA_CMD_IDLE2                        0x97
  8.2050 +#define ATA_CMD_IDLE_IMMEDIATE1              0xE1
  8.2051 +#define ATA_CMD_IDLE_IMMEDIATE2              0x95
  8.2052 +#define ATA_CMD_INITIALIZE_DRIVE_PARAMETERS  0x91
  8.2053 +#define ATA_CMD_INITIALIZE_DEVICE_PARAMETERS 0x91
  8.2054 +#define ATA_CMD_NOP                          0x00
  8.2055 +#define ATA_CMD_PACKET                       0xA0
  8.2056 +#define ATA_CMD_READ_BUFFER                  0xE4
  8.2057 +#define ATA_CMD_READ_DMA                     0xC8
  8.2058 +#define ATA_CMD_READ_DMA_QUEUED              0xC7
  8.2059 +#define ATA_CMD_READ_MULTIPLE                0xC4
  8.2060 +#define ATA_CMD_READ_SECTORS                 0x20
  8.2061 +#define ATA_CMD_READ_VERIFY_SECTORS          0x40
  8.2062 +#define ATA_CMD_RECALIBRATE                  0x10
  8.2063 +#define ATA_CMD_SEEK                         0x70
  8.2064 +#define ATA_CMD_SET_FEATURES                 0xEF
  8.2065 +#define ATA_CMD_SET_MULTIPLE_MODE            0xC6
  8.2066 +#define ATA_CMD_SLEEP1                       0xE6
  8.2067 +#define ATA_CMD_SLEEP2                       0x99
  8.2068 +#define ATA_CMD_STANDBY1                     0xE2
  8.2069 +#define ATA_CMD_STANDBY2                     0x96
  8.2070 +#define ATA_CMD_STANDBY_IMMEDIATE1           0xE0
  8.2071 +#define ATA_CMD_STANDBY_IMMEDIATE2           0x94
  8.2072 +#define ATA_CMD_WRITE_BUFFER                 0xE8
  8.2073 +#define ATA_CMD_WRITE_DMA                    0xCA
  8.2074 +#define ATA_CMD_WRITE_DMA_QUEUED             0xCC
  8.2075 +#define ATA_CMD_WRITE_MULTIPLE               0xC5
  8.2076 +#define ATA_CMD_WRITE_SECTORS                0x30
  8.2077 +#define ATA_CMD_WRITE_VERIFY                 0x3C
  8.2078 +
  8.2079 +#define ATA_IFACE_NONE    0x00
  8.2080 +#define ATA_IFACE_ISA     0x00
  8.2081 +#define ATA_IFACE_PCI     0x01
  8.2082 +
  8.2083 +#define ATA_TYPE_NONE     0x00
  8.2084 +#define ATA_TYPE_UNKNOWN  0x01
  8.2085 +#define ATA_TYPE_ATA      0x02
  8.2086 +#define ATA_TYPE_ATAPI    0x03
  8.2087 +
  8.2088 +#define ATA_DEVICE_NONE  0x00
  8.2089 +#define ATA_DEVICE_HD    0xFF
  8.2090 +#define ATA_DEVICE_CDROM 0x05
  8.2091 +
  8.2092 +#define ATA_MODE_NONE    0x00
  8.2093 +#define ATA_MODE_PIO16   0x00
  8.2094 +#define ATA_MODE_PIO32   0x01
  8.2095 +#define ATA_MODE_ISADMA  0x02
  8.2096 +#define ATA_MODE_PCIDMA  0x03
  8.2097 +#define ATA_MODE_USEIRQ  0x10
  8.2098 +
  8.2099 +#define ATA_TRANSLATION_NONE  0
  8.2100 +#define ATA_TRANSLATION_LBA   1
  8.2101 +#define ATA_TRANSLATION_LARGE 2
  8.2102 +#define ATA_TRANSLATION_RECHS 3
  8.2103 +
  8.2104 +#define ATA_DATA_NO      0x00
  8.2105 +#define ATA_DATA_IN      0x01
  8.2106 +#define ATA_DATA_OUT     0x02
  8.2107 +  
  8.2108 +// ---------------------------------------------------------------------------
  8.2109 +// ATA/ATAPI driver : initialization
  8.2110 +// ---------------------------------------------------------------------------
  8.2111 +void ata_init( )
  8.2112 +{
  8.2113 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.2114 +  Bit8u  channel, device;
  8.2115 +
  8.2116 +  // Channels info init. 
  8.2117 +  for (channel=0; channel<BX_MAX_ATA_INTERFACES; channel++) {
  8.2118 +    write_byte(ebda_seg,&EbdaData->ata.channels[channel].iface,ATA_IFACE_NONE);
  8.2119 +    write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1,0x0);
  8.2120 +    write_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2,0x0);
  8.2121 +    write_byte(ebda_seg,&EbdaData->ata.channels[channel].irq,0);
  8.2122 +    }
  8.2123 +
  8.2124 +  // Devices info init. 
  8.2125 +  for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
  8.2126 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_NONE);
  8.2127 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_NONE);
  8.2128 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].removable,0);
  8.2129 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].lock,0);
  8.2130 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].mode,ATA_MODE_NONE);
  8.2131 +    write_word(ebda_seg,&EbdaData->ata.devices[device].blksize,0);
  8.2132 +    write_byte(ebda_seg,&EbdaData->ata.devices[device].translation,ATA_TRANSLATION_NONE);
  8.2133 +    write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads,0);
  8.2134 +    write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders,0);
  8.2135 +    write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt,0);
  8.2136 +    write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads,0);
  8.2137 +    write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders,0);
  8.2138 +    write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt,0);
  8.2139 +    
  8.2140 +    write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors,0L);
  8.2141 +    }
  8.2142 +
  8.2143 +  // hdidmap  and cdidmap init. 
  8.2144 +  for (device=0; device<BX_MAX_ATA_DEVICES; device++) {
  8.2145 +    write_byte(ebda_seg,&EbdaData->ata.hdidmap[device],BX_MAX_ATA_DEVICES);
  8.2146 +    write_byte(ebda_seg,&EbdaData->ata.cdidmap[device],BX_MAX_ATA_DEVICES);
  8.2147 +    }
  8.2148 +
  8.2149 +  write_byte(ebda_seg,&EbdaData->ata.hdcount,0);
  8.2150 +  write_byte(ebda_seg,&EbdaData->ata.cdcount,0);
  8.2151 +}
  8.2152 +
  8.2153 +// ---------------------------------------------------------------------------
  8.2154 +// ATA/ATAPI driver : device detection
  8.2155 +// ---------------------------------------------------------------------------
  8.2156 +
  8.2157 +void ata_detect( )
  8.2158 +{
  8.2159 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.2160 +  Bit8u  hdcount, cdcount, device, type;
  8.2161 +  Bit8u  buffer[0x0200];
  8.2162 +
  8.2163 +#if BX_MAX_ATA_INTERFACES > 0
  8.2164 +  write_byte(ebda_seg,&EbdaData->ata.channels[0].iface,ATA_IFACE_ISA);
  8.2165 +  write_word(ebda_seg,&EbdaData->ata.channels[0].iobase1,0x1f0);
  8.2166 +  write_word(ebda_seg,&EbdaData->ata.channels[0].iobase2,0x3f0);
  8.2167 +  write_byte(ebda_seg,&EbdaData->ata.channels[0].irq,14);
  8.2168 +#endif
  8.2169 +#if BX_MAX_ATA_INTERFACES > 1
  8.2170 +  write_byte(ebda_seg,&EbdaData->ata.channels[1].iface,ATA_IFACE_ISA);
  8.2171 +  write_word(ebda_seg,&EbdaData->ata.channels[1].iobase1,0x170);
  8.2172 +  write_word(ebda_seg,&EbdaData->ata.channels[1].iobase2,0x370);
  8.2173 +  write_byte(ebda_seg,&EbdaData->ata.channels[1].irq,15);
  8.2174 +#endif
  8.2175 +#if BX_MAX_ATA_INTERFACES > 2
  8.2176 +  write_byte(ebda_seg,&EbdaData->ata.channels[2].iface,ATA_IFACE_ISA);
  8.2177 +  write_word(ebda_seg,&EbdaData->ata.channels[2].iobase1,0x1e8);
  8.2178 +  write_word(ebda_seg,&EbdaData->ata.channels[2].iobase2,0x3e0);
  8.2179 +  write_byte(ebda_seg,&EbdaData->ata.channels[2].irq,12);
  8.2180 +#endif
  8.2181 +#if BX_MAX_ATA_INTERFACES > 3
  8.2182 +  write_byte(ebda_seg,&EbdaData->ata.channels[3].iface,ATA_IFACE_ISA);
  8.2183 +  write_word(ebda_seg,&EbdaData->ata.channels[3].iobase1,0x168);
  8.2184 +  write_word(ebda_seg,&EbdaData->ata.channels[3].iobase2,0x360);
  8.2185 +  write_byte(ebda_seg,&EbdaData->ata.channels[3].irq,11);
  8.2186 +#endif
  8.2187 +#if BX_MAX_ATA_INTERFACES > 4
  8.2188 +#error Please fill the ATA interface informations
  8.2189 +#endif
  8.2190 +
  8.2191 +  // Device detection
  8.2192 +  hdcount=cdcount=0;
  8.2193 +  
  8.2194 +  for(device=0; device<BX_MAX_ATA_DEVICES; device++) {
  8.2195 +    Bit16u iobase1, iobase2;
  8.2196 +    Bit8u  channel, slave, shift;
  8.2197 +    Bit8u  sc, sn, cl, ch, st;
  8.2198 +
  8.2199 +    channel = device / 2;
  8.2200 +    slave = device % 2;
  8.2201 +
  8.2202 +    iobase1 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase1);
  8.2203 +    iobase2 =read_word(ebda_seg,&EbdaData->ata.channels[channel].iobase2);
  8.2204 +
  8.2205 +    // Disable interrupts
  8.2206 +    outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
  8.2207 +
  8.2208 +    // Look for device
  8.2209 +    outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
  8.2210 +    outb(iobase1+ATA_CB_SC, 0x55);
  8.2211 +    outb(iobase1+ATA_CB_SN, 0xaa);
  8.2212 +    outb(iobase1+ATA_CB_SC, 0xaa);
  8.2213 +    outb(iobase1+ATA_CB_SN, 0x55);
  8.2214 +    outb(iobase1+ATA_CB_SC, 0x55);
  8.2215 +    outb(iobase1+ATA_CB_SN, 0xaa);
  8.2216 +
  8.2217 +    // If we found something
  8.2218 +    sc = inb(iobase1+ATA_CB_SC);
  8.2219 +    sn = inb(iobase1+ATA_CB_SN);
  8.2220 +
  8.2221 +    if ( (sc == 0x55) && (sn == 0xaa) ) {
  8.2222 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_UNKNOWN);
  8.2223 +    
  8.2224 +      // reset the channel
  8.2225 +      ata_reset (device);
  8.2226 +      
  8.2227 +      // check for ATA or ATAPI
  8.2228 +      outb(iobase1+ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
  8.2229 +      sc = inb(iobase1+ATA_CB_SC);
  8.2230 +      sn = inb(iobase1+ATA_CB_SN);
  8.2231 +      if ( (sc==0x01) && (sn==0x01) ) {
  8.2232 +        cl = inb(iobase1+ATA_CB_CL);
  8.2233 +        ch = inb(iobase1+ATA_CB_CH);
  8.2234 +        st = inb(iobase1+ATA_CB_STAT);
  8.2235 +
  8.2236 +        if ( (cl==0x14) && (ch==0xeb) ) {
  8.2237 +          write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATAPI);
  8.2238 +          }
  8.2239 +        else if ( (cl==0x00) && (ch==0x00) && (st!=0x00) ) {
  8.2240 +          write_byte(ebda_seg,&EbdaData->ata.devices[device].type,ATA_TYPE_ATA);
  8.2241 +          }
  8.2242 +        }
  8.2243 +      }
  8.2244 +
  8.2245 +    type=read_byte(ebda_seg,&EbdaData->ata.devices[device].type);
  8.2246 +    
  8.2247 +    // Now we send a IDENTIFY command to ATA device 
  8.2248 +    if(type == ATA_TYPE_ATA) {
  8.2249 +      Bit32u sectors;
  8.2250 +      Bit16u cylinders, heads, spt, blksize;
  8.2251 +      Bit8u  translation, removable, mode;
  8.2252 +
  8.2253 +      //Temporary values to do the transfer
  8.2254 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
  8.2255 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
  8.2256 +
  8.2257 +      if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE, 1, 0, 0, 0, 0L, get_SS(),buffer) !=0 )
  8.2258 +        BX_PANIC("ata-detect: Failed to detect ATA device\n");
  8.2259 +
  8.2260 +      removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
  8.2261 +      mode      = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
  8.2262 +      blksize   = read_word(get_SS(),buffer+10);
  8.2263 +      
  8.2264 +      cylinders = read_word(get_SS(),buffer+(1*2)); // word 1
  8.2265 +      heads     = read_word(get_SS(),buffer+(3*2)); // word 3
  8.2266 +      spt       = read_word(get_SS(),buffer+(6*2)); // word 6
  8.2267 +
  8.2268 +      sectors   = read_dword(get_SS(),buffer+(60*2)); // word 60 and word 61
  8.2269 +
  8.2270 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_HD);
  8.2271 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
  8.2272 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
  8.2273 +      write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
  8.2274 +      write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.heads, heads);
  8.2275 +      write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.cylinders, cylinders);
  8.2276 +      write_word(ebda_seg,&EbdaData->ata.devices[device].pchs.spt, spt);
  8.2277 +      write_dword(ebda_seg,&EbdaData->ata.devices[device].sectors, sectors);
  8.2278 +      BX_INFO("ata%d-%d: PCHS=%u/%d/%d translation=", channel, slave,cylinders, heads, spt);
  8.2279 +
  8.2280 +      translation = inb_cmos(0x39 + channel/2);
  8.2281 +      for (shift=device%4; shift>0; shift--) translation >>= 2;
  8.2282 +      translation &= 0x03;
  8.2283 +
  8.2284 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].translation, translation);
  8.2285 +
  8.2286 +      switch (translation) {
  8.2287 +        case ATA_TRANSLATION_NONE:
  8.2288 +          BX_INFO("none");
  8.2289 +          break;
  8.2290 +        case ATA_TRANSLATION_LBA:
  8.2291 +          BX_INFO("lba");
  8.2292 +          break;
  8.2293 +        case ATA_TRANSLATION_LARGE:
  8.2294 +          BX_INFO("large");
  8.2295 +          break;
  8.2296 +        case ATA_TRANSLATION_RECHS:
  8.2297 +          BX_INFO("r-echs");
  8.2298 +          break;
  8.2299 +        }
  8.2300 +      switch (translation) {
  8.2301 +        case ATA_TRANSLATION_NONE:
  8.2302 +          break;
  8.2303 +        case ATA_TRANSLATION_LBA:
  8.2304 +          spt = 63;
  8.2305 +          sectors /= 63;
  8.2306 +          heads = sectors / 1024;
  8.2307 +          if (heads>128) heads = 255;
  8.2308 +          else if (heads>64) heads = 128;
  8.2309 +          else if (heads>32) heads = 64;
  8.2310 +          else if (heads>16) heads = 32;
  8.2311 +          else heads=16;
  8.2312 +          cylinders = sectors / heads;
  8.2313 +          break;
  8.2314 +        case ATA_TRANSLATION_RECHS:
  8.2315 +          // Take care not to overflow
  8.2316 +          if (heads==16) {
  8.2317 +            if(cylinders>61439) cylinders=61439;
  8.2318 +            heads=15;
  8.2319 +            cylinders = (Bit16u)((Bit32u)(cylinders)*16/15);
  8.2320 +            }
  8.2321 +          // then go through the large bitshift process
  8.2322 +        case ATA_TRANSLATION_LARGE:
  8.2323 +          while(cylinders > 1024) {
  8.2324 +            cylinders >>= 1;
  8.2325 +            heads <<= 1;
  8.2326 +
  8.2327 +            // If we max out the head count
  8.2328 +            if (heads > 127) break;
  8.2329 +          }
  8.2330 +          break;
  8.2331 +        }
  8.2332 +      // clip to 1024 cylinders in lchs
  8.2333 +      if (cylinders > 1024) cylinders=1024;
  8.2334 +      BX_INFO(" LCHS=%d/%d/%d\n", cylinders, heads, spt);
  8.2335 +
  8.2336 +      write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.heads, heads);
  8.2337 +      write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.cylinders, cylinders);
  8.2338 +      write_word(ebda_seg,&EbdaData->ata.devices[device].lchs.spt, spt);
  8.2339 + 
  8.2340 +      // fill hdidmap 
  8.2341 +      write_byte(ebda_seg,&EbdaData->ata.hdidmap[hdcount], device);
  8.2342 +      hdcount++;
  8.2343 +      }
  8.2344 +    
  8.2345 +    // Now we send a IDENTIFY command to ATAPI device
  8.2346 +    if(type == ATA_TYPE_ATAPI) {
  8.2347 + 
  8.2348 +      Bit8u  type, removable, mode;
  8.2349 +      Bit16u blksize;
  8.2350 +
  8.2351 +      //Temporary values to do the transfer
  8.2352 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].device,ATA_DEVICE_CDROM);
  8.2353 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, ATA_MODE_PIO16);
  8.2354 +
  8.2355 +      if (ata_cmd_data_in(device,ATA_CMD_IDENTIFY_DEVICE_PACKET, 1, 0, 0, 0, 0L, get_SS(),buffer) != 0)
  8.2356 +        BX_PANIC("ata-detect: Failed to detect ATAPI device\n");
  8.2357 +
  8.2358 +      type      = read_byte(get_SS(),buffer+1) & 0x1f;
  8.2359 +      removable = (read_byte(get_SS(),buffer+0) & 0x80) ? 1 : 0;
  8.2360 +      mode      = read_byte(get_SS(),buffer+96) ? ATA_MODE_PIO32 : ATA_MODE_PIO16;
  8.2361 +      blksize   = 2048;
  8.2362 +
  8.2363 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].device, type);
  8.2364 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].removable, removable);
  8.2365 +      write_byte(ebda_seg,&EbdaData->ata.devices[device].mode, mode);
  8.2366 +      write_word(ebda_seg,&EbdaData->ata.devices[device].blksize, blksize);
  8.2367 +
  8.2368 +      // fill cdidmap 
  8.2369 +      write_byte(ebda_seg,&EbdaData->ata.cdidmap[cdcount], device);
  8.2370 +      cdcount++;
  8.2371 +      }
  8.2372 +  
  8.2373 +      {
  8.2374 +      Bit32u sizeinmb;
  8.2375 +      Bit16u ataversion;
  8.2376 +      Bit8u  c, i, version, model[41];
  8.2377 +      
  8.2378 +      switch (type) {
  8.2379 +        case ATA_TYPE_ATA:
  8.2380 +          sizeinmb = read_dword(ebda_seg,&EbdaData->ata.devices[device].sectors);
  8.2381 +          sizeinmb >>= 11;
  8.2382 +        case ATA_TYPE_ATAPI:
  8.2383 +          // Read ATA/ATAPI version
  8.2384 +          ataversion=((Bit16u)(read_byte(get_SS(),buffer+161))<<8)|read_byte(get_SS(),buffer+160);
  8.2385 +          for(version=15;version>0;version--) { 
  8.2386 +            if((ataversion&(1<<version))!=0)
  8.2387 +            break;
  8.2388 +            }
  8.2389 +
  8.2390 +          // Read model name
  8.2391 +          for(i=0;i<20;i++){
  8.2392 +            write_byte(get_SS(),model+(i*2),read_byte(get_SS(),buffer+(i*2)+54+1));
  8.2393 +            write_byte(get_SS(),model+(i*2)+1,read_byte(get_SS(),buffer+(i*2)+54));
  8.2394 +            }
  8.2395 +
  8.2396 +          // Reformat
  8.2397 +          write_byte(get_SS(),model+40,0x00);
  8.2398 +          for(i=39;i>0;i--){
  8.2399 +            if(read_byte(get_SS(),model+i)==0x20)
  8.2400 +              write_byte(get_SS(),model+i,0x00);
  8.2401 +            else break;
  8.2402 +            }
  8.2403 +          break;
  8.2404 +        }
  8.2405 +
  8.2406 +      switch (type) {
  8.2407 +        case ATA_TYPE_ATA:
  8.2408 +          printf("ata%d %s: ",channel,slave?" slave":"master");
  8.2409 +          i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
  8.2410 +          printf(" ATA-%d Hard-Disk (%d MBytes)\n",version,(Bit16u)sizeinmb);
  8.2411 +          break;
  8.2412 +        case ATA_TYPE_ATAPI:
  8.2413 +          printf("ata%d %s: ",channel,slave?" slave":"master");
  8.2414 +          i=0; while(c=read_byte(get_SS(),model+i++)) printf("%c",c);
  8.2415 +          if(read_byte(ebda_seg,&EbdaData->ata.devices[device].device)==ATA_DEVICE_CDROM)
  8.2416 +            printf(" ATAPI-%d CD-Rom/DVD-Rom\n",version);
  8.2417 +          else
  8.2418 +            printf(" ATAPI-%d Device\n",version);
  8.2419 +          break;
  8.2420 +        case ATA_TYPE_UNKNOWN:
  8.2421 +          printf("ata%d %s: Unknown device\n",channel,slave?" slave":"master");
  8.2422 +          break;
  8.2423 +        }
  8.2424 +      }
  8.2425 +    }
  8.2426 +
  8.2427 +  // Store the devices counts
  8.2428 +  write_byte(ebda_seg,&EbdaData->ata.hdcount, hdcount);
  8.2429 +  write_byte(ebda_seg,&EbdaData->ata.cdcount, cdcount);
  8.2430 +  write_byte(0x40,0x75, hdcount);
  8.2431 + 
  8.2432 +  printf("\n");
  8.2433 +
  8.2434 +  // FIXME : should use bios=cmos|auto|disable bits
  8.2435 +  // FIXME : should know about translation bits
  8.2436 +  // FIXME : move hard_drive_post here 
  8.2437 +  
  8.2438 +}
  8.2439 +
  8.2440 +// ---------------------------------------------------------------------------
  8.2441 +// ATA/ATAPI driver : software reset 
  8.2442 +// ---------------------------------------------------------------------------
  8.2443 +// ATA-3
  8.2444 +// 8.2.1 Software reset - Device 0
  8.2445 +
  8.2446 +void   ata_reset(device)
  8.2447 +Bit16u device;
  8.2448 +{
  8.2449 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.2450 +  Bit16u iobase1, iobase2;
  8.2451 +  Bit8u  channel, slave, sn, sc; 
  8.2452 +  Bit16u max;
  8.2453 +
  8.2454 +  channel = device / 2;
  8.2455 +  slave = device % 2;
  8.2456 +
  8.2457 +  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
  8.2458 +  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
  8.2459 +
  8.2460 +  // Reset
  8.2461 +
  8.2462 +// 8.2.1 (a) -- set SRST in DC
  8.2463 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN | ATA_CB_DC_SRST);
  8.2464 +
  8.2465 +// 8.2.1 (b) -- wait for BSY
  8.2466 +  max=0xff;
  8.2467 +  while(--max>0) {
  8.2468 +    Bit8u status = inb(iobase1+ATA_CB_STAT);
  8.2469 +    if ((status & ATA_CB_STAT_BSY) != 0) break;
  8.2470 +  }
  8.2471 +
  8.2472 +// 8.2.1 (f) -- clear SRST
  8.2473 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
  8.2474 +
  8.2475 +  if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_NONE) {
  8.2476 +
  8.2477 +// 8.2.1 (g) -- check for sc==sn==0x01
  8.2478 +    // select device
  8.2479 +    outb(iobase1+ATA_CB_DH, slave?ATA_CB_DH_DEV1:ATA_CB_DH_DEV0);
  8.2480 +    sc = inb(iobase1+ATA_CB_SC);
  8.2481 +    sn = inb(iobase1+ATA_CB_SN);
  8.2482 +
  8.2483 +    if ( (sc==0x01) && (sn==0x01) ) {
  8.2484 +
  8.2485 +// 8.2.1 (h) -- wait for not BSY
  8.2486 +      max=0xff;
  8.2487 +      while(--max>0) {
  8.2488 +        Bit8u status = inb(iobase1+ATA_CB_STAT);
  8.2489 +        if ((status & ATA_CB_STAT_BSY) == 0) break;
  8.2490 +        }
  8.2491 +      }
  8.2492 +    }
  8.2493 +
  8.2494 +// 8.2.1 (i) -- wait for DRDY
  8.2495 +  max=0xfff;
  8.2496 +  while(--max>0) {
  8.2497 +    Bit8u status = inb(iobase1+ATA_CB_STAT);
  8.2498 +      if ((status & ATA_CB_STAT_RDY) != 0) break;
  8.2499 +  }
  8.2500 +
  8.2501 +  // Enable interrupts
  8.2502 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
  8.2503 +}
  8.2504 +
  8.2505 +// ---------------------------------------------------------------------------
  8.2506 +// ATA/ATAPI driver : execute a non data command 
  8.2507 +// ---------------------------------------------------------------------------
  8.2508 +
  8.2509 +Bit16u ata_cmd_non_data()
  8.2510 +{return 0;}
  8.2511 +
  8.2512 +// ---------------------------------------------------------------------------
  8.2513 +// ATA/ATAPI driver : execute a data-in command
  8.2514 +// ---------------------------------------------------------------------------
  8.2515 +      // returns
  8.2516 +      // 0 : no error
  8.2517 +      // 1 : BUSY bit set
  8.2518 +      // 2 : read error
  8.2519 +      // 3 : expected DRQ=1
  8.2520 +      // 4 : no sectors left to read/verify
  8.2521 +      // 5 : more sectors to read/verify
  8.2522 +      // 6 : no sectors left to write
  8.2523 +      // 7 : more sectors to write
  8.2524 +Bit16u ata_cmd_data_in(device, command, count, cylinder, head, sector, lba, segment, offset)
  8.2525 +Bit16u device, command, count, cylinder, head, sector, segment, offset;
  8.2526 +Bit32u lba;
  8.2527 +{
  8.2528 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.2529 +  Bit16u iobase1, iobase2, blksize;
  8.2530 +  Bit8u  channel, slave;
  8.2531 +  Bit8u  status, current, mode;
  8.2532 +
  8.2533 +  channel = device / 2;
  8.2534 +  slave   = device % 2;
  8.2535 +
  8.2536 +  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
  8.2537 +  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
  8.2538 +  mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
  8.2539 +  blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
  8.2540 +  if (mode == ATA_MODE_PIO32) blksize>>=2;
  8.2541 +  else blksize>>=1;
  8.2542 +
  8.2543 +  // sector will be 0 only on lba access. Convert to lba-chs
  8.2544 +  if (sector == 0) {
  8.2545 +    sector = (Bit16u) (lba & 0x000000ffL);
  8.2546 +    lba >>= 8;
  8.2547 +    cylinder = (Bit16u) (lba & 0x0000ffffL);
  8.2548 +    lba >>= 16;
  8.2549 +    head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
  8.2550 +    }
  8.2551 +
  8.2552 +  // Reset count of transferred data
  8.2553 +  write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
  8.2554 +  write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
  8.2555 +  current = 0;
  8.2556 +
  8.2557 +  status = inb(iobase1 + ATA_CB_STAT);
  8.2558 +  if (status & ATA_CB_STAT_BSY) return 1;
  8.2559 +
  8.2560 +  outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
  8.2561 +  outb(iobase1 + ATA_CB_FR, 0x00);
  8.2562 +  outb(iobase1 + ATA_CB_SC, count);
  8.2563 +  outb(iobase1 + ATA_CB_SN, sector);
  8.2564 +  outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
  8.2565 +  outb(iobase1 + ATA_CB_CH, cylinder >> 8);
  8.2566 +  outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
  8.2567 +  outb(iobase1 + ATA_CB_CMD, command);
  8.2568 +
  8.2569 +  while (1) {
  8.2570 +    status = inb(iobase1 + ATA_CB_STAT);
  8.2571 +    if ( !(status & ATA_CB_STAT_BSY) ) break;
  8.2572 +    }
  8.2573 +
  8.2574 +  if (status & ATA_CB_STAT_ERR) {
  8.2575 +    BX_DEBUG_ATA("ata_cmd_data_in : read error\n");
  8.2576 +    return 2;
  8.2577 +    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
  8.2578 +    BX_DEBUG_ATA("ata_cmd_data_in : DRQ not set (status %02x)\n", (unsigned) status);
  8.2579 +    return 3;
  8.2580 +  }
  8.2581 +
  8.2582 +  // FIXME : move seg/off translation here
  8.2583 +
  8.2584 +ASM_START
  8.2585 +        sti  ;; enable higher priority interrupts
  8.2586 +ASM_END
  8.2587 +
  8.2588 +  while (1) {
  8.2589 +
  8.2590 +ASM_START
  8.2591 +        push bp
  8.2592 +        mov  bp, sp
  8.2593 +        mov  di, _ata_cmd_data_in.offset + 2[bp]  
  8.2594 +        mov  ax, _ata_cmd_data_in.segment + 2[bp] 
  8.2595 +        mov  cx, _ata_cmd_data_in.blksize + 2[bp] 
  8.2596 +
  8.2597 +        ;; adjust if there will be an overrun. 2K max sector size
  8.2598 +        cmp   di, #0xf800 ;; 
  8.2599 +        jbe   ata_in_no_adjust
  8.2600 +
  8.2601 +ata_in_adjust:
  8.2602 +        sub   di, #0x0800 ;; sub 2 kbytes from offset
  8.2603 +        add   ax, #0x0080 ;; add 2 Kbytes to segment
  8.2604 +
  8.2605 +ata_in_no_adjust:
  8.2606 +        mov   es, ax      ;; segment in es
  8.2607 +
  8.2608 +        mov   dx, _ata_cmd_data_in.iobase1 + 2[bp] ;; ATA data read port
  8.2609 +
  8.2610 +        mov  ah, _ata_cmd_data_in.mode + 2[bp] 
  8.2611 +        cmp  ah, #ATA_MODE_PIO32
  8.2612 +        je   ata_in_32
  8.2613 +
  8.2614 +ata_in_16:
  8.2615 +        rep
  8.2616 +          insw ;; CX words transfered from port(DX) to ES:[DI]
  8.2617 +        jmp ata_in_done
  8.2618 +
  8.2619 +ata_in_32:
  8.2620 +        rep
  8.2621 +          insd ;; CX dwords transfered from port(DX) to ES:[DI]
  8.2622 +
  8.2623 +ata_in_done:
  8.2624 +        mov  _ata_cmd_data_in.offset + 2[bp], di
  8.2625 +        mov  _ata_cmd_data_in.segment + 2[bp], es
  8.2626 +        pop  bp
  8.2627 +ASM_END
  8.2628 +
  8.2629 +    current++;
  8.2630 +    write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
  8.2631 +    count--;
  8.2632 +    status = inb(iobase1 + ATA_CB_STAT);
  8.2633 +    if (count == 0) {
  8.2634 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
  8.2635 +          != ATA_CB_STAT_RDY ) {
  8.2636 +        BX_DEBUG_ATA("ata_cmd_data_in : no sectors left (status %02x)\n", (unsigned) status);
  8.2637 +        return 4;
  8.2638 +        }
  8.2639 +      break;
  8.2640 +      }
  8.2641 +    else {
  8.2642 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
  8.2643 +          != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
  8.2644 +        BX_DEBUG_ATA("ata_cmd_data_in : more sectors left (status %02x)\n", (unsigned) status);
  8.2645 +        return 5;
  8.2646 +      }
  8.2647 +      continue;
  8.2648 +    }
  8.2649 +  }
  8.2650 +  // Enable interrupts
  8.2651 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
  8.2652 +  return 0;
  8.2653 +}
  8.2654 +
  8.2655 +// ---------------------------------------------------------------------------
  8.2656 +// ATA/ATAPI driver : execute a data-out command
  8.2657 +// ---------------------------------------------------------------------------
  8.2658 +      // returns
  8.2659 +      // 0 : no error
  8.2660 +      // 1 : BUSY bit set
  8.2661 +      // 2 : read error
  8.2662 +      // 3 : expected DRQ=1
  8.2663 +      // 4 : no sectors left to read/verify
  8.2664 +      // 5 : more sectors to read/verify
  8.2665 +      // 6 : no sectors left to write
  8.2666 +      // 7 : more sectors to write
  8.2667 +Bit16u ata_cmd_data_out(device, command, count, cylinder, head, sector, lba, segment, offset)
  8.2668 +Bit16u device, command, count, cylinder, head, sector, segment, offset;
  8.2669 +Bit32u lba;
  8.2670 +{
  8.2671 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.2672 +  Bit16u iobase1, iobase2, blksize;
  8.2673 +  Bit8u  channel, slave;
  8.2674 +  Bit8u  status, current, mode;
  8.2675 +
  8.2676 +  channel = device / 2;
  8.2677 +  slave   = device % 2;
  8.2678 +
  8.2679 +  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
  8.2680 +  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
  8.2681 +  mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
  8.2682 +  blksize = 0x200; // was = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
  8.2683 +  if (mode == ATA_MODE_PIO32) blksize>>=2;
  8.2684 +  else blksize>>=1;
  8.2685 +
  8.2686 +  // sector will be 0 only on lba access. Convert to lba-chs
  8.2687 +  if (sector == 0) {
  8.2688 +    sector = (Bit16u) (lba & 0x000000ffL);
  8.2689 +    lba >>= 8;
  8.2690 +    cylinder = (Bit16u) (lba & 0x0000ffffL);
  8.2691 +    lba >>= 16;
  8.2692 +    head = ((Bit16u) (lba & 0x0000000fL)) | 0x40;
  8.2693 +    }
  8.2694 +
  8.2695 +  // Reset count of transferred data
  8.2696 +  write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
  8.2697 +  write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
  8.2698 +  current = 0;
  8.2699 +
  8.2700 +  status = inb(iobase1 + ATA_CB_STAT);
  8.2701 +  if (status & ATA_CB_STAT_BSY) return 1;
  8.2702 +
  8.2703 +  outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
  8.2704 +  outb(iobase1 + ATA_CB_FR, 0x00);
  8.2705 +  outb(iobase1 + ATA_CB_SC, count);
  8.2706 +  outb(iobase1 + ATA_CB_SN, sector);
  8.2707 +  outb(iobase1 + ATA_CB_CL, cylinder & 0x00ff);
  8.2708 +  outb(iobase1 + ATA_CB_CH, cylinder >> 8);
  8.2709 +  outb(iobase1 + ATA_CB_DH, (slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0) | (Bit8u) head );
  8.2710 +  outb(iobase1 + ATA_CB_CMD, command);
  8.2711 +
  8.2712 +  while (1) {
  8.2713 +    status = inb(iobase1 + ATA_CB_STAT);
  8.2714 +    if ( !(status & ATA_CB_STAT_BSY) ) break;
  8.2715 +    }
  8.2716 +
  8.2717 +  if (status & ATA_CB_STAT_ERR) {
  8.2718 +    BX_DEBUG_ATA("ata_cmd_data_out : read error\n");
  8.2719 +    return 2;
  8.2720 +    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
  8.2721 +    BX_DEBUG_ATA("ata_cmd_data_out : DRQ not set (status %02x)\n", (unsigned) status);
  8.2722 +    return 3;
  8.2723 +    }
  8.2724 +
  8.2725 +  // FIXME : move seg/off translation here
  8.2726 +
  8.2727 +ASM_START
  8.2728 +        sti  ;; enable higher priority interrupts
  8.2729 +ASM_END
  8.2730 +
  8.2731 +  while (1) {
  8.2732 +
  8.2733 +ASM_START
  8.2734 +        push bp
  8.2735 +        mov  bp, sp
  8.2736 +        mov  si, _ata_cmd_data_out.offset + 2[bp]  
  8.2737 +        mov  ax, _ata_cmd_data_out.segment + 2[bp] 
  8.2738 +        mov  cx, _ata_cmd_data_out.blksize + 2[bp] 
  8.2739 +
  8.2740 +        ;; adjust if there will be an overrun. 2K max sector size
  8.2741 +        cmp   si, #0xf800 ;; 
  8.2742 +        jbe   ata_out_no_adjust
  8.2743 +
  8.2744 +ata_out_adjust:
  8.2745 +        sub   si, #0x0800 ;; sub 2 kbytes from offset
  8.2746 +        add   ax, #0x0080 ;; add 2 Kbytes to segment
  8.2747 +
  8.2748 +ata_out_no_adjust:
  8.2749 +        mov   es, ax      ;; segment in es
  8.2750 +
  8.2751 +        mov   dx, _ata_cmd_data_out.iobase1 + 2[bp] ;; ATA data write port
  8.2752 +
  8.2753 +        mov  ah, _ata_cmd_data_out.mode + 2[bp] 
  8.2754 +        cmp  ah, #ATA_MODE_PIO32
  8.2755 +        je   ata_out_32
  8.2756 +
  8.2757 +ata_out_16:
  8.2758 +        seg ES
  8.2759 +        rep
  8.2760 +          outsw ;; CX words transfered from port(DX) to ES:[SI]
  8.2761 +        jmp ata_out_done
  8.2762 +
  8.2763 +ata_out_32:
  8.2764 +        seg ES
  8.2765 +        rep
  8.2766 +          outsd ;; CX dwords transfered from port(DX) to ES:[SI]
  8.2767 +
  8.2768 +ata_out_done:
  8.2769 +        mov  _ata_cmd_data_out.offset + 2[bp], si
  8.2770 +        mov  _ata_cmd_data_out.segment + 2[bp], es
  8.2771 +        pop  bp
  8.2772 +ASM_END
  8.2773 +
  8.2774 +    current++;
  8.2775 +    write_word(ebda_seg, &EbdaData->ata.trsfsectors,current);
  8.2776 +    count--;
  8.2777 +    status = inb(iobase1 + ATA_CB_STAT);
  8.2778 +    if (count == 0) {
  8.2779 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
  8.2780 +          != ATA_CB_STAT_RDY ) {
  8.2781 +        BX_DEBUG_ATA("ata_cmd_data_out : no sectors left (status %02x)\n", (unsigned) status);
  8.2782 +        return 6;
  8.2783 +        }
  8.2784 +      break;
  8.2785 +      }
  8.2786 +    else {
  8.2787 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
  8.2788 +          != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
  8.2789 +        BX_DEBUG_ATA("ata_cmd_data_out : more sectors left (status %02x)\n", (unsigned) status);
  8.2790 +        return 7;
  8.2791 +      }
  8.2792 +      continue;
  8.2793 +    }
  8.2794 +  }
  8.2795 +  // Enable interrupts
  8.2796 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
  8.2797 +  return 0;
  8.2798 +}
  8.2799 +
  8.2800 +// ---------------------------------------------------------------------------
  8.2801 +// ATA/ATAPI driver : execute a packet command
  8.2802 +// ---------------------------------------------------------------------------
  8.2803 +      // returns
  8.2804 +      // 0 : no error
  8.2805 +      // 1 : error in parameters
  8.2806 +      // 2 : BUSY bit set
  8.2807 +      // 3 : error
  8.2808 +      // 4 : not ready
  8.2809 +Bit16u ata_cmd_packet(device, cmdlen, cmdseg, cmdoff, header, length, inout, bufseg, bufoff)
  8.2810 +Bit8u  cmdlen,inout;
  8.2811 +Bit16u device,cmdseg, cmdoff, bufseg, bufoff;
  8.2812 +Bit16u header;
  8.2813 +Bit32u length;
  8.2814 +{
  8.2815 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.2816 +  Bit16u iobase1, iobase2;
  8.2817 +  Bit16u lcount, lbefore, lafter, count;
  8.2818 +  Bit8u  channel, slave;
  8.2819 +  Bit8u  status, mode, lmode;
  8.2820 +  Bit32u total, transfer;
  8.2821 +
  8.2822 +  channel = device / 2;
  8.2823 +  slave = device % 2;
  8.2824 +
  8.2825 +  // Data out is not supported yet
  8.2826 +  if (inout == ATA_DATA_OUT) {
  8.2827 +    BX_INFO("ata_cmd_packet: DATA_OUT not supported yet\n");
  8.2828 +    return 1;
  8.2829 +    }
  8.2830 +
  8.2831 +  // The header length must be even
  8.2832 +  if (header & 1) {
  8.2833 +    BX_DEBUG_ATA("ata_cmd_packet : header must be even (%04x)\n",header);
  8.2834 +    return 1;
  8.2835 +    }
  8.2836 +
  8.2837 +  iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
  8.2838 +  iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
  8.2839 +  mode    = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
  8.2840 +  transfer= 0L;
  8.2841 +
  8.2842 +  if (cmdlen < 12) cmdlen=12;
  8.2843 +  if (cmdlen > 12) cmdlen=16;
  8.2844 +  cmdlen>>=1;
  8.2845 +
  8.2846 +  // Reset count of transferred data
  8.2847 +  write_word(ebda_seg, &EbdaData->ata.trsfsectors,0);
  8.2848 +  write_dword(ebda_seg, &EbdaData->ata.trsfbytes,0L);
  8.2849 +
  8.2850 +  status = inb(iobase1 + ATA_CB_STAT);
  8.2851 +  if (status & ATA_CB_STAT_BSY) return 2;
  8.2852 +
  8.2853 +  outb(iobase2 + ATA_CB_DC, ATA_CB_DC_HD15 | ATA_CB_DC_NIEN);
  8.2854 +  // outb(iobase1 + ATA_CB_FR, 0x00);
  8.2855 +  // outb(iobase1 + ATA_CB_SC, 0x00);
  8.2856 +  // outb(iobase1 + ATA_CB_SN, 0x00);
  8.2857 +  outb(iobase1 + ATA_CB_CL, 0xfff0 & 0x00ff);
  8.2858 +  outb(iobase1 + ATA_CB_CH, 0xfff0 >> 8);
  8.2859 +  outb(iobase1 + ATA_CB_DH, slave ? ATA_CB_DH_DEV1 : ATA_CB_DH_DEV0);
  8.2860 +  outb(iobase1 + ATA_CB_CMD, ATA_CMD_PACKET);
  8.2861 +
  8.2862 +  // Device should ok to receive command
  8.2863 +  while (1) {
  8.2864 +    status = inb(iobase1 + ATA_CB_STAT);
  8.2865 +    if ( !(status & ATA_CB_STAT_BSY) ) break;
  8.2866 +    }
  8.2867 +
  8.2868 +  if (status & ATA_CB_STAT_ERR) {
  8.2869 +    BX_DEBUG_ATA("ata_cmd_packet : error, status is %02x\n",status);
  8.2870 +    return 3;
  8.2871 +    } else if ( !(status & ATA_CB_STAT_DRQ) ) {
  8.2872 +    BX_DEBUG_ATA("ata_cmd_packet : DRQ not set (status %02x)\n", (unsigned) status);
  8.2873 +    return 4;
  8.2874 +    }
  8.2875 +
  8.2876 +  // Normalize address
  8.2877 +  cmdseg += (cmdoff / 16);
  8.2878 +  cmdoff %= 16;
  8.2879 +
  8.2880 +  // Send command to device
  8.2881 +ASM_START
  8.2882 +      sti  ;; enable higher priority interrupts
  8.2883 + 
  8.2884 +      push bp
  8.2885 +      mov  bp, sp
  8.2886 +    
  8.2887 +      mov  si, _ata_cmd_packet.cmdoff + 2[bp]  
  8.2888 +      mov  ax, _ata_cmd_packet.cmdseg + 2[bp] 
  8.2889 +      mov  cx, _ata_cmd_packet.cmdlen + 2[bp] 
  8.2890 +      mov  es, ax      ;; segment in es
  8.2891 +
  8.2892 +      mov  dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data write port
  8.2893 +
  8.2894 +      seg ES
  8.2895 +      rep
  8.2896 +        outsw ;; CX words transfered from port(DX) to ES:[SI]
  8.2897 +
  8.2898 +      pop  bp
  8.2899 +ASM_END
  8.2900 +
  8.2901 +  if (inout == ATA_DATA_NO) {
  8.2902 +    status = inb(iobase1 + ATA_CB_STAT);
  8.2903 +    }
  8.2904 +  else {
  8.2905 +  while (1) {
  8.2906 +
  8.2907 +      status = inb(iobase1 + ATA_CB_STAT);
  8.2908 +
  8.2909 +      // Check if command completed
  8.2910 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_DRQ) ) ==0 ) break;
  8.2911 +
  8.2912 +      if (status & ATA_CB_STAT_ERR) {
  8.2913 +        BX_DEBUG_ATA("ata_cmd_packet : error (status %02x)\n",status);
  8.2914 +        return 3;
  8.2915 +      }
  8.2916 +
  8.2917 +      // Device must be ready to send data
  8.2918 +      if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
  8.2919 +            != (ATA_CB_STAT_RDY | ATA_CB_STAT_DRQ) ) {
  8.2920 +        BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", status);
  8.2921 +        return 4;
  8.2922 +        }
  8.2923 +
  8.2924 +      // Normalize address
  8.2925 +      bufseg += (bufoff / 16);
  8.2926 +      bufoff %= 16;
  8.2927 +    
  8.2928 +      // Get the byte count
  8.2929 +      lcount =  ((Bit16u)(inb(iobase1 + ATA_CB_CH))<<8)+inb(iobase1 + ATA_CB_CL);
  8.2930 +
  8.2931 +      // adjust to read what we want
  8.2932 +      if(header>lcount) {
  8.2933 +         lbefore=lcount;
  8.2934 +         header-=lcount;
  8.2935 +         lcount=0;
  8.2936 +         }
  8.2937 +      else {
  8.2938 +        lbefore=header;
  8.2939 +        header=0;
  8.2940 +        lcount-=lbefore;
  8.2941 +        }
  8.2942 +
  8.2943 +      if(lcount>length) {
  8.2944 +        lafter=lcount-length;
  8.2945 +        lcount=length;
  8.2946 +        length=0;
  8.2947 +        }
  8.2948 +      else {
  8.2949 +        lafter=0;
  8.2950 +        length-=lcount;
  8.2951 +        }
  8.2952 +
  8.2953 +      // Save byte count
  8.2954 +      count = lcount;
  8.2955 +
  8.2956 +      BX_DEBUG_ATA("Trying to read %04x bytes (%04x %04x %04x) ",lbefore+lcount+lafter,lbefore,lcount,lafter);
  8.2957 +      BX_DEBUG_ATA("to 0x%04x:0x%04x\n",bufseg,bufoff);
  8.2958 +
  8.2959 +      // If counts not dividable by 4, use 16bits mode
  8.2960 +      lmode = mode;
  8.2961 +      if (lbefore & 0x03) lmode=ATA_MODE_PIO16;
  8.2962 +      if (lcount  & 0x03) lmode=ATA_MODE_PIO16;
  8.2963 +      if (lafter  & 0x03) lmode=ATA_MODE_PIO16;
  8.2964 +
  8.2965 +      // adds an extra byte if count are odd. before is always even
  8.2966 +      if (lcount & 0x01) {
  8.2967 +        lcount+=1;
  8.2968 +        if ((lafter > 0) && (lafter & 0x01)) {
  8.2969 +          lafter-=1;
  8.2970 +          }
  8.2971 +        }
  8.2972 +
  8.2973 +      if (lmode == ATA_MODE_PIO32) {
  8.2974 +        lcount>>=2; lbefore>>=2; lafter>>=2;
  8.2975 +        }
  8.2976 +      else {
  8.2977 +        lcount>>=1; lbefore>>=1; lafter>>=1;
  8.2978 +        }
  8.2979 +
  8.2980 +       ;  // FIXME bcc bug
  8.2981 +
  8.2982 +ASM_START
  8.2983 +        push bp
  8.2984 +        mov  bp, sp
  8.2985 +
  8.2986 +        mov  dx, _ata_cmd_packet.iobase1 + 2[bp] ;; ATA data read port
  8.2987 +
  8.2988 +        mov  cx, _ata_cmd_packet.lbefore + 2[bp] 
  8.2989 +        jcxz ata_packet_no_before
  8.2990 +
  8.2991 +        mov  ah, _ata_cmd_packet.lmode + 2[bp] 
  8.2992 +        cmp  ah, #ATA_MODE_PIO32
  8.2993 +        je   ata_packet_in_before_32
  8.2994 +
  8.2995 +ata_packet_in_before_16:
  8.2996 +        in   ax, dx
  8.2997 +        loop ata_packet_in_before_16
  8.2998 +        jmp  ata_packet_no_before
  8.2999 +
  8.3000 +ata_packet_in_before_32:
  8.3001 +        push eax
  8.3002 +ata_packet_in_before_32_loop:
  8.3003 +        in   eax, dx
  8.3004 +        loop ata_packet_in_before_32_loop
  8.3005 +        pop  eax
  8.3006 +
  8.3007 +ata_packet_no_before:
  8.3008 +        mov  cx, _ata_cmd_packet.lcount + 2[bp] 
  8.3009 +        jcxz ata_packet_after
  8.3010 +
  8.3011 +        mov  di, _ata_cmd_packet.bufoff + 2[bp]  
  8.3012 +        mov  ax, _ata_cmd_packet.bufseg + 2[bp] 
  8.3013 +        mov  es, ax
  8.3014 +
  8.3015 +        mov  ah, _ata_cmd_packet.lmode + 2[bp] 
  8.3016 +        cmp  ah, #ATA_MODE_PIO32
  8.3017 +        je   ata_packet_in_32
  8.3018 +
  8.3019 +ata_packet_in_16:
  8.3020 +        rep
  8.3021 +          insw ;; CX words transfered tp port(DX) to ES:[DI]
  8.3022 +        jmp ata_packet_after
  8.3023 +
  8.3024 +ata_packet_in_32:
  8.3025 +        rep
  8.3026 +          insd ;; CX dwords transfered to port(DX) to ES:[DI]
  8.3027 +
  8.3028 +ata_packet_after:
  8.3029 +        mov  cx, _ata_cmd_packet.lafter + 2[bp] 
  8.3030 +        jcxz ata_packet_done
  8.3031 +
  8.3032 +        mov  ah, _ata_cmd_packet.lmode + 2[bp] 
  8.3033 +        cmp  ah, #ATA_MODE_PIO32
  8.3034 +        je   ata_packet_in_after_32
  8.3035 +
  8.3036 +ata_packet_in_after_16:
  8.3037 +        in   ax, dx
  8.3038 +        loop ata_packet_in_after_16
  8.3039 +        jmp  ata_packet_done
  8.3040 +
  8.3041 +ata_packet_in_after_32:
  8.3042 +        push eax
  8.3043 +ata_packet_in_after_32_loop:
  8.3044 +        in   eax, dx
  8.3045 +        loop ata_packet_in_after_32_loop
  8.3046 +        pop  eax
  8.3047 +
  8.3048 +ata_packet_done:
  8.3049 +        pop  bp
  8.3050 +ASM_END
  8.3051 +
  8.3052 +      // Compute new buffer address
  8.3053 +      bufoff += count;
  8.3054 +
  8.3055 +      // Save transferred bytes count
  8.3056 +      transfer += count;
  8.3057 +      write_dword(ebda_seg, &EbdaData->ata.trsfbytes,transfer);
  8.3058 +      }
  8.3059 +    }
  8.3060 +
  8.3061 +  // Final check, device must be ready
  8.3062 +  if ( (status & (ATA_CB_STAT_BSY | ATA_CB_STAT_RDY | ATA_CB_STAT_DF | ATA_CB_STAT_DRQ | ATA_CB_STAT_ERR) ) 
  8.3063 +         != ATA_CB_STAT_RDY ) {
  8.3064 +    BX_DEBUG_ATA("ata_cmd_packet : not ready (status %02x)\n", (unsigned) status);
  8.3065 +    return 4;
  8.3066 +    }
  8.3067 +
  8.3068 +  // Enable interrupts
  8.3069 +  outb(iobase2+ATA_CB_DC, ATA_CB_DC_HD15);
  8.3070 +  return 0;
  8.3071 +}
  8.3072 +
  8.3073 +// ---------------------------------------------------------------------------
  8.3074 +// End of ATA/ATAPI Driver
  8.3075 +// ---------------------------------------------------------------------------
  8.3076 +
  8.3077 +// ---------------------------------------------------------------------------
  8.3078 +// Start of ATA/ATAPI generic functions
  8.3079 +// ---------------------------------------------------------------------------
  8.3080 +
  8.3081 +  Bit16u 
  8.3082 +atapi_get_sense(device)
  8.3083 +  Bit16u device;
  8.3084 +{
  8.3085 +  Bit8u  atacmd[12];
  8.3086 +  Bit8u  buffer[16];
  8.3087 +  Bit8u i;
  8.3088 +
  8.3089 +  memsetb(get_SS(),atacmd,0,12);
  8.3090 +
  8.3091 +  // Request SENSE 
  8.3092 +  atacmd[0]=0x03;    
  8.3093 +  atacmd[4]=0x20;    
  8.3094 +  if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 16L, ATA_DATA_IN, get_SS(), buffer) != 0)
  8.3095 +    return 0x0002;
  8.3096 +
  8.3097 +  if ((buffer[0] & 0x7e) == 0x70) {
  8.3098 +    return (((Bit16u)buffer[2]&0x0f)*0x100)+buffer[12];
  8.3099 +    }
  8.3100 +
  8.3101 +  return 0;
  8.3102 +}
  8.3103 +
  8.3104 +  Bit16u 
  8.3105 +atapi_is_ready(device)
  8.3106 +  Bit16u device;
  8.3107 +{
  8.3108 +  Bit8u  atacmd[12];
  8.3109 +  Bit8u  buffer[];
  8.3110 +
  8.3111 +  memsetb(get_SS(),atacmd,0,12);
  8.3112 + 
  8.3113 +  // Test Unit Ready
  8.3114 +  if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
  8.3115 +    return 0x000f;
  8.3116 +
  8.3117 +  if (atapi_get_sense(device) !=0 ) {
  8.3118 +    memsetb(get_SS(),atacmd,0,12);
  8.3119 +
  8.3120 +    // try to send Test Unit Ready again
  8.3121 +    if (ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 0L, ATA_DATA_NO, get_SS(), buffer) != 0)
  8.3122 +      return 0x000f;
  8.3123 +
  8.3124 +    return atapi_get_sense(device);
  8.3125 +    }
  8.3126 +  return 0;
  8.3127 +}
  8.3128 +
  8.3129 +  Bit16u 
  8.3130 +atapi_is_cdrom(device)
  8.3131 +  Bit8u device;
  8.3132 +{
  8.3133 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.3134 +
  8.3135 +  if (device >= BX_MAX_ATA_DEVICES)
  8.3136 +    return 0;
  8.3137 +
  8.3138 +  if (read_byte(ebda_seg,&EbdaData->ata.devices[device].type) != ATA_TYPE_ATAPI)
  8.3139 +    return 0;
  8.3140 +
  8.3141 +  if (read_byte(ebda_seg,&EbdaData->ata.devices[device].device) != ATA_DEVICE_CDROM)
  8.3142 +    return 0;
  8.3143 +
  8.3144 +  return 1;
  8.3145 +}
  8.3146 +
  8.3147 +// ---------------------------------------------------------------------------
  8.3148 +// End of ATA/ATAPI generic functions
  8.3149 +// ---------------------------------------------------------------------------
  8.3150 +
  8.3151 +#endif // BX_USE_ATADRV
  8.3152 +
  8.3153 +#if BX_ELTORITO_BOOT
  8.3154 +
  8.3155 +// ---------------------------------------------------------------------------
  8.3156 +// Start of El-Torito boot functions
  8.3157 +// ---------------------------------------------------------------------------
  8.3158 +
  8.3159 +  void
  8.3160 +cdemu_init()
  8.3161 +{
  8.3162 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.3163 +
  8.3164 +  // the only important data is this one for now
  8.3165 +  write_byte(ebda_seg,&EbdaData->cdemu.active,0x00);
  8.3166 +}
  8.3167 +
  8.3168 +  Bit8u
  8.3169 +cdemu_isactive()
  8.3170 +{
  8.3171 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.3172 +
  8.3173 +  return(read_byte(ebda_seg,&EbdaData->cdemu.active));
  8.3174 +}
  8.3175 +
  8.3176 +  Bit8u
  8.3177 +cdemu_emulated_drive()
  8.3178 +{
  8.3179 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.3180 +
  8.3181 +  return(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
  8.3182 +}
  8.3183 +
  8.3184 +static char isotag[6]="CD001";
  8.3185 +static char eltorito[24]="EL TORITO SPECIFICATION";
  8.3186 +//
  8.3187 +// Returns ah: emulated drive, al: error code
  8.3188 +//
  8.3189 +  Bit16u 
  8.3190 +cdrom_boot()
  8.3191 +{
  8.3192 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.3193 +  Bit8u  atacmd[12], buffer[2048];
  8.3194 +  Bit32u lba;
  8.3195 +  Bit16u boot_segment, nbsectors, i, error;
  8.3196 +  Bit8u  device;
  8.3197 +
  8.3198 +  // Find out the first cdrom
  8.3199 +  for (device=0; device<BX_MAX_ATA_DEVICES;device++) {
  8.3200 +    if (atapi_is_cdrom(device)) break;
  8.3201 +    }
  8.3202 +  
  8.3203 +  // if not found
  8.3204 +  if(device >= BX_MAX_ATA_DEVICES) return 2;
  8.3205 +
  8.3206 +  // Read the Boot Record Volume Descriptor
  8.3207 +  memsetb(get_SS(),atacmd,0,12);
  8.3208 +  atacmd[0]=0x28;                      // READ command
  8.3209 +  atacmd[7]=(0x01 & 0xff00) >> 8;      // Sectors
  8.3210 +  atacmd[8]=(0x01 & 0x00ff);           // Sectors
  8.3211 +  atacmd[2]=(0x11 & 0xff000000) >> 24; // LBA
  8.3212 +  atacmd[3]=(0x11 & 0x00ff0000) >> 16;
  8.3213 +  atacmd[4]=(0x11 & 0x0000ff00) >> 8;
  8.3214 +  atacmd[5]=(0x11 & 0x000000ff);
  8.3215 +  if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
  8.3216 +    return 3;
  8.3217 +
  8.3218 +  // Validity checks
  8.3219 +  if(buffer[0]!=0)return 4;
  8.3220 +  for(i=0;i<5;i++){
  8.3221 +    if(buffer[1+i]!=read_byte(0xf000,&isotag[i]))return 5;
  8.3222 +   }
  8.3223 +  for(i=0;i<23;i++)
  8.3224 +    if(buffer[7+i]!=read_byte(0xf000,&eltorito[i]))return 6;
  8.3225 +  
  8.3226 +  // ok, now we calculate the Boot catalog address
  8.3227 +  lba=buffer[0x4A]*0x1000000+buffer[0x49]*0x10000+buffer[0x48]*0x100+buffer[0x47];
  8.3228 +
  8.3229 +  // And we read the Boot Catalog
  8.3230 +  memsetb(get_SS(),atacmd,0,12);
  8.3231 +  atacmd[0]=0x28;                      // READ command
  8.3232 +  atacmd[7]=(0x01 & 0xff00) >> 8;      // Sectors
  8.3233 +  atacmd[8]=(0x01 & 0x00ff);           // Sectors
  8.3234 +  atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
  8.3235 +  atacmd[3]=(lba & 0x00ff0000) >> 16;
  8.3236 +  atacmd[4]=(lba & 0x0000ff00) >> 8;
  8.3237 +  atacmd[5]=(lba & 0x000000ff);
  8.3238 +  if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, 2048L, ATA_DATA_IN, get_SS(), buffer)) != 0)
  8.3239 +    return 7;
  8.3240 + 
  8.3241 +  // Validation entry
  8.3242 +  if(buffer[0x00]!=0x01)return 8;   // Header
  8.3243 +  if(buffer[0x01]!=0x00)return 9;   // Platform
  8.3244 +  if(buffer[0x1E]!=0x55)return 10;  // key 1
  8.3245 +  if(buffer[0x1F]!=0xAA)return 10;  // key 2
  8.3246 +
  8.3247 +  // Initial/Default Entry
  8.3248 +  if(buffer[0x20]!=0x88)return 11; // Bootable
  8.3249 +
  8.3250 +  write_byte(ebda_seg,&EbdaData->cdemu.media,buffer[0x21]);
  8.3251 +  if(buffer[0x21]==0){
  8.3252 +    // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0. 
  8.3253 +    // Win2000 cd boot needs to know it booted from cd
  8.3254 +    write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0xE0);
  8.3255 +    } 
  8.3256 +  else if(buffer[0x21]<4)
  8.3257 +    write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x00);
  8.3258 +  else
  8.3259 +    write_byte(ebda_seg,&EbdaData->cdemu.emulated_drive,0x80);
  8.3260 +
  8.3261 +  write_byte(ebda_seg,&EbdaData->cdemu.controller_index,device/2);
  8.3262 +  write_byte(ebda_seg,&EbdaData->cdemu.device_spec,device%2);
  8.3263 +
  8.3264 +  boot_segment=buffer[0x23]*0x100+buffer[0x22];
  8.3265 +  if(boot_segment==0x0000)boot_segment=0x07C0;
  8.3266 +
  8.3267 +  write_word(ebda_seg,&EbdaData->cdemu.load_segment,boot_segment);
  8.3268 +  write_word(ebda_seg,&EbdaData->cdemu.buffer_segment,0x0000);
  8.3269 +  
  8.3270 +  nbsectors=buffer[0x27]*0x100+buffer[0x26];
  8.3271 +  write_word(ebda_seg,&EbdaData->cdemu.sector_count,nbsectors);
  8.3272 +
  8.3273 +  lba=buffer[0x2B]*0x1000000+buffer[0x2A]*0x10000+buffer[0x29]*0x100+buffer[0x28];
  8.3274 +  write_dword(ebda_seg,&EbdaData->cdemu.ilba,lba);
  8.3275 +
  8.3276 +  // And we read the image in memory
  8.3277 +  memsetb(get_SS(),atacmd,0,12);
  8.3278 +  atacmd[0]=0x28;                      // READ command
  8.3279 +  atacmd[7]=((1+(nbsectors-1)/4) & 0xff00) >> 8;      // Sectors
  8.3280 +  atacmd[8]=((1+(nbsectors-1)/4) & 0x00ff);           // Sectors
  8.3281 +  atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
  8.3282 +  atacmd[3]=(lba & 0x00ff0000) >> 16;
  8.3283 +  atacmd[4]=(lba & 0x0000ff00) >> 8;
  8.3284 +  atacmd[5]=(lba & 0x000000ff);
  8.3285 +  if((error = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, nbsectors*512L, ATA_DATA_IN, boot_segment,0)) != 0)
  8.3286 +    return 12;
  8.3287 +
  8.3288 +  // Remember the media type
  8.3289 +  switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
  8.3290 +    case 0x01:  // 1.2M floppy
  8.3291 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,15);
  8.3292 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
  8.3293 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
  8.3294 +      break;
  8.3295 +    case 0x02:  // 1.44M floppy
  8.3296 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,18);
  8.3297 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
  8.3298 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
  8.3299 +      break;
  8.3300 +    case 0x03:  // 2.88M floppy
  8.3301 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,36);
  8.3302 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,80);
  8.3303 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,2);
  8.3304 +      break;
  8.3305 +    case 0x04:  // Harddrive
  8.3306 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.spt,read_byte(boot_segment,446+6)&0x3f);
  8.3307 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders,
  8.3308 +	      (read_byte(boot_segment,446+6)<<2) + read_byte(boot_segment,446+7) + 1);
  8.3309 +      write_word(ebda_seg,&EbdaData->cdemu.vdevice.heads,read_byte(boot_segment,446+5) + 1);
  8.3310 +      break;
  8.3311 +   }
  8.3312 +
  8.3313 +  if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0) {
  8.3314 +    // Increase bios installed hardware number of devices
  8.3315 +    if(read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)==0x00)
  8.3316 +      write_byte(0x40,0x10,read_byte(0x40,0x10)|0x41);
  8.3317 +    else
  8.3318 +      write_byte(ebda_seg, &EbdaData->ata.hdcount, read_byte(ebda_seg, &EbdaData->ata.hdcount) + 1);
  8.3319 +   }
  8.3320 +
  8.3321 +  
  8.3322 +  // everything is ok, so from now on, the emulation is active
  8.3323 +  if(read_byte(ebda_seg,&EbdaData->cdemu.media)!=0)
  8.3324 +    write_byte(ebda_seg,&EbdaData->cdemu.active,0x01);
  8.3325 +
  8.3326 +  // return the boot drive + no error
  8.3327 +  return (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive)*0x100)+0;
  8.3328 +}
  8.3329 +
  8.3330 +// ---------------------------------------------------------------------------
  8.3331 +// End of El-Torito boot functions
  8.3332 +// ---------------------------------------------------------------------------
  8.3333 +#endif // BX_ELTORITO_BOOT
  8.3334 +
  8.3335 +  void
  8.3336 +int14_function(regs, ds, iret_addr)
  8.3337 +  pusha_regs_t regs; // regs pushed from PUSHA instruction
  8.3338 +  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
  8.3339 +  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
  8.3340 +{
  8.3341 +  Bit16u addr,timer,val16;
  8.3342 +  Bit8u timeout;
  8.3343 +
  8.3344 +  ASM_START
  8.3345 +  sti
  8.3346 +  ASM_END
  8.3347 +
  8.3348 +  addr = read_word(0x0040, (regs.u.r16.dx << 1));
  8.3349 +  timeout = read_byte(0x0040, 0x007C + regs.u.r16.dx);
  8.3350 +  if ((regs.u.r16.dx < 4) && (addr > 0)) {
  8.3351 +    switch (regs.u.r8.ah) {
  8.3352 +      case 0:
  8.3353 +        outb(addr+3, inb(addr+3) | 0x80);
  8.3354 +        if (regs.u.r8.al & 0xE0 == 0) {
  8.3355 +          outb(addr, 0x17);
  8.3356 +          outb(addr+1, 0x04);
  8.3357 +        } else {
  8.3358 +          val16 = 0x600 >> ((regs.u.r8.al & 0xE0) >> 5);
  8.3359 +          outb(addr, val16 & 0xFF);
  8.3360 +          outb(addr+1, val16 >> 8);
  8.3361 +        }
  8.3362 +        outb(addr+3, regs.u.r8.al & 0x1F);
  8.3363 +        regs.u.r8.ah = inb(addr+5);
  8.3364 +        regs.u.r8.al = inb(addr+6);
  8.3365 +        ClearCF(iret_addr.flags);
  8.3366 +        break;
  8.3367 +      case 1:
  8.3368 +        timer = read_word(0x0040, 0x006C);
  8.3369 +        while (((inb(addr+5) & 0x60) != 0x60) && (timeout)) {
  8.3370 +          val16 = read_word(0x0040, 0x006C);
  8.3371 +          if (val16 != timer) {
  8.3372 +            timer = val16;
  8.3373 +            timeout--;
  8.3374 +            }
  8.3375 +          }
  8.3376 +        if (timeout) outb(addr, regs.u.r8.al);
  8.3377 +        regs.u.r8.ah = inb(addr+5);
  8.3378 +        if (!timeout) regs.u.r8.ah |= 0x80;
  8.3379 +        ClearCF(iret_addr.flags);
  8.3380 +        break;
  8.3381 +      case 2:
  8.3382 +        timer = read_word(0x0040, 0x006C);
  8.3383 +        while (((inb(addr+5) & 0x01) == 0) && (timeout)) {
  8.3384 +          val16 = read_word(0x0040, 0x006C);
  8.3385 +          if (val16 != timer) {
  8.3386 +            timer = val16;
  8.3387 +            timeout--;
  8.3388 +            }
  8.3389 +          }
  8.3390 +        if (timeout) {
  8.3391 +          regs.u.r8.ah = 0;
  8.3392 +          regs.u.r8.al = inb(addr);
  8.3393 +        } else {
  8.3394 +          regs.u.r8.ah = inb(addr+5);
  8.3395 +          }
  8.3396 +        ClearCF(iret_addr.flags);
  8.3397 +        break;
  8.3398 +      case 3:
  8.3399 +        regs.u.r8.ah = inb(addr+5);
  8.3400 +        regs.u.r8.al = inb(addr+6);
  8.3401 +        ClearCF(iret_addr.flags);
  8.3402 +        break;
  8.3403 +      default:
  8.3404 +        SetCF(iret_addr.flags); // Unsupported
  8.3405 +      }
  8.3406 +  } else {
  8.3407 +    SetCF(iret_addr.flags); // Unsupported
  8.3408 +    }
  8.3409 +}
  8.3410 +
  8.3411 +  void
  8.3412 +int15_function(regs, ES, DS, FLAGS)
  8.3413 +  pusha_regs_t regs; // REGS pushed via pusha
  8.3414 +  Bit16u ES, DS, FLAGS;
  8.3415 +{
  8.3416 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.3417 +  bx_bool prev_a20_enable;
  8.3418 +  Bit16u  base15_00;
  8.3419 +  Bit8u   base23_16;
  8.3420 +  Bit16u  ss;
  8.3421 +  Bit16u  CX,DX;
  8.3422 +
  8.3423 +  Bit16u bRegister;
  8.3424 +  Bit8u irqDisable;
  8.3425 +
  8.3426 +BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
  8.3427 +
  8.3428 +  switch (regs.u.r8.ah) {
  8.3429 +    case 0x24: /* A20 Control */
  8.3430 +      switch (regs.u.r8.al) {
  8.3431 +        case 0x00:
  8.3432 +          set_enable_a20(0);
  8.3433 +          CLEAR_CF();
  8.3434 +          regs.u.r8.ah = 0;
  8.3435 +          break;
  8.3436 +        case 0x01:
  8.3437 +          set_enable_a20(1);
  8.3438 +          CLEAR_CF();
  8.3439 +          regs.u.r8.ah = 0;
  8.3440 +          break;
  8.3441 +        case 0x02:
  8.3442 +          regs.u.r8.al = (inb(0x92) >> 1) & 0x01;
  8.3443 +          CLEAR_CF();
  8.3444 +          regs.u.r8.ah = 0;
  8.3445 +          break;
  8.3446 +        case 0x03:
  8.3447 +          CLEAR_CF();
  8.3448 +          regs.u.r8.ah = 0;
  8.3449 +          regs.u.r16.bx = 3;
  8.3450 +          break;
  8.3451 +        default:
  8.3452 +          BX_INFO("int15: Func 24h, subfunc %02xh, A20 gate control not supported\n", (unsigned) regs.u.r8.al);
  8.3453 +          SET_CF();
  8.3454 +          regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3455 +      }
  8.3456 +      break;
  8.3457 +
  8.3458 +    case 0x41:
  8.3459 +      SET_CF();
  8.3460 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3461 +      break;
  8.3462 +
  8.3463 +    case 0x4f:
  8.3464 +      /* keyboard intercept */
  8.3465 +#if BX_CPU < 2
  8.3466 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3467 +#else
  8.3468 +      // nop
  8.3469 +#endif
  8.3470 +      SET_CF();
  8.3471 +      break;
  8.3472 +
  8.3473 +    case 0x52:    // removable media eject
  8.3474 +      CLEAR_CF();
  8.3475 +      regs.u.r8.ah = 0;  // "ok ejection may proceed"
  8.3476 +      break;
  8.3477 +
  8.3478 +    case 0x83: {
  8.3479 +      if( regs.u.r8.al == 0 ) {
  8.3480 +        // Set Interval requested.
  8.3481 +        if( ( read_byte( 0x40, 0xA0 ) & 1 ) == 0 ) {
  8.3482 +          // Interval not already set.
  8.3483 +          write_byte( 0x40, 0xA0, 1 );  // Set status byte.
  8.3484 +          write_word( 0x40, 0x98, ES ); // Byte location, segment
  8.3485 +          write_word( 0x40, 0x9A, regs.u.r16.bx ); // Byte location, offset
  8.3486 +          write_word( 0x40, 0x9C, regs.u.r16.dx ); // Low word, delay
  8.3487 +          write_word( 0x40, 0x9E, regs.u.r16.cx ); // High word, delay.
  8.3488 +          CLEAR_CF( );
  8.3489 +          irqDisable = inb( 0xA1 );
  8.3490 +          outb( 0xA1, irqDisable & 0xFE );
  8.3491 +          bRegister = inb_cmos( 0xB );  // Unmask IRQ8 so INT70 will get through.
  8.3492 +          outb_cmos( 0xB, bRegister | 0x40 ); // Turn on the Periodic Interrupt timer
  8.3493 +        } else {
  8.3494 +          // Interval already set.
  8.3495 +          BX_DEBUG_INT15("int15: Func 83h, failed, already waiting.\n" );
  8.3496 +          SET_CF();
  8.3497 +          regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3498 +        }
  8.3499 +      } else if( regs.u.r8.al == 1 ) {
  8.3500 +        // Clear Interval requested
  8.3501 +        write_byte( 0x40, 0xA0, 0 );  // Clear status byte
  8.3502 +        CLEAR_CF( );
  8.3503 +        bRegister = inb_cmos( 0xB );
  8.3504 +        outb_cmos( 0xB, bRegister & ~0x40 );  // Turn off the Periodic Interrupt timer
  8.3505 +      } else {
  8.3506 +        BX_DEBUG_INT15("int15: Func 83h, failed.\n" );
  8.3507 +        SET_CF();
  8.3508 +        regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3509 +        regs.u.r8.al--;
  8.3510 +      }
  8.3511 +
  8.3512 +      break;
  8.3513 +    }
  8.3514 +
  8.3515 +    case 0x87:
  8.3516 +#if BX_CPU < 3
  8.3517 +#  error "Int15 function 87h not supported on < 80386"
  8.3518 +#endif
  8.3519 +      // +++ should probably have descriptor checks
  8.3520 +      // +++ should have exception handlers
  8.3521 +
  8.3522 + // turn off interrupts
  8.3523 +ASM_START
  8.3524 +  cli
  8.3525 +ASM_END
  8.3526 +
  8.3527 +      prev_a20_enable = set_enable_a20(1); // enable A20 line
  8.3528 +
  8.3529 +      // 128K max of transfer on 386+ ???
  8.3530 +      // source == destination ???
  8.3531 +
  8.3532 +      // ES:SI points to descriptor table
  8.3533 +      // offset   use     initially  comments
  8.3534 +      // ==============================================
  8.3535 +      // 00..07   Unused  zeros      Null descriptor
  8.3536 +      // 08..0f   GDT     zeros      filled in by BIOS
  8.3537 +      // 10..17   source  ssssssss   source of data
  8.3538 +      // 18..1f   dest    dddddddd   destination of data
  8.3539 +      // 20..27   CS      zeros      filled in by BIOS
  8.3540 +      // 28..2f   SS      zeros      filled in by BIOS
  8.3541 +
  8.3542 +      //es:si
  8.3543 +      //eeee0
  8.3544 +      //0ssss
  8.3545 +      //-----
  8.3546 +
  8.3547 +// check for access rights of source & dest here
  8.3548 +
  8.3549 +      // Initialize GDT descriptor
  8.3550 +      base15_00 = (ES << 4) + regs.u.r16.si;
  8.3551 +      base23_16 = ES >> 12;
  8.3552 +      if (base15_00 < (ES<<4))
  8.3553 +        base23_16++;
  8.3554 +      write_word(ES, regs.u.r16.si+0x08+0, 47);       // limit 15:00 = 6 * 8bytes/descriptor
  8.3555 +      write_word(ES, regs.u.r16.si+0x08+2, base15_00);// base 15:00
  8.3556 +      write_byte(ES, regs.u.r16.si+0x08+4, base23_16);// base 23:16
  8.3557 +      write_byte(ES, regs.u.r16.si+0x08+5, 0x93);     // access
  8.3558 +      write_word(ES, regs.u.r16.si+0x08+6, 0x0000);   // base 31:24/reserved/limit 19:16
  8.3559 +
  8.3560 +      // Initialize CS descriptor
  8.3561 +      write_word(ES, regs.u.r16.si+0x20+0, 0xffff);// limit 15:00 = normal 64K limit
  8.3562 +      write_word(ES, regs.u.r16.si+0x20+2, 0x0000);// base 15:00
  8.3563 +      write_byte(ES, regs.u.r16.si+0x20+4, 0x000f);// base 23:16
  8.3564 +      write_byte(ES, regs.u.r16.si+0x20+5, 0x9b);  // access
  8.3565 +      write_word(ES, regs.u.r16.si+0x20+6, 0x0000);// base 31:24/reserved/limit 19:16
  8.3566 +
  8.3567 +      // Initialize SS descriptor
  8.3568 +      ss = get_SS();
  8.3569 +      base15_00 = ss << 4;
  8.3570 +      base23_16 = ss >> 12;
  8.3571 +      write_word(ES, regs.u.r16.si+0x28+0, 0xffff);   // limit 15:00 = normal 64K limit
  8.3572 +      write_word(ES, regs.u.r16.si+0x28+2, base15_00);// base 15:00
  8.3573 +      write_byte(ES, regs.u.r16.si+0x28+4, base23_16);// base 23:16
  8.3574 +      write_byte(ES, regs.u.r16.si+0x28+5, 0x93);     // access
  8.3575 +      write_word(ES, regs.u.r16.si+0x28+6, 0x0000);   // base 31:24/reserved/limit 19:16
  8.3576 +
  8.3577 +      CX = regs.u.r16.cx;
  8.3578 +ASM_START
  8.3579 +      // Compile generates locals offset info relative to SP.
  8.3580 +      // Get CX (word count) from stack.
  8.3581 +      mov  bx, sp
  8.3582 +      SEG SS
  8.3583 +        mov  cx, _int15_function.CX [bx]
  8.3584 +
  8.3585 +      // since we need to set SS:SP, save them to the BDA
  8.3586 +      // for future restore
  8.3587 +      push eax
  8.3588 +      xor eax, eax
  8.3589 +      mov ds, ax
  8.3590 +      mov 0x0469, ss
  8.3591 +      mov 0x0467, sp
  8.3592 +
  8.3593 +      SEG ES
  8.3594 +        lgdt [si + 0x08]
  8.3595 +      SEG CS
  8.3596 +        lidt [pmode_IDT_info]
  8.3597 +      ;;  perhaps do something with IDT here
  8.3598 +
  8.3599 +      ;; set PE bit in CR0
  8.3600 +      mov  eax, cr0
  8.3601 +      or   al, #0x01
  8.3602 +      mov  cr0, eax
  8.3603 +      ;; far jump to flush CPU queue after transition to protected mode
  8.3604 +      JMP_AP(0x0020, protected_mode)
  8.3605 +
  8.3606 +protected_mode:
  8.3607 +      ;; GDT points to valid descriptor table, now load SS, DS, ES
  8.3608 +      mov  ax, #0x28 ;; 101 000 = 5th descriptor in table, TI=GDT, RPL=00
  8.3609 +      mov  ss, ax
  8.3610 +      mov  ax, #0x10 ;; 010 000 = 2nd descriptor in table, TI=GDT, RPL=00
  8.3611 +      mov  ds, ax
  8.3612 +      mov  ax, #0x18 ;; 011 000 = 3rd descriptor in table, TI=GDT, RPL=00
  8.3613 +      mov  es, ax
  8.3614 +      xor  si, si
  8.3615 +      xor  di, di
  8.3616 +      cld
  8.3617 +      rep
  8.3618 +        movsw  ;; move CX words from DS:SI to ES:DI
  8.3619 +
  8.3620 +      ;; make sure DS and ES limits are 64KB
  8.3621 +      mov ax, #0x28
  8.3622 +      mov ds, ax
  8.3623 +      mov es, ax
  8.3624 +
  8.3625 +      ;; reset PG bit in CR0 ???
  8.3626 +      mov  eax, cr0
  8.3627 +      and  al, #0xFE
  8.3628 +      mov  cr0, eax
  8.3629 +
  8.3630 +      ;; far jump to flush CPU queue after transition to real mode
  8.3631 +      JMP_AP(0xf000, real_mode)
  8.3632 +
  8.3633 +real_mode:
  8.3634 +      ;; restore IDT to normal real-mode defaults
  8.3635 +      SEG CS
  8.3636 +        lidt [rmode_IDT_info]
  8.3637 +
  8.3638 +      // restore SS:SP from the BDA
  8.3639 +      xor ax, ax
  8.3640 +      mov ds, ax
  8.3641 +      mov ss, 0x0469
  8.3642 +      mov sp, 0x0467
  8.3643 +      pop eax
  8.3644 +ASM_END
  8.3645 +
  8.3646 +      set_enable_a20(prev_a20_enable);
  8.3647 +
  8.3648 + // turn back on interrupts
  8.3649 +ASM_START
  8.3650 +  sti
  8.3651 +ASM_END
  8.3652 +
  8.3653 +      regs.u.r8.ah = 0;
  8.3654 +      CLEAR_CF();
  8.3655 +      break;
  8.3656 +
  8.3657 +
  8.3658 +    case 0x88:
  8.3659 +      // Get the amount of extended memory (above 1M)
  8.3660 +#if BX_CPU < 2
  8.3661 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3662 +      SET_CF();
  8.3663 +#else
  8.3664 +      regs.u.r8.al = inb_cmos(0x30);
  8.3665 +      regs.u.r8.ah = inb_cmos(0x31);
  8.3666 +
  8.3667 +      // limit to 15M
  8.3668 +      if(regs.u.r16.ax > 0x3c00)
  8.3669 +        regs.u.r16.ax = 0x3c00;
  8.3670 +
  8.3671 +      CLEAR_CF();
  8.3672 +#endif
  8.3673 +      break;
  8.3674 +
  8.3675 +    case 0x90:
  8.3676 +      /* Device busy interrupt.  Called by Int 16h when no key available */
  8.3677 +      break;
  8.3678 +
  8.3679 +    case 0x91:
  8.3680 +      /* Interrupt complete.  Called by Int 16h when key becomes available */
  8.3681 +      break;
  8.3682 +
  8.3683 +    case 0xbf:
  8.3684 +      BX_INFO("*** int 15h function AH=bf not yet supported!\n");
  8.3685 +      SET_CF();
  8.3686 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3687 +      break;
  8.3688 +
  8.3689 +    case 0xC0:
  8.3690 +#if 0
  8.3691 +      SET_CF();
  8.3692 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3693 +      break;
  8.3694 +#endif
  8.3695 +      CLEAR_CF();
  8.3696 +      regs.u.r8.ah = 0;
  8.3697 +      regs.u.r16.bx =  BIOS_CONFIG_TABLE;
  8.3698 +      ES = 0xF000;
  8.3699 +      break;
  8.3700 +
  8.3701 +    case 0xc1:
  8.3702 +      ES = ebda_seg;
  8.3703 +      CLEAR_CF();
  8.3704 +      break;
  8.3705 +
  8.3706 +    case 0xd8:
  8.3707 +      bios_printf(BIOS_PRINTF_DEBUG, "EISA BIOS not present\n");
  8.3708 +      SET_CF();
  8.3709 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3710 +      break;
  8.3711 +
  8.3712 +    default:
  8.3713 +      BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
  8.3714 +        (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
  8.3715 +      SET_CF();
  8.3716 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3717 +      break;
  8.3718 +    }
  8.3719 +}
  8.3720 +
  8.3721 +#if BX_USE_PS2_MOUSE
  8.3722 +  void
  8.3723 +int15_function_mouse(regs, ES, DS, FLAGS)
  8.3724 +  pusha_regs_t regs; // REGS pushed via pusha
  8.3725 +  Bit16u ES, DS, FLAGS;
  8.3726 +{
  8.3727 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.3728 +  Bit8u  mouse_flags_1, mouse_flags_2;
  8.3729 +  Bit16u mouse_driver_seg;
  8.3730 +  Bit16u mouse_driver_offset;
  8.3731 +  Bit8u  comm_byte, prev_command_byte;
  8.3732 +  Bit8u  ret, mouse_data1, mouse_data2, mouse_data3;
  8.3733 +
  8.3734 +BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
  8.3735 +
  8.3736 +  switch (regs.u.r8.ah) {
  8.3737 +    case 0xC2:
  8.3738 +      // Return Codes status in AH
  8.3739 +      // =========================
  8.3740 +      // 00: success
  8.3741 +      // 01: invalid subfunction (AL > 7)
  8.3742 +      // 02: invalid input value (out of allowable range)
  8.3743 +      // 03: interface error
  8.3744 +      // 04: resend command received from mouse controller,
  8.3745 +      //     device driver should attempt command again
  8.3746 +      // 05: cannot enable mouse, since no far call has been installed
  8.3747 +      // 80/86: mouse service not implemented
  8.3748 +
  8.3749 +      switch (regs.u.r8.al) {
  8.3750 +        case 0: // Disable/Enable Mouse
  8.3751 +BX_DEBUG_INT15("case 0:\n");
  8.3752 +          switch (regs.u.r8.bh) {
  8.3753 +            case 0: // Disable Mouse
  8.3754 +BX_DEBUG_INT15("case 0: disable mouse\n");
  8.3755 +              inhibit_mouse_int_and_events(); // disable IRQ12 and packets
  8.3756 +              ret = send_to_mouse_ctrl(0xF5); // disable mouse command
  8.3757 +              if (ret == 0) {
  8.3758 +                ret = get_mouse_data(&mouse_data1);
  8.3759 +                if ( (ret == 0) || (mouse_data1 == 0xFA) ) {
  8.3760 +                  CLEAR_CF();
  8.3761 +                  regs.u.r8.ah = 0;
  8.3762 +                  return;
  8.3763 +                  }
  8.3764 +                }
  8.3765 +
  8.3766 +              // error
  8.3767 +              SET_CF();
  8.3768 +              regs.u.r8.ah = ret;
  8.3769 +              return;
  8.3770 +              break;
  8.3771 +
  8.3772 +            case 1: // Enable Mouse
  8.3773 +BX_DEBUG_INT15("case 1: enable mouse\n");
  8.3774 +              mouse_flags_2 = read_byte(ebda_seg, 0x0027);
  8.3775 +              if ( (mouse_flags_2 & 0x80) == 0 ) {
  8.3776 +                BX_DEBUG_INT15("INT 15h C2 Enable Mouse, no far call handler\n");
  8.3777 +                SET_CF();  // error
  8.3778 +                regs.u.r8.ah = 5; // no far call installed
  8.3779 +                return;
  8.3780 +                }
  8.3781 +              inhibit_mouse_int_and_events(); // disable IRQ12 and packets
  8.3782 +              ret = send_to_mouse_ctrl(0xF4); // enable mouse command
  8.3783 +              if (ret == 0) {
  8.3784 +                ret = get_mouse_data(&mouse_data1);
  8.3785 +                if ( (ret == 0) && (mouse_data1 == 0xFA) ) {
  8.3786 +                  enable_mouse_int_and_events(); // turn IRQ12 and packet generation on
  8.3787 +                  CLEAR_CF();
  8.3788 +                  regs.u.r8.ah = 0;
  8.3789 +                  return;
  8.3790 +                  }
  8.3791 +                }
  8.3792 +              SET_CF();
  8.3793 +              regs.u.r8.ah = ret;
  8.3794 +              return;
  8.3795 +
  8.3796 +            default: // invalid subfunction
  8.3797 +              BX_DEBUG_INT15("INT 15h C2 AL=0, BH=%02x\n", (unsigned) regs.u.r8.bh);
  8.3798 +              SET_CF();  // error
  8.3799 +              regs.u.r8.ah = 1; // invalid subfunction
  8.3800 +              return;
  8.3801 +            }
  8.3802 +          break;
  8.3803 +
  8.3804 +        case 1: // Reset Mouse
  8.3805 +        case 5: // Initialize Mouse
  8.3806 +BX_DEBUG_INT15("case 1 or 5:\n");
  8.3807 +          if (regs.u.r8.al == 5) {
  8.3808 +            if (regs.u.r8.bh != 3) {
  8.3809 +              SET_CF();
  8.3810 +              regs.u.r8.ah = 0x02; // invalid input
  8.3811 +              return;
  8.3812 +            }
  8.3813 +            mouse_flags_2 = read_byte(ebda_seg, 0x0027);
  8.3814 +            mouse_flags_2 = (mouse_flags_2 & 0x00) | regs.u.r8.bh;
  8.3815 +            mouse_flags_1 = 0x00;
  8.3816 +            write_byte(ebda_seg, 0x0026, mouse_flags_1);
  8.3817 +            write_byte(ebda_seg, 0x0027, mouse_flags_2);
  8.3818 +          }
  8.3819 +
  8.3820 +          inhibit_mouse_int_and_events(); // disable IRQ12 and packets
  8.3821 +          ret = send_to_mouse_ctrl(0xFF); // reset mouse command
  8.3822 +          if (ret == 0) {
  8.3823 +            ret = get_mouse_data(&mouse_data3);
  8.3824 +            // if no mouse attached, it will return RESEND
  8.3825 +            if (mouse_data3 == 0xfe) {
  8.3826 +              SET_CF();
  8.3827 +              return;
  8.3828 +            }
  8.3829 +            if (mouse_data3 != 0xfa)
  8.3830 +              BX_PANIC("Mouse reset returned %02x (should be ack)\n", (unsigned)mouse_data3);
  8.3831 +            if ( ret == 0 ) {
  8.3832 +              ret = get_mouse_data(&mouse_data1);
  8.3833 +              if ( ret == 0 ) {
  8.3834 +                ret = get_mouse_data(&mouse_data2);
  8.3835 +                if ( ret == 0 ) {
  8.3836 +                  // turn IRQ12 and packet generation on
  8.3837 +                  enable_mouse_int_and_events();
  8.3838 +                  CLEAR_CF();
  8.3839 +                  regs.u.r8.ah = 0;
  8.3840 +                  regs.u.r8.bl = mouse_data1;
  8.3841 +                  regs.u.r8.bh = mouse_data2;
  8.3842 +                  return;
  8.3843 +                  }
  8.3844 +                }
  8.3845 +              }
  8.3846 +            }
  8.3847 +
  8.3848 +          // error
  8.3849 +          SET_CF();
  8.3850 +          regs.u.r8.ah = ret;
  8.3851 +          return;
  8.3852 +
  8.3853 +        case 2: // Set Sample Rate
  8.3854 +BX_DEBUG_INT15("case 2:\n");
  8.3855 +          switch (regs.u.r8.bh) {
  8.3856 +            case 0: mouse_data1 = 10; break; //  10 reports/sec
  8.3857 +            case 1: mouse_data1 = 20; break; //  20 reports/sec
  8.3858 +            case 2: mouse_data1 = 40; break; //  40 reports/sec
  8.3859 +            case 3: mouse_data1 = 60; break; //  60 reports/sec
  8.3860 +            case 4: mouse_data1 = 80; break; //  80 reports/sec
  8.3861 +            case 5: mouse_data1 = 100; break; // 100 reports/sec (default)
  8.3862 +            case 6: mouse_data1 = 200; break; // 200 reports/sec
  8.3863 +            default: mouse_data1 = 0;
  8.3864 +          }
  8.3865 +          if (mouse_data1 > 0) {
  8.3866 +            ret = send_to_mouse_ctrl(0xF3); // set sample rate command
  8.3867 +            if (ret == 0) {
  8.3868 +              ret = get_mouse_data(&mouse_data2);
  8.3869 +              ret = send_to_mouse_ctrl(mouse_data1);
  8.3870 +              ret = get_mouse_data(&mouse_data2);
  8.3871 +              CLEAR_CF();
  8.3872 +              regs.u.r8.ah = 0;
  8.3873 +            } else {
  8.3874 +              // error
  8.3875 +              SET_CF();
  8.3876 +              regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3877 +            }
  8.3878 +          } else {
  8.3879 +            // error
  8.3880 +            SET_CF();
  8.3881 +            regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3882 +          }
  8.3883 +          break;
  8.3884 +
  8.3885 +        case 3: // Set Resolution
  8.3886 +BX_DEBUG_INT15("case 3:\n");
  8.3887 +          // BX:
  8.3888 +          //      0 =  25 dpi, 1 count  per millimeter
  8.3889 +          //      1 =  50 dpi, 2 counts per millimeter
  8.3890 +          //      2 = 100 dpi, 4 counts per millimeter
  8.3891 +          //      3 = 200 dpi, 8 counts per millimeter
  8.3892 +          CLEAR_CF();
  8.3893 +          regs.u.r8.ah = 0;
  8.3894 +          break;
  8.3895 +
  8.3896 +        case 4: // Get Device ID
  8.3897 +BX_DEBUG_INT15("case 4:\n");
  8.3898 +          inhibit_mouse_int_and_events(); // disable IRQ12 and packets
  8.3899 +          ret = send_to_mouse_ctrl(0xF2); // get mouse ID command
  8.3900 +          if (ret == 0) {
  8.3901 +            ret = get_mouse_data(&mouse_data1);
  8.3902 +            ret = get_mouse_data(&mouse_data2);
  8.3903 +            CLEAR_CF();
  8.3904 +            regs.u.r8.ah = 0;
  8.3905 +            regs.u.r8.bh = mouse_data2;
  8.3906 +          } else {
  8.3907 +            // error
  8.3908 +            SET_CF();
  8.3909 +            regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3910 +          }
  8.3911 +          break;
  8.3912 +
  8.3913 +        case 6: // Return Status & Set Scaling Factor...
  8.3914 +BX_DEBUG_INT15("case 6:\n");
  8.3915 +          switch (regs.u.r8.bh) {
  8.3916 +            case 0: // Return Status
  8.3917 +              comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
  8.3918 +              ret = send_to_mouse_ctrl(0xE9); // get mouse info command
  8.3919 +              if (ret == 0) {
  8.3920 +                ret = get_mouse_data(&mouse_data1);
  8.3921 +                if (mouse_data1 != 0xfa)
  8.3922 +                  BX_PANIC("Mouse status returned %02x (should be ack)\n", (unsigned)mouse_data1);
  8.3923 +                if (ret == 0) {
  8.3924 +                  ret = get_mouse_data(&mouse_data1);
  8.3925 +                  if ( ret == 0 ) {
  8.3926 +                    ret = get_mouse_data(&mouse_data2);
  8.3927 +                    if ( ret == 0 ) {
  8.3928 +                      ret = get_mouse_data(&mouse_data3);
  8.3929 +                      if ( ret == 0 ) {
  8.3930 +                        CLEAR_CF();
  8.3931 +                        regs.u.r8.ah = 0;
  8.3932 +                        regs.u.r8.bl = mouse_data1;
  8.3933 +                        regs.u.r8.cl = mouse_data2;
  8.3934 +                        regs.u.r8.dl = mouse_data3;
  8.3935 +                        set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
  8.3936 +                        return;
  8.3937 +                        }
  8.3938 +                      }
  8.3939 +                    }
  8.3940 +                  }
  8.3941 +                }
  8.3942 +
  8.3943 +              // error
  8.3944 +              SET_CF();
  8.3945 +              regs.u.r8.ah = ret;
  8.3946 +              set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
  8.3947 +              return;
  8.3948 +
  8.3949 +            case 1: // Set Scaling Factor to 1:1
  8.3950 +            case 2: // Set Scaling Factor to 2:1
  8.3951 +              comm_byte = inhibit_mouse_int_and_events(); // disable IRQ12 and packets
  8.3952 +              if (regs.u.r8.bh == 1) {
  8.3953 +                ret = send_to_mouse_ctrl(0xE6);
  8.3954 +              } else {
  8.3955 +                ret = send_to_mouse_ctrl(0xE7);
  8.3956 +              }
  8.3957 +              if (ret == 0) {
  8.3958 +                get_mouse_data(&mouse_data1);
  8.3959 +                ret = (mouse_data1 != 0xFA);
  8.3960 +              }
  8.3961 +              if (ret == 0) {
  8.3962 +                CLEAR_CF();
  8.3963 +                regs.u.r8.ah = 0;
  8.3964 +              } else {
  8.3965 +                // error
  8.3966 +                SET_CF();
  8.3967 +                regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.3968 +              }
  8.3969 +              set_kbd_command_byte(comm_byte); // restore IRQ12 and serial enable
  8.3970 +              break;
  8.3971 +
  8.3972 +            default:
  8.3973 +              BX_PANIC("INT 15h C2 AL=6, BH=%02x\n", (unsigned) regs.u.r8.bh);
  8.3974 +            }
  8.3975 +          break;
  8.3976 +
  8.3977 +        case 7: // Set Mouse Handler Address
  8.3978 +BX_DEBUG_INT15("case 7:\n");
  8.3979 +          mouse_driver_seg = ES;
  8.3980 +          mouse_driver_offset = regs.u.r16.bx;
  8.3981 +          write_word(ebda_seg, 0x0022, mouse_driver_offset);
  8.3982 +          write_word(ebda_seg, 0x0024, mouse_driver_seg);
  8.3983 +          mouse_flags_2 = read_byte(ebda_seg, 0x0027);
  8.3984 +          if (mouse_driver_offset == 0 && mouse_driver_seg == 0) {
  8.3985 +            /* remove handler */
  8.3986 +            if ( (mouse_flags_2 & 0x80) != 0 ) {
  8.3987 +              mouse_flags_2 &= ~0x80;
  8.3988 +              inhibit_mouse_int_and_events(); // disable IRQ12 and packets
  8.3989 +              }
  8.3990 +            }
  8.3991 +          else {
  8.3992 +            /* install handler */
  8.3993 +            mouse_flags_2 |= 0x80;
  8.3994 +            }
  8.3995 +          write_byte(ebda_seg, 0x0027, mouse_flags_2);
  8.3996 +          CLEAR_CF();
  8.3997 +          regs.u.r8.ah = 0;
  8.3998 +          break;
  8.3999 +
  8.4000 +        default:
  8.4001 +BX_DEBUG_INT15("case default:\n");
  8.4002 +          regs.u.r8.ah = 1; // invalid function
  8.4003 +          SET_CF();
  8.4004 +        }
  8.4005 +      break;
  8.4006 +
  8.4007 +    default:
  8.4008 +      BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
  8.4009 +        (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
  8.4010 +      SET_CF();
  8.4011 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.4012 +      break;
  8.4013 +    }
  8.4014 +}
  8.4015 +#endif
  8.4016 +
  8.4017 +  void
  8.4018 +int15_function32(regs, ES, DS, FLAGS)
  8.4019 +  pushad_regs_t regs; // REGS pushed via pushad
  8.4020 +  Bit16u ES, DS, FLAGS;
  8.4021 +{
  8.4022 +  Bit32u  extended_memory_size=0; // 64bits long
  8.4023 +  Bit16u  CX,DX;
  8.4024 +
  8.4025 +BX_DEBUG_INT15("int15 AX=%04x\n",regs.u.r16.ax);
  8.4026 +
  8.4027 +  switch (regs.u.r8.ah) {
  8.4028 +    case 0x86:
  8.4029 +      // Wait for CX:DX microseconds. currently using the 
  8.4030 +      // refresh request port 0x61 bit4, toggling every 15usec 
  8.4031 +
  8.4032 +      CX = regs.u.r16.cx;
  8.4033 +      DX = regs.u.r16.dx;
  8.4034 +
  8.4035 +ASM_START
  8.4036 +      sti
  8.4037 +
  8.4038 +      ;; Get the count in eax
  8.4039 +      mov  bx, sp
  8.4040 +      SEG SS
  8.4041 +        mov  ax, _int15_function.CX [bx]
  8.4042 +      shl  eax, #16
  8.4043 +      SEG SS
  8.4044 +        mov  ax, _int15_function.DX [bx]
  8.4045 +
  8.4046 +      ;; convert to numbers of 15usec ticks
  8.4047 +      mov ebx, #15
  8.4048 +      xor edx, edx
  8.4049 +      div eax, ebx
  8.4050 +      mov ecx, eax
  8.4051 +
  8.4052 +      ;; wait for ecx number of refresh requests
  8.4053 +      in al, #0x61
  8.4054 +      and al,#0x10
  8.4055 +      mov ah, al
  8.4056 +
  8.4057 +      or ecx, ecx
  8.4058 +      je int1586_tick_end
  8.4059 +int1586_tick:
  8.4060 +      in al, #0x61
  8.4061 +      and al,#0x10
  8.4062 +      cmp al, ah
  8.4063 +      je  int1586_tick
  8.4064 +      mov ah, al
  8.4065 +      dec ecx
  8.4066 +      jnz int1586_tick
  8.4067 +int1586_tick_end:
  8.4068 +ASM_END
  8.4069 +
  8.4070 +      break;
  8.4071 +
  8.4072 +    case 0xe8:
  8.4073 +        switch(regs.u.r8.al)
  8.4074 +        {
  8.4075 +         case 0x20: // coded by osmaker aka K.J.
  8.4076 +            if(regs.u.r32.edx == 0x534D4150)
  8.4077 +            {
  8.4078 +#ifdef VMXASSIST
  8.4079 +		if ((regs.u.r16.bx / 0x14)* 0x14 == regs.u.r16.bx) {
  8.4080 +		    Bit16u e820_table_size = read_word(0xe000, 0x8) * 0x14;
  8.4081 +
  8.4082 +		    if (regs.u.r16.bx + 0x14 <= e820_table_size) {
  8.4083 +			memcpyb(ES, regs.u.r16.di,
  8.4084 +				0xe000, 0x10 + regs.u.r16.bx, 0x14);
  8.4085 +		    }
  8.4086 +		    regs.u.r32.ebx += 0x14;
  8.4087 +		    if ((regs.u.r32.ebx + 0x14 - 1) > e820_table_size)
  8.4088 +			regs.u.r32.ebx = 0;
  8.4089 +		    regs.u.r32.eax = 0x534D4150;
  8.4090 +		    regs.u.r32.ecx = 0x14;
  8.4091 +		    CLEAR_CF();
  8.4092 +		    return;
  8.4093 +		} else if (regs.u.r16.bx == 1) {
  8.4094 +		    extended_memory_size = inb_cmos(0x35);
  8.4095 +		    extended_memory_size <<= 8;
  8.4096 +		    extended_memory_size |= inb_cmos(0x34);
  8.4097 +		    extended_memory_size *= 64;
  8.4098 +		    if (extended_memory_size > 0x3bc000) // greater than EFF00000???
  8.4099 +		    {
  8.4100 +			extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
  8.4101 +		    }
  8.4102 +		    extended_memory_size *= 1024;
  8.4103 +		    extended_memory_size += 15728640; // make up for the 16mb of memory that is chopped off
  8.4104 +
  8.4105 +		    if (extended_memory_size <= 15728640)
  8.4106 +		    {
  8.4107 +			extended_memory_size = inb_cmos(0x31);
  8.4108 +			extended_memory_size <<= 8;
  8.4109 +			extended_memory_size |= inb_cmos(0x30);
  8.4110 +			extended_memory_size *= 1024;
  8.4111 +		    }
  8.4112 +
  8.4113 +		    write_word(ES, regs.u.r16.di, 0x0000);
  8.4114 +		    write_word(ES, regs.u.r16.di+2, 0x0010);
  8.4115 +		    write_word(ES, regs.u.r16.di+4, 0x0000);
  8.4116 +		    write_word(ES, regs.u.r16.di+6, 0x0000);
  8.4117 +
  8.4118 +		    write_word(ES, regs.u.r16.di+8, extended_memory_size);
  8.4119 +		    extended_memory_size >>= 16;
  8.4120 +		    write_word(ES, regs.u.r16.di+10, extended_memory_size);
  8.4121 +		    extended_memory_size >>= 16;
  8.4122 +		    write_word(ES, regs.u.r16.di+12, extended_memory_size);
  8.4123 +		    extended_memory_size >>= 16;
  8.4124 +		    write_word(ES, regs.u.r16.di+14, extended_memory_size);
  8.4125 +
  8.4126 +		    write_word(ES, regs.u.r16.di+16, 0x1);
  8.4127 +		    write_word(ES, regs.u.r16.di+18, 0x0);
  8.4128 +
  8.4129 +		    regs.u.r32.ebx = 0;
  8.4130 +		    regs.u.r32.eax = 0x534D4150;
  8.4131 +		    regs.u.r32.ecx = 0x14;
  8.4132 +		    CLEAR_CF();
  8.4133 +		    return;
  8.4134 +		} else { /* AX=E820, DX=534D4150, BX unrecognized */
  8.4135 +		    goto int15_unimplemented;
  8.4136 +		}
  8.4137 +#else
  8.4138 +                switch(regs.u.r16.bx)
  8.4139 +                {
  8.4140 +                    case 0:
  8.4141 +                        write_word(ES, regs.u.r16.di, 0x00);
  8.4142 +                        write_word(ES, regs.u.r16.di+2, 0x00);
  8.4143 +                        write_word(ES, regs.u.r16.di+4, 0x00);
  8.4144 +                        write_word(ES, regs.u.r16.di+6, 0x00);
  8.4145 +
  8.4146 +                        write_word(ES, regs.u.r16.di+8, 0xFC00);
  8.4147 +                        write_word(ES, regs.u.r16.di+10, 0x0009);
  8.4148 +                        write_word(ES, regs.u.r16.di+12, 0x0000);
  8.4149 +                        write_word(ES, regs.u.r16.di+14, 0x0000);
  8.4150 +
  8.4151 +                        write_word(ES, regs.u.r16.di+16, 0x1);
  8.4152 +                        write_word(ES, regs.u.r16.di+18, 0x0);
  8.4153 +
  8.4154 +                        regs.u.r32.ebx = 1;
  8.4155 +
  8.4156 +                        regs.u.r32.eax = 0x534D4150;
  8.4157 +                        regs.u.r32.ecx = 0x14;
  8.4158 +                        CLEAR_CF();
  8.4159 +                        return;
  8.4160 +                        break;
  8.4161 +                    case 1:
  8.4162 +                        extended_memory_size = inb_cmos(0x35);
  8.4163 +                        extended_memory_size <<= 8;
  8.4164 +                        extended_memory_size |= inb_cmos(0x34);
  8.4165 +                        extended_memory_size *= 64;
  8.4166 +                        if(extended_memory_size > 0x3bc000) // greater than EFF00000???
  8.4167 +                        {
  8.4168 +                            extended_memory_size = 0x3bc000; // everything after this is reserved memory until we get to 0x100000000
  8.4169 +                        }
  8.4170 +                        extended_memory_size *= 1024;
  8.4171 +                        extended_memory_size += 15728640; // make up for the 16mb of memory that is chopped off
  8.4172 +
  8.4173 +                        if(extended_memory_size <= 15728640)
  8.4174 +                        {
  8.4175 +                            extended_memory_size = inb_cmos(0x31);
  8.4176 +                            extended_memory_size <<= 8;
  8.4177 +                            extended_memory_size |= inb_cmos(0x30);
  8.4178 +                            extended_memory_size *= 1024;
  8.4179 +                        }
  8.4180 +
  8.4181 +                        write_word(ES, regs.u.r16.di, 0x0000);
  8.4182 +                        write_word(ES, regs.u.r16.di+2, 0x0010);
  8.4183 +                        write_word(ES, regs.u.r16.di+4, 0x0000);
  8.4184 +                        write_word(ES, regs.u.r16.di+6, 0x0000);
  8.4185 +
  8.4186 +                        write_word(ES, regs.u.r16.di+8, extended_memory_size);
  8.4187 +                        extended_memory_size >>= 16;
  8.4188 +                        write_word(ES, regs.u.r16.di+10, extended_memory_size);
  8.4189 +                        extended_memory_size >>= 16;
  8.4190 +                        write_word(ES, regs.u.r16.di+12, extended_memory_size);
  8.4191 +                        extended_memory_size >>= 16;
  8.4192 +                        write_word(ES, regs.u.r16.di+14, extended_memory_size);
  8.4193 +
  8.4194 +                        write_word(ES, regs.u.r16.di+16, 0x1);
  8.4195 +                        write_word(ES, regs.u.r16.di+18, 0x0);
  8.4196 +
  8.4197 +                        regs.u.r32.ebx = 0;
  8.4198 +                        regs.u.r32.eax = 0x534D4150;
  8.4199 +                        regs.u.r32.ecx = 0x14;
  8.4200 +                        CLEAR_CF();
  8.4201 +                        return;
  8.4202 +                        break;
  8.4203 +                    default:  /* AX=E820, DX=534D4150, BX unrecognized */
  8.4204 +                        goto int15_unimplemented;
  8.4205 +                        break;
  8.4206 +                }
  8.4207 +#endif
  8.4208 +	    } else {
  8.4209 +	      // if DX != 0x534D4150)
  8.4210 +	      goto int15_unimplemented;
  8.4211 +	    }
  8.4212 +            break;
  8.4213 +
  8.4214 +        case 0x01: 
  8.4215 +          // do we have any reason to fail here ?
  8.4216 +          CLEAR_CF();
  8.4217 +
  8.4218 +          // my real system sets ax and bx to 0
  8.4219 +          // this is confirmed by Ralph Brown list
  8.4220 +          // but syslinux v1.48 is known to behave 
  8.4221 +          // strangely if ax is set to 0
  8.4222 +          // regs.u.r16.ax = 0;
  8.4223 +          // regs.u.r16.bx = 0;
  8.4224 +
  8.4225 +          // Get the amount of extended memory (above 1M)
  8.4226 +          regs.u.r8.cl = inb_cmos(0x30);
  8.4227 +          regs.u.r8.ch = inb_cmos(0x31);
  8.4228 +          
  8.4229 +          // limit to 15M
  8.4230 +          if(regs.u.r16.cx > 0x3c00)
  8.4231 +          {
  8.4232 +            regs.u.r16.cx = 0x3c00;
  8.4233 +          }
  8.4234 +
  8.4235 +          // Get the amount of extended memory above 16M in 64k blocs
  8.4236 +          regs.u.r8.dl = inb_cmos(0x34);
  8.4237 +          regs.u.r8.dh = inb_cmos(0x35);
  8.4238 +
  8.4239 +          // Set configured memory equal to extended memory
  8.4240 +          regs.u.r16.ax = regs.u.r16.cx;
  8.4241 +          regs.u.r16.bx = regs.u.r16.dx;
  8.4242 +          break;
  8.4243 +	default:  /* AH=0xE8?? but not implemented */
  8.4244 +	  goto int15_unimplemented;
  8.4245 +       }
  8.4246 +       break;
  8.4247 +    int15_unimplemented:
  8.4248 +       // fall into the default
  8.4249 +    default:
  8.4250 +      BX_INFO("*** int 15h function AX=%04x, BX=%04x not yet supported!\n",
  8.4251 +        (unsigned) regs.u.r16.ax, (unsigned) regs.u.r16.bx);
  8.4252 +      SET_CF();
  8.4253 +      regs.u.r8.ah = UNSUPPORTED_FUNCTION;
  8.4254 +      break;
  8.4255 +    }
  8.4256 +}
  8.4257 +
  8.4258 +  void
  8.4259 +int16_function(DI, SI, BP, SP, BX, DX, CX, AX, FLAGS)
  8.4260 +  Bit16u DI, SI, BP, SP, BX, DX, CX, AX, FLAGS;
  8.4261 +{
  8.4262 +  Bit8u scan_code, ascii_code, shift_flags, count;
  8.4263 +  Bit16u kbd_code, max;
  8.4264 +
  8.4265 +  BX_DEBUG_INT16("int16: AX=%04x BX=%04x CX=%04x DX=%04x \n", AX, BX, CX, DX);
  8.4266 +
  8.4267 +  switch (GET_AH()) {
  8.4268 +    case 0x00: /* read keyboard input */
  8.4269 +
  8.4270 +      if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
  8.4271 +        BX_PANIC("KBD: int16h: out of keyboard input\n");
  8.4272 +        }
  8.4273 +      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
  8.4274 +      else if (ascii_code == 0xE0) ascii_code = 0;
  8.4275 +      AX = (scan_code << 8) | ascii_code;
  8.4276 +      break;
  8.4277 +
  8.4278 +    case 0x01: /* check keyboard status */
  8.4279 +      if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
  8.4280 +        SET_ZF();
  8.4281 +        return;
  8.4282 +        }
  8.4283 +      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
  8.4284 +      else if (ascii_code == 0xE0) ascii_code = 0;
  8.4285 +      AX = (scan_code << 8) | ascii_code;
  8.4286 +      CLEAR_ZF();
  8.4287 +      break;
  8.4288 +
  8.4289 +    case 0x02: /* get shift flag status */
  8.4290 +      shift_flags = read_byte(0x0040, 0x17);
  8.4291 +      SET_AL(shift_flags);
  8.4292 +      break;
  8.4293 +
  8.4294 +    case 0x05: /* store key-stroke into buffer */
  8.4295 +      if ( !enqueue_key(GET_CH(), GET_CL()) ) {
  8.4296 +        SET_AL(1);
  8.4297 +        }
  8.4298 +      else {
  8.4299 +        SET_AL(0);
  8.4300 +        }
  8.4301 +      break;
  8.4302 +
  8.4303 +    case 0x09: /* GET KEYBOARD FUNCTIONALITY */
  8.4304 +      // bit Bochs Description     
  8.4305 +      //  7    0   reserved
  8.4306 +      //  6    0   INT 16/AH=20h-22h supported (122-key keyboard support)
  8.4307 +      //  5    1   INT 16/AH=10h-12h supported (enhanced keyboard support)
  8.4308 +      //  4    1   INT 16/AH=0Ah supported
  8.4309 +      //  3    0   INT 16/AX=0306h supported
  8.4310 +      //  2    0   INT 16/AX=0305h supported
  8.4311 +      //  1    0   INT 16/AX=0304h supported
  8.4312 +      //  0    0   INT 16/AX=0300h supported
  8.4313 +      //
  8.4314 +      SET_AL(0x30);
  8.4315 +      break;
  8.4316 +
  8.4317 +    case 0x0A: /* GET KEYBOARD ID */
  8.4318 +      count = 2;
  8.4319 +      kbd_code = 0x0;
  8.4320 +      outb(0x60, 0xf2);
  8.4321 +      /* Wait for data */
  8.4322 +      max=0xffff;
  8.4323 +      while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
  8.4324 +      if (max>0x0) {
  8.4325 +        if ((inb(0x60) == 0xfa)) {
  8.4326 +          do {
  8.4327 +            max=0xffff;
  8.4328 +            while ( ((inb(0x64) & 0x01) == 0) && (--max>0) ) outb(0x80, 0x00);
  8.4329 +            if (max>0x0) {
  8.4330 +              kbd_code >>= 8;
  8.4331 +              kbd_code |= (inb(0x60) << 8);
  8.4332 +            }
  8.4333 +          } while (--count>0);
  8.4334 +	}
  8.4335 +      }
  8.4336 +      BX=kbd_code;
  8.4337 +      break;
  8.4338 +
  8.4339 +    case 0x10: /* read MF-II keyboard input */
  8.4340 +
  8.4341 +      if ( !dequeue_key(&scan_code, &ascii_code, 1) ) {
  8.4342 +        BX_PANIC("KBD: int16h: out of keyboard input\n");
  8.4343 +        }
  8.4344 +      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
  8.4345 +      AX = (scan_code << 8) | ascii_code;
  8.4346 +      break;
  8.4347 +
  8.4348 +    case 0x11: /* check MF-II keyboard status */
  8.4349 +      if ( !dequeue_key(&scan_code, &ascii_code, 0) ) {
  8.4350 +        SET_ZF();
  8.4351 +        return;
  8.4352 +        }
  8.4353 +      if (scan_code !=0 && ascii_code == 0xF0) ascii_code = 0;
  8.4354 +      AX = (scan_code << 8) | ascii_code;
  8.4355 +      CLEAR_ZF();
  8.4356 +      break;
  8.4357 +
  8.4358 +    case 0x12: /* get extended keyboard status */
  8.4359 +      shift_flags = read_byte(0x0040, 0x17);
  8.4360 +      SET_AL(shift_flags);
  8.4361 +      shift_flags = read_byte(0x0040, 0x18);
  8.4362 +      SET_AH(shift_flags);
  8.4363 +      BX_DEBUG_INT16("int16: func 12 sending %04x\n",AX);
  8.4364 +      break;
  8.4365 +
  8.4366 +    case 0x92: /* keyboard capability check called by DOS 5.0+ keyb */
  8.4367 +      SET_AH(0x80); // function int16 ah=0x10-0x12 supported
  8.4368 +      break;
  8.4369 +
  8.4370 +    case 0xA2: /* 122 keys capability check called by DOS 5.0+ keyb */
  8.4371 +      // don't change AH : function int16 ah=0x20-0x22 NOT supported
  8.4372 +      break;
  8.4373 +
  8.4374 +    case 0x6F:
  8.4375 +      if (GET_AL() == 0x08)
  8.4376 +	SET_AH(0x02); // unsupported, aka normal keyboard
  8.4377 +
  8.4378 +    default:
  8.4379 +      BX_INFO("KBD: unsupported int 16h function %02x\n", GET_AH());
  8.4380 +    }
  8.4381 +}
  8.4382 +
  8.4383 +  unsigned int
  8.4384 +dequeue_key(scan_code, ascii_code, incr)
  8.4385 +  Bit8u *scan_code;
  8.4386 +  Bit8u *ascii_code;
  8.4387 +  unsigned int incr;
  8.4388 +{
  8.4389 +  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail;
  8.4390 +  Bit16u ss;
  8.4391 +  Bit8u  acode, scode;
  8.4392 +
  8.4393 +#if BX_CPU < 2
  8.4394 +  buffer_start = 0x001E;
  8.4395 +  buffer_end   = 0x003E;
  8.4396 +#else
  8.4397 +  buffer_start = read_word(0x0040, 0x0080);
  8.4398 +  buffer_end   = read_word(0x0040, 0x0082);
  8.4399 +#endif
  8.4400 +
  8.4401 +  buffer_head = read_word(0x0040, 0x001a);
  8.4402 +  buffer_tail = read_word(0x0040, 0x001c);
  8.4403 +
  8.4404 +  if (buffer_head != buffer_tail) {
  8.4405 +    ss = get_SS();
  8.4406 +    acode = read_byte(0x0040, buffer_head);
  8.4407 +    scode = read_byte(0x0040, buffer_head+1);
  8.4408 +    write_byte(ss, ascii_code, acode);
  8.4409 +    write_byte(ss, scan_code, scode);
  8.4410 +
  8.4411 +    if (incr) {
  8.4412 +      buffer_head += 2;
  8.4413 +      if (buffer_head >= buffer_end)
  8.4414 +        buffer_head = buffer_start;
  8.4415 +      write_word(0x0040, 0x001a, buffer_head);
  8.4416 +      }
  8.4417 +    return(1);
  8.4418 +    }
  8.4419 +  else {
  8.4420 +    return(0);
  8.4421 +    }
  8.4422 +}
  8.4423 +
  8.4424 +static char panic_msg_keyb_buffer_full[] = "%s: keyboard input buffer full\n";
  8.4425 +
  8.4426 +  Bit8u
  8.4427 +inhibit_mouse_int_and_events()
  8.4428 +{
  8.4429 +  Bit8u command_byte, prev_command_byte;
  8.4430 +
  8.4431 +  // Turn off IRQ generation and aux data line
  8.4432 +  if ( inb(0x64) & 0x02 )
  8.4433 +    BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
  8.4434 +  outb(0x64, 0x20); // get command byte
  8.4435 +  while ( (inb(0x64) & 0x01) != 0x01 );
  8.4436 +  prev_command_byte = inb(0x60);
  8.4437 +  command_byte = prev_command_byte;
  8.4438 +  //while ( (inb(0x64) & 0x02) );
  8.4439 +  if ( inb(0x64) & 0x02 )
  8.4440 +    BX_PANIC(panic_msg_keyb_buffer_full,"inhibmouse");
  8.4441 +  command_byte &= 0xfd; // turn off IRQ 12 generation
  8.4442 +  command_byte |= 0x20; // disable mouse serial clock line
  8.4443 +  outb(0x64, 0x60); // write command byte
  8.4444 +  outb(0x60, command_byte);
  8.4445 +  return(prev_command_byte);
  8.4446 +}
  8.4447 +
  8.4448 +  void
  8.4449 +enable_mouse_int_and_events()
  8.4450 +{
  8.4451 +  Bit8u command_byte;
  8.4452 +
  8.4453 +  // Turn on IRQ generation and aux data line
  8.4454 +  if ( inb(0x64) & 0x02 )
  8.4455 +    BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
  8.4456 +  outb(0x64, 0x20); // get command byte
  8.4457 +  while ( (inb(0x64) & 0x01) != 0x01 );
  8.4458 +  command_byte = inb(0x60);
  8.4459 +  //while ( (inb(0x64) & 0x02) );
  8.4460 +  if ( inb(0x64) & 0x02 )
  8.4461 +    BX_PANIC(panic_msg_keyb_buffer_full,"enabmouse");
  8.4462 +  command_byte |= 0x02; // turn on IRQ 12 generation
  8.4463 +  command_byte &= 0xdf; // enable mouse serial clock line
  8.4464 +  outb(0x64, 0x60); // write command byte
  8.4465 +  outb(0x60, command_byte);
  8.4466 +}
  8.4467 +
  8.4468 +  Bit8u
  8.4469 +send_to_mouse_ctrl(sendbyte)
  8.4470 +  Bit8u sendbyte;
  8.4471 +{
  8.4472 +  Bit8u response;
  8.4473 +
  8.4474 +  // wait for chance to write to ctrl
  8.4475 +  if ( inb(0x64) & 0x02 )
  8.4476 +    BX_PANIC(panic_msg_keyb_buffer_full,"sendmouse");
  8.4477 +  outb(0x64, 0xD4);
  8.4478 +  outb(0x60, sendbyte);
  8.4479 +  return(0);
  8.4480 +}
  8.4481 +
  8.4482 +
  8.4483 +  Bit8u
  8.4484 +get_mouse_data(data)
  8.4485 +  Bit8u *data;
  8.4486 +{
  8.4487 +  Bit8u response;
  8.4488 +  Bit16u ss;
  8.4489 +
  8.4490 +  while ( (inb(0x64) & 0x21) != 0x21 ) {
  8.4491 +    }
  8.4492 +
  8.4493 +  response = inb(0x60);
  8.4494 +
  8.4495 +  ss = get_SS();
  8.4496 +  write_byte(ss, data, response);
  8.4497 +  return(0);
  8.4498 +}
  8.4499 +
  8.4500 +  void
  8.4501 +set_kbd_command_byte(command_byte)
  8.4502 +  Bit8u command_byte;
  8.4503 +{
  8.4504 +  if ( inb(0x64) & 0x02 )
  8.4505 +    BX_PANIC(panic_msg_keyb_buffer_full,"setkbdcomm");
  8.4506 +  outb(0x64, 0xD4);
  8.4507 +
  8.4508 +  outb(0x64, 0x60); // write command byte
  8.4509 +  outb(0x60, command_byte);
  8.4510 +}
  8.4511 +
  8.4512 +  void
  8.4513 +int09_function(DI, SI, BP, SP, BX, DX, CX, AX)
  8.4514 +  Bit16u DI, SI, BP, SP, BX, DX, CX, AX;
  8.4515 +{
  8.4516 +  Bit8u scancode, asciicode, shift_flags;
  8.4517 +  Bit8u mf2_flags, mf2_state, led_flags;
  8.4518 +
  8.4519 +  //
  8.4520 +  // DS has been set to F000 before call
  8.4521 +  //
  8.4522 +
  8.4523 +
  8.4524 +  scancode = GET_AL();
  8.4525 +
  8.4526 +  if (scancode == 0) {
  8.4527 +    BX_INFO("KBD: int09 handler: AL=0\n");
  8.4528 +    return;
  8.4529 +    }
  8.4530 +
  8.4531 +
  8.4532 +  shift_flags = read_byte(0x0040, 0x17);
  8.4533 +  mf2_flags = read_byte(0x0040, 0x18);
  8.4534 +  mf2_state = read_byte(0x0040, 0x96);
  8.4535 +  led_flags = read_byte(0x0040, 0x97);
  8.4536 +  asciicode = 0;
  8.4537 +
  8.4538 +  switch (scancode) {
  8.4539 +    case 0x3a: /* Caps Lock press */
  8.4540 +      shift_flags ^= 0x40;
  8.4541 +      write_byte(0x0040, 0x17, shift_flags);
  8.4542 +      mf2_flags |= 0x40;
  8.4543 +      write_byte(0x0040, 0x18, mf2_flags);
  8.4544 +      led_flags ^= 0x04;
  8.4545 +      write_byte(0x0040, 0x97, led_flags);
  8.4546 +      break;
  8.4547 +    case 0xba: /* Caps Lock release */
  8.4548 +      mf2_flags &= ~0x40;
  8.4549 +      write_byte(0x0040, 0x18, mf2_flags);
  8.4550 +      break;
  8.4551 +
  8.4552 +    case 0x2a: /* L Shift press */
  8.4553 +      /*shift_flags &= ~0x40;*/
  8.4554 +      shift_flags |= 0x02;
  8.4555 +      write_byte(0x0040, 0x17, shift_flags);
  8.4556 +      led_flags &= ~0x04;
  8.4557 +      write_byte(0x0040, 0x97, led_flags);
  8.4558 +      break;
  8.4559 +    case 0xaa: /* L Shift release */
  8.4560 +      shift_flags &= ~0x02;
  8.4561 +      write_byte(0x0040, 0x17, shift_flags);
  8.4562 +      break;
  8.4563 +
  8.4564 +    case 0x36: /* R Shift press */
  8.4565 +      /*shift_flags &= ~0x40;*/
  8.4566 +      shift_flags |= 0x01;
  8.4567 +      write_byte(0x0040, 0x17, shift_flags);
  8.4568 +      led_flags &= ~0x04;
  8.4569 +      write_byte(0x0040, 0x97, led_flags);
  8.4570 +      break;
  8.4571 +    case 0xb6: /* R Shift release */
  8.4572 +      shift_flags &= ~0x01;
  8.4573 +      write_byte(0x0040, 0x17, shift_flags);
  8.4574 +      break;
  8.4575 +
  8.4576 +    case 0x1d: /* Ctrl press */
  8.4577 +      shift_flags |= 0x04;
  8.4578 +      write_byte(0x0040, 0x17, shift_flags);
  8.4579 +      if (mf2_state & 0x01) {
  8.4580 +        mf2_flags |= 0x04;
  8.4581 +      } else {
  8.4582 +        mf2_flags |= 0x01;
  8.4583 +        }
  8.4584 +      write_byte(0x0040, 0x18, mf2_flags);
  8.4585 +      break;
  8.4586 +    case 0x9d: /* Ctrl release */
  8.4587 +      shift_flags &= ~0x04;
  8.4588 +      write_byte(0x0040, 0x17, shift_flags);
  8.4589 +      if (mf2_state & 0x01) {
  8.4590 +        mf2_flags &= ~0x04;
  8.4591 +      } else {
  8.4592 +        mf2_flags &= ~0x01;
  8.4593 +        }
  8.4594 +      write_byte(0x0040, 0x18, mf2_flags);
  8.4595 +      break;
  8.4596 +
  8.4597 +    case 0x38: /* Alt press */
  8.4598 +      shift_flags |= 0x08;
  8.4599 +      write_byte(0x0040, 0x17, shift_flags);
  8.4600 +      if (mf2_state & 0x01) {
  8.4601 +        mf2_flags |= 0x08;
  8.4602 +      } else {
  8.4603 +        mf2_flags |= 0x02;
  8.4604 +        }
  8.4605 +      write_byte(0x0040, 0x18, mf2_flags);
  8.4606 +      break;
  8.4607 +    case 0xb8: /* Alt release */
  8.4608 +      shift_flags &= ~0x08;
  8.4609 +      write_byte(0x0040, 0x17, shift_flags);
  8.4610 +      if (mf2_state & 0x01) {
  8.4611 +        mf2_flags &= ~0x08;
  8.4612 +      } else {
  8.4613 +        mf2_flags &= ~0x02;
  8.4614 +        }
  8.4615 +      write_byte(0x0040, 0x18, mf2_flags);
  8.4616 +      break;
  8.4617 +
  8.4618 +    case 0x45: /* Num Lock press */
  8.4619 +      if ((mf2_state & 0x01) == 0) {
  8.4620 +        mf2_flags |= 0x20;
  8.4621 +        write_byte(0x0040, 0x18, mf2_flags);
  8.4622 +        shift_flags ^= 0x20;
  8.4623 +        led_flags ^= 0x02;
  8.4624 +        write_byte(0x0040, 0x17, shift_flags);
  8.4625 +        write_byte(0x0040, 0x97, led_flags);
  8.4626 +        }
  8.4627 +      break;
  8.4628 +    case 0xc5: /* Num Lock release */
  8.4629 +      if ((mf2_state & 0x01) == 0) {
  8.4630 +        mf2_flags &= ~0x20;
  8.4631 +        write_byte(0x0040, 0x18, mf2_flags);
  8.4632 +        }
  8.4633 +      break;
  8.4634 +
  8.4635 +    case 0x46: /* Scroll Lock press */
  8.4636 +      mf2_flags |= 0x10;
  8.4637 +      write_byte(0x0040, 0x18, mf2_flags);
  8.4638 +      shift_flags ^= 0x10;
  8.4639 +      led_flags ^= 0x01;
  8.4640 +      write_byte(0x0040, 0x17, shift_flags);
  8.4641 +      write_byte(0x0040, 0x97, led_flags);
  8.4642 +      break;
  8.4643 +
  8.4644 +    case 0xc6: /* Scroll Lock release */
  8.4645 +      mf2_flags &= ~0x10;
  8.4646 +      write_byte(0x0040, 0x18, mf2_flags);
  8.4647 +      break;
  8.4648 +
  8.4649 +    default:
  8.4650 +      if (scancode & 0x80) return; /* toss key releases ... */
  8.4651 +      if (scancode > MAX_SCAN_CODE) {
  8.4652 +        BX_INFO("KBD: int09h_handler(): unknown scancode read!\n");
  8.4653 +        return;
  8.4654 +        }
  8.4655 +      if (shift_flags & 0x08) { /* ALT */
  8.4656 +        asciicode = scan_to_scanascii[scancode].alt;
  8.4657 +        scancode = scan_to_scanascii[scancode].alt >> 8;
  8.4658 +        }
  8.4659 +      else if (shift_flags & 0x04) { /* CONTROL */
  8.4660 +        asciicode = scan_to_scanascii[scancode].control;
  8.4661 +        scancode = scan_to_scanascii[scancode].control >> 8;
  8.4662 +        }
  8.4663 +      else if (shift_flags & 0x03) { /* LSHIFT + RSHIFT */
  8.4664 +        /* check if lock state should be ignored 
  8.4665 +         * because a SHIFT key are pressed */
  8.4666 +         
  8.4667 +        if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
  8.4668 +          asciicode = scan_to_scanascii[scancode].normal;
  8.4669 +          scancode = scan_to_scanascii[scancode].normal >> 8;
  8.4670 +          }
  8.4671 +        else {
  8.4672 +          asciicode = scan_to_scanascii[scancode].shift;
  8.4673 +          scancode = scan_to_scanascii[scancode].shift >> 8;
  8.4674 +          }
  8.4675 +        }
  8.4676 +      else {
  8.4677 +        /* check if lock is on */
  8.4678 +        if (shift_flags & scan_to_scanascii[scancode].lock_flags) {
  8.4679 +          asciicode = scan_to_scanascii[scancode].shift;
  8.4680 +          scancode = scan_to_scanascii[scancode].shift >> 8;
  8.4681 +          }
  8.4682 +        else {
  8.4683 +          asciicode = scan_to_scanascii[scancode].normal;
  8.4684 +          scancode = scan_to_scanascii[scancode].normal >> 8;
  8.4685 +          }
  8.4686 +        }
  8.4687 +      if (scancode==0 && asciicode==0) {
  8.4688 +        BX_INFO("KBD: int09h_handler(): scancode & asciicode are zero?\n");
  8.4689 +        }
  8.4690 +      enqueue_key(scancode, asciicode);
  8.4691 +      break;
  8.4692 +    }
  8.4693 +  mf2_state &= ~0x01;
  8.4694 +}
  8.4695 +
  8.4696 +  unsigned int
  8.4697 +enqueue_key(scan_code, ascii_code)
  8.4698 +  Bit8u scan_code, ascii_code;
  8.4699 +{
  8.4700 +  Bit16u buffer_start, buffer_end, buffer_head, buffer_tail, temp_tail;
  8.4701 +
  8.4702 +  //BX_INFO("KBD:   enqueue_key() called scan:%02x, ascii:%02x\n",
  8.4703 +  //    scan_code, ascii_code);
  8.4704 +
  8.4705 +#if BX_CPU < 2
  8.4706 +  buffer_start = 0x001E;
  8.4707 +  buffer_end   = 0x003E;
  8.4708 +#else
  8.4709 +  buffer_start = read_word(0x0040, 0x0080);
  8.4710 +  buffer_end   = read_word(0x0040, 0x0082);
  8.4711 +#endif
  8.4712 +
  8.4713 +  buffer_head = read_word(0x0040, 0x001A);
  8.4714 +  buffer_tail = read_word(0x0040, 0x001C);
  8.4715 +
  8.4716 +  temp_tail = buffer_tail;
  8.4717 +  buffer_tail += 2;
  8.4718 +  if (buffer_tail >= buffer_end)
  8.4719 +    buffer_tail = buffer_start;
  8.4720 +
  8.4721 +  if (buffer_tail == buffer_head) {
  8.4722 +    return(0);
  8.4723 +    }
  8.4724 +
  8.4725 +   write_byte(0x0040, temp_tail, ascii_code);
  8.4726 +   write_byte(0x0040, temp_tail+1, scan_code);
  8.4727 +   write_word(0x0040, 0x001C, buffer_tail);
  8.4728 +   return(1);
  8.4729 +}
  8.4730 +
  8.4731 +
  8.4732 +  void
  8.4733 +int74_function(make_farcall, Z, Y, X, status)
  8.4734 +  Bit16u make_farcall, Z, Y, X, status;
  8.4735 +{
  8.4736 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.4737 +  Bit8u  in_byte, index, package_count;
  8.4738 +  Bit8u  mouse_flags_1, mouse_flags_2;
  8.4739 +
  8.4740 +BX_DEBUG_INT74("entering int74_function\n");
  8.4741 +  make_farcall = 0;
  8.4742 +
  8.4743 +  in_byte = inb(0x64);
  8.4744 +  if ( (in_byte & 0x21) != 0x21 ) {
  8.4745 +    return;
  8.4746 +    }
  8.4747 +  in_byte = inb(0x60);
  8.4748 +BX_DEBUG_INT74("int74: read byte %02x\n", in_byte);
  8.4749 +
  8.4750 +  mouse_flags_1 = read_byte(ebda_seg, 0x0026);
  8.4751 +  mouse_flags_2 = read_byte(ebda_seg, 0x0027);
  8.4752 +
  8.4753 +  if ( (mouse_flags_2 & 0x80) != 0x80 ) {
  8.4754 +      //    BX_PANIC("int74_function:\n");
  8.4755 +      return;
  8.4756 +    }
  8.4757 +
  8.4758 +  package_count = mouse_flags_2 & 0x07;
  8.4759 +  index = mouse_flags_1 & 0x07;
  8.4760 +  write_byte(ebda_seg, 0x28 + index, in_byte);
  8.4761 +
  8.4762 +  if ( (index+1) >= package_count ) {
  8.4763 +BX_DEBUG_INT74("int74_function: make_farcall=1\n");
  8.4764 +    status = read_byte(ebda_seg, 0x0028 + 0);
  8.4765 +    X      = read_byte(ebda_seg, 0x0028 + 1);
  8.4766 +    Y      = read_byte(ebda_seg, 0x0028 + 2);
  8.4767 +    Z      = 0;
  8.4768 +    mouse_flags_1 = 0;
  8.4769 +    // check if far call handler installed
  8.4770 +    if (mouse_flags_2 & 0x80)
  8.4771 +      make_farcall = 1;
  8.4772 +    }
  8.4773 +  else {
  8.4774 +    mouse_flags_1++;
  8.4775 +    }
  8.4776 +  write_byte(ebda_seg, 0x0026, mouse_flags_1);
  8.4777 +}
  8.4778 +
  8.4779 +#define SET_DISK_RET_STATUS(status) write_byte(0x0040, 0x0074, status)
  8.4780 +
  8.4781 +#if BX_USE_ATADRV
  8.4782 +
  8.4783 +  void
  8.4784 +int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
  8.4785 +  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
  8.4786 +{
  8.4787 +  Bit32u lba;
  8.4788 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.4789 +  Bit16u cylinder, head, sector;
  8.4790 +  Bit16u segment, offset;
  8.4791 +  Bit16u npc, nph, npspt, nlc, nlh, nlspt;
  8.4792 +  Bit16u size, count;
  8.4793 +  Bit8u  device, status;
  8.4794 +
  8.4795 +  BX_DEBUG_INT13_HD("int13_harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
  8.4796 +
  8.4797 +  write_byte(0x0040, 0x008e, 0);  // clear completion flag
  8.4798 +
  8.4799 +  // basic check : device has to be defined
  8.4800 +  if ( (GET_ELDL() < 0x80) || (GET_ELDL() >= 0x80 + BX_MAX_ATA_DEVICES) ) {
  8.4801 +    BX_INFO("int13_harddisk: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
  8.4802 +    goto int13_fail;
  8.4803 +    }
  8.4804 +
  8.4805 +  // Get the ata channel
  8.4806 +  device=read_byte(ebda_seg,&EbdaData->ata.hdidmap[GET_ELDL()-0x80]);
  8.4807 +
  8.4808 +  // basic check : device has to be valid 
  8.4809 +  if (device >= BX_MAX_ATA_DEVICES) {
  8.4810 +    BX_INFO("int13_harddisk: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
  8.4811 +    goto int13_fail;
  8.4812 +    }
  8.4813 +  
  8.4814 +  switch (GET_AH()) {
  8.4815 +
  8.4816 +    case 0x00: /* disk controller reset */
  8.4817 +      ata_reset (device);
  8.4818 +      goto int13_success;
  8.4819 +      break;
  8.4820 +
  8.4821 +    case 0x01: /* read disk status */
  8.4822 +      status = read_byte(0x0040, 0x0074);
  8.4823 +      SET_AH(status);
  8.4824 +      SET_DISK_RET_STATUS(0);
  8.4825 +      /* set CF if error status read */
  8.4826 +      if (status) goto int13_fail_nostatus;
  8.4827 +      else        goto int13_success_noah;
  8.4828 +      break;
  8.4829 +
  8.4830 +    case 0x02: // read disk sectors
  8.4831 +    case 0x03: // write disk sectors 
  8.4832 +    case 0x04: // verify disk sectors
  8.4833 +
  8.4834 +      count       = GET_AL();
  8.4835 +      cylinder    = GET_CH();
  8.4836 +      cylinder   |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
  8.4837 +      sector      = (GET_CL() & 0x3f);
  8.4838 +      head        = GET_DH();
  8.4839 +
  8.4840 +      segment = ES;
  8.4841 +      offset  = BX;
  8.4842 +
  8.4843 +      if ( (count > 128) || (count == 0) ) {
  8.4844 +        BX_INFO("int13_harddisk: function %02x, count out of range!\n",GET_AH());
  8.4845 +        goto int13_fail;
  8.4846 +        }
  8.4847 +
  8.4848 +      nlc   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
  8.4849 +      nlh   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
  8.4850 +      nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
  8.4851 +
  8.4852 +      // sanity check on cyl heads, sec
  8.4853 +      if( (cylinder >= nlc) || (head >= nlh) || (sector > nlspt )) {
  8.4854 +        BX_INFO("int13_harddisk: function %02x, parameters out of range %04x/%04x/%04x!\n", GET_AH(), cylinder, head, sector);
  8.4855 +        goto int13_fail;
  8.4856 +        }
  8.4857 +      
  8.4858 +      // FIXME verify
  8.4859 +      if ( GET_AH() == 0x04 ) goto int13_success;
  8.4860 +
  8.4861 +      nph   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
  8.4862 +      npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
  8.4863 +
  8.4864 +      // if needed, translate lchs to lba, and execute command
  8.4865 +      if ( (nph != nlh) || (npspt != nlspt)) {
  8.4866 +        lba = ((((Bit32u)cylinder * (Bit32u)nlh) + (Bit32u)head) * (Bit32u)nlspt) + (Bit32u)sector - 1;
  8.4867 +        sector = 0; // this forces the command to be lba
  8.4868 +        }
  8.4869 +
  8.4870 +      if ( GET_AH() == 0x02 )
  8.4871 +        status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, cylinder, head, sector, lba, segment, offset);
  8.4872 +      else
  8.4873 +        status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, cylinder, head, sector, lba, segment, offset);
  8.4874 +
  8.4875 +      // Set nb of sector transferred
  8.4876 +      SET_AL(read_word(ebda_seg, &EbdaData->ata.trsfsectors));
  8.4877 +
  8.4878 +      if (status != 0) {
  8.4879 +        BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
  8.4880 +        SET_AH(0x0c);
  8.4881 +        goto int13_fail_noah;
  8.4882 +        }
  8.4883 +
  8.4884 +      goto int13_success;
  8.4885 +      break;
  8.4886 +
  8.4887 +    case 0x05: /* format disk track */
  8.4888 +      BX_INFO("format disk track called\n");
  8.4889 +      goto int13_success;
  8.4890 +      return;
  8.4891 +      break;
  8.4892 +
  8.4893 +    case 0x08: /* read disk drive parameters */
  8.4894 +      
  8.4895 +      // Get logical geometry from table
  8.4896 +      nlc   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.cylinders);
  8.4897 +      nlh   = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.heads);
  8.4898 +      nlspt = read_word(ebda_seg, &EbdaData->ata.devices[device].lchs.spt);
  8.4899 +      count = read_byte(ebda_seg, &EbdaData->ata.hdcount);
  8.4900 +
  8.4901 +      nlc = nlc - 2; /* 0 based , last sector not used */
  8.4902 +      SET_AL(0);
  8.4903 +      SET_CH(nlc & 0xff);
  8.4904 +      SET_CL(((nlc >> 2) & 0xc0) | (nlspt & 0x3f));
  8.4905 +      SET_DH(nlh - 1);
  8.4906 +      SET_DL(count); /* FIXME returns 0, 1, or n hard drives */
  8.4907 +
  8.4908 +      // FIXME should set ES & DI
  8.4909 +      
  8.4910 +      goto int13_success;
  8.4911 +      break;
  8.4912 +
  8.4913 +    case 0x10: /* check drive ready */
  8.4914 +      // should look at 40:8E also???
  8.4915 +      
  8.4916 +      // Read the status from controller
  8.4917 +      status = inb(read_word(ebda_seg, &EbdaData->ata.channels[device/2].iobase1) + ATA_CB_STAT);
  8.4918 +      if ( (status & ( ATA_CB_STAT_BSY | ATA_CB_STAT_RDY )) == ATA_CB_STAT_RDY ) {
  8.4919 +        goto int13_success;
  8.4920 +        }
  8.4921 +      else {
  8.4922 +        SET_AH(0xAA);
  8.4923 +        goto int13_fail_noah;
  8.4924 +        }
  8.4925 +      break;
  8.4926 +
  8.4927 +    case 0x15: /* read disk drive size */
  8.4928 +
  8.4929 +      // Get physical geometry from table
  8.4930 +      npc   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
  8.4931 +      nph   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
  8.4932 +      npspt = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
  8.4933 +
  8.4934 +      // Compute sector count seen by int13
  8.4935 +      lba = (Bit32u)(npc - 1) * (Bit32u)nph * (Bit32u)npspt;
  8.4936 +      CX = lba >> 16;
  8.4937 +      DX = lba & 0xffff;
  8.4938 +
  8.4939 +      SET_AH(3);  // hard disk accessible
  8.4940 +      goto int13_success_noah;
  8.4941 +      break;
  8.4942 +
  8.4943 +    case 0x41: // IBM/MS installation check
  8.4944 +      BX=0xaa55;     // install check
  8.4945 +      SET_AH(0x30);  // EDD 3.0
  8.4946 +      CX=0x0007;     // ext disk access and edd, removable supported
  8.4947 +      goto int13_success_noah;
  8.4948 +      break;
  8.4949 +
  8.4950 +    case 0x42: // IBM/MS extended read
  8.4951 +    case 0x43: // IBM/MS extended write
  8.4952 +    case 0x44: // IBM/MS verify
  8.4953 +    case 0x47: // IBM/MS extended seek
  8.4954 +
  8.4955 +      count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
  8.4956 +      segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
  8.4957 +      offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
  8.4958 + 
  8.4959 +      // Can't use 64 bits lba
  8.4960 +      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
  8.4961 +      if (lba != 0L) {
  8.4962 +        BX_PANIC("int13_harddisk: function %02x. Can't use 64bits lba\n",GET_AH());
  8.4963 +        goto int13_fail;
  8.4964 +        }
  8.4965 +
  8.4966 +      // Get 32 bits lba and check
  8.4967 +      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
  8.4968 +      if (lba >= read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors) ) {
  8.4969 +        BX_INFO("int13_harddisk: function %02x. LBA out of range\n",GET_AH());
  8.4970 +        goto int13_fail;
  8.4971 +        }
  8.4972 +
  8.4973 +      // If verify or seek
  8.4974 +      if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
  8.4975 +        goto int13_success;
  8.4976 +      
  8.4977 +      // Execute the command
  8.4978 +      if ( GET_AH() == 0x42 )
  8.4979 +        status=ata_cmd_data_in(device, ATA_CMD_READ_SECTORS, count, 0, 0, 0, lba, segment, offset);
  8.4980 +      else
  8.4981 +        status=ata_cmd_data_out(device, ATA_CMD_WRITE_SECTORS, count, 0, 0, 0, lba, segment, offset);
  8.4982 +
  8.4983 +      count=read_word(ebda_seg, &EbdaData->ata.trsfsectors);
  8.4984 +      write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
  8.4985 +
  8.4986 +      if (status != 0) {
  8.4987 +        BX_INFO("int13_harddisk: function %02x, error %02x !\n",GET_AH(),status);
  8.4988 +        SET_AH(0x0c);
  8.4989 +        goto int13_fail_noah;
  8.4990 +        }
  8.4991 +
  8.4992 +      goto int13_success;
  8.4993 +      break;
  8.4994 +
  8.4995 +    case 0x45: // IBM/MS lock/unlock drive
  8.4996 +    case 0x49: // IBM/MS extended media change
  8.4997 +      goto int13_success;    // Always success for HD
  8.4998 +      break;
  8.4999 +      
  8.5000 +    case 0x46: // IBM/MS eject media
  8.5001 +      SET_AH(0xb2);          // Volume Not Removable
  8.5002 +      goto int13_fail_noah;  // Always fail for HD
  8.5003 +      break;
  8.5004 +
  8.5005 +    case 0x48: // IBM/MS get drive parameters
  8.5006 +      size=read_word(DS,SI+(Bit16u)&Int13DPT->size);
  8.5007 +
  8.5008 +      // Buffer is too small
  8.5009 +      if(size < 0x1a) 
  8.5010 +        goto int13_fail;
  8.5011 +
  8.5012 +      // EDD 1.x
  8.5013 +      if(size >= 0x1a) {
  8.5014 +        Bit16u   blksize;
  8.5015 +
  8.5016 +        npc     = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.cylinders);
  8.5017 +        nph     = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.heads);
  8.5018 +        npspt   = read_word(ebda_seg, &EbdaData->ata.devices[device].pchs.spt);
  8.5019 +        lba     = read_dword(ebda_seg, &EbdaData->ata.devices[device].sectors);
  8.5020 +        blksize = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
  8.5021 +
  8.5022 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
  8.5023 +        write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x02); // geometry is valid
  8.5024 +        write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, (Bit32u)npc);
  8.5025 +        write_dword(DS, SI+(Bit16u)&Int13DPT->heads, (Bit32u)nph);
  8.5026 +        write_dword(DS, SI+(Bit16u)&Int13DPT->spt, (Bit32u)npspt);
  8.5027 +        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, lba);  // FIXME should be Bit64
  8.5028 +        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0L);  
  8.5029 +        write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);  
  8.5030 +        }
  8.5031 +
  8.5032 +      // EDD 2.x
  8.5033 +      if(size >= 0x1e) {
  8.5034 +        Bit8u  channel, dev, irq, mode, checksum, i, translation;
  8.5035 +        Bit16u iobase1, iobase2, options;
  8.5036 +
  8.5037 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
  8.5038 +
  8.5039 +        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);  
  8.5040 +        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);  
  8.5041 +
  8.5042 +        // Fill in dpte
  8.5043 +        channel = device / 2;
  8.5044 +        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
  8.5045 +        iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
  8.5046 +        irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
  8.5047 +        mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
  8.5048 +        translation = read_byte(ebda_seg, &EbdaData->ata.devices[device].translation);
  8.5049 +
  8.5050 +        options  = (translation==ATA_TRANSLATION_NONE?0:1<<3); // chs translation
  8.5051 +        options |= (1<<4); // lba translation
  8.5052 +        options |= (mode==ATA_MODE_PIO32?1:0<<7);
  8.5053 +        options |= (translation==ATA_TRANSLATION_LBA?1:0<<9); 
  8.5054 +        options |= (translation==ATA_TRANSLATION_RECHS?3:0<<9); 
  8.5055 +
  8.5056 +        write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
  8.5057 +        write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
  8.5058 +        write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
  8.5059 +        write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
  8.5060 +        write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
  8.5061 +        write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
  8.5062 +        write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
  8.5063 +        write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
  8.5064 +        write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
  8.5065 +        write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
  8.5066 +        write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
  8.5067 + 
  8.5068 +        checksum=0;
  8.5069 +        for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
  8.5070 +        checksum = ~checksum;
  8.5071 +        write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
  8.5072 +        }
  8.5073 +
  8.5074 +      // EDD 3.x
  8.5075 +      if(size >= 0x42) {
  8.5076 +        Bit8u channel, iface, checksum, i;
  8.5077 +        Bit16u iobase1;
  8.5078 +
  8.5079 +        channel = device / 2;
  8.5080 +        iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
  8.5081 +        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
  8.5082 +
  8.5083 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
  8.5084 +        write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
  8.5085 +        write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
  8.5086 +        write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
  8.5087 +        write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
  8.5088 +
  8.5089 +        if (iface==ATA_IFACE_ISA) {
  8.5090 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
  8.5091 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
  8.5092 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
  8.5093 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
  8.5094 +          }
  8.5095 +        else { 
  8.5096 +          // FIXME PCI
  8.5097 +          }
  8.5098 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
  8.5099 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
  8.5100 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
  8.5101 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
  8.5102 +
  8.5103 +        if (iface==ATA_IFACE_ISA) {
  8.5104 +          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
  8.5105 +          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
  8.5106 +          write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
  8.5107 +          }
  8.5108 +        else { 
  8.5109 +          // FIXME PCI
  8.5110 +          }
  8.5111 +        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
  8.5112 +        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
  8.5113 +        write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
  8.5114 +        write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
  8.5115 +
  8.5116 +        checksum=0;
  8.5117 +        for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
  8.5118 +        checksum = ~checksum;
  8.5119 +        write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
  8.5120 +        }
  8.5121 +
  8.5122 +      goto int13_success;
  8.5123 +      break;
  8.5124 +
  8.5125 +    case 0x4e: // // IBM/MS set hardware configuration
  8.5126 +      // DMA, prefetch, PIO maximum not supported
  8.5127 +      switch (GET_AL()) {
  8.5128 +        case 0x01:
  8.5129 +        case 0x03:
  8.5130 +        case 0x04:
  8.5131 +        case 0x06:
  8.5132 +          goto int13_success;
  8.5133 +          break;
  8.5134 +        default :
  8.5135 +          goto int13_fail;
  8.5136 +        }
  8.5137 +      break;
  8.5138 +
  8.5139 +    case 0x09: /* initialize drive parameters */
  8.5140 +    case 0x0c: /* seek to specified cylinder */
  8.5141 +    case 0x0d: /* alternate disk reset */
  8.5142 +    case 0x11: /* recalibrate */
  8.5143 +    case 0x14: /* controller internal diagnostic */
  8.5144 +      BX_INFO("int13h_harddisk function %02xh unimplemented, returns success\n", GET_AH());
  8.5145 +      goto int13_success;
  8.5146 +      break;
  8.5147 +
  8.5148 +    case 0x0a: /* read disk sectors with ECC */
  8.5149 +    case 0x0b: /* write disk sectors with ECC */
  8.5150 +    case 0x18: // set media type for format
  8.5151 +    case 0x50: // IBM/MS send packet command
  8.5152 +    default:
  8.5153 +      BX_INFO("int13_harddisk function %02xh unsupported, returns fail\n", GET_AH());
  8.5154 +      goto int13_fail;
  8.5155 +      break;
  8.5156 +    }
  8.5157 +
  8.5158 +int13_fail:
  8.5159 +    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
  8.5160 +int13_fail_noah:
  8.5161 +    SET_DISK_RET_STATUS(GET_AH());
  8.5162 +int13_fail_nostatus:
  8.5163 +    SET_CF();     // error occurred
  8.5164 +    return;
  8.5165 +
  8.5166 +int13_success:
  8.5167 +    SET_AH(0x00); // no error
  8.5168 +int13_success_noah:
  8.5169 +    SET_DISK_RET_STATUS(0x00);
  8.5170 +    CLEAR_CF();   // no error
  8.5171 +    return;
  8.5172 +}
  8.5173 +
  8.5174 +// ---------------------------------------------------------------------------
  8.5175 +// Start of int13 for cdrom
  8.5176 +// ---------------------------------------------------------------------------
  8.5177 +
  8.5178 +  void
  8.5179 +int13_cdrom(EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
  8.5180 +  Bit16u EHBX, DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
  8.5181 +{
  8.5182 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.5183 +  Bit8u  device, status, locks;
  8.5184 +  Bit8u  atacmd[12];
  8.5185 +  Bit32u lba;
  8.5186 +  Bit16u count, segment, offset, i, size;
  8.5187 +
  8.5188 +  BX_DEBUG_INT13_CD("int13_cdrom: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
  8.5189 +  // BX_DEBUG_INT13_CD("int13_cdrom: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
  8.5190 +  
  8.5191 +  SET_DISK_RET_STATUS(0x00);
  8.5192 +
  8.5193 +  /* basic check : device should be 0xE0+ */
  8.5194 +  if( (GET_ELDL() < 0xE0) || (GET_ELDL() >= 0xE0+BX_MAX_ATA_DEVICES) ) {
  8.5195 +    BX_INFO("int13_cdrom: function %02x, ELDL out of range %02x\n", GET_AH(), GET_ELDL());
  8.5196 +    goto int13_fail;
  8.5197 +    }
  8.5198 +
  8.5199 +  // Get the ata channel
  8.5200 +  device=read_byte(ebda_seg,&EbdaData->ata.cdidmap[GET_ELDL()-0xE0]);
  8.5201 +
  8.5202 +  /* basic check : device has to be valid  */
  8.5203 +  if (device >= BX_MAX_ATA_DEVICES) {
  8.5204 +    BX_INFO("int13_cdrom: function %02x, unmapped device for ELDL=%02x\n", GET_AH(), GET_ELDL());
  8.5205 +    goto int13_fail;
  8.5206 +    }
  8.5207 +  
  8.5208 +  switch (GET_AH()) {
  8.5209 +
  8.5210 +    // all those functions return SUCCESS
  8.5211 +    case 0x00: /* disk controller reset */
  8.5212 +    case 0x09: /* initialize drive parameters */
  8.5213 +    case 0x0c: /* seek to specified cylinder */
  8.5214 +    case 0x0d: /* alternate disk reset */  
  8.5215 +    case 0x10: /* check drive ready */    
  8.5216 +    case 0x11: /* recalibrate */      
  8.5217 +    case 0x14: /* controller internal diagnostic */
  8.5218 +    case 0x16: /* detect disk change */
  8.5219 +      goto int13_success;
  8.5220 +      break;
  8.5221 +
  8.5222 +    // all those functions return disk write-protected
  8.5223 +    case 0x03: /* write disk sectors */
  8.5224 +    case 0x05: /* format disk track */
  8.5225 +    case 0x43: // IBM/MS extended write
  8.5226 +      SET_AH(0x03);
  8.5227 +      goto int13_fail_noah;
  8.5228 +      break;
  8.5229 +
  8.5230 +    case 0x01: /* read disk status */
  8.5231 +      status = read_byte(0x0040, 0x0074);
  8.5232 +      SET_AH(status);
  8.5233 +      SET_DISK_RET_STATUS(0);
  8.5234 +
  8.5235 +      /* set CF if error status read */
  8.5236 +      if (status) goto int13_fail_nostatus;
  8.5237 +      else        goto int13_success_noah;
  8.5238 +      break;      
  8.5239 +
  8.5240 +    case 0x15: /* read disk drive size */
  8.5241 +      SET_AH(0x02);
  8.5242 +      goto int13_fail_noah;
  8.5243 +      break;
  8.5244 +
  8.5245 +    case 0x41: // IBM/MS installation check
  8.5246 +      BX=0xaa55;     // install check
  8.5247 +      SET_AH(0x30);  // EDD 2.1
  8.5248 +      CX=0x0007;     // ext disk access, removable and edd
  8.5249 +      goto int13_success_noah;
  8.5250 +      break;
  8.5251 +
  8.5252 +    case 0x42: // IBM/MS extended read
  8.5253 +    case 0x44: // IBM/MS verify sectors
  8.5254 +    case 0x47: // IBM/MS extended seek
  8.5255 +       
  8.5256 +      count=read_word(DS, SI+(Bit16u)&Int13Ext->count);
  8.5257 +      segment=read_word(DS, SI+(Bit16u)&Int13Ext->segment);
  8.5258 +      offset=read_word(DS, SI+(Bit16u)&Int13Ext->offset);
  8.5259 + 
  8.5260 +      // Can't use 64 bits lba
  8.5261 +      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba2);
  8.5262 +      if (lba != 0L) {
  8.5263 +        BX_PANIC("int13_cdrom: function %02x. Can't use 64bits lba\n",GET_AH());
  8.5264 +        goto int13_fail;
  8.5265 +        }
  8.5266 +
  8.5267 +      // Get 32 bits lba 
  8.5268 +      lba=read_dword(DS, SI+(Bit16u)&Int13Ext->lba1);
  8.5269 +
  8.5270 +      // If verify or seek
  8.5271 +      if (( GET_AH() == 0x44 ) || ( GET_AH() == 0x47 ))
  8.5272 +        goto int13_success;
  8.5273 +      
  8.5274 +      memsetb(get_SS(),atacmd,0,12);
  8.5275 +      atacmd[0]=0x28;                      // READ command
  8.5276 +      atacmd[7]=(count & 0xff00) >> 8;     // Sectors
  8.5277 +      atacmd[8]=(count & 0x00ff);          // Sectors
  8.5278 +      atacmd[2]=(lba & 0xff000000) >> 24;  // LBA
  8.5279 +      atacmd[3]=(lba & 0x00ff0000) >> 16;
  8.5280 +      atacmd[4]=(lba & 0x0000ff00) >> 8;
  8.5281 +      atacmd[5]=(lba & 0x000000ff);
  8.5282 +      status = ata_cmd_packet(device, 12, get_SS(), atacmd, 0, count*2048L, ATA_DATA_IN, segment,offset); 
  8.5283 +
  8.5284 +      count = (Bit16u)(read_dword(ebda_seg, &EbdaData->ata.trsfbytes) >> 11);
  8.5285 +      write_word(DS, SI+(Bit16u)&Int13Ext->count, count);
  8.5286 +
  8.5287 +      if (status != 0) {
  8.5288 +        BX_INFO("int13_cdrom: function %02x, status %02x !\n",GET_AH(),status);
  8.5289 +        SET_AH(0x0c);
  8.5290 +        goto int13_fail_noah;
  8.5291 +        }
  8.5292 +
  8.5293 +      goto int13_success;
  8.5294 +      break;
  8.5295 +
  8.5296 +    case 0x45: // IBM/MS lock/unlock drive
  8.5297 +      if (GET_AL() > 2) goto int13_fail;
  8.5298 +
  8.5299 +      locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
  8.5300 +
  8.5301 +      switch (GET_AL()) {
  8.5302 +        case 0 :  // lock
  8.5303 +          if (locks == 0xff) {
  8.5304 +            SET_AH(0xb4);
  8.5305 +            SET_AL(1);
  8.5306 +            goto int13_fail_noah;
  8.5307 +            }
  8.5308 +          write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, ++locks);
  8.5309 +          SET_AL(1);
  8.5310 +          break;
  8.5311 +        case 1 :  // unlock
  8.5312 +          if (locks == 0x00) {
  8.5313 +            SET_AH(0xb0);
  8.5314 +            SET_AL(0);
  8.5315 +            goto int13_fail_noah;
  8.5316 +            }
  8.5317 +          write_byte(ebda_seg, &EbdaData->ata.devices[device].lock, --locks);
  8.5318 +          SET_AL(locks==0?0:1);
  8.5319 +          break;
  8.5320 +        case 2 :  // status
  8.5321 +          SET_AL(locks==0?0:1);
  8.5322 +          break;
  8.5323 +        }
  8.5324 +      goto int13_success;
  8.5325 +      break;
  8.5326 +
  8.5327 +    case 0x46: // IBM/MS eject media
  8.5328 +      locks = read_byte(ebda_seg, &EbdaData->ata.devices[device].lock);
  8.5329 +      
  8.5330 +      if (locks != 0) {
  8.5331 +        SET_AH(0xb1); // media locked
  8.5332 +        goto int13_fail_noah;
  8.5333 +        }
  8.5334 +      // FIXME should handle 0x31 no media in device
  8.5335 +      // FIXME should handle 0xb5 valid request failed
  8.5336 +    
  8.5337 +      // Call removable media eject
  8.5338 +      ASM_START
  8.5339 +        push bp
  8.5340 +        mov  bp, sp
  8.5341 +
  8.5342 +        mov ah, #0x52
  8.5343 +        int 15
  8.5344 +        mov _int13_cdrom.status + 2[bp], ah
  8.5345 +        jnc int13_cdrom_rme_end
  8.5346 +        mov _int13_cdrom.status, #1
  8.5347 +int13_cdrom_rme_end:
  8.5348 +        pop bp
  8.5349 +      ASM_END
  8.5350 +
  8.5351 +      if (status != 0) {
  8.5352 +        SET_AH(0xb1); // media locked
  8.5353 +        goto int13_fail_noah;
  8.5354 +      }
  8.5355 +
  8.5356 +      goto int13_success;
  8.5357 +      break;
  8.5358 +
  8.5359 +    case 0x48: // IBM/MS get drive parameters
  8.5360 +      size = read_word(DS,SI+(Bit16u)&Int13Ext->size);
  8.5361 +
  8.5362 +      // Buffer is too small
  8.5363 +      if(size < 0x1a) 
  8.5364 +        goto int13_fail;
  8.5365 +
  8.5366 +      // EDD 1.x
  8.5367 +      if(size >= 0x1a) {
  8.5368 +        Bit16u   cylinders, heads, spt, blksize;
  8.5369 +
  8.5370 +        blksize   = read_word(ebda_seg, &EbdaData->ata.devices[device].blksize);
  8.5371 +
  8.5372 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1a);
  8.5373 +        write_word(DS, SI+(Bit16u)&Int13DPT->infos, 0x74); // removable, media change, lockable, max values
  8.5374 +        write_dword(DS, SI+(Bit16u)&Int13DPT->cylinders, 0xffffffff);
  8.5375 +        write_dword(DS, SI+(Bit16u)&Int13DPT->heads, 0xffffffff);
  8.5376 +        write_dword(DS, SI+(Bit16u)&Int13DPT->spt, 0xffffffff);
  8.5377 +        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count1, 0xffffffff);  // FIXME should be Bit64
  8.5378 +        write_dword(DS, SI+(Bit16u)&Int13DPT->sector_count2, 0xffffffff);  
  8.5379 +        write_word(DS, SI+(Bit16u)&Int13DPT->blksize, blksize);  
  8.5380 +        }
  8.5381 +
  8.5382 +      // EDD 2.x
  8.5383 +      if(size >= 0x1e) {
  8.5384 +        Bit8u  channel, dev, irq, mode, checksum, i;
  8.5385 +        Bit16u iobase1, iobase2, options;
  8.5386 +
  8.5387 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x1e);
  8.5388 +
  8.5389 +        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_segment, ebda_seg);  
  8.5390 +        write_word(DS, SI+(Bit16u)&Int13DPT->dpte_offset, &EbdaData->ata.dpte);  
  8.5391 +
  8.5392 +        // Fill in dpte
  8.5393 +        channel = device / 2;
  8.5394 +        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
  8.5395 +        iobase2 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase2);
  8.5396 +        irq = read_byte(ebda_seg, &EbdaData->ata.channels[channel].irq);
  8.5397 +        mode = read_byte(ebda_seg, &EbdaData->ata.devices[device].mode);
  8.5398 +
  8.5399 +        // FIXME atapi device
  8.5400 +        options  = (1<<4); // lba translation
  8.5401 +        options |= (1<<5); // removable device
  8.5402 +        options |= (1<<6); // atapi device
  8.5403 +        options |= (mode==ATA_MODE_PIO32?1:0<<7);
  8.5404 +
  8.5405 +        write_word(ebda_seg, &EbdaData->ata.dpte.iobase1, iobase1);
  8.5406 +        write_word(ebda_seg, &EbdaData->ata.dpte.iobase2, iobase2);
  8.5407 +        write_byte(ebda_seg, &EbdaData->ata.dpte.prefix, (0xe | (device % 2))<<4 );
  8.5408 +        write_byte(ebda_seg, &EbdaData->ata.dpte.unused, 0xcb );
  8.5409 +        write_byte(ebda_seg, &EbdaData->ata.dpte.irq, irq );
  8.5410 +        write_byte(ebda_seg, &EbdaData->ata.dpte.blkcount, 1 );
  8.5411 +        write_byte(ebda_seg, &EbdaData->ata.dpte.dma, 0 );
  8.5412 +        write_byte(ebda_seg, &EbdaData->ata.dpte.pio, 0 );
  8.5413 +        write_word(ebda_seg, &EbdaData->ata.dpte.options, options);
  8.5414 +        write_word(ebda_seg, &EbdaData->ata.dpte.reserved, 0);
  8.5415 +        write_byte(ebda_seg, &EbdaData->ata.dpte.revision, 0x11);
  8.5416 +
  8.5417 +        checksum=0;
  8.5418 +        for (i=0; i<15; i++) checksum+=read_byte(ebda_seg, (&EbdaData->ata.dpte) + i);
  8.5419 +        checksum = ~checksum;
  8.5420 +        write_byte(ebda_seg, &EbdaData->ata.dpte.checksum, checksum);
  8.5421 +        }
  8.5422 +
  8.5423 +      // EDD 3.x
  8.5424 +      if(size >= 0x42) {
  8.5425 +        Bit8u channel, iface, checksum, i;
  8.5426 +        Bit16u iobase1;
  8.5427 +
  8.5428 +        channel = device / 2;
  8.5429 +        iface = read_byte(ebda_seg, &EbdaData->ata.channels[channel].iface);
  8.5430 +        iobase1 = read_word(ebda_seg, &EbdaData->ata.channels[channel].iobase1);
  8.5431 +
  8.5432 +        write_word(DS, SI+(Bit16u)&Int13DPT->size, 0x42);
  8.5433 +        write_word(DS, SI+(Bit16u)&Int13DPT->key, 0xbedd);
  8.5434 +        write_byte(DS, SI+(Bit16u)&Int13DPT->dpi_length, 0x24);
  8.5435 +        write_byte(DS, SI+(Bit16u)&Int13DPT->reserved1, 0);
  8.5436 +        write_word(DS, SI+(Bit16u)&Int13DPT->reserved2, 0);
  8.5437 +
  8.5438 +        if (iface==ATA_IFACE_ISA) {
  8.5439 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[0], 'I');
  8.5440 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[1], 'S');
  8.5441 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[2], 'A');
  8.5442 +          write_byte(DS, SI+(Bit16u)&Int13DPT->host_bus[3], 0);
  8.5443 +          }
  8.5444 +        else { 
  8.5445 +          // FIXME PCI
  8.5446 +          }
  8.5447 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[0], 'A');
  8.5448 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[1], 'T');
  8.5449 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[2], 'A');
  8.5450 +        write_byte(DS, SI+(Bit16u)&Int13DPT->iface_type[3], 0);
  8.5451 +
  8.5452 +        if (iface==ATA_IFACE_ISA) {
  8.5453 +          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[0], iobase1);
  8.5454 +          write_word(DS, SI+(Bit16u)&Int13DPT->iface_path[2], 0);
  8.5455 +          write_dword(DS, SI+(Bit16u)&Int13DPT->iface_path[4], 0L);
  8.5456 +          }
  8.5457 +        else { 
  8.5458 +          // FIXME PCI
  8.5459 +          }
  8.5460 +        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[0], device%2);
  8.5461 +        write_byte(DS, SI+(Bit16u)&Int13DPT->device_path[1], 0);
  8.5462 +        write_word(DS, SI+(Bit16u)&Int13DPT->device_path[2], 0);
  8.5463 +        write_dword(DS, SI+(Bit16u)&Int13DPT->device_path[4], 0L);
  8.5464 +
  8.5465 +        checksum=0;
  8.5466 +        for (i=30; i<64; i++) checksum+=read_byte(DS, SI + i);
  8.5467 +        checksum = ~checksum;
  8.5468 +        write_byte(DS, SI+(Bit16u)&Int13DPT->checksum, checksum);
  8.5469 +        }
  8.5470 +
  8.5471 +      goto int13_success;
  8.5472 +      break;
  8.5473 +
  8.5474 +    case 0x49: // IBM/MS extended media change
  8.5475 +      // always send changed ??
  8.5476 +      SET_AH(06);
  8.5477 +      goto int13_fail_nostatus;
  8.5478 +      break;
  8.5479 +      
  8.5480 +    case 0x4e: // // IBM/MS set hardware configuration
  8.5481 +      // DMA, prefetch, PIO maximum not supported
  8.5482 +      switch (GET_AL()) {
  8.5483 +        case 0x01:
  8.5484 +        case 0x03:
  8.5485 +        case 0x04:
  8.5486 +        case 0x06:
  8.5487 +          goto int13_success;
  8.5488 +          break;
  8.5489 +        default :
  8.5490 +          goto int13_fail;
  8.5491 +        }
  8.5492 +      break;
  8.5493 +
  8.5494 +    // all those functions return unimplemented
  8.5495 +    case 0x02: /* read sectors */
  8.5496 +    case 0x04: /* verify sectors */
  8.5497 +    case 0x08: /* read disk drive parameters */
  8.5498 +    case 0x0a: /* read disk sectors with ECC */
  8.5499 +    case 0x0b: /* write disk sectors with ECC */
  8.5500 +    case 0x18: /* set media type for format */
  8.5501 +    case 0x50: // ? - send packet command
  8.5502 +    default:
  8.5503 +      BX_INFO("int13_cdrom: unsupported AH=%02x\n", GET_AH());
  8.5504 +      goto int13_fail;
  8.5505 +      break;
  8.5506 +    }
  8.5507 +
  8.5508 +int13_fail:
  8.5509 +    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
  8.5510 +int13_fail_noah:
  8.5511 +    SET_DISK_RET_STATUS(GET_AH());
  8.5512 +int13_fail_nostatus:
  8.5513 +    SET_CF();     // error occurred
  8.5514 +    return;
  8.5515 +
  8.5516 +int13_success:
  8.5517 +    SET_AH(0x00); // no error
  8.5518 +int13_success_noah:
  8.5519 +    SET_DISK_RET_STATUS(0x00);
  8.5520 +    CLEAR_CF();   // no error
  8.5521 +    return;
  8.5522 +}
  8.5523 +
  8.5524 +// ---------------------------------------------------------------------------
  8.5525 +// End of int13 for cdrom
  8.5526 +// ---------------------------------------------------------------------------
  8.5527 +
  8.5528 +#if BX_ELTORITO_BOOT
  8.5529 +// ---------------------------------------------------------------------------
  8.5530 +// Start of int13 for eltorito functions
  8.5531 +// ---------------------------------------------------------------------------
  8.5532 +
  8.5533 +  void
  8.5534 +int13_eltorito(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
  8.5535 +  Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
  8.5536 +{
  8.5537 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.5538 +
  8.5539 +  BX_DEBUG_INT13_ET("int13_eltorito: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
  8.5540 +  // BX_DEBUG_INT13_ET("int13_eltorito: SS=%04x DS=%04x ES=%04x DI=%04x SI=%04x\n",get_SS(), DS, ES, DI, SI);
  8.5541 +  
  8.5542 +  switch (GET_AH()) {
  8.5543 +
  8.5544 +    // FIXME ElTorito Various. Should be implemented
  8.5545 +    case 0x4a: // ElTorito - Initiate disk emu
  8.5546 +    case 0x4c: // ElTorito - Initiate disk emu and boot
  8.5547 +    case 0x4d: // ElTorito - Return Boot catalog
  8.5548 +      BX_PANIC("Int13 eltorito call with AX=%04x. Please report\n",AX);
  8.5549 +      goto int13_fail;
  8.5550 +      break;
  8.5551 +
  8.5552 +    case 0x4b: // ElTorito - Terminate disk emu
  8.5553 +      // FIXME ElTorito Hardcoded
  8.5554 +      write_byte(DS,SI+0x00,0x13);
  8.5555 +      write_byte(DS,SI+0x01,read_byte(ebda_seg,&EbdaData->cdemu.media));
  8.5556 +      write_byte(DS,SI+0x02,read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive));
  8.5557 +      write_byte(DS,SI+0x03,read_byte(ebda_seg,&EbdaData->cdemu.controller_index));
  8.5558 +      write_dword(DS,SI+0x04,read_dword(ebda_seg,&EbdaData->cdemu.ilba));
  8.5559 +      write_word(DS,SI+0x08,read_word(ebda_seg,&EbdaData->cdemu.device_spec));
  8.5560 +      write_word(DS,SI+0x0a,read_word(ebda_seg,&EbdaData->cdemu.buffer_segment));
  8.5561 +      write_word(DS,SI+0x0c,read_word(ebda_seg,&EbdaData->cdemu.load_segment));
  8.5562 +      write_word(DS,SI+0x0e,read_word(ebda_seg,&EbdaData->cdemu.sector_count));
  8.5563 +      write_byte(DS,SI+0x10,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.cylinders));
  8.5564 +      write_byte(DS,SI+0x11,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.spt));
  8.5565 +      write_byte(DS,SI+0x12,read_byte(ebda_seg,&EbdaData->cdemu.vdevice.heads));
  8.5566 +
  8.5567 +      // If we have to terminate emulation
  8.5568 +      if(GET_AL() == 0x00) {
  8.5569 +        // FIXME ElTorito Various. Should be handled accordingly to spec
  8.5570 +        write_byte(ebda_seg,&EbdaData->cdemu.active, 0x00); // bye bye
  8.5571 +        }
  8.5572 +
  8.5573 +      goto int13_success;
  8.5574 +      break;
  8.5575 +
  8.5576 +    default:
  8.5577 +      BX_INFO("int13_eltorito: unsupported AH=%02x\n", GET_AH());
  8.5578 +      goto int13_fail;
  8.5579 +      break;
  8.5580 +    }
  8.5581 +
  8.5582 +int13_fail:
  8.5583 +    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
  8.5584 +    SET_DISK_RET_STATUS(GET_AH());
  8.5585 +    SET_CF();     // error occurred
  8.5586 +    return;
  8.5587 +
  8.5588 +int13_success:
  8.5589 +    SET_AH(0x00); // no error
  8.5590 +    SET_DISK_RET_STATUS(0x00);
  8.5591 +    CLEAR_CF();   // no error
  8.5592 +    return;
  8.5593 +}
  8.5594 +
  8.5595 +// ---------------------------------------------------------------------------
  8.5596 +// End of int13 for eltorito functions
  8.5597 +// ---------------------------------------------------------------------------
  8.5598 +
  8.5599 +// ---------------------------------------------------------------------------
  8.5600 +// Start of int13 when emulating a device from the cd
  8.5601 +// ---------------------------------------------------------------------------
  8.5602 +
  8.5603 +  void
  8.5604 +int13_cdemu(DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS)
  8.5605 +  Bit16u DS, ES, DI, SI, BP, SP, BX, DX, CX, AX, IP, CS, FLAGS;
  8.5606 +{
  8.5607 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.5608 +  Bit8u  device, status;
  8.5609 +  Bit16u vheads, vspt, vcylinders;
  8.5610 +  Bit16u head, sector, cylinder, nbsectors;
  8.5611 +  Bit32u vlba, ilba, slba, elba;
  8.5612 +  Bit16u before, segment, offset;
  8.5613 +  Bit8u  atacmd[12];
  8.5614 +
  8.5615 +  BX_DEBUG_INT13_ET("int13_cdemu: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
  8.5616 +  //BX_DEBUG_INT13_ET("int13_cdemu: SS=%04x ES=%04x DI=%04x SI=%04x\n", get_SS(), ES, DI, SI);
  8.5617 +  
  8.5618 +  /* at this point, we are emulating a floppy/harddisk */
  8.5619 +  
  8.5620 +  // Recompute the device number 
  8.5621 +  device  = read_byte(ebda_seg,&EbdaData->cdemu.controller_index) * 2;
  8.5622 +  device += read_byte(ebda_seg,&EbdaData->cdemu.device_spec);
  8.5623 +
  8.5624 +  SET_DISK_RET_STATUS(0x00);
  8.5625 +
  8.5626 +  /* basic checks : emulation should be active, dl should equal the emulated drive */
  8.5627 +  if( (read_byte(ebda_seg,&EbdaData->cdemu.active) ==0 )
  8.5628 +   || (read_byte(ebda_seg,&EbdaData->cdemu.emulated_drive ) != GET_DL())) {
  8.5629 +    BX_INFO("int13_cdemu: function %02x, emulation not active for DL= %02x\n", GET_AH(), GET_DL());
  8.5630 +    goto int13_fail;
  8.5631 +    }
  8.5632 +  
  8.5633 +  switch (GET_AH()) {
  8.5634 +
  8.5635 +    // all those functions return SUCCESS
  8.5636 +    case 0x00: /* disk controller reset */
  8.5637 +    case 0x09: /* initialize drive parameters */
  8.5638 +    case 0x0c: /* seek to specified cylinder */
  8.5639 +    case 0x0d: /* alternate disk reset */  // FIXME ElTorito Various. should really reset ?
  8.5640 +    case 0x10: /* check drive ready */     // FIXME ElTorito Various. should check if ready ?
  8.5641 +    case 0x11: /* recalibrate */      
  8.5642 +    case 0x14: /* controller internal diagnostic */
  8.5643 +    case 0x16: /* detect disk change */
  8.5644 +      goto int13_success;
  8.5645 +      break;
  8.5646 +
  8.5647 +    // all those functions return disk write-protected
  8.5648 +    case 0x03: /* write disk sectors */
  8.5649 +    case 0x05: /* format disk track */
  8.5650 +      SET_AH(0x03);
  8.5651 +      goto int13_fail_noah;
  8.5652 +      break;
  8.5653 +
  8.5654 +    case 0x01: /* read disk status */
  8.5655 +      status=read_byte(0x0040, 0x0074);
  8.5656 +      SET_AH(status);
  8.5657 +      SET_DISK_RET_STATUS(0);
  8.5658 +
  8.5659 +      /* set CF if error status read */
  8.5660 +      if (status) goto int13_fail_nostatus;
  8.5661 +      else        goto int13_success_noah;
  8.5662 +      break;
  8.5663 +
  8.5664 +    case 0x02: // read disk sectors
  8.5665 +    case 0x04: // verify disk sectors
  8.5666 +      vspt       = read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); 
  8.5667 +      vcylinders = read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders); 
  8.5668 +      vheads     = read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads); 
  8.5669 +
  8.5670 +      ilba       = read_dword(ebda_seg,&EbdaData->cdemu.ilba);
  8.5671 +
  8.5672 +      sector    = GET_CL() & 0x003f;
  8.5673 +      cylinder  = (GET_CL() & 0x00c0) << 2 | GET_CH();
  8.5674 +      head      = GET_DH();
  8.5675 +      nbsectors = GET_AL();
  8.5676 +      segment   = ES;
  8.5677 +      offset    = BX;
  8.5678 +
  8.5679 +      // no sector to read ?
  8.5680 +      if(nbsectors==0) goto int13_success;
  8.5681 +
  8.5682 +      // sanity checks sco openserver needs this!
  8.5683 +      if ((sector   >  vspt)
  8.5684 +       || (cylinder >= vcylinders)
  8.5685 +       || (head     >= vheads)) {
  8.5686 +        goto int13_fail;
  8.5687 +        }
  8.5688 +
  8.5689 +      // After controls, verify do nothing
  8.5690 +      if (GET_AH() == 0x04) goto int13_success;
  8.5691 +
  8.5692 +      segment = ES+(BX / 16);
  8.5693 +      offset  = BX % 16;
  8.5694 +
  8.5695 +      // calculate the virtual lba inside the image
  8.5696 +      vlba=((((Bit32u)cylinder*(Bit32u)vheads)+(Bit32u)head)*(Bit32u)vspt)+((Bit32u)(sector-1));
  8.5697 + 
  8.5698 +      // In advance so we don't loose the count
  8.5699 +      SET_AL(nbsectors);
  8.5700 +
  8.5701 +      // start lba on cd
  8.5702 +      slba  = (Bit32u)vlba/4; 
  8.5703 +      before= (Bit16u)vlba%4;
  8.5704 +
  8.5705 +      // end lba on cd
  8.5706 +      elba = (Bit32u)(vlba+nbsectors-1)/4;
  8.5707 +      
  8.5708 +      memsetb(get_SS(),atacmd,0,12);
  8.5709 +      atacmd[0]=0x28;                      // READ command
  8.5710 +      atacmd[7]=((Bit16u)(elba-slba+1) & 0xff00) >> 8; // Sectors
  8.5711 +      atacmd[8]=((Bit16u)(elba-slba+1) & 0x00ff);      // Sectors
  8.5712 +      atacmd[2]=(ilba+slba & 0xff000000) >> 24;  // LBA
  8.5713 +      atacmd[3]=(ilba+slba & 0x00ff0000) >> 16;
  8.5714 +      atacmd[4]=(ilba+slba & 0x0000ff00) >> 8;
  8.5715 +      atacmd[5]=(ilba+slba & 0x000000ff);
  8.5716 +      if((status = ata_cmd_packet(device, 12, get_SS(), atacmd, before*512, nbsectors*512L, ATA_DATA_IN, segment,offset)) != 0) {
  8.5717 +        BX_INFO("int13_cdemu: function %02x, error %02x !\n",GET_AH(),status);
  8.5718 +        SET_AH(0x02);
  8.5719 +        SET_AL(0);
  8.5720 +        goto int13_fail_noah;
  8.5721 +        }
  8.5722 +
  8.5723 +      goto int13_success;
  8.5724 +      break;
  8.5725 +
  8.5726 +    case 0x08: /* read disk drive parameters */
  8.5727 +      vspt=read_word(ebda_seg,&EbdaData->cdemu.vdevice.spt); 
  8.5728 +      vcylinders=read_word(ebda_seg,&EbdaData->cdemu.vdevice.cylinders) - 1; 
  8.5729 +      vheads=read_word(ebda_seg,&EbdaData->cdemu.vdevice.heads) - 1; 
  8.5730 + 
  8.5731 +      SET_AL( 0x00 );
  8.5732 +      SET_BL( 0x00 );
  8.5733 +      SET_CH( vcylinders & 0xff );
  8.5734 +      SET_CL((( vcylinders >> 2) & 0xc0) | ( vspt  & 0x3f ));
  8.5735 +      SET_DH( vheads );
  8.5736 +      SET_DL( 0x02 );   // FIXME ElTorito Various. should send the real count of drives 1 or 2
  8.5737 +                        // FIXME ElTorito Harddisk. should send the HD count
  8.5738 + 
  8.5739 +      switch(read_byte(ebda_seg,&EbdaData->cdemu.media)) {
  8.5740 +        case 0x01: SET_BL( 0x02 ); break;
  8.5741 +        case 0x02: SET_BL( 0x04 ); break;
  8.5742 +        case 0x03: SET_BL( 0x06 ); break;
  8.5743 +        }
  8.5744 +
  8.5745 +ASM_START
  8.5746 +      push bp
  8.5747 +      mov  bp, sp
  8.5748 +      mov ax, #diskette_param_table2
  8.5749 +      mov _int13_cdemu.DI+2[bp], ax
  8.5750 +      mov _int13_cdemu.ES+2[bp], cs
  8.5751 +      pop  bp
  8.5752 +ASM_END
  8.5753 +      goto int13_success;
  8.5754 +      break;
  8.5755 +
  8.5756 +    case 0x15: /* read disk drive size */
  8.5757 +      // FIXME ElTorito Harddisk. What geometry to send ?
  8.5758 +      SET_AH(0x03);
  8.5759 +      goto int13_success_noah;
  8.5760 +      break;
  8.5761 +
  8.5762 +    // all those functions return unimplemented
  8.5763 +    case 0x0a: /* read disk sectors with ECC */
  8.5764 +    case 0x0b: /* write disk sectors with ECC */
  8.5765 +    case 0x18: /* set media type for format */
  8.5766 +    case 0x41: // IBM/MS installation check
  8.5767 +      // FIXME ElTorito Harddisk. Darwin would like to use EDD
  8.5768 +    case 0x42: // IBM/MS extended read
  8.5769 +    case 0x43: // IBM/MS extended write
  8.5770 +    case 0x44: // IBM/MS verify sectors
  8.5771 +    case 0x45: // IBM/MS lock/unlock drive
  8.5772 +    case 0x46: // IBM/MS eject media
  8.5773 +    case 0x47: // IBM/MS extended seek
  8.5774 +    case 0x48: // IBM/MS get drive parameters 
  8.5775 +    case 0x49: // IBM/MS extended media change
  8.5776 +    case 0x4e: // ? - set hardware configuration
  8.5777 +    case 0x50: // ? - send packet command
  8.5778 +    default:
  8.5779 +      BX_INFO("int13_cdemu function AH=%02x unsupported, returns fail\n", GET_AH());
  8.5780 +      goto int13_fail;
  8.5781 +      break;
  8.5782 +    }
  8.5783 +
  8.5784 +int13_fail:
  8.5785 +    SET_AH(0x01); // defaults to invalid function in AH or invalid parameter
  8.5786 +int13_fail_noah:
  8.5787 +    SET_DISK_RET_STATUS(GET_AH());
  8.5788 +int13_fail_nostatus:
  8.5789 +    SET_CF();     // error occurred
  8.5790 +    return;
  8.5791 +
  8.5792 +int13_success:
  8.5793 +    SET_AH(0x00); // no error
  8.5794 +int13_success_noah:
  8.5795 +    SET_DISK_RET_STATUS(0x00);
  8.5796 +    CLEAR_CF();   // no error
  8.5797 +    return;
  8.5798 +}
  8.5799 +
  8.5800 +// ---------------------------------------------------------------------------
  8.5801 +// End of int13 when emulating a device from the cd
  8.5802 +// ---------------------------------------------------------------------------
  8.5803 +
  8.5804 +#endif // BX_ELTORITO_BOOT
  8.5805 +
  8.5806 +#else //BX_USE_ATADRV
  8.5807 +
  8.5808 +  void
  8.5809 +outLBA(cylinder,hd_heads,head,hd_sectors,sector,dl)
  8.5810 +  Bit16u cylinder;
  8.5811 +  Bit16u hd_heads;
  8.5812 +  Bit16u head;
  8.5813 +  Bit16u hd_sectors;
  8.5814 +  Bit16u sector;
  8.5815 +  Bit16u dl;
  8.5816 +{
  8.5817 +ASM_START
  8.5818 +        push   bp
  8.5819 +        mov    bp, sp
  8.5820 +        push   eax
  8.5821 +        push   ebx
  8.5822 +        push   edx
  8.5823 +        xor    eax,eax
  8.5824 +        mov    ax,4[bp]  // cylinder
  8.5825 +        xor    ebx,ebx
  8.5826 +        mov    bl,6[bp]  // hd_heads
  8.5827 +        imul   ebx
  8.5828 +
  8.5829 +        mov    bl,8[bp]  // head
  8.5830 +        add    eax,ebx
  8.5831 +        mov    bl,10[bp] // hd_sectors
  8.5832 +        imul   ebx
  8.5833 +        mov    bl,12[bp] // sector
  8.5834 +        add    eax,ebx
  8.5835 +
  8.5836 +        dec    eax
  8.5837 +        mov    dx,#0x1f3
  8.5838 +        out    dx,al
  8.5839 +        mov    dx,#0x1f4
  8.5840 +        mov    al,ah
  8.5841 +        out    dx,al
  8.5842 +        shr    eax,#16
  8.5843 +        mov    dx,#0x1f5
  8.5844 +        out    dx,al
  8.5845 +        and    ah,#0xf
  8.5846 +        mov    bl,14[bp] // dl
  8.5847 +        and    bl,#1
  8.5848 +        shl    bl,#4
  8.5849 +        or     ah,bl
  8.5850 +        or     ah,#0xe0
  8.5851 +        mov    al,ah
  8.5852 +        mov    dx,#0x01f6
  8.5853 +        out    dx,al
  8.5854 +        pop    edx
  8.5855 +        pop    ebx
  8.5856 +        pop    eax
  8.5857 +        pop    bp
  8.5858 +ASM_END
  8.5859 +}
  8.5860 +
  8.5861 +  void
  8.5862 +int13_harddisk(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
  8.5863 +  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
  8.5864 +{
  8.5865 +  Bit8u    drive, num_sectors, sector, head, status, mod;
  8.5866 +  Bit8u    drive_map;
  8.5867 +  Bit8u    n_drives;
  8.5868 +  Bit16u   cyl_mod, ax;
  8.5869 +  Bit16u   max_cylinder, cylinder, total_sectors;
  8.5870 +  Bit16u   hd_cylinders;
  8.5871 +  Bit8u    hd_heads, hd_sectors;
  8.5872 +  Bit16u   val16;
  8.5873 +  Bit8u    sector_count;
  8.5874 +  unsigned int i;
  8.5875 +  Bit16u   tempbx;
  8.5876 +  Bit16u   dpsize;
  8.5877 +
  8.5878 +  Bit16u   count, segment, offset;
  8.5879 +  Bit32u   lba;
  8.5880 +  Bit16u   error;
  8.5881 +
  8.5882 +  BX_DEBUG_INT13_HD("int13 harddisk: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
  8.5883 +
  8.5884 +  write_byte(0x0040, 0x008e, 0);  // clear completion flag
  8.5885 +
  8.5886 +  /* at this point, DL is >= 0x80 to be passed from the floppy int13h
  8.5887 +     handler code */
  8.5888 +  /* check how many disks first (cmos reg 0x12), return an error if
  8.5889 +     drive not present */
  8.5890 +  drive_map = inb_cmos(0x12);
  8.5891 +  drive_map = (((drive_map & 0xf0)==0) ? 0 : 1) |
  8.5892 +              (((drive_map & 0x0f)==0) ? 0 : 2);
  8.5893 +  n_drives = (drive_map==0) ? 0 :
  8.5894 +    ((drive_map==3) ? 2 : 1);
  8.5895 +
  8.5896 +  if (!(drive_map & (1<<(GET_ELDL()&0x7f)))) { /* allow 0, 1, or 2 disks */
  8.5897 +    SET_AH(0x01);
  8.5898 +    SET_DISK_RET_STATUS(0x01);
  8.5899 +    SET_CF(); /* error occurred */
  8.5900 +    return;
  8.5901 +    }
  8.5902 +
  8.5903 +  switch (GET_AH()) {
  8.5904 +
  8.5905 +    case 0x00: /* disk controller reset */
  8.5906 +BX_DEBUG_INT13_HD("int13_f00\n");
  8.5907 +
  8.5908 +      SET_AH(0);
  8.5909 +      SET_DISK_RET_STATUS(0);
  8.5910 +      set_diskette_ret_status(0);
  8.5911 +      set_diskette_current_cyl(0, 0); /* current cylinder, diskette 1 */
  8.5912 +      set_diskette_current_cyl(1, 0); /* current cylinder, diskette 2 */
  8.5913 +      CLEAR_CF(); /* successful */
  8.5914 +      return;
  8.5915 +      break;
  8.5916 +
  8.5917 +    case 0x01: /* read disk status */
  8.5918 +BX_DEBUG_INT13_HD("int13_f01\n");
  8.5919 +      status = read_byte(0x0040, 0x0074);
  8.5920 +      SET_AH(status);
  8.5921 +      SET_DISK_RET_STATUS(0);
  8.5922 +      /* set CF if error status read */
  8.5923 +      if (status) SET_CF();
  8.5924 +      else        CLEAR_CF();
  8.5925 +      return;
  8.5926 +      break;
  8.5927 +
  8.5928 +    case 0x04: // verify disk sectors
  8.5929 +    case 0x02: // read disk sectors
  8.5930 +      drive = GET_ELDL();
  8.5931 +      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
  8.5932 +
  8.5933 +      num_sectors = GET_AL();
  8.5934 +      cylinder    = (GET_CL() & 0x00c0) << 2 | GET_CH();
  8.5935 +      sector      = (GET_CL() & 0x3f);
  8.5936 +      head        = GET_DH();
  8.5937 +
  8.5938 +
  8.5939 +      if (hd_cylinders > 1024) {
  8.5940 +        if (hd_cylinders <= 2048) {
  8.5941 +          cylinder <<= 1;
  8.5942 +          }
  8.5943 +        else if (hd_cylinders <= 4096) {
  8.5944 +          cylinder <<= 2;
  8.5945 +          }
  8.5946 +        else if (hd_cylinders <= 8192) {
  8.5947 +          cylinder <<= 3;
  8.5948 +          }
  8.5949 +        else { // hd_cylinders <= 16384
  8.5950 +          cylinder <<= 4;
  8.5951 +          }
  8.5952 +
  8.5953 +        ax = head / hd_heads;
  8.5954 +        cyl_mod = ax & 0xff;
  8.5955 +        head    = ax >> 8;
  8.5956 +        cylinder |= cyl_mod;
  8.5957 +        }
  8.5958 +
  8.5959 +      if ( (cylinder >= hd_cylinders) ||
  8.5960 +           (sector > hd_sectors) ||
  8.5961 +           (head >= hd_heads) ) {
  8.5962 +        SET_AH(1);
  8.5963 +        SET_DISK_RET_STATUS(1);
  8.5964 +        SET_CF(); /* error occurred */
  8.5965 +        return;
  8.5966 +        }
  8.5967 +
  8.5968 +      if ( (num_sectors > 128) || (num_sectors == 0) )
  8.5969 +        BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
  8.5970 +
  8.5971 +      if (head > 15)
  8.5972 +        BX_PANIC("hard drive BIOS:(read/verify) head > 15\n");
  8.5973 +
  8.5974 +      if ( GET_AH() == 0x04 ) {
  8.5975 +        SET_AH(0);
  8.5976 +        SET_DISK_RET_STATUS(0);
  8.5977 +        CLEAR_CF();
  8.5978 +        return;
  8.5979 +        }
  8.5980 +
  8.5981 +      status = inb(0x1f7);
  8.5982 +      if (status & 0x80) {
  8.5983 +        BX_PANIC("hard drive BIOS:(read/verify) BUSY bit set\n");
  8.5984 +        }
  8.5985 +      outb(0x01f2, num_sectors);
  8.5986 +      /* activate LBA? (tomv) */
  8.5987 +      if (hd_heads > 16) {
  8.5988 +BX_DEBUG_INT13_HD("CHS: %x %x %x\n", cylinder, head, sector);
  8.5989 +        outLBA(cylinder,hd_heads,head,hd_sectors,sector,drive);
  8.5990 +        }
  8.5991 +      else {
  8.5992 +        outb(0x01f3, sector);
  8.5993 +        outb(0x01f4, cylinder & 0x00ff);
  8.5994 +        outb(0x01f5, cylinder >> 8);
  8.5995 +        outb(0x01f6, 0xa0 | ((drive & 0x01)<<4) | (head & 0x0f));
  8.5996 +        }
  8.5997 +      outb(0x01f7, 0x20);
  8.5998 +
  8.5999 +      while (1) {
  8.6000 +        status = inb(0x1f7);
  8.6001 +        if ( !(status & 0x80) ) break;
  8.6002 +        }
  8.6003 +
  8.6004 +      if (status & 0x01) {
  8.6005 +        BX_PANIC("hard drive BIOS:(read/verify) read error\n");
  8.6006 +      } else if ( !(status & 0x08) ) {
  8.6007 +        BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
  8.6008 +        BX_PANIC("hard drive BIOS:(read/verify) expected DRQ=1\n");
  8.6009 +      }
  8.6010 +
  8.6011 +      sector_count = 0;
  8.6012 +      tempbx = BX;
  8.6013 +
  8.6014 +ASM_START
  8.6015 +  sti  ;; enable higher priority interrupts
  8.6016 +ASM_END
  8.6017 +
  8.6018 +      while (1) {
  8.6019 +ASM_START
  8.6020 +        ;; store temp bx in real DI register
  8.6021 +        push bp
  8.6022 +        mov  bp, sp
  8.6023 +        mov  di, _int13_harddisk.tempbx + 2 [bp]
  8.6024 +        pop  bp
  8.6025 +
  8.6026 +        ;; adjust if there will be an overrun
  8.6027 +        cmp   di, #0xfe00
  8.6028 +        jbe   i13_f02_no_adjust
  8.6029 +i13_f02_adjust:
  8.6030 +        sub   di, #0x0200 ; sub 512 bytes from offset
  8.6031 +        mov   ax, es
  8.6032 +        add   ax, #0x0020 ; add 512 to segment
  8.6033 +        mov   es, ax
  8.6034 +
  8.6035 +i13_f02_no_adjust:
  8.6036 +        mov  cx, #0x0100   ;; counter (256 words = 512b)
  8.6037 +        mov  dx, #0x01f0  ;; AT data read port
  8.6038 +
  8.6039 +        rep
  8.6040 +          insw ;; CX words transfered from port(DX) to ES:[DI]
  8.6041 +
  8.6042 +i13_f02_done:
  8.6043 +        ;; store real DI register back to temp bx
  8.6044 +        push bp
  8.6045 +        mov  bp, sp
  8.6046 +        mov  _int13_harddisk.tempbx + 2 [bp], di
  8.6047 +        pop  bp
  8.6048 +ASM_END
  8.6049 +
  8.6050 +        sector_count++;
  8.6051 +        num_sectors--;
  8.6052 +        if (num_sectors == 0) {
  8.6053 +          status = inb(0x1f7);
  8.6054 +          if ( (status & 0xc9) != 0x40 )
  8.6055 +            BX_PANIC("no sectors left to read/verify, status is %02x\n", (unsigned) status);
  8.6056 +          break;
  8.6057 +          }
  8.6058 +        else {
  8.6059 +          status = inb(0x1f7);
  8.6060 +          if ( (status & 0xc9) != 0x48 )
  8.6061 +            BX_PANIC("more sectors left to read/verify, status is %02x\n", (unsigned) status);
  8.6062 +          continue;
  8.6063 +          }
  8.6064 +        }
  8.6065 +
  8.6066 +      SET_AH(0);
  8.6067 +      SET_DISK_RET_STATUS(0);
  8.6068 +      SET_AL(sector_count);
  8.6069 +      CLEAR_CF(); /* successful */
  8.6070 +      return;
  8.6071 +      break;
  8.6072 +
  8.6073 +
  8.6074 +    case 0x03: /* write disk sectors */
  8.6075 +BX_DEBUG_INT13_HD("int13_f03\n");
  8.6076 +      drive = GET_ELDL ();
  8.6077 +      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
  8.6078 +
  8.6079 +      num_sectors = GET_AL();
  8.6080 +      cylinder    = GET_CH();
  8.6081 +      cylinder    |= ( ((Bit16u) GET_CL()) << 2) & 0x300;
  8.6082 +      sector      = (GET_CL() & 0x3f);
  8.6083 +      head        = GET_DH();
  8.6084 +
  8.6085 +      if (hd_cylinders > 1024) {
  8.6086 +        if (hd_cylinders <= 2048) {
  8.6087 +          cylinder <<= 1;
  8.6088 +          }
  8.6089 +        else if (hd_cylinders <= 4096) {
  8.6090 +          cylinder <<= 2;
  8.6091 +          }
  8.6092 +        else if (hd_cylinders <= 8192) {
  8.6093 +          cylinder <<= 3;
  8.6094 +          }
  8.6095 +        else { // hd_cylinders <= 16384
  8.6096 +          cylinder <<= 4;
  8.6097 +          }
  8.6098 +
  8.6099 +        ax = head / hd_heads;
  8.6100 +        cyl_mod = ax & 0xff;
  8.6101 +        head    = ax >> 8;
  8.6102 +        cylinder |= cyl_mod;
  8.6103 +        }
  8.6104 +
  8.6105 +      if ( (cylinder >= hd_cylinders) ||
  8.6106 +           (sector > hd_sectors) ||
  8.6107 +           (head >= hd_heads) ) {
  8.6108 +        SET_AH( 1);
  8.6109 +        SET_DISK_RET_STATUS(1);
  8.6110 +        SET_CF(); /* error occurred */
  8.6111 +        return;
  8.6112 +        }
  8.6113 +
  8.6114 +      if ( (num_sectors > 128) || (num_sectors == 0) )
  8.6115 +        BX_PANIC("int13_harddisk(): num_sectors out of range!\n");
  8.6116 +
  8.6117 +      if (head > 15)
  8.6118 +        BX_PANIC("hard drive BIOS:(read) head > 15\n");
  8.6119 +
  8.6120 +      status = inb(0x1f7);
  8.6121 +      if (status & 0x80) {
  8.6122 +        BX_PANIC("hard drive BIOS:(read) BUSY bit set\n");
  8.6123 +        }
  8.6124 +// should check for Drive Ready Bit also in status reg
  8.6125 +      outb(0x01f2, num_sectors);
  8.6126 +
  8.6127 +      /* activate LBA? (tomv) */
  8.6128 +      if (hd_heads > 16) {
  8.6129 +BX_DEBUG_INT13_HD("CHS (write): %x %x %x\n", cylinder, head, sector);
  8.6130 +        outLBA(cylinder,hd_heads,head,hd_sectors,sector,GET_ELDL());
  8.6131 +        }
  8.6132 +      else {
  8.6133 +        outb(0x01f3, sector);
  8.6134 +        outb(0x01f4, cylinder & 0x00ff);
  8.6135 +        outb(0x01f5, cylinder >> 8);
  8.6136 +        outb(0x01f6, 0xa0 | ((GET_ELDL() & 0x01)<<4) | (head & 0x0f));
  8.6137 +        }
  8.6138 +      outb(0x01f7, 0x30);
  8.6139 +
  8.6140 +      // wait for busy bit to turn off after seeking
  8.6141 +      while (1) {
  8.6142 +        status = inb(0x1f7);
  8.6143 +        if ( !(status & 0x80) ) break;
  8.6144 +        }
  8.6145 +
  8.6146 +      if ( !(status & 0x08) ) {
  8.6147 +        BX_DEBUG_INT13_HD("status was %02x\n", (unsigned) status);
  8.6148 +        BX_PANIC("hard drive BIOS:(write) data-request bit not set\n");
  8.6149 +        }
  8.6150 +
  8.6151 +      sector_count = 0;
  8.6152 +      tempbx = BX;
  8.6153 +
  8.6154 +ASM_START
  8.6155 +  sti  ;; enable higher priority interrupts
  8.6156 +ASM_END
  8.6157 +
  8.6158 +      while (1) {
  8.6159 +ASM_START
  8.6160 +        ;; store temp bx in real SI register
  8.6161 +        push bp
  8.6162 +        mov  bp, sp
  8.6163 +        mov  si, _int13_harddisk.tempbx + 2 [bp]
  8.6164 +        pop  bp
  8.6165 +
  8.6166 +        ;; adjust if there will be an overrun
  8.6167 +        cmp   si, #0xfe00
  8.6168 +        jbe   i13_f03_no_adjust
  8.6169 +i13_f03_adjust:
  8.6170 +        sub   si, #0x0200 ; sub 512 bytes from offset
  8.6171 +        mov   ax, es
  8.6172 +        add   ax, #0x0020 ; add 512 to segment
  8.6173 +        mov   es, ax
  8.6174 +
  8.6175 +i13_f03_no_adjust:
  8.6176 +        mov  cx, #0x0100   ;; counter (256 words = 512b)
  8.6177 +        mov  dx, #0x01f0  ;; AT data read port
  8.6178 +
  8.6179 +        seg ES
  8.6180 +        rep
  8.6181 +          outsw ;; CX words tranfered from ES:[SI] to port(DX)
  8.6182 +
  8.6183 +        ;; store real SI register back to temp bx
  8.6184 +        push bp
  8.6185 +        mov  bp, sp
  8.6186 +        mov  _int13_harddisk.tempbx + 2 [bp], si
  8.6187 +        pop  bp
  8.6188 +ASM_END
  8.6189 +
  8.6190 +        sector_count++;
  8.6191 +        num_sectors--;
  8.6192 +        if (num_sectors == 0) {
  8.6193 +          status = inb(0x1f7);
  8.6194 +          if ( (status & 0xe9) != 0x40 )
  8.6195 +            BX_PANIC("no sectors left to write, status is %02x\n", (unsigned) status);
  8.6196 +          break;
  8.6197 +          }
  8.6198 +        else {
  8.6199 +          status = inb(0x1f7);
  8.6200 +          if ( (status & 0xc9) != 0x48 )
  8.6201 +            BX_PANIC("more sectors left to write, status is %02x\n", (unsigned) status);
  8.6202 +          continue;
  8.6203 +          }
  8.6204 +        }
  8.6205 +
  8.6206 +      SET_AH(0);
  8.6207 +      SET_DISK_RET_STATUS(0);
  8.6208 +      SET_AL(sector_count);
  8.6209 +      CLEAR_CF(); /* successful */
  8.6210 +      return;
  8.6211 +      break;
  8.6212 +
  8.6213 +    case 0x05: /* format disk track */
  8.6214 +BX_DEBUG_INT13_HD("int13_f05\n");
  8.6215 +      BX_PANIC("format disk track called\n");
  8.6216 +      /* nop */
  8.6217 +      SET_AH(0);
  8.6218 +      SET_DISK_RET_STATUS(0);
  8.6219 +      CLEAR_CF(); /* successful */
  8.6220 +      return;
  8.6221 +      break;
  8.6222 +
  8.6223 +    case 0x08: /* read disk drive parameters */
  8.6224 +BX_DEBUG_INT13_HD("int13_f08\n");
  8.6225 +      
  8.6226 +      drive = GET_ELDL ();
  8.6227 +      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
  8.6228 +
  8.6229 +      // translate CHS
  8.6230 +      //
  8.6231 +      if (hd_cylinders <= 1024) {
  8.6232 +        // hd_cylinders >>= 0;
  8.6233 +        // hd_heads <<= 0;
  8.6234 +        }
  8.6235 +      else if (hd_cylinders <= 2048) {
  8.6236 +        hd_cylinders >>= 1;
  8.6237 +        hd_heads <<= 1;
  8.6238 +        }
  8.6239 +      else if (hd_cylinders <= 4096) {
  8.6240 +        hd_cylinders >>= 2;
  8.6241 +        hd_heads <<= 2;
  8.6242 +        }
  8.6243 +      else if (hd_cylinders <= 8192) {
  8.6244 +        hd_cylinders >>= 3;
  8.6245 +        hd_heads <<= 3;
  8.6246 +        }
  8.6247 +      else { // hd_cylinders <= 16384
  8.6248 +        hd_cylinders >>= 4;
  8.6249 +        hd_heads <<= 4;
  8.6250 +        }
  8.6251 +
  8.6252 +      max_cylinder = hd_cylinders - 2; /* 0 based */
  8.6253 +      SET_AL(0);
  8.6254 +      SET_CH(max_cylinder & 0xff);
  8.6255 +      SET_CL(((max_cylinder >> 2) & 0xc0) | (hd_sectors & 0x3f));
  8.6256 +      SET_DH(hd_heads - 1);
  8.6257 +      SET_DL(n_drives); /* returns 0, 1, or 2 hard drives */
  8.6258 +      SET_AH(0);
  8.6259 +      SET_DISK_RET_STATUS(0);
  8.6260 +      CLEAR_CF(); /* successful */
  8.6261 +
  8.6262 +      return;
  8.6263 +      break;
  8.6264 +
  8.6265 +    case 0x09: /* initialize drive parameters */
  8.6266 +BX_DEBUG_INT13_HD("int13_f09\n");
  8.6267 +      SET_AH(0);
  8.6268 +      SET_DISK_RET_STATUS(0);
  8.6269 +      CLEAR_CF(); /* successful */
  8.6270 +      return;
  8.6271 +      break;
  8.6272 +
  8.6273 +    case 0x0a: /* read disk sectors with ECC */
  8.6274 +BX_DEBUG_INT13_HD("int13_f0a\n");
  8.6275 +    case 0x0b: /* write disk sectors with ECC */
  8.6276 +BX_DEBUG_INT13_HD("int13_f0b\n");
  8.6277 +      BX_PANIC("int13h Functions 0Ah & 0Bh not implemented!\n");
  8.6278 +      return;
  8.6279 +      break;
  8.6280 +
  8.6281 +    case 0x0c: /* seek to specified cylinder */
  8.6282 +BX_DEBUG_INT13_HD("int13_f0c\n");
  8.6283 +      BX_INFO("int13h function 0ch (seek) not implemented!\n");
  8.6284 +      SET_AH(0);
  8.6285 +      SET_DISK_RET_STATUS(0);
  8.6286 +      CLEAR_CF(); /* successful */
  8.6287 +      return;
  8.6288 +      break;
  8.6289 +
  8.6290 +    case 0x0d: /* alternate disk reset */
  8.6291 +BX_DEBUG_INT13_HD("int13_f0d\n");
  8.6292 +      SET_AH(0);
  8.6293 +      SET_DISK_RET_STATUS(0);
  8.6294 +      CLEAR_CF(); /* successful */
  8.6295 +      return;
  8.6296 +      break;
  8.6297 +
  8.6298 +    case 0x10: /* check drive ready */
  8.6299 +BX_DEBUG_INT13_HD("int13_f10\n");
  8.6300 +      //SET_AH(0);
  8.6301 +      //SET_DISK_RET_STATUS(0);
  8.6302 +      //CLEAR_CF(); /* successful */
  8.6303 +      //return;
  8.6304 +      //break;
  8.6305 +
  8.6306 +      // should look at 40:8E also???
  8.6307 +      status = inb(0x01f7);
  8.6308 +      if ( (status & 0xc0) == 0x40 ) {
  8.6309 +        SET_AH(0);
  8.6310 +        SET_DISK_RET_STATUS(0);
  8.6311 +        CLEAR_CF(); // drive ready
  8.6312 +        return;
  8.6313 +        }
  8.6314 +      else {
  8.6315 +        SET_AH(0xAA);
  8.6316 +        SET_DISK_RET_STATUS(0xAA);
  8.6317 +        SET_CF(); // not ready
  8.6318 +        return;
  8.6319 +        }
  8.6320 +      break;
  8.6321 +
  8.6322 +    case 0x11: /* recalibrate */
  8.6323 +BX_DEBUG_INT13_HD("int13_f11\n");
  8.6324 +      SET_AH(0);
  8.6325 +      SET_DISK_RET_STATUS(0);
  8.6326 +      CLEAR_CF(); /* successful */
  8.6327 +      return;
  8.6328 +      break;
  8.6329 +
  8.6330 +    case 0x14: /* controller internal diagnostic */
  8.6331 +BX_DEBUG_INT13_HD("int13_f14\n");
  8.6332 +      SET_AH(0);
  8.6333 +      SET_DISK_RET_STATUS(0);
  8.6334 +      CLEAR_CF(); /* successful */
  8.6335 +      SET_AL(0);
  8.6336 +      return;
  8.6337 +      break;
  8.6338 +
  8.6339 +    case 0x15: /* read disk drive size */
  8.6340 +      drive = GET_ELDL();
  8.6341 +      get_hd_geometry(drive, &hd_cylinders, &hd_heads, &hd_sectors);
  8.6342 +ASM_START
  8.6343 +      push bp
  8.6344 +      mov  bp, sp
  8.6345 +      mov  al, _int13_harddisk.hd_heads + 2 [bp]
  8.6346 +      mov  ah, _int13_harddisk.hd_sectors + 2 [bp]
  8.6347 +      mul  al, ah ;; ax = heads * sectors
  8.6348 +      mov  bx, _int13_harddisk.hd_cylinders + 2 [bp]
  8.6349 +      dec  bx     ;; use (cylinders - 1) ???
  8.6350 +      mul  ax, bx ;; dx:ax = (cylinders -1) * (heads * sectors)
  8.6351 +      ;; now we need to move the 32bit result dx:ax to what the
  8.6352 +      ;; BIOS wants which is cx:dx.
  8.6353 +      ;; and then into CX:DX on the stack
  8.6354 +      mov  _int13_harddisk.CX + 2 [bp], dx
  8.6355 +      mov  _int13_harddisk.DX + 2 [bp], ax
  8.6356 +      pop  bp
  8.6357 +ASM_END
  8.6358 +      SET_AH(3);  // hard disk accessible
  8.6359 +      SET_DISK_RET_STATUS(0); // ??? should this be 0
  8.6360 +      CLEAR_CF(); // successful
  8.6361 +      return;
  8.6362 +      break;
  8.6363 +
  8.6364 +    case 0x18: // set media type for format
  8.6365 +    case 0x41: // IBM/MS 
  8.6366 +    case 0x42: // IBM/MS 
  8.6367 +    case 0x43: // IBM/MS 
  8.6368 +    case 0x44: // IBM/MS 
  8.6369 +    case 0x45: // IBM/MS lock/unlock drive
  8.6370 +    case 0x46: // IBM/MS eject media
  8.6371 +    case 0x47: // IBM/MS extended seek
  8.6372 +    case 0x49: // IBM/MS extended media change
  8.6373 +    case 0x50: // IBM/MS send packet command
  8.6374 +    default:
  8.6375 +      BX_INFO("int13_harddisk: unsupported AH=%02x\n", GET_AH());
  8.6376 +
  8.6377 +      SET_AH(1);  // code=invalid function in AH or invalid parameter
  8.6378 +      SET_DISK_RET_STATUS(1);
  8.6379 +      SET_CF(); /* unsuccessful */
  8.6380 +      return;
  8.6381 +      break;
  8.6382 +    }
  8.6383 +}
  8.6384 +
  8.6385 +static char panic_msg_reg12h[] = "HD%d cmos reg 12h not type F\n";
  8.6386 +static char panic_msg_reg19h[] = "HD%d cmos reg %02xh not user definable type 47\n";
  8.6387 +
  8.6388 +  void
  8.6389 +get_hd_geometry(drive, hd_cylinders, hd_heads, hd_sectors)
  8.6390 +  Bit8u drive;
  8.6391 +  Bit16u *hd_cylinders;
  8.6392 +  Bit8u  *hd_heads;
  8.6393 +  Bit8u  *hd_sectors;
  8.6394 +{
  8.6395 +  Bit8u hd_type;
  8.6396 +  Bit16u ss;
  8.6397 +  Bit16u cylinders;
  8.6398 +  Bit8u iobase;
  8.6399 +
  8.6400 +  ss = get_SS();
  8.6401 +  if (drive == 0x80) {
  8.6402 +    hd_type = inb_cmos(0x12) & 0xf0;
  8.6403 +    if (hd_type != 0xf0)
  8.6404 +      BX_INFO(panic_msg_reg12h,0);
  8.6405 +    hd_type = inb_cmos(0x19); // HD0: extended type
  8.6406 +    if (hd_type != 47)
  8.6407 +      BX_INFO(panic_msg_reg19h,0,0x19);
  8.6408 +    iobase = 0x1b;
  8.6409 +  } else {
  8.6410 +    hd_type = inb_cmos(0x12) & 0x0f;
  8.6411 +    if (hd_type != 0x0f)
  8.6412 +      BX_INFO(panic_msg_reg12h,1);
  8.6413 +    hd_type = inb_cmos(0x1a); // HD0: extended type
  8.6414 +    if (hd_type != 47)
  8.6415 +      BX_INFO(panic_msg_reg19h,0,0x1a);
  8.6416 +    iobase = 0x24;
  8.6417 +  }
  8.6418 +
  8.6419 +  // cylinders
  8.6420 +  cylinders = inb_cmos(iobase) | (inb_cmos(iobase+1) << 8);
  8.6421 +  write_word(ss, hd_cylinders, cylinders);
  8.6422 +
  8.6423 +  // heads
  8.6424 +  write_byte(ss, hd_heads, inb_cmos(iobase+2));
  8.6425 +
  8.6426 +  // sectors per track
  8.6427 +  write_byte(ss, hd_sectors, inb_cmos(iobase+8));
  8.6428 +}
  8.6429 +
  8.6430 +#endif //else BX_USE_ATADRV
  8.6431 +
  8.6432 +
  8.6433 +//////////////////////
  8.6434 +// FLOPPY functions //
  8.6435 +//////////////////////
  8.6436 +
  8.6437 +  bx_bool
  8.6438 +floppy_media_known(drive)
  8.6439 +  Bit16u drive;
  8.6440 +{
  8.6441 +  Bit8u  val8;
  8.6442 +  Bit16u media_state_offset;
  8.6443 +
  8.6444 +  val8 = read_byte(0x0040, 0x003e); // diskette recal status
  8.6445 +  if (drive)
  8.6446 +    val8 >>= 1;
  8.6447 +  val8 &= 0x01;
  8.6448 +  if (val8 == 0)
  8.6449 +    return(0);
  8.6450 +
  8.6451 +  media_state_offset = 0x0090;
  8.6452 +  if (drive)
  8.6453 +    media_state_offset += 1;
  8.6454 +
  8.6455 +  val8 = read_byte(0x0040, media_state_offset);
  8.6456 +  val8 = (val8 >> 4) & 0x01;
  8.6457 +  if (val8 == 0)
  8.6458 +    return(0);
  8.6459 +
  8.6460 +  // check pass, return KNOWN
  8.6461 +  return(1);
  8.6462 +}
  8.6463 +
  8.6464 +  bx_bool
  8.6465 +floppy_media_sense(drive)
  8.6466 +  Bit16u drive;
  8.6467 +{
  8.6468 +  bx_bool retval;
  8.6469 +  Bit16u  media_state_offset;
  8.6470 +  Bit8u   drive_type, config_data, media_state;
  8.6471 +
  8.6472 +  if (floppy_drive_recal(drive) == 0) {
  8.6473 +    return(0);
  8.6474 +    }
  8.6475 +
  8.6476 +  // for now cheat and get drive type from CMOS,
  8.6477 +  // assume media is same as drive type
  8.6478 +
  8.6479 +  // ** config_data **
  8.6480 +  // Bitfields for diskette media control:
  8.6481 +  // Bit(s)  Description (Table M0028)
  8.6482 +  //  7-6  last data rate set by controller
  8.6483 +  //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
  8.6484 +  //  5-4  last diskette drive step rate selected
  8.6485 +  //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
  8.6486 +  //  3-2  {data rate at start of operation}
  8.6487 +  //  1-0  reserved
  8.6488 +
  8.6489 +  // ** media_state **
  8.6490 +  // Bitfields for diskette drive media state:
  8.6491 +  // Bit(s)  Description (Table M0030)
  8.6492 +  //  7-6  data rate
  8.6493 +  //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
  8.6494 +  //  5  double stepping required (e.g. 360kB in 1.2MB)
  8.6495 +  //  4  media type established
  8.6496 +  //  3  drive capable of supporting 4MB media
  8.6497 +  //  2-0  on exit from BIOS, contains
  8.6498 +  //    000 trying 360kB in 360kB
  8.6499 +  //    001 trying 360kB in 1.2MB
  8.6500 +  //    010 trying 1.2MB in 1.2MB
  8.6501 +  //    011 360kB in 360kB established
  8.6502 +  //    100 360kB in 1.2MB established
  8.6503 +  //    101 1.2MB in 1.2MB established
  8.6504 +  //    110 reserved
  8.6505 +  //    111 all other formats/drives
  8.6506 +
  8.6507 +  drive_type = inb_cmos(0x10);
  8.6508 +  if (drive == 0)
  8.6509 +    drive_type >>= 4;
  8.6510 +  else
  8.6511 +    drive_type &= 0x0f;
  8.6512 +  if ( drive_type == 1 ) {
  8.6513 +    // 360K 5.25" drive
  8.6514 +    config_data = 0x00; // 0000 0000
  8.6515 +    media_state = 0x25; // 0010 0101
  8.6516 +    retval = 1;
  8.6517 +    }
  8.6518 +  else if ( drive_type == 2 ) {
  8.6519 +    // 1.2 MB 5.25" drive
  8.6520 +    config_data = 0x00; // 0000 0000
  8.6521 +    media_state = 0x25; // 0010 0101   // need double stepping??? (bit 5)
  8.6522 +    retval = 1;
  8.6523 +    }
  8.6524 +  else if ( drive_type == 3 ) {
  8.6525 +    // 720K 3.5" drive
  8.6526 +    config_data = 0x00; // 0000 0000 ???
  8.6527 +    media_state = 0x17; // 0001 0111
  8.6528 +    retval = 1;
  8.6529 +    }
  8.6530 +  else if ( drive_type == 4 ) {
  8.6531 +    // 1.44 MB 3.5" drive
  8.6532 +    config_data = 0x00; // 0000 0000
  8.6533 +    media_state = 0x17; // 0001 0111
  8.6534 +    retval = 1;
  8.6535 +    }
  8.6536 +  else if ( drive_type == 5 ) {
  8.6537 +    // 2.88 MB 3.5" drive
  8.6538 +    config_data = 0xCC; // 1100 1100
  8.6539 +    media_state = 0xD7; // 1101 0111
  8.6540 +    retval = 1;
  8.6541 +    }
  8.6542 +  //
  8.6543 +  // Extended floppy size uses special cmos setting 
  8.6544 +  else if ( drive_type == 6 ) {
  8.6545 +    // 160k 5.25" drive
  8.6546 +    config_data = 0x00; // 0000 0000
  8.6547 +    media_state = 0x27; // 0010 0111
  8.6548 +    retval = 1;
  8.6549 +    }
  8.6550 +  else if ( drive_type == 7 ) {
  8.6551 +    // 180k 5.25" drive
  8.6552 +    config_data = 0x00; // 0000 0000
  8.6553 +    media_state = 0x27; // 0010 0111
  8.6554 +    retval = 1;
  8.6555 +    }
  8.6556 +  else if ( drive_type == 8 ) {
  8.6557 +    // 320k 5.25" drive
  8.6558 +    config_data = 0x00; // 0000 0000
  8.6559 +    media_state = 0x27; // 0010 0111
  8.6560 +    retval = 1;
  8.6561 +    }
  8.6562 +
  8.6563 +  else {
  8.6564 +    // not recognized
  8.6565 +    config_data = 0x00; // 0000 0000
  8.6566 +    media_state = 0x00; // 0000 0000
  8.6567 +    retval = 0;
  8.6568 +    }
  8.6569 +
  8.6570 +  if (drive == 0)
  8.6571 +    media_state_offset = 0x90;
  8.6572 +  else
  8.6573 +    media_state_offset = 0x91;
  8.6574 +  write_byte(0x0040, 0x008B, config_data);
  8.6575 +  write_byte(0x0040, media_state_offset, media_state);
  8.6576 +
  8.6577 +  return(retval);
  8.6578 +}
  8.6579 +
  8.6580 +  bx_bool
  8.6581 +floppy_drive_recal(drive)
  8.6582 +  Bit16u drive;
  8.6583 +{
  8.6584 +  Bit8u  val8, dor;
  8.6585 +  Bit16u curr_cyl_offset;
  8.6586 +
  8.6587 +  // set 40:3e bit 7 to 0
  8.6588 +  val8 = read_byte(0x0000, 0x043e);
  8.6589 +  val8 &= 0x7f;
  8.6590 +  write_byte(0x0000, 0x043e, val8);
  8.6591 +
  8.6592 +  // turn on motor of selected drive, DMA & int enabled, normal operation
  8.6593 +  if (drive)
  8.6594 +    dor = 0x20;
  8.6595 +  else
  8.6596 +    dor = 0x10;
  8.6597 +  dor |= 0x0c;
  8.6598 +  dor |= drive;
  8.6599 +  outb(0x03f2, dor);
  8.6600 +
  8.6601 +  // reset the disk motor timeout value of INT 08
  8.6602 +  write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
  8.6603 +
  8.6604 +  // check port 3f4 for drive readiness
  8.6605 +  val8 = inb(0x3f4);
  8.6606 +  if ( (val8 & 0xf0) != 0x80 )
  8.6607 +    BX_PANIC("floppy recal:f07: ctrl not ready\n");
  8.6608 +
  8.6609 +  // send Recalibrate command (2 bytes) to controller
  8.6610 +  outb(0x03f5, 0x07);  // 07: Recalibrate
  8.6611 +  outb(0x03f5, drive); // 0=drive0, 1=drive1
  8.6612 +
  8.6613 + // turn on interrupts
  8.6614 +ASM_START
  8.6615 +  sti
  8.6616 +ASM_END
  8.6617 +
  8.6618 +  // wait on 40:3e bit 7 to become 1
  8.6619 +  val8 = (read_byte(0x0000, 0x043e) & 0x80);
  8.6620 +  while ( val8 == 0 ) {
  8.6621 +    val8 = (read_byte(0x0000, 0x043e) & 0x80);
  8.6622 +    }
  8.6623 +
  8.6624 + val8 = 0; // separate asm from while() loop
  8.6625 + // turn off interrupts
  8.6626 +ASM_START
  8.6627 +  cli
  8.6628 +ASM_END
  8.6629 +
  8.6630 +  // set 40:3e bit 7 to 0, and calibrated bit
  8.6631 +  val8 = read_byte(0x0000, 0x043e);
  8.6632 +  val8 &= 0x7f;
  8.6633 +  if (drive) {
  8.6634 +    val8 |= 0x02; // Drive 1 calibrated
  8.6635 +    curr_cyl_offset = 0x0095;
  8.6636 +    }
  8.6637 +  else {
  8.6638 +    val8 |= 0x01; // Drive 0 calibrated
  8.6639 +    curr_cyl_offset = 0x0094;
  8.6640 +    }
  8.6641 +  write_byte(0x0040, 0x003e, val8);
  8.6642 +  write_byte(0x0040, curr_cyl_offset, 0); // current cylinder is 0
  8.6643 +
  8.6644 +  return(1);
  8.6645 +}
  8.6646 +
  8.6647 +
  8.6648 +
  8.6649 +  bx_bool
  8.6650 +floppy_drive_exists(drive)
  8.6651 +  Bit16u drive;
  8.6652 +{
  8.6653 +  Bit8u  drive_type;
  8.6654 +
  8.6655 +  // check CMOS to see if drive exists
  8.6656 +  drive_type = inb_cmos(0x10);
  8.6657 +  if (drive == 0)
  8.6658 +    drive_type >>= 4;
  8.6659 +  else
  8.6660 +    drive_type &= 0x0f;
  8.6661 +  if ( drive_type == 0 )
  8.6662 +    return(0);
  8.6663 +  else
  8.6664 +    return(1);
  8.6665 +}
  8.6666 +
  8.6667 +#if BX_SUPPORT_FLOPPY
  8.6668 +  void
  8.6669 +int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
  8.6670 +  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
  8.6671 +{
  8.6672 +  Bit8u  drive, num_sectors, track, sector, head, status;
  8.6673 +  Bit16u base_address, base_count, base_es;
  8.6674 +  Bit8u  page, mode_register, val8, dor;
  8.6675 +  Bit8u  return_status[7];
  8.6676 +  Bit8u  drive_type, num_floppies, ah;
  8.6677 +  Bit16u es, last_addr;
  8.6678 +
  8.6679 +  BX_DEBUG_INT13_FL("int13_diskette: AX=%04x BX=%04x CX=%04x DX=%04x ES=%04x\n", AX, BX, CX, DX, ES);
  8.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);
  8.6681 +
  8.6682 +  ah = GET_AH();
  8.6683 +
  8.6684 +  switch ( ah ) {
  8.6685 +    case 0x00: // diskette controller reset
  8.6686 +BX_DEBUG_INT13_FL("floppy f00\n");
  8.6687 +      drive = GET_ELDL();
  8.6688 +      if (drive > 1) {
  8.6689 +        SET_AH(1); // invalid param
  8.6690 +        set_diskette_ret_status(1);
  8.6691 +        SET_CF();
  8.6692 +        return;
  8.6693 +        }
  8.6694 +      drive_type = inb_cmos(0x10);
  8.6695 +
  8.6696 +      if (drive == 0)
  8.6697 +        drive_type >>= 4;
  8.6698 +      else
  8.6699 +        drive_type &= 0x0f;
  8.6700 +      if (drive_type == 0) {
  8.6701 +        SET_AH(0x80); // drive not responding
  8.6702 +        set_diskette_ret_status(0x80);
  8.6703 +        SET_CF();
  8.6704 +        return;
  8.6705 +        }
  8.6706 +      SET_AH(0);
  8.6707 +      set_diskette_ret_status(0);
  8.6708 +      CLEAR_CF(); // successful
  8.6709 +      set_diskette_current_cyl(drive, 0); // current cylinder
  8.6710 +      return;
  8.6711 +
  8.6712 +    case 0x01: // Read Diskette Status
  8.6713 +      CLEAR_CF();
  8.6714 +      val8 = read_byte(0x0000, 0x0441);
  8.6715 +      SET_AH(val8);
  8.6716 +      if (val8) {
  8.6717 +        SET_CF();
  8.6718 +        }
  8.6719 +      return;
  8.6720 +
  8.6721 +    case 0x02: // Read Diskette Sectors
  8.6722 +    case 0x03: // Write Diskette Sectors
  8.6723 +    case 0x04: // Verify Diskette Sectors
  8.6724 +      num_sectors = GET_AL();
  8.6725 +      track       = GET_CH();
  8.6726 +      sector      = GET_CL();
  8.6727 +      head        = GET_DH();
  8.6728 +      drive       = GET_ELDL();
  8.6729 +
  8.6730 +      if ( (drive > 1) || (head > 1) ||
  8.6731 +           (num_sectors == 0) || (num_sectors > 72) ) {
  8.6732 +BX_INFO("floppy: drive>1 || head>1 ...\n");
  8.6733 +        SET_AH(1);
  8.6734 +        set_diskette_ret_status(1);
  8.6735 +        SET_AL(0); // no sectors read
  8.6736 +        SET_CF(); // error occurred
  8.6737 +        return;
  8.6738 +        }
  8.6739 +
  8.6740 +      // see if drive exists
  8.6741 +      if (floppy_drive_exists(drive) == 0) {
  8.6742 +        SET_AH(0x80); // not responding
  8.6743 +        set_diskette_ret_status(0x80);
  8.6744 +        SET_AL(0); // no sectors read
  8.6745 +        SET_CF(); // error occurred
  8.6746 +        return;
  8.6747 +        }
  8.6748 +
  8.6749 +      // see if media in drive, and type is known
  8.6750 +      if (floppy_media_known(drive) == 0) {
  8.6751 +        if (floppy_media_sense(drive) == 0) {
  8.6752 +          SET_AH(0x0C); // Media type not found
  8.6753 +          set_diskette_ret_status(0x0C);
  8.6754 +          SET_AL(0); // no sectors read
  8.6755 +          SET_CF(); // error occurred
  8.6756 +          return;
  8.6757 +          }
  8.6758 +        }
  8.6759 +
  8.6760 +      if (ah == 0x02) {
  8.6761 +        // Read Diskette Sectors
  8.6762 +
  8.6763 +        //-----------------------------------
  8.6764 +        // set up DMA controller for transfer
  8.6765 +        //-----------------------------------
  8.6766 +
  8.6767 +        // es:bx = pointer to where to place information from diskette
  8.6768 +        // port 04: DMA-1 base and current address, channel 2
  8.6769 +        // port 05: DMA-1 base and current count, channel 2
  8.6770 +        page = (ES >> 12);   // upper 4 bits
  8.6771 +        base_es = (ES << 4); // lower 16bits contributed by ES
  8.6772 +        base_address = base_es + BX; // lower 16 bits of address
  8.6773 +                                     // contributed by ES:BX
  8.6774 +        if ( base_address < base_es ) {
  8.6775 +          // in case of carry, adjust page by 1
  8.6776 +          page++;
  8.6777 +          }
  8.6778 +        base_count = (num_sectors * 512) - 1;
  8.6779 +
  8.6780 +        // check for 64K boundary overrun
  8.6781 +        last_addr = base_address + base_count;
  8.6782 +        if (last_addr < base_address) {
  8.6783 +          SET_AH(0x09);
  8.6784 +          set_diskette_ret_status(0x09);
  8.6785 +          SET_AL(0); // no sectors read
  8.6786 +          SET_CF(); // error occurred
  8.6787 +          return;
  8.6788 +          }
  8.6789 +
  8.6790 +        BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
  8.6791 +        outb(0x000a, 0x06);
  8.6792 +
  8.6793 +  BX_DEBUG_INT13_FL("clear flip-flop\n");
  8.6794 +        outb(0x000c, 0x00); // clear flip-flop
  8.6795 +        outb(0x0004, base_address);
  8.6796 +        outb(0x0004, base_address>>8);
  8.6797 +  BX_DEBUG_INT13_FL("clear flip-flop\n");
  8.6798 +        outb(0x000c, 0x00); // clear flip-flop
  8.6799 +        outb(0x0005, base_count);
  8.6800 +        outb(0x0005, base_count>>8);
  8.6801 +
  8.6802 +        // port 0b: DMA-1 Mode Register
  8.6803 +        mode_register = 0x46; // single mode, increment, autoinit disable,
  8.6804 +                              // transfer type=write, channel 2
  8.6805 +  BX_DEBUG_INT13_FL("setting mode register\n");
  8.6806 +        outb(0x000b, mode_register);
  8.6807 +
  8.6808 +  BX_DEBUG_INT13_FL("setting page register\n");
  8.6809 +        // port 81: DMA-1 Page Register, channel 2
  8.6810 +        outb(0x0081, page);
  8.6811 +
  8.6812 +  BX_DEBUG_INT13_FL("unmask chan 2\n");
  8.6813 +        outb(0x000a, 0x02); // unmask channel 2
  8.6814 +
  8.6815 +        BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
  8.6816 +        outb(0x000a, 0x02);
  8.6817 +
  8.6818 +        //--------------------------------------
  8.6819 +        // set up floppy controller for transfer
  8.6820 +        //--------------------------------------
  8.6821 +
  8.6822 +        // set 40:3e bit 7 to 0
  8.6823 +        val8 = read_byte(0x0000, 0x043e);
  8.6824 +        val8 &= 0x7f;
  8.6825 +        write_byte(0x0000, 0x043e, val8);
  8.6826 +
  8.6827 +        // turn on motor of selected drive, DMA & int enabled, normal operation
  8.6828 +        if (drive)
  8.6829 +          dor = 0x20;
  8.6830 +        else
  8.6831 +          dor = 0x10;
  8.6832 +        dor |= 0x0c;
  8.6833 +        dor |= drive;
  8.6834 +        outb(0x03f2, dor);
  8.6835 +
  8.6836 +        // reset the disk motor timeout value of INT 08
  8.6837 +        write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
  8.6838 +
  8.6839 +        // check port 3f4 for drive readiness
  8.6840 +        val8 = inb(0x3f4);
  8.6841 +        if ( (val8 & 0xf0) != 0x80 )
  8.6842 +          BX_PANIC("int13_diskette:f02: ctrl not ready\n");
  8.6843 +
  8.6844 +        // send read-normal-data command (9 bytes) to controller
  8.6845 +        outb(0x03f5, 0xe6); // e6: read normal data
  8.6846 +        outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
  8.6847 +        outb(0x03f5, track);
  8.6848 +        outb(0x03f5, head);
  8.6849 +        outb(0x03f5, sector);
  8.6850 +        outb(0x03f5, 2); // 512 byte sector size
  8.6851 +        outb(0x03f5, 0); // last sector number possible on track
  8.6852 +        outb(0x03f5, 0); // Gap length
  8.6853 +        outb(0x03f5, 0xff); // Gap length
  8.6854 +
  8.6855 +       // turn on interrupts
  8.6856 +  ASM_START
  8.6857 +        sti
  8.6858 +  ASM_END
  8.6859 +
  8.6860 +        // wait on 40:3e bit 7 to become 1
  8.6861 +        val8 = (read_byte(0x0000, 0x043e) & 0x80);
  8.6862 +        while ( val8 == 0 ) {
  8.6863 +          val8 = (read_byte(0x0000, 0x043e) & 0x80);
  8.6864 +          }
  8.6865 +
  8.6866 +       val8 = 0; // separate asm from while() loop
  8.6867 +       // turn off interrupts
  8.6868 +  ASM_START
  8.6869 +        cli
  8.6870 +  ASM_END
  8.6871 +
  8.6872 +        // set 40:3e bit 7 to 0
  8.6873 +        val8 = read_byte(0x0000, 0x043e);
  8.6874 +        val8 &= 0x7f;
  8.6875 +        write_byte(0x0000, 0x043e, val8);
  8.6876 +
  8.6877 +        // check port 3f4 for accessibility to status bytes
  8.6878 +        val8 = inb(0x3f4);
  8.6879 +        if ( (val8 & 0xc0) != 0xc0 )
  8.6880 +          BX_PANIC("int13_diskette: ctrl not ready\n");
  8.6881 +
  8.6882 +        // read 7 return status bytes from controller
  8.6883 +        // using loop index broken, have to unroll...
  8.6884 +        return_status[0] = inb(0x3f5);
  8.6885 +        return_status[1] = inb(0x3f5);
  8.6886 +        return_status[2] = inb(0x3f5);
  8.6887 +        return_status[3] = inb(0x3f5);
  8.6888 +        return_status[4] = inb(0x3f5);
  8.6889 +        return_status[5] = inb(0x3f5);
  8.6890 +        return_status[6] = inb(0x3f5);
  8.6891 +        // record in BIOS Data Area
  8.6892 +        write_byte(0x0040, 0x0042, return_status[0]);
  8.6893 +        write_byte(0x0040, 0x0043, return_status[1]);
  8.6894 +        write_byte(0x0040, 0x0044, return_status[2]);
  8.6895 +        write_byte(0x0040, 0x0045, return_status[3]);
  8.6896 +        write_byte(0x0040, 0x0046, return_status[4]);
  8.6897 +        write_byte(0x0040, 0x0047, return_status[5]);
  8.6898 +        write_byte(0x0040, 0x0048, return_status[6]);
  8.6899 +
  8.6900 +        if ( (return_status[0] & 0xc0) != 0 ) {
  8.6901 +          SET_AH(0x20);
  8.6902 +          set_diskette_ret_status(0x20);
  8.6903 +          SET_AL(0); // no sectors read
  8.6904 +          SET_CF(); // error occurred
  8.6905 +          return;
  8.6906 +          }
  8.6907 +
  8.6908 +        // ??? should track be new val from return_status[3] ?
  8.6909 +        set_diskette_current_cyl(drive, track);
  8.6910 +        // AL = number of sectors read (same value as passed)
  8.6911 +        SET_AH(0x00); // success
  8.6912 +        CLEAR_CF();   // success
  8.6913 +        return;
  8.6914 +        }
  8.6915 +      else if (ah == 0x03) {
  8.6916 +        // Write Diskette Sectors
  8.6917 +
  8.6918 +        //-----------------------------------
  8.6919 +        // set up DMA controller for transfer
  8.6920 +        //-----------------------------------
  8.6921 +
  8.6922 +        // es:bx = pointer to where to place information from diskette
  8.6923 +        // port 04: DMA-1 base and current address, channel 2
  8.6924 +        // port 05: DMA-1 base and current count, channel 2
  8.6925 +        page = (ES >> 12);   // upper 4 bits
  8.6926 +        base_es = (ES << 4); // lower 16bits contributed by ES
  8.6927 +        base_address = base_es + BX; // lower 16 bits of address
  8.6928 +                                     // contributed by ES:BX
  8.6929 +        if ( base_address < base_es ) {
  8.6930 +          // in case of carry, adjust page by 1
  8.6931 +          page++;
  8.6932 +          }
  8.6933 +        base_count = (num_sectors * 512) - 1;
  8.6934 +
  8.6935 +        // check for 64K boundary overrun
  8.6936 +        last_addr = base_address + base_count;
  8.6937 +        if (last_addr < base_address) {
  8.6938 +          SET_AH(0x09);
  8.6939 +          set_diskette_ret_status(0x09);
  8.6940 +          SET_AL(0); // no sectors read
  8.6941 +          SET_CF(); // error occurred
  8.6942 +          return;
  8.6943 +          }
  8.6944 +
  8.6945 +        BX_DEBUG_INT13_FL("masking DMA-1 c2\n");
  8.6946 +        outb(0x000a, 0x06);
  8.6947 +
  8.6948 +        outb(0x000c, 0x00); // clear flip-flop
  8.6949 +        outb(0x0004, base_address);
  8.6950 +        outb(0x0004, base_address>>8);
  8.6951 +        outb(0x000c, 0x00); // clear flip-flop
  8.6952 +        outb(0x0005, base_count);
  8.6953 +        outb(0x0005, base_count>>8);
  8.6954 +
  8.6955 +        // port 0b: DMA-1 Mode Register
  8.6956 +        mode_register = 0x4a; // single mode, increment, autoinit disable,
  8.6957 +                              // transfer type=read, channel 2
  8.6958 +        outb(0x000b, mode_register);
  8.6959 +
  8.6960 +        // port 81: DMA-1 Page Register, channel 2
  8.6961 +        outb(0x0081, page);
  8.6962 +
  8.6963 +        BX_DEBUG_INT13_FL("unmasking DMA-1 c2\n");
  8.6964 +        outb(0x000a, 0x02);
  8.6965 +
  8.6966 +        //--------------------------------------
  8.6967 +        // set up floppy controller for transfer
  8.6968 +        //--------------------------------------
  8.6969 +
  8.6970 +        // set 40:3e bit 7 to 0
  8.6971 +        val8 = read_byte(0x0000, 0x043e);
  8.6972 +        val8 &= 0x7f;
  8.6973 +        write_byte(0x0000, 0x043e, val8);
  8.6974 +
  8.6975 +        // turn on motor of selected drive, DMA & int enabled, normal operation
  8.6976 +        if (drive)
  8.6977 +          dor = 0x20;
  8.6978 +        else
  8.6979 +          dor = 0x10;
  8.6980 +        dor |= 0x0c;
  8.6981 +        dor |= drive;
  8.6982 +        outb(0x03f2, dor);
  8.6983 +
  8.6984 +        // reset the disk motor timeout value of INT 08
  8.6985 +        write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
  8.6986 +
  8.6987 +        // check port 3f4 for drive readiness
  8.6988 +        val8 = inb(0x3f4);
  8.6989 +        if ( (val8 & 0xf0) != 0x80 )
  8.6990 +          BX_PANIC("int13_diskette:f03: ctrl not ready\n");
  8.6991 +
  8.6992 +        // send read-normal-data command (9 bytes) to controller
  8.6993 +        outb(0x03f5, 0xc5); // c5: write normal data
  8.6994 +        outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
  8.6995 +        outb(0x03f5, track);
  8.6996 +        outb(0x03f5, head);
  8.6997 +        outb(0x03f5, sector);
  8.6998 +        outb(0x03f5, 2); // 512 byte sector size
  8.6999 +        outb(0x03f5, 0); // last sector number possible on track
  8.7000 +        outb(0x03f5, 0); // Gap length
  8.7001 +        outb(0x03f5, 0xff); // Gap length
  8.7002 +
  8.7003 +       // turn on interrupts
  8.7004 +  ASM_START
  8.7005 +        sti
  8.7006 +  ASM_END
  8.7007 +
  8.7008 +        // wait on 40:3e bit 7 to become 1
  8.7009 +        val8 = (read_byte(0x0000, 0x043e) & 0x80);
  8.7010 +        while ( val8 == 0 ) {
  8.7011 +          val8 = (read_byte(0x0000, 0x043e) & 0x80);
  8.7012 +          }
  8.7013 +
  8.7014 +       val8 = 0; // separate asm from while() loop
  8.7015 +       // turn off interrupts
  8.7016 +  ASM_START
  8.7017 +        cli
  8.7018 +  ASM_END
  8.7019 +
  8.7020 +        // set 40:3e bit 7 to 0
  8.7021 +        val8 = read_byte(0x0000, 0x043e);
  8.7022 +        val8 &= 0x7f;
  8.7023 +        write_byte(0x0000, 0x043e, val8);
  8.7024 +
  8.7025 +        // check port 3f4 for accessibility to status bytes
  8.7026 +        val8 = inb(0x3f4);
  8.7027 +        if ( (val8 & 0xc0) != 0xc0 )
  8.7028 +          BX_PANIC("int13_diskette: ctrl not ready\n");
  8.7029 +
  8.7030 +        // read 7 return status bytes from controller
  8.7031 +        // using loop index broken, have to unroll...
  8.7032 +        return_status[0] = inb(0x3f5);
  8.7033 +        return_status[1] = inb(0x3f5);
  8.7034 +        return_status[2] = inb(0x3f5);
  8.7035 +        return_status[3] = inb(0x3f5);
  8.7036 +        return_status[4] = inb(0x3f5);
  8.7037 +        return_status[5] = inb(0x3f5);
  8.7038 +        return_status[6] = inb(0x3f5);
  8.7039 +        // record in BIOS Data Area
  8.7040 +        write_byte(0x0040, 0x0042, return_status[0]);
  8.7041 +        write_byte(0x0040, 0x0043, return_status[1]);
  8.7042 +        write_byte(0x0040, 0x0044, return_status[2]);
  8.7043 +        write_byte(0x0040, 0x0045, return_status[3]);
  8.7044 +        write_byte(0x0040, 0x0046, return_status[4]);
  8.7045 +        write_byte(0x0040, 0x0047, return_status[5]);
  8.7046 +        write_byte(0x0040, 0x0048, return_status[6]);
  8.7047 +
  8.7048 +        if ( (return_status[0] & 0xc0) != 0 ) {
  8.7049 +          if ( (return_status[1] & 0x02) != 0 ) {
  8.7050 +            // diskette not writable.
  8.7051 +            // AH=status code=0x03 (tried to write on write-protected disk)
  8.7052 +            // AL=number of sectors written=0
  8.7053 +            AX = 0x0300;
  8.7054 +            SET_CF();
  8.7055 +            return;
  8.7056 +          } else {
  8.7057 +            BX_PANIC("int13_diskette_function: read error\n");
  8.7058 +          }
  8.7059 +        }
  8.7060 +
  8.7061 +        // ??? should track be new val from return_status[3] ?
  8.7062 +        set_diskette_current_cyl(drive, track);
  8.7063 +        // AL = number of sectors read (same value as passed)
  8.7064 +        SET_AH(0x00); // success
  8.7065 +        CLEAR_CF();   // success
  8.7066 +        return;
  8.7067 +        }
  8.7068 +      else {  // if (ah == 0x04)
  8.7069 +        // Verify Diskette Sectors
  8.7070 +
  8.7071 +        // ??? should track be new val from return_status[3] ?
  8.7072 +        set_diskette_current_cyl(drive, track);
  8.7073 +        // AL = number of sectors verified (same value as passed)
  8.7074 +        CLEAR_CF();   // success
  8.7075 +        SET_AH(0x00); // success
  8.7076 +        return;
  8.7077 +        }
  8.7078 +
  8.7079 +
  8.7080 +    case 0x05: // format diskette track
  8.7081 +BX_DEBUG_INT13_FL("floppy f05\n");
  8.7082 +
  8.7083 +      num_sectors = GET_AL();
  8.7084 +      track       = GET_CH();
  8.7085 +      head        = GET_DH();
  8.7086 +      drive       = GET_ELDL();
  8.7087 +
  8.7088 +      if ((drive > 1) || (head > 1) || (track > 79) ||
  8.7089 +          (num_sectors == 0) || (num_sectors > 18)) {
  8.7090 +        SET_AH(1);
  8.7091 +        set_diskette_ret_status(1);
  8.7092 +        SET_CF(); // error occurred
  8.7093 +        }
  8.7094 +
  8.7095 +      // see if drive exists
  8.7096 +      if (floppy_drive_exists(drive) == 0) {
  8.7097 +        SET_AH(0x80); // drive not responding
  8.7098 +        set_diskette_ret_status(0x80);
  8.7099 +        SET_CF(); // error occurred
  8.7100 +        return;
  8.7101 +        }
  8.7102 +
  8.7103 +      // see if media in drive, and type is known
  8.7104 +      if (floppy_media_known(drive) == 0) {
  8.7105 +        if (floppy_media_sense(drive) == 0) {
  8.7106 +          SET_AH(0x0C); // Media type not found
  8.7107 +          set_diskette_ret_status(0x0C);
  8.7108 +          SET_AL(0); // no sectors read
  8.7109 +          SET_CF(); // error occurred
  8.7110 +          return;
  8.7111 +          }
  8.7112 +        }
  8.7113 +
  8.7114 +      // set up DMA controller for transfer
  8.7115 +      page = (ES >> 12);   // upper 4 bits
  8.7116 +      base_es = (ES << 4); // lower 16bits contributed by ES
  8.7117 +      base_address = base_es + BX; // lower 16 bits of address
  8.7118 +                                   // contributed by ES:BX
  8.7119 +      if ( base_address < base_es ) {
  8.7120 +        // in case of carry, adjust page by 1
  8.7121 +        page++;
  8.7122 +        }
  8.7123 +      base_count = (num_sectors * 4) - 1;
  8.7124 +
  8.7125 +      // check for 64K boundary overrun
  8.7126 +      last_addr = base_address + base_count;
  8.7127 +      if (last_addr < base_address) {
  8.7128 +        SET_AH(0x09);
  8.7129 +        set_diskette_ret_status(0x09);
  8.7130 +        SET_AL(0); // no sectors read
  8.7131 +        SET_CF(); // error occurred
  8.7132 +        return;
  8.7133 +        }
  8.7134 +
  8.7135 +      outb(0x000a, 0x06);
  8.7136 +      outb(0x000c, 0x00); // clear flip-flop
  8.7137 +      outb(0x0004, base_address);
  8.7138 +      outb(0x0004, base_address>>8);
  8.7139 +      outb(0x000c, 0x00); // clear flip-flop
  8.7140 +      outb(0x0005, base_count);
  8.7141 +      outb(0x0005, base_count>>8);
  8.7142 +      mode_register = 0x4a; // single mode, increment, autoinit disable,
  8.7143 +                            // transfer type=read, channel 2
  8.7144 +      outb(0x000b, mode_register);
  8.7145 +      // port 81: DMA-1 Page Register, channel 2
  8.7146 +      outb(0x0081, page);
  8.7147 +      outb(0x000a, 0x02);
  8.7148 +
  8.7149 +      // set up floppy controller for transfer
  8.7150 +      val8 = read_byte(0x0000, 0x043e);
  8.7151 +      val8 &= 0x7f;
  8.7152 +      write_byte(0x0000, 0x043e, val8);
  8.7153 +      // turn on motor of selected drive, DMA & int enabled, normal operation
  8.7154 +      if (drive)
  8.7155 +        dor = 0x20;
  8.7156 +      else
  8.7157 +        dor = 0x10;
  8.7158 +      dor |= 0x0c;
  8.7159 +      dor |= drive;
  8.7160 +      outb(0x03f2, dor);
  8.7161 +
  8.7162 +      // reset the disk motor timeout value of INT 08
  8.7163 +      write_byte(0x40,0x40, BX_FLOPPY_ON_CNT);
  8.7164 +
  8.7165 +      // check port 3f4 for drive readiness
  8.7166 +      val8 = inb(0x3f4);
  8.7167 +      if ( (val8 & 0xf0) != 0x80 )
  8.7168 +        BX_PANIC("int13_diskette:f05: ctrl not ready\n");
  8.7169 +
  8.7170 +      // send read-normal-data command (6 bytes) to controller
  8.7171 +      outb(0x03f5, 0x4d); // 4d: format track
  8.7172 +      outb(0x03f5, (head << 2) | drive); // HD DR1 DR2
  8.7173 +      outb(0x03f5, 2); // 512 byte sector size
  8.7174 +      outb(0x03f5, num_sectors); // number of sectors per track
  8.7175 +      outb(0x03f5, 0); // Gap length
  8.7176 +      outb(0x03f5, 0xf6); // Fill byte
  8.7177 +      // turn on interrupts
  8.7178 +  ASM_START
  8.7179 +      sti
  8.7180 +  ASM_END
  8.7181 +      // wait on 40:3e bit 7 to become 1
  8.7182 +      val8 = (read_byte(0x0000, 0x043e) & 0x80);
  8.7183 +      while ( val8 == 0 ) {
  8.7184 +        val8 = (read_byte(0x0000, 0x043e) & 0x80);
  8.7185 +        }
  8.7186 +     val8 = 0; // separate asm from while() loop
  8.7187 +     // turn off interrupts
  8.7188 +  ASM_START
  8.7189 +      cli
  8.7190 +  ASM_END
  8.7191 +      // set 40:3e bit 7 to 0
  8.7192 +      val8 = read_byte(0x0000, 0x043e);
  8.7193 +      val8 &= 0x7f;
  8.7194 +      write_byte(0x0000, 0x043e, val8);
  8.7195 +      // check port 3f4 for accessibility to status bytes
  8.7196 +      val8 = inb(0x3f4);
  8.7197 +      if ( (val8 & 0xc0) != 0xc0 )
  8.7198 +        BX_PANIC("int13_diskette: ctrl not ready\n");
  8.7199 +
  8.7200 +      // read 7 return status bytes from controller
  8.7201 +      // using loop index broken, have to unroll...
  8.7202 +      return_status[0] = inb(0x3f5);
  8.7203 +      return_status[1] = inb(0x3f5);
  8.7204 +      return_status[2] = inb(0x3f5);
  8.7205 +      return_status[3] = inb(0x3f5);
  8.7206 +      return_status[4] = inb(0x3f5);
  8.7207 +      return_status[5] = inb(0x3f5);
  8.7208 +      return_status[6] = inb(0x3f5);
  8.7209 +      // record in BIOS Data Area
  8.7210 +      write_byte(0x0040, 0x0042, return_status[0]);
  8.7211 +      write_byte(0x0040, 0x0043, return_status[1]);
  8.7212 +      write_byte(0x0040, 0x0044, return_status[2]);
  8.7213 +      write_byte(0x0040, 0x0045, return_status[3]);
  8.7214 +      write_byte(0x0040, 0x0046, return_status[4]);
  8.7215 +      write_byte(0x0040, 0x0047, return_status[5]);
  8.7216 +      write_byte(0x0040, 0x0048, return_status[6]);
  8.7217 +
  8.7218 +      if ( (return_status[0] & 0xc0) != 0 ) {
  8.7219 +        if ( (return_status[1] & 0x02) != 0 ) {
  8.7220 +          // diskette not writable.
  8.7221 +          // AH=status code=0x03 (tried to write on write-protected disk)
  8.7222 +          // AL=number of sectors written=0
  8.7223 +          AX = 0x0300;
  8.7224 +          SET_CF();
  8.7225 +          return;
  8.7226 +        } else {
  8.7227 +          BX_PANIC("int13_diskette_function: write error\n");
  8.7228 +        }
  8.7229 +      }
  8.7230 +
  8.7231 +      SET_AH(0);
  8.7232 +      set_diskette_ret_status(0);
  8.7233 +      set_diskette_current_cyl(drive, 0);
  8.7234 +      CLEAR_CF(); // successful
  8.7235 +      return;
  8.7236 +
  8.7237 +
  8.7238 +    case 0x08: // read diskette drive parameters
  8.7239 +BX_DEBUG_INT13_FL("floppy f08\n");
  8.7240 +      drive = GET_ELDL();
  8.7241 +
  8.7242 +      if (drive > 1) {
  8.7243 +        AX = 0;
  8.7244 +        BX = 0;
  8.7245 +        CX = 0;
  8.7246 +        DX = 0;
  8.7247 +        ES = 0;
  8.7248 +        DI = 0;
  8.7249 +        SET_DL(num_floppies);
  8.7250 +        SET_CF();
  8.7251 +        return;
  8.7252 +        }
  8.7253 +
  8.7254 +      drive_type = inb_cmos(0x10);
  8.7255 +      num_floppies = 0;
  8.7256 +      if (drive_type & 0xf0)
  8.7257 +        num_floppies++;
  8.7258 +      if (drive_type & 0x0f)
  8.7259 +        num_floppies++;
  8.7260 +
  8.7261 +      if (drive == 0)
  8.7262 +        drive_type >>= 4;
  8.7263 +      else
  8.7264 +        drive_type &= 0x0f;
  8.7265 +
  8.7266 +      SET_BH(0);
  8.7267 +      SET_BL(drive_type);
  8.7268 +      SET_AH(0);
  8.7269 +      SET_AL(0);
  8.7270 +      SET_DL(num_floppies);
  8.7271 +
  8.7272 +      switch (drive_type) {
  8.7273 +        case 0: // none
  8.7274 +          CX = 0;
  8.7275 +          SET_DH(0); // max head #
  8.7276 +          break;
  8.7277 +
  8.7278 +        case 1: // 360KB, 5.25"
  8.7279 +          CX = 0x2709; // 40 tracks, 9 sectors
  8.7280 +          SET_DH(1); // max head #
  8.7281 +          break;
  8.7282 +
  8.7283 +        case 2: // 1.2MB, 5.25"
  8.7284 +          CX = 0x4f0f; // 80 tracks, 15 sectors
  8.7285 +          SET_DH(1); // max head #
  8.7286 +          break;
  8.7287 +
  8.7288 +        case 3: // 720KB, 3.5"
  8.7289 +          CX = 0x4f09; // 80 tracks, 9 sectors
  8.7290 +          SET_DH(1); // max head #
  8.7291 +          break;
  8.7292 +
  8.7293 +        case 4: // 1.44MB, 3.5"
  8.7294 +          CX = 0x4f12; // 80 tracks, 18 sectors
  8.7295 +          SET_DH(1); // max head #
  8.7296 +          break;
  8.7297 +
  8.7298 +        case 5: // 2.88MB, 3.5"
  8.7299 +          CX = 0x4f24; // 80 tracks, 36 sectors
  8.7300 +          SET_DH(1); // max head #
  8.7301 +          break;
  8.7302 +
  8.7303 +        case 6: // 160k, 5.25"
  8.7304 +          CX = 0x2708; // 40 tracks, 8 sectors
  8.7305 +          SET_DH(0); // max head #
  8.7306 +          break;
  8.7307 +
  8.7308 +        case 7: // 180k, 5.25"
  8.7309 +          CX = 0x2709; // 40 tracks, 9 sectors
  8.7310 +          SET_DH(0); // max head #
  8.7311 +          break;
  8.7312 +
  8.7313 +        case 8: // 320k, 5.25"
  8.7314 +          CX = 0x2708; // 40 tracks, 8 sectors
  8.7315 +          SET_DH(1); // max head #
  8.7316 +          break;
  8.7317 +
  8.7318 +        default: // ?
  8.7319 +          BX_PANIC("floppy: int13: bad floppy type\n");
  8.7320 +        }
  8.7321 +
  8.7322 +      /* set es & di to point to 11 byte diskette param table in ROM */
  8.7323 +ASM_START
  8.7324 +      push bp
  8.7325 +      mov  bp, sp
  8.7326 +      mov ax, #diskette_param_table2
  8.7327 +      mov _int13_diskette_function.DI+2[bp], ax
  8.7328 +      mov _int13_diskette_function.ES+2[bp], cs
  8.7329 +      pop  bp
  8.7330 +ASM_END
  8.7331 +      CLEAR_CF(); // success
  8.7332 +      /* disk status not changed upon success */
  8.7333 +      return;
  8.7334 +
  8.7335 +
  8.7336 +    case 0x15: // read diskette drive type
  8.7337 +BX_DEBUG_INT13_FL("floppy f15\n");
  8.7338 +      drive = GET_ELDL();
  8.7339 +      if (drive > 1) {
  8.7340 +        SET_AH(0); // only 2 drives supported
  8.7341 +        // set_diskette_ret_status here ???
  8.7342 +        SET_CF();
  8.7343 +        return;
  8.7344 +        }
  8.7345 +      drive_type = inb_cmos(0x10);
  8.7346 +
  8.7347 +      if (drive == 0)
  8.7348 +        drive_type >>= 4;
  8.7349 +      else
  8.7350 +        drive_type &= 0x0f;
  8.7351 +      CLEAR_CF(); // successful, not present
  8.7352 +      if (drive_type==0) {
  8.7353 +        SET_AH(0); // drive not present
  8.7354 +        }
  8.7355 +      else {
  8.7356 +        SET_AH(1); // drive present, does not support change line
  8.7357 +        }
  8.7358 +
  8.7359 +      return;
  8.7360 +
  8.7361 +    case 0x16: // get diskette change line status
  8.7362 +BX_DEBUG_INT13_FL("floppy f16\n");
  8.7363 +      drive = GET_ELDL();
  8.7364 +      if (drive > 1) {
  8.7365 +        SET_AH(0x01); // invalid drive
  8.7366 +        set_diskette_ret_status(0x01);
  8.7367 +        SET_CF();
  8.7368 +        return;
  8.7369 +        }
  8.7370 +
  8.7371 +      SET_AH(0x06); // change line not supported
  8.7372 +      set_diskette_ret_status(0x06);
  8.7373 +      SET_CF();
  8.7374 +      return;
  8.7375 +
  8.7376 +    case 0x17: // set diskette type for format(old)
  8.7377 +BX_DEBUG_INT13_FL("floppy f17\n");
  8.7378 +      /* not used for 1.44M floppies */
  8.7379 +      SET_AH(0x01); // not supported
  8.7380 +      set_diskette_ret_status(1); /* not supported */
  8.7381 +      SET_CF();
  8.7382 +      return;
  8.7383 +
  8.7384 +    case 0x18: // set diskette type for format(new)
  8.7385 +BX_DEBUG_INT13_FL("floppy f18\n");
  8.7386 +      SET_AH(0x01); // do later
  8.7387 +      set_diskette_ret_status(1);
  8.7388 +      SET_CF();
  8.7389 +      return;
  8.7390 +
  8.7391 +    default:
  8.7392 +        BX_INFO("int13_diskette: unsupported AH=%02x\n", GET_AH());
  8.7393 +
  8.7394 +      // if ( (ah==0x20) || ((ah>=0x41) && (ah<=0x49)) || (ah==0x4e) ) {
  8.7395 +        SET_AH(0x01); // ???
  8.7396 +        set_diskette_ret_status(1);
  8.7397 +        SET_CF();
  8.7398 +        return;
  8.7399 +      //   }
  8.7400 +    }
  8.7401 +}
  8.7402 +#else  // #if BX_SUPPORT_FLOPPY
  8.7403 +  void
  8.7404 +int13_diskette_function(DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS)
  8.7405 +  Bit16u DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS;
  8.7406 +{
  8.7407 +  Bit8u  val8;
  8.7408 +
  8.7409 +  switch ( GET_AH() ) {
  8.7410 +
  8.7411 +    case 0x01: // Read Diskette Status
  8.7412 +      CLEAR_CF();
  8.7413 +      val8 = read_byte(0x0000, 0x0441);
  8.7414 +      SET_AH(val8);
  8.7415 +      if (val8) {
  8.7416 +        SET_CF();
  8.7417 +        }
  8.7418 +      return;
  8.7419 +
  8.7420 +    default:
  8.7421 +      SET_CF();
  8.7422 +      write_byte(0x0000, 0x0441, 0x01);
  8.7423 +      SET_AH(0x01);
  8.7424 +    }
  8.7425 +}
  8.7426 +#endif  // #if BX_SUPPORT_FLOPPY
  8.7427 +
  8.7428 + void
  8.7429 +set_diskette_ret_status(value)
  8.7430 +  Bit8u value;
  8.7431 +{
  8.7432 +  write_byte(0x0040, 0x0041, value);
  8.7433 +}
  8.7434 +
  8.7435 +  void
  8.7436 +set_diskette_current_cyl(drive, cyl)
  8.7437 +  Bit8u drive;
  8.7438 +  Bit8u cyl;
  8.7439 +{
  8.7440 +  if (drive > 1)
  8.7441 +    BX_PANIC("set_diskette_current_cyl(): drive > 1\n");
  8.7442 +  write_byte(0x0040, 0x0094+drive, cyl);
  8.7443 +}
  8.7444 +
  8.7445 +  void
  8.7446 +determine_floppy_media(drive)
  8.7447 +  Bit16u drive;
  8.7448 +{
  8.7449 +#if 0
  8.7450 +  Bit8u  val8, DOR, ctrl_info;
  8.7451 +
  8.7452 +  ctrl_info = read_byte(0x0040, 0x008F);
  8.7453 +  if (drive==1)
  8.7454 +    ctrl_info >>= 4;
  8.7455 +  else
  8.7456 +    ctrl_info &= 0x0f;
  8.7457 +
  8.7458 +#if 0
  8.7459 +  if (drive == 0) {
  8.7460 +    DOR = 0x1c; // DOR: drive0 motor on, DMA&int enabled, normal op, drive select 0
  8.7461 +    }
  8.7462 +  else {
  8.7463 +    DOR = 0x2d; // DOR: drive1 motor on, DMA&int enabled, normal op, drive select 1
  8.7464 +    }
  8.7465 +#endif
  8.7466 +
  8.7467 +  if ( (ctrl_info & 0x04) != 0x04 ) {
  8.7468 +    // Drive not determined means no drive exists, done.
  8.7469 +    return;
  8.7470 +    }
  8.7471 +
  8.7472 +#if 0
  8.7473 +  // check Main Status Register for readiness
  8.7474 +  val8 = inb(0x03f4) & 0x80; // Main Status Register
  8.7475 +  if (val8 != 0x80)
  8.7476 +    BX_PANIC("d_f_m: MRQ bit not set\n");
  8.7477 +
  8.7478 +  // change line
  8.7479 +
  8.7480 +  // existing BDA values
  8.7481 +
  8.7482 +  // turn on drive motor
  8.7483 +  outb(0x03f2, DOR); // Digital Output Register
  8.7484 +  //
  8.7485 +#endif
  8.7486 +  BX_PANIC("d_f_m: OK so far\n");
  8.7487 +#endif
  8.7488 +}
  8.7489 +
  8.7490 +  void
  8.7491 +int17_function(regs, ds, iret_addr)
  8.7492 +  pusha_regs_t regs; // regs pushed from PUSHA instruction
  8.7493 +  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
  8.7494 +  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
  8.7495 +{
  8.7496 +  Bit16u addr,timeout;
  8.7497 +  Bit8u val8;
  8.7498 +
  8.7499 +  ASM_START
  8.7500 +  sti
  8.7501 +  ASM_END
  8.7502 +
  8.7503 +  addr = read_word(0x0040, (regs.u.r16.dx << 1) + 8);
  8.7504 +  if ((regs.u.r8.ah < 3) && (regs.u.r16.dx < 3) && (addr > 0)) {
  8.7505 +    timeout = read_byte(0x0040, 0x0078 + regs.u.r16.dx) << 8;
  8.7506 +    if (regs.u.r8.ah == 0) {
  8.7507 +      outb(addr, regs.u.r8.al);
  8.7508 +      val8 = inb(addr+2);
  8.7509 +      outb(addr+2, val8 | 0x01); // send strobe
  8.7510 +      ASM_START
  8.7511 +      nop
  8.7512 +      ASM_END
  8.7513 +      outb(addr+2, val8 & ~0x01);
  8.7514 +      while (((inb(addr+1) & 0x40) == 0x40) && (timeout)) {
  8.7515 +        timeout--;
  8.7516 +      }
  8.7517 +    }
  8.7518 +    if (regs.u.r8.ah == 1) {
  8.7519 +      val8 = inb(addr+2);
  8.7520 +      outb(addr+2, val8 & ~0x04); // send init
  8.7521 +      ASM_START
  8.7522 +      nop
  8.7523 +      ASM_END
  8.7524 +      outb(addr+2, val8 | 0x04);
  8.7525 +    }
  8.7526 +    val8 = inb(addr+1);
  8.7527 +    regs.u.r8.ah = (val8 ^ 0x48);
  8.7528 +    if (!timeout) regs.u.r8.ah |= 0x01;
  8.7529 +    ClearCF(iret_addr.flags);
  8.7530 +  } else {
  8.7531 +    SetCF(iret_addr.flags); // Unsupported
  8.7532 +  }
  8.7533 +}
  8.7534 +
  8.7535 +// returns bootsegment in ax, drive in bl
  8.7536 +  Bit32u 
  8.7537 +int19_function(bseqnr)
  8.7538 +Bit8u bseqnr;
  8.7539 +{
  8.7540 +  Bit16u ebda_seg=read_word(0x0040,0x000E);
  8.7541 +  Bit16u bootseq;
  8.7542 +  Bit8u  bootdrv;
  8.7543 +  Bit8u  bootcd;
  8.7544 +  Bit8u  bootchk;
  8.7545 +  Bit16u bootseg;
  8.7546 +  Bit16u status;
  8.7547 +  Bit8u  lastdrive=0;
  8.7548 +
  8.7549 +  // if BX_ELTORITO_BOOT is not defined, old behavior
  8.7550 +  //   check bit 5 in CMOS reg 0x2d.  load either 0x00 or 0x80 into DL
  8.7551 +  //   in preparation for the intial INT 13h (0=floppy A:, 0x80=C:)
  8.7552 +  //     0: system boot sequence, first drive C: then A:
  8.7553 +  //     1: system boot sequence, first drive A: then C:
  8.7554 +  // else BX_ELTORITO_BOOT is defined
  8.7555 +  //   CMOS regs 0x3D and 0x38 contain the boot sequence:
  8.7556 +  //     CMOS reg 0x3D & 0x0f : 1st boot device
  8.7557 +  //     CMOS reg 0x3D & 0xf0 : 2nd boot device
  8.7558 +  //     CMOS reg 0x38 & 0xf0 : 3rd boot device
  8.7559 +  //   boot device codes:
  8.7560 +  //     0x00 : not defined
  8.7561 +  //     0x01 : first floppy 
  8.7562 +  //     0x02 : first harddrive
  8.7563 +  //     0x03 : first cdrom
  8.7564 +  //     else : boot failure
  8.7565 +
  8.7566 +  // Get the boot sequence
  8.7567 +#if BX_ELTORITO_BOOT
  8.7568 +  bootseq=inb_cmos(0x3d);
  8.7569 +  bootseq|=((inb_cmos(0x38) & 0xf0) << 4);
  8.7570 +
  8.7571 +  if (bseqnr==2) bootseq >>= 4;
  8.7572 +  if (bseqnr==3) bootseq >>= 8;
  8.7573 +  if (bootseq<0x10) lastdrive = 1;
  8.7574 +  bootdrv=0x00; bootcd=0;
  8.7575 +  switch(bootseq & 0x0f) {
  8.7576 +    case 0x01: bootdrv=0x00; bootcd=0; break;
  8.7577 +    case 0x02: bootdrv=0x80; bootcd=0; break;
  8.7578 +    case 0x03: bootdrv=0x00; bootcd=1; break;
  8.7579 +    default:   return 0x00000000;
  8.7580 +    }
  8.7581 +#else
  8.7582 +  bootseq=inb_cmos(0x2d);
  8.7583 +
  8.7584 +  if (bseqnr==2) {
  8.7585 +    bootseq ^= 0x20;
  8.7586 +    lastdrive = 1;
  8.7587 +  }
  8.7588 +  bootdrv=0x00; bootcd=0;
  8.7589 +  if((bootseq&0x20)==0) bootdrv=0x80;
  8.7590 +#endif // BX_ELTORITO_BOOT
  8.7591 +
  8.7592 +#if BX_ELTORITO_BOOT
  8.7593 +  // We have to boot from cd
  8.7594 +  if (bootcd != 0) {
  8.7595 +    status = cdrom_boot();
  8.7596 +
  8.7597 +    // If failure
  8.7598 +    if ( (status & 0x00ff) !=0 ) {
  8.7599 +      print_cdromboot_failure(status);
  8.7600 +      print_boot_failure(bootcd, bootdrv, 1, lastdrive);
  8.7601 +      return 0x00000000;
  8.7602 +      }
  8.7603 +
  8.7604 +    bootseg = read_word(ebda_seg,&EbdaData->cdemu.load_segment);
  8.7605 +    bootdrv = (Bit8u)(status>>8);
  8.7606 +    }
  8.7607 +
  8.7608 +#endif // BX_ELTORITO_BOOT
  8.7609 +
  8.7610 +  // We have to boot from harddisk or floppy
  8.7611 +  if (bootcd == 0) {
  8.7612 +    bootseg=0x07c0;
  8.7613 +
  8.7614 +ASM_START
  8.7615 +    push bp
  8.7616 +    mov  bp, sp
  8.7617 +
  8.7618 +    mov  ax, #0x0000
  8.7619 +    mov  _int19_function.status + 2[bp], ax
  8.7620 +    mov  dl, _int19_function.bootdrv + 2[bp]
  8.7621 +    mov  ax, _int19_function.bootseg + 2[bp]
  8.7622 +    mov  es, ax         ;; segment
  8.7623 +    mov  bx, #0x0000    ;; offset
  8.7624 +    mov  ah, #0x02      ;; function 2, read diskette sector
  8.7625 +    mov  al, #0x01      ;; read 1 sector
  8.7626 +    mov  ch, #0x00      ;; track 0
  8.7627 +    mov  cl, #0x01      ;; sector 1
  8.7628 +    mov  dh, #0x00      ;; head 0
  8.7629 +    int  #0x13          ;; read sector
  8.7630 +    jnc  int19_load_done
  8.7631 +    mov  ax, #0x0001
  8.7632 +    mov  _int19_function.status + 2[bp], ax
  8.7633 +
  8.7634 +int19_load_done:
  8.7635 +    pop  bp
  8.7636 +ASM_END
  8.7637 +    
  8.7638 +    if (status != 0) {
  8.7639 +      print_boot_failure(bootcd, bootdrv, 1, lastdrive);
  8.7640 +      return 0x00000000;
  8.7641 +      }
  8.7642 +    }
  8.7643 +
  8.7644 +  // check signature if instructed by cmos reg 0x38, only for floppy
  8.7645 +  // bootchk = 1 : signature check disabled
  8.7646 +  // bootchk = 0 : signature check enabled
  8.7647 +  if (bootdrv != 0) bootchk = 0;
  8.7648 +  else bootchk = inb_cmos(0x38) & 0x01;
  8.7649 +
  8.7650 +#if BX_ELTORITO_BOOT
  8.7651 +  // if boot from cd, no signature check
  8.7652 +  if (bootcd != 0)
  8.7653 +    bootchk = 1;
  8.7654 +#endif // BX_ELTORITO_BOOT
  8.7655 +
  8.7656 +  if (bootchk == 0) {
  8.7657 +    if (read_word(bootseg,0x1fe) != 0xaa55) {
  8.7658 +      print_boot_failure(bootcd, bootdrv, 0, lastdrive);
  8.7659 +      return 0x00000000;
  8.7660 +      }
  8.7661 +    }
  8.7662 +  
  8.7663 +#if BX_ELTORITO_BOOT
  8.7664 +  // Print out the boot string
  8.7665 +  print_boot_device(bootcd, bootdrv);
  8.7666 +#else // BX_ELTORITO_BOOT
  8.7667 +  print_boot_device(0, bootdrv);
  8.7668 +#endif // BX_ELTORITO_BOOT
  8.7669 +
  8.7670 +  // return the boot segment
  8.7671 +  return (((Bit32u)bootdrv) << 16) + bootseg;
  8.7672 +}
  8.7673 +
  8.7674 +  void
  8.7675 +int1a_function(regs, ds, iret_addr)
  8.7676 +  pusha_regs_t regs; // regs pushed from PUSHA instruction
  8.7677 +  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
  8.7678 +  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
  8.7679 +{
  8.7680 +  Bit8u val8;
  8.7681 +
  8.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);
  8.7683 +
  8.7684 +  ASM_START
  8.7685 +  sti
  8.7686 +  ASM_END
  8.7687 +
  8.7688 +  switch (regs.u.r8.ah) {
  8.7689 +    case 0: // get current clock count
  8.7690 +      ASM_START
  8.7691 +      cli
  8.7692 +      ASM_END
  8.7693 +      regs.u.r16.cx = BiosData->ticks_high;
  8.7694 +      regs.u.r16.dx = BiosData->ticks_low;
  8.7695 +      regs.u.r8.al  = BiosData->midnight_flag;
  8.7696 +      BiosData->midnight_flag = 0; // reset flag
  8.7697 +      ASM_START
  8.7698 +      sti
  8.7699 +      ASM_END
  8.7700 +      // AH already 0
  8.7701 +      ClearCF(iret_addr.flags); // OK
  8.7702 +      break;
  8.7703 +
  8.7704 +    case 1: // Set Current Clock Count
  8.7705 +      ASM_START
  8.7706 +      cli
  8.7707 +      ASM_END
  8.7708 +      BiosData->ticks_high = regs.u.r16.cx;
  8.7709 +      BiosData->ticks_low  = regs.u.r16.dx;
  8.7710 +      BiosData->midnight_flag = 0; // reset flag
  8.7711 +      ASM_START
  8.7712 +      sti
  8.7713 +      ASM_END
  8.7714 +      regs.u.r8.ah = 0;
  8.7715 +      ClearCF(iret_addr.flags); // OK
  8.7716 +      break;
  8.7717 +
  8.7718 +
  8.7719 +    case 2: // Read CMOS Time
  8.7720 +      if (rtc_updating()) {
  8.7721 +        SetCF(iret_addr.flags);
  8.7722 +        break;
  8.7723 +        }
  8.7724 +
  8.7725 +      regs.u.r8.dh = inb_cmos(0x00); // Seconds
  8.7726 +      regs.u.r8.cl = inb_cmos(0x02); // Minutes
  8.7727 +      regs.u.r8.ch = inb_cmos(0x04); // Hours
  8.7728 +      regs.u.r8.dl = inb_cmos(0x0b) & 0x01; // Stat Reg B
  8.7729 +      regs.u.r8.ah = 0;
  8.7730 +      regs.u.r8.al = regs.u.r8.ch;
  8.7731 +      ClearCF(iret_addr.flags); // OK
  8.7732 +      break;
  8.7733 +
  8.7734 +    case 3: // Set CMOS Time
  8.7735 +      // Using a debugger, I notice the following masking/setting
  8.7736 +      // of bits in Status Register B, by setting Reg B to
  8.7737 +      // a few values and getting its value after INT 1A was called.
  8.7738 +      //
  8.7739 +      //        try#1       try#2       try#3
  8.7740 +      // before 1111 1101   0111 1101   0000 0000
  8.7741 +      // after  0110 0010   0110 0010   0000 0010
  8.7742 +      //
  8.7743 +      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
  8.7744 +      // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
  8.7745 +      if (rtc_updating()) {
  8.7746 +        init_rtc();
  8.7747 +        // fall through as if an update were not in progress
  8.7748 +        }
  8.7749 +      outb_cmos(0x00, regs.u.r8.dh); // Seconds
  8.7750 +      outb_cmos(0x02, regs.u.r8.cl); // Minutes
  8.7751 +      outb_cmos(0x04, regs.u.r8.ch); // Hours
  8.7752 +      // Set Daylight Savings time enabled bit to requested value
  8.7753 +      val8 = (inb_cmos(0x0b) & 0x60) | 0x02 | (regs.u.r8.dl & 0x01);
  8.7754 +      // (reg B already selected)
  8.7755 +      outb_cmos(0x0b, val8);
  8.7756 +      regs.u.r8.ah = 0;
  8.7757 +      regs.u.r8.al = val8; // val last written to Reg B
  8.7758 +      ClearCF(iret_addr.flags); // OK
  8.7759 +      break;
  8.7760 +
  8.7761 +    case 4: // Read CMOS Date
  8.7762 +      regs.u.r8.ah = 0;
  8.7763 +      if (rtc_updating()) {
  8.7764 +        SetCF(iret_addr.flags);
  8.7765 +        break;
  8.7766 +        }
  8.7767 +      regs.u.r8.cl = inb_cmos(0x09); // Year
  8.7768 +      regs.u.r8.dh = inb_cmos(0x08); // Month
  8.7769 +      regs.u.r8.dl = inb_cmos(0x07); // Day of Month
  8.7770 +      regs.u.r8.ch = inb_cmos(0x32); // Century
  8.7771 +      regs.u.r8.al = regs.u.r8.ch;
  8.7772 +      ClearCF(iret_addr.flags); // OK
  8.7773 +      break;
  8.7774 +
  8.7775 +    case 5: // Set CMOS Date
  8.7776 +      // Using a debugger, I notice the following masking/setting
  8.7777 +      // of bits in Status Register B, by setting Reg B to
  8.7778 +      // a few values and getting its value after INT 1A was called.
  8.7779 +      //
  8.7780 +      //        try#1       try#2       try#3       try#4
  8.7781 +      // before 1111 1101   0111 1101   0000 0010   0000 0000
  8.7782 +      // after  0110 1101   0111 1101   0000 0010   0000 0000
  8.7783 +      //
  8.7784 +      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
  8.7785 +      // My assumption: RegB = (RegB & 01111111b)
  8.7786 +      if (rtc_updating()) {
  8.7787 +        init_rtc();
  8.7788 +        SetCF(iret_addr.flags);
  8.7789 +        break;
  8.7790 +        }
  8.7791 +      outb_cmos(0x09, regs.u.r8.cl); // Year
  8.7792 +      outb_cmos(0x08, regs.u.r8.dh); // Month
  8.7793 +      outb_cmos(0x07, regs.u.r8.dl); // Day of Month
  8.7794 +      outb_cmos(0x32, regs.u.r8.ch); // Century
  8.7795 +      val8 = inb_cmos(0x0b) & 0x7f; // clear halt-clock bit
  8.7796 +      outb_cmos(0x0b, val8);
  8.7797 +      regs.u.r8.ah = 0;
  8.7798 +      regs.u.r8.al = val8; // AL = val last written to Reg B
  8.7799 +      ClearCF(iret_addr.flags); // OK
  8.7800 +      break;
  8.7801 +
  8.7802 +    case 6: // Set Alarm Time in CMOS
  8.7803 +      // Using a debugger, I notice the following masking/setting
  8.7804 +      // of bits in Status Register B, by setting Reg B to
  8.7805 +      // a few values and getting its value after INT 1A was called.
  8.7806 +      //
  8.7807 +      //        try#1       try#2       try#3
  8.7808 +      // before 1101 1111   0101 1111   0000 0000
  8.7809 +      // after  0110 1111   0111 1111   0010 0000
  8.7810 +      //
  8.7811 +      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
  8.7812 +      // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
  8.7813 +      val8 = inb_cmos(0x0b); // Get Status Reg B
  8.7814 +      regs.u.r16.ax = 0;
  8.7815 +      if (val8 & 0x20) {
  8.7816 +        // Alarm interrupt enabled already
  8.7817 +        SetCF(iret_addr.flags); // Error: alarm in use
  8.7818 +        break;
  8.7819 +        }
  8.7820 +      if (rtc_updating()) {
  8.7821 +        init_rtc();
  8.7822 +        // fall through as if an update were not in progress
  8.7823 +        }
  8.7824 +      outb_cmos(0x01, regs.u.r8.dh); // Seconds alarm
  8.7825 +      outb_cmos(0x03, regs.u.r8.cl); // Minutes alarm
  8.7826 +      outb_cmos(0x05, regs.u.r8.ch); // Hours alarm
  8.7827 +      outb(0xa1, inb(0xa1) & 0xfe); // enable IRQ 8
  8.7828 +      // enable Status Reg B alarm bit, clear halt clock bit
  8.7829 +      outb_cmos(0x0b, (val8 & 0x7f) | 0x20);
  8.7830 +      ClearCF(iret_addr.flags); // OK
  8.7831 +      break;
  8.7832 +
  8.7833 +    case 7: // Turn off Alarm
  8.7834 +      // Using a debugger, I notice the following masking/setting
  8.7835 +      // of bits in Status Register B, by setting Reg B to
  8.7836 +      // a few values and getting its value after INT 1A was called.
  8.7837 +      //
  8.7838 +      //        try#1       try#2       try#3       try#4
  8.7839 +      // before 1111 1101   0111 1101   0010 0000   0010 0010
  8.7840 +      // after  0100 0101   0101 0101   0000 0000   0000 0010
  8.7841 +      //
  8.7842 +      // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
  8.7843 +      // My assumption: RegB = (RegB & 01010111b)
  8.7844 +      val8 = inb_cmos(0x0b); // Get Status Reg B
  8.7845 +      // clear clock-halt bit, disable alarm bit
  8.7846 +      outb_cmos(0x0b, val8 & 0x57); // disable alarm bit
  8.7847 +      regs.u.r8.ah = 0;
  8.7848 +      regs.u.r8.al = val8; // val last written to Reg B
  8.7849 +      ClearCF(iret_addr.flags); // OK
  8.7850 +      break;
  8.7851 +#if BX_PCIBIOS
  8.7852 +    case 0xb1:
  8.7853 +      // real mode PCI BIOS functions now handled in assembler code
  8.7854 +      // this C code handles the error code for information only
  8.7855 +      if (regs.u.r8.bl == 0xff) {
  8.7856 +        BX_INFO("PCI BIOS: PCI not present\n");
  8.7857 +      } else if (regs.u.r8.bl == 0x81) {
  8.7858 +        BX_INFO("unsupported PCI BIOS function 0x%02x\n", regs.u.r8.al);
  8.7859 +      } else if (regs.u.r8.bl == 0x83) {
  8.7860 +        BX_INFO("bad PCI vendor ID %04x\n", regs.u.r16.dx);
  8.7861 +      } else if (regs.u.r8.bl == 0x86) {
  8.7862 +        BX_INFO("PCI device %04x:%04x not found\n", regs.u.r16.dx, regs.u.r16.cx);
  8.7863 +      }
  8.7864 +      regs.u.r8.ah = regs.u.r8.bl;
  8.7865 +      SetCF(iret_addr.flags);
  8.7866 +      break;
  8.7867 +#endif
  8.7868 +
  8.7869 +    default:
  8.7870 +      SetCF(iret_addr.flags); // Unsupported
  8.7871 +    }
  8.7872 +}
  8.7873 +
  8.7874 +  void
  8.7875 +int70_function(regs, ds, iret_addr)
  8.7876 +  pusha_regs_t regs; // regs pushed from PUSHA instruction
  8.7877 +  Bit16u ds; // previous DS:, DS set to 0x0000 by asm wrapper
  8.7878 +  iret_addr_t  iret_addr; // CS,IP,Flags pushed from original INT call
  8.7879 +{
  8.7880 +  // INT 70h: IRQ 8 - CMOS RTC interrupt from periodic or alarm modes
  8.7881 +  Bit8u registerB = 0, registerC = 0;
  8.7882 +
  8.7883 +  // Check which modes are enabled and have occurred.
  8.7884 +  registerB = inb_cmos( 0xB );
  8.7885 +  registerC = inb_cmos( 0xC );
  8.7886 +
  8.7887 +  if( ( registerB & 0x60 ) != 0 ) {
  8.7888 +    if( ( registerC & 0x20 ) != 0 ) {
  8.7889 +      // Handle Alarm Interrupt.
  8.7890 +ASM_START
  8.7891 +      sti
  8.7892 +      int #0x4a
  8.7893 +      cli
  8.7894 +ASM_END
  8.7895 +    }
  8.7896 +    if( ( registerC & 0x40 ) != 0 ) {
  8.7897 +      // Handle Periodic Interrupt.
  8.7898 +
  8.7899 +      if( read_byte( 0x40, 0xA0 ) != 0 ) {
  8.7900 +        // Wait Interval (Int 15, AH=83) active.
  8.7901 +        Bit32u time, toggle;
  8.7902 +
  8.7903 +        time = read_dword( 0x40, 0x9C );  // Time left in microseconds.
  8.7904 +        if( time < 0x3D1 ) {
  8.7905 +          // Done waiting.
  8.7906 +          Bit16u segment, offset;
  8.7907 +
  8.7908 +          offset = read_word( 0x40, 0x98 );
  8.7909 +          segment = read_word( 0x40, 0x9A );
  8.7910 +          write_byte( 0x40, 0xA0, 0 );  // Turn of status byte.
  8.7911 +          outb_cmos( 0xB, registerB & 0x37 ); // Clear the Periodic Interrupt.
  8.7912 +          write_byte( segment, offset, 0x80 );  // Write to specified flag byte.
  8.7913 +        } else {
  8.7914 +          // Continue waiting.
  8.7915 +          time -= 0x3D1;
  8.7916 +          write_dword( 0x40, 0x9C, time );
  8.7917 +        }
  8.7918 +      }
  8.7919 +    }
  8.7920 +  }
  8.7921 +
  8.7922 +ASM_START
  8.7923 +  call eoi_both_pics
  8.7924 +ASM_END
  8.7925 +}
  8.7926 +
  8.7927 +
  8.7928 +ASM_START
  8.7929 +;------------------------------------------
  8.7930 +;- INT74h : PS/2 mouse hardware interrupt -
  8.7931 +;------------------------------------------
  8.7932 +int74_handler:
  8.7933 +  sti
  8.7934 +  pusha
  8.7935 +  push ds         ;; save DS
  8.7936 +  push #0x00 ;; placeholder for status
  8.7937 +  push #0x00 ;; placeholder for X
  8.7938 +  push #0x00 ;; placeholder for Y
  8.7939 +  push #0x00 ;; placeholder for Z
  8.7940 +  push #0x00 ;; placeholder for make_far_call boolean
  8.7941 +  call _int74_function
  8.7942 +  pop  cx      ;; remove make_far_call from stack
  8.7943 +  jcxz int74_done
  8.7944 +
  8.7945 +  ;; make far call to EBDA:0022
  8.7946 +  push #0x00
  8.7947 +  pop ds
  8.7948 +  push 0x040E     ;; push 0000:040E (opcodes 0xff, 0x36, 0x0E, 0x04)
  8.7949 +  pop ds
  8.7950 +  //CALL_EP(0x0022) ;; call far routine (call_Ep DS:0022 :opcodes 0xff, 0x1e, 0x22, 0x00)
  8.7951 +  call far ptr[0x22]
  8.7952 +int74_done:
  8.7953 +  cli
  8.7954 +  call eoi_both_pics
  8.7955 +  add sp, #8     ;; pop status, x, y, z
  8.7956 +
  8.7957 +  pop ds          ;; restore DS
  8.7958 +  popa
  8.7959 +  iret
  8.7960 +
  8.7961 +
  8.7962 +;; This will perform an IRET, but will retain value of current CF
  8.7963 +;; by altering flags on stack.  Better than RETF #02.
  8.7964 +iret_modify_cf:
  8.7965 +  jc   carry_set
  8.7966 +  push bp
  8.7967 +  mov  bp, sp
  8.7968 +  and  BYTE [bp + 0x06], #0xfe
  8.7969 +  pop  bp
  8.7970 +  iret
  8.7971 +carry_set:
  8.7972 +  push bp
  8.7973 +  mov  bp, sp
  8.7974 +  or   BYTE [bp + 0x06], #0x01
  8.7975 +  pop  bp
  8.7976 +  iret
  8.7977 +
  8.7978 +
  8.7979 +;----------------------
  8.7980 +;- INT13h (relocated) -
  8.7981 +;----------------------
  8.7982 +;
  8.7983 +; int13_relocated is a little bit messed up since I played with it
  8.7984 +; I have to rewrite it:
  8.7985 +;   - call a function that detect which function to call
  8.7986 +;   - make all called C function get the same parameters list
  8.7987 +;
  8.7988 +int13_relocated:
  8.7989 +
  8.7990 +#if BX_ELTORITO_BOOT
  8.7991 +  ;; check for an eltorito function
  8.7992 +  cmp   ah,#0x4a
  8.7993 +  jb    int13_not_eltorito
  8.7994 +  cmp   ah,#0x4d
  8.7995 +  ja    int13_not_eltorito
  8.7996 +
  8.7997 +  pusha
  8.7998 +  push  es
  8.7999 +  push  ds
  8.8000 +  push  ss
  8.8001 +  pop   ds
  8.8002 +
  8.8003 +  push  #int13_out
  8.8004 +  jmp   _int13_eltorito      ;; ELDX not used
  8.8005 +
  8.8006 +int13_not_eltorito:
  8.8007 +  push  ax
  8.8008 +  push  bx
  8.8009 +  push  cx
  8.8010 +  push  dx
  8.8011 +
  8.8012 +  ;; check if emulation active
  8.8013 +  call  _cdemu_isactive
  8.8014 +  cmp   al,#0x00
  8.8015 +  je    int13_cdemu_inactive
  8.8016 +
  8.8017 +  ;; check if access to the emulated drive
  8.8018 +  call  _cdemu_emulated_drive
  8.8019 +  pop   dx
  8.8020 +  push  dx
  8.8021 +  cmp   al,dl                ;; int13 on emulated drive
  8.8022 +  jne   int13_nocdemu
  8.8023 +
  8.8024 +  pop   dx
  8.8025 +  pop   cx
  8.8026 +  pop   bx
  8.8027 +  pop   ax
  8.8028 +
  8.8029 +  pusha
  8.8030 +  push  es
  8.8031 +  push  ds
  8.8032 +  push  ss
  8.8033 +  pop   ds
  8.8034 +
  8.8035 +  push  #int13_out
  8.8036 +  jmp   _int13_cdemu         ;; ELDX not used
  8.8037 +
  8.8038 +int13_nocdemu:
  8.8039 +  and   dl,#0xE0             ;; mask to get device class, including cdroms
  8.8040 +  cmp   al,dl                ;; al is 0x00 or 0x80
  8.8041 +  jne   int13_cdemu_inactive ;; inactive for device class
  8.8042 +
  8.8043 +  pop   dx
  8.8044 +  pop   cx
  8.8045 +  pop   bx
  8.8046 +  pop   ax
  8.8047 +
  8.8048 +  push  ax
  8.8049 +  push  cx
  8.8050 +  push  dx
  8.8051 +  push  bx
  8.8052 +
  8.8053 +  dec   dl                   ;; real drive is dl - 1
  8.8054 +  jmp   int13_legacy
  8.8055 +
  8.8056 +int13_cdemu_inactive:
  8.8057 +  pop   dx
  8.8058 +  pop   cx
  8.8059 +  pop   bx
  8.8060 +  pop   ax
  8.8061 +
  8.8062 +#endif // BX_ELTORITO_BOOT
  8.8063 +
  8.8064 +int13_noeltorito:
  8.8065 +
  8.8066 +  push  ax
  8.8067 +  push  cx
  8.8068 +  push  dx
  8.8069 +  push  bx
  8.8070 +
  8.8071 +int13_legacy:
  8.8072 +
  8.8073 +  push  dx                   ;; push eltorito value of dx instead of sp
  8.8074 +
  8.8075 +  push  bp
  8.8076 +  push  si
  8.8077 +  push  di
  8.8078 +
  8.8079 +  push  es
  8.8080 +  push  ds
  8.8081 +  push  ss
  8.8082 +  pop   ds
  8.8083 +
  8.8084 +  ;; now the 16-bit registers can be restored with:
  8.8085 +  ;; pop ds; pop es; popa; iret
  8.8086 +  ;; arguments passed to functions should be
  8.8087 +  ;; DS, ES, DI, SI, BP, ELDX, BX, DX, CX, AX, IP, CS, FLAGS
  8.8088 +
  8.8089 +  test  dl, #0x80
  8.8090 +  jnz   int13_notfloppy
  8.8091 +
  8.8092 +  push #int13_out
  8.8093 +  jmp _int13_diskette_function
  8.8094 +
  8.8095 +int13_notfloppy:
  8.8096 +
  8.8097 +#if BX_USE_ATADRV
  8.8098 +
  8.8099 +  cmp   dl, #0xE0
  8.8100 +  jb    int13_notcdrom
  8.8101 +
  8.8102 +  // ebx is modified: BSD 5.2.1 boot loader problem
  8.8103 +  // someone should figure out which 32 bit register that actually are used
  8.8104 +
  8.8105 +  shr   ebx, #16
  8.8106 +  push  bx
  8.8107 +
  8.8108 +  call  _int13_cdrom
  8.8109 +
  8.8110 +  pop   bx
  8.8111 +  shl   ebx, #16
  8.8112 +
  8.8113 +  jmp int13_out
  8.8114 +
  8.8115 +int13_notcdrom:
  8.8116 +
  8.8117 +#endif
  8.8118 +
  8.8119 +int13_disk:
  8.8120 +  call  _int13_harddisk
  8.8121 +
  8.8122 +int13_out:
  8.8123 +  pop ds
  8.8124 +  pop es
  8.8125 +  popa
  8.8126 +  iret 
  8.8127 +
  8.8128 +
  8.8129 +;----------
  8.8130 +;- INT18h -
  8.8131 +;----------
  8.8132 +int18_handler: ;; Boot Failure routing
  8.8133 +  call _int18_panic_msg
  8.8134 +  hlt
  8.8135 +  iret
  8.8136 +
  8.8137 +;----------
  8.8138 +;- INT19h -
  8.8139 +;----------
  8.8140 +int19_relocated: ;; Boot function, relocated
  8.8141 +
  8.8142 +  ;; int19 was beginning to be really complex, so now it
  8.8143 +  ;; just calls an C function, that does the work
  8.8144 +  ;; it returns in BL the boot drive, and in AX the boot segment
  8.8145 +  ;; the boot segment will be 0x0000 if something has failed
  8.8146 +
  8.8147 +  push bp
  8.8148 +  mov  bp, sp
  8.8149 +
  8.8150 +  ;; drop ds
  8.8151 +  xor  ax, ax
  8.8152 +  mov  ds, ax
  8.8153 +
  8.8154 +  ;; 1st boot device
  8.8155 +  mov  ax, #0x0001
  8.8156 +  push ax
  8.8157 +  call _int19_function
  8.8158 +  inc  sp
  8.8159 +  inc  sp
  8.8160 +  ;; bl contains the boot drive
  8.8161 +  ;; ax contains the boot segment or 0 if failure
  8.8162 +
  8.8163 +  test       ax, ax  ;; if ax is 0 try next boot device
  8.8164 +  jnz        boot_setup
  8.8165 +
  8.8166 +  ;; 2nd boot device
  8.8167 +  mov  ax, #0x0002
  8.8168 +  push ax
  8.8169 +  call _int19_function
  8.8170 +  inc  sp
  8.8171 +  inc  sp
  8.8172 +  test       ax, ax  ;; if ax is 0 try next boot device
  8.8173 +  jnz        boot_setup
  8.8174 +
  8.8175 +  ;; 3rd boot device
  8.8176 +  mov  ax, #0x0003
  8.8177 +  push ax
  8.8178 +  call _int19_function
  8.8179 +  inc  sp
  8.8180 +  inc  sp
  8.8181 +  test       ax, ax  ;; if ax is 0 call int18
  8.8182 +  jz         int18_handler
  8.8183 +
  8.8184 +boot_setup:
  8.8185 +  mov dl,    bl      ;; set drive so guest os find it
  8.8186 +  shl eax,   #0x04   ;; convert seg to ip
  8.8187 +  mov 2[bp], ax      ;; set ip
  8.8188 +
  8.8189 +  shr eax,   #0x04   ;; get cs back
  8.8190 +  and ax,    #0xF000 ;; remove what went in ip
  8.8191 +  mov 4[bp], ax      ;; set cs
  8.8192 +  xor ax,    ax
  8.8193 +  mov es,    ax      ;; set es to zero fixes [ 549815 ]
  8.8194 +  mov [bp],  ax      ;; set bp to zero
  8.8195 +  mov ax,    #0xaa55 ;; set ok flag
  8.8196 +
  8.8197 +  pop bp
  8.8198 +  iret               ;; Beam me up Scotty
  8.8199 +
  8.8200 +;----------
  8.8201 +;- INT1Ch -
  8.8202 +;----------
  8.8203 +int1c_handler: ;; User Timer Tick
  8.8204 +  iret
  8.8205 +
  8.8206 +
  8.8207 +;----------------------
  8.8208 +;- POST: Floppy Drive -
  8.8209 +;----------------------
  8.8210 +floppy_drive_post:
  8.8211 +  mov  ax, #0x0000
  8.8212 +  mov  ds, ax
  8.8213 +
  8.8214 +  mov  al, #0x00
  8.8215 +  mov  0x043e, al ;; drive 0 & 1 uncalibrated, no interrupt has occurred
  8.8216 +
  8.8217 +  mov  0x043f, al  ;; diskette motor status: read op, drive0, motors off
  8.8218 +
  8.8219 +  mov  0x0440, al  ;; diskette motor timeout counter: not active
  8.8220 +  mov  0x0441, al  ;; diskette controller status return code
  8.8221 +
  8.8222 +  mov  0x0442, al  ;; disk & diskette controller status register 0
  8.8223 +  mov  0x0443, al  ;; diskette controller status register 1
  8.8224 +  mov  0x0444, al  ;; diskette controller status register 2
  8.8225 +  mov  0x0445, al  ;; diskette controller cylinder number
  8.8226 +  mov  0x0446, al  ;; diskette controller head number
  8.8227 +  mov  0x0447, al  ;; diskette controller sector number
  8.8228 +  mov  0x0448, al  ;; diskette controller bytes written
  8.8229 +
  8.8230 +  mov  0x048b, al  ;; diskette configuration data
  8.8231 +
  8.8232 +  ;; -----------------------------------------------------------------
  8.8233 +  ;; (048F) diskette controller information
  8.8234 +  ;;
  8.8235 +  mov  al, #0x10   ;; get CMOS diskette drive type
  8.8236 +  out  0x70, AL
  8.8237 +  in   AL, 0x71
  8.8238 +  mov  ah, al      ;; save byte to AH
  8.8239 +
  8.8240 +look_drive0:
  8.8241 +  shr  al, #4      ;; look at top 4 bits for drive 0
  8.8242 +  jz   f0_missing  ;; jump if no drive0
  8.8243 +  mov  bl, #0x07   ;; drive0 determined, multi-rate, has changed line
  8.8244 +  jmp  look_drive1
  8.8245 +f0_missing:
  8.8246 +  mov  bl, #0x00   ;; no drive0
  8.8247 +
  8.8248 +look_drive1:
  8.8249 +  mov  al, ah      ;; restore from AH
  8.8250 +  and  al, #0x0f   ;; look at bottom 4 bits for drive 1
  8.8251 +  jz   f1_missing  ;; jump if no drive1
  8.8252 +  or   bl, #0x70   ;; drive1 determined, multi-rate, has changed line
  8.8253 +f1_missing:
  8.8254 +                   ;; leave high bits in BL zerod
  8.8255 +  mov  0x048f, bl  ;; put new val in BDA (diskette controller information)
  8.8256 +  ;; -----------------------------------------------------------------
  8.8257 +
  8.8258 +  mov  al, #0x00
  8.8259 +  mov  0x0490, al  ;; diskette 0 media state
  8.8260 +  mov  0x0491, al  ;; diskette 1 media state
  8.8261 +
  8.8262 +                   ;; diskette 0,1 operational starting state
  8.8263 +                   ;; drive type has not been determined,
  8.8264 +                   ;; has no changed detection line
  8.8265 +  mov  0x0492, al
  8.8266 +  mov  0x0493, al
  8.8267 +
  8.8268 +  mov  0x0494, al  ;; diskette 0 current cylinder
  8.8269 +  mov  0x0495, al  ;; diskette 1 current cylinder
  8.8270 +
  8.8271 +  mov  al, #0x02
  8.8272 +  out  #0x0a, al   ;; clear DMA-1 channel 2 mask bit
  8.8273 +
  8.8274 +  SET_INT_VECTOR(0x1E, #0xF000, #diskette_param_table2)
  8.8275 +  SET_INT_VECTOR(0x40, #0xF000, #int13_diskette)
  8.8276 +  SET_INT_VECTOR(0x0E, #0xF000, #int0e_handler) ;; IRQ 6
  8.8277 +
  8.8278 +  ret
  8.8279 +
  8.8280 +
  8.8281 +;--------------------
  8.8282 +;- POST: HARD DRIVE -
  8.8283 +;--------------------
  8.8284 +; relocated here because the primary POST area isnt big enough.
  8.8285 +hard_drive_post:
  8.8286 +  // IRQ 14 = INT 76h
  8.8287 +  // INT 76h calls INT 15h function ax=9100
  8.8288 +
  8.8289 +  mov  al, #0x0a   ; 0000 1010 = reserved, disable IRQ 14
  8.8290 +  mov  dx, #0x03f6
  8.8291 +  out  dx, al
  8.8292 +
  8.8293 +  mov  ax, #0x0000
  8.8294 +  mov  ds, ax
  8.8295 +  mov  0x0474, al /* hard disk status of last operation */
  8.8296 +  mov  0x0477, al /* hard disk port offset (XT only ???) */
  8.8297 +  mov  0x048c, al /* hard disk status register */
  8.8298 +  mov  0x048d, al /* hard disk error register */
  8.8299 +  mov  0x048e, al /* hard disk task complete flag */
  8.8300 +  mov  al, #0x01
  8.8301 +  mov  0x0475, al /* hard disk number attached */
  8.8302 +  mov  al, #0xc0
  8.8303 +  mov  0x0476, al /* hard disk control byte */
  8.8304 +  SET_INT_VECTOR(0x13, #0xF000, #int13_handler)
  8.8305 +  SET_INT_VECTOR(0x76, #0xF000, #int76_handler)
  8.8306 +  ;; INT 41h: hard disk 0 configuration pointer
  8.8307 +  ;; INT 46h: hard disk 1 configuration pointer
  8.8308 +  SET_INT_VECTOR(0x41, #EBDA_SEG, #0x003D)
  8.8309 +  SET_INT_VECTOR(0x46, #EBDA_SEG, #0x004D)
  8.8310 +
  8.8311 +  ;; move disk geometry data from CMOS to EBDA disk parameter table(s)
  8.8312 +  mov  al, #0x12
  8.8313 +  out  #0x70, al
  8.8314 +  in   al, #0x71
  8.8315 +  and  al, #0xf0
  8.8316 +  cmp  al, #0xf0
  8.8317 +  je   post_d0_extended
  8.8318 +  jmp check_for_hd1
  8.8319 +post_d0_extended:
  8.8320 +  mov  al, #0x19
  8.8321 +  out  #0x70, al
  8.8322 +  in   al, #0x71
  8.8323 +  cmp  al, #47  ;; decimal 47 - user definable
  8.8324 +  je   post_d0_type47
  8.8325 +  HALT(__LINE__)
  8.8326 +post_d0_type47:
  8.8327 +  ;; CMOS  purpose                  param table offset
  8.8328 +  ;; 1b    cylinders low            0
  8.8329 +  ;; 1c    cylinders high           1
  8.8330 +  ;; 1d    heads                    2
  8.8331 +  ;; 1e    write pre-comp low       5
  8.8332 +  ;; 1f    write pre-comp high      6
  8.8333 +  ;; 20    retries/bad map/heads>8  8
  8.8334 +  ;; 21    landing zone low         C
  8.8335 +  ;; 22    landing zone high        D
  8.8336 +  ;; 23    sectors/track            E
  8.8337 +
  8.8338 +  mov  ax, #EBDA_SEG
  8.8339 +  mov  ds, ax
  8.8340 +
  8.8341 +  ;;; Filling EBDA table for hard disk 0.
  8.8342 +  mov  al, #0x1f
  8.8343 +  out  #0x70, al
  8.8344 +  in   al, #0x71
  8.8345 +  mov  ah, al
  8.8346 +  mov  al, #0x1e
  8.8347 +  out  #0x70, al
  8.8348 +  in   al, #0x71
  8.8349 +  mov   (0x003d + 0x05), ax ;; write precomp word
  8.8350 +
  8.8351 +  mov  al, #0x20
  8.8352 +  out  #0x70, al
  8.8353 +  in   al, #0x71
  8.8354 +  mov   (0x003d + 0x08), al ;; drive control byte
  8.8355 +
  8.8356 +  mov  al, #0x22
  8.8357 +  out  #0x70, al
  8.8358 +  in   al, #0x71
  8.8359 +  mov  ah, al
  8.8360 +  mov  al, #0x21
  8.8361 +  out  #0x70, al
  8.8362 +  in   al, #0x71
  8.8363 +  mov   (0x003d + 0x0C), ax ;; landing zone word
  8.8364 +
  8.8365 +  mov  al, #0x1c   ;; get cylinders word in AX
  8.8366 +  out  #0x70, al
  8.8367 +  in   al, #0x71   ;; high byte
  8.8368 +  mov  ah, al
  8.8369 +  mov  al, #0x1b
  8.8370 +  out  #0x70, al
  8.8371 +  in   al, #0x71   ;; low byte
  8.8372 +  mov  bx, ax      ;; BX = cylinders
  8.8373 +
  8.8374 +  mov  al, #0x1d
  8.8375 +  out  #0x70, al
  8.8376 +  in   al, #0x71
  8.8377 +  mov  cl, al      ;; CL = heads
  8.8378 +
  8.8379 +  mov  al, #0x23
  8.8380 +  out  #0x70, al
  8.8381 +  in   al, #0x71
  8.8382 +  mov  dl, al      ;; DL = sectors
  8.8383 +
  8.8384 +  cmp  bx, #1024
  8.8385 +  jnbe hd0_post_logical_chs ;; if cylinders > 1024, use translated style CHS
  8.8386 +
  8.8387 +hd0_post_physical_chs:
  8.8388 +  ;; no logical CHS mapping used, just physical CHS
  8.8389 +  ;; use Standard Fixed Disk Parameter Table (FDPT)
  8.8390 +  mov   (0x003d + 0x00), bx ;; number of physical cylinders
  8.8391 +  mov   (0x003d + 0x02), cl ;; number of physical heads
  8.8392 +  mov   (0x003d + 0x0E), dl ;; number of physical sectors
  8.8393 +  jmp check_for_hd1
  8.8394 +
  8.8395 +hd0_post_logical_chs:
  8.8396 +  ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
  8.8397 +  mov   (0x003d + 0x09), bx ;; number of physical cylinders
  8.8398 +  mov   (0x003d + 0x0b), cl ;; number of physical heads
  8.8399 +  mov   (0x003d + 0x04), dl ;; number of physical sectors
  8.8400 +  mov   (0x003d + 0x0e), dl ;; number of logical sectors (same)
  8.8401 +  mov al, #0xa0
  8.8402 +  mov   (0x003d + 0x03), al ;; A0h signature, indicates translated table
  8.8403 +
  8.8404 +  cmp bx, #2048
  8.8405 +  jnbe hd0_post_above_2048
  8.8406 +  ;; 1024 < c <= 2048 cylinders
  8.8407 +  shr bx, #0x01
  8.8408 +  shl cl, #0x01
  8.8409 +  jmp hd0_post_store_logical
  8.8410 +
  8.8411 +hd0_post_above_2048:
  8.8412 +  cmp bx, #4096
  8.8413 +  jnbe hd0_post_above_4096
  8.8414 +  ;; 2048 < c <= 4096 cylinders
  8.8415 +  shr bx, #0x02
  8.8416 +  shl cl, #0x02
  8.8417 +  jmp hd0_post_store_logical
  8.8418 +
  8.8419 +hd0_post_above_4096:
  8.8420 +  cmp bx, #8192
  8.8421 +  jnbe hd0_post_above_8192
  8.8422 +  ;; 4096 < c <= 8192 cylinders
  8.8423 +  shr bx, #0x03
  8.8424 +  shl cl, #0x03
  8.8425 +  jmp hd0_post_store_logical
  8.8426 +
  8.8427 +hd0_post_above_8192:
  8.8428 +  ;; 8192 < c <= 16384 cylinders
  8.8429 +  shr bx, #0x04
  8.8430 +  shl cl, #0x04
  8.8431 +
  8.8432 +hd0_post_store_logical:
  8.8433 +  mov   (0x003d + 0x00), bx ;; number of physical cylinders
  8.8434 +  mov   (0x003d + 0x02), cl ;; number of physical heads
  8.8435 +  ;; checksum
  8.8436 +  mov   cl, #0x0f     ;; repeat count
  8.8437 +  mov   si, #0x003d   ;; offset to disk0 FDPT
  8.8438 +  mov   al, #0x00     ;; sum
  8.8439 +hd0_post_checksum_loop:
  8.8440 +  add   al, [si]
  8.8441 +  inc   si
  8.8442 +  dec   cl
  8.8443 +  jnz hd0_post_checksum_loop
  8.8444 +  not   al  ;; now take 2s complement
  8.8445 +  inc   al
  8.8446 +  mov   [si], al
  8.8447 +;;; Done filling EBDA table for hard disk 0.
  8.8448 +
  8.8449 +
  8.8450 +check_for_hd1:
  8.8451 +  ;; is there really a second hard disk?  if not, return now
  8.8452 +  mov  al, #0x12
  8.8453 +  out  #0x70, al
  8.8454 +  in   al, #0x71
  8.8455 +  and  al, #0x0f
  8.8456 +  jnz   post_d1_exists
  8.8457 +  ret
  8.8458 +post_d1_exists:
  8.8459 +  ;; check that the hd type is really 0x0f.
  8.8460 +  cmp al, #0x0f
  8.8461 +  jz post_d1_extended
  8.8462 +  HALT(__LINE__)
  8.8463 +post_d1_extended:
  8.8464 +  ;; check that the extended type is 47 - user definable
  8.8465 +  mov  al, #0x1a
  8.8466 +  out  #0x70, al
  8.8467 +  in   al, #0x71
  8.8468 +  cmp  al, #47  ;; decimal 47 - user definable
  8.8469 +  je   post_d1_type47
  8.8470 +  HALT(__LINE__)
  8.8471 +post_d1_type47:
  8.8472 +  ;; Table for disk1.
  8.8473 +  ;; CMOS  purpose                  param table offset
  8.8474 +  ;; 0x24    cylinders low            0
  8.8475 +  ;; 0x25    cylinders high           1
  8.8476 +  ;; 0x26    heads                    2
  8.8477 +  ;; 0x27    write pre-comp low       5
  8.8478 +  ;; 0x28    write pre-comp high      6
  8.8479 +  ;; 0x29    heads>8                  8
  8.8480 +  ;; 0x2a    landing zone low         C
  8.8481 +  ;; 0x2b    landing zone high        D
  8.8482 +  ;; 0x2c    sectors/track            E
  8.8483 +;;; Fill EBDA table for hard disk 1.
  8.8484 +  mov  ax, #EBDA_SEG
  8.8485 +  mov  ds, ax
  8.8486 +  mov  al, #0x28
  8.8487 +  out  #0x70, al
  8.8488 +  in   al, #0x71
  8.8489 +  mov  ah, al
  8.8490 +  mov  al, #0x27
  8.8491 +  out  #0x70, al
  8.8492 +  in   al, #0x71
  8.8493 +  mov   (0x004d + 0x05), ax ;; write precomp word
  8.8494 +
  8.8495 +  mov  al, #0x29
  8.8496 +  out  #0x70, al
  8.8497 +  in   al, #0x71
  8.8498 +  mov   (0x004d + 0x08), al ;; drive control byte
  8.8499 +
  8.8500 +  mov  al, #0x2b
  8.8501 +  out  #0x70, al
  8.8502 +  in   al, #0x71
  8.8503 +  mov  ah, al
  8.8504 +  mov  al, #0x2a
  8.8505 +  out  #0x70, al
  8.8506 +  in   al, #0x71
  8.8507 +  mov   (0x004d + 0x0C), ax ;; landing zone word
  8.8508 +
  8.8509 +  mov  al, #0x25   ;; get cylinders word in AX
  8.8510 +  out  #0x70, al
  8.8511 +  in   al, #0x71   ;; high byte
  8.8512 +  mov  ah, al
  8.8513 +  mov  al, #0x24
  8.8514 +  out  #0x70, al
  8.8515 +  in   al, #0x71   ;; low byte
  8.8516 +  mov  bx, ax      ;; BX = cylinders
  8.8517 +
  8.8518 +  mov  al, #0x26
  8.8519 +  out  #0x70, al
  8.8520 +  in   al, #0x71
  8.8521 +  mov  cl, al      ;; CL = heads
  8.8522 +
  8.8523 +  mov  al, #0x2c
  8.8524 +  out  #0x70, al
  8.8525 +  in   al, #0x71
  8.8526 +  mov  dl, al      ;; DL = sectors
  8.8527 +
  8.8528 +  cmp  bx, #1024
  8.8529 +  jnbe hd1_post_logical_chs ;; if cylinders > 1024, use translated style CHS
  8.8530 +
  8.8531 +hd1_post_physical_chs:
  8.8532 +  ;; no logical CHS mapping used, just physical CHS
  8.8533 +  ;; use Standard Fixed Disk Parameter Table (FDPT)
  8.8534 +  mov   (0x004d + 0x00), bx ;; number of physical cylinders
  8.8535 +  mov   (0x004d + 0x02), cl ;; number of physical heads
  8.8536 +  mov   (0x004d + 0x0E), dl ;; number of physical sectors
  8.8537 +  ret
  8.8538 +
  8.8539 +hd1_post_logical_chs:
  8.8540 +  ;; complies with Phoenix style Translated Fixed Disk Parameter Table (FDPT)
  8.8541 +  mov   (0x004d + 0x09), bx ;; number of physical cylinders
  8.8542 +  mov   (0x004d + 0x0b), cl ;; number of physical heads
  8.8543 +  mov   (0x004d + 0x04), dl ;; number of physical sectors
  8.8544 +  mov   (0x004d + 0x0e), dl ;; number of logical sectors (same)
  8.8545 +  mov al, #0xa0
  8.8546 +  mov   (0x004d + 0x03), al ;; A0h signature, indicates translated table
  8.8547 +
  8.8548 +  cmp bx, #2048
  8.8549 +  jnbe hd1_post_above_2048
  8.8550 +  ;; 1024 < c <= 2048 cylinders
  8.8551 +  shr bx, #0x01
  8.8552 +  shl cl, #0x01
  8.8553 +  jmp hd1_post_store_logical
  8.8554 +
  8.8555 +hd1_post_above_2048:
  8.8556 +  cmp bx, #4096
  8.8557 +  jnbe hd1_post_above_4096
  8.8558 +  ;; 2048 < c <= 4096 cylinders
  8.8559 +  shr bx, #0x02
  8.8560 +  shl cl, #0x02
  8.8561 +  jmp hd1_post_store_logical
  8.8562 +
  8.8563 +hd1_post_above_4096:
  8.8564 +  cmp bx, #8192
  8.8565 +  jnbe hd1_post_above_8192
  8.8566 +  ;; 4096 < c <= 8192 cylinders
  8.8567 +  shr bx, #0x03
  8.8568 +  shl cl, #0x03
  8.8569 +  jmp hd1_post_store_logical
  8.8570 +
  8.8571 +hd1_post_above_8192:
  8.8572 +  ;; 8192 < c <= 16384 cylinders
  8.8573 +  shr bx, #0x04
  8.8574 +  shl cl, #0x04
  8.8575 +
  8.8576 +hd1_post_store_logical:
  8.8577 +  mov   (0x004d + 0x00), bx ;; number of physical cylinders
  8.8578 +  mov   (0x004d + 0x02), cl ;; number of physical heads
  8.8579 +  ;; checksum
  8.8580 +  mov   cl, #0x0f     ;; repeat count
  8.8581 +  mov   si, #0x004d   ;; offset to disk0 FDPT
  8.8582 +  mov   al, #0x00     ;; sum
  8.8583 +hd1_post_checksum_loop:
  8.8584 +  add   al, [si]
  8.8585 +  inc   si
  8.8586 +  dec   cl
  8.8587 +  jnz hd1_post_checksum_loop
  8.8588 +  not   al  ;; now take 2s complement
  8.8589 +  inc   al
  8.8590 +  mov   [si], al
  8.8591 +;;; Done filling EBDA table for hard disk 1.
  8.8592 +
  8.8593 +  ret
  8.8594 +
  8.8595 +;--------------------
  8.8596 +;- POST: EBDA segment
  8.8597 +;--------------------
  8.8598 +; relocated here because the primary POST area isnt big enough.
  8.8599 +ebda_post:
  8.8600 +#if BX_USE_EBDA
  8.8601 +  mov ax, #EBDA_SEG
  8.8602 +  mov ds, ax
  8.8603 +  mov byte ptr [0x0], #EBDA_SIZE
  8.8604 +#endif
  8.8605 +  xor ax, ax            ; mov EBDA seg into 40E
  8.8606 +  mov ds, ax
  8.8607 +  mov word ptr [0x40E], #EBDA_SEG
  8.8608 +  ret;;
  8.8609 +
  8.8610 +;--------------------
  8.8611 +;- POST: EOI + jmp via [0x40:67)
  8.8612 +;--------------------
  8.8613 +; relocated here because the primary POST area isnt big enough.
  8.8614 +eoi_jmp_post:
  8.8615 +  call eoi_both_pics
  8.8616 +
  8.8617 +  xor ax, ax
  8.8618 +  mov ds, ax
  8.8619 +
  8.8620 +  jmp far ptr [0x467]
  8.8621 +
  8.8622 +
  8.8623 +;--------------------
  8.8624 +eoi_both_pics:
  8.8625 +  mov   al, #0x20
  8.8626 +  out   #0xA0, al ;; slave  PIC EOI
  8.8627 +eoi_master_pic:
  8.8628 +  mov   al, #0x20
  8.8629 +  out   #0x20, al ;; master PIC EOI
  8.8630 +  ret
  8.8631 +
  8.8632 +;--------------------
  8.8633 +BcdToBin:
  8.8634 +  ;; in:  AL in BCD format
  8.8635 +  ;; out: AL in binary format, AH will always be 0
  8.8636 +  ;; trashes BX
  8.8637 +  mov  bl, al
  8.8638 +  and  bl, #0x0f ;; bl has low digit
  8.8639 +  shr  al, #4    ;; al has high digit
  8.8640 +  mov  bh, #10
  8.8641 +  mul  al, bh    ;; multiply high digit by 10 (result in AX)
  8.8642 +  add  al, bl    ;;   then add low digit
  8.8643 +  ret
  8.8644 +
  8.8645 +;--------------------
  8.8646 +timer_tick_post:
  8.8647 +  ;; Setup the Timer Ticks Count (0x46C:dword) and
  8.8648 +  ;;   Timer Ticks Roller Flag (0x470:byte)
  8.8649 +  ;; The Timer Ticks Count needs to be set according to
  8.8650 +  ;; the current CMOS time, as if ticks have been occurring
  8.8651 +  ;; at 18.2hz since midnight up to this point.  Calculating
  8.8652 +  ;; this is a little complicated.  Here are the factors I gather
  8.8653 +  ;; regarding this.  14,318,180 hz was the original clock speed,
  8.8654 +  ;; chosen so it could be divided by either 3 to drive the 5Mhz CPU
  8.8655 +  ;; at the time, or 4 to drive the CGA video adapter.  The div3
  8.8656 +  ;; source was divided again by 4 to feed a 1.193Mhz signal to
  8.8657 +  ;; the timer.  With a maximum 16bit timer count, this is again
  8.8658 +  ;; divided down by 65536 to 18.2hz.
  8.8659 +  ;;
  8.8660 +  ;; 14,318,180 Hz clock
  8.8661 +  ;;   /3 = 4,772,726 Hz fed to orginal 5Mhz CPU
  8.8662 +  ;;   /4 = 1,193,181 Hz fed to timer
  8.8663 +  ;;   /65536 (maximum timer count) = 18.20650736 ticks/second
  8.8664 +  ;; 1 second = 18.20650736 ticks
  8.8665 +  ;; 1 minute = 1092.390442 ticks
  8.8666 +  ;; 1 hour   = 65543.42651 ticks
  8.8667 +  ;;
  8.8668 +  ;; Given the values in the CMOS clock, one could calculate
  8.8669 +  ;; the number of ticks by the following:
  8.8670 +  ;;   ticks = (BcdToBin(seconds) * 18.206507) +
  8.8671 +  ;;           (BcdToBin(minutes) * 1092.3904)
  8.8672 +  ;;           (BcdToBin(hours)   * 65543.427)
  8.8673 +  ;; To get a little more accuracy, since Im using integer
  8.8674 +  ;; arithmatic, I use:
  8.8675 +  ;;   ticks = (BcdToBin(seconds) * 18206507) / 1000000 +
  8.8676 +  ;;           (BcdToBin(minutes) * 10923904) / 10000 +
  8.8677 +  ;;           (BcdToBin(hours)   * 65543427) / 1000
  8.8678 +
  8.8679 +  ;; assuming DS=0000
  8.8680 +
  8.8681 +  ;; get CMOS seconds
  8.8682 +  xor  eax, eax ;; clear EAX
  8.8683 +  mov  al, #0x00
  8.8684 +  out  #0x70, al
  8.8685 +  in   al, #0x71 ;; AL has CMOS seconds in BCD
  8.8686 +  call BcdToBin  ;; EAX now has seconds in binary
  8.8687 +  mov  edx, #18206507
  8.8688 +  mul  eax, edx
  8.8689 +  mov  ebx, #1000000
  8.8690 +  xor  edx, edx
  8.8691 +  div  eax, ebx
  8.8692 +  mov  ecx, eax  ;; ECX will accumulate total ticks
  8.8693 +
  8.8694 +  ;; get CMOS minutes
  8.8695 +  xor  eax, eax ;; clear EAX
  8.8696 +  mov  al, #0x02
  8.8697 +  out  #0x70, al
  8.8698 +  in   al, #0x71 ;; AL has CMOS minutes in BCD
  8.8699 +  call BcdToBin  ;; EAX now has minutes in binary
  8.8700 +  mov  edx, #10923904
  8.8701 +  mul  eax, edx
  8.8702 +  mov  ebx, #10000
  8.8703 +  xor  edx, edx
  8.8704 +  div  eax, ebx
  8.8705 +  add  ecx, eax  ;; add to total ticks
  8.8706 +
  8.8707 +  ;; get CMOS hours
  8.8708 +  xor  eax, eax ;; clear EAX
  8.8709 +  mov  al, #0x04
  8.8710 +  out  #0x70, al
  8.8711 +  in   al, #0x71 ;; AL has CMOS hours in BCD
  8.8712 +  call BcdToBin  ;; EAX now has hours in binary
  8.8713 +  mov  edx, #65543427
  8.8714 +  mul  eax, edx
  8.8715 +  mov  ebx, #1000
  8.8716 +  xor  edx, edx
  8.8717 +  div  eax, ebx
  8.8718 +  add  ecx, eax  ;; add to total ticks
  8.8719 +
  8.8720 +  mov  0x46C, ecx ;; Timer Ticks Count
  8.8721 +  xor  al, al
  8.8722 +  mov  0x470, al  ;; Timer Ticks Rollover Flag
  8.8723 +  ret
  8.8724 +
  8.8725 +;--------------------
  8.8726 +int76_handler:
  8.8727 +  ;; record completion in BIOS task complete flag
  8.8728 +  push  ax
  8.8729 +  push  ds
  8.8730 +  mov   ax, #0x0040
  8.8731 +  mov   ds, ax
  8.8732 +  mov   0x008E, #0xff
  8.8733 +  call  eoi_both_pics
  8.8734 +  pop   ds
  8.8735 +  pop   ax
  8.8736 +  iret
  8.8737 +
  8.8738 +
  8.8739 +;--------------------
  8.8740 +#if BX_APM
  8.8741 +
  8.8742 +use32 386
  8.8743 +#define APM_PROT32
  8.8744 +#include "apmbios.S"
  8.8745 +
  8.8746 +use16 386
  8.8747 +#define APM_PROT16
  8.8748 +#include "apmbios.S"
  8.8749 +
  8.8750 +#define APM_REAL
  8.8751 +#include "apmbios.S"
  8.8752 +
  8.8753 +#endif
  8.8754 +
  8.8755 +;--------------------
  8.8756 +#if BX_PCIBIOS
  8.8757 +use32 386
  8.8758 +.align 16
  8.8759 +bios32_structure:
  8.8760 +  db 0x5f, 0x33, 0x32, 0x5f  ;; "_32_" signature
  8.8761 +  dw bios32_entry_point, 0xf ;; 32 bit physical address
  8.8762 +  db 0             ;; revision level
  8.8763 +  ;; length in paragraphs and checksum stored in a word to prevent errors
  8.8764 +  dw (~(((bios32_entry_point >> 8) + (bios32_entry_point & 0xff) + 0x32) \
  8.8765 +        & 0xff) << 8) + 0x01
  8.8766 +  db 0,0,0,0,0     ;; reserved
  8.8767 +
  8.8768 +.align 16
  8.8769 +bios32_entry_point:
  8.8770 +  pushf
  8.8771 +  cmp eax, #0x49435024
  8.8772 +  jne unknown_service
  8.8773 +  mov eax, #0x80000000
  8.8774 +  mov dx, #0x0cf8
  8.8775 +  out dx, eax
  8.8776 +  mov dx, #0x0cfc
  8.8777 +  in  eax, dx
  8.8778 +  cmp eax, #0x12378086
  8.8779 +  jne unknown_service
  8.8780 +  mov ebx, #0x000f0000
  8.8781 +  mov ecx, #0
  8.8782 +  mov edx, #pcibios_protected
  8.8783 +  xor al, al
  8.8784 +  jmp bios32_end
  8.8785 +unknown_service:
  8.8786 +  mov al, #0x80
  8.8787 +bios32_end:
  8.8788 +  popf
  8.8789 +  retf
  8.8790 +
  8.8791 +.align 16
  8.8792 +pcibios_protected:
  8.8793 +  pushf
  8.8794 +  cli
  8.8795 +  push esi
  8.8796 +  push edi
  8.8797 +  cmp al, #0x01 ;; installation check
  8.8798 +  jne pci_pro_f02
  8.8799 +  mov bx, #0x0210
  8.8800 +  mov cx, #0
  8.8801 +  mov edx, #0x20494350
  8.8802 +  mov al, #0x01
  8.8803 +  jmp pci_pro_ok
  8.8804 +pci_pro_f02: ;; find pci device
  8.8805 +  cmp al, #0x02
  8.8806 +  jne pci_pro_f08
  8.8807 +  shl ecx, #16
  8.8808 +  mov cx, dx
  8.8809 +  mov bx, #0x0000
  8.8810 +  mov di, #0x00
  8.8811 +pci_pro_devloop:
  8.8812 +  call pci_pro_select_reg
  8.8813 +  mov dx, #0x0cfc
  8.8814 +  in  eax, dx
  8.8815 +  cmp eax, ecx
  8.8816 +  jne pci_pro_nextdev
  8.8817 +  cmp si, #0
  8.8818 +  je  pci_pro_ok
  8.8819 +  dec si
  8.8820 +pci_pro_nextdev:
  8.8821 +  inc bx
  8.8822 +  cmp bx, #0x0100
  8.8823 +  jne pci_pro_devloop
  8.8824 +  mov ah, #0x86
  8.8825 +  jmp pci_pro_fail
  8.8826 +pci_pro_f08: ;; read configuration byte
  8.8827 +  cmp al, #0x08
  8.8828 +  jne pci_pro_f09
  8.8829 +  call pci_pro_select_reg
  8.8830 +  push edx
  8.8831 +  mov dx, di
  8.8832 +  and dx, #0x03
  8.8833 +  add dx, #0x0cfc
  8.8834 +  in  al, dx
  8.8835 +  pop edx
  8.8836 +  mov cl, al
  8.8837 +  jmp pci_pro_ok
  8.8838 +pci_pro_f09: ;; read configuration word
  8.8839 +  cmp al, #0x09
  8.8840 +  jne pci_pro_f0a
  8.8841 +  call pci_pro_select_reg
  8.8842 +  push edx
  8.8843 +  mov dx, di
  8.8844 +  and dx, #0x02
  8.8845 +  add dx, #0x0cfc
  8.8846 +  in  ax, dx
  8.8847 +  pop edx
  8.8848 +  mov cx, ax
  8.8849 +  jmp pci_pro_ok
  8.8850 +pci_pro_f0a: ;; read configuration dword
  8.8851 +  cmp al, #0x0a
  8.8852 +  jne pci_pro_f0b
  8.8853 +  call pci_pro_select_reg
  8.8854 +  push edx
  8.8855 +  mov dx, #0x0cfc
  8.8856 +  in  eax, dx
  8.88