Index: /trunk/src/VBox/Devices/PC/BIOS/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS/Makefile.kmk	(revision 42769)
+++ /trunk/src/VBox/Devices/PC/BIOS/Makefile.kmk	(revision 42770)
@@ -58,4 +58,5 @@
  	scsi.c \
  	ahci.c \
+ 	apm.c \
  	pcibios.c \
  	pciutil.c \
@@ -63,4 +64,5 @@
  	../../BiosCommonCode/support.asm \
  	pcibio32.asm \
+ 	apm_pm.asm \
  	$(VBoxPcBios32_1_TARGET) \
  	orgs.asm
Index: /trunk/src/VBox/Devices/PC/BIOS/apm.c
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS/apm.c	(revision 42770)
+++ /trunk/src/VBox/Devices/PC/BIOS/apm.c	(revision 42770)
@@ -0,0 +1,219 @@
+/* $Id$ */
+/** @file
+ * APM BIOS support. Implements APM version 1.2.
+ */
+
+/*
+ * Copyright (C) 2004-2012 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include "biosint.h"
+#include "inlines.h"
+
+#define DEBUG_APM  1 //@todo!
+#if DEBUG_APM
+#  define BX_DEBUG_APM(...) BX_DEBUG(__VA_ARGS__)
+#else
+#  define BX_DEBUG_APM(...)
+#endif
+
+/* Implemented in assembly. */
+extern void apm_pm16_entry(void);
+#pragma aux apm_pm16_entry "*"
+extern void apm_pm32_entry(void);
+#pragma aux apm_pm32_entry "*"
+
+/* APM function codes. */
+enum apm_func {
+    APM_CHECK   = 0x00,     /* APM Installation Check */
+    APM_RM_CONN = 0x01,     /* APM Real Mode Interface Connect */
+    APM_PM_CONN = 0x02,     /* APM Protected Mode 16-bit Interface Connect */
+    APM_32_CONN = 0x03,     /* APM Protected Mode 32-bit Interface Connect */
+    APM_DISCONN = 0x04,     /* APM Interface Disconnect */
+    APM_IDLE    = 0x05,     /* CPU Idle */
+    APM_BUSY    = 0x06,     /* CPU Busy */
+    APM_SET_PWR = 0x07,     /* Set Power State */
+    APM_ENBL_PM = 0x08,     /* Enable/Disable Power Management */
+    APM_SET_DFL = 0x09,     /* Restore APM BIOS Power-On Defaults */
+    APM_STATUS  = 0x0A,     /* Get Power Status */
+    APM_GET_EVT = 0x0B,     /* Get PM Event */
+    APM_GET_PWR = 0x0C,     /* Get Power State */
+    APM_DEVPM   = 0x0D,     /* Enable/Disable Device Power Management */
+    APM_DRV_VER = 0x0E,     /* APM Driver Version */
+    APM_ENGAGE  = 0x0F,     /* Engage/Disengage Power Management */
+    APM_GET_CAP = 0x10      /* Get Capabilities */
+};
+
+enum apm_error {
+    APM_ERR_PM_DISABLED = 0x01,     /* Power Management functionality disabled */
+    APM_ERR_RM_INUSE    = 0x02,     /* Real mode interface connection already established */
+    APM_ERR_NOT_CONN    = 0x03,     /* Interface not connected */
+    APM_ERR_PM_16_INUSE = 0x05,     /* 16-bit protected mode interface connection already established */
+    APM_ERR_NO_PM_16    = 0x06,     /* 16-bit protected mode interface not supported */
+    APM_ERR_PM_32_INUSE = 0x07,     /* 32-bit protected mode interface connection already established */
+    APM_ERR_NO_PM_32    = 0x08,     /* 32-bit protected mode interface not supported */
+    APM_ERR_BAD_DEV_ID  = 0x09,     /* Unrecognized device ID */
+    APM_ERR_INVAL_PARAM = 0x0A,     /* Parameter out of range */
+    APM_ERR_NOT_ENGAGED = 0x0B,     /* Interface not engaged */
+    APM_ERR_UNSUPPORTED = 0x0C,     /* Function not supported */
+    APM_ERR_NO_RSM_TMR  = 0x0D,     /* Resume timer disabled */
+    APM_ERR_NO_EVENTS   = 0x80      /* No power management events pending */
+};
+
+enum apm_power_state {
+    APM_PS_ENABLED      = 0x00,     /* APM enabled */
+    APM_PS_STANDBY      = 0x01,     /* Standby */
+    APM_PS_SUSPEND      = 0x02,     /* Suspend */
+    APM_PS_OFF          = 0x03,     /* Suspend */
+};
+
+#define APM_PORT        0x8900      /* Bochs power control port. */
+
+// @todo: merge with system.c
+#define AX      r.gr.u.r16.ax
+#define BX      r.gr.u.r16.bx
+#define CX      r.gr.u.r16.cx
+#define DX      r.gr.u.r16.dx
+#define SI      r.gr.u.r16.si
+#define DI      r.gr.u.r16.di
+#define BP      r.gr.u.r16.bp
+#define SP      r.gr.u.r16.sp
+#define FLAGS   r.fl.u.r16.flags
+#define EAX     r.gr.u.r32.eax
+#define EBX     r.gr.u.r32.ebx
+#define ECX     r.gr.u.r32.ecx
+#define EDX     r.gr.u.r32.edx
+#define ES      r.es
+
+#define APM_BIOS_SEG        0xF000      /* Real-mode APM segment. */
+#define APM_BIOS_SEG_LEN    0xFFF0      /* Length of APM segment. */
+
+/* The APM BIOS interface uses 32-bit registers *only* in the 32-bit
+ * protected mode connect call. Rather than saving/restoring 32-bit
+ * registers all the time, simply set the high words of those registers
+ * when necessary.
+ */
+void set_ebx_hi(uint16_t val);
+#pragma aux set_ebx_hi =    \
+    ".386"                  \
+    "shl    ebx, 16"        \
+    parm [bx] modify exact [bx] nomemory;
+
+void set_esi_hi(uint16_t val);
+#pragma aux set_esi_hi =    \
+    ".386"                  \
+    "shl    esi, 16"        \
+    parm [si] modify exact [si] nomemory;
+
+
+/* The APM handler has unique requirements. It must be callable from real and
+ * protected mode, both 16-bit and 32-bit. In protected mode, the caller must
+ * ensures that appropriate selectors are available; these only cover the BIOS
+ * code and data, hence the BIOS Data Area or EBDA cannot be accessed. CMOS is
+ * a good place to store information which needs to be accessible from several
+ * different contexts.
+ *
+ * Note that the 32-bit protected-mode handler only needs to thunk down to the
+ * 16-bit code. There's no need for separate 16-bit and 32-bit implementation.
+ */
+
+/* Output a null-terminated string to a specified port, without the
+ * terminating null character.
+ */
+static void apm_out_str_asm(uint16_t port, const char *s);
+#pragma aux apm_out_str_asm =   \
+    "mov    al, [bx]"       \
+    "next:"                 \
+    "out    dx, al"         \
+    "inc    bx"             \
+    "mov    al, [bx]"       \
+    "or     al, al"         \
+    "jnz    next"           \
+    parm [dx] [bx] modify exact [ax bx] nomemory;
+
+/* Wrapper to avoid unnecessary inlining. */
+void apm_out_str(const char *s, uint16_t port)
+{
+    if (*s)
+        apm_out_str_asm(port, s);
+}
+
+void BIOSCALL apm_function(sys_regs_t r)
+{
+    BX_DEBUG_APM("APM: AX=%04X BX=%04X CX=%04X\n", AX, BX, CX);
+
+    CLEAR_CF();         /* Boldly expect success. */
+    switch (GET_AL()) {
+    case APM_CHECK:
+        AX = 0x0102;    /* Version 1.2 */
+        BX = 0x504D;    /* 'PM' */
+        CX = 3;         /* Bits 0/1: 16-bit/32-bit PM interface */
+        break;
+    case APM_RM_CONN:
+        // @todo: validate device ID
+        // @todo: validate current connection state
+        // @todo: change connection state
+        break;
+    case APM_32_CONN:
+        // @todo: validate device ID
+        // @todo: validate current connection state
+        // @todo: change connection state
+        AX = APM_BIOS_SEG;              /* 32-bit PM code segment (RM segment base). */
+        BX = (uint16_t)apm_pm32_entry;  /* 32-bit entry point offset. */
+        CX = APM_BIOS_SEG;              /* 16-bit code segment. */
+        DX = APM_BIOS_SEG;              /* 16-bit data segment. */
+        SI = APM_BIOS_SEG_LEN;          /* 32-bit code segment length. */
+        DI = APM_BIOS_SEG_LEN;          /* Data segment length. */
+        set_ebx_hi(0);
+        set_esi_hi(APM_BIOS_SEG_LEN);   /* 16-bit code segment length. */
+        break;
+    case APM_IDLE:
+        int_enable();   /* Simply halt the CPU with interrupts enabled. */
+        halt();
+        break;
+    case APM_SET_PWR:
+        // @todo: validate device ID
+        // @todo: validate current connection state
+        switch (CX) {
+        case APM_PS_STANDBY:
+            apm_out_str("Standby", APM_PORT);
+            break;
+        case APM_PS_SUSPEND:
+            apm_out_str("Suspend", APM_PORT);
+            break;
+        case APM_PS_OFF:
+            apm_out_str("Shutdown", APM_PORT);  /* Should not return. */
+            break;
+        default:
+            SET_AH(APM_ERR_INVAL_PARAM);
+            SET_CF();
+        }
+        break;
+    case APM_DRV_VER:
+        AX = 0x0102;    // @todo: Not right - must take driver version into account!
+        break;
+    case APM_DISCONN:
+        // @todo: actually perform a disconnect...
+    case APM_BUSY:      /* Nothing to do as APM Idle doesn't slow CPU clock. */
+        break;
+    case APM_GET_EVT:
+        // @todo: error should be different if interface not connected + engaged
+        SET_AH(APM_ERR_NO_EVENTS);  /* PM events don't happen. */
+        SET_CF();       
+        break;
+    default:
+        BX_INFO("APM: Unsupported function AX=%04X BX=%04X called\n", AX, BX);
+        SET_AH(APM_ERR_UNSUPPORTED);
+        SET_CF();
+    }
+}
Index: /trunk/src/VBox/Devices/PC/BIOS/apm_pm.asm
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS/apm_pm.asm	(revision 42770)
+++ /trunk/src/VBox/Devices/PC/BIOS/apm_pm.asm	(revision 42770)
@@ -0,0 +1,192 @@
+;;
+;; Copyright (C) 2006-2012 Oracle Corporation
+;;
+;; This file is part of VirtualBox Open Source Edition (OSE), as
+;; available from http://www.virtualbox.org. This file is free software;
+;; you can redistribute it and/or modify it under the terms of the GNU
+;; General Public License (GPL) as published by the Free Software
+;; Foundation, in version 2 as it comes in the "COPYING" file of the
+;; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+;; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;;
+;; --------------------------------------------------------------------
+;;
+;; Protected-mode APM implementation.
+;;
+
+
+;; 16-bit protected mode APM entry point
+
+_TEXT		segment public 'CODE'
+
+extern		_apm_function:near	; implemented in C code
+
+
+public		apm_pm16_entry
+
+.286
+
+
+; APM function dispatch table
+apm_disp:
+		dw	offset apmf_disconnect	; 04h
+		dw	offset apmf_idle	; 05h
+		dw	offset apmf_busy	; 06h
+		dw	offset apmf_set_state	; 07h
+		dw	offset apmf_enable	; 08h
+		dw	offset apmf_restore	; 09h
+		dw	offset apmf_get_status	; 0Ah
+		dw	offset apmf_get_event	; 0Bh
+		dw	offset apmf_pwr_state	; 0Ch
+		dw	offset apmf_dev_pm	; 0Dh
+		dw	offset apmf_version	; 0Eh
+		dw	offset apmf_engage	; 0Fh
+		dw	offset apmf_get_caps	; 10h
+apm_disp_end:
+
+;
+; APM worker routine. Function code in AL; it is assumed that AL >= 4.
+; Caller must preserve BP.
+;
+apm_worker	proc	near
+
+		sti			; TODO ?? necessary ??
+
+		push	ax		; check if function is supported...
+		xor	ah, ah
+		sub	al, 4
+		mov	bp, ax
+		shl	bp, 1
+		cmp	al, (apm_disp_end - apm_disp) / 2
+		pop	ax
+		mov	ah, 53h		; put back APM function
+		jae	apmw_bad_func	; validate function range
+
+		jmp	apm_disp[bp]	; and dispatch
+
+apmf_disconnect:			; function 04h
+		jmp	apmw_success
+
+apmf_idle:				; function 05h
+		sti
+		hlt
+		jmp	apmw_success
+
+apmf_busy:				; function 06h
+;		jmp	apmw_success
+
+apmf_set_state:				; function 07h
+;		jmp	apmw_success
+
+apmf_enable:				; function 08h
+		jmp	apmw_success
+
+apmf_restore:				; function 09h
+;		jmp	apmw_success
+
+apmf_get_status:			; function 0Ah
+		jmp	apmw_bad_func
+
+apmf_get_event:				; function 0Bh
+		mov	ah, 80h
+		jmp	apmw_failure
+
+apmf_pwr_state:				; function 0Ch
+
+apmf_dev_pm:				; function 0Dh
+		jmp	apmw_bad_func
+
+apmf_version:				; function 0Eh
+		mov	ax, 0102h
+		jmp	apmw_success
+
+apmf_engage:				; function 0Fh
+		; TODO do something?
+		jmp	apmw_success
+
+apmf_get_caps:				; function 10h
+		mov	bl, 0		; no batteries
+		mov	cx, 0		; no special caps
+		jmp	apmw_success
+
+apmw_success:
+		clc			; successful return
+		ret
+
+apmw_bad_func:
+		mov	ah, 09h		; unrecognized device ID - generic
+
+apmw_failure:
+		stc			; error for unsupported functions
+		ret
+
+apm_worker	endp
+
+
+
+apm_pm16_entry:
+		stc
+		retf			; return to 16-bit caller
+
+apm_pm16_entry_from_32:
+
+		push	ds		; save registers
+		push	bp
+
+		push	cs
+		pop	bp
+		add	bp, 8		; calculate data selector
+		mov	ds, bp		; load data segment
+
+		call	apm_worker	; call APM handler
+
+		pop	bp
+		pop	ds		; restore registers
+
+.386
+		retfd			; return to 32-bit code
+
+_TEXT		ends
+
+
+.386
+
+BIOS32		segment	public 'CODE' use32
+
+public		apm_pm32_entry
+
+;; 32-bit protected mode APM entry point and thunk
+
+;; According to the APM spec, only CS (32-bit) is defined. 16-bit code
+;; selector and the data selector can be derived from it.
+
+apm_pm32_entry:
+
+;		cli
+;		hlt
+
+		push	ebp		; ebp is not used by APM
+
+		push	cs		; return address for 16-bit code
+		push	apm_pm32_back
+
+		push	cs
+		pop	ebp
+		add	ebp, 8		; calculate 16-bit code selector
+		push	ebp		; push 16-bit code selector
+
+		xor	ebp, ebp	; manually pad 16-bit offset
+		push	bp		; to a 32-bit value
+		push	apm_pm16_entry_from_32
+
+		mov	ah, 3		; mark as originating in 32-bit PM
+		retf			; off to 16-bit code...
+
+apm_pm32_back:				; return here from 16-bit code
+
+		pop	ebp		; restore scratch register
+		retf
+
+BIOS32		ends
+
+		end
Index: /trunk/src/VBox/Devices/PC/BIOS/makefile
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS/makefile	(revision 42769)
+++ /trunk/src/VBox/Devices/PC/BIOS/makefile	(revision 42770)
@@ -31,6 +31,6 @@
 
 OBJS = 	bios.obj print.obj ata.obj floppy.obj eltorito.obj boot.obj &
-	keyboard.obj disk.obj serial.obj system.obj timepci.obj &
-	ps2mouse.obj parallel.obj logo.obj scsi.obj ahci.obj &
+	keyboard.obj disk.obj serial.obj system.obj timepci.obj logo.obj &
+	ps2mouse.obj parallel.obj scsi.obj ahci.obj apm.obj apm_pm.obj &
 	pcibios.obj pciutil.obj vds.obj pcibio32.obj pci32.obj orgs.obj
 
Index: /trunk/src/VBox/Devices/PC/BIOS/notes.txt
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS/notes.txt	(revision 42769)
+++ /trunk/src/VBox/Devices/PC/BIOS/notes.txt	(revision 42770)
@@ -46,4 +46,7 @@
 - PC DOS 6.x/7.x QCONFIG is a rare user of INT 16h fn 0Ah (read keyboard ID).
 
+- Windows 98 is one of the few APM 1.2 users; Windows 95 uses APM 1.1, while
+  newer systems prefer ACPI.
+
 
  Notes on BIOS implementation
@@ -72,4 +75,16 @@
   the type unsigned.
 
+- The PCI BIOS services are implemented in C, compiled twice as 16-bit and
+  32-bit code. This reduces the development effort and significantly lowers
+  the risk of discrepancies between 16-bit and 32-bit implementation. Care
+  must be taken because the 16-bit implementation can be executed in both
+  real and protected mode.
+
+- APM can be in theory implemented only once for real, 16-bit protected and
+  32-bit protected mode. Unfortunately this is very inconvenient in C since
+  the default stack size changes between 16-bit and 32-bit callers. Therefore
+  real mode APM (which supports most functions) is implemented in C and
+  protected-mode APM is written in assembler for both 16-bit and 32-bit calls,
+  with a small 32->16 thunk.
 
 
Index: /trunk/src/VBox/Devices/PC/BIOS/orgs.asm
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS/orgs.asm	(revision 42769)
+++ /trunk/src/VBox/Devices/PC/BIOS/orgs.asm	(revision 42770)
@@ -112,4 +112,5 @@
 extrn		_int70_function:near
 extrn		_int74_function:near
+extrn		_apm_function:near
 extrn		_ata_init:near
 extrn		_ahci_init:near
@@ -1519,6 +1520,4 @@
 int15_handler:
 		pushf
-		cmp	ah, 53h		; APM function?
-		je	apm_call
 		push	ds
 		push	es
@@ -1529,9 +1528,11 @@
 		je	int15_handler32
 		pusha
-		cmp	ah, 0C2h
+		cmp	ah, 53h		; APM function?
+		je	apm_call
+		cmp	ah, 0C2h	; PS/2 mouse function?
 		je	int15_handler_mouse
 
 		call	_int15_function
-int15_handler_mouse_ret:
+int15_handler_popa_ret:
 		popa
 int15_handler32_ret:
@@ -1542,13 +1543,10 @@
 
 apm_call:
-; TODO!!
-		popf
-		stc
-		jmp	iret_modify_cf
-;		jmp	apmreal_entry
+		call	_apm_function
+		jmp	int15_handler_popa_ret
 
 int15_handler_mouse:
 		call	_int15_function_mouse
-		jmp	int15_handler_mouse_ret
+		jmp	int15_handler_popa_ret
 
 int15_handler32:
