Index: /trunk/src/VBox/Devices/Storage/DevBusLogic.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/DevBusLogic.cpp	(revision 43641)
+++ /trunk/src/VBox/Devices/Storage/DevBusLogic.cpp	(revision 43642)
@@ -16,12 +16,10 @@
  */
 
-/* Implemented looking at the driver source in the linux kernel (drivers/scsi/BusLogic.[ch]).
- * See also: http://www.drdobbs.com/184410111
- */
+/* Based on the Multi-Master Ultra SCSI Systems Technical Reference Manual */
 
 /*******************************************************************************
 *   Header Files                                                               *
 *******************************************************************************/
-//#define DEBUG
+
 #define LOG_GROUP LOG_GROUP_DEV_BUSLOGIC
 #include <VBox/vmm/pdmdev.h>
@@ -69,6 +67,6 @@
 #define BUSLOGIC_SAVED_STATE_MINOR_PRE_ERROR_HANDLING 1
 
-/** The duration of software-initiated reset. Not documented, set to 500 us. */
-#define BUSLOGIC_RESET_DURATION_NS      (500*1000)
+/** The duration of software-initiated reset. Not documented, set to 2 ms. */
+#define BUSLOGIC_RESET_DURATION_NS      (2000*1000)
 
 /**
@@ -270,4 +268,13 @@
 AssertCompileSize(HostAdapterLocalRam, 256);
 
+/* Compatible ISA base I/O port addresses. Disabled if zero. */
+#define NUM_ISA_BASES       8
+#define MAX_ISA_BASE        (NUM_ISA_BASES - 1)
+#define ISA_BASE_DISABLED   6
+
+static uint16_t     aISABases[NUM_ISA_BASES] = {
+    0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0, 0
+};
+
 /** Pointer to a task state structure. */
 typedef struct BUSLOGICTASKSTATE *PBUSLOGICTASKSTATE;
@@ -338,16 +345,15 @@
     bool                            fIRQEnabled;
     /** Flag whether the ISA I/O port range is disabled
-     * to prevent the BIOs to access the device. */
-    bool                            fISAEnabled;
+     * to prevent the BIOS to access the device. */ 
+    bool                            fISAEnabled;    //@todo: unused, to be removed
     /** Flag whether 24-bit mailboxes are in use (default is 32-bit). */
     bool                            fMbxIs24Bit;    //@todo: save?
     /** ISA I/O port base (encoded in FW-compatible format). */
-    uint8_t                         uISABaseCode;   //@todo: save?
-
+    uint8_t                         uISABaseCode;
+
+    /** ISA I/O port base (disabled if zero). */
+    RTIOPORT                        IOISABase;     //@todo: recalculate when restoring state
     /** Default ISA I/O port base in FW-compatible format. */
     uint8_t                         uDefaultISABaseCode;
-
-    /** ISA I/O port base (disabled if zero). */
-    uint16_t                        uISABase;       //@todo: recalculate when restoring state
 
     /** Number of mailboxes the guest set up. */
@@ -795,4 +801,6 @@
 #define PDMILEDPORTS_2_PBUSLOGIC(pInterface)       ( (PBUSLOGIC)((uintptr_t)(pInterface) - RT_OFFSETOF(BUSLOGIC, ILeds)) )
 
+static int buslogicRegisterISARange(PBUSLOGIC pBusLogic, uint8_t uBaseCode);
+
 /**
  * Assert IRQ line of the BusLogic adapter.
@@ -899,6 +907,7 @@
  * @returns VBox status code.
  * @param   pBusLogic Pointer to the BusLogic device instance.
- */
-static int buslogicHwReset(PBUSLOGIC pBusLogic)
+ * @param   fResetIO  Flag determining whether ISA I/O should be reset.
+ */
+static int buslogicHwReset(PBUSLOGIC pBusLogic, bool fResetIO)
 {
     LogFlowFunc(("pBusLogic=%#p\n", pBusLogic));
@@ -913,8 +922,12 @@
     pBusLogic->cbCommandParametersLeft = 0;
     pBusLogic->fIRQEnabled = true;
-    pBusLogic->fISAEnabled = true;
     pBusLogic->uMailboxOutgoingPositionCurrent = 0;
     pBusLogic->uMailboxIncomingPositionCurrent = 0;
 
+    /* Guest-initiated HBA reset does not affect ISA port I/O. */
+    if (fResetIO)
+    {
+        buslogicRegisterISARange(pBusLogic, pBusLogic->uDefaultISABaseCode);
+    }
     buslogicInitializeLocalRam(pBusLogic);
     vboxscsiInitialize(&pBusLogic->VBoxSCSI);
@@ -965,5 +978,5 @@
     pBusLogic->u64ResetTime = PDMDevHlpTMTimeVirtGetNano(pBusLogic->CTX_SUFF(pDevIns));
 
-    buslogicHwReset(pBusLogic);
+    buslogicHwReset(pBusLogic, false);
 
     /* We set the diagnostic active in the status register. */
@@ -1361,5 +1374,5 @@
             /* It seems VMware does not provide valid information here too, lets do the same :) */
             pReply->InformationIsValid = 0;
-            pReply->IsaIOPort = 0xff; /* Make it invalid. */
+            pReply->IsaIOPort = pBusLogic->uISABaseCode;
             pReply->IRQ = PCIDevGetInterruptLine(&pBusLogic->dev);
             pBusLogic->cbReplyParametersLeft = sizeof(ReplyInquirePCIHostAdapterInformation);
@@ -1368,12 +1381,17 @@
         case BUSLOGICCOMMAND_MODIFY_IO_ADDRESS:
         {
+            /* Modify the ISA-compatible I/O port base. Note that this technically
+             * violates the PCI spec, as this address is not reported through PCI.
+             * However, it is required for compatibility with old drivers.
+             */
+#ifdef IN_RING3
+            Log(("ISA I/O for PCI (code %x)\n", pBusLogic->aCommandBuffer[0]));
+            buslogicRegisterISARange(pBusLogic, pBusLogic->aCommandBuffer[0]);
             pBusLogic->cbReplyParametersLeft = 0;
-            if (pBusLogic->aCommandBuffer[0] == 0x06)
-            {
-                Log(("Disabling ISA I/O ports.\n"));
-                pBusLogic->fISAEnabled = false;
-            }
             fSuppressIrq = true;
             break;
+#else
+            AssertMsgFailed(("Must never get here!\n"));
+#endif
         }
         case BUSLOGICCOMMAND_INQUIRE_BOARD_ID:
@@ -1410,5 +1428,5 @@
                 /* First pass - set the number of following parameter bytes. */
                 pBusLogic->cbCommandParametersLeft = pBusLogic->aCommandBuffer[0];
-                Log(("Set HA options: %u bytes follow\n", pBusLogic->aCommandBuffer[0]));
+                Log(("Set HA options: %u bytes follow\n", pBusLogic->cbCommandParametersLeft));
             }
             else
@@ -1447,5 +1465,8 @@
             //@todo: What should the DMA channel be?
             pReply->fDmaChannel6  = 1;
-            /* The IRQ is not necessarily representable in this structure. */
+            /* The PCI IRQ is not necessarily representable in this structure.
+             * If that is the case, the guest likely won't function correctly,
+             * therefore we log a warning.
+             */
             switch (uPciIrq) {
             case 9:     pReply->fIrqChannel9  = 1; break;
@@ -1456,5 +1477,5 @@
             case 15:    pReply->fIrqChannel15 = 1; break;
             default:
-                Log(("Inquire configuration: PCI IRQ %d cannot be represented\n", uPciIrq));
+                LogRel(("Warning: PCI IRQ %d cannot be represented as ISA!\n", uPciIrq));
                 break;
             }
@@ -1463,4 +1484,9 @@
         case BUSLOGICCOMMAND_INQUIRE_EXTENDED_SETUP_INFORMATION:
         {
+            /* Some Adaptec AHA-154x drivers (e.g. OS/2) execute this command and expect
+             * it to fail. If it succeeds, the drivers refuse to load. However, some newer 
+             * Adaptec 154x models supposedly support it too?? 
+             */
+
             /* The reply length is set by the guest and is found in the first byte of the command buffer. */
             pBusLogic->cbReplyParametersLeft = pBusLogic->aCommandBuffer[0];
@@ -1486,4 +1512,11 @@
             PReplyInquireSetupInformation pReply = (PReplyInquireSetupInformation)pBusLogic->aReplyBuffer;
             memset(pReply, 0, sizeof(ReplyInquireSetupInformation));
+            pReply->cMailbox = pBusLogic->cMailbox;
+            pReply->uSignature = 'B';
+            /* The 'D' signature prevents Adaptec's OS/2 drivers from getting too
+             * friendly with BusLogic hardware and upsetting the HBA state.
+             */
+            pReply->uCharacterD = 'D';      /* BusLogic model. */
+            pReply->uHostBusType = 'F';     /* PCI bus. */
             break;
         }
@@ -1556,4 +1589,14 @@
             pBusLogic->cbReplyParametersLeft = 8;
             break;
+        case BUSLOGICCOMMAND_INQUIRE_INSTALLED_DEVICES_ID_8_TO_15:
+            /* See note about cheating above. */
+            memset(pBusLogic->aReplyBuffer, 0, 8);
+            for (int i = 0; i < 8; ++i)
+            {
+                if (pBusLogic->aDeviceStates[i + 8].fPresent)
+                    pBusLogic->aReplyBuffer[i] = 1;
+            }
+            pBusLogic->cbReplyParametersLeft = 8;
+            break;
         case BUSLOGICCOMMAND_INQUIRE_TARGET_DEVICES:
         {
@@ -1608,4 +1651,11 @@
             pBusLogic->LocalRam.structured.autoSCSIData.uBusOffDelay = pBusLogic->aCommandBuffer[0];
             Log(("Bus-off time: %d\n", pBusLogic->aCommandBuffer[0]));
+            break;
+        }
+        case BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE:
+        {
+            pBusLogic->cbReplyParametersLeft = 0;
+            pBusLogic->LocalRam.structured.autoSCSIData.uDMATransferRate = pBusLogic->aCommandBuffer[0];
+            Log(("Bus transfer rate: %02X\n", pBusLogic->aCommandBuffer[0]));
             break;
         }
@@ -1817,4 +1867,5 @@
                     case BUSLOGICCOMMAND_SET_PREEMPT_TIME_ON_BUS:
                     case BUSLOGICCOMMAND_SET_TIME_OFF_BUS:
+                    case BUSLOGICCOMMAND_SET_BUS_TRANSFER_RATE:
                         pBusLogic->cbCommandParametersLeft = 1;
                         break;
@@ -1841,4 +1892,12 @@
             else
             {
+#ifndef IN_RING3
+                /* This command must be executed in R3 as it rehooks the ISA I/O port. */
+                if (pBusLogic->uOperationCode == BUSLOGICCOMMAND_MODIFY_IO_ADDRESS) 
+                {
+                    rc = VINF_IOM_R3_IOPORT_WRITE;
+                    break;
+                }
+#endif
                 /*
                  * The real adapter would set the Command register busy bit in the status register.
@@ -1932,5 +1991,5 @@
                                        RTIOPORT Port, uint32_t *pu32, unsigned cb)
 {
-    PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);;
+    PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
     unsigned iRegister = Port % 4;
 
@@ -1970,6 +2029,59 @@
 
 #ifdef IN_RING3
-/**
- * Port I/O Handler for IN operations - legacy port.
+
+static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
+{
+    int rc;
+    PBUSLOGICTASKSTATE pTaskState;
+    uint32_t           uTargetDevice;
+
+    rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState);
+    AssertMsgRCReturn(rc, ("Getting task from cache failed rc=%Rrc\n", rc), rc);
+
+    pTaskState->fBIOS = true;
+
+    rc = vboxscsiSetupRequest(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, &uTargetDevice);
+    AssertMsgRCReturn(rc, ("Setting up SCSI request failed rc=%Rrc\n", rc), rc);
+
+    pTaskState->PDMScsiRequest.pvUser = pTaskState;
+
+    pTaskState->CTX_SUFF(pTargetDevice) = &pBusLogic->aDeviceStates[uTargetDevice];
+
+    if (!pTaskState->CTX_SUFF(pTargetDevice)->fPresent)
+    {
+        /* Device is not present. */
+        AssertMsg(pTaskState->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY,
+                    ("Device is not present but command is not inquiry\n"));
+
+        SCSIINQUIRYDATA ScsiInquiryData;
+
+        memset(&ScsiInquiryData, 0, sizeof(SCSIINQUIRYDATA));
+        ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
+        ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
+
+        memcpy(pBusLogic->VBoxSCSI.pBuf, &ScsiInquiryData, 5);
+
+        rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest);
+        AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc);
+
+        RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
+    }
+    else
+    {
+        LogFlowFunc(("before increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
+        ASMAtomicIncU32(&pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests);
+        LogFlowFunc(("after increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
+
+        rc = pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector->pfnSCSIRequestSend(pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector,
+                                                                                        &pTaskState->PDMScsiRequest);
+        AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
+    }
+
+    return rc;
+}
+
+
+/**
+ * Port I/O Handler for IN operations - BIOS port.
  *
  * @returns VBox status code.
@@ -1981,5 +2093,5 @@
  * @param   cb          Number of bytes read.
  */
-static int  buslogicBIOSIOPortRead (PPDMDEVINS pDevIns, void *pvUser,
+static int  buslogicBIOSIOPortRead(PPDMDEVINS pDevIns, void *pvUser,
                                    RTIOPORT Port, uint32_t *pu32, unsigned cb)
 {
@@ -1989,7 +2101,4 @@
     Assert(cb == 1);
 
-    if (!pBusLogic->fISAEnabled)
-        return VINF_SUCCESS;
-
     rc = vboxscsiReadRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), pu32);
 
@@ -1997,4 +2106,137 @@
     //      __FUNCTION__, pu32, 1, pu32, (Port - BUSLOGIC_BIOS_IO_PORT), rc));
 
+    return rc;
+}
+
+/**
+ * Port I/O Handler for OUT operations - BIOS port.
+ *
+ * @returns VBox status code.
+ *
+ * @param   pDevIns     The device instance.
+ * @param   pvUser      User argument.
+ * @param   uPort       Port number used for the IN operation.
+ * @param   u32         The value to output.
+ * @param   cb          The value size in bytes.
+ */
+static int buslogicBIOSIOPortWrite(PPDMDEVINS pDevIns, void *pvUser,
+                                   RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+    int rc;
+    PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
+
+    Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n",
+          pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
+
+    Assert(cb == 1);
+
+    rc = vboxscsiWriteRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), (uint8_t)u32);
+    if (rc == VERR_MORE_DATA)
+    {
+        rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
+        AssertRC(rc);
+    }
+    else if (RT_FAILURE(rc))
+        AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
+
+    return VINF_SUCCESS;
+}
+
+/**
+ * Port I/O Handler for primary port range OUT string operations.
+ * @see FNIOMIOPORTOUTSTRING for details.
+ */
+static DECLCALLBACK(int) buslogicBIOSIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
+{
+    PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
+    int rc;
+
+    Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
+          pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
+
+    rc = vboxscsiWriteString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT),
+                             pGCPtrSrc, pcTransfer, cb);
+    if (rc == VERR_MORE_DATA)
+    {
+        rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
+        AssertRC(rc);
+    }
+    else if (RT_FAILURE(rc))
+        AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
+
+    return rc;
+}
+
+/**
+ * Port I/O Handler for primary port range IN string operations.
+ * @see FNIOMIOPORTINSTRING for details.
+ */
+static DECLCALLBACK(int) buslogicBIOSIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
+{
+    PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
+
+    LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
+                 pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
+
+    return vboxscsiReadString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT),
+                              pGCPtrDst, pcTransfer, cb);
+}
+
+/**
+ * Update the ISA I/O range.
+ *
+ * @returns nothing.
+ * @param   pBusLogic       Pointer to the BusLogic device instance.
+ * @param   uBaseCode       Encoded ISA I/O base; only low 3 bits are used.
+ */
+static int buslogicRegisterISARange(PBUSLOGIC pBusLogic, uint8_t uBaseCode)
+{
+    uint8_t     uCode = uBaseCode & MAX_ISA_BASE;
+    uint16_t    uNewBase = aISABases[uCode];
+    int         rc = VINF_SUCCESS;
+
+    LogFlowFunc(("ISA I/O code %02X, new base %X\n", uBaseCode, uNewBase));
+
+    /* Check if the same port range is already registered. */
+    if (uNewBase != pBusLogic->IOISABase)
+    {
+        /* Unregister the old range, if any. */
+        if (pBusLogic->IOISABase) 
+            rc = PDMDevHlpIOPortDeregister(pBusLogic->CTX_SUFF(pDevIns), pBusLogic->IOISABase, 4);
+
+        if (RT_SUCCESS(rc)) 
+        {
+            pBusLogic->IOISABase = 0;   /* First mark as unregistered. */
+            pBusLogic->uISABaseCode = ISA_BASE_DISABLED;
+
+            if (uNewBase)
+            {
+                /* Register the new range if requested. */
+                rc = PDMDevHlpIOPortRegister(pBusLogic->CTX_SUFF(pDevIns), uNewBase, 4, NULL,
+                                             buslogicIOPortWrite, buslogicIOPortRead,
+                                             NULL, NULL,
+                                             "BusLogic ISA");
+                if (RT_SUCCESS(rc))
+                {
+                    pBusLogic->IOISABase = uNewBase;
+                    pBusLogic->uISABaseCode = uCode;
+                }
+            }
+        }
+        if (RT_SUCCESS(rc))
+        {
+            if (uNewBase)
+            {
+                Log(("ISA I/O base: %x\n", uNewBase));
+                LogRel(("buslogic: ISA I/O base: %x\n", uNewBase));
+            }
+            else
+            {
+                Log(("Disabling ISA I/O ports.\n"));
+                LogRel(("buslogic: ISA I/O disabled\n"));
+            }
+        }
+
+    }
     return rc;
 }
@@ -2052,132 +2294,4 @@
 }
 
-
-static int buslogicPrepareBIOSSCSIRequest(PBUSLOGIC pBusLogic)
-{
-    int rc;
-    PBUSLOGICTASKSTATE pTaskState;
-    uint32_t           uTargetDevice;
-
-    rc = RTMemCacheAllocEx(pBusLogic->hTaskCache, (void **)&pTaskState);
-    AssertMsgRCReturn(rc, ("Getting task from cache failed rc=%Rrc\n", rc), rc);
-
-    pTaskState->fBIOS = true;
-
-    rc = vboxscsiSetupRequest(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest, &uTargetDevice);
-    AssertMsgRCReturn(rc, ("Setting up SCSI request failed rc=%Rrc\n", rc), rc);
-
-    pTaskState->PDMScsiRequest.pvUser = pTaskState;
-
-    pTaskState->CTX_SUFF(pTargetDevice) = &pBusLogic->aDeviceStates[uTargetDevice];
-
-    if (!pTaskState->CTX_SUFF(pTargetDevice)->fPresent)
-    {
-        /* Device is not present. */
-        AssertMsg(pTaskState->PDMScsiRequest.pbCDB[0] == SCSI_INQUIRY,
-                    ("Device is not present but command is not inquiry\n"));
-
-        SCSIINQUIRYDATA ScsiInquiryData;
-
-        memset(&ScsiInquiryData, 0, sizeof(SCSIINQUIRYDATA));
-        ScsiInquiryData.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
-        ScsiInquiryData.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
-
-        memcpy(pBusLogic->VBoxSCSI.pBuf, &ScsiInquiryData, 5);
-
-        rc = vboxscsiRequestFinished(&pBusLogic->VBoxSCSI, &pTaskState->PDMScsiRequest);
-        AssertMsgRCReturn(rc, ("Finishing BIOS SCSI request failed rc=%Rrc\n", rc), rc);
-
-        RTMemCacheFree(pBusLogic->hTaskCache, pTaskState);
-    }
-    else
-    {
-        LogFlowFunc(("before increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
-        ASMAtomicIncU32(&pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests);
-        LogFlowFunc(("after increment %u\n", pTaskState->CTX_SUFF(pTargetDevice)->cOutstandingRequests));
-
-        rc = pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector->pfnSCSIRequestSend(pTaskState->CTX_SUFF(pTargetDevice)->pDrvSCSIConnector,
-                                                                                        &pTaskState->PDMScsiRequest);
-        AssertMsgRC(rc, ("Sending request to SCSI layer failed rc=%Rrc\n", rc));
-    }
-
-    return rc;
-}
-
-/**
- * Port I/O Handler for OUT operations - legacy port.
- *
- * @returns VBox status code.
- *
- * @param   pDevIns     The device instance.
- * @param   pvUser      User argument.
- * @param   uPort       Port number used for the IN operation.
- * @param   u32         The value to output.
- * @param   cb          The value size in bytes.
- */
-static int buslogicBIOSIOPortWrite (PPDMDEVINS pDevIns, void *pvUser,
-                                   RTIOPORT Port, uint32_t u32, unsigned cb)
-{
-    int rc;
-    PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
-
-    Log2(("#%d %s: pvUser=%#p cb=%d u32=%#x Port=%#x\n",
-          pDevIns->iInstance, __FUNCTION__, pvUser, cb, u32, Port));
-
-    Assert(cb == 1);
-
-    if (!pBusLogic->fISAEnabled)
-        return VINF_SUCCESS;
-
-    rc = vboxscsiWriteRegister(&pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT), (uint8_t)u32);
-    if (rc == VERR_MORE_DATA)
-    {
-        rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
-        AssertRC(rc);
-    }
-    else if (RT_FAILURE(rc))
-        AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
-
-    return VINF_SUCCESS;
-}
-
-/**
- * Port I/O Handler for primary port range OUT string operations.
- * @see FNIOMIOPORTOUTSTRING for details.
- */
-static DECLCALLBACK(int) buslogicBIOSIOPortWriteStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
-{
-    PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
-    int rc;
-
-    Log2(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
-          pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
-
-    rc = vboxscsiWriteString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT),
-                             pGCPtrSrc, pcTransfer, cb);
-    if (rc == VERR_MORE_DATA)
-    {
-        rc = buslogicPrepareBIOSSCSIRequest(pBusLogic);
-        AssertRC(rc);
-    }
-    else if (RT_FAILURE(rc))
-        AssertMsgFailed(("Writing BIOS register failed %Rrc\n", rc));
-
-    return rc;
-}
-
-/**
- * Port I/O Handler for primary port range IN string operations.
- * @see FNIOMIOPORTINSTRING for details.
- */
-static DECLCALLBACK(int) buslogicBIOSIOPortReadStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
-{
-    PBUSLOGIC pBusLogic = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
-
-    LogFlowFunc(("#%d %s: pvUser=%#p cb=%d Port=%#x\n",
-                 pDevIns->iInstance, __FUNCTION__, pvUser, cb, Port));
-
-    return vboxscsiReadString(pDevIns, &pBusLogic->VBoxSCSI, (Port - BUSLOGIC_BIOS_IO_PORT),
-                              pGCPtrDst, pcTransfer, cb);
-}
 
 static DECLCALLBACK(int) buslogicMMIOMap(PPCIDEVICE pPciDev, /*unsigned*/ int iRegion,
@@ -2611,5 +2725,5 @@
     SSMR3PutU8    (pSSM, pBusLogic->cbReplyParametersLeft);
     SSMR3PutBool  (pSSM, pBusLogic->fIRQEnabled);
-    SSMR3PutBool  (pSSM, pBusLogic->fISAEnabled);
+    SSMR3PutU8    (pSSM, pBusLogic->uISABaseCode);
     SSMR3PutU32   (pSSM, pBusLogic->cMailbox);
     SSMR3PutGCPhys(pSSM, pBusLogic->GCPhysAddrMailboxOutgoingBase);
@@ -2669,4 +2783,5 @@
     PBUSLOGIC pThis = PDMINS_2_DATA(pDevIns, PBUSLOGIC);
 
+    buslogicRegisterISARange(pThis, pThis->uISABaseCode);
     buslogicKick(pThis);
     return VINF_SUCCESS;
@@ -2716,5 +2831,5 @@
     SSMR3GetU8    (pSSM, &pBusLogic->cbReplyParametersLeft);
     SSMR3GetBool  (pSSM, &pBusLogic->fIRQEnabled);
-    SSMR3GetBool  (pSSM, &pBusLogic->fISAEnabled);
+    SSMR3GetU8    (pSSM, &pBusLogic->uISABaseCode);
     SSMR3GetU32   (pSSM, &pBusLogic->cMailbox);
     SSMR3GetGCPhys(pSSM, &pBusLogic->GCPhysAddrMailboxOutgoingBase);
@@ -3065,5 +3180,5 @@
     ASMAtomicWriteBool(&pThis->fSignalIdle, false);
 
-    buslogicHwReset(pThis);
+    buslogicHwReset(pThis, true);
     return true;
 }
@@ -3082,5 +3197,5 @@
     {
         ASMAtomicWriteBool(&pThis->fSignalIdle, false);
-        buslogicHwReset(pThis);
+        buslogicHwReset(pThis, true);
     }
 }
@@ -3166,4 +3281,5 @@
     int        rc = VINF_SUCCESS;
     bool       fBootable = true;
+    char       achISACompat[16];
     PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
 
@@ -3174,5 +3290,6 @@
                               "GCEnabled\0"
                               "R0Enabled\0"
-                              "Bootable\0"))
+                              "Bootable\0"
+                              "ISACompat\0"))
         return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
                                 N_("BusLogic configuration error: unknown option specified"));
@@ -3194,4 +3311,20 @@
                                 N_("BusLogic configuration error: failed to read Bootable as boolean"));
     Log(("%s: fBootable=%RTbool\n", __FUNCTION__, fBootable));
+    rc = CFGMR3QueryStringDef(pCfg, "ISACompat", achISACompat, sizeof(achISACompat), "Alternate");
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc,
+                                N_("BusLogic configuration error: failed to read ISACompat as string"));
+    Log(("%s: ISACompat=%s\n", __FUNCTION__, achISACompat));
+
+    /* Grok the ISACompat setting. */
+    if (!strcmp(achISACompat, "Disabled"))
+        pThis->uDefaultISABaseCode = ISA_BASE_DISABLED;
+    else if (!strcmp(achISACompat, "Primary"))
+        pThis->uDefaultISABaseCode = 0;     /* I/O base at 330h. */
+    else if (!strcmp(achISACompat, "Alternate"))
+        pThis->uDefaultISABaseCode = 1;     /* I/O base at 334h. */
+    else
+        return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
+                                N_("BusLogic configuration error: invalid ISACompat setting"));
 
     pThis->pDevInsR3 = pDevIns;
@@ -3216,5 +3349,5 @@
 
     /*
-     * Register the PCI device, it's I/O regions.
+     * Register the PCI device and its I/O regions.
      */
     rc = PDMDevHlpPCIRegister (pDevIns, &pThis->dev);
@@ -3232,5 +3365,5 @@
     if (fBootable)
     {
-        /* Register I/O port space in ISA region for BIOS access. */
+        /* Register I/O port space for BIOS access. */
         rc = PDMDevHlpIOPortRegister(pDevIns, BUSLOGIC_BIOS_IO_PORT, 3, NULL,
                                      buslogicBIOSIOPortWrite, buslogicBIOSIOPortRead,
@@ -3238,6 +3371,11 @@
                                      "BusLogic BIOS");
         if (RT_FAILURE(rc))
-            return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register legacy I/O handlers"));
-    }
+            return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register BIOS I/O handlers"));
+    }
+
+    /* Set up the compatibility I/O range. */
+    rc = buslogicRegisterISARange(pThis, pThis->uDefaultISABaseCode);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register ISA I/O handlers"));
 
     /* Initialize task cache. */
@@ -3324,5 +3462,5 @@
         return PDMDEV_SET_ERROR(pDevIns, rc, N_("BusLogic cannot register save state handlers"));
 
-    rc = buslogicHwReset(pThis);
+    rc = buslogicHwReset(pThis, true);
     AssertMsgRC(rc, ("hardware reset of BusLogic host adapter failed rc=%Rrc\n", rc));
 
@@ -3389,3 +3527,2 @@
 #endif /* IN_RING3 */
 #endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
-
