/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1992 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 source code is provided to you solely for       */
/*    the purpose of assisting you in your development of OS/2 device        */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Developer Connection Device Driver       */
/*    Source Kit for OS/2. This Copyright statement may not be removed.      */
/*                                                                           */
/*****************************************************************************/
/* SCCSID = src/dev/dasd/cdrom/ibm1s506/s506sm.c, idskflt, c.basedd 99/03/11 */
/**************************************************************************
 *
 * SOURCE FILE NAME = S506SM.C
 *
 * DESCRIPTIVE NAME = IBM1S506.ADD - Adapter Driver for ST506/IDE DASD
 *
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION : I/O State Machine for ST-506 Adapter Driver
 *
 * Purpose:
 *
 *
 *
 *
 *
*//*
 * Edit History for defect # 148406:
 *
 * Edit    Date    Comments
 * ----- --------  -------------------------------------------------------------
 * [001] 30-May-95 Added Shishir Shah's changes to latest version of this
 *                 source file from IBM. /jlh
 *
 *      1) In SetIOAddress():
 *          - Check for DMA Flags and Create DMA Scatter Gather List
 *          - If DMASGLIst == 1 OR DMASGEnabled
 *            DO Scatter Gather I/O
 *          - If Request is greater than 256 sectors, do PIO
 *      2) In InterruptState():
 *          - If DMA IO Completed, Check Status of Request.
 *          - Post IO Complete.
 *      3) Added ComputeDMASGList() and ProgramDMAController()
 *      4) In StartBlockIO():
 *          - Added enable of IRQ timer if DMA I/O rather than just on
 *            write operation.
 * [002] 06-15-95   Add support for PCI Bus Master DMA. /jlh
 *      1) In SetIOAddress():
 *         - Add code to initialize for Bus Master transfer.
 *         - Remove PCI Type F DMA scatter/gather capability
 *      2) In InterruptState():
 *         - Handle additional shutdown procedure for Bus Master DMA
 *      3) Add CreateBMSGList()
 *
 * [003] 08-31-95  Integrated latest IBM driver changes. IBM changes flagged with
 *                 unique IBM change codes as in table above. /jlh
 *
 * [004] 09-27-95  Add code to cause retries when PCI Bus Master error status
 *                 is set. /jlh
 *
 * [005] 09-27-95  Add code to wait for Bus Master DMA active bit to clear
 *                 prior to stopping DMA engine. Some drives were causing data
 *                 corruption problems due to what appear to be longer than
 *                 normal delays at command completion in conjunction with DMA
 *                 transfers. /jlh
 *
 * [006] 09-29-95  Added changes to work around the random start-of-transfer
 *                 interrupt noticed on some drives. An interrupt at the beginning
 *                 of the xfer while in DMA mode is considered an error, and the
 *                 xfer retried. /mol
 *
 *       1) In StartBlockIO():
 *            - Added code to start the Bus Master controller IDE engine after the
 *              command is sent to the drive.
 *       2) In SetIOAddress():
 *            - Removed the code that starts the Bus Master controller. To avoid
 *              unwanted data transfers the Bus Master cotroller will be
 *              activated after the command is sent to the drive.
 *       3) In InterruptState():
 *            - Moved the code that handles the Bus Master IDE controller before
 *              the first IDE status read to avoid data transfers if this is
 *              a start-of-transfer interrupt.
 *
 * [007] 10-03-95  Add passthru capability via DoOtherIO mechanism. /jlh
 *
 *      1) In DoOtherIO add code to setup passthru requests.
 *
 * [008] 11-14-95  Rearrange code to integrate in latest IBM code changes. /jlh
 *
 * [010] 11-06-95  Add code to increment counters for operations. /jlh
 *
 * [011] 11-29-95  Add safeguard for the (unlikely) case the bus master IDE controller
 *                 locks up. /mol
 *
 *       1) In SetIOAddress test ACBF_BM_DMA_FORCEPIO and force PIO xfer if set.
 *
 *       2) In ErrorState, if the driver timed out and the Active bit is still on,
 *          set the ACBF_BM_DMA_FORCEIO flag.
 *
 * [012] 12-13-95   Add code to passthru only certain commands. Passthru commands
 *                  without writes to the command register are invalid. Commands
 *                  other than Identify, ATAPI Identify, Set Features, and SMART are
 *                  also invalid. /jlh
 *
 * [013] 12-21-95   Add code to increment error counters in
 *                  in DeviceCounters array of UCB structure. /jlh
 *
 * [014] 01-19-96   Add code to preserve D0DMA and D1DMA in BMISTA. /jlh
*/

 #define INCL_NOBASEAPI
 #define INCL_NOPMAPI
 #define INCL_NO_SCB
 #define INCL_INITRP_ONLY
 #include "os2.h"
 #include "dos.h"
 #include "dskinit.h"

 #include "iorb.h"
 #include "reqpkt.h"
 #include "dhcalls.h"
 #include "addcalls.h"
 #include "scsi.h"

 #include "s506cons.h"
 #include "s506type.h"
 #include "s506regs.h"
 #include "s506ext.h"
 #include "s506pro.h"
#ifdef MCA                                                         /*@V117508*/
 #include "s506ps2.h"                                              /*@V117508*/
#endif                                                             /*@V117508*/


/*---------------------------------------------*/
/*  StartSM()                                  */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/
VOID NEAR StartSM( NPACB npACB )                                    /*@V147576, function renamed */
{
  /*-------------------------------------------------*/
  /* ACB Use Count Checks                            */
  /* --------------------                            */
  /* The state machine is reentrant on a per ACB     */
  /* basis.                                          */
  /*-------------------------------------------------*/

  DISABLE

  npACB->UseCount++;
#ifdef DEBUG
  if( npACB->UseCount > 2 )
  {
     _asm int 3
  }
#endif

  if( npACB->UseCount == 1 )
  {
    do
    {
      ENABLE
      do
      {
        npACB->Flags &= ~ACBF_WAITSTATE;

        switch (npACB->State)
        {
          case ACBS_START:
            StartState(npACB);
            break;
          case ACBS_INTERRUPT:
            InterruptState(npACB);
            break;
          case ACBS_DONE:
            DoneState(npACB);
            break;
          case ACBS_SUSPEND:                                        /*@V147576*/
            SuspendState(npACB);                                    /*@V147576*/
            break;                                                  /*@V147576*/
          case ACBS_RETRY:
            RetryState(npACB);
            break;
          case ACBS_RESETCHECK:
            ResetCheck(npACB);
            break;
          case ACBS_ERROR:
            ErrorState(npACB);
            break;
        }

      } while ( !(npACB->Flags & ACBF_WAITSTATE) );

      DISABLE
    }
    while ( --npACB->UseCount );
  }
}

/*---------------------------------------------*/
/* FixedIRQ0, FixedIRQ1, FixedIRQ0, FixedIRQ1  */
/* ------------------------------------------  */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

USHORT FAR FixedIRQ0()
{
  return( FixedInterrupt( ACBPtrs[0].npACB ) >> 1);                  /*@V87325*/
}

USHORT FAR FixedIRQ1()
{
  return( FixedInterrupt( ACBPtrs[1].npACB ) >> 1);                  /*@V87325*/
}

USHORT FAR FixedIRQ2()
{
  return( FixedInterrupt( ACBPtrs[2].npACB ) >> 1);                  /*@V87325*/
}

USHORT FAR FixedIRQ3()
{
  return( FixedInterrupt( ACBPtrs[3].npACB ) >> 1);                  /*@V87325*/
}


USHORT NEAR FixedInterrupt( NPACB npACB )
{
  USHORT       Claimed = 0;
  NPHWRESOURCE npHWR;                                               /*@V147576*/

  if ( npACB )
  {
     npACB->AtInterrupt++;                                          /*@V162458*/

#ifdef DEBUG                                                        /*@V159438*/
     /*                                                               @V159438
     ** Stop if still processing an interrupt inside this driver.     @V159438
     */                                                             /*@V159438*/
     if( npACB->AtInterrupt > 1 )                                   /*@V159438*/
     {                                                              /*@V159438*/
        _asm int 3                                                  /*@V159438*/
     }                                                              /*@V159438*/
#endif                                                              /*@V159438*/

#ifdef DEBUG                                                        /*@V159438*/
     /*                                                               @VVVVVVV
     ** If this is a nested interrupt, record the curent state of
     ** this driver.  For this to yield information, break into this
     ** driver with the debugger and look at the address of the
     ** kernal global: interruptlevel.  Edit the address of
     ** interruptlevel into: pIRQNestLevel.  Let this driver run.
     */
     {
        USHORT lvl;

        if( pIRQNestLevel && (*pIRQNestLevel < MAX_DEBUG_RECORDS) )
        {
           lvl = *pIRQNestLevel;
           debRec[lvl].npACB    = npACB;
           debRec[lvl].IntLevel = npACB->IntLevel;
           debRec[lvl].UnitId   = npACB->UnitId;
           debRec[lvl].State    = npACB->State;
           debRec[lvl].Flags    = npACB->Flags;
           debRec[lvl].ReqFlags = npACB->ReqFlags;
           debRec[lvl].pIORB    = npACB->pIORB;
           debRec[lvl].StartRBA = npACB->StartRBA;
           debRec[lvl].SecReq   = npACB->SecReq;
           debRec[lvl].SecToGo  = npACB->SecToGo;
           if( npACB->pIORB )
           {
              debRec[lvl].CommandCode     = npACB->pIORB->CommandCode;
              debRec[lvl].CommandModifier = npACB->pIORB->CommandModifier;
              debRec[lvl].IORBStatus      = npACB->IORBStatus;
              debRec[lvl].IORBError       = npACB->IORBError;
           }
           else
           {
              debRec[lvl].CommandCode     = 0;
              debRec[lvl].CommandModifier = 0;
              debRec[lvl].IORBStatus      = 0;
              debRec[lvl].IORBError       = 0;
           }
           if( lvl > 8 )
           {
              /*
              ** At this point something is probably seriously broken
              ** so stop hopefully before the trap to allow examination
              ** of the debug data stored above.
              */
              _asm int 3
           }
        }
     }                                                              /*@AAAAAAA*/
#endif                                                              /*@V159438*/
                                                                    /*@V117508*/
#ifdef MCA                                                          /*VVVVVVVV*/
     /* We could be in a shared interrupt situation so we need to determine if */
     /* the IDE was responsible for the interrupt.                             */
     /* The assumption being made here is that the Lacuna method of determining*/
     /* IRQ ownership is the odd way of doing so, and that the MACH 5 method   */
     /* is the method that will be used if another IDE/MCA system is developed */

     if ( SystemType == LACUNA )
     {
        if ( Lacuna_IDE_Generated_IRQ ( npACB ) )
           return( ~Claimed );
     }
     else
     {
        if ( MCA_IDE_Generated_IRQ ( npACB ) )
           return( ~Claimed );
     }                                                              /*AAAAAAAA*/
#endif                                                              /*@V117508*/


     npHWR = &HWResource[npACB->HWResourceIndex];                   /*@V147576*/
     if( npHWR->Flags & HWRF_SUSPENDED )                            /*@V147576*/
     {                                                               /*@V93531*/
        if( !npHWR->SuspendIRQaddr )                                /*@V147576*/
        {                                                            /*@V93531*/
           _asm int 3     /* suspended without a suspend address? */ /*@V93531*/
        }                                                            /*@V93531*/
        else                                                         /*@V93531*/
        {                                                           /*@V117508*/

#ifdef MCA                                                          /*@V117508*/
           Clear_IDE_IRQ_Status_Bit ( npACB );                      /*@V117508*/
#endif                                                              /*@V117508*/
           Claimed = ~(*npHWR->SuspendIRQaddr)();                   /*@V147576*/
        }                                                           /*@V117508*/
     }                                                               /*@V93531*/
     else                                                            /*@V93531*/
     {
        /*----------------------------------------------------*/     /*@V72465*/
        /*  Read the controller status reg to clear interrupt */     /*@V72465*/
        /*  if the hardware is a DPT card which requires a    */     /*@V72465*/
        /*  read to clear the interrupt.                      */     /*@V72465*/
        /*----------------------------------------------------*/    /*@V106915*/
                                                                    /*@V106915*/
        if ( npACB->Flags & ACBF_IRQCLEAR )                         /*@V106915*/
        {                                                           /*@V106915*/
           GetStatusReg( npACB );                                    /*@V72465*/
        }                                                           /*@V106915*/

#ifdef MCA
        Clear_IDE_IRQ_Status_Bit ( npACB );
#endif

        if( (npACB->Channel ==   NATIVE_PRIMARY_CHANNEL) ||         
            (npACB->Channel == NATIVE_SECONDARY_CHANNEL)  )
        {
           if( !OurPCIInterrupt( npACB ))
           {
              return( ~Claimed );
           }

           if( ( npACB->ReqFlags & ACBR_DMAIO )     &&
               ( npACB->npUCB->Flags & UCBF_BM_DMA ) )
           {
              USHORT Data, Port;

              Port = npACB->BMISTA;           /* Bus Master Status Register */
              inp ( Port, Data );

              First_BMIStatus = Data;          /* Save it for Interrupt State */

              if( Data & ACBX_BMISTA_INTERRUPT ) /* IDE interrupt was detected */
              {
                 ClearBMISTA_INT( Data, npACB );
              }
           }
        }                                                           

        DISABLE                                                     /*@V126233*/
        if ( npACB->Flags & ACBF_INTERRUPT )
        {
           npACB->Flags &= ~ACBF_INTERRUPT;

           if ( npACB->IRQTimerHandle )
           {
              ADD_CancelTimer( npACB->IRQTimerHandle );
              npACB->IRQTimerHandle = 0;
           }
           else
           {
#ifdef DEBUG_SPURIOUS                                               /*@V159438*/
              _asm                                                  /*@V159438*/
              {                                                     /*@V159438*/
                 push ax                                            /*@V159438*/
                 mov ax, 0x3333                                     /*@V159438*/
                 int 3                                              /*@V159438*/
                 pop ax                                             /*@V159438*/
              }                                                     /*@V159438*/
#endif                                                              /*@V159438*/
              goto SpuriousInt;                                      /*@V77133*/
           }

           Claimed = 1;

           ENABLE

           DevHelp_EOI( npACB->IntLevel );

           StartSM( npACB );                                        /*@V147576*/
        }
        else
        {
SpuriousInt:                                                         /*@V77133*/
#ifdef DEBUG_SPURIOUS                                               /*@V159438*/
           _asm                                                     /*@V159438*/
           {                                                        /*@V159438*/
              push ax                                               /*@V159438*/
              mov ax, 0x6666                                        /*@V159438*/
              int 3                                                 /*@V159438*/
              pop ax                                                /*@V159438*/
           }                                                        /*@V159438*/
#endif                                                              /*@V159438*/
           npACB->SpuriousIRQ++;
           Claimed = 1;

           ENABLE                                                   /*@V126233*/

           DevHelp_EOI( npACB->IntLevel );
        }
     }

     npACB->AtInterrupt--;                                          /*@V162458*/
  }
#ifdef DEBUG                                                        /*@V159438*/
  else                                                              /*@V159438*/
  {                                                                 /*@V159438*/
     _asm                                                           /*@V159438*/
     {                                                              /*@V159438*/
        push ax                                                     /*@V159438*/
        mov ax, 0x7777                                              /*@V159438*/
        int 3                                                       /*@V159438*/
        pop ax                                                      /*@V159438*/
     }                                                              /*@V159438*/
  }                                                                 /*@V159438*/
#endif                                                              /*@V159438*/

  return( ~Claimed );
}


/*                                                                    @V147576
** StartState()                                                       @VVVVVVV
**
** Route the current IORB on the ACB's IORB queue to the appropriate
** IORB handler.
**
*/
VOID NEAR StartState( NPACB npACB )
{
  PIORB         pIORB;
  USHORT        CmdCode;
  USHORT        CmdModifier;
  NPHWRESOURCE  npHWR;
  NPUCB         npUCB;                                              /*@V185215*/

  /*
  ** Allocate the IDE HW interface.  This is required as the HW
  ** interface may be shared by more than 1 ACB.  HW resources are
  ** freed in DoneState.
  */
  if( AllocateHWResources( npACB ) )
  {
     return;
  }
  else
  {
     npHWR = &HWResource[npACB->HWResourceIndex];
  }

  DISABLE                                                           /*@V147576*/
  if( NextIORB( npACB, npHWR ) )                                    /*@VVVVVVV*/
  {
    /*--------------------------------------------------------*/
    /* No more IORBs so go to sleep, stay in ACBS_START, and  */
    /* mark the state machine as inactive.                    */
    /*--------------------------------------------------------*/
    FreeHWResources( npACB );
    npACB->Flags |= ACBF_WAITSTATE;
    npACB->Flags &= ~ACBF_SM_ACTIVE;
    ENABLE
    return;
  }                                                                 /*@AAAAAAA*/
  ENABLE                                                            /*@V147576*/

  pIORB = npACB->pIORB;

  CmdCode     = pIORB->CommandCode;
  CmdModifier = pIORB->CommandModifier;

  npUCB = npACB->npUCB = (NPUCB)npACB->pIORB->UnitHandle;           /*@V185215*/
  npACB->UnitId = npACB->npUCB->UnitId;                             /*@V157085*/

  // All I/O operations should be started with:                     /*@V185215*/
  //   - retrys enabled                                             /*@V185215*/
  //   - multiple mode disabled                                     /*@V185215*/
  //   - geometry reset not required                                /*@V185215*/
  //   - resets enabled if initialization determined OK             /*@V185215*/
  // The controlling bits for these behaviors can only be changed   /*@V185215*/
  // from within the state machine.                                 /*@V185215*/
  //                                                                /*@V185215*/
  // Reset Flags bits (next 3 lines) moved to here from             /*@V185215*/
  // InitACBRequest().                                              /*@V185215*/
  npACB->Flags &= ~(  ACBF_DISABLERETRY                             /*@V117435*//*@V185215*/
                    | ACBF_MULTIPLEMODE                             /*@V117435*//*@V185215*/
                    | ACBF_DISKPARMRESET );                         /*@V117435*//*@V185215*/
  // Initial state of the disable reset flag: ACBF_DISABLERESET,    /*@V185215*/
  // is saved in the UCB Flags during initialization.               /*@V185215*/
  if( npUCB->Flags & UCBF_DISABLERESET )                            /*@V185215*/
  {                                                                 /*@V185215*/
     npACB->Flags |= ACBF_DISABLERESET;                             /*@V185215*/
  }                                                                 /*@V185215*/
  else                                                              /*@V185215*/
  {                                                                 /*@V185215*/
     npACB->Flags &= ~ACBF_DISABLERESET;                            /*@V185215*/
  }                                                                 /*@V185215*/

  /*------------------------------------------------*/
  /* Check that unit is allocated for IORB commands */
  /* which require an allocated unit                */
  /*------------------------------------------------*/
  if ( !(CmdCode == IOCC_CONFIGURATION) &&
       !(CmdCode == IOCC_UNIT_CONTROL && CmdModifier == IOCM_ALLOCATE_UNIT) &&
                                                                    /*@V147576*/
       !(CmdCode == IOCC_DEVICE_CONTROL && CmdModifier == IOCM_SUSPEND) &&
                                                                    /*@V87325*/
       !(CmdCode == IOCC_RESOURCE && CmdModifier == IOCM_REPORT_RESOURCES) )         /*@V87325*/
  {
    if ( !(npACB->npUCB->Flags & UCBF_ALLOCATED) && !InitActive )
    {
      /* Unit not allocated */
      npACB->IORBStatus |= IORB_ERROR;                              /*@V147576*/
      npACB->IORBError  = IOERR_UNIT_NOT_ALLOCATED;                 /*@V147576*/

      npACB->State = ACBS_DONE;
      return;
    }
  }

  /*-------------------------------------------*/
  /* Route the iorb to the appropriate handler */
  /*-------------------------------------------*/

  switch ( CmdCode )
  {
    case IOCC_CONFIGURATION:
      switch( CmdModifier )
      {
        case IOCM_GET_DEVICE_TABLE:  break;                         /*@V108783*/
        case IOCM_COMPLETE_INIT:     CompleteInit(npACB);    break;
        default:                     CmdNotSupported(npACB); break;
      }
    break;

    case IOCC_UNIT_CONTROL:
      switch( CmdModifier )
      {
        case IOCM_ALLOCATE_UNIT:     AllocateUnit(npACB);    break;
        case IOCM_DEALLOCATE_UNIT:   DeallocateUnit(npACB);  break;
        case IOCM_CHANGE_UNITINFO:   ChangeUnitInfo(npACB);  break;
        default:                     CmdNotSupported(npACB); break;
      }
    break;

    case IOCC_GEOMETRY:
      switch( CmdModifier )
      {
        case IOCM_GET_MEDIA_GEOMETRY:
        case IOCM_GET_DEVICE_GEOMETRY: GetGeometry(npACB);     break;
        default:                       CmdNotSupported(npACB); break;
      }
    break;

    case IOCC_EXECUTE_IO:
      switch( CmdModifier )
      {
        case IOCM_NO_OPERATION:
        case IOCM_READ:
        case IOCM_READ_VERIFY:
        case IOCM_WRITE:
        case IOCM_WRITE_VERIFY:      StartIO(npACB);         break; /*@V147576*/
        default:                     CmdNotSupported(npACB); break;
      }
    break;

    /* Begin [008] Add adapter pass thru command support */

    case IOCC_ADAPTER_PASSTHRU:                                 /*@V151345*/
      {
      PIORB_ADAPTER_PASSTHRU pIORB=(PIORB_ADAPTER_PASSTHRU)npACB->pIORB;
        switch ( CmdModifier )
          {
          case IOCM_EXECUTE_ATA:      StartIO(npACB);          break;
          default:                    CmdNotSupported(npACB);  break;
          }
      }
    break;

    /* End [008] */

    case IOCC_DEVICE_CONTROL:                                       /*@V147576*/
      switch ( CmdModifier )                                        /*@V147576*/
      {                                                             /*@V147576*/
        case IOCM_SUSPEND:           Suspend(npACB);         break; /*@V147576*/
        case IOCM_LOCK_MEDIA:        LockMedia(npACB);       break; /*@V149971*/
        case IOCM_UNLOCK_MEDIA:      UnLockMedia(npACB);     break; /*@V149971*/
        case IOCM_EJECT_MEDIA:       EjectMedia(npACB);      break; /*@V149971*/
        default:                     CmdNotSupported(npACB); break; /*@V147576*/
      }                                                             /*@V147576*/
    break;                                                          /*@V147576*/

    case IOCC_UNIT_STATUS:
      switch( CmdModifier )
      {
        case IOCM_GET_UNIT_STATUS:   StartIO(npACB);         break; /*@V162789*/
        default:                     CmdNotSupported(npACB); break;
      }
    break;

    case IOCC_RESOURCE :                                            /*@V87325*/
      switch( CmdModifier )                                         /*@V87325*/
      {                                                             /*@V87325*/
        case IOCM_REPORT_RESOURCES:  GetUnitResources(npACB);       /*@V87325*/
                                     break;                         /*@V87325*/
        default:                     CmdNotSupported(npACB);        /*@V87325*/
                                     break;                         /*@V87325*/
      }                                                             /*@V87325*/
    break;                                                          /*@V87325*/

    default:
      CmdNotSupported(npACB);
    break;
  }

  return;
}


/*-------------------------------------------------*/
/*  StartIO()                                      */
/*                                                 */
/*  Called from StartState() to begin processing   */
/*  an I/O operation which requires HW set-up and  */
/*  interrupt processing.                          */
/*-------------------------------------------------*/

VOID NEAR StartIO(NPACB npACB)                                      /*@V147576, renamed */
{
  NPUCB         npUCB;

  npUCB = (NPUCB)npACB->npUCB;

  DISABLE
  npACB->ElapsedTime = 0;
  ENABLE

  if ( npUCB->Flags & UCBF_NOTPRESENT )                             /*@V149971*/
  {                                                                 /*@V149971*/
    npACB->IORBStatus &= ~IORB_RECOV_ERROR;                         /*@V149971*/
    npACB->IORBStatus |= IORB_ERROR;                                /*@V149971*/
    npACB->IORBError = IOERR_UNIT_NOT_READY;                        /*@V157085*/
                                                                    /*@V149971*/
    npACB->State = ACBS_DONE;                                       /*@V149971*/
    return;                                                         /*@V149971*/
  }

  if ( npACB->Flags & ACBF_PS2IDEPORT  )
  {
    if ( BIOSActive )
    {
      SetupPS2IDEPort( npACB->IOPorts[FI_PDAT], ON );                /*@V147576*//*@V153916*/
    }

    SetPS2DiskLight( 1 );
  }

  if( !InitACBRequest( npACB ) )
  {
     if( npACB->ReqFlags & ACBR_OTHERIO )
     {
       StartOtherIO( npACB );
     }
     else if( npACB->ReqFlags & ACBR_BLOCKIO )
     {
       StartBlockIO( npACB );
     }
     else
     {
       _asm int 3
     }
  }
}

/*---------------------------------------------*/
/* RetryState                                  */
/* ----------                                  */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

VOID NEAR RetryState(NPACB npACB)
{
  if ( npACB->ReqFlags & ACBR_OTHERIO )
  {
    StartOtherIO( npACB );
  }
  else if ( npACB->ReqFlags & ACBR_BLOCKIO )
  {
    StartBlockIO( npACB );
  }
}

/*---------------------------------------------*/
/* InitACBRequest                              */
/* --------------                              */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

USHORT NEAR InitACBRequest( NPACB npACB )
{
  PIORB         pIORB;
  USHORT        CmdMod;
  NPUCB         npUCB;
  PPassThruATA  picp;                         /* [012] ptr to passthru command structure */
  PIORB_ADAPTER_PASSTHRU  piorb_pt;           /* [012] ptr to passthru IORB structure */

  pIORB   = npACB->pIORB;

  CmdMod  = pIORB->CommandModifier;

  npUCB         = npACB->npUCB;
  npUCB->Flags &= ~(UCBF_DIAG_FAILED);                               /*@V77133*/

  npACB->ReqFlags      = 0;
  npACB->TimerFlags    = 0;
  npACB->DataErrorCnt  = 0;
  npACB->BusyTimeoutCnt = 0;                                         /*@V85057*/
  npACB->cResets       = 0;                                          /*@V77133*/

  if ( pIORB->RequestControl & IORB_DISABLE_RETRY )
  {
    npACB->Flags |= ACBF_DISABLERETRY;
  }

  /*------------------------------------------------------*/
  /* Translate IORB Command Code/Modifier to ACBR_* flags */
  /*------------------------------------------------------*/
  /* Begin [007] Add support for passthru commands */

  if ( pIORB->CommandCode == IOCC_EXECUTE_IO )
  {
    switch ( CmdMod )
    {
      case IOCM_READ:
        npACB->IOSGPtrs.Mode  = PORT_TO_SGLIST;
        npACB->ReqFlags |= npUCB->ReqFlags | ACBR_READ;
#ifdef ENABLE_COUNTERS                                              /* [010] */
        ++npUCB->DeviceCounters.TotalReadOperations;                /* [010] */
        npUCB->DeviceCounters.TotalSectorsRead +=                   /* [010] */
          (ULONG)(((PIORB_EXECUTEIO)pIORB)->BlockCount);                               /* [010] */
#endif                                                              /* [010] */
        break;

      case IOCM_WRITE:
        npACB->IOSGPtrs.Mode  = SGLIST_TO_PORT;
        npACB->ReqFlags |= npUCB->ReqFlags | ACBR_WRITE;
#ifdef ENABLE_COUNTERS                                              /* [010] */
        ++npUCB->DeviceCounters.TotalWriteOperations;               /* [010] */
        npUCB->DeviceCounters.TotalSectorsWritten +=                /* [010] */
          (ULONG)(((PIORB_EXECUTEIO)pIORB)->BlockCount);                               /* [010] */
#endif                                                              /* [010] */
        break;

      case IOCM_WRITE_VERIFY:
        npACB->IOSGPtrs.Mode  = SGLIST_TO_PORT;
        npACB->ReqFlags |=  npUCB->ReqFlags | (ACBR_WRITEV | ACBR_WRITE);
#ifdef ENABLE_COUNTERS                                              /* [010] */
        ++npUCB->DeviceCounters.TotalWriteOperations;               /* [010] */
        npUCB->DeviceCounters.TotalSectorsWritten +=                /* [010] */
          (ULONG)(((PIORB_EXECUTEIO)pIORB)->BlockCount);                               /* [010] */
#endif                                                              /* [010] */
        break;

      case IOCM_READ_VERIFY:
        npACB->ReqFlags |= npUCB->ReqFlags | ACBR_VERIFY;
#ifdef ENABLE_COUNTERS                                              /* [010] */
        ++npUCB->DeviceCounters.TotalReadOperations;                /* [010] */
        npUCB->DeviceCounters.TotalSectorsRead +=                   /* [010] */
          (ULONG)(((PIORB_EXECUTEIO)pIORB)->BlockCount);                               /* [010] */
#endif                                                              /* [010] */
        break;

      case IOCM_NO_OPERATION:
        npACB->IOSGPtrs.Mode  = PORT_TO_SGLIST;
        npACB->ReqFlags |= npUCB->ReqFlags;
#ifdef ENABLE_COUNTERS                                              /* [010] */
        ++npUCB->DeviceCounters.TotalReadOperations;                /* [010] */
        npUCB->DeviceCounters.TotalSectorsRead +=                   /* [010] */
          (ULONG)(((PIORB_EXECUTEIO)pIORB)->BlockCount);                               /* [010] */
#endif                                                              /* [010] */
        break;

      default:
        npACB->IORBStatus |= IORB_ERROR;                              /*@V147576*/
        npACB->IORBError  = IOERR_CMD_NOT_SUPPORTED;                  /*@V147576*/
        npACB->State = ACBS_DONE;
        return( TRUE );                                               /*@V147576*/
    }
  }
  else if ( pIORB->CommandCode == IOCC_ADAPTER_PASSTHRU )
  {

    if(!SetupFromATA(npACB,npUCB))                                  /*@V151345*/
      {                                                             /*@VVVVVVV*/
      //
      // unsupported passthru command
      //
      npACB->IORBStatus |= IORB_ERROR;
      npACB->IORBError  = IOERR_CMD_NOT_SUPPORTED;
      npACB->State = ACBS_DONE;
      return( TRUE );
      }                                                             /*@AAAAAAA*/
  }                                                                 /*@V151345*/
  else if( pIORB->CommandCode == IOCC_UNIT_STATUS )                 /*@V162789*/
  {                                                                 /*@V162789*/
     switch( CmdMod )                                               /*@V157085*/
     {                                                              /*@V157085*/
     case IOCM_GET_UNIT_STATUS:                                     /*@V157085*/
        /*                                                            @V162789
        ** Setup a seek to see if there is a drive out there.         @V162789
        */                                                          /*@V162789*/
        if( SetupSeek( npACB, npUCB ) )                             /*@V157085*/
        {                                                           /*@V157085*/
           /*                                                         @V157085
           ** Must work the first time, because if this is a          @V157085
           ** removable media device then error info gets lost.       @V157085
           */                                                       /*@V157085*/
           npACB->Flags |= ACBF_DISABLERESET | ACBF_DISABLERETRY;   /*@V157085*/
           break;                                                   /*@V157085*/
        }                                                           /*@V157085*/

     default:                                                       /*@V157085*/
        npACB->IORBStatus |= IORB_ERROR;                            /*@V162789*/
        npACB->IORBError   = IOERR_CMD_NOT_SUPPORTED;               /*@V162789*/
        npACB->State = ACBS_DONE;                                   /*@V162789*/
        return( TRUE );                                             /*@V162789*/
     }                                                              /*@V157085*/
  }                                                                 /*@V162789*/

  /* End [007] */

  /*------------------------------------------------*/
  /* Check for MULTIPLE MODE                        */
  /*                                                */
  /* If the unit is supports Multiple Mode I/O      */
  /* then issue the command to enable multiple mode */
  /*                                                */
  /* If the command was previously issued           */
  /* successfully, then set the MULTIPLEMODE flag   */
  /* in the ACB.                                    */
  /*                                                */
  /* Do not activate Multiple Mode support until    */               /*@V75103*/
  /* we are done with the BIOS in case the BIOS     */               /*@V75103*/
  /* has Multiple Mode enabled with a different     */               /*@V75103*/
  /* blocksize.                                     */               /*@V75103*/
  /*                                                */
  /*------------------------------------------------*/
  if ( npUCB->Flags & UCBF_SMSENABLED && !BIOSActive )               /*@V75103*/
  {
    if ( npUCB->Flags & UCBF_MULTIPLEMODE )
    {
      npACB->Flags |= ACBF_MULTIPLEMODE;
    }
    else
    {
      npACB->ReqFlags |= ACBR_SETMULTIPLE;
    }
  }

  /*-----------------------------------------------*/
  /* Set the S/G poincers for Block IO operations  */
  /*-----------------------------------------------*/
  if ( npACB->ReqFlags & ACBR_BLOCKIO  )                            /*@V151345*/
  {
    InitBlockIO( npACB );
  }

  return( FALSE );                                                  /*@V147576*/
}


BOOL NEAR SetupFromATA(NPACB npACB, NPUCB npUCB)                    /*@V151345*/
{                                                                   /*@VVVVVVV*/
  PPassThruATA  picp;                         /* [012] ptr to passthru command structure */
  PIORB_ADAPTER_PASSTHRU  piorb_pt;           /* [012] ptr ti passthru IORB structure */

  /* Setup pointers to IORB and IssueCommandParameters Structures */

  piorb_pt = (PIORB_ADAPTER_PASSTHRU) npACB->pIORB;
  picp = (PPassThruATA) piorb_pt->pControllerCmd;

  if ( !(picp->RegisterTransferMap & PTA_RTMWR_COMMAND) )
  {
    return( FALSE );
  }
  else if ( picp->TaskFileIn.Command == FX_SMARTCMD )
  {
    if (picp->TaskFileIn.Features == 0xD7)
    {
      return( FALSE );
    }
  }
  /* Begin [007] Add code to detect passthru request and set it up. */

  /* Setup pointers to IORB and IssueCommandParameters Structures */

  npACB->StartRBA = 0;

  piorb_pt = (PIORB_ADAPTER_PASSTHRU) npACB->pIORB;
  picp = (PPassThruATA) piorb_pt->pControllerCmd;

  npACB->IOSGPtrs.cSGList       = piorb_pt->cSGList;
  npACB->IOSGPtrs.pSGList       = piorb_pt->pSGList;
  npACB->IOSGPtrs.iSGList       = 0;
  npACB->IOSGPtrs.SGOffset      = 0;
  npACB->IOSGPtrs.iSGListStart  = 0;
  npACB->IOSGPtrs.SGOffsetStart = 0;

  // copy caller supplied tack file register info


  npACB->IORegs[FI_PWRP]    = picp->TaskFileIn.Features;
  npACB->IORegs[FI_PSECCNT] = picp->TaskFileIn.SectorCount;
  npACB->IORegs[FI_PSECNUM] = picp->TaskFileIn.SectorNumber;
  npACB->IORegs[FI_PCYLL]   = picp->TaskFileIn.CylinderLow;
  npACB->IORegs[FI_PCYLH]   = picp->TaskFileIn.CylinderHigh;
  npACB->IORegs[FI_PDRHD]  &= 0x40;
  npACB->IORegs[FI_PDRHD]  |= (picp->TaskFileIn.DriveHead & 0x4F);
  npACB->IORegs[FI_PCMD]    = picp->TaskFileIn.Command;
  npACB->IOPendingMask      = (picp->RegisterTransferMap &
                              PTA_RTMWR_REGMASK) | FM_PDRHD;

  // initialize mode, don't know yet
  npACB->IOSGPtrs.Mode  = 0;

  npACB->ReqFlags = npUCB->ReqFlags = ACBR_PASSTHRU;

  npACB->SecRemain         = 0;
  npACB->SecToGo           = 0;
  npACB->SecReq            = 0;
  npACB->SecDone           = 0;
  npACB->BytesToTransfer   = 0;
  npACB->SecPerInt         = 0;

  // if we have a scatter/gather list this is a DATA command
  if ( piorb_pt->cSGList )
  {
    // scatter gather list is in SECTOR format

    npACB->SecRemain         = piorb_pt->cSGList;
    npACB->SecToGo           = piorb_pt->cSGList;
    npACB->SecReq            = npACB->SecToGo;
    npACB->SecDone           = 0;
    npACB->BytesToTransfer   = piorb_pt->cSGList * 512;
    npACB->SecPerInt         = 1;

    // if the IROB says inbound
    // then set this as a READ command

    if ( piorb_pt->Flags & PT_DIRECTION_IN )
    {
      npACB->IOSGPtrs.Mode  = PORT_TO_SGLIST;
      npACB->ReqFlags |= npUCB->ReqFlags | ACBR_READ;
    }

    // IORB doesn't say IN, so MUST be OUT (with scatter gather list set)

    else
    {
      npACB->IOSGPtrs.Mode  = SGLIST_TO_PORT;
      npACB->ReqFlags |= npUCB->ReqFlags | ACBR_WRITE;
    }
  }
  else
    {
     npACB->ReqFlags |= npUCB->ReqFlags | ACBR_NONDATA;
    }

  /* End [007] */

  return TRUE;                                                      /*@AAAAAAA*/
}                                                                   /*@V151345*/


/*                                                                    @V162789
** Setup to perform a seek to the first sector on the drive.          @V162789
*/                                                                  /*@V162789*/
BOOL NEAR SetupSeek( NPACB npACB, NPUCB npUCB )                     /*@V162789*/
{                                                                   /*@V162789*/
                                                                    /*@V162789*/
   npACB->StartRBA = 0;                                             /*@V162789*/
                                                                    /*@V162789*/
   npACB->IOSGPtrs.cSGList       = 0;                               /*@V162789*/
   npACB->IOSGPtrs.pSGList       = NULL;                            /*@V162789*/
   npACB->IOSGPtrs.iSGList       = 0;                               /*@V162789*/
   npACB->IOSGPtrs.SGOffset      = 0;                               /*@V162789*/
   npACB->IOSGPtrs.iSGListStart  = 0;                               /*@V162789*/
   npACB->IOSGPtrs.SGOffsetStart = 0;                               /*@V162789*/
   npACB->IOSGPtrs.Mode          = 0;                               /*@V162789*/
                                                                    /*@V162789*/
   npACB->IORegs[FI_PWRP]    = 0;                                   /*@V162789*/
   npACB->IORegs[FI_PSECCNT] = 0;                                   /*@V162789*/
   npACB->IORegs[FI_PSECNUM] = 1;                                   /*@V162789*/
   npACB->IORegs[FI_PCYLL]   = 0;                                   /*@V162789*/
   npACB->IORegs[FI_PCYLH]   = 0;                                   /*@V162789*/
   npACB->IORegs[FI_PDRHD]   = (npUCB->UnitId & 0x01) << 4;         /*@V162789*/
   npACB->IORegs[FI_PCMD]    = FX_SEEK;                             /*@V162789*/
   npACB->IOPendingMask      = (FM_PSECCNT | FM_PCYLL | FM_PCYLH |  /*@V162789*/
                                FM_PDRHD | FM_PCMD);                /*@V162789*/
                                                                    /*@V162789*/
   npACB->SecRemain         = 0;                                    /*@V162789*/
   npACB->SecToGo           = 0;                                    /*@V162789*/
   npACB->SecReq            = 0;                                    /*@V162789*/
   npACB->SecDone           = 0;                                    /*@V162789*/
   npACB->BytesToTransfer   = 0;                                    /*@V162789*/
   npACB->SecPerInt         = 0;                                    /*@V162789*/
                                                                    /*@V162789*/
   npACB->ReqFlags = npUCB->ReqFlags = ACBR_NONDATA;                /*@V162789*/
                                                                    /*@V162789*/
   return( TRUE );                                                  /*@V162789*/
}                                                                   /*@V162789*/


/*---------------------------------------------*/
/* InitBlockIO                                 */
/* ------------                                */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

VOID NEAR InitBlockIO( NPACB npACB )
{
  PIORB_EXECUTEIO pIORB = (PIORB_EXECUTEIO) npACB->pIORB;

  // if NOT a Passthru command
  // then setup
  // PASSTHRU requests are setup already
  if(!(npACB->ReqFlags & ACBR_PASSTHRU))                             /*@V151345*/
    {                                                                /*@V151345*/
    npACB->IOSGPtrs.cSGList       = pIORB->cSGList;
    npACB->IOSGPtrs.pSGList       = pIORB->pSGList;
    npACB->IOSGPtrs.iSGList       = 0;
    npACB->IOSGPtrs.SGOffset      = 0;
    npACB->IOSGPtrs.iSGListStart  = 0;
    npACB->IOSGPtrs.SGOffsetStart = 0;

    npACB->StartRBA          = pIORB->RBA;

    if ( npACB->npUCB->Flags & UCBF_ONTRACK )                         /*@V108555*/
    {                                                                 /*@V108555*/
      npACB->StartRBA += ONTRACK_SECTOR_OFFSET;                       /*@V108555*/
    }                                                                 /*@V108555*/

    npACB->SecRemain         = pIORB->BlockCount;
    npACB->SecDone           = 0;

    /* Calculate total bytes in transfer request */

    npACB->BytesToTransfer   = (ULONG) pIORB->BlockCount * (ULONG) pIORB->BlockSize;  /* [002] */

    pIORB->BlocksXferred     = 0;
    } /* endif */                                                     /*@V151345*/
}

/*---------------------------------------------*/
/* StartOtherIO                                */
/* ------------                                */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

USHORT NEAR StartOtherIO( NPACB npACB )
{
  USHORT        ReqFlags;
  NPUCB         npUCB = npACB->npUCB;
  USHORT        Port;
  USHORT        Data;
  PPassThruATA  picp;   /* [007] ptr to passthru command structure */
  PIORB_ADAPTER_PASSTHRU  piorb_pt;/* [007] ptr to passthru IORB structure */

  if(!(npACB->ReqFlags & ACBR_PASSTHRU))                               /*@V151345*/
    {                                                                  /*@V151345*/
    npACB->IORegs[FI_PDRHD] = 0xA0 | (npACB->UnitId << 4);
    npACB->IOPendingMask |= FM_PDRHD | FM_PCMD;                        /*@V157085*/
    } /* endif */
  else                                                                 /*@V151345*/
    {                                                                  /*@V151345*/
    npACB->IORegs[FI_PDRHD]   |= (UCHAR) 0xA0 | (npACB->UnitId << 4);  /*@V151345*/
    }                                                                  /*@V151345*/

  ReqFlags = npACB->ReqFlags;

  if ( ReqFlags & ACBR_RESETCONTROLLER )
  {
    npACB->ReqMask = ~ACBR_RESETCONTROLLER;
    DoReset( npACB );
    goto StartOtherIOExit;
  }

  if ( ReqFlags & ACBR_RECAL )
  {
    npACB->IORegs[FI_PCMD] = FX_RECAL;
    npACB->ReqMask         = ~ACBR_RECAL;
  }
  else if ( ReqFlags & ACBR_SETPARAM )
  {
    /*------------------------------------------*/
    /* Set Device Control Register for >8 Heads */
    /*                                          */
    /* Some BIOSes set this on a per-device     */
    /* basis which caues problems if one drive  */
    /* has more than 8 heads and one has less.  */
    /*------------------------------------------*/
    Port = npACB->IOPorts[FI_RFDR];
    Data = npACB->IORegs[FI_RFDR];
    outp( Port, Data);
    IODelay();

    npUCB = npACB->npUCB;

    npACB->IORegs[FI_PCMD]    = FX_SETP;
    npACB->IORegs[FI_PSECCNT] = (UCHAR) (npUCB->PhysGeom.SectorsPerTrack);
    npACB->IORegs[FI_PDRHD]  |= (UCHAR) (npUCB->PhysGeom.NumHeads - 1);

    npACB->IOPendingMask |= FM_PSECCNT;
    npACB->ReqMask        = ~ACBR_SETPARAM;
  }
  else if ( ReqFlags & ACBR_SETLOGPARAM )
  {
    npUCB->ReqFlags |= ACBR_SETPARAM;

    npACB->IORegs[FI_PCMD]    = FX_SETP;
    npACB->IORegs[FI_PSECCNT] = (UCHAR) (npUCB->LogGeom.SectorsPerTrack);
    npACB->IORegs[FI_PDRHD]  |= (UCHAR) (npUCB->LogGeom.NumHeads - 1);

    npACB->IORegs[FI_PWRP]  = npUCB->WrtPreCmp >> 2;

    npACB->IOPendingMask |= FM_PWRP | FM_PSECCNT;
    npACB->ReqMask        = ~ACBR_SETLOGPARAM;
    npACB->Flags         |= ACBF_DISKPARMRESET;
  }
  else if ( ReqFlags & ACBR_SETMULTIPLE )
  {
    npACB->IORegs[FI_PCMD] = FX_SETMUL;

    npACB->IORegs[FI_PSECCNT] = (UCHAR) npUCB->SecPerBlk;
    npACB->IOPendingMask     |= FM_PSECCNT;

    npACB->ReqMask = ~ACBR_SETMULTIPLE;
  }
  else if ( ReqFlags & ACBR_IDENTIFY )
  {
    npACB->IORegs[FI_PCMD] = FX_IDENTIFY;
    npACB->ReqMask         = ~ACBR_IDENTIFY;                        /*@V162789*/
  }
#ifdef CMD640X
  else if ( ReqFlags & ACBR_SETPIOMODE )
  {
    npACB->IORegs[FI_PCMD] = FX_SETFEATUR;
    npACB->IORegs[FI_PWRP] = (UCHAR) 0xef; /* set feat reg: set xfer mode */
                       /* set mode/flow ctrl */
    npACB->IORegs[FI_PSECCNT] = (UCHAR) (npUCB->PioMode | 0x08);
    npACB->ReqMask         = ~ACBR_SETPIOMODE;
  }
#endif /* CMD640X */

  else if ( ReqFlags & ACBR_NONDATA )                               /*@V151345*/
  {
    npACB->ReqMask         = ~ACBR_NONDATA;                         /*@V162789*/
  }
  else
  {
    _asm int 3
  }

  /*--------------------------------------*/
  /* Set the Next State fields in the ACB */
  /*--------------------------------------*/
  npACB->Flags |= (ACBF_INTERRUPT | ACBF_WAITSTATE);
  npACB->State  = ACBS_INTERRUPT;

  /*-----------------------------------------*/
  /* Start the IRQ timer and output the copy */
  /* of the TASK File Regs in the ACB to the */
  /* controller.                             */
  /*-----------------------------------------*/
  if ( ADD_StartTimerMS((PULONG) &npACB->IRQTimerHandle,
                        (ULONG)  npACB->IRQTimeOut,
                        (PFN)    IRQTimer,
                        (PVOID)  npACB,
                        (ULONG)  0                ) )
  {
     _asm int 3
  }

  SendCmdPacket( npACB );

#ifdef DEBUG
  if( !npACB->pIORB )
  {
     _asm int 3
  }
#endif

StartOtherIOExit: ;

}


/*---------------------------------------------*/
/* StartBlockIO                                */
/* ------------                                */
/*                                             */
/* Starts a Read/Write/Verify type operation   */
/*                                             */
/*---------------------------------------------*/

USHORT NEAR StartBlockIO( NPACB npACB )
{
  USHORT   Port, Data;                  /* [006] */

  /*--------------------------------------*/
  /* Set CHS/LBA address and Sector Count */
  /* in ACB.                              */
  /*--------------------------------------*/
  if(!(npACB->ReqFlags & ACBR_PASSTHRU))                              /*@V151345*/
    {                                                                 /*@V151345*/
    SetIOAddress( npACB );
    } /* endif */
  else                                                                /*@V151345*/
    {                                                                 /*@V151345*/
    npACB->IORegs[FI_PDRHD]   |= (UCHAR) 0xA0 | (npACB->UnitId << 4); /*@V151345*/
    }                                                                 /*@V151345*/

  /*--------------------------------------*/
  /* Set the Next State fields in the ACB */
  /*--------------------------------------*/
  npACB->State  = ACBS_INTERRUPT;

  /*-----------------------------------------*/
  /* Start the IRQ timer and output the copy */
  /* of the TASK File Regs in the ACB to the */
  /* controller.                             */
  /*-----------------------------------------*/
/* [001.4] Changed following line to add check for DMA I/O also */
  if( (!(npACB->ReqFlags & ACBR_WRITE)) || (npACB->ReqFlags & ACBR_DMAIO) )
  {
    npACB->Flags |= (ACBF_INTERRUPT | ACBF_WAITSTATE);

    if ( ADD_StartTimerMS((PULONG) &npACB->IRQTimerHandle,
                          (ULONG)  npACB->IRQTimeOut,
                          (PFN)    IRQTimer,
                          (PVOID)  npACB,
                          (ULONG)  0                ) )
    {
       _asm int 3
    }
  }

  if ( !SendCmdPacket( npACB ) )
  {
#ifdef DEBUG_WATCH_PCI_CONFIG_REGS
     // Dump the contents of configuration space to registers.
     {
        USHORT   i;
        BOOL     readRC;
        ULONG    ulDataRead;

        ulDataRead = npACB->BMICOM;
        _asm
        {
           push    ax
           push    bx
           mov     ax, 1
           mov     bx, ulDataRead
           int     3
           pop     bx
           pop     ax
        }

        ulDataRead = npACB->BMISTA;
        _asm
        {
           push    ax
           push    bx
           mov     ax, 2
           mov     bx, ulDataRead
           int     3
           pop     bx
           pop     ax
        }

        ulDataRead = npACB->BMIDTP;
        _asm
        {
           push    ax
           push    bx
           mov     ax, 3
           mov     bx, ulDataRead
           int     3
           pop     bx
           pop     ax
        }

        for( i = 0; i < 0x4c; i++ )  // nothing beyond 0x4b in IDE config space
        {
           if( !(readRC = ReadPCIConfigSpace( &npACB->PCIInfo,
                                              i,
                                              &ulDataRead,
                                              1 )) )
           {
              _asm
              {
                 push    ax
                 push    bx
                 mov     ax, i
                 mov     bx, ulDataRead
                 int     3
                 pop     bx
                 pop     ax
              }
           }
           else
           {
              // Error on config space read,
              _asm
              {
                 push    ax
                 push    bx
                 mov     ax, -1
                 mov     bx, -1
                 int     3
                 pop     bx
                 pop     ax
              }
           }
        }
     }
#endif

     /* Begin [006] */
     /* If bus master DMA xfer */

     if( npACB->BM_CommandCode & ACBX_BMICOM_START )
     {
         Port = npACB->BMICOM;           /* Set port address to BM Command reg */
         Data = npACB->BM_CommandCode;   /* BM controller command */
         outp ( Port, Data );            /* Start BM controller */
     }
     /* End [006] */

    /*-----------------------------------------------*/
    /* For a 'Class 2' operation WRITE, etc. we need */
    /* to 'prime the pump' by writing the first      */
    /* block to the controller.                      */
    /*-----------------------------------------------*/
    if (!(npACB->ReqFlags & ACBR_DMAIO))
    {
      if ( npACB->ReqFlags & ACBR_WRITE )
      {
        DoBlockIO( npACB, 0 );
      }
    }
  }
#ifdef DEBUG
  if( !npACB->pIORB )
  {
     _asm int 3
  }
#endif
}

/*---------------------------------------------*/
/* SetIOAddress                                */
/* ------------                                */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

VOID NEAR SetIOAddress( NPACB npACB )
{
  NPUCB    npUCB    = npACB->npUCB;
  USHORT   ReqFlags = npACB->ReqFlags;
  USHORT   Flags    = npACB->Flags;
  ULONG    CurLBA;
  USHORT   Port;                                                    /* [002.1] */
  USHORT   Data;                                                    /* [002.1] */
  CHS_ADDR chs;

  npACB->BM_CommandCode = 0;                                        /* [006] */

  /*-------------------------------------*/
  /* Save S/G List pointers at start     */
  /* of operation.                       */
  /*-------------------------------------*/
  npACB->IOSGPtrs.iSGListStart   = npACB->IOSGPtrs.iSGList;
  npACB->IOSGPtrs.SGOffsetStart  = npACB->IOSGPtrs.SGOffset;

  /*-------------------------------------*/
  /* Check for controller transfer limit */
  /* of 256 sectors.                     */
  /*-------------------------------------*/
  npACB->SecToGo = (npACB->SecRemain >= MAX_XFER_SEC) ?
                                              MAX_XFER_SEC : npACB->SecRemain;

  CurLBA = npACB->StartRBA + npACB->SecDone;

  /*--------------------------------------------------*/
  /* For READ/WRITE operation check for MULTIPLE MODE */
  /* and calculate appropriate SecPerInt value.       */
  /*--------------------------------------------------*/
  npACB->SecPerInt = 1;

  if ( ReqFlags & ACBR_MULTIPLEMODE )
  {
    if ( Flags & ACBF_MULTIPLEMODE )
    {
      npACB->SecPerInt = (npACB->SecToGo > npUCB->SecPerBlk) ?
                                     npUCB->SecPerBlk : npACB->SecToGo;
    }
  }
  /*---------------------------------------------------*/
  /* For READ VERIFY operations there is one interrupt */
  /* when the operation is complete.                   */
  /*---------------------------------------------------*/
  else if ( npACB->ReqFlags & ACBR_VERIFY )
  {
    npACB->SecPerInt = npACB->SecToGo;
  }
  else
  {
     _asm int 3
  }

  npACB->SecReq = npACB->SecToGo;

  npACB->IORegs[FI_PSECCNT] = (UCHAR) npACB->SecToGo;

  /*--------------------------------------------*/
  /* Calculate I/O address. Calculation depends */
  /* on whether we are running the controller   */
  /* in RBA or CHS mode.                        */
  /*--------------------------------------------*/
  if ( npUCB->Flags & UCBF_LBAMODE )
  {
    npACB->IORegs[FI_PSECNUM] = (UCHAR)   CurLBA;
    npACB->IORegs[FI_PCYLL]   = (UCHAR)  (CurLBA >> 8);
    npACB->IORegs[FI_PCYLH]   = (UCHAR)  (CurLBA >> 16);
    npACB->IORegs[FI_PDRHD]   = (UCHAR)  0xE0 | (npACB->UnitId << 4) |
                                          (UCHAR) ((CurLBA >> 24) & 0x0F);

  }
  else
  {
    ADD_ConvRBAtoCHS(CurLBA,
                     &npUCB->PhysGeom,
                     &chs                  );


    npACB->IORegs[FI_PSECNUM] = (UCHAR)  chs.Sector;
    npACB->IORegs[FI_PCYLL]   = (UCHAR)  chs.Cylinder;
    npACB->IORegs[FI_PCYLH]   = (UCHAR) (chs.Cylinder >> 8);
    npACB->IORegs[FI_PDRHD]   = (UCHAR) 0xA0 | (npACB->UnitId << 4)
                                                          | chs.Head;
  }

  npACB->IORegs[FI_PWRP]  = npUCB->WrtPreCmp >> 2;

  if ( ReqFlags & ACBR_READ )
  {
    npACB->IORegs[FI_PCMD] = (Flags & ACBF_MULTIPLEMODE) ? FX_CREADMUL
                                                         : FX_CREAD;
  }
  else if ( ReqFlags & ACBR_WRITE )
  {
    npACB->IORegs[FI_PCMD] = (Flags & ACBF_MULTIPLEMODE) ? FX_CWRITEMUL
                                                         : FX_CWRITE;
  }
  else if ( ReqFlags & ACBR_VERIFY )
  {
    npACB->IORegs[FI_PCMD] = FX_VERIFY;
  }

  /* Begin [001.1] Check/Setup DMA mode for this unit */
  /* Begin [002.1] Remove scatter/gather function for type F slave DMA */
  /*               Add Bus Master DMA capability including scatter/gather */

  /*------------------------------------------------*/
  /* Check for DMA Mode for this Unit                                   */
  /*                                                */
  /*------------------------------------------------*/

  if ( (!InitActive) &&
       (npUCB->Flags & UCBF_BM_DMA) &&
       (ReqFlags & (ACBR_READ | ACBR_WRITE)) )
  {
    /* Check forced PIO mode flag and try to create scatter/gather list */
    if( !(npACB->ReqFlags & ACBR_BM_DMA_FORCEPIO) &&                /*[011.1]*/
        !(CreateBMSGList( npACB )) )
    {
      /* Shut down Bus Master DMA controller if active */

      Port = npACB->BMISTA;           /* Set port address to BM Status reg */
      inp ( Port, Data );

      if ( Data & ACBX_BMISTA_ACTIVE )
      {
         StopBMDMA( npACB );
      }

      ClearBMISTA_INT( Data, npACB );

      if ( ReqFlags & ACBR_READ )
      {
        npACB->BM_CommandCode = (ACBX_BMICOM_RW | ACBX_BMICOM_START);
        npACB->IORegs[FI_PCMD] = FX_DMAREAD;

#ifdef ENABLE_COUNTERS                                              /* [010] */
        ++npUCB->DeviceCounters.TotalBMReadOperations;              /* [010] */
#endif                                                              /* [010] */

      }
      else
      {
        npACB->BM_CommandCode = ACBX_BMICOM_START;
        npACB->IORegs[FI_PCMD] = FX_DMAWRITE;

#ifdef ENABLE_COUNTERS                                              /* [010] */
        ++npUCB->DeviceCounters.TotalBMWriteOperations;             /* [010] */
#endif                                                              /* [010] */

      }

      Port = npACB->BMIDTP;           /* address descriptor base register in PIIX */
      Data = (USHORT) npACB->BMDMA_SGList;
      outwp ( Port, Data );
      Port += 2;
      Data = (USHORT) (npACB->BMDMA_SGList >> 16);
      outwp ( Port, Data );

      /* [006]
       * Bus Master DMA is now ready for the transfer.  It must be started after the
       * command is issued to the drive, to avoid data corruption in case a spurious
       * interrupt occured at the bginning of the xfer and the drive is in an unknown
       * state.
       */

      npACB->ReqFlags |= ACBR_DMAIO;
    } /* if( !CreateBMSGList( npACB ) ) */
  } /* End of handler for Bus Master DMA */
  else if ( (npUCB->Flags & UCBF_DMAMODE)  &&
            (ReqFlags & (ACBR_READ | ACBR_WRITE)))
  {
    npACB->DMAExtMode = npUCB->DMAType;
    if ( (npACB->IOSGPtrs.cSGList == 1) )
    {
      if ( ReqFlags & ACBR_READ )
      {
        npACB->IORegs[FI_PCMD] = FX_DMAREAD;
        npACB->DMAMode = (USHORT) ( DMADemandMode | DMATypeWrite );
      }
      else
      {
        npACB->IORegs[FI_PCMD] = FX_DMAWRITE;
        npACB->DMAMode = (USHORT) ( DMATypeRead | DMADemandMode );
      }
      ProgramDMAController ( npACB );
      npACB->ReqFlags |= ACBR_DMAIO;
    }
  } /* else if ( (npUCB->Flags & UCBF_DMAMODE) */

  /* End [001.1] [002.1] [002.2] */

  npACB->IOPendingMask |= (FM_PCMD | FM_PWRP | FM_PSECNUM | FM_PSECCNT |
                                              FM_PCYLL | FM_PCYLH | FM_PDRHD );
}

/*---------------------------------------------*/
/* InterruptState                              */
/* --------------                              */
/*                                             */
/* Continues an ongoing I/O operation.         */
/*                                             */
/*---------------------------------------------*/
VOID NEAR InterruptState(NPACB npACB)
{
  USHORT        Status;
  USHORT        rc = 0;           // [006] must initialize

  /* Begin [001.2] Add variables needed for DMA interrupt processing */

  USHORT        data;
  USHORT        port;
  ULONG         XferPtr;
  USHORT        DMAAddrPort     = npACB->DMAAddrPort    ;
  USHORT        DMAAddrpagePort = npACB->DMAAddrpagePort;

  /* End [001.2] */

  register int  loop;           /* [005] loop counter for wait BM not active loop */

  if ( npACB->IRQTimerHandle )
  {
     _asm int 3
  }

#ifdef DEBUG
  if( !npACB->pIORB )
  {
     _asm int 3
  }
#endif

  /* Begin [006] */
  if( ( npACB->ReqFlags & ACBR_DMAIO ) &&
      ( npACB->npUCB->Flags & UCBF_BM_DMA ) )
  {
          /* Begin [005] Wait for BM DMA active to go away for a while. */

          port = npACB->BMISTA;
          for( loop=256 ; loop ; --loop )
          {
            inp(port,data);
            if ( !(data & ACBX_BMISTA_ACTIVE) )
            {
              break;
            }
          }

          if( (npACB->Channel ==   NATIVE_PRIMARY_CHANNEL) ||       
              (npACB->Channel == NATIVE_SECONDARY_CHANNEL)  )
          {
             data |= (First_BMIStatus &= 0xFE);
          }                                                         

          /*********************************************************/
          /* See page 80 of the 82371FB PCI ISA IDE Accelerator HW */
          /* reference manual for a description of the error       */
          /* conditions detected below.                            */
          /*********************************************************/

          if( data & ACBX_BMISTA_ACTIVE )
          {

#ifdef DEBUG_DMA_ERROR
              _asm
              {
                 push ax
                 mov ax, 0xAAAA
                 int 3
                 pop ax
              }
#endif

              rc = 1;
          }
          else if( !(data & ACBX_BMISTA_ACTIVE) &&
                   !(data & ACBX_BMISTA_INTERRUPT) )
          {

#ifdef DEBUG_DMA_ERROR
             _asm
              {
                 push ax
                 mov ax, 0xA1A1
                 int 3
                 pop ax
              }
#endif

              rc = 1;
          }

          StopBMDMA( npACB );

          ClearBMISTA_INT( data, npACB );

          /* Begin [004] Fix so that Bus Master errors will definitely
          result in retries */

          inp ( port, data );

          if ( data & ACBX_BMISTA_ERROR )
          {

#ifdef DEBUG_DMA_ERROR
             _asm
             {
                push ax
                mov ax, 0xA2A2
                int 3
                pop ax
             }
#endif

             ClearBMISTA_error( data, npACB );
             rc = 1;
          }
  }

  /*----------------------------------------*/
  /* Check the STATUS Reg for the ERROR bit */
  /*----------------------------------------*/
  Status = GetStatusReg( npACB );

  if( (rc) || (Status & FX_ERROR) )
  {
    npACB->State = ACBS_ERROR;
    npACB->IORegs[FI_PSTAT] = Status;
  }
  else
  {
    NPUCB npUCB = npACB->npUCB;                                     /*@V157085*/
                                                                    /*@V157085*/
    if( !InitActive && (npUCB->Flags & UCBF_REMOVABLE) )            /*@V157085*/
    {                                                               /*@V157085*/
       /*                                                             @V157085
       ** The current command succeeded and the previous command      @V157085
       ** failed so if the current command actually accessed the      @V157085
       ** media then assume the media is back and inform the caller   @V157085
       ** of this event by turning on the media changed bit.  Do      @V157085
       ** not notify the caller until the current operation is an     @V157085
       ** operation which actually accesses the media.                @V157085
       */                                                           /*@V157085*/
       DISABLE                                                      /*@V157085*/
       if( !(npUCB->Flags & UCBF_READY) )                           /*@V157085*/
       {                                                            /*@V157085*/
          USHORT CmdCode     = npACB->pIORB->CommandCode;           /*@V157085*/
          USHORT CmdModifier = npACB->pIORB->CommandModifier;       /*@V157785*/
                                                                    /*@V157785*/
          switch( CmdCode )                                         /*@V157085*/
          {                                                         /*@V157085*/
          case IOCC_EXECUTE_IO:                                     /*@V157085*/
             switch( CmdModifier )                                  /*@V157085*/
             {                                                      /*@V157085*/
             case IOCM_READ:                                        /*@V157085*/
             case IOCM_READ_VERIFY:                                 /*@V157085*/
             case IOCM_WRITE:                                       /*@V157085*/
             case IOCM_WRITE_VERIFY:                                /*@V157085*/
                npUCB->Flags |= (UCBF_MEDIA_CHANGED & UCBF_READY);  /*@V157085*/
                break;                                              /*@V157085*/
             default:                                               /*@V157085*/
                break;                                              /*@V157085*/
             }                                                      /*@V157085*/
          default:                                                  /*@V157085*/
             break;                                                 /*@V157085*/
          }                                                         /*@V157085*/
       } /* endif */                                                /*@V157085*/
       ENABLE                                                       /*@V157085*/
    }                                                               /*@V157085*/

    /*--------------------------------------------*/
    /* Handle Other Operations                    */
    /*      Recal, SetParam, Identify             */
    /*--------------------------------------------*/
    if( npACB->ReqFlags & ACBR_OTHERIO )
    {
      DoOtherIO( npACB );

      {                                                             /*@V162789*/
         USHORT CmdCode     = npACB->pIORB->CommandCode;            /*@V162789*/
         USHORT CmdModifier = npACB->pIORB->CommandModifier;        /*@V162789*/
                                                                    /*@V162789*/
         switch( CmdCode )                                          /*@V162789*/
         {                                                          /*@V162789*/
         case IOCC_UNIT_STATUS:                                     /*@V162789*/
            switch( CmdModifier )                                   /*@V162789*/
            {                                                       /*@V162789*/
            case IOCM_GET_UNIT_STATUS:                              /*@V162789*/
               /*                                                     @V162789
               ** Interpret the device's current status.              @V162789
               */                                                   /*@V162789*/
               GetUnitStatus( npACB, Status );                      /*@V162789*/
               break;                                               /*@V162789*/
                                                                    /*@V162789*/
            default:                                                /*@V162789*/
               break;                                               /*@V162789*/
            }                                                       /*@V162789*/
                                                                    /*@V162789*/
         default:                                                   /*@V162789*/
            break;                                                  /*@V162789*/
         }                                                          /*@V162789*/
      }
    }

    /*----------------------*/
    /* Handle Block I/O Ops */
    /*----------------------*/
    else if ( npACB->ReqFlags & ACBR_BLOCKIO )
    {

      /* Begin [001.2] [002.2] Add handling for DMA I/O operations */
      /*    Modified to add Bus Master DMA shutdown when command completes */
      /*    Interrupt routine now supports two separate shutdown procedures */
      /*    for slave and Bus Master DMA */

      if ( npACB->ReqFlags & ACBR_DMAIO )
      {
        if( !( npACB->npUCB->Flags & UCBF_BM_DMA ) )
        {
          data = ( npACB->DMAChannel | 0x0004 );
          port = ( npACB->DMAMaskPort );
          outp ( port, data );
/* [002.2] Move to common spot in conditional          rc = 0; */
  #ifdef BETA
          _asm
          {
              push      ax
              push      dx
              push      bx
              mov       dx, DMAAddrPort
              in        al, dx
              mov       ah, al
              in        al, dx
              xchg      ah, al
              mov       bx, ax
              shl       ax, 1
              shr       bx, 15
              mov       word ptr XferPtr, ax
              mov       dx, DMAAddrpagePort
              in        al, dx
              and       ax, 0FEH
              or        ax, bx
              or        ax, 1000h
              mov       word ptr XferPtr+2, ax

              pop       bx
              pop       dx
              pop       ax
          }
          if ((ULONG)(npACB->DMASGList[0].ppXferBuf+npACB->DMASGList[0].XferBufLen +1)
                      != XferPtr)
          {
            _asm
            {
                mov ax, 55h
                mov bx, 0aah
                int 3
            }
          }
#endif
        } /* End of handler for slave DMA interrupt processing */
        npACB->SecToGo = 0;
        npACB->ReqFlags &= ~ACBR_DMAIO; /* reset DMA XFER in progress */
        rc = 0;
      } /* if ( npACB->ReqFlags & ACBR_DMAIO ) */
      else
      {
        rc = DoBlockIO( npACB, npACB->SecPerInt );
      }

      /* End [001.2] [002.2] */

      if ( !rc && !npACB->SecToGo )
      {
        npACB->SecRemain -= npACB->SecReq;
        npACB->SecDone   += npACB->SecReq;

        /*----------------------------------------------------*/
        /* If the current operation is complete but there     */
        /* are additional sectors remaining. Start the        */
        /* next part of the operation.                        */
        /*----------------------------------------------------*/

        if ( npACB->SecRemain )
        {
          StartBlockIO( npACB );
        }
        else
        {
          /*--------------------------------------------*/
          /* For WRITE VERIFY operations, start the     */
          /* verify part of the operation.              */
          /*--------------------------------------------*/

          if ( npACB->ReqFlags & ACBR_WRITEV )
          {
            InitBlockIO( npACB );
            npACB->ReqFlags &= ~(ACBR_WRITE | ACBR_WRITEV);
            npACB->ReqFlags |=  ACBR_VERIFY;
            npACB->State     = ACBS_RETRY;
          }
          else
          {
            if(!(npACB->ReqFlags & ACBR_PASSTHRU))                           /*@V151345*/
              {                                                              /*@V151345*/
              ((PIORB_EXECUTEIO) npACB->pIORB)->BlocksXferred = npACB->SecDone;
              } /* endif */                                                  /*@V151345*/
            npACB->State = ACBS_DONE;
          }
        }
      }
    }
    else
    {
       _asm int 3
    }

  }

}

/*---------------------------------------------*/
/* DoBlockIO                                   */
/* ---------                                   */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

USHORT NEAR DoBlockIO( NPACB npACB, USHORT cSec )
{
  USHORT        rc = 0;

  npACB->SecToGo -= cSec;

  /*---------------------------------------------------*/
  /* Servicing of DRQ is not needed for READ Verify or */
  /* the last interrupt of a Write operation.          */
  /*---------------------------------------------------*/

  if ( !((npACB->ReqFlags & ACBR_VERIFY) ||
        ((npACB->ReqFlags & ACBR_WRITE) && !npACB->SecToGo)) )
  {
    if ( !WaitDRQ( npACB ) )
    {
      /*----------------------------------------------------*/
      /* If there will be additional interrupts after       */
      /* this one, start the IRQ timer and set the IRQ flag */
      /*----------------------------------------------------*/
      if ( npACB->SecToGo )
      {
        if ( ADD_StartTimerMS((PULONG) &npACB->IRQTimerHandle,
                              (ULONG)  npACB->IRQTimeOut,
                              (PFN)    IRQTimer,
                              (PVOID)  npACB,
                              (ULONG)  0                ) )
        {
           _asm int 3
        }
        npACB->Flags |= (ACBF_INTERRUPT | ACBF_WAITSTATE);
      }


      if ( npACB->ReqFlags & ACBR_WRITE )
      {
        if ( npACB->SecToGo < npACB->SecPerInt )
        {
          npACB->SecPerInt = npACB->SecToGo;
        }
      }

      npACB->IOSGPtrs.numTotalBytes = ((ULONG) npACB->SecPerInt << SECTORSHIFT);

#ifdef CMD640X
      if (npACB->Flags & ACBF_16BIT)
        ADD_XferIOW( &npACB->IOSGPtrs );
      else
        ADD_XferIOD( &npACB->IOSGPtrs );

#else
      ADD_XferIOW( &npACB->IOSGPtrs );
#endif /* CMD640X */


      if ( npACB->ReqFlags & ACBR_READ )
      {
        if ( npACB->SecToGo < npACB->SecPerInt )
        {
          npACB->SecPerInt = npACB->SecToGo;
        }
      }

    }
    else
    {
      /*----------------------------------------------------*/
      /* DRQ Timeout                                        */
      /*                                                    */
      /* WaitDRQ will has set the new state and error info. */
      /* This just returns a flag to the caller to stop     */
      /* processing.                                        */
      /*----------------------------------------------------*/
      rc = 1;
    }
  }
  return ( rc );
}

/*---------------------------------------------*/
/* DoOtherIO                                   */
/* ---------                                   */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/
VOID NEAR DoOtherIO( NPACB npACB )
{
  NPUCB         npUCB = npACB->npUCB;

  npACB->ReqFlags  &= npACB->ReqMask;
  npUCB->ReqFlags  &= npACB->ReqMask;

  /* [007] moved next statement here from end of routine */

  npACB->State = ( npACB->ReqFlags & ~(ACBR_NONDATA | ACBR_PASSTHRU))  ? ACBS_RETRY : ACBS_DONE;
                                                                                /*@V151345*/
  if ( npACB->ReqMask == ~ACBR_SETMULTIPLE )                                    /*@V151345*/
  {
    npACB->Flags |= ACBF_MULTIPLEMODE;
    npUCB->Flags |= UCBF_MULTIPLEMODE;
  }

}

/*---------------------------------------------*/
/* DoneState                                   */
/* ---------                                   */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/
VOID NEAR DoneState(NPACB npACB)
{
  PIORB pIORB = npACB->pIORB;

  /*------------------------------------------------------------*/
  /* Reset the drive back to its original INT 13 parms if the   */
  /* BIOS is still active.                                      */
  /*                                                            */
  /* Note:                                                      */
  /* -----                                                      */  /*@V117435*/
  /* Normally the physical drive geometry would be set to       */  /*@V117435*/
  /* the Int13 geometry. This reset is done for the benefit     */  /*@V117435*/
  /* of some ESDI BIOSes which expect the drive geometry to be  */  /*@V117435*/
  /* set to its logical (Int13 geometry) even though            */  /*@V117435*/
  /* there is a different physical geometry.                    */  /*@V117435*/
  /*                                                            */  /*@V117435*/
  /* If the drive required translate support (>16 heads)        */  /*@V117435*/
  /* we leave the drive set at the physical geometry we         */  /*@V117435*/
  /* determined.                                                */  /*@V117435*/
  /*                                                            */  /*@V117435*/
  /* In the case of Ontrack, we may also have a different       */  /*@V117435*/
  /* physical geometry than Int13 geometry, however, in         */  /*@V117435*/
  /* this case, we want to leave the drive at its physical      */  /*@V117435*/
  /* (Identify) geometry for compatibility with Ontrack's       */  /*@V117435*/
  /* XBIOS extensions.                                          */  /*@V117435*/
  /*------------------------------------------------------------*/  /*@V117435*/


  if(!(npACB->ReqFlags & ACBR_PASSTHRU))                              /*@V151345*/
    {                                                                 /*@V151345*/
    if ( BIOSActive && InitIOComplete
          && !(npACB->Flags & ACBF_DISKPARMRESET)
          && !(npACB->npUCB->Flags & UCBF_ONTRACK)                    /*@V117435*/
          && !(npACB->npUCB->Flags & UCBF_ATAPIDEVICE)                /*@V147576*/
          && (npACB->npUCB->LogGeom.NumHeads <= 16) )
    {
      npACB->ReqFlags = ACBR_SETLOGPARAM;
      npACB->State    = ACBS_RETRY;
      return;
    }
    }                                                                 /*@V151345*/


  /*---------------------------------------------------------*/     /*@V117435*/
  /* If we did reset the disk geometry to its BIOS settings  */     /*@V117435*/
  /* remember to reset the geometry to the driver's settings */     /*@V117435*/
  /* the next time the unit is accessed.                     */     /*@V117435*/
  /*---------------------------------------------------------*/     /*@V117435*/
  if ( npACB->Flags & ACBF_DISKPARMRESET )                          /*@V117435*/
  {                                                                 /*@V117435*/
    npACB->npUCB->ReqFlags |= (UCBR_RECAL | UCBR_SETPARAM);         /*@V117435*/
  }                                                                 /*@V117435*/
  else                                                              /*@V117435*/
  {
    npACB->npUCB->ReqFlags |= (npACB->ReqFlags & (UCBR_RECAL | UCBR_SETPARAM));
  }

  /* Begin [007] Transfer of register data for passthru command */

  if ( npACB->ReqFlags & ACBR_PASSTHRU)                             /*@V151345*/
  {
    USHORT i,IOMask, Port, Data;
    PBYTE                   preg;
    PPassThruATA            picp;
    PIORB_ADAPTER_PASSTHRU  piorb_pt;

    npACB->ReqFlags = npACB->npUCB->ReqFlags = 0;         /* don't run this code again */
                                                                    /*@V151345*/

    npACB->ReqMask = 0;                                   /* don't run this code again */

    piorb_pt = (PIORB_ADAPTER_PASSTHRU) npACB->pIORB;
    picp = (PPassThruATA) piorb_pt->pControllerCmd;

    if(HIUSHORT(picp))                                              /*@V151345*/
      {                                                             /*@V151345*/
      IOMask = (picp->RegisterTransferMap & PTA_RTMRD_REGMASK) >>
                PTA_RTMRD_SHIFT;
      preg = (PBYTE) &(picp->TaskFileOut);

      for ( i = FI_PWRP; IOMask; i++ )
      {
        IOMask >>= 1;

        if ( IOMask & 0x0001 )
        {
          Port = npACB->IOPorts[i];
          inp( Port, Data);
          preg[i-1] = (BYTE) Data;
          IODelay();
        }
      }
      } /* endif */                                                 /*@V151345*/

  }

  /* End [007] */

  if ( npACB->Flags & ACBF_PS2IDEPORT  )
  {
    SetPS2DiskLight( 0 );
  }

  // All done with HW, so OK to free HW resources.
  FreeHWResources( npACB );                                         /*@V147576*/

  IORBDone( npACB, pIORB );                                         /*@V147576*/

  if ( npACB->IRQTimerHandle )
  {
     _asm int 3
  }
  if ( npACB->RetryTimerHandle )
  {
     _asm int 3
  }

  if ( npACB->UseCount != 1 )
  {
     _asm int 3
  }

  npACB->State = ACBS_START;                                        /*@V147576*/

  return;
}

/*---------------------------------------------*/
/* ErrorState                                  */
/* ----------                                  */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/
VOID NEAR ErrorState(NPACB npACB)
{
  USHORT        Reset = 0;
  USHORT        port, data;       /*[011.2]*/


  DISABLE
  if ( npACB->IRQTimerHandle )
  {
    ADD_CancelTimer( npACB->IRQTimerHandle);

    npACB->IRQTimerHandle  = 0;
    npACB->Flags          &= ~(ACBF_INTERRUPT | ACBF_MULTIPLEMODE);  /*@V77133*/
  }

  npACB->UseCount = 1;

  ENABLE

  npACB->IORBStatus |= IORB_RECOV_ERROR;
  npACB->IORBError   = MapError( npACB );

  if ( (npACB->TimerFlags & (ACBT_IRQ | ACBT_DRQ | ACBT_RESETFAIL)) )/*@V85057*/
  {
    Reset = 1;
  }
  else if ( npACB->TimerFlags & ACBT_BUSY )                          /*@V85057*/
  {                                                                  /*@V85057*/
    if ( !(npACB->BusyTimeoutCnt % MAX_BUSY_TIMEOUTS) )              /*@V85057*/
    {                                                                /*@V85057*/
      Reset = 1;                                                     /*@V85057*/
    }                                                                /*@V85057*/
  }                                                                  /*@V85057*/
  else if ( !(npACB->IORegs[FI_PSTAT] & FX_READY) )
  {
    Reset = 1;
  }
  else if ( !(npACB->DataErrorCnt % 10) )
  {
    Reset = 1;
  }

  /* Begin [011.2]. If a bus master DMA xfer timed out, we must */
  /* deactivate the bus master IDE controller and force a retry in PIO mode. */

  if( (npACB->ReqFlags & ACBR_DMAIO) && (npACB->npUCB->Flags & UCBF_BM_DMA) ) /*@V179942*/
  {                                                                           /*VVVVVVVV*/
     if((npACB->DataErrorCnt == 1) &&
                                   (npACB->IORBError == IOERR_DEVICE_ULTRA_CRC))
     {
        /**********************************************/
        /* Retry the Ultra DMA operation one (1) time */
        /**********************************************/
     } else {
        port = npACB->BMISTA;
        inp( port, data );

        if ( data & ACBX_BMISTA_ACTIVE )
        {
           StopBMDMA( npACB );      /* controller is locked, Clear Active bit *//*AAAAAAAA*/
                                                                                /*@V179942*/
           /*******************************************************/
           /* Changed DMA_FORCEPIO flag from an ACB Flags option  */
           /* to an ACB ReqFlags option as this mechanism should  */
           /* remain on for the remainder of the I/O operation but*/
           /* be reset at the beginning of the next.  ReqFlags is */
           /* always reset at the beginning of each request.      */
           /*******************************************************//*@V159438*/

           /***************************/
           /* force retry in PIO mode */
           /***************************/

           npACB->ReqFlags |= (ACBR_BM_DMA_FORCEPIO | ACBR_SETMULTIPLE); /*@V159438*/
        }
        npACB->ReqFlags &= ~ACBR_DMAIO;       /* clear DMA IO flag */
     }
  }

  npACB->TimerFlags = 0;

  if ( Reset && !(npACB->Flags & ACBF_DISABLERESET) &&            /*@V77133*/
       !(npACB->npUCB->Flags & UCBF_ATAPIDEVICE ) )               /*@V87325*/
  {
    DoReset( npACB );
  }
  else if( (npACB->npUCB->Flags & UCBF_REMOVABLE) &&                /*@V157085*/
           ((npACB->IORBError == IOERR_UNIT_NOT_READY) ) )          /*@V157085*/
  {                                                                 /*@V157085*/
     npACB->IORBStatus = IORB_ERROR;                                /*@V157085*/
     npACB->State = ACBS_DONE;                                      /*@V157085*/
  }                                                                 /*@V157085*/
  else if( (npACB->npUCB->Flags & UCBF_REMOVABLE) &&                /*@V157085*/
           (npACB->IORBError == IOERR_MEDIA_CHANGED) )              /*@V157085*/
  {                                                                 /*@V157085*/
     npACB->State = ACBS_DONE;                                      /*@V157085*/
  }                                                                 /*@V157085*/
  else
  {
     SetRetryState( npACB );
  }
}

/*---------------------------------------------*/
/*                                             */
/* SetRetryState                               */
/* -------------                               */
/*                                             */
/* This routine determines the next state      */
/* after we handled an error or completed      */
/* a reset.                                    */
/*                                             */
/*---------------------------------------------*/

VOID NEAR SetRetryState(NPACB npACB)
{
  ULONG         ElapsedTime;

  if ( !npACB->ReqFlags )
  {
    npACB->State = ACBS_DONE;
    return;
  }

  npACB->ReqFlags |= ACBR_SETPARAM;

  if ( npACB->Flags & ACBF_MULTIPLEMODE )                            /*@V77133*/
  {                                                                  /*@V77133*/
    npACB->ReqFlags |= ACBR_SETMULTIPLE;                             /*@V77133*/
  }                                                                  /*@V77133*/

  /*---------------------------------*/
  /* Complete this request if:       */
  /*                                 */
  /*  - Retries disabled             */
  /*  - Max elapsed time expired     */
  /*  - The unit failed after reset  */
  /*---------------------------------*/
  DISABLE
  ElapsedTime = npACB->ElapsedTime;
  ENABLE

  if ( npACB->Flags & ACBF_DISABLERETRY            ||
         ElapsedTime > npACB->TimeOut              ||
           npACB->DataErrorCnt > MAX_DATA_ERRORS   ||
             npACB->npUCB->Flags & UCBF_DIAG_FAILED   )
  {
    npACB->IORBStatus &= ~IORB_RECOV_ERROR;
    npACB->IORBStatus |=  IORB_ERROR;
    npACB->State       =  ACBS_DONE;
  }

  /*-----------------------------------*/
  /* Otherwise                         */
  /*                                   */
  /*  -Reset the S/G buffer pointers   */
  /*   to where they were at the start */
  /*   of the operation                */
  /*                                   */
  /*  -Schedule a retry                */
  /*-----------------------------------*/

  else
  {
    if ( npACB->ReqFlags & ACBR_BLOCKIO )
    {
      UpdateBlockIOPtrs( npACB );
    }
                                                                  /* @v181721 */
    if ( ADD_StartTimerMS((PULONG) &npACB->RetryTimerHandle,
                          (ULONG)  npACB->DelayedRetryInterval,
                          (PFN)    DelayedRetry,
                          (PVOID)  npACB,
                          (ULONG)  0              ) )
    {
       _asm int 3
    }
    npACB->Flags |= ACBF_WAITSTATE;
  }
}

/*---------------------------------------------*/
/* DoReset                                     */
/* -------                                     */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

VOID NEAR DoReset( NPACB npACB )
{

  USHORT        DiagCode;
  USHORT        i;

  DISABLE
  if ( npACB->ResetTimerHandle )
  {
    ENABLE
    return;
  }
  ENABLE

  npACB->Flags &= ~(ACBF_INTERRUPT | ACBF_MULTIPLEMODE);             /*@V77133*/

  /*---------------------------------------------*/
  /* Set the RECAL/SETPARAM request flags in all */
  /* UCBs.                                       */
  /*---------------------------------------------*/

  if ( npACB->ReqMask != ~ACBR_RESETCONTROLLER )
  {
    for ( i=0; i < npACB->cUnits; i++ )
    {
#ifdef CMD640X
      npACB->UnitCB[i].ReqFlags |= (UCBR_RECAL | UCBR_SETPARAM | UCBR_SETPIOMODE);
#else
      npACB->UnitCB[i].ReqFlags   |= (UCBR_RECAL | UCBR_SETPARAM);
#endif /* CMD640X */
      npACB->UnitCB[i].Flags      &= ~(UCBF_MULTIPLEMODE |           /*@V77133*/
                                       UCBF_DIAG_FAILED);            /*@V77133*/
      npACB->UnitCB[i].DiagStatus =  0;                              /*@V77133*/
    }

#ifdef CMD640X
    npACB->ReqFlags |= (ACBR_RECAL | ACBR_SETPARAM | ACBR_SETPIOMODE);
#else
    npACB->ReqFlags |= (ACBR_RECAL | ACBR_SETPARAM);
#endif /* CMD640X */
  }

  /*------------------------------------*/
  /* Issue the RESET to the controller  */
  /*------------------------------------*/
  npACB->cResets++;                                                  /*@V77133*/
  SendReset( npACB );

  npACB->DelayedResetCtr = (DELAYED_RESET_MAX / DELAYED_RESET_INTERVAL);

  /*------------------------------------*/
  /* Wait for the controller to recover */
  /* and check the diagnostic code.     */
  /*                                    */
  /* Disable drives which failed to     */
  /* recover.                           */
  /*------------------------------------*/
  npACB->State  = ACBS_RESETCHECK;
}

/*---------------------------------------------*/
/*                                             */
/* ResetCheck                                  */
/* ----------                                  */
/*                                             */
/* This routine is called to check if a reset  */
/* has completed and determines what to do     */
/* next.                                       */
/*                                             */
/*---------------------------------------------*/
VOID NEAR ResetCheck( NPACB npACB )
{
  USHORT    ResetComplete = 1;                                       /*@V77133*/
  UCHAR     Status;                                                 /*@V195085*/

  /*-----------------------------------*/
  /* See if we should continue         */
  /* waiting for the reset to complete */
  /*-----------------------------------*/
  if ( npACB->DelayedResetCtr )
  {
    npACB->DelayedResetCtr--;

    /*-----------------------------------------*/
    /* If diagnostic results are not available */
    /* schedule another retry                  */
    /*-----------------------------------------*/
    if( GetDiagResults( npACB, &Status ) )                          /*@V195085*/
    {
      if( Status == 0xff )                                          /*@V195085*/
      {                                                             /*@V195085*/
         // Controller is gone, more retries are not going to help. /*@V195085*/
         // So stop retrying and report the error.                  /*@V195085*/
         npACB->DelayedResetCtr = 0;                                /*@V195085*/
         npACB->TimerFlags |= ACBT_RESETFAIL;                       /*@V195085*/
         npACB->State = ACBS_DONE;                                  /*@V195085*/
      }                                                             /*@V195085*/
      else                                                          /*@V195085*/
      {                                                             /*@V195085*/
        if ( ADD_StartTimerMS((PULONG) &npACB->ResetTimerHandle,
                              (ULONG)  npACB->DelayedResetInterval,
                              (PFN)    DelayedReset,
                              (PVOID)  npACB,
                              (ULONG)  0              ) )
        {
          _asm int 3
        }
        npACB->Flags |= ACBF_WAITSTATE;
      }                                                             /*@V195085*/

      ResetComplete = 0;                                             /*@V77133*/
    }                                                                /*@V77133*/
  }                                                                  /*@V77133*/
  else                                                               /*@V77133*/
  {                                                                  /*@V77133*/
    npACB->TimerFlags |=  ACBT_RESETFAIL;                            /*@V77133*/
  }                                                                  /*@V77133*/
                                                                     /*@V77133*/
  /*---------------------------------------------------------*/      /*@V77133*/
  /* If the RESET is complete but failed:                    */      /*@V77133*/
  /*   Do the reset again until the reset count is exhausted */      /*@V77133*/
  /*   Otherwise retry or fail the original request          */      /*@V77133*/
  /*---------------------------------------------------------*/      /*@V77133*/
  if ( ResetComplete )                                               /*@V77133*/
  {                                                                  /*@V77133*/
    npACB->ReqFlags   &= ~ACBR_RESETCONTROLLER;                      /*@V77133*/
                                                                     /*@V77133*/
    if ( npACB->TimerFlags & ACBT_RESETFAIL )                        /*@V77133*/
    {                                                                /*@V77133*/
      if ( (npACB->cResets < MAX_RESET_RETRY) && !InitActive )       /*@V77133*/
      {                                                              /*@V77133*/
        npACB->State  =  ACBS_ERROR;                                 /*@V77133*/
      }                                                              /*@V77133*/
      else                                                           /*@V77133*/
      {                                                              /*@V77133*/
        SetRetryState( npACB );                                      /*@V77133*/
      }                                                              /*@V77133*/
    }                                                                /*@V77133*/
    else                                                             /*@V77133*/
    {                                                                /*@V77133*/
      SetRetryState( npACB );                                        /*@V77133*/
    }                                                                /*@V77133*/
  }                                                                  /*@V77133*/
}

/*----------------------------------------------------*/
/*                                                    */
/* GetDiagResults                                     */
/* --------------                                     */
/*                                                    */
/* This routine is called after a controller          */
/* reset is started to collect diagnostic             */
/* results from the controller                        */
/*                                                    */
/* Return 0 if reset and diagnostics are complete.    */
/* Return 1 if reset and diagnostics are incomplete.  */
/*                                                    */
/* Always return the contents of the status register. */
/*                                                    */
/*----------------------------------------------------*/
USHORT NEAR GetDiagResults( NPACB npACB, PUCHAR Status )            /*@V195085*/
{
  USHORT    rc = 0;
  USHORT    DiagCode;

  /*------------------------------------*/
  /* If the controller is ready to talk */
  /* to us!                             */
  /*------------------------------------*/
  npACB->IORegs[FI_PSTAT] = *Status = GetStatusReg( npACB );        /*@V195085*/

  // Some controllers with no devices attached report all 1's in    /*@V195085*/
  // in status register.  If this is the case, the device is not    /*@V195085*/
  // going to come ready or report any meaningful status            /*@V195085*/
  if ( !(*Status & FX_BUSY) )                                       /*@V195085*/
  {
    DiagCode = GetErrorReg( npACB, 0 );

    /*------------------------------------------------*/
    /* Error code indicates one or both drives failed */
    /*------------------------------------------------*/
    if ( DiagCode != FX_DIAG_PASSED )
    {
      /*-------------------------------------*/
      /* Error code indicates Drive 0 failed */
      /*-------------------------------------*/
      if ( (DiagCode & 0x0f) != FX_DIAG_PASSED )
      {
        npACB->UnitCB[0].Flags |= UCBF_DIAG_FAILED;
        npACB->UnitCB[0].DiagStatus = DiagCode & 0x0f;

        npACB->TimerFlags |= ACBT_RESETFAIL;                         /*@V85057*/
      }

      /*-------------------------------------*/
      /* Error code indicates Drive 1 failed */
      /*-------------------------------------*/
      if ( DiagCode & FX_DIAG_DRIVE1 )
      {
        DiagCode = GetErrorReg( npACB, 1 );

        if ( DiagCode != FX_DIAG_PASSED)
        {
          npACB->UnitCB[1].Flags |= UCBF_DIAG_FAILED;
          npACB->UnitCB[1].DiagStatus = DiagCode & 0x0f;

          /*---------------------------------------*/                /*@V85057*/
          /* Connor drives used in IBM L40SX mark  */                /*@V85057*/
          /* non-installed Unit 1 as defective in  */                /*@V85057*/
          /* diagnostic results.                   */                /*@V85057*/
          /*---------------------------------------*/                /*@V85057*/
          if ( npACB->cUnits > 1 )                                   /*@V85057*/
          {                                                          /*@V85057*/
            npACB->TimerFlags |= ACBT_RESETFAIL;                     /*@V85057*/
          }                                                          /*@V85057*/
        }
      }
    }
  }
  else
  {
    rc = 1;
  }

  return( rc );
}


/*---------------------------------------------*/
/* UpdateBlockIOPtrs                           */
/* -----------------                           */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/
VOID NEAR UpdateBlockIOPtrs( NPACB npACB )
{
  npACB->IOSGPtrs.iSGList  = npACB->IOSGPtrs.iSGListStart;
  npACB->IOSGPtrs.SGOffset = npACB->IOSGPtrs.SGOffsetStart;
}

/*---------------------------------------------*/
/* SendCmdPacket                               */
/* -------------                               */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/
USHORT NEAR SendCmdPacket( NPACB npACB )
{
  USHORT        IOMask;
  USHORT        Port;
  USHORT        Data;
  USHORT        i;

  IOMask = npACB->IOPendingMask;

  npACB->IOPendingMask = 0;

  /*------------------------------------------*/                     /*@V77133*/
  /* Insure drive status is not BUSY prior to */                     /*@V77133*/
  /* selecting drive                          */                     /*@V77133*/
  /*------------------------------------------*/                     /*@V77133*/
                                                                     /*@V77133*/
  if ( CheckReady( npACB ) && npACB->TimerFlags & ACBT_BUSY )        /*@V77133*/
  {                                                                  /*@V77133*/
    goto Send_Error;                                                 /*@V77133*/
  }                                                                  /*@V77133*/

  if ( IOMask & FM_PDRHD )
  {
    IOMask &= ~FM_PDRHD;
    Port = npACB->IOPorts[FI_PDRHD];
    Data = npACB->IORegs[FI_PDRHD];
    outp( Port, Data);
    IODelay();

    if ( CheckReady( npACB ) )
    {
                                                                     /*@V87325*/
      /* ATAPI Devices are not ready until they recieve an ATAPI */  /*@V87325*/
      /* command                                                 */  /*@V87325*/
                                                                     /*@V87325*/
      if ( !(npACB->npUCB->Flags & UCBF_ATAPIDEVICE ) )              /*@V87325*/
      {                                                              /*@V87325*/
        goto Send_Error;
      }                                                              /*@V87325*/
    }
  }                                                                  /*@V87325*/

  /*                                                                  @V162458
  ** About to write the command register which will sooner            @V162458
  ** or later cause an interrupt.  If we are currently running        @V162458
  ** on the interrupt stack, disable interrupts and return to         @V162458
  ** to kernel.  The kernel will enable interrupts at the RETI.       @V162458
  ** This prevents an interrupt stack overflow caused by the          @V162458
  ** HW re-interrupting before the driver and kernel have done        @V162458
  ** the RETI.                                                        @V162458
  */                                                                /*@V162458*/
  if( npACB->AtInterrupt )                                          /*@V162458*/
  {                                                                 /*@V162458*/
     DISABLE;                                                       /*@V162458*/
  }                                                                 /*@V162458*/

  for ( i = FI_PWRP; IOMask; i++ )
  {
    IOMask >>= 1;

    if ( IOMask & 0x0001 )
    {
      Port = npACB->IOPorts[i];
      Data = npACB->IORegs[i];

#ifdef DEBUG_WATCH_IDE_COMMANDS
      // Show IDE commands.
      if( Port == npACB->IOPorts[FI_PCMD] )
      {
         _asm
         {
            push ax
            push bx
            mov  ax, Port
            mov  bx, Data
            int 3
            pop  bx
            pop  ax
         }
      }
#endif
      outp( Port, Data);
      IODelay();
    }
  }

  return( 0 );

  Send_Error:
    npACB->State = ACBS_ERROR;
    npACB->Flags &= ~ACBF_INTERRUPT;

    return( 1 );
}


/*---------------------------------------------*/
/* WaitDRQ                                     */
/* -------                                     */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/
USHORT NEAR WaitDRQ( NPACB npACB )
{
  ULONG         WaitDRQCtr;
  USHORT        rc          = 0;
  USHORT        Status;

  npACB->TimerFlags &= ~(ACBT_DRQ | ACBT_BUSY);

  WaitDRQCtr = WaitDRQCount;

  while ( --WaitDRQCtr )
  {
    Status = GetStatusReg( npACB );

    if ( !(Status & FX_BUSY) )
    {
      if ( Status & (FX_ERROR | FX_WRTFLT | FX_DRQ) && !Calibrate )
        break;
    }
  }

  npACB->IORegs[FI_PSTAT] = Status;

  if ( !WaitDRQCtr )
  {
    npACB->TimerFlags |= ((Status & FX_BUSY) ? ACBT_BUSY : ACBT_DRQ);
    rc = 1;
  }
  else if ( Status & (FX_ERROR | FX_WRTFLT) )
  {
    rc = 1;
  }

  if ( rc )
  {
    npACB->State  = ACBS_ERROR;
  }

  return ( rc );
}

/*---------------------------------------------*/
/* CheckReady                                  */
/* ----------                                  */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/
USHORT NEAR CheckReady( NPACB npACB )
{
  ULONG         WaitCount;
  USHORT        Status;

  npACB->TimerFlags &= ~ACBT_BUSY;

  WaitCount = CheckReadyCount;

  while( --WaitCount )
  {
    Status = GetStatusReg( npACB );

    if ( !(Status & FX_BUSY) && (Status & FX_READY) )
    {
      if ( !Calibrate )
        break;
    }
  }

  if ( Status & FX_BUSY )
  {
    npACB->TimerFlags |= ACBT_BUSY;
  }

  npACB->IORegs[FI_PSTAT] = Status;

  return ( ((npACB->TimerFlags & ACBT_BUSY) || !(Status & FX_READY)) ? 1 : 0 );
}


/*---------------------------------------------*/
/* SendReset                                   */
/* ----------                                  */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/
USHORT NEAR SendReset( NPACB npACB )
{
  USHORT        Port;
  USHORT        Data;

  npACB->Flags &= ~ACBF_INTERRUPT;

  SelectUnit( npACB, 0 );

  Port = npACB->IOPorts[FI_RFDR];
  Data = 0x08 | FX_SRST;

  outp( Port, Data );

  IODelay();
  IODelay();

  npACB->IORegs[FI_RFDR] = Data = 0x08;
  outp( Port, Data );

  IODelay();
  IODelay();
}


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
VOID NEAR SelectUnit( NPACB npACB, USHORT UnitId )
{
  USHORT        Port;
  USHORT        Data;

  Port = npACB->IOPorts[FI_PDRHD];
  Data = (npACB->IORegs[FI_PDRHD] &= ~0x0010);
  npACB->IORegs[FI_PDRHD] = (Data |= (UnitId << 4));

  outp( Port, Data );
  IODelay();
}


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
USHORT NEAR GetStatusReg( NPACB npACB )
{
  USHORT data, port;

  port = npACB->IOPorts[FI_PSTAT];

  inp( port, data );

  return( data );
}


/*----------------------------------------------------------*/      /*@V157085*/
/*  SendAckMediaChange()                                    */      /*@V157085*/
/*                                                          */      /*@V157085*/
/*  Send the drive a Media Change Acknowledge command to    */      /*@V157085*/
/*  clear the error bit (ERR) in the Status Register and    */      /*@V157085*/
/*  clear media changed (MC) bit in the Error Register.     */      /*@V157085*/
/*                                                          */      /*@V157085*/
/*  This is done as a synchronious operation (with          */      /*@V157085*/
/*  interrupts disabled) because the command is vendor      */      /*@V157085*/
/*  specific and is also, hopefully, a relatively  rare     */      /*@V157085*/
/*  command.                                                */      /*@V157085*/
/*                                                          */      /*@V157085*/
/*----------------------------------------------------------*/      /*@V157085*/
VOID SendAckMediaChange( NPACB npACB )                              /*@V157085*/
{                                                                   /*@V157085*/
   USHORT        Port;                                              /*@V157085*/
   USHORT        Data;                                              /*@V157085*/
                                                                    /*@V157085*/
   /* Turn off interrupts, temporarily */                           /*@V157085*/
   Data = npACB->IORegs[FI_RFDR] = FX_nIEN;                         /*@V157085*/
   Port = npACB->IOPorts[FI_RFDR];                                  /*@V157085*/
   outp(Port, Data);                                                /*@V157085*/
   IODelay();                                                       /*@V157085*/
                                                                    /*@V157085*/
   /* Write the Acknowledge Media Change command */                 /*@V157085*/
   Data = npACB->IORegs[FI_PCMD] = FX_ACK_MEDIA_CHANGE;             /*@V157085*/
   Port = npACB->IOPorts[FI_PCMD];                                  /*@V157085*/
   outp(Port, Data);                                                /*@V157085*/
   IODelay();                                                       /*@V157085*/
                                                                    /*@V157085*/
   if( CheckReady( npACB ) )                                        /*@V157085*/
   {                                                                /*@V157085*/
#ifdef DEBUG                                                        /*@V157085*/
      if( npACB->IORegs[FI_PSTAT] & FX_ERROR )                      /*@V157085*/
      {                                                             /*@V157085*/
         _asm int 3                                                 /*@V157085*/
      }                                                             /*@V157085*/
      /* Read the error register. */                                /*@V157085*/
      Port = npACB->IOPorts[FI_PERR];                               /*@V157085*/
      inp( Port, Data );                                            /*@V157085*/
      npACB->IORegs[FI_PERR] = Data;                                /*@V157085*/
      if( Data & FX_MC )                                            /*@V157085*/
      {                                                             /*@V157085*/
         _asm int 3                                                 /*@V157085*/
      }                                                             /*@V157085*/
#endif                                                              /*@V157085*/
   }                                                                /*@V157085*/
                                                                    /*@V157085*/
   /* Turn interrupts back on */                                    /*@V157085*/
   Data = npACB->IORegs[FI_RFDR] = FX_DCRRes;                       /*@V157085*/
   Port = npACB->IOPorts[FI_RFDR];                                  /*@V157085*/
   outp(Port, Data);                                                /*@V157085*/
   IODelay();                                                       /*@V157085*/
                                                                    /*@V157085*/
   return;                                                          /*@V157085*/
}                                                                   /*@V157085*/


/*----------------------------------------------------------*/      /*@V155162*/
/*  GetMediaError()                                         */      /*@V155162*/
/*                                                          */      /*@V155162*/
/*  Read the Removable Media Status error.  Called only for */      /*@V155162*/
/*  removable media devices.                                */      /*@V155162*/
/*                                                          */      /*@V155162*/
/*----------------------------------------------------------*/      /*@V155162*/
USHORT NEAR GetMediaError( NPACB npACB )                            /*@V155162*/
{                                                                   /*@V155162*/
   USHORT        Port;                                              /*@V155162*/
   USHORT        Data, Data2;                                       /*@V155162*//*@V185215*/
                                                                    /*@V155162*/
   /* Turn off interrupts, temporarily */                           /*@V155162*/
   Data = npACB->IORegs[FI_RFDR] = FX_nIEN;                         /*@V155162*/
   Port = npACB->IOPorts[FI_RFDR];                                  /*@V155162*/
   outp(Port, Data);                                                /*@V155162*/
   IODelay();                                                       /*@V157085*/
                                                                    /*@V155162*/
   /* Write the Get Media Status command */                         /*@V155162*/
   Data = npACB->IORegs[FI_PCMD] = FX_GET_MEDIA_STATUS;             /*@V155162*/
   Port = npACB->IOPorts[FI_PCMD];                                  /*@V155162*/
   outp(Port, Data);                                                /*@V155162*/
   IODelay();                                                       /*@V157085*/
                                                                    /*@V155162*/
   /* Wait for INTRQ */                                             /*@V155162*/
   if( WaitDRQ( npACB ) )                                           /*@V155162*/
   {                                                                /*@V155162*/
      /* Read the Error register */                                 /*@V155162*/
      Port = npACB->IOPorts[FI_PERR];                               /*@V155162*/
      inp( Port, Data );                                            /*@V155162*/
   }                                                                /*@V155162*/
   else                                                             /*@V155162*/
   {                                                                /*@V155162*/
      /* The drive did not report a Media Status. */                /*@V155162*/
      Data = 0;                                                     /*@V155162*/
   }                                                                /*@V155162*/
                                                                    /*@V155162*/
   npACB->IORegs[FI_PERR] = Data;                                   /*@V155162*/
                                                                    /*@V157085*/
   /* Turn interrupts back on */                                    /*@V157085*/
   Data2 = npACB->IORegs[FI_RFDR] = FX_DCRRes;                      /*@V157085*//*@V185215*/
   Port = npACB->IOPorts[FI_RFDR];                                  /*@V157085*/
   outp(Port, Data2);                                               /*@V157085*//*@V185215*/
                                                                    /*@V155162*/
   return( Data );                                                  /*@V155162*/
}                                                                   /*@V155162*/


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/
USHORT NEAR GetErrorReg( NPACB npACB, USHORT Unit )
{
  USHORT        Port;
  USHORT        Data;
  USHORT        ErrorReg;

  Port = npACB->IOPorts[FI_PDRHD];
  Data = npACB->IORegs[FI_PDRHD];

  Data &= ~0x10;
  Data |= (Unit << 4);

  outp(Port, Data);
  IODelay();

  Port = npACB->IOPorts[FI_PERR];
  inp(Port, ErrorReg );
  IODelay();

  Port = npACB->IOPorts[FI_PDRHD];
  Data = npACB->IORegs[FI_PDRHD];

  outp(Port, Data);
  IODelay();

  return( ErrorReg );
}


/*------------------------------------*/
/*                                    */
/* MapError                           */
/*                                    */
/*------------------------------------*/

USHORT NEAR MapError( NPACB npACB )
{
  USHORT        IORBError = IOERR_DEVICE_NONSPECIFIC;
  USHORT        ErrCode;
  USHORT        Status;
  NPUCB         npUCB     = npACB->npUCB;

  if ( npACB->TimerFlags & (ACBT_DRQ | ACBT_READY) )                 /*@V85057*/
  {
    IORBError = IOERR_ADAPTER_TIMEOUT;
  }
  else if ( npACB->TimerFlags & ACBT_BUSY )                          /*@V85057*/
  {                                                                  /*@V85057*/
    npACB->BusyTimeoutCnt++;                                         /*@V85057*/
    IORBError = IOERR_ADAPTER_TIMEOUT;                               /*@V85057*/
  }                                                                  /*@V85057*/
  else if ( npACB->TimerFlags & ACBT_IRQ )
  {
    IORBError = IOERR_ADAPTER_DEVICE_TIMEOUT;
  }
  else if ( npACB->TimerFlags & ACBT_RESETFAIL )                     /*@V77133*/
  {                                                                  /*@V77133*/
    IORBError = IOERR_ADAPTER_DIAGFAIL;                              /*@V77133*/
  }                                                                  /*@V77133*/
  else if ( !((Status = npACB->IORegs[FI_PSTAT]) & FX_READY) )
  {
    IORBError = IOERR_UNIT_NOT_READY;
  }
  else if ( Status & FX_ERROR )
  {
    ErrCode = npACB->IORegs[FI_PERR] = GetErrorReg( npACB, npACB->UnitId);

    npACB->DataErrorCnt++;

    if ( ErrCode & (FX_AMNF | FX_IDNF ) )
    {
      IORBError = IOERR_RBA_ADDRESSING_ERROR;

      if( ErrCode & FX_AMNF )             /* Begin [013] Added error counters */
      {
        ++npUCB->DeviceCounters.TotalReadErrors;
      } else {
        ++npUCB->DeviceCounters.TotalSeekErrors;
      }                                                          /* End [013] */
    }
    else if ( ErrCode & FX_ECCERROR )
    {
      IORBError = IOERR_RBA_CRC_ERROR;
                                          /* Begin [013] Added error counters */
      ++npUCB->DeviceCounters.TotalReadErrors;
                                                                 /* End [013] */

      // if device is removable media                               /*@V182921*/
      if( npUCB->Flags & UCBF_REMOVABLE )                           /*@V182921*/
      {                                                             /*@V182921*/
         if( ErrCode = GetMediaError( npACB ) )                     /*@V182921*/
         {                                                          /*@V182921*/
            // Media Status reported some error, so disable         /*@V182921*/
            // further retries.                                     /*@V182921*/
            npACB->Flags |= ACBF_DISABLERETRY;                      /*@V182921*/
            if( ErrCode & FX_WRT_PRT )                              /*@V182921*/
            {                                                       /*@V182921*/
               IORBError = IOERR_MEDIA_WRITE_PROTECT;               /*@V182921*/
            } /* endif */                                           /*@V182921*/
            else if( ErrCode & FX_MC )                              /*@V182921*/
            {                                                       /*@V182921*/
               /* Handled below */                                  /*@V182921*/
            }                                                       /*@V182921*/
            else                                                    /*@V182921*/
            {                                                       /*@V182921*/
               IORBError = IOERR_DEVICE_NONSPECIFIC;                /*@V182921*/
            } /* endelse */                                         /*@V182921*/
         } /* endif */                                              /*@V182921*/
      }                                                             /*@V182921*/

    }
    else if( ErrCode & FX_ICRC )                                    /*@V179942*/
    {                                                               /*VVVVVVVV*/
       if( ErrCode & FX_ABORT )
       {
          /*************************************/
          /*        Ultra DMA CRC error        */
          /*************************************/

          IORBError = IOERR_DEVICE_ULTRA_CRC;

          /*************************************/
          /*        Error Counters             */
          /*************************************/

          if ( npUCB->ReqFlags & ACBR_WRITE )
          {
            ++npUCB->DeviceCounters.TotalWriteErrors;
          } else {
            ++npUCB->DeviceCounters.TotalReadErrors;
          }
       } else {
          /***************************/
          /* ( ErrCode & FX_BADBLK ) */                             /*AAAAAAAA*/
          /***************************/                             /*@V179942*/

          npACB->DataErrorCnt = -1;                      /* Terminate retries */

          IORBError = IOERR_RBA_CRC_ERROR;
                                          /* Begin [013] Added error counters */
          if ( npUCB->ReqFlags & ACBR_WRITE )
          {
            ++npUCB->DeviceCounters.TotalWriteErrors;
          } else {
            ++npUCB->DeviceCounters.TotalReadErrors;
          }                                                      /* End [013] */
       }
    }
    else if ( ErrCode & FX_TRK0 )                                   /*@V151345*/
    {                                                               /*@VVVVVVV*/
      if(npUCB->Flags & UCBF_REMOVABLE)
        {
        /* Media is not present, map this to a not ready error. */  /*@V157085*/
        IORBError = IOERR_UNIT_NOT_READY;                           /*@V157085*/
        npUCB->Flags &= ~UCBF_READY;                                /*@V157085*/
        } /* endif */
      else
        {
        IORBError = IOERR_DEVICE_NONSPECIFIC;
        /* Begin [013] Added error counters */
        ++npUCB->DeviceCounters.TotalSeekErrors;
        /* End [013] */                                             /*@AAAAAAA*/
        } /* endelse */                                             /*@V151345*/
    }
    else if ( ErrCode & FX_ABORT )
    {
      IORBError = IOERR_DEVICE_REQ_NOT_SUPPORTED;

      /* Begin [013] Added error counters */
      if ( npUCB->ReqFlags & ACBR_WRITE )
      {
        ++npUCB->DeviceCounters.TotalWriteErrors;
      } else {
        ++npUCB->DeviceCounters.TotalReadErrors;
      }                                                          /* End [013] */

      // if device is removable media     SRD                       /*@V151345*/
      if( npUCB->Flags & UCBF_REMOVABLE )                           /*@VVVVVVV*/
      {
         if( ErrCode = GetMediaError( npACB ) )
         {
            // Media Status reported some error, so disable
            // further retries.
            npACB->Flags |= ACBF_DISABLERETRY;
            if( ErrCode & FX_WRT_PRT )
            {
               IORBError = IOERR_MEDIA_WRITE_PROTECT;
            } /* endif */
            else if( ErrCode & FX_MC )
            {
               /* Handled below */
            }
            else
            {
               IORBError = IOERR_DEVICE_NONSPECIFIC;
            } /* endelse */                                         /*@AAAAAAA*/
         } /* endif */                                              /*@V151345*/
      }
    }                                                               /*@V151345*/

    /*                                                                @V157085
    ** The spec is not clear on whether the Media Changed bit         @V157085
    ** always goes active by itself or not.  Always acknowledge       @V157085
    ** the media change and just in case the bit can be set with      @V157085
    ** other error bits, check it seperately.                         @V157085
    */                                                              /*@V157085*/
    if( ErrCode & FX_MC )                                           /*@V157085*/
    {                                                               /*@V157085*/
       if( npUCB->Flags & UCBF_REMOVABLE )                          /*@V157085*/
       {                                                            /*@V157085*/
          /* Media Changed */                                       /*@V157085*/
          SendAckMediaChange( npACB );                              /*@V157085*/
          IORBError = IOERR_MEDIA_CHANGED;                          /*@V157085*/
          npUCB->Flags |= UCBF_READY;                               /*@V157085*/
       }                                                            /*@V157085*/
    }                                                               /*@V157085*/
  }
  return ( IORBError );
}

/* Begin [001.3] Added two functions to support DMA functionality */

/*------------------------------------*/
/*                                    */
/* ComputeDMASGlist                   */
/*                                    */
/*------------------------------------*/
ComputeDMASGList( NPACB npACB )
{
  /*------------------------------------------------------*/
  /* Copy scatter gather list from IORB as xfer count     */
  /* should be zero based and scatter/gather list must be */
  /* aligned at a dword boundry.                          */
  /*------------------------------------------------------*/

  USHORT            rc = 0;
  USHORT            i;
  PSCATGATENTRY     pSGList;

  pSGList =  npACB->IOSGPtrs.pSGList;
  for (i=0; i < npACB->IOSGPtrs.cSGList; i++)
  {
    npACB->DMASGList[i].ppXferBuf  = pSGList->ppXferBuf;
    npACB->DMASGList[i].XferBufLen = ( pSGList->XferBufLen - 1);
    if ( pSGList->ppXferBuf & 0x0001 )
    {
//           _asm { int 3 };
      rc =1;
      return (rc);
    }
    if ( (pSGList->XferBufLen >> 9) >= 128 )
    {
//          _asm { int 3 };
      rc = 1;
      return (rc);
    }
    pSGList++;
  }
#ifdef DEBUG
  if ( i > 1 )
  {
    _asm
    {
        int 3
        mov ax, ax
        mov bx, bx
        mov cx, cx
    }
    npACB->SGIOCount++;
  }
#endif
  i--;
  npACB->DMASGList[i].XferBufLen |= 0x10000000;
  npACB->IOCount++;
  return (rc);
}

/* Begin [002.3] */

/*--------------------------------------------*/
/* CreateBMSGList                             */
/* --------------                             */
/*                                            */
/* Arguments:                                 */
/*                                            */
/*                                            */
/* Actions:                                   */
/*      Takes OS/2 scatter/gather list and    */
/*      builds SFF-8038i compatible list for  */
/*      DMA controller.                       */
/*                                            */
/*                                            */
/* Returns:                                   */
/*      0 if successful                       */
/*                                            */
/*                                            */
/*--------------------------------------------*/

int CreateBMSGList( NPACB npACB )
{
  USHORT          i;
  PPRD            pSGL            = npACB->pBMDMA_SGL;              /*@V159438*/
  ULONG           BytesLeft;
  ULONG           PhysicalSgl;
  ULONG           PhysicalAddress;
  ULONG           Length;
  ULONG           LengthLeftInBoundary;
  ULONG           DescriptorCount = 0;
  ULONG           TempLength;
  PSCATGATENTRY   pSGList;

  BytesLeft = npACB->BytesToTransfer;

  /* process each entry in OS/2 scatter/gather list */

  pSGList =  npACB->IOSGPtrs.pSGList;
  for (i=0; i < npACB->IOSGPtrs.cSGList; i++,pSGList++)
  {

    Length = pSGList->XferBufLen;       /* get length of memory region */
    PhysicalAddress = pSGList->ppXferBuf;

    if ( Length > BytesLeft )
    {
#ifdef DEBUG_WDCIDEOS
      {
         _asm int 3;
      }
#endif
      Length = BytesLeft;               /* Don't set up entry bigger than total */
    }                                   /* transfer length reported */

    if( (PhysicalAddress & 1) ||        /* if on odd byte boundary, do PIO */
        (Length & 1) )                  /* if odd transfer length, do PIO */
    {
#ifdef ENABLE_COUNTERS                                              /* [010] */
      ++npACB->npUCB->DeviceCounters.ByteMisalignedBuffers;       /* [010] */
#endif                                                              /* [010] */
      return ( 1 );                     /* fail conversion */
    }


    while( Length )
    {

      pSGL->MR_PhysicalBaseAddress = PhysicalAddress;
      TempLength = Length;              /* make copy of total length */

      /* Can't exceed 64KB size per entry in BM DMA SG List */

      if( TempLength > MR_64K_LIMIT )
      {
#ifdef DEBUG_WDCIDEOS
        {
          _asm mov ax,1
          _asm int 3;
        }
#endif

        TempLength = MR_64K_LIMIT;      /* force to max size */
      }

      if ( TempLength > BytesLeft )
      {
#ifdef DEBUG_WDCIDEOS
        {
          _asm mov ax,2
          _asm int 3;
        }
#endif

        TempLength = BytesLeft;         /* Don't exceed remaining transfer length */
      }

      /* Can't cross 64KB boundary so check for it and adjust */

      LengthLeftInBoundary = MR_64K_LIMIT - (PhysicalAddress & (MR_64K_LIMIT-1));
      if ( TempLength > LengthLeftInBoundary )
      {
        TempLength = LengthLeftInBoundary;
#ifdef ENABLE_COUNTERS                                              /* [010] */
        ++npACB->npUCB->DeviceCounters.ByteMisalignedBuffers;       /* [010] */
#endif                                                              /* [010] */
      }

      /* Adjust counts */

      PhysicalAddress += TempLength;
      Length -= TempLength;
      BytesLeft -= TempLength;

      /* Create SGL descriptor entry */
      pSGL->ByteCountAndEOT = (ULONG) TempLength;
      ++DescriptorCount;
      if(BytesLeft == 0)
      {
        pSGL->ByteCountAndEOT |= PRD_EOT_FLAG;
      }
      else if ( DescriptorCount > npACB->BMDMA_SGListCount )
      {
#ifdef DEBUG_WDCIDEOS
        {
          _asm mov ax,4
          _asm int 3;
        }
#endif
        return( 1 );                    /* ran out of descriptors */

      }
      pSGL++;
    } /* while( Length ) */
  } /* for (i=0; i < npACB->IOSGPtrs.cSGList; i++,pSGList++) */

  return( 0 );                          /* finished building sg list */
} /* End of CreateBMSGList */

/* End [002.3] */

/*-------------------------*/
/*                         */
/* ProgramDMAController    */
/*                         */
/*-------------------------*/

ProgramDMAController ( NPACB  npACB )
{

  USHORT  DMAMode         = npACB->DMAMode        ;
  USHORT  DMAExtMode      = npACB->DMAExtMode     ;
  USHORT  DMAChannel      = npACB->DMAChannel     ;
  USHORT  DMACountPort    = npACB->DMACountPort   ;
  USHORT  DMAAddrPort     = npACB->DMAAddrPort    ;
  USHORT  DMAAddrpagePort = npACB->DMAAddrpagePort;
  USHORT  DMAModePort     = npACB->DMAModePort    ;
  USHORT  DMAChainMode    = npACB->DMAChainMode   ;
  USHORT  DMAExtModePort  = npACB->DMAExtModePort ;
  USHORT  DMAMaskPort     = npACB->DMAMaskPort    ;
  USHORT  DMAClrBytePtr   = npACB->DMAClrBytePtr  ;
  USHORT  DMASGDCmdPort   = npACB->DMASGDCmdPort  ;
  USHORT  DMASGDPtrPort   = npACB->DMASGDPtrPort  ;
  USHORT   XferCount;
  ULONG    XferAddr;
  ULONG    physSGList;

  DISABLE;

  // Mask Off DMA Channel and Setup the DMA Mode and Extended Mode ports

  _asm
  {
      push  dx
      push  ax
      mov   ax, DMAChannel
      or    al, 00000100b               ; Mask DMA Channel OFF.
      mov   dx, DMAMaskPort
      out   dx, al

      and   al, 00000011b               ; AL = DMA Channel
      or    ax, DMAMode
      mov   dx, DMAModePort
      out   dx, al                      ; Write DMA Mode Port

      and   al, 00000011b               ; AL = DMA Channel
      or    ax, DMAExtMode
      mov   dx, DMAExtModePort
      out   dx, al                      ; Write Extended Mode, DMA Type

      and   al, 00000011b               ; AL = DMA Channel
      mov   dx, DMAClrBytePtr
      out   dx, al                      ; Clear Byte Pointer
      pop   ax
      pop   dx
  }

  if (npACB->IOSGPtrs.cSGList == 1 )
  {
    XferAddr = npACB->DMASGList[0].ppXferBuf;
    XferCount=  (USHORT) (((npACB->DMASGList[0].XferBufLen + 1) >> 1) - 1);

    _asm
    {
        push  dx
        push  ax
        mov   dx, DMAExtModePort
        mov   ax, DMAChannel
        and   ax, 3
        or    ax, DMAExtMode
        and   ax, 11110111b
        out   dx, al
        mov   dx, DMACountPort
        mov   ax, XferCount             ; Count of Data Buffer

        out   dx, al
        mov   al, ah
        out   dx, al                    ; Count in words programmed

        xor   ax, ax
        add   dx, 400h
        out   dx, al                    ; High count = 0

        mov   ax, word ptr [XferAddr]   ; Buffer address
        test  ax, 1
        jz    xxxxx
        int   3
xxxxx:
        shr   ax, 1                     ; Address shifed by 1
        mov   dx, word ptr [XferAddr+2]
        shl   dx, 15
        or    ax, dx                    ; Copy bit 16 of address
        mov   dx, DMAAddrPort
        out   dx, al                    ; Address bits 8_1

        mov   al, ah
        out   dx, al                    ; address bits 16_9
        mov   dx, DMAAddrpagePort

        mov   ax, word ptr [XferAddr+2] ; Buffer address
        and   ax, 0FFFEH                ; bit 16 of address ignored
        out   dx, al                    ; address bits 23-17
        add   dx, 400h

        mov   al, ah
        out   dx, al                    ; address bits 31_24
        pop   ax
        pop   dx
    }
  } /* if (npACB->IOSGPtrs.cSGList == 1 ) */

  /* PROGRAM Scatter Gather Pointer */

  else
  {
    physSGList = npACB->physSGListAddr;
    _asm
    {
        int   3
        push  dx
        push  ax
        mov   dx, DMASGDPtrPort
        mov   ax, word ptr [physSGList]
        out   dx, ax                    ; Write Scatter Gather List Pointer
        add   dx, 2

        mov   ax, word ptr [physSGList+2]
        out   dx, ax                    ; Write Scatter Gather List Pointer

        mov   dx, DMASGDCmdPort
        mov   al, DMAStartSGCmd         ; Start Scatter Gather DMA
        out   dx, al
        pop   ax
        pop   dx
    };
  }

  // UNMASK DMA Channel
  _asm
  {
      push  dx
      push  ax
      mov   ax, DMAChannel
      and   ax, 3
      mov   dx, DMAMaskPort
      out   dx, al                      ; Unmask DMA Channel
      jmp   SHORT $+2
      pop   ax
      pop   dx
  };
  ENABLE;

}

/* End [001.3] */

/*-------------------------*/                                       /*@V179942*/
/*                         */                                       /*VVVVVVVV*/
/* StopBMDMA()             */
/*                         */
/*-------------------------*/
VOID StopBMDMA( NPACB npACB )
{
   USHORT data, port;

   port = npACB->BMICOM;                               /* BM Command Register */

   /*****************************************/
   /* Note: Important to write out correct  */
   /* direction bit per Intel               */
   /*****************************************/
   data  = npACB->BM_CommandCode;                       /* get DIRECTION bit  */

   data &= ~ACBX_BMICOM_START;                          /* turn OFF Start bit */

   outp ( port, data );
}

/*-------------------------*/
/*                         */
/* ClearBMISTA_INT()       */
/*                         */
/*-------------------------*/
VOID ClearBMISTA_INT( USHORT Data, NPACB npACB )
{
   USHORT port;

   port = npACB->BMISTA;                        /* BM Status Register */

   Data &= ( ACBX_BMISTA_INTERRUPT |            /* clear INTR flag if set     */
             ACBX_BMISTA_D0DMA     |            /* DMA Capability bit Drive 0 */
             ACBX_BMISTA_D1DMA     );           /*                    Drive 1 */

   outp ( port, Data );
}

/*-------------------------*/
/*                         */
/* ClearBMISTA_error()     */
/*                         */
/*-------------------------*/
VOID ClearBMISTA_error( USHORT Data, NPACB npACB )
{
   USHORT port;

   port = npACB->BMISTA;                    /* BM Status Register */

   Data &= ( ACBX_BMISTA_ERROR |            /* Turn off Error Bit by writing 1*/
             ACBX_BMISTA_D0DMA |            /* DMA Capability bit Drive 0 */
             ACBX_BMISTA_D1DMA );           /*                    Drive 1 */

   outp ( port, Data );                                             /*AAAAAAAA*/
}                                                                   /*@V179942*/

/*----------------------------------------------------------------*/
/*   OurPCIInterrupt()                                            */
/*                                                                */
/*   This routine will determine if this is our interrupt by      */
/*   looking at PCI config registers and seeing if the Intterrupt */ 
/*   bit is set.                                                  */
/*----------------------------------------------------------------*/
BOOL OurPCIInterrupt( NPACB npACB )
{
   ULONG  MRdMode;

   if( ReadPCIConfigSpace( &npACB->PCIInfo,
                          PCI_CR_BM_MRDMODE,
                                   &MRdMode,
                                         1 ))
   {
      return( FALSE );
   }

   if( MRdMode & NATIVE_PRIMARY_CHANNEL )
   {
      /* MRdMode = NATIVE_PRIMARY_CHANNEL; */
      MRdMode &= 0x0F;

      /* ----------------------------------------------------- */
      /* Clear the PCI Interrupt by writing out a 1 in the bit */
      /* ----------------------------------------------------- */
      if( WritePCIConfigSpace( &npACB->PCIInfo,
                               PCI_CR_BM_MRDMODE,
                                         MRdMode,
                                              1 ))
      {
         return( FALSE );
      }
      GetStatusreg( npACB );                         /* Required to clear int */
      return( TRUE );
   }
   if( MRdMode & NATIVE_SECONDARY_CHANNEL )
   {
      /* MRdMode = NATIVE_SECONDARY_CHANNEL; */
      MRdMode &= 0x0F;

      /* ----------------------------------------------------- */
      /* Clear the PCI Interrupt by writing out a 1 in the bit */
      /* ----------------------------------------------------- */
      if( WritePCIConfigSpace( &npACB->PCIInfo,
                               PCI_CR_BM_MRDMODE,
                                         MRdMode,
                                              1 ))
      {
         return( FALSE );
      }

      GetStatusreg( npACB );

      return( TRUE );
   }
   return( FALSE );
}

