#include <dos.h>
#include <stdio.h>
#include <conio.h>

#include "EncPci.h"

typedef unsigned long ULONG;
typedef ULONG *PULONG;
typedef unsigned short USHORT;
typedef USHORT *PUSHORT;
typedef unsigned char UCHAR;
typedef UCHAR *PUCHAR;

short CardNumber ;

#define TRUE 1
#define FALSE 0
#define S_OK TRUE
#define S_FALSE FALSE
#define Outportb outportb

// Based on ImsUtil.cpp:

void WriteByte(USHORT Card, USHORT Addr, UCHAR Val)
{
    Addr += Card ;
    Outportb (Addr, Val);
}

void ReadByte(USHORT Card, USHORT Addr, PUCHAR Val)
{
    UCHAR Temp;
    Addr += Card ;
    Temp = inportb(Addr) ;
    *Val = Temp;
}

BOOLEAN IsValidChipNumber(short ChipNumber)
{
    switch (ChipNumber) {
       case Chip1:
       case Chip2:
          return TRUE;
    }
    return FALSE ; // Unexpected value.
}

BOOLEAN IsValidEncoderNumber(short EncoderNumber, short Level)
{
// Level=1: Only one encoder is valid.
// Level=2: Also pair of encoders (A+B or C+D) is valid.
// Level=3: Also all encoders (A+B+C+D) is valid.
    switch (EncoderNumber) {
       case Encoder1:
       case Encoder2:
          return TRUE;

       case Encoder3:
          if (Level >= 2)
             return TRUE;
          else
             return FALSE;

       case Encoder4:
          if (Level == 3)
             return TRUE;
    }
    return FALSE ; // Unexpected value.
}

// Based on ImsEncPciUtil.cpp

short GetPCIByte (unsigned char* Data, unsigned char BusNu, unsigned char DeviceNu, short Address)
// (Formerly GetPCIdata)
// Input:
//   BusNu
//   DeviceNu
//   Address
// Output:
//   Data
// Return Code:
//   1 - Success (Byte read and stored in Data).
//   0 - Failure (Byte not read, Status is stored in Data).
// Note:
//   For 'Status' description, refer to Table 729, above.
{
	char Status ;
	unsigned char Dt ;
	asm	{
    Mov AX,0xB108
    Mov BH,BusNu
    Mov BL,DeviceNu
    Mov DI,Address
    Int 0x1A
    Mov Status,AH
    Mov Dt,CL
	}
        if (Status == 0) {
	        *Data = Dt ;
                return (1) ;
        }
        else {
                *Data = Status ;
        	return (0) ;
        }
}

short GetPCIWord (unsigned short* Data, unsigned char BusNu, unsigned char DeviceNu, short Address)
// Input:
//   BusNu
//   DeviceNu
//   Address
// Output:
//   Data
// Return Code:
//   1 - Success (Word read and stored in Data).
//   0 - Failure (Word not read, Status is stored in Data).
// Note:
//   For 'Status' description, refer to Table 729, above.
{
	char Status ;
	unsigned short Dt ;
	asm	{
    Mov AX,0xB109
    Mov BH,BusNu
    Mov BL,DeviceNu
    Mov DI,Address
    Int 0x1A
    Mov Status,AH
    Mov Dt,CX
	}
        if (Status == 0) {
	        *Data = Dt ;
                return (1) ;
        }
        else {
                *Data = Status ;
        	return (0) ;
        }
}

short FindPCIdev (unsigned char* DeviceNu, unsigned char* BusNu, short VendorID, short DeviceID, short DeviceIndex)
// Input:
//   VendorID
//   DeviceID
//   DeviceIndex (0, 1, 2, ...)
// Output:
//   DeviceNu
//   BusNu
// Return Code:
//   1 - Success (device found, DeviceNu & BusNu have been updated).
//   0 - Failure (device not found, DeviceNu is set to Status in this case).
// Note:
//   For 'Status' description, refer to Table 729, above.
{
char Status,Bu,Dv;
	asm	{
    Mov AX,0xB102
    Mov CX,DeviceID
    Mov DX,VendorID
    Mov SI,DeviceIndex
    Int 0x1A
    Mov Status,AH
    Mov Bu,BH
    Mov Dv,BL
	}
        if (Status == 0) {
        	*DeviceNu=Dv;
	        *BusNu=Bu;
                return (1) ;
        }
        else {
        	*DeviceNu=Status;
			return (0) ;
        }
}

unsigned char ExtractBusNu (long CardHandle)
{
// Returns BusNu extracted from CardHandle.
	return ((unsigned char) (CardHandle >> 24)) ;
}

unsigned char ExtractDeviceNu (long CardHandle)
{
// Returns DeviceNu extracted from CardHandle.
	return ((unsigned char) (CardHandle >> 16)) ;
}

unsigned short ExtractBaseAddress (long CardHandle)
{
// Returns BaseAddress extracted from CardHandle.
	return ((unsigned short) (CardHandle)) ;
}


// Based on ImsPcictl.cpp:

short TestActiveChip(long CardHandle) 
//  * Test the active (current) chip:
//  * Write into the F register all the 64 combinations as follows:
//    * The CHIPS bit kept in its original setting (0 or 1).
//    * The IRQEN bit kept 0.
//  * If all those combinations are read successfully, test succeeded.
//    Otherwise, test failed.
//  * At test's end, return the F register to its original setting.
{
    unsigned char OldValue, NewValue, CurValue, Mask, i ;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    ReadByte(CardNumber, 15, &OldValue) ;
    Mask = OldValue & 4 ;

    for (i = 0 ; i <= 63 ; i++) {
       NewValue =  ((i & 0x38) << 2) | ((i & 4) << 1) | (i & 3) |  Mask ;
       //            IRQSEL              IRQPOL             LEDS      CHIPS
       WriteByte(CardNumber, 15, NewValue) ;
       ReadByte(CardNumber, 15, &CurValue) ;
       if (CurValue != NewValue) {
          WriteByte(CardNumber, 15, OldValue) ;  // Return original setting.
          return 0;
       }
    }
    WriteByte(CardNumber, 15, OldValue) ;  // Return original setting.
    return 1;
}

short SetLed(long CardHandle, short LedNumber, short OnOffMode) 
{
    unsigned char OldValue, NewValue, CurValue, Mask ;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if ( (LedNumber != LedYellow) && (LedNumber != LedRed) ) {
       return 0;
    }

    switch (LedNumber) {
       case LedYellow:
          Mask = 2 ;
          break ;

       case LedRed:
          Mask = 1 ;
          break ;

       default:
          return 0 ; // Unexpected value.
    }

    ReadByte(CardNumber, 15, &OldValue) ;
    NewValue =  (OnOffMode == TurnOn)? OldValue |  Mask :
                                       OldValue & ~Mask ;
    WriteByte(CardNumber, 15, NewValue) ;
    ReadByte(CardNumber, 15, &CurValue) ;
    if (CurValue == NewValue) {
       return 1;
    } else {
       return 0;
    }
}

short GetLed(long CardHandle, short LedNumber, short* OnOffMode)
{
    unsigned char CurValue, Mask ;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if ( (LedNumber != LedYellow) && (LedNumber != LedRed) ) {
       return 0;
    }

    switch (LedNumber) {
       case LedYellow:
          Mask = 2 ;
          break ;

       case LedRed:
          Mask = 1 ;
          break ;

       default:
          return 0 ; // Unexpected value.
    }

    ReadByte(CardNumber, 15, &CurValue) ;
    *OnOffMode = ( (CurValue & Mask) == 0 ) ? TurnOff : TurnOn ;
    return 1;
}

short SetEncoderResolution(long CardHandle, short EncoderNumber, short ClocksPerCycle) 
{
    unsigned char OldValue, NewValue, CurValue, Mask;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &OldValue) ;
    switch (ClocksPerCycle) {
       case Clock1:
          Mask = 0 ;
          break ;

       case Clock2:
          Mask = 1 ;
          break ;

       case Clock4:
          Mask = 2 ;
          break ;

       default: // Unexpected value
          return 0;
    }
    NewValue = (OldValue & ~3) | Mask ;
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return 1;
    } else {
       return 0;
    }
}

short GetEncoderResolution(long CardHandle, short EncoderNumber, short* ClocksPerCycle)
{
    unsigned char CurValue, Mask;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &CurValue) ;
    Mask = CurValue & 3 ;
    switch (Mask) {
       case 0:
          *ClocksPerCycle = Clock1 ;
          break ;

       case 1:
          *ClocksPerCycle = Clock2 ;
          break ;

       case 2:
          *ClocksPerCycle = Clock4 ;
          break ;

       default: // Unexpected value
          return 0;
//          *ClocksPerCycle = UnexpectedValue ;
//          break ;
    }
    return 1;
}

short SetEncoderEventFlagSource(long CardHandle, short EncoderNumber, short EventFlagSource) 
{
    unsigned char OldValue, NewValue, CurValue, Mask;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &OldValue) ;
    switch (EventFlagSource) {
       case EventFlagSourceNone:
          Mask = 0 ;
          break ;

       case EventFlagSourceSetPoint:
          Mask = 4 ;
          break ;

       case EventFlagSourceOverflow:
          Mask = 8 ;
          break ;

       case EventFlagSourceIndex:
          Mask = 12 ;
          break ;

       default: // Unexpected value
          return 0;
    }
    NewValue = (OldValue & ~12) | Mask ;
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return 1;
    } else {
       return 0;
    }
}

short GetEncoderEventFlagSource(long CardHandle, short EncoderNumber, short* EventFlagSource)
{
    unsigned char CurValue, Mask;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &CurValue) ;
    Mask = CurValue & 12 ;
    switch (Mask) {
       case 0:
          *EventFlagSource = EventFlagSourceNone ;
          break ;

       case 4:
          *EventFlagSource = EventFlagSourceSetPoint ;
          break ;

       case 8:
          *EventFlagSource = EventFlagSourceOverflow ;
          break ;

       case 12:
          *EventFlagSource = EventFlagSourceIndex ;
          break ;
    }
    return 1;
}

short SetHoldEncoderEventFlag(long CardHandle, short EncoderNumber, short OnOffMode) 
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &OldValue) ;
    switch (OnOffMode) {
       case TurnOn:
          NewValue = OldValue | 16 ;
          break ;

       case TurnOff:
          NewValue = OldValue & ~16 ;
          break ;

       default: // Unexpected value
          return 0;
    }
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return 1;
    } else {
       return 0;
    }
}

short GetHoldEncoderEventFlag(long CardHandle, short EncoderNumber, short* OnOffMode)
{
    unsigned char CurValue;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &CurValue) ;
    *OnOffMode = ((CurValue & 16) == 0)? TurnOff : TurnOn ;
    return 1;
}

short SetEncoderEventFlagPolarity(long CardHandle, short EncoderNumber, short Polarity) 
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &OldValue) ;
    switch (Polarity) {
       case ActiveOnHigh:
          NewValue = OldValue & ~32 ;
          break ;

       case ActiveOnLow:
          NewValue = OldValue | 32 ;
          break ;

       default: // Unexpected value
          return 0;
    }
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return 1;
    } else {
       return 0;
    }
}

short GetEncoderEventFlagPolarity(long CardHandle, short EncoderNumber, short* Polarity)
{
    unsigned char CurValue;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &CurValue) ;
    *Polarity = ((CurValue & 32) == 0)? ActiveOnHigh : ActiveOnLow ;
    return 1;
}

short WriteOutputs(long CardHandle, short Outputs) 
{
    unsigned char OutputRegister ;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    OutputRegister = (Outputs << 4) & 0xF0 ;
    WriteByte(CardNumber, 14, OutputRegister) ;
    return 1;
}

short ReadInputs(long CardHandle, short* Inputs, short* EncoderEventFlags)
{
    unsigned char InputRegister ;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    ReadByte(CardNumber, 14, &InputRegister) ;
    *EncoderEventFlags = InputRegister & 3 ;
    *Inputs = (InputRegister >> 2) & 0x3F ;
    return 1;
}

short ClearEncoderEventFlag(long CardHandle, short EncoderNumber) 
{
    unsigned char Dummy ;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    // Following ReadByte clears the Encoder EventFlag:
    ReadByte(CardNumber, Addr, &Dummy) ;
    return 1;
}

short SetIndexResetsPositionCounter(long CardHandle, short EncoderNumber, short EnabledDisabledMode) 
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &OldValue) ;
    switch (EnabledDisabledMode) {
       case SetEnabled:
          NewValue = OldValue | 64 ;
          break ;

       case SetDisabled:
          NewValue = OldValue & ~64 ;
          break ;

       default: // Unexpected value
          return 0;
    }
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return 1;
    } else {
       return 0;
    }
}

short GetIndexResetsPositionCounter(long CardHandle, short EncoderNumber, short* EnabledDisabledMode) 
{
    unsigned char CurValue;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &CurValue) ;
    *EnabledDisabledMode = ((CurValue & 64) == 0)? SetDisabled : SetEnabled ;
    return 1;
}

short SetIndexPulsePolarity(long CardHandle, short EncoderNumber, short Polarity) 
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &OldValue) ;
    switch (Polarity) {
       case ActiveOnHigh:
          NewValue = OldValue | 128 ;
          break ;

       case ActiveOnLow:
          NewValue = OldValue & ~128 ;
          break ;

       default: // Unexpected value
          return 0;
    }
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return 1;
    } else {
       return 0;
    }
}

short GetIndexPulsePolarity(long CardHandle, short EncoderNumber, short* Polarity) 
{
    unsigned char CurValue;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }


    Addr = 0 ;   // Address of first Encoder Mode register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &CurValue) ;
    *Polarity = ((CurValue & 128) == 0)? ActiveOnLow : ActiveOnHigh ;
    return 1;
}

short GetSetPoint(long CardHandle, short EncoderNumber, long* SetPoint) 
{
    unsigned char SetPointLow, SetPointMiddle, SetPointHigh;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }



    Addr = 2 ;   // Address of first LOW SetPoint register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &SetPointLow) ;

    Addr = 4 ;   // Address of first MIDDLE SetPoint register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &SetPointMiddle) ;

    Addr = 6 ;   // Address of first HIGH SetPoint register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &SetPointHigh) ;

    *SetPoint = (unsigned long) SetPointLow +
                      ((unsigned long) SetPointMiddle << 8) +
                      ((unsigned long) SetPointHigh << 16) ;
    return 1;
}

short SetSetPoint(long CardHandle, short EncoderNumber, long SetPoint) 
{
    unsigned char SetPointLow, SetPointMiddle, SetPointHigh;
    unsigned char CurValue;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }

    if ( (SetPoint < 0) || (SetPoint > 0xFFFFFF) ) {
       // SetPoint exceeds 24 bit unsigned number.
       return 0;
    }


    SetPointLow    = SetPoint & 0xFF ;
    SetPointMiddle = (SetPoint & 0xFF00)   >> 8 ;
    SetPointHigh   = (SetPoint & 0xFF0000) >> 16 ;

    Addr = 2 ;   // Address of first LOW SetPoint register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    WriteByte(CardNumber, Addr, SetPointLow) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue != SetPointLow) {
       return 0;
    }

    Addr = 4 ;   // Address of first MIDDLE SetPoint register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    WriteByte(CardNumber, Addr, SetPointMiddle) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue != SetPointMiddle) {
       return 0;
    }

    Addr = 6 ;   // Address of first HIGH SetPoint register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    WriteByte(CardNumber, Addr, SetPointHigh) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue != SetPointHigh) {
       return 0;
    }

    return 1;
}

short ReadPositionCounter(long CardHandle, short EncoderNumber, long* PositionCounter) 
{
    unsigned char PositionCounterLow, PositionCounterMiddle, PositionCounterHigh;
    short Addr;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 1)) {
       return 0;
    }



    Addr = 8 ;   // Address of first LOW PositionCounter register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &PositionCounterLow) ;

    Addr = 10 ;   // Address of first MIDDLE PositionCounter register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &PositionCounterMiddle) ;

    Addr = 12 ;   // Address of first HIGH PositionCounter register.
    if (EncoderNumber == Encoder2)
       ++Addr;   // Current encoder is B or D.
    ReadByte(CardNumber, Addr, &PositionCounterHigh) ;

    *PositionCounter = (unsigned long) PositionCounterLow +
                      ((unsigned long) PositionCounterMiddle << 8) +
                      ((unsigned long) PositionCounterHigh << 16) ;
    return 1;
}

short RequestPositionCounter(long CardHandle, short EncoderNumber, short OutputsMirror) 
{
    unsigned char OldValue, NewValue ;
    unsigned char RequestMask ;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 3)) {
       return 0;
    }

    switch (EncoderNumber) {
       case Encoder1:
          RequestMask = 4 ;
          break ;

       case Encoder2:
          RequestMask = 8 ;
          break ;

       case Encoder3:
          RequestMask = 12 ;
          break ;

       case Encoder4:
          // (Legal value)
          break ;

       default:
          return 0 ; // Unexpected value.
    }

    if (EncoderNumber == Encoder4) {
       // Request for all 4 encoders.
       ReadByte(CardNumber, 15, &OldValue) ;
       NewValue =  OldValue | 3 ;
       WriteByte(CardNumber, 15, NewValue) ;
       WriteByte(CardNumber, 15, OldValue) ;  // Return original LEDS status.
      }
    else {
       // Request for one or two encoders.
       // Turn on the required (one or two) strobe(s):
       NewValue = (unsigned char) (OutputsMirror << 4) | RequestMask ;
       WriteByte(CardNumber, 14, NewValue) ;
      }
    return 1;
}

short ResetPositionCounter(long CardHandle, short EncoderNumber, short OutputsMirror) 
{
    unsigned char NewValue ;
    unsigned char ResetMask ;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidEncoderNumber(EncoderNumber, 2)) {
       return 0;
    }


    switch (EncoderNumber) {
       case Encoder1:
          ResetMask = 1 ;
          break ;

       case Encoder2:
          ResetMask = 2 ;
          break ;

       case Encoder3:
          ResetMask = 3 ;
          break ;

       default:
          return 0 ; // Unexpected value.
    }

    NewValue = (unsigned char) (OutputsMirror << 4) | ResetMask ;
    WriteByte(CardNumber, 14, NewValue) ;
    return 1;
}

short SetActiveChip(long CardHandle, short ChipNumber) 
{
    unsigned char NewValue, CurValue;
    short OldChipsMask ;
    short NewChipsMask ;
    short LedMask ;

    CardNumber=ExtractBaseAddress(CardHandle) ;

    if (!IsValidChipNumber(ChipNumber)) {
       return 0;
    }

    NewChipsMask = (ChipNumber == Chip1)? 4 : 0 ;

    ReadByte(CardNumber, 15, &CurValue) ;
    OldChipsMask = CurValue & 4 ;  // Current CHIPS state
    if (OldChipsMask == NewChipsMask) {
       // The required chip is already selected.
       return 1;
      }
    // The required chip is NOT selected. select it now:
    LedMask = CurValue & 3 ;  // Current LEDS state - should be kept the same.
    NewValue = NewChipsMask | LedMask ;
    WriteByte(CardNumber, 15, NewValue) ;
    ReadByte(CardNumber, 15, &CurValue) ;
    //CurValue = CurValue & 7 ; // Consider only the LEDS & CHIPS bits.
    CurValue = CurValue & 4 ; // Consider only the CHIPS bit.
    //if (CurValue == NewValue) {
    if (CurValue == NewChipsMask) {
       return 1;
      }
    else {
       if (OldChipsMask == 4) {
          // There was an attempt to switch from Chip #1 to Chip#2, but
          // there's only Chip #1 on the card. So, switch back to Chip#1:
          NewValue = 4 | LedMask ;
          WriteByte(CardNumber, 15, NewValue) ;
         }
       return 0 ;
      }
}

short GetActiveChip(long CardHandle, short* ChipNumber) 
{
    unsigned char  CurValue;
    short ChipsMask ;

    CardNumber=ExtractBaseAddress(CardHandle) ;


    ReadByte(CardNumber, 15, &CurValue) ;
    ChipsMask = CurValue & 4 ;
    *ChipNumber = (ChipsMask == 0)? Chip2 : Chip1 ;
    return 1;
}

short GetCardHandle(long* CardHandle, short CardIndex) 
{
// Return code:
//   1 - Success:
//               *-----------------------------------------*
//               |                 CardHandle              |
//               |-----------------------------------------|
//               |  BusNu   | DeviceNu |    BaseAddress    |
//       *-------+----------+----------+-------------------|
//       | Byte: |    3     |    2     |        0-1        |
//       *-------------------------------------------------*
//   0 or -1 - Failure:
//       Return code = 0  indicates FindPCIdev failure, which is normal after
//                        all card(s) have been detected.
//                        BusNu & DeviceNu are irrelevant.
//                        BaseAddress = Status produced by FindPCIdev.
//       Return code = -1 indicates GetPCIWord failure after a successful
//                        FindPCIdev, which is abnormal.
//                        BaseAddress = Status produced by GetPCIWord.
short VendorID, DeviceID ;
unsigned char DeviceNu ;
unsigned char BusNu ;
unsigned short BaseAddress ;
short OK ;

	VendorID = 0x4452 ;
	DeviceID = 0x4950 ;
        OK = FindPCIdev(&DeviceNu, &BusNu, VendorID, DeviceID, CardIndex) ;
        if (OK == 1) {
                // FindPCIdev success.
                // Try to produce BaseAddress (located in address 0x10, see Table 878, above):
                OK = GetPCIWord(&BaseAddress, BusNu, DeviceNu, 0x10) ;
                // (About the internal structure of BaseAddress,
                // refer to Table 882, above.)
                if (OK == 1)
                        // GetPCIWord success:
                        BaseAddress &= 0xFFFC ; // 2 low bits are not part of the base address.
                // (Else BaseAddress contains the status returned by GetPCIWord.)
                *CardHandle = (BusNu << 24) | (DeviceNu << 16) | BaseAddress ;
                return ((OK == 1)? 1 : -1) ;  // (Success or GetPCIWord failure)
        }
        else {
                // FindPCIdev failure.
                *CardHandle = DeviceNu ; // Note that in case FindPCIdev
                // fails, it returns in DeviceNu the STAUS.
                return (0) ;  // FindPCIdev failure
        }
}

short GetRevisionID(long CardHandle, short* RevisionID) 
{
// Return Code:
//   1 - Success (RevisionID read).
//   0 - Failure (RevisionID not read. Instead, it contains Status of GetPCIByte).
short OK ;
unsigned char RevisionIDByte ;

        // Read RevisionID (located in address 8, see Table 878, above):
        OK = GetPCIByte(&RevisionIDByte, ExtractBusNu(CardHandle), ExtractDeviceNu(CardHandle), 8) ;
        *RevisionID = RevisionIDByte ;
        // (We have to read as byte and return the argument as short,
        // therefore can't read directly into RevisionID.)
        return (OK) ;
}

