Index: /trunk/src/VBox/Devices/Input/DevPS2.cpp
===================================================================
--- /trunk/src/VBox/Devices/Input/DevPS2.cpp	(revision 48213)
+++ /trunk/src/VBox/Devices/Input/DevPS2.cpp	(revision 48214)
@@ -54,5 +54,5 @@
 #include "PS2Dev.h"
 
-#define PCKBD_SAVED_STATE_VERSION 6
+#define PCKBD_SAVED_STATE_VERSION 7
 
 
@@ -119,4 +119,5 @@
 #define KBD_MODE_RFU            0x80
 
+#ifndef VBOX_WITH_NEW_PS2M
 /* Mouse Commands */
 #define AUX_SET_SCALE11         0xE6    /* Set 1:1 scaling */
@@ -173,4 +174,5 @@
     int     count;
 } MouseEventQueue;
+#endif
 
 /**
@@ -181,6 +183,8 @@
 typedef struct KBDState
 {
+#ifndef VBOX_WITH_NEW_PS2M
     MouseCmdQueue mouse_command_queue;
     MouseEventQueue mouse_event_queue;
+#endif
     uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
     uint8_t status;
@@ -190,4 +194,5 @@
     int32_t translate;
     int32_t xlat_state;
+#ifndef VBOX_WITH_NEW_PS2M
     /* mouse state */
     int32_t mouse_write_cmd;
@@ -205,4 +210,5 @@
     uint8_t mouse_buttons;
     uint8_t mouse_buttons_reported;
+#endif
 
     uint32_t    Alignment0;
@@ -222,4 +228,12 @@
 #endif
 
+#ifdef VBOX_WITH_NEW_PS2M
+    /** Mouse state (implemented in separate PS2M module). */
+#ifdef VBOX_DEVICE_STRUCT_TESTCASE
+    uint8_t                     AuxFiller[PS2M_STRUCT_FILLER];
+#else
+    PS2M                        Aux;
+#endif
+#else
     /**
      * Mouse port - LUN#1.
@@ -240,4 +254,5 @@
         R3PTRTYPE(PPDMIMOUSECONNECTOR)      pDrv;
     } Mouse;
+#endif
 } KBDState;
 
@@ -247,6 +262,8 @@
 static void kbd_update_irq(KBDState *s)
 {
+#ifndef VBOX_WITH_NEW_PS2M
     MouseCmdQueue *mcq = &s->mouse_command_queue;
     MouseEventQueue *meq = &s->mouse_event_queue;
+#endif
     int irq12_level, irq1_level;
     uint8_t val;
@@ -292,4 +309,11 @@
             }
         }
+#ifdef VBOX_WITH_NEW_PS2M
+        else if (!(s->mode & KBD_MODE_DISABLE_MOUSE) && PS2MByteFromAux(&s->Aux, &val) == VINF_SUCCESS)
+        {
+            s->dbbout = val;
+            s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
+        }
+#else
         else if ((mcq->count || meq->count) && !(s->mode & KBD_MODE_DISABLE_MOUSE))
         {
@@ -310,4 +334,5 @@
             }
         }
+#endif
     }
     /* Determine new IRQ state. */
@@ -334,4 +359,5 @@
 }
 
+#ifndef VBOX_WITH_NEW_PS2M
 static void kbd_queue(KBDState *s, int b, int aux)
 {
@@ -375,4 +401,5 @@
     kbd_update_irq(s);
 }
+#endif
 
 static void kbc_dbb_out(void *opaque, uint8_t val)
@@ -385,4 +412,15 @@
     if (s->mode & KBD_MODE_KBD_INT)
         PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 1, 1);
+}
+
+static void kbc_dbb_out_aux(void *opaque, uint8_t val)
+{
+    KBDState *s = (KBDState*)opaque;
+
+    s->dbbout = val;
+    /* Set the aux OBF and raise IRQ. */
+    s->status |= KBD_STAT_OBF | KBD_STAT_MOUSE_OBF;
+    if (s->mode & KBD_MODE_MOUSE_INT)
+        PDMDevHlpISASetIrq(s->CTX_SUFF(pDevIns), 12, PDM_IRQ_LEVEL_HIGH);
 }
 
@@ -557,4 +595,15 @@
 }
 
+PS2M *KBDGetPS2MFromDevIns(PPDMDEVINS pDevIns)
+{
+    KBDState *pThis = PDMINS_2_DATA(pDevIns, KBDState *);
+#ifdef VBOX_WITH_NEW_PS2M
+    return &pThis->Aux;
+#else
+    return NULL;
+#endif
+}
+
+#ifndef VBOX_WITH_NEW_PS2M
 static void kbd_mouse_set_reported_buttons(KBDState *s, unsigned fButtons, unsigned fButtonMask)
 {
@@ -914,4 +963,5 @@
     return rc;
 }
+#endif
 
 static int kbd_write_data(void *opaque, uint32_t addr, uint32_t val)
@@ -942,5 +992,5 @@
         break;
     case KBD_CCMD_WRITE_AUX_OBUF:
-        kbd_queue(s, val, 1);
+        kbc_dbb_out_aux(s, val);
         break;
     case KBD_CCMD_WRITE_OUTPORT:
@@ -964,5 +1014,11 @@
         /* Automatically enables aux interface. */
         s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+#ifdef VBOX_WITH_NEW_PS2M
+        rc = PS2MByteToAux(&s->Aux, val);
+        if (rc == VINF_SUCCESS)
+            kbd_update_irq(s);
+#else
         rc = kbd_write_mouse(s, val);
+#endif
         break;
     default:
@@ -979,8 +1035,10 @@
 {
     KBDState *s = (KBDState*)opaque;
+#ifndef VBOX_WITH_NEW_PS2M
     MouseCmdQueue *mcq;
     MouseEventQueue *meq;
 
     s->mouse_write_cmd = -1;
+#endif
     s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
     s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
@@ -988,4 +1046,5 @@
     s->write_cmd = 0;
     s->translate = 0;
+#ifndef VBOX_WITH_NEW_PS2M
     if (s->mouse_status)
     {
@@ -1013,10 +1072,13 @@
     meq->wptr = 0;
     meq->count = 0;
+#endif
 }
 
 static void kbd_save(QEMUFile* f, void* opaque)
 {
+#ifndef VBOX_WITH_NEW_PS2M
     uint32_t    cItems;
     int i;
+#endif
     KBDState *s = (KBDState*)opaque;
 
@@ -1025,4 +1087,5 @@
     qemu_put_8s(f, &s->mode);
     qemu_put_8s(f, &s->dbbout);
+#ifndef VBOX_WITH_NEW_PS2M
     qemu_put_be32s(f, &s->mouse_write_cmd);
     qemu_put_8s(f, &s->mouse_status);
@@ -1051,4 +1114,5 @@
         SSMR3PutU8(f, s->mouse_event_queue.data[i]);
     Log(("kbd_save: %d mouse event queue items stored\n", s->mouse_event_queue.count));
+#endif
 
     /* terminator */
@@ -1084,4 +1148,5 @@
         qemu_get_8s(f, &s->dbbout);
     }
+#ifndef VBOX_WITH_NEW_PS2M
     qemu_get_be32s(f, (uint32_t *)&s->mouse_write_cmd);
     qemu_get_8s(f, &s->mouse_status);
@@ -1115,4 +1180,5 @@
     s->mouse_event_queue.rptr = 0;
     s->mouse_event_queue.wptr = 0;
+#endif
 
     /* Determine the translation state. */
@@ -1136,4 +1202,5 @@
     }
 
+#ifndef VBOX_WITH_NEW_PS2M
     rc = SSMR3GetU32(f, &u32);
     if (RT_FAILURE(rc))
@@ -1171,4 +1238,19 @@
     s->mouse_event_queue.count = u32;
     Log(("kbd_load: %d mouse event queue items loaded\n", u32));
+#else
+    if (version_id <= 6)
+    {
+        rc = SSMR3GetU32(f, &u32);
+        if (RT_FAILURE(rc))
+            return rc;
+        for (i = 0; i < u32; i++)
+        {
+            rc = SSMR3GetU8(f, &u8Dummy);
+            if (RT_FAILURE(rc))
+                return rc;
+        }
+        Log(("kbd_load: %d mouse event queue items discarded from old saved state\n", u32));
+    }
+#endif
 
     /* terminator */
@@ -1181,6 +1263,8 @@
         return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
     }
+#ifndef VBOX_WITH_NEW_PS2M
     /* Resend a notification to Main if the device is active */
     kbd_mouse_update_downstream_status(s);
+#endif
     return 0;
 }
@@ -1308,4 +1392,7 @@
     kbd_save(pSSM, pThis);
     PS2KSaveState(&pThis->Kbd, pSSM);
+#ifdef VBOX_WITH_NEW_PS2M
+    PS2MSaveState(&pThis->Aux, pSSM);
+#endif
     return VINF_SUCCESS;
 }
@@ -1330,4 +1417,8 @@
     if (uVersion >= 6)
         rc = PS2KLoadState(&pThis->Kbd, pSSM, uVersion);
+#ifdef VBOX_WITH_NEW_PS2M
+    if (uVersion >= 7)
+        rc = PS2MLoadState(&pThis->Aux, pSSM, uVersion);
+#endif
     return rc;
 }
@@ -1345,7 +1436,11 @@
     kbd_reset(pThis);
     PS2KReset(&pThis->Kbd);
-}
-
-
+#ifdef VBOX_WITH_NEW_PS2M
+    PS2MReset(&pThis->Aux);
+#endif
+}
+
+
+#ifndef VBOX_WITH_NEW_PS2M
 /* -=-=-=-=-=- Mouse: IBase  -=-=-=-=-=- */
 
@@ -1403,5 +1498,5 @@
     NOREF(pInterface); NOREF(cContacts); NOREF(pau64Contacts); NOREF(u32ScanTime);
 }
-
+#endif
 
 /* -=-=-=-=-=- real code -=-=-=-=-=- */
@@ -1444,4 +1539,7 @@
         /* LUN #1: aux/mouse */
         case 1:
+#ifdef VBOX_WITH_NEW_PS2M
+            rc = PS2MAttach(&pThis->Aux, pDevIns, iLUN, fFlags);
+#else
             rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Aux (Mouse) Port");
             if (RT_SUCCESS(rc))
@@ -1461,4 +1559,5 @@
             else
                 AssertLogRelMsgFailed(("Failed to attach LUN #1! rc=%Rrc\n", rc));
+#endif
             break;
 
@@ -1526,4 +1625,7 @@
     pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
     PS2KRelocate(&pThis->Kbd, offDelta, pDevIns);
+#ifdef VBOX_WITH_NEW_PS2M
+    PS2MRelocate(&pThis->Aux, offDelta, pDevIns);
+#endif
 }
 
@@ -1553,4 +1655,5 @@
     if (RT_FAILURE(rc))
         return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to query \"R0Enabled\" from the config"));
+fGCEnabled = fR0Enabled = false;
     Log(("pckbd: fGCEnabled=%RTbool fR0Enabled=%RTbool\n", fGCEnabled, fR0Enabled));
 
@@ -1567,8 +1670,14 @@
         return rc;
 
+#ifdef VBOX_WITH_NEW_PS2M
+    rc = PS2MConstruct(&pThis->Aux, pDevIns, pThis, iInstance);
+    if (RT_FAILURE(rc))
+        return rc;
+#else
     pThis->Mouse.IBase.pfnQueryInterface    = kbdMouseQueryInterface;
     pThis->Mouse.IPort.pfnPutEvent          = kbdMousePutEvent;
     pThis->Mouse.IPort.pfnPutEventAbs       = kbdMousePutEventAbs;
     pThis->Mouse.IPort.pfnPutEventMultiTouch = kbdMousePutEventMultiTouch;
+#endif
 
     /*
Index: /trunk/src/VBox/Devices/Input/PS2Dev.h
===================================================================
--- /trunk/src/VBox/Devices/Input/PS2Dev.h	(revision 48213)
+++ /trunk/src/VBox/Devices/Input/PS2Dev.h	(revision 48214)
@@ -18,8 +18,9 @@
 #define PS2DEV_H
 
-/** The size of the PS2K structure filler.
+/** The size of the PS2K/PS2M structure fillers.
  * @note Must be at least as big as the real struct. Compile time assert
  *       makes sure this is so. */
 #define PS2K_STRUCT_FILLER  512
+#define PS2M_STRUCT_FILLER  512
 
 /* Hide the internal structure. */
@@ -31,4 +32,12 @@
 #endif
 
+#if !(defined(IN_PS2M) || defined(VBOX_DEVICE_STRUCT_TESTCASE))
+typedef struct PS2M
+{
+    uint8_t     abFiller[PS2M_STRUCT_FILLER];
+} PS2M;
+#endif
+
+/* Internal PS/2 Keyboard interface. */
 typedef struct PS2K *PPS2K;
 
@@ -43,7 +52,26 @@
 int  PS2KLoadState(PPS2K pThis, PSSMHANDLE pSSM, uint32_t uVersion);
 
+PS2K *KBDGetPS2KFromDevIns(PPDMDEVINS pDevIns);
+
+
+/* Internal PS/2 Auxiliary device interface. */
+typedef struct PS2M *PPS2M;
+
+int  PS2MByteToAux(PPS2M pThis, uint8_t cmd);
+int  PS2MByteFromAux(PPS2M pThis, uint8_t *pVal);
+
+int  PS2MConstruct(PPS2M pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance);
+int  PS2MAttach(PPS2M pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags);
+void PS2MReset(PPS2M pThis);
+void PS2MRelocate(PPS2M pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns);
+void PS2MSaveState(PPS2M pThis, PSSMHANDLE pSSM);
+int  PS2MLoadState(PPS2M pThis, PSSMHANDLE pSSM, uint32_t uVersion);
+
+PS2M *KBDGetPS2MFromDevIns(PPDMDEVINS pDevIns);
+
+
+/* Shared keyboard/aux internal interface. */
 void KBCUpdateInterrupts(void *pKbc);
 
-PS2K *KBDGetPS2KFromDevIns(PPDMDEVINS pDevIns);
 
 ///@todo: This should live with the KBC implementation.
Index: /trunk/src/VBox/Devices/Input/PS2M.cpp
===================================================================
--- /trunk/src/VBox/Devices/Input/PS2M.cpp	(revision 48214)
+++ /trunk/src/VBox/Devices/Input/PS2M.cpp	(revision 48214)
@@ -0,0 +1,1134 @@
+/** @file
+ * PS2M - PS/2 auxiliary device (mouse) emulation.
+ */
+
+/*
+ * Copyright (C) 2007-2013 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.
+ */
+
+/*
+ * References:
+ *
+ * The Undocumented PC (2nd Ed.), Frank van Gilluwe, Addison-Wesley, 1996.
+ * IBM TrackPoint System Version 4.0 Engineering Specification, 1999.
+ * ELAN Microelectronics eKM8025 USB & PS/2 Mouse Controller, 2006.
+ *
+ *
+ * Notes:
+ *
+ *  - The auxiliary device commands are very similar to keyboard commands.
+ *    Most keyboard commands which do not specifically deal with the keyboard
+ *    (enable, disable, reset) have identical counterparts.
+ *  - The code refers to 'auxiliary device' and 'mouse'; these terms are not
+ *    quite interchangeable. 'Auxiliary device' is used when referring to the
+ *    generic PS/2 auxiliary device interface and 'mouse' when referring to
+ *    a mouse attached to the auxiliary port.
+ *  - The basic modes of operation are reset, stream, and remote. Those are
+ *    mutually exclusive. Stream and remote modes can additionally have wrap
+ *    mode enabled.
+ *  - The auxiliary device sends unsolicited data to the host only when it is
+ *    both in stream mode and enabled. Otherwise it only responds to commands.
+ *
+ *
+ * There are three report packet formats supported by the emulated device. The
+ * standard three-byte PS/2 format (with middle button support), IntelliMouse
+ * four-byte format with added scroll wheel, and IntelliMouse Explorer four-byte
+ * format with reduced scroll wheel range but two additional buttons. Note that
+ * the first three bytes of the report are always the same.
+ *
+ * Upon reset, the mouse is always in the standard PS/2 mode. A special 'knock'
+ * sequence can be used to switch to ImPS/2 or ImEx mode. Three consecutive
+ * Set Sampling Rate (0F3h) commands with arguments 200, 100, 80 switch to ImPS/2
+ * mode. While in ImPS/2 mode, three consecutive Set Sampling Rate commands with
+ * arguments 200, 200, 80 switch to ImEx mode. The Read ID (0F2h) command will
+ * report the currently selected protocol.
+ *
+ *
+ * Standard PS/2 pointing device three-byte report packet format:
+ *
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 1 | Y ovfl | X ovfl | Y sign | X sign |  Sync  | M btn  | R btn  | L btn  |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 2 |              X movement delta (two's complement)                      |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 3 |              Y movement delta (two's complement)                      |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ *
+ *   - The sync bit is always set. It allows software to synchronize data packets
+ *     as the X/Y position data typically does not have bit 4 set.
+ *   - The overflow bits are set if motion exceeds accumulator range. We use the
+ *     maximum range (effectively 9 bits) and do not set the overflow bits.
+ *   - Movement in the up/right direction is defined as having positive sign.
+ *
+ *
+ * IntelliMouse PS/2 (ImPS/2) fourth report packet byte:
+ *
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 4 |              Z movement delta (two's complement)                      |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ *
+ *   - The valid range for Z delta values is only -8/+7, i.e. 4 bits.
+ *
+ * IntelliMouse Explorer (ImEx) fourth report packet byte:
+ *
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * |Bit/byte|  bit 7 |  bit 6 |  bit 5 |  bit 4 |  bit 3 |  bit 2 |  bit 1 |  bit 0 |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ * | Byte 4 |   0    |   0    | Btn 5  | Btn 4  |  Z mov't delta (two's complement) |
+ * +--------+--------+--------+--------+--------+--------+--------+--------+--------+
+ *
+ */
+
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#define LOG_GROUP   LOG_GROUP_DEV_KBD
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/err.h>
+#include <iprt/assert.h>
+#include <iprt/uuid.h>
+#include "VBoxDD.h"
+#define IN_PS2M
+#include "PS2Dev.h"
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+/** @name Auxiliary device commands sent by the system.
+ * @{ */
+#define ACMD_SET_SCALE_11   0xE6    /* Set 1:1 scaling. */
+#define ACMD_SET_SCALE_21   0xE7    /* Set 2:1 scaling. */
+#define ACMD_SET_RES        0xE8    /* Set resolution. */
+#define ACMD_REQ_STATUS     0xE9    /* Get device status. */
+#define ACMD_SET_STREAM     0xEA    /* Set stream mode. */
+#define ACMD_READ_REMOTE    0xEB    /* Read remote data. */
+#define ACMD_RESET_WRAP     0xEC    /* Exit wrap mode. */
+#define ACMD_INVALID_1      0xED
+#define ACMD_SET_WRAP       0xEE    /* Set wrap (echo) mode. */
+#define ACMD_INVALID_2      0xEF
+#define ACMD_SET_REMOTE     0xF0    /* Set remote mode. */
+#define ACMD_INVALID_3      0xF1
+#define ACMD_READ_ID        0xF2    /* Read device ID. */
+#define ACMD_SET_SAMP_RATE  0xF3    /* Set sampling rate. */
+#define ACMD_ENABLE         0xF4    /* Enable (streaming mode). */
+#define ACMD_DFLT_DISABLE   0xF5    /* Disable and set defaults. */
+#define ACMD_SET_DEFAULT    0xF6    /* Set defaults. */
+#define ACMD_INVALID_4      0xF7
+#define ACMD_INVALID_5      0xF8
+#define ACMD_INVALID_6      0xF9
+#define ACMD_INVALID_7      0xFA
+#define ACMD_INVALID_8      0xFB
+#define ACMD_INVALID_9      0xFC
+#define ACMD_INVALID_10     0xFD
+#define ACMD_RESEND         0xFE    /* Resend response. */
+#define ACMD_RESET          0xFF    /* Reset device. */
+/** @} */
+
+/** @name Auxiliary device responses sent to the system.
+ * @{ */
+#define ARSP_ID             0x00
+#define ARSP_BAT_OK         0xAA    /* Self-test passed. */
+#define ARSP_ACK            0xFA    /* Command acknowledged. */
+#define ARSP_ERROR          0xFC    /* Bad command. */
+#define ARSP_RESEND         0xFE    /* Requesting resend. */
+/** @} */
+
+/** Define a simple PS/2 input device queue. */
+#define DEF_PS2Q_TYPE(name, size)   \
+     typedef struct {               \
+        uint32_t    rpos;           \
+        uint32_t    wpos;           \
+        uint32_t    cUsed;          \
+        uint32_t    cSize;          \
+        uint8_t     abQueue[size];  \
+     } name
+
+/* Internal mouse queue sizes. The input queue is relatively large,
+ * but the command queue only needs to handle a few bytes.
+ */
+#define AUX_EVT_QUEUE_SIZE        256
+#define AUX_CMD_QUEUE_SIZE          8
+
+/*******************************************************************************
+*   Structures and Typedefs                                                    *
+*******************************************************************************/
+
+DEF_PS2Q_TYPE(AuxEvtQ, AUX_EVT_QUEUE_SIZE);
+DEF_PS2Q_TYPE(AuxCmdQ, AUX_CMD_QUEUE_SIZE);
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE //@todo: hack
+DEF_PS2Q_TYPE(GeneriQ, 1);
+#endif
+
+/* Auxiliary device special modes of operation. */
+typedef enum {
+    AUX_MODE_STD,           /* Standard operation. */
+    AUX_MODE_RESET,         /* Currently in reset. */
+    AUX_MODE_WRAP           /* Wrap mode (echoing input). */
+} PS2M_MODE;
+
+/* Auxiliary device operational state. */
+typedef enum {
+    AUX_STATE_SCALING = RT_BIT(4),  /* 2:1 scaling in effect. */
+    AUX_STATE_ENABLED = RT_BIT(5),  /* Reporting enabled in stream mode. */
+    AUX_STATE_REMOTE  = RT_BIT(6)   /* Remote mode (reports on request). */
+} PS2M_STATE;
+
+/* Protocols supported by the PS/2 mouse. */
+typedef enum {
+    PS2M_PROTO_PS2STD = 0,  /* Standard PS/2 mouse protocol. */
+    PS2M_PROTO_IMPS2  = 3,  /* IntelliMouse PS/2 protocol. */
+    PS2M_PROTO_IMEX   = 4   /* IntelliMouse Explorer protocol. */
+} PS2M_PROTO;
+
+/* Protocol selection 'knock' states. */
+typedef enum {
+    PS2M_KNOCK_INITIAL,
+    PS2M_KNOCK_IMPS2_1ST,
+    PS2M_KNOCK_IMPS2_2ND,
+    PS2M_KNOCK_IMEX_1ST,
+    PS2M_KNOCK_IMEX_2ND
+} PS2M_KNOCK_STATE;
+
+/**
+ * The PS/2 auxiliary device instance data.
+ */
+typedef struct PS2M
+{
+    /** Pointer to parent device (keyboard controller). */
+    R3PTRTYPE(void *)   pParent;
+    /** Operational state. */
+    uint8_t             u8State;
+    /** Configured sampling rate. */
+    uint8_t             u8SampleRate;
+    /** Configured resolution. */
+    uint8_t             u8Resolution;
+    /** Currently processed command (if any). */
+    uint8_t             u8CurrCmd;
+    /** Set if the throttle delay is active. */
+    bool                fThrottleActive;
+    /** Operational mode. */
+    PS2M_MODE           enmMode;
+    /** Currently used protocol. */
+    PS2M_PROTO          enmProtocol;
+    /** Currently used protocol. */
+    PS2M_KNOCK_STATE    enmKnockState;
+    /** Buffer holding mouse events to be sent to the host. */
+    AuxEvtQ             evtQ;
+    /** Command response queue (priority). */
+    AuxCmdQ             cmdQ;
+    /** Accumulated horizontal movement. */
+    int32_t             iAccumX;
+    /** Accumulated vertical movement. */
+    int32_t             iAccumY;
+    /** Accumulated Z axis movement. */
+    int32_t             iAccumZ;
+    /** Accumulated button presses. */
+    uint32_t            fAccumB;
+    /** Throttling delay in milliseconds. */
+    unsigned            uThrottleDelay;
+#if HC_ARCH_BITS == 32
+    uint32_t            Alignment0;
+#endif
+
+    /** The device critical section protecting everything - R3 Ptr */
+    R3PTRTYPE(PPDMCRITSECT) pCritSectR3;
+    /** Command delay timer - R3 Ptr. */
+    PTMTIMERR3          pDelayTimerR3;
+    /** Interrupt throttling timer - R3 Ptr. */
+    PTMTIMERR3          pThrottleTimerR3;
+    RTR3PTR             Alignment1;
+
+    /** Command delay timer - RC Ptr. */
+    PTMTIMERRC          pDelayTimerRC;
+    /** Interrupt throttling timer - RC Ptr. */
+    PTMTIMERRC          pThrottleTimerRC;
+
+    /** Command delay timer - R0 Ptr. */
+    PTMTIMERR0          pDelayTimerR0;
+    /** Interrupt throttling timer - R0 Ptr. */
+    PTMTIMERR0          pThrottleTimerR0;
+
+    /**
+     * Mouse port - LUN#1.
+     *
+     * @implements  PDMIBASE
+     * @implements  PDMIMOUSEPORT
+     */
+    struct
+    {
+        /** The base interface for the mouse port. */
+        PDMIBASE                            IBase;
+        /** The keyboard port base interface. */
+        PDMIMOUSEPORT                       IPort;
+
+        /** The base interface of the attached mouse driver. */
+        R3PTRTYPE(PPDMIBASE)                pDrvBase;
+        /** The keyboard interface of the attached mouse driver. */
+        R3PTRTYPE(PPDMIMOUSECONNECTOR)      pDrv;
+    } Mouse;
+} PS2M, *PPS2M;
+
+AssertCompile(PS2M_STRUCT_FILLER >= sizeof(PS2M));
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+/* Key type flags. */
+#define KF_E0        0x01    /* E0 prefix. */
+#define KF_NB        0x02    /* No break code. */
+#define KF_GK        0x04    /* Gray navigation key. */
+#define KF_PS        0x08    /* Print Screen key. */
+#define KF_PB        0x10    /* Pause/Break key. */
+#define KF_NL        0x20    /* Num Lock key. */
+#define KF_NS        0x40    /* NumPad '/' key. */
+
+/*******************************************************************************
+*   Global Variables                                                           *
+*******************************************************************************/
+
+
+/*******************************************************************************
+*   Internal Functions                                                         *
+*******************************************************************************/
+
+
+/**
+ * Clear a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ */
+static void ps2kClearQueue(GeneriQ *pQ)
+{
+    LogFlowFunc(("Clearing queue %p\n", pQ));
+    pQ->wpos  = pQ->rpos;
+    pQ->cUsed = 0;
+}
+
+
+/**
+ * Add a byte to a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ * @param   val                 The byte to store.
+ */
+static void ps2kInsertQueue(GeneriQ *pQ, uint8_t val)
+{
+    /* Check if queue is full. */
+    if (pQ->cUsed >= pQ->cSize)
+    {
+        LogFlowFunc(("queue %p full (%d entries)\n", pQ, pQ->cUsed));
+        return;
+    }
+    /* Insert data and update circular buffer write position. */
+    pQ->abQueue[pQ->wpos] = val;
+    if (++pQ->wpos == pQ->cSize)
+        pQ->wpos = 0;   /* Roll over. */
+    ++pQ->cUsed;
+    LogFlowFunc(("inserted 0x%02X into queue %p\n", val, pQ));
+}
+
+#ifdef IN_RING3
+
+/**
+ * Save a queue state.
+ *
+ * @param   pSSM                SSM handle to write the state to.
+ * @param   pQ                  Pointer to the queue.
+ */
+static void ps2kSaveQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
+{
+    uint32_t    cItems = pQ->cUsed;
+    int         i;
+
+    /* Only save the number of items. Note that the read/write
+     * positions aren't saved as they will be rebuilt on load.
+     */
+    SSMR3PutU32(pSSM, cItems);
+
+    LogFlow(("Storing %d items from queue %p\n", cItems, pQ));
+
+    /* Save queue data - only the bytes actually used (typically zero). */
+    for (i = pQ->rpos; cItems-- > 0; i = (i + 1) % pQ->cSize)
+        SSMR3PutU8(pSSM, pQ->abQueue[i]);
+}
+
+/**
+ * Load a queue state.
+ *
+ * @param   pSSM                SSM handle to read the state from.
+ * @param   pQ                  Pointer to the queue.
+ *
+ * @return  int                 VBox status/error code.
+ */
+static int ps2kLoadQueue(PSSMHANDLE pSSM, GeneriQ *pQ)
+{
+    int         rc;
+
+    /* On load, always put the read pointer at zero. */
+    SSMR3GetU32(pSSM, &pQ->cUsed);
+
+    LogFlow(("Loading %d items to queue %p\n", pQ->cUsed, pQ));
+
+    if (pQ->cUsed > pQ->cSize)
+    {
+        AssertMsgFailed(("Saved size=%u, actual=%u\n", pQ->cUsed, pQ->cSize));
+        return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+    }
+
+    /* Recalculate queue positions and load data in one go. */
+    pQ->rpos = 0;
+    pQ->wpos = pQ->cUsed;
+    rc = SSMR3GetMem(pSSM, pQ->abQueue, pQ->cUsed);
+
+    return rc;
+}
+
+/* Report a change in status down (or is it up?) the driver chain. */
+static void ps2mSetDriverState(PPS2M pThis, bool fEnabled)
+{
+    PPDMIMOUSECONNECTOR pDrv = pThis->Mouse.pDrv;
+    if (pDrv)
+        pDrv->pfnReportModes(pDrv, fEnabled, false, false);
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Retrieve a byte from a queue.
+ *
+ * @param   pQ                  Pointer to the queue.
+ * @param   pVal                Pointer to storage for the byte.
+ *
+ * @return  int                 VINF_TRY_AGAIN if queue is empty,
+ *                              VINF_SUCCESS if a byte was read.
+ */
+static int ps2kRemoveQueue(GeneriQ *pQ, uint8_t *pVal)
+{
+    int     rc = VINF_TRY_AGAIN;
+
+    Assert(pVal);
+    if (pQ->cUsed)
+    {
+        *pVal = pQ->abQueue[pQ->rpos];
+        if (++pQ->rpos == pQ->cSize)
+            pQ->rpos = 0;   /* Roll over. */
+        --pQ->cUsed;
+        rc = VINF_SUCCESS;
+        LogFlowFunc(("removed 0x%02X from queue %p\n", *pVal, pQ));
+    } else
+        LogFlowFunc(("queue %p empty\n", pQ));
+    return rc;
+}
+
+static void ps2mSetRate(PPS2M pThis, uint8_t rate)
+{
+    pThis->uThrottleDelay = 1000 / rate;
+    pThis->u8SampleRate = rate;
+    LogFlowFunc(("Sampling rate %u, throttle delay %u ms\n", pThis->u8SampleRate, pThis->uThrottleDelay));
+}
+
+static void ps2mSetDefaults(PPS2M pThis)
+{
+    LogFlowFunc(("Set mouse defaults\n"));
+    /* Standard protocol, reporting disabled, resolution 2, 1:1 scaling. */
+    pThis->enmProtocol  = PS2M_PROTO_PS2STD;
+    pThis->u8State      = 0;
+    pThis->u8Resolution = 2;
+
+    /* Sample rate 100 reports per second. */
+    ps2mSetRate(pThis, 100);
+
+    /* Accumulators and button status bits are cleared. */
+    ps2kClearQueue((GeneriQ *)&pThis->evtQ);
+    //@todo accumulators/current state
+}
+
+/* Handle the sampling rate 'knock' sequence which selects protocol. */
+static void ps2mRateProtocolKnock(PPS2M pThis, uint8_t rate)
+{
+    if (pThis->enmProtocol == PS2M_PROTO_PS2STD)
+    {
+        switch (pThis->enmKnockState) 
+        {
+        case PS2M_KNOCK_INITIAL:
+            if (rate == 200)
+                pThis->enmKnockState = PS2M_KNOCK_IMPS2_1ST;
+            break;
+        case PS2M_KNOCK_IMPS2_1ST:
+            if (rate == 100)
+                pThis->enmKnockState = PS2M_KNOCK_IMPS2_2ND;
+            else
+                pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+            break;
+        case PS2M_KNOCK_IMPS2_2ND:
+            if (rate == 80)
+            {
+                pThis->enmProtocol = PS2M_PROTO_IMPS2;
+                LogRelFlow(("PS2M: Switching mouse to ImPS/2 protocol.\n"));
+            }
+        default:
+            pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+        }
+    } 
+    else if (pThis->enmProtocol == PS2M_PROTO_IMPS2) 
+    {
+        switch (pThis->enmKnockState) 
+        {
+        case PS2M_KNOCK_INITIAL:
+            if (rate == 200)
+                pThis->enmKnockState = PS2M_KNOCK_IMEX_1ST;
+            break;
+        case PS2M_KNOCK_IMEX_1ST:
+            if (rate == 200)
+                pThis->enmKnockState = PS2M_KNOCK_IMEX_2ND;
+            else
+                pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+            break;
+        case PS2M_KNOCK_IMEX_2ND:
+            if (rate == 80)
+            {
+                pThis->enmProtocol = PS2M_PROTO_IMEX;
+                LogRelFlow(("PS2M: Switching mouse to ImEx protocol.\n"));
+            }
+        default:
+            pThis->enmKnockState = PS2M_KNOCK_INITIAL;
+        }
+    }
+}
+
+/**
+ * Receive and process a byte sent by the keyboard controller.
+ *
+ * @param   pThis               The PS/2 auxiliary device instance data.
+ * @param   cmd                 The command (or data) byte.
+ */
+int PS2MByteToAux(PPS2M pThis, uint8_t cmd)
+{
+    uint8_t u8Val;
+    bool    fHandled = true;
+
+    LogFlowFunc(("cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
+//LogRel(("aux: cmd=0x%02X, active cmd=0x%02X\n", cmd, pThis->u8CurrCmd));
+
+    switch (cmd)
+    {
+        case ACMD_SET_SCALE_11:
+            pThis->u8State &= ~AUX_STATE_SCALING;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_SCALE_21:
+            pThis->u8State |= AUX_STATE_SCALING;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_REQ_STATUS:
+            /* Report current status, sample rate, and resolution. */
+            //@todo: buttons
+            u8Val  = pThis->u8State;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, u8Val);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8Resolution);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->u8SampleRate);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_STREAM:
+            pThis->u8State &= ~AUX_STATE_REMOTE;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_RESET_WRAP:
+            pThis->enmMode = AUX_MODE_STD;
+            /* NB: Stream mode reporting remains disabled! */
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_WRAP:
+            pThis->enmMode = AUX_MODE_WRAP;
+            pThis->u8State &= ~AUX_STATE_ENABLED;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_REMOTE:
+            pThis->u8State |= AUX_STATE_REMOTE;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_READ_ID:
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, pThis->enmProtocol);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_ENABLE:
+            pThis->u8State |= AUX_STATE_ENABLED;
+            //@todo: R3 only!
+#ifdef IN_RING3
+            ps2mSetDriverState(pThis, true);
+#endif
+            ps2kClearQueue((GeneriQ *)&pThis->evtQ);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_DFLT_DISABLE:
+            ps2mSetDefaults(pThis);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_SET_DEFAULT:
+            ps2mSetDefaults(pThis);
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_RESEND:
+            pThis->u8CurrCmd = 0;
+            break;
+        case ACMD_RESET:
+            ps2mSetDefaults(pThis);
+            ///@todo reset more?
+            pThis->u8CurrCmd = cmd;
+            pThis->enmMode   = AUX_MODE_RESET;
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            /* Slightly delay reset completion; it might take hundreds of ms. */
+            TMTimerSetMillies(pThis->CTX_SUFF(pDelayTimer), 1);
+            break;
+        /* The following commands need a parameter. */
+        case ACMD_SET_RES:
+        case ACMD_SET_SAMP_RATE:
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+            pThis->u8CurrCmd = cmd;
+            break;
+        default:
+            /* Sending a command instead of a parameter starts the new command. */
+            switch (pThis->u8CurrCmd)
+            {
+                case ACMD_SET_RES:
+                    //@todo reject unsupported resolutions
+                    pThis->u8Resolution = cmd;
+                    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+                    pThis->u8CurrCmd = 0;
+                    break;
+                case ACMD_SET_SAMP_RATE:
+                    //@todo reject unsupported rates                    
+                    ps2mSetRate(pThis, cmd);
+                    ps2mRateProtocolKnock(pThis, cmd);
+                    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_ACK);
+                    pThis->u8CurrCmd = 0;
+                    break;
+                default:
+                    fHandled = false;
+            }
+            /* Fall through only to handle unrecognized commands. */
+            if (fHandled)
+                break;
+
+        case ACMD_INVALID_1:
+        case ACMD_INVALID_2:
+        case ACMD_INVALID_3:
+        case ACMD_INVALID_4:
+        case ACMD_INVALID_5:
+        case ACMD_INVALID_6:
+        case ACMD_INVALID_7:
+        case ACMD_INVALID_8:
+        case ACMD_INVALID_9:
+        case ACMD_INVALID_10:
+            Log(("Unsupported command 0x%02X!\n", cmd));
+            ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_RESEND);
+            pThis->u8CurrCmd = 0;
+            break;
+    }
+    LogFlowFunc(("Active cmd now 0x%02X; updating interrupts\n", pThis->u8CurrCmd));
+//    KBCUpdateInterrupts(pThis->pParent);
+    return VINF_SUCCESS;
+}
+
+/**
+ * Send a byte (keystroke or command response) to the keyboard controller.
+ *
+ * @returns VINF_SUCCESS or VINF_TRY_AGAIN.
+ * @param   pThis               The PS/2 auxiliary device instance data.
+ * @param   pb                  Where to return the byte we've read.
+ * @remarks Caller must have entered the device critical section.
+ */
+int PS2MByteFromAux(PPS2M pThis, uint8_t *pb)
+{
+    int         rc;
+
+    AssertPtr(pb);
+
+    /* Anything in the command queue has priority over data
+     * in the event queue. Additionally, keystrokes are //@todo: true?
+     * blocked if a command is currently in progress, even if
+     * the command queue is empty.
+     */
+    //@todo: Probably should flush/not fill queue if stream mode reporting disabled?!
+    rc = ps2kRemoveQueue((GeneriQ *)&pThis->cmdQ, pb);
+    if (rc != VINF_SUCCESS && !pThis->u8CurrCmd && (pThis->u8State & AUX_STATE_ENABLED))
+        rc = ps2kRemoveQueue((GeneriQ *)&pThis->evtQ, pb);
+
+    LogFlowFunc(("mouse sends 0x%02x (%svalid data)\n", *pb, rc == VINF_SUCCESS ? "" : "not "));
+//if (rc == VINF_SUCCESS) LogRel(("aux: sends 0x%02X\n", *pb));
+
+    return rc;
+}
+
+#ifdef IN_RING3
+
+/* Three-button event mask. */
+#define PS2M_STD_BTN_MASK   (RT_BIT(0) | RT_BIT(1) | RT_BIT(2))
+
+/* Report accumulated movement and button presses, then clear the accumulators. */
+static void ps2mReportAccumulatedEvents(PPS2M pThis)
+{
+    uint8_t     val;
+    int8_t      dX, dY, dZ;
+
+    /* Clamp the accumulated delta values to the allowed range. */
+    dX = RT_MIN(RT_MAX(pThis->iAccumX, -256), 255);
+    dY = RT_MIN(RT_MAX(pThis->iAccumY, -256), 255);
+    dZ = RT_MIN(RT_MAX(pThis->iAccumZ, -8), 7);
+
+    /* Start with the sync bit and buttons 1-3. */
+    val = RT_BIT(3) | (pThis->fAccumB & PS2M_STD_BTN_MASK);
+    /* Set the X/Y sign bits. */
+    if (dX < 0)
+        val |= RT_BIT(4);
+    if (dY < 0)
+        val |= RT_BIT(5);
+
+    /* Send the standard 3-byte packet (always the same). */
+    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, val);
+    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dX);
+    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dY);
+
+    /* Add fourth byte if extended protocol is in use. */
+    if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
+    {
+        if (pThis->enmProtocol == PS2M_PROTO_IMPS2)
+            ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dZ);
+        else
+        {
+            Assert(pThis->enmProtocol == PS2M_PROTO_IMEX);
+            /* Z value uses 4 bits; buttons 4/5 in bits 4 and 5. */
+            val  = dZ & 0x0f;
+            val |= (pThis->fAccumB << 1) & (RT_BIT(4) | RT_BIT(5));
+            ps2kInsertQueue((GeneriQ *)&pThis->evtQ, dZ);
+        }
+    }
+
+    /* Clear the accumulators. */
+    pThis->iAccumX = pThis->iAccumY = pThis->iAccumZ = pThis->fAccumB = 0;
+
+    /* Poke the KBC to update its state. */
+    KBCUpdateInterrupts(pThis->pParent);
+}
+
+/* Event rate throttling timer to emulate the auxiliary device sampling rate.
+ */
+static DECLCALLBACK(void) ps2mThrottleTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    PPS2M       pThis = (PS2M *)pvUser; NOREF(pDevIns);
+    uint32_t    uHaveEvents;
+
+    /* Grab the lock to avoid races with PutEvent(). */
+    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
+    AssertReleaseRC(rc);
+
+#if 0
+    /* If the input queue is not empty, restart the timer. */
+#else
+    /* If more movement is accumulated, report it and restart the timer. */
+    uHaveEvents = pThis->iAccumX | pThis->iAccumY | pThis->iAccumZ | pThis->fAccumB;
+    LogFlowFunc(("Have%s events\n", uHaveEvents ? "" : " no"));
+
+    if (uHaveEvents)
+#endif
+    {
+        ps2mReportAccumulatedEvents(pThis);
+        TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
+    }
+    else
+        pThis->fThrottleActive = false;
+
+    PDMCritSectLeave(pThis->pCritSectR3);
+}
+
+/* The auxiliary device is specified to take up to about 500 milliseconds. We need
+ * to delay sending the result to the host for at least a tiny little while.
+ */
+static DECLCALLBACK(void) ps2mDelayTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
+{
+    PPS2M pThis = (PS2M *)pvUser; NOREF(pDevIns);
+
+    LogFlowFunc(("Delay timer: cmd %02X\n", pThis->u8CurrCmd));
+
+    Assert(pThis->u8CurrCmd == ACMD_RESET);
+    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, ARSP_BAT_OK);
+    ps2kInsertQueue((GeneriQ *)&pThis->cmdQ, 0);
+    pThis->enmMode   = AUX_MODE_STD;
+    pThis->u8CurrCmd = 0;
+
+    ///@todo Might want a PS2MCompleteCommand() to push last response, clear command, and kick the KBC...
+    /* Give the KBC a kick. */
+    KBCUpdateInterrupts(pThis->pParent);
+
+    //@todo: move to its proper home!
+    ps2mSetDriverState(pThis, true);
+}
+
+
+/**
+ * Debug device info handler. Prints basic auxiliary device state.
+ *
+ * @param   pDevIns     Device instance which registered the info.
+ * @param   pHlp        Callback functions for doing output.
+ * @param   pszArgs     Argument string. Optional and specific to the handler.
+ */
+static DECLCALLBACK(void) ps2mInfoState(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+    static const char   *pcszModes[] = { "normal", "reset", "wrap" };
+    static const char   *pcszProtocols[] = { "PS/2", NULL, NULL, "ImPS/2", "ImEx" };
+    PPS2M   pThis = KBDGetPS2MFromDevIns(pDevIns);
+    NOREF(pszArgs);
+
+    Assert(pThis->enmMode <= RT_ELEMENTS(pcszModes));
+    Assert(pThis->enmProtocol <= RT_ELEMENTS(pcszProtocols));
+    pHlp->pfnPrintf(pHlp, "PS/2 mouse state: %s, %s mode, reporting %s\n",
+                    pcszModes[pThis->enmMode],
+                    pThis->u8State & AUX_STATE_REMOTE  ? "remote"  : "stream",
+                    pThis->u8State & AUX_STATE_ENABLED ? "enabled" : "disabled");
+    pHlp->pfnPrintf(pHlp, "Protocol: %s, scaling %u:1\n",
+                    pcszProtocols[pThis->enmProtocol], pThis->u8State & AUX_STATE_SCALING ? 2 : 1);
+    pHlp->pfnPrintf(pHlp, "Active command %02X\n", pThis->u8CurrCmd);
+    pHlp->pfnPrintf(pHlp, "Sampling rate %u reports/sec, resolution %u counts/mm\n",
+                    pThis->u8SampleRate, 1 << pThis->u8Resolution);
+    pHlp->pfnPrintf(pHlp, "Command queue: %d items (%d max)\n",
+                    pThis->cmdQ.cUsed, pThis->cmdQ.cSize);
+    pHlp->pfnPrintf(pHlp, "Event queue  : %d items (%d max)\n",
+                    pThis->evtQ.cUsed, pThis->evtQ.cSize);
+}
+
+/* -=-=-=-=-=- Mouse: IBase  -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) ps2mQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+    PPS2M pThis = RT_FROM_MEMBER(pInterface, PS2M, Mouse.IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->Mouse.IBase);
+    PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMOUSEPORT, &pThis->Mouse.IPort);
+    return NULL;
+}
+
+
+/* -=-=-=-=-=- Mouse: IMousePort  -=-=-=-=-=- */
+
+/**
+ * Mouse event handler.
+ *
+ * @returns VBox status code.
+ * @param   pThis           The PS/2 auxiliary device instance data.
+ * @param   dx              X direction movement delta.
+ * @param   dy              Y direction movement delta.
+ * @param   dz              Z (vertical scroll) movement delta.
+ * @param   dw              W (horizontal scroll) movement delta.
+ * @param   fButtons        Depressed button mask.
+ */
+static int ps2mPutEventWorker(PPS2M pThis, int32_t dx, int32_t dy,
+                              int32_t dz, int32_t dw, uint32_t fButtons)
+{
+    int             rc = VINF_SUCCESS;
+
+    /* Update internal accumulators and button state. */
+    pThis->iAccumX += dx;
+    pThis->iAccumY += dy;
+    pThis->iAccumZ += dz;
+    pThis->fAccumB |= fButtons & PS2M_STD_BTN_MASK; //@todo: accumulate based on current protocol?
+
+#if 1
+    /* Report the event and the throttle timer unless it's already running. */
+    if (!pThis->fThrottleActive)
+    {
+        ps2mReportAccumulatedEvents(pThis);
+        pThis->fThrottleActive = true;
+        TMTimerSetMillies(pThis->CTX_SUFF(pThrottleTimer), pThis->uThrottleDelay);
+    }
+#else
+    /* Clamp the delta values to the allowed range. */
+    dx = RT_MIN(RT_MAX(dx, -256), 255);
+    dy = RT_MIN(RT_MAX(dy, -256), 255);
+
+    /* Start with the sync bit. */
+    val  = RT_BIT(3);
+    /* Add buttons 1-3. */
+    val |= fButtons & PS2M_STD_BTN_MASK;
+    /* Set the X/Y sign bits. */
+    if (dx < 0)
+        val |= RT_BIT(4);
+    if (dy < 0)
+        val |= RT_BIT(5);
+
+    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, val);
+    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dx);
+    ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dy);
+    if (pThis->enmProtocol > PS2M_PROTO_PS2STD)
+    {
+        ps2kInsertQueue((GeneriQ *)&pThis->evtQ, (uint8_t)dz);
+    }
+#endif
+
+    return rc;
+}
+
+/* -=-=-=-=-=- Mouse: IMousePort  -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIMOUSEPORT, pfnPutEvent}
+ */
+static DECLCALLBACK(int) ps2mPutEvent(PPDMIMOUSEPORT pInterface, int32_t dx, int32_t dy,
+                                      int32_t dz, int32_t dw, uint32_t fButtons)
+{
+    PPS2M       pThis = RT_FROM_MEMBER(pInterface, PS2M, Mouse.IPort);
+    int rc = PDMCritSectEnter(pThis->pCritSectR3, VERR_SEM_BUSY);
+    AssertReleaseRC(rc);
+
+    LogFlowFunc(("dX=%d dY=%d dZ=%d dW=%d buttons=%02X\n", dx, dy, dz, dw, fButtons));
+    /* NB: The PS/2 Y axis direction is inverted relative to ours. */
+    ps2mPutEventWorker(pThis, dx, -dy, dz, dw, fButtons);
+
+    PDMCritSectLeave(pThis->pCritSectR3);
+    return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIMOUSEPORT, pfnPutEventAbs}
+ */
+static DECLCALLBACK(int) ps2mPutEventAbs(PPDMIMOUSEPORT pInterface, uint32_t x, uint32_t y,
+                                         int32_t dz, int32_t dw, uint32_t fButtons)
+{
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+    NOREF(pInterface); NOREF(x); NOREF(y); NOREF(dz); NOREF(dw); NOREF(fButtons);
+}
+
+/**
+ * @interface_method_impl{PDMIMOUSEPORT, pfnPutEventMultiTouch}
+ */
+static DECLCALLBACK(int) ps2mPutEventMT(PPDMIMOUSEPORT pInterface, uint8_t cContacts,
+                                        const uint64_t *pau64Contacts, uint32_t u32ScanTime)
+{
+    AssertFailedReturn(VERR_NOT_SUPPORTED);
+    NOREF(pInterface); NOREF(cContacts); NOREF(pau64Contacts); NOREF(u32ScanTime);
+}
+
+
+
+/**
+ * Attach command.
+ *
+ * This is called to let the device attach to a driver for a
+ * specified LUN.
+ *
+ * This is like plugging in the mouse after turning on the
+ * system.
+ *
+ * @returns VBox status code.
+ * @param   pThis       The PS/2 auxiliary device instance data.
+ * @param   pDevIns     The device instance.
+ * @param   iLUN        The logical unit which is being detached.
+ * @param   fFlags      Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+int PS2MAttach(PPS2M pThis, PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+    int         rc;
+
+    /* The LUN must be 1, i.e. mouse. */
+    Assert(iLUN == 1);
+    AssertMsgReturn(fFlags & PDM_TACH_FLAGS_NOT_HOT_PLUG,
+                    ("PS/2 mouse does not support hotplugging\n"),
+                    VERR_INVALID_PARAMETER);
+
+    LogFlowFunc(("iLUN=%d\n", iLUN));
+
+    rc = PDMDevHlpDriverAttach(pDevIns, iLUN, &pThis->Mouse.IBase, &pThis->Mouse.pDrvBase, "Mouse Port");
+    if (RT_SUCCESS(rc))
+    {
+        pThis->Mouse.pDrv = PDMIBASE_QUERY_INTERFACE(pThis->Mouse.pDrvBase, PDMIMOUSECONNECTOR);
+        if (!pThis->Mouse.pDrv)
+        {
+            AssertLogRelMsgFailed(("LUN #1 doesn't have a mouse interface! rc=%Rrc\n", rc));
+            rc = VERR_PDM_MISSING_INTERFACE;
+        }
+    }
+    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
+    {
+        Log(("%s/%d: warning: no driver attached to LUN #1!\n", pDevIns->pReg->szName, pDevIns->iInstance));
+        rc = VINF_SUCCESS;
+    }
+    else
+        AssertLogRelMsgFailed(("Failed to attach LUN #1! rc=%Rrc\n", rc));
+
+    return rc;
+}
+
+void PS2MSaveState(PPS2M pThis, PSSMHANDLE pSSM)
+{
+    uint32_t    cPressed = 0;
+
+    LogFlowFunc(("Saving PS2M state\n"));
+
+    /* Save the core auxiliary device state. */
+    SSMR3PutU8(pSSM, pThis->u8State);
+    SSMR3PutU8(pSSM, pThis->u8SampleRate);
+    SSMR3PutU8(pSSM, pThis->u8Resolution);
+    SSMR3PutU8(pSSM, pThis->u8CurrCmd);
+    SSMR3PutU8(pSSM, pThis->enmMode);
+    SSMR3PutU8(pSSM, pThis->enmProtocol);
+    SSMR3PutU8(pSSM, pThis->enmKnockState);
+
+    /* Save the command and event queues. */
+    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
+    ps2kSaveQueue(pSSM, (GeneriQ *)&pThis->evtQ);
+
+    /* Save the command delay timer. Note that the rate throttling
+     * timer is *not* saved.
+     */
+    TMR3TimerSave(pThis->CTX_SUFF(pDelayTimer), pSSM);
+}
+
+int PS2MLoadState(PPS2M pThis, PSSMHANDLE pSSM, uint32_t uVersion)
+{
+    uint8_t     u8;
+    int         rc;
+
+    NOREF(uVersion);
+    LogFlowFunc(("Loading PS2M state version %u\n", uVersion));
+
+    /* Load the basic auxiliary device state. */
+    SSMR3GetU8(pSSM, &pThis->u8State);
+    SSMR3GetU8(pSSM, &pThis->u8SampleRate);
+    SSMR3GetU8(pSSM, &pThis->u8Resolution);
+    SSMR3GetU8(pSSM, &pThis->u8CurrCmd);
+    SSMR3GetU8(pSSM, &u8);
+    pThis->enmMode       = (PS2M_MODE)u8;
+    SSMR3GetU8(pSSM, &u8);
+    pThis->enmProtocol   = (PS2M_PROTO)u8;
+    SSMR3GetU8(pSSM, &u8);
+    pThis->enmKnockState = (PS2M_KNOCK_STATE)u8;
+
+    /* Load the command and event queues. */
+    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->cmdQ);
+    AssertRCReturn(rc, rc);
+    rc = ps2kLoadQueue(pSSM, (GeneriQ *)&pThis->evtQ);
+    AssertRCReturn(rc, rc);
+
+    /* Load the command delay timer, just in case. */
+    rc = TMR3TimerLoad(pThis->CTX_SUFF(pDelayTimer), pSSM);
+    AssertRCReturn(rc, rc);
+
+    /* Recalculate the throttling delay. */
+    ps2mSetRate(pThis, pThis->u8SampleRate);
+
+    //@todo: Is this the right place/logic?
+    ps2mSetDriverState(pThis, !!(pThis->u8State & AUX_STATE_ENABLED));
+
+    return rc;
+}
+
+void PS2MReset(PPS2M pThis)
+{
+    LogFlowFunc(("Resetting PS2M\n"));
+
+    pThis->u8CurrCmd         = 0;
+
+    /* Clear the queues. */
+    ps2kClearQueue((GeneriQ *)&pThis->cmdQ);
+    ps2mSetDefaults(pThis);     /* Also clears event queue. */
+
+    /* Activate the PS/2 mouse by default. */
+//    if (pThis->Mouse.pDrv)
+//        pThis->Mouse.pDrv->pfnSetActive(pThis->Mouse.pDrv, true);
+}
+
+void PS2MRelocate(PPS2M pThis, RTGCINTPTR offDelta, PPDMDEVINS pDevIns)
+{
+    LogFlowFunc(("Relocating PS2M\n"));
+    pThis->pDelayTimerRC    = TMTimerRCPtr(pThis->pDelayTimerR3);
+    pThis->pThrottleTimerRC = TMTimerRCPtr(pThis->pThrottleTimerR3);
+    NOREF(offDelta);
+}
+
+int PS2MConstruct(PPS2M pThis, PPDMDEVINS pDevIns, void *pParent, int iInstance)
+{
+    int     rc;
+
+    LogFlowFunc(("iInstance=%d\n", iInstance));
+
+    pThis->pParent = pParent;
+
+    /* Initialize the queues. */
+    pThis->evtQ.cSize = AUX_EVT_QUEUE_SIZE;
+    pThis->cmdQ.cSize = AUX_CMD_QUEUE_SIZE;
+
+    pThis->Mouse.IBase.pfnQueryInterface     = ps2mQueryInterface;
+    pThis->Mouse.IPort.pfnPutEvent           = ps2mPutEvent;
+    pThis->Mouse.IPort.pfnPutEventAbs        = ps2mPutEventAbs;
+    pThis->Mouse.IPort.pfnPutEventMultiTouch = ps2mPutEventMT;
+
+    /*
+     * Initialize the critical section pointer(s).
+     */
+    pThis->pCritSectR3 = pDevIns->pCritSectRoR3;
+
+    /*
+     * Create the input rate throttling timer. Does not use virtual time!
+     */
+    PTMTIMER pTimer;
+    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_REAL, ps2mThrottleTimer, pThis,
+                                TMTIMER_FLAGS_NO_CRIT_SECT, "PS2M Throttle Timer", &pTimer);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pThis->pThrottleTimerR3 = pTimer;
+    pThis->pThrottleTimerR0 = TMTimerR0Ptr(pTimer);
+    pThis->pThrottleTimerRC = TMTimerRCPtr(pTimer);
+
+    /*
+     * Create the command delay timer.
+     */
+    rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, ps2mDelayTimer, pThis,
+                                TMTIMER_FLAGS_NO_CRIT_SECT, "PS2M Delay Timer", &pTimer);
+    if (RT_FAILURE(rc))
+        return rc;
+
+    pThis->pDelayTimerR3 = pTimer;
+    pThis->pDelayTimerR0 = TMTimerR0Ptr(pTimer);
+    pThis->pDelayTimerRC = TMTimerRCPtr(pTimer);
+
+    /*
+     * Register debugger info callbacks.
+     */
+    PDMDevHlpDBGFInfoRegister(pDevIns, "ps2m", "Display PS/2 mouse state.", ps2mInfoState);
+
+    //@todo: Where should we do this?
+    ps2mSetDriverState(pThis, true);
+    pThis->u8State = 0;
+    pThis->enmMode = AUX_MODE_STD;
+
+    return rc;
+}
+
+#endif
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
Index: /trunk/src/VBox/Devices/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/Makefile.kmk	(revision 48213)
+++ /trunk/src/VBox/Devices/Makefile.kmk	(revision 48214)
@@ -129,4 +129,5 @@
  	Input/DevPS2.cpp \
  	Input/PS2K.cpp \
+ 	Input/PS2M.cpp \
  	Input/UsbKbd.cpp \
  	Input/UsbMouse.cpp \
@@ -453,4 +454,11 @@
 
 
+ # --- Input bits. ---
+
+ ifdef VBOX_WITH_NEW_PS2M
+  VBoxDD_DEFS           += VBOX_WITH_NEW_PS2M
+ endif
+
+
  # --- Audio bits. ---
 
@@ -757,4 +765,5 @@
   	Input/DevPS2.cpp \
  	Input/PS2K.cpp \
+ 	Input/PS2M.cpp \
   	PC/DevACPI.cpp \
   	PC/DevPit-i8254.cpp \
@@ -836,4 +845,8 @@
   VBoxDDGC_SOURCES      += \
   	Storage/DevLsiLogicSCSI.cpp
+  endif
+
+  ifdef VBOX_WITH_NEW_PS2M
+  VBoxDDGC_DEFS         += VBOX_WITH_NEW_PS2M
   endif
 
@@ -894,4 +907,5 @@
  	Input/DevPS2.cpp \
  	Input/PS2K.cpp \
+ 	Input/PS2M.cpp \
  	PC/DevACPI.cpp \
  	PC/DevPit-i8254.cpp \
@@ -984,4 +998,8 @@
  VBoxDDR0_SOURCES       += \
  	Storage/DevLsiLogicSCSI.cpp
+ endif
+
+ ifdef VBOX_WITH_NEW_PS2M
+  VBoxDDR0_DEFS         += VBOX_WITH_NEW_PS2M
  endif
 
Index: /trunk/src/VBox/Devices/testcase/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Devices/testcase/Makefile.kmk	(revision 48213)
+++ /trunk/src/VBox/Devices/testcase/Makefile.kmk	(revision 48214)
@@ -36,4 +36,5 @@
 	$(if $(VBOX_WITH_BUSLOGIC),VBOX_WITH_BUSLOGIC,) \
 	$(if $(VBOX_WITH_LSILOGIC),VBOX_WITH_LSILOGIC,) \
+	$(if $(VBOX_WITH_NEW_PS2M),VBOX_WITH_NEW_PS2M,) \
 	$(if $(VBOX_WITH_HGSMI),VBOX_WITH_HGSMI,) \
 	$(if $(VBOX_WITH_CRHGSMI),VBOX_WITH_CRHGSMI,) \
Index: /trunk/src/VBox/Devices/testcase/tstDeviceStructSize.cpp
===================================================================
--- /trunk/src/VBox/Devices/testcase/tstDeviceStructSize.cpp	(revision 48213)
+++ /trunk/src/VBox/Devices/testcase/tstDeviceStructSize.cpp	(revision 48214)
@@ -37,4 +37,6 @@
 #undef LOG_GROUP
 #include "../Input/PS2K.cpp"
+#undef LOG_GROUP
+#include "../Input/PS2M.cpp"
 #ifdef VBOX_WITH_E1000
 # undef LOG_GROUP
Index: /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp
===================================================================
--- /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 48213)
+++ /trunk/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp	(revision 48214)
@@ -46,4 +46,6 @@
 #undef LOG_GROUP
 #include "../Input/PS2K.cpp"
+#undef LOG_GROUP
+#include "../Input/PS2M.cpp"
 #undef LOG_GROUP
 #include "../Network/DevPCNet.cpp"
@@ -362,4 +364,5 @@
 
     /* Input/pckbd.c */
+#ifndef VBOX_WITH_NEW_PS2M
     GEN_CHECK_SIZE(MouseCmdQueue);
     GEN_CHECK_OFF(MouseCmdQueue, data);
@@ -372,10 +375,12 @@
     GEN_CHECK_OFF(MouseEventQueue, wptr);
     GEN_CHECK_OFF(MouseEventQueue, count);
+#endif
     GEN_CHECK_SIZE(KBDState);
-    GEN_CHECK_OFF(KBDState, mouse_command_queue);
-    GEN_CHECK_OFF(KBDState, mouse_event_queue);
     GEN_CHECK_OFF(KBDState, write_cmd);
     GEN_CHECK_OFF(KBDState, status);
     GEN_CHECK_OFF(KBDState, mode);
+#ifndef VBOX_WITH_NEW_PS2M
+    GEN_CHECK_OFF(KBDState, mouse_command_queue);
+    GEN_CHECK_OFF(KBDState, mouse_event_queue);
     GEN_CHECK_OFF(KBDState, mouse_write_cmd);
     GEN_CHECK_OFF(KBDState, mouse_status);
@@ -390,4 +395,5 @@
     GEN_CHECK_OFF(KBDState, mouse_dw);
     GEN_CHECK_OFF(KBDState, mouse_buttons);
+#endif
     GEN_CHECK_OFF(KBDState, pDevInsR3);
     GEN_CHECK_OFF(KBDState, pDevInsR0);
@@ -400,4 +406,6 @@
     GEN_CHECK_OFF(KbdCmdQ, abQueue);
     GEN_CHECK_SIZE(KbdCmdQ);
+    /* Input/PS2K.c */
+    GEN_CHECK_SIZE(PS2K);
     GEN_CHECK_OFF(PS2K, fScanning);
     GEN_CHECK_OFF(PS2K, fNumLockOn);
@@ -419,8 +427,35 @@
     GEN_CHECK_OFF(PS2K, Keyboard.pDrvBase);
     GEN_CHECK_OFF(PS2K, Keyboard.pDrv);
+#ifdef VBOX_WITH_NEW_PS2M
+    /* Input/PS2M.c */
+    GEN_CHECK_SIZE(PS2M);
+    GEN_CHECK_OFF(PS2M, u8State);
+    GEN_CHECK_OFF(PS2M, u8SampleRate);
+    GEN_CHECK_OFF(PS2M, u8CurrCmd);
+    GEN_CHECK_OFF(PS2M, fThrottleActive);
+    GEN_CHECK_OFF(PS2M, enmMode);
+    GEN_CHECK_OFF(PS2M, enmKnockState);
+    GEN_CHECK_OFF(PS2M, iAccumX);
+    GEN_CHECK_OFF(PS2M, fAccumB);
+    GEN_CHECK_OFF(PS2M, uThrottleDelay);
+    GEN_CHECK_OFF(PS2M, evtQ);
+    GEN_CHECK_OFF(PS2M, cmdQ);
+    GEN_CHECK_OFF(PS2M, pDelayTimerRC);
+    GEN_CHECK_OFF(PS2M, pDelayTimerR3);
+    GEN_CHECK_OFF(PS2M, pDelayTimerR0);
+    GEN_CHECK_OFF(PS2M, pThrottleTimerRC);
+    GEN_CHECK_OFF(PS2M, pThrottleTimerR3);
+    GEN_CHECK_OFF(PS2M, pThrottleTimerR0);
+    GEN_CHECK_OFF(PS2M, pCritSectR3);
+    GEN_CHECK_OFF(PS2M, Mouse.IBase);
+    GEN_CHECK_OFF(PS2M, Mouse.IPort);
+    GEN_CHECK_OFF(PS2M, Mouse.pDrvBase);
+    GEN_CHECK_OFF(PS2M, Mouse.pDrv);
+#else
     GEN_CHECK_OFF(KBDState, Mouse.IBase);
     GEN_CHECK_OFF(KBDState, Mouse.IPort);
     GEN_CHECK_OFF(KBDState, Mouse.pDrvBase);
     GEN_CHECK_OFF(KBDState, Mouse.pDrv);
+#endif
 
     /* Network/DevPCNet.cpp */
