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

#include "enc.h"

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

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

// Based on LcicApi.cpp:

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

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

// Based on LcicCtl.cpp:

BOOLEAN IsValidCardNumber(short CardNumber)
{
	if ( (CardNumber >= 0) && (CardNumber <= 15) )
		return TRUE;
	else
		return FALSE ; // Unexpected value.
}

BOOLEAN IsValidChipNumber(short ChipNumber)
{
    switch (ChipNumber) {
       case Chip1:
       case Chip2:
          return TRUE;
	  break ;

       default:
          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;
          break ;

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

       case Encoder4:
          if (Level == 3)
             return TRUE;
          else
             return FALSE;
          break ;

       default:
          return FALSE ; // Unexpected value.
    }
}

BOOLEAN TestActiveChip (short CardNumber)
//  * 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 ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

    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 S_FALSE;
       }
    }
    WriteByte(CardNumber, 15, OldValue) ;  // Return original setting.
    return S_OK;
}

BOOLEAN SetLed (short CardNumber, short LedNumber, short OnOffMode)
{
    unsigned char OldValue, NewValue, CurValue, Mask ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }
    if ( (LedNumber != LedYellow) && (LedNumber != LedRed) ) {
       return S_FALSE;
    }

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

       case LedRed:
          Mask = 2 ;
          break ;

       default:
          return S_FALSE ; // 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 S_OK;
    } else {
       return S_FALSE;
    }
}

BOOLEAN GetLed (short CardNumber, short LedNumber, short* OnOffMode)
{
    unsigned char CurValue, Mask ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }
    if ( (LedNumber != LedYellow) && (LedNumber != LedRed) ) {
       return S_FALSE;
    }

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

       case LedRed:
          Mask = 2 ;
          break ;

       default:
          return S_FALSE ; // Unexpected value.
    }

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

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

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


    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 S_FALSE;
    }
    NewValue = (OldValue & ~3) | Mask ;
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return S_OK;
    } else {
       return S_FALSE;
    }
}

BOOLEAN GetEncoderResolution (short CardNumber, short EncoderNumber, short* ClocksPerCycle)
{
    unsigned char CurValue, Mask;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


    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 S_FALSE;
//          *ClocksPerCycle = UnexpectedValue ;
//          break ;
    }
    return S_OK;
}

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

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


    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 S_FALSE;
    }
    NewValue = (OldValue & ~12) | Mask ;
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return S_OK;
    } else {
       return S_FALSE;
    }
}

BOOLEAN GetEncoderEventFlagSource (short CardNumber, short EncoderNumber, short* EventFlagSource)
{
    unsigned char CurValue, Mask;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


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

BOOLEAN SetHoldEncoderEventFlag (short CardNumber, short EncoderNumber, short OnOffMode)
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


    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 S_FALSE;
    }
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return S_OK;
    } else {
       return S_FALSE;
    }
}

BOOLEAN GetHoldEncoderEventFlag (short CardNumber, short EncoderNumber, short* OnOffMode)
{
    unsigned char CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


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

BOOLEAN SetEncoderEventFlagPolarity (short CardNumber, short EncoderNumber, short Polarity)
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


    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 S_FALSE;
    }
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return S_OK;
    } else {
       return S_FALSE;
    }
}

BOOLEAN GetEncoderEventFlagPolarity (short CardNumber, short EncoderNumber, short* Polarity)
{
    unsigned char CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


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

BOOLEAN WriteOutputs (short CardNumber, short Outputs)
{
    unsigned char OutputRegister ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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

BOOLEAN ReadInputs (short CardNumber, short* Inputs, short* EncoderEventFlags)
{
    unsigned char InputRegister ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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

BOOLEAN ClearEncoderEventFlag (short CardNumber, short EncoderNumber)
{
    unsigned char Dummy ;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


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

BOOLEAN SetIndexResetsPositionCounter (short CardNumber, short EncoderNumber, short EnabledDisabledMode)
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


    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 S_FALSE;
    }
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return S_OK;
    } else {
       return S_FALSE;
    }
}

BOOLEAN GetIndexResetsPositionCounter (short CardNumber, short EncoderNumber, short* EnabledDisabledMode)
{
    unsigned char CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


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

BOOLEAN SetIndexPulsePolarity (short CardNumber, short EncoderNumber, short Polarity)
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


    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 S_FALSE;
    }
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return S_OK;
    } else {
       return S_FALSE;
    }
}

BOOLEAN GetIndexPulsePolarity (short CardNumber, short EncoderNumber, short* Polarity)
{
    unsigned char CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


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

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

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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



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

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

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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

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


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

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

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

    return S_OK;
}

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

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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



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

BOOLEAN RequestPositionCounter (short CardNumber, short EncoderNumber, short OutputsMirror)
{
    unsigned char OldValue, NewValue ;
    unsigned char RequestMask ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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

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

       case Encoder2:
          RequestMask = 8 ;
          break ;

       case Encoder3:
          RequestMask = 12 ;
          break ;

       case Encoder4:
          // (Legal value)
          break ;

       default:
          return S_FALSE ; // 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 S_OK;
}

BOOLEAN ResetPositionCounter (short CardNumber, short EncoderNumber, short OutputsMirror)
{
    unsigned char NewValue ;
    unsigned char ResetMask ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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


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

       case Encoder2:
          ResetMask = 2 ;
          break ;

       case Encoder3:
          ResetMask = 3 ;
          break ;

       default:
          return S_FALSE ; // Unexpected value.
    }

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

// IRQ functions:

BOOLEAN SetInterruptMode (short CardNumber, short EnabledDisabledMode)
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }


    Addr = 15 ;   // Address of General Mode register.
    ReadByte(CardNumber, Addr, &OldValue) ;
    switch (EnabledDisabledMode) {
       case SetEnabled:
          NewValue = OldValue | 16 ;
          break ;

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

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

BOOLEAN GetInterruptMode (short CardNumber, short* EnabledDisabledMode)
{
    unsigned char CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }


    Addr = 15 ;   // Address of General Mode register.
    ReadByte(CardNumber, Addr, &CurValue) ;
    *EnabledDisabledMode = ((CurValue & 16) == 0)? SetDisabled : SetEnabled ;
    return S_OK;
}

BOOLEAN SetInterruptPolarity (short CardNumber, short Polarity)
{
    unsigned char OldValue, NewValue, CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }


    Addr = 15 ;   // Address of General Mode register.
    ReadByte(CardNumber, Addr, &OldValue) ;
    switch (Polarity) {
       case ActiveOnHigh:
          NewValue = OldValue | 8 ;
          break ;

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

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

BOOLEAN GetInterruptPolarity (short CardNumber, short* Polarity)
{
    unsigned char CurValue;
    short Addr;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }


    Addr = 15 ;   // Address of General Mode register.
    ReadByte(CardNumber, Addr, &CurValue) ;
    *Polarity = ((CurValue & 8) == 0)? ActiveOnLow : ActiveOnHigh ;
    return S_OK;
}

BOOLEAN SetInterruptSource (short CardNumber, short InterruptSource)
{
    unsigned char OldValue, NewValue, CurValue ;
    unsigned char Mask ;
    short Addr ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

    Addr = 15 ;   // Address of General Mode register.
    ReadByte(CardNumber, Addr, &OldValue) ;
    switch (InterruptSource) {
       case InterruptSourceEncoder1Event: // Event in the first  encoder in the selected chip (A or C).
          Mask = 0 ;
          break ;

       case InterruptSourceEncoder2Event: // Event in the second encoder in the selected chip (B or D).
          Mask = 1 ;
          break ;

       case InterruptSourceChipInput0: // Input #0 in the selected chip.
          Mask = 2 ;
          break ;

       case InterruptSourceChipInput1: // Input #1 in the selected chip.
          Mask = 3 ;
          break ;

       case InterruptSourceChipInput2: // Input #2 in the selected chip.
          Mask = 4 ;
          break ;

       case InterruptSourceChipInput3: // Input #3 in the selected chip.
          Mask = 5 ;
          break ;

       case InterruptSourceChipInput4: // Input #4 in the selected chip.
          Mask = 6 ;
          break ;

       case InterruptSourceVoltageFailureOrChip2Input5: // Voltage Failure / Input #5 in Chip #2.
          Mask = 7 ;
          break ;

       default: // Unexpected value
          return S_FALSE;
    }
    Mask = Mask << 5 ;
    NewValue = (OldValue & 0x1F) | Mask ;
    WriteByte(CardNumber, Addr, NewValue) ;
    ReadByte(CardNumber, Addr, &CurValue) ;
    if (CurValue == NewValue) {
       return S_OK;
    } else {
       return S_FALSE;
    }
}

BOOLEAN GetInterruptSource (short CardNumber, short* InterruptSource)
{
    unsigned char CurValue ;
    unsigned char Mask ;
    short Addr ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }


    Addr = 15 ;   // Address of General Mode register.
    ReadByte(CardNumber, Addr, &CurValue) ;
    Mask = (CurValue >> 5) & 7 ;
    switch (Mask) {
       case 0:
          *InterruptSource = InterruptSourceEncoder1Event ; // Event in the first  encoder in the selected chip (A or C).
          break ;

       case 1:
          *InterruptSource = InterruptSourceEncoder2Event ; // Event in the second encoder in the selected chip (B or D).
          break ;

       case 2:
          *InterruptSource = InterruptSourceChipInput0 ; // Input #0 in the selected chip.
          break ;
          
       case 3:
          *InterruptSource = InterruptSourceChipInput1 ; // Input #1 in the selected chip.
          break ;
          
       case 4:
          *InterruptSource = InterruptSourceChipInput2 ; // Input #2 in the selected chip.
          break ;
          
       case 5:
          *InterruptSource = InterruptSourceChipInput3 ; // Input #3 in the selected chip.
          break ;
          
       case 6:
          *InterruptSource = InterruptSourceChipInput4 ; // Input #4 in the selected chip.
          break ;
          
       case 7:
          *InterruptSource = InterruptSourceVoltageFailureOrChip2Input5 ; // Voltage Failure / Input #5 in Chip #2.
          break ;
    }
    return S_OK;
}

BOOLEAN SetActiveChip(short CardNumber, short ChipNumber)
{
    unsigned char NewValue, CurValue;
    short OldChipsMask ;
    short NewChipsMask ;
    short LedMask ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }

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

    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 S_OK;
      }
    // 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 S_OK;
      }
    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 S_FALSE ;
      }
}

BOOLEAN GetActiveChip(short CardNumber, short* ChipNumber)
{
    unsigned char  CurValue;
    short ChipsMask ;

    if (!IsValidCardNumber(CardNumber)) {
       return S_FALSE;
    }


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


