/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/*static char *SCCSID = "%w% %e%";*/
/**************************************************************************
 *
 * 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:
 *
 *
 *
 *
 *
*/

 #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 "s506cons.h"
 #include "s506type.h"
 #include "s506regs.h"
 #include "s506ext.h"
 #include "s506pro.h"


/*---------------------------------------------*/
/* FixedExecute                                */
/* ------------                                */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

VOID NEAR FixedExecute(NPACB npACB)
{
  /*-------------------------------------------------*/
  /* ACB Use Count Checks                            */
  /* --------------------                            */
  /* The state machine is reentrant on a per ACB     */
  /* basis.                                          */
  /* Handling of asynchronous events IRQs, Timeouts, */
  /* is deferred until the ACB flags indicate a      */
  /* WAITSTATE is about to be entered for the        */
  /* ACB in question.                                */
  /*-------------------------------------------------*/

  DISABLE

  npACB->UseCount++;

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

#ifdef DEBUG
        LogCall( npACB, *( ((PUSHORT) &npACB) -1 ) );
#endif

        switch (npACB->State)
        {
          case ACBS_START:
            StartState(npACB);
            break;
          case ACBS_RETRY:
            RetryState(npACB);
            break;
          case ACBS_INTERRUPT:
            InterruptState(npACB);
            break;
          case ACBS_DONE:
            DoneState(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);
}

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

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

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


USHORT NEAR FixedInterrupt( NPACB npACB )
{
  USHORT     Claimed = 0;

  if ( npACB )
  {
   if ((!(npACB->ResourceFlags & ACBRF_CURR_OWNER))&&                /*@V93531*/
       (npACB->Flags & ACBF_SM_SUSPENDED))                           /*@V93531*/
   {                                                                 /*@V93531*/
      if (!npACB->SuspendIRQaddr)                                    /*@V93531*/
      {                                                              /*@V93531*/
         _asm { int 3}  /* suspended without a suspend address? */   /*@V93531*/
      }                                                              /*@V93531*/
      else                                                           /*@V93531*/
         Claimed = ~(*npACB->SuspendIRQaddr)();                      /*@V95155*/
   }                                                                 /*@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*/

    if ( npACB->Flags & ACBF_INTERRUPT )
    {
      DISABLE

      npACB->Flags &= ~ACBF_INTERRUPT;

      if ( npACB->IRQTimerHandle )
      {
        ADD_CancelTimer( npACB->IRQTimerHandle);
        npACB->IRQTimerHandle = 0;
      }
      else
      {
        _asm { int 3 }
        goto SpuriousInt;                                            /*@V77133*/
      }

      Claimed = 1;

      ENABLE

      DevHelp_EOI(npACB->IntLevel);

      FixedExecute(npACB);
    }
    else
    {

    SpuriousInt:                                                     /*@V77133*/

#ifdef DEBUG
      _asm { int 3 }
#endif

      npACB->SpuriousIRQ++;

      Claimed = 1;
      DevHelp_EOI( npACB->IntLevel );
    }
   }
  }
  return( ~Claimed );
}



/*---------------------------------------------*/
/* StartState                                  */
/* ----------                                  */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

VOID NEAR StartState(NPACB npACB)
{
  NPUCB         npUCB;

  if ( npACB->Flags & ACBF_PS2IDEPORT  )
  {
    if ( BIOSActive )
    {
      SetupPS2IDEPort( 1 );
    }

    SetPS2DiskLight( 1 );
  }

  npUCB = (NPUCB) npACB->pIORB->UnitHandle;

  npACB->UnitId = npUCB->UnitId;
  npACB->npUCB  = npUCB;

  DISABLE
  npACB->ElapsedTime = 0;
  ENABLE

  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;

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

  pIORB   = npACB->pIORB;

  CmdMod  = pIORB->CommandModifier;

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

  npACB->ReqFlags      = 0;
  npACB->IORBError     = 0;
  npACB->IORBStatus    = 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;
  }

  /*-----------------------------------------------------*/
  /* Tranlate IORB Command Code/Modifier to ACBR_* flags */
  /*-----------------------------------------------------*/
  switch ( CmdMod )
  {
    case IOCM_READ:
      npACB->IOSGPtrs.Mode  = PORT_TO_SGLIST;
      npACB->ReqFlags |= npUCB->ReqFlags | ACBR_READ;
      break;

    case IOCM_WRITE:
      npACB->IOSGPtrs.Mode  = SGLIST_TO_PORT;
      npACB->ReqFlags |= npUCB->ReqFlags | ACBR_WRITE;
      break;

    case IOCM_WRITE_VERIFY:
      npACB->IOSGPtrs.Mode  = SGLIST_TO_PORT;
      npACB->ReqFlags |=  npUCB->ReqFlags | (ACBR_WRITEV | ACBR_WRITE);
      break;

    case IOCM_READ_VERIFY:
      npACB->ReqFlags |= npUCB->ReqFlags | ACBR_VERIFY;
      break;

    case IOCM_NO_OPERATION:
      npACB->IOSGPtrs.Mode  = PORT_TO_SGLIST;
      npACB->ReqFlags |= npUCB->ReqFlags;
      break;

    default:
      pIORB->Status   |= IORB_ERROR;
      pIORB->ErrorCode = IOERR_CMD_NOT_SUPPORTED;

      npACB->State = ACBS_DONE;
  }

  /*------------------------------------------------*/
  /* 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 pointers for Block IO operations  */
  /*-----------------------------------------------*/
  if ( npACB->ReqFlags & (ACBR_BLOCKIO | ACBR_IDENTIFY) )
  {
    InitBlockIO( npACB );
  }

}

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

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

  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;

  pIORB->BlocksXferred     = 0;
}

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

USHORT NEAR StartOtherIO( NPACB npACB )
{
  USHORT        ReqFlags;
  NPUCB         npUCB = npACB->npUCB;
  USHORT        Port;
  USHORT        Data;

  npACB->IORegs[FI_PDRHD] = 0xA0 | (npACB->UnitId << 4);

  npACB->IOPendingMask = FM_PDRHD | FM_PCMD;

  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;
  }
  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 );

StartOtherIOExit: ;

}


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

USHORT NEAR StartBlockIO( NPACB npACB )
{

  /*--------------------------------------*/
  /* Set CHS/LBA address and Sector Count */
  /* in ACB.                              */
  /*--------------------------------------*/
  SetIOAddress( npACB );

  /*--------------------------------------*/
  /* 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.                             */
  /*-----------------------------------------*/
  if ( !(npACB->ReqFlags & ACBR_WRITE) )
  {
    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 ) )
  {
    /*-----------------------------------------------*/
    /* For a 'Class 2' operation WRITE, etc. we need */
    /* to 'prime the pump' by writting the first     */
    /* block to the controller.                      */
    /*-----------------------------------------------*/
    if ( npACB->ReqFlags & ACBR_WRITE )
    {
      DoBlockIO( npACB, 0 );
    }
  }
}

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

VOID NEAR SetIOAddress( NPACB npACB )
{
  NPUCB    npUCB    = npACB->npUCB;
  USHORT   ReqFlags = npACB->ReqFlags;
  USHORT   Flags    = npACB->Flags;
  ULONG    CurLBA;
  CHS_ADDR chs;

  /*-------------------------------------*/
  /* 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;
  }

  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;

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

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

  if ( Status & FX_ERROR )
  {
    npACB->State = ACBS_ERROR;
    npACB->IORegs[FI_PSTAT] = Status;
  }
  else
  {

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

    /*----------------------*/
    /* Handle Block I/O Ops */
    /*----------------------*/
    else if ( npACB->ReqFlags & ACBR_BLOCKIO )
    {
      rc = DoBlockIO( npACB, npACB->SecPerInt );

      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
          {
            ((PIORB_EXECUTEIO) npACB->pIORB)->BlocksXferred = npACB->SecDone;
            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);
      ADD_XferIOW( &npACB->IOSGPtrs );


      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;

  if ( npACB->ReqMask == ~ACBR_IDENTIFY )
  {
    if ( !WaitDRQ( npACB ) )
    {
      npACB->IOSGPtrs.numTotalBytes = 512;
      ADD_XferIOW( &npACB->IOSGPtrs );
    }
  }
  else if ( npACB->ReqMask == ~ACBR_SETMULTIPLE )
  {
    npACB->Flags |= ACBF_MULTIPLEMODE;
    npUCB->Flags |= UCBF_MULTIPLEMODE;
  }

  npACB->State = ( npACB->ReqFlags ) ? ACBS_RETRY : ACBS_DONE;
}



/*---------------------------------------------*/
/* 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           */
  /*---------------------------------------------*/

  if ( BIOSActive && InitIOComplete
        && !(npACB->Flags & ACBF_DISKPARMRESET)
          && (npACB->npUCB->LogGeom.NumHeads <= 16) )
  {
    npACB->ReqFlags = ACBR_SETLOGPARAM;
    npACB->State    = ACBS_RETRY;
    return;
  }

  npACB->npUCB->ReqFlags |= (npACB->ReqFlags & (UCBR_RECAL | UCBR_SETPARAM));

  pIORB->ErrorCode = npACB->IORBError;
  pIORB->Status    = npACB->IORBStatus;


  IORBDone(npACB);

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

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

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

  NextIORB(npACB);

  if (npACB->State == ACBS_START )
  {
    npACB->Flags &= ~ACBF_WAITSTATE;
  }
  else
  {
    npACB->Flags |=  ACBF_WAITSTATE;
    npACB->Flags &=  ~ACBF_SM_ACTIVE;
  }

  ENABLE
}

/*---------------------------------------------*/
/* ErrorState                                  */
/* ----------                                  */
/*                                             */
/*                                             */
/*                                             */
/*---------------------------------------------*/

VOID NEAR ErrorState(NPACB npACB)
{
  USHORT        Reset = 0;


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

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

  npACB->UseCount = 1;

  ENABLE

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

  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;
  }

  npACB->TimerFlags = 0;

  if ( Reset && !(npACB->Flags & ACBF_DISABLERESET) &&            /*@V77133*/
       !(npACB->npUCB->Flags & UCBF_ATAPI_DEVICE ) )              /*@V87325*/
  {
    DoReset( npACB );
  }
  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 );
    }

    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++ )
    {
      npACB->UnitCB[i].ReqFlags   |= (UCBR_RECAL | UCBR_SETPARAM);
      npACB->UnitCB[i].Flags      &= ~(UCBF_MULTIPLEMODE |           /*@V77133*/
                                           UCBF_DIAG_FAILED);        /*@V77133*/
      npACB->UnitCB[i].DiagStatus =  0;                              /*@V77133*/
    }

    npACB->ReqFlags |= (ACBR_RECAL | ACBR_SETPARAM);
  }

  /*------------------------------------*/
  /* 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;
  npACB->Flags &= ~ACBF_WAITSTATE;
}

/*---------------------------------------------*/
/*                                             */
/* 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*/

  /*-----------------------------------*/
  /* 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 ) )
    {
      if ( ADD_StartTimerMS((PULONG) &npACB->ResetTimerHandle,
                            (ULONG)  npACB->DelayedResetInterval,
                            (PFN)    DelayedReset,
                            (PVOID)  npACB,
                            (ULONG)  0              ) )
      {
         _asm { int 3 }
      }
      npACB->Flags |= ACBF_WAITSTATE;

      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->Flags &= ~ACBF_WAITSTATE;                             /*@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                 */
/*                                             */
/*---------------------------------------------*/

USHORT NEAR GetDiagResults( NPACB npACB )
{
  USHORT    rc = 0;
  USHORT    DiagCode;
  USHORT    Status;

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

  if ( !(Status & FX_BUSY) )
  {
    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_ATAPI_DEVICE ) )             /*@V87325*/
      {                                                              /*@V87325*/
        goto Send_Error;
      }                                                              /*@V87325*/
    }
  }                                                                  /*@V87325*/

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

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

  return( 0 );

  Send_Error:
    npACB->State = ACBS_ERROR;
    npACB->Flags &= ~(ACBF_INTERRUPT | ACBF_WAITSTATE);

    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;
    npACB->Flags &= ~ACBF_WAITSTATE;
  }

  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        Port;
  USHORT        Data;

  Port = npACB->IOPorts[FI_PSTAT];
  inp( Port, Data )
  return( Data );
}

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

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 );
}


/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

USHORT NEAR MapError( NPACB npACB )
{
  USHORT        IORBError = IOERR_DEVICE_NONSPECIFIC;
  USHORT        ErrCode;
  USHORT        Status;

  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;
    }
    else if ( ErrCode & FX_ECCERROR )
    {
      IORBError = IOERR_RBA_CRC_ERROR;
    }
    else if ( ErrCode & FX_BADBLK )
    {
      npACB->DataErrorCnt = -1;                 /* Terminate retries          */
      IORBError = IOERR_RBA_CRC_ERROR;
    }
    else if ( ErrCode & FX_TRK0 )
    {
      IORBError = IOERR_DEVICE_NONSPECIFIC;
    }
    else if ( ErrCode & FX_ABORT )
    {
      IORBError = IOERR_DEVICE_REQ_NOT_SUPPORTED;
    }
  }
  return ( IORBError );
}

#ifdef DEBUG

/*------------------------------------*/
/*                                    */
/*                                    */
/*                                    */
/*------------------------------------*/

LogCall( NPACB npACB, USHORT EntryPt )
{
  DISABLE

  LogBuf[LogPtr+0]  = OFFSETOF(npACB->pIORB);
  LogBuf[LogPtr+1]  = SELECTOROF(npACB->pIORB);
  LogBuf[LogPtr+2]  = npACB->State | (npACB->UseCount << 8);
  LogBuf[LogPtr+3]  = EntryPt;

  LogPtr += 4;

  if ( LogPtr >= sizeof(LogBuf)/sizeof(LogBuf[0]) )
  {
    LogPtr = 0;
  }

  LogBuf[LogPtr]   = -1;
  LogBuf[LogPtr+1] = -1;

  ENABLE
}
#endif
