Unit IntvControls;

Interface

Uses INIFiles,HexWrite,Classes;

Type
  GRIP_SLOT     = Byte;
  GRIP_CLASS    = Byte;
  GRIP_INDEX    = Byte;
  GRIP_VALUE    = Word;
  GRIP_BITFIELD = Cardinal;

Const
  { Standard Classes }

  GRIP_CLASS_BUTTON   = 1;
  GRIP_CLASS_AXIS     = 2;
  GRIP_CLASS_POV_HAT  = 3;
  GRIP_CLASS_VELOCITY = 4;
  GRIP_CLASS_THROTTLE = 5;
  GRIP_CLASS_ANALOG   = 6;

  MaxChannelLength = 256;

  { Define interface functions }

  ifKeypad_1       = $00000001; { LongInts 1-4 }
  ifKeypad_2       = $00000002;
  ifKeypad_3       = $00000004;
  ifKeypad_4       = $00000008;
  ifKeypad_5       = $00000010;
  ifKeypad_6       = $00000020;
  ifKeypad_7       = $00000040;
  ifKeypad_8       = $00000080;
  ifKeypad_9       = $00000100;
  ifKeypad_C       = $00000200;
  ifKeypad_0       = $00000400;
  ifKeypad_E       = $00000800;

  ifSide_T         = $00001000;
  ifSide_L         = $00002000;
  ifSide_R         = $00004000; { Leave 8 blank }

  ifDisc_N         = $00010000;
  ifDisc_NNE       = $00020000;
  ifDisc_NE        = $00040000;
  ifDisc_ENE       = $00080000;
  ifDisc_E         = $00100000;
  ifDisc_ESE       = $00200000;
  ifDisc_SE        = $00400000;
  ifDisc_SSE       = $00800000;
  ifDisc_S         = $01000000;
  ifDisc_SSW       = $02000000;
  ifDisc_SW        = $04000000;
  ifDisc_WSW       = $08000000;
  ifDisc_W         = $10000000;
  ifDisc_WNW       = $20000000;
  ifDisc_NW        = $40000000;
  ifDisc_NNW       = $80000000;

  ifKB_LBRACE      = $00000001; { LongInt 5 }
  ifKB_COMMA       = $00000002;
  ifKB_N           = $00000004;
  ifKB_V           = $00000008;
  ifKB_X           = $00000010;
  ifKB_SPACE       = $00000020;
  ifKB_PERIOD      = $00000040;
  ifKB_M           = $00000080;
  ifKB_B           = $00000100;
  ifKB_C           = $00000200;
  ifKB_Z           = $00000400;
  ifKB_RBRACE      = $00000800;
  ifKB_SCOLON      = $00001000;
  ifKB_K           = $00002000;
  ifKB_H           = $00004000;
  ifKB_F           = $00008000;
  ifKB_S           = $00010000;
  ifKB_EQUAL       = $00020000;
  ifKB_P           = $00040000;
  ifKB_I           = $00080000;
  ifKB_Y           = $00100000;
  ifKB_R           = $00200000;
  ifKB_W           = $00400000;
  ifKB_Q           = $00800000;
  ifKB_OQUOTE      = $01000000;
  ifKB_9           = $02000000;
  ifKB_7           = $04000000;
  ifKB_5           = $08000000;
  ifKB_3           = $10000000;
  ifKB_1           = $20000000;
  ifKB_0           = $40000000;
  ifKB_8           = $80000000;
  ifKB_6           = $00000001; { LongInt 6 }
  ifKB_4           = $00000002;
  ifKB_2           = $00000004;
  ifKB_BSLASH      = $00000008;
  ifKB_BACKSPC     = $00000010;
  ifKB_O           = $00000020;
  ifKB_U           = $00000040;
  ifKB_T           = $00000080;
  ifKB_E           = $00000100;
  ifKB_LCTRL       = $00000200;
  ifKB_L           = $00000400;
  ifKB_J           = $00000800;
  ifKB_G           = $00001000;
  ifKB_D           = $00002000;
  ifKB_A           = $00004000;
  ifKB_LSHIFT      = $00008000;

  ifSYN_1          = $00000001; { LongInt 7 }
  ifSYN_2          = $00000002;
  ifSYN_3          = $00000004;
  ifSYN_4          = $00000008;
  ifSYN_5          = $00000010;
  ifSYN_6          = $00000020;
  ifSYN_7          = $00000040;
  ifSYN_8          = $00000080;
  ifSYN_9          = $00000100;
  ifSYN_10         = $00000200;
  ifSYN_11         = $00000400;
  ifSYN_12         = $00000800;
  ifSYN_13         = $00001000;
  ifSYN_14         = $00002000;
  ifSYN_15         = $00004000;
  ifSYN_16         = $00008000;
  ifSYN_17         = $00010000;
  ifSYN_18         = $00020000;
  ifSYN_19         = $00040000;
  ifSYN_20         = $00080000;
  ifSYN_21         = $00100000;
  ifSYN_22         = $00200000;
  ifSYN_23         = $00400000;
  ifSYN_24         = $00800000;
  ifSYN_25         = $01000000;
  ifSYN_26         = $02000000;
  ifSYN_27         = $04000000;
  ifSYN_28         = $08000000;
  ifSYN_29         = $10000000;
  ifSYN_30         = $20000000;
  ifSYN_31         = $40000000;
  ifSYN_32         = $80000000;
  ifSYN_33         = $00000001; { LongInt 8 }
  ifSYN_34         = $00000002;
  ifSYN_35         = $00000004;
  ifSYN_36         = $00000008;
  ifSYN_37         = $00000010;
  ifSYN_38         = $00000020;
  ifSYN_39         = $00000040;
  ifSYN_40         = $00000080;
  ifSYN_41         = $00000100;
  ifSYN_42         = $00000200;
  ifSYN_43         = $00000400;
  ifSYN_44         = $00000800;
  ifSYN_45         = $00001000;
  ifSYN_46         = $00002000;
  ifSYN_47         = $00004000;
  ifSYN_48         = $00008000;

  ifPower          = $00008000;  { LongInt 1 }
  ifReset          = $00008000;  { LongInt 2 }

{
  ifPower          = $00010000;
  ifReset          = $00020000;
}
  { Define interface masks }

  imKeypad         = ifKeypad_1 + ifKeypad_2 + ifKeypad_3 + ifKeypad_4 +
                     ifKeypad_5 + ifKeypad_6 + ifKeypad_7 + ifKeypad_8 +
                     ifKeypad_9 + ifKeypad_C + ifKeypad_0 + ifKeypad_E;
  imSide           = ifSide_T + ifSide_L + ifSide_R;
  imDisc           = ifDisc_N + ifDisc_NNE + ifDisc_NE + ifDisc_ENE +
                     ifDisc_E + ifDisc_ESE + ifDisc_SE + ifDisc_SSE +
                     ifDisc_S + ifDisc_SSW + ifDisc_SW + ifDisc_WSW +
                     ifDisc_W + ifDisc_WNW + ifDisc_NW + ifDisc_NNW;

  imConsole        = ifPower + ifReset;
  imNoConsole      = Not imConsole;

  imNoDisc         = Not imDisc;
  imAllCaps        = $FFFFFFFF;

  { Define controller masks }

  cmController     = imKeypad + imSide + imDisc;

  { Console flags }

  cfPower   = $01;
  cfReset   = $02;

  { Controller bit flags }

  KP_1 = $81;
  KP_2 = $41;
  KP_3 = $21;
  KP_4 = $82;
  KP_5 = $42;
  KP_6 = $22;
  KP_7 = $84;
  KP_8 = $44;
  KP_9 = $24;
  KP_C = $88;
  KP_0 = $48;
  KP_E = $28;

  Side_Up = $A0;
  Side_BL = $60;
  Side_BR = $C0;

  Disc_S = $01;
  Disc_E = $02;
  Disc_N = $04;
  Disc_W = $08;

  Disc_NE = $16;
  Disc_NW = $1C;
  Disc_SW = $19;
  Disc_SE = $13;

  Disc_ENE = $06;
  Disc_NNE = $14;
  Disc_NNW = $0C;
  Disc_WNW = $18;
  Disc_WSW = $09;
  Disc_SSW = $11;
  Disc_SSE = $03;
  Disc_ESE = $12;

  ifKeypad_Mask : Array[1..12] Of LongWord =
   (ifKeypad_1,ifkeypad_2,ifKeypad_3,ifKeypad_4,ifKeypad_5,ifKeypad_6,
    ifKeypad_7,ifkeypad_8,ifKeypad_9,ifKeypad_C,ifKeypad_0,ifKeypad_E);
  ifSide_Mask : Array[1..3] Of LongWord = (ifSide_T,ifSide_L,ifSide_R);
  ifDisc_Mask : Array[1..16] Of LongWord =
   (ifDisc_N,ifDisc_NNE,ifDisc_NE,ifDisc_ENE,
    ifDisc_E,ifDisc_ESE,ifDisc_SE,ifDisc_SSE,
    ifDisc_S,ifDisc_SSW,ifDisc_SW,ifDisc_WSW,
    ifDisc_W,ifDisc_WNW,ifDisc_NW,ifDisc_NNW);

  Keypad_Mask: Array[1..12] Of Byte =
   (KP_1,KP_2,KP_3,KP_4,KP_5,KP_6,KP_7,KP_8,KP_9,KP_C,KP_0,KP_E);
  Side_Mask: Array[1..3] Of Byte = (Side_Up,Side_BL,Side_BR);
  Disc_Mask: Array[1..16] Of Byte =
   (Disc_N,Disc_NNE,Disc_NE,Disc_ENE,
    Disc_E,Disc_ESE,Disc_SE,Disc_SSE,
    Disc_S,Disc_SSW,Disc_SW,Disc_WSW,
    Disc_W,Disc_WNW,Disc_NW,Disc_NNW);

  { Other constants }

  Max_Digital_Con  = 32;
  Max_Digital_Btn  = 32;
  Max_Digital_Axis = 32;
  Max_Digital_Hat  = 32;
  Max_Digital_Thr  = 32;
  Max_Digital_Ana  = 32;

  Max_Joy_Btn   = 8;
  Max_Joy_Axis  = 6;

  Indy500Trans1 : Packed Array[0..3] Of Byte = ($EF,$FF,$DF,$CF);
  Indy500Trans2 : Packed Array[0..3] Of Byte = ($FE,$FF,$FD,$FC);

  { Define analog joystick extensions (Thanks to Vojtech Pavlik, author }
  { of the Linux joystick driver)                                       }

  JS_AN_BUTTONS_CHF    = $01;
  JS_AN_HAT1_CHF       = $02;
  JS_AN_HAT2_CHF       = $04;
  JS_AN_ANY_CHF        = $07;
  JS_AN_HAT_FCS        = $08;
  JS_AN_HATS_ALL       = $0e;
  JS_AN_BUTTON_PXY_X   = $10;
  JS_AN_BUTTON_PXY_Y   = $20;
  JS_AN_BUTTON_PXY_U   = $40;
  JS_AN_BUTTON_PXY_V   = $80;
  JS_AN_BUTTONS_PXY_XY = $30;
  JS_AN_BUTTONS_PXY_UV = $c0;
  JS_AN_BUTTONS_PXY    = $f0;

  LPTPorts : Array[0..2] Of Word = ($3BC,$378,$278);

Type
  TInterfaceName  = String[20];

  TIntvControls  = Record
    P       : Array[1..4] Of Word;
    Console : Byte;
  End;

  TCapability     = Record
    Cont : Array[1..4] Of LongWord;
  End; { TCapability }

  { Analog axis record }

  TAxis = Record
    Cap : TCapability;
    Min : LongInt;
    Mid : LongInt;
    Max : LongInt;
  End;

  { Record defining capabilities of standard PC joystick, including     }
  { extended versions such as the Thrustmaster FCS, CH FlightStick Pro, }
  { and other assorted gamepads.                                        }

  TJoystickRec    = Record
    Axis : Array[1..Max_Joy_Axis] Of TAxis;      { Allow for 2 hats      }
    Btn  : Array[1..Max_Joy_Btn]  Of TCapability; { Allow up to 8 buttons }
  End;

  { Record defining digital controller capabilities }

  TDigitalRec = Record
    Loaded   : Boolean;
    ClassMap : LongInt;
    NumBtns  : Integer;
    NumAxes  : Integer;
    NumHats  : Integer;
    NumThrs  : Integer;
    NumAnas  : Integer;
    Btn  : Array[1..Max_Digital_Btn]  Of TCapability;
    Axis : Array[1..Max_Digital_Axis] Of TCapability;
    Hat  : Array[1..Max_Digital_Hat]  Of TCapability;
    Thr  : Array[1..Max_Digital_Thr]  Of TAxis;
    Ana  : Array[1..Max_Digital_Ana]  Of TAxis;
  End;

  TDigitalInfoRec = Record
    Buttons  : GRIP_BITFIELD;
    Axis     : Array[1..Max_Digital_Axis] Of Word;
    Hat      : Array[1..Max_Digital_Hat]  Of Word;
    Throttle : Array[1..Max_Digital_Thr]  Of Word;
    Analog   : Array[1..Max_Digital_Ana]  Of Word;
  End;

  { TInterface abstract class: VERY emulator-specific }

  TInterface = Class
  Public
    Name     : TInterfaceName;
    Enabled  : Boolean;
    Active   : Boolean;
    ReadMask : Byte;
    Constructor Create(St: String);
    Destructor  Destroy;                                          Override;
    Procedure   LoadConfiguration(F: TINIFile);                   Virtual;
    Procedure   SaveConfiguration(F: TINIFile);                   Virtual;
    Procedure   GetControllerInfo(Var Controllers: TCapability;
                                  Var Info: TIntvControls);      Virtual; Abstract;
    Procedure   Activate;                                         Virtual;
    Procedure   Deactivate;                                       Virtual;
    Procedure   GetDigitalAny(On: Boolean;
                              Var Cap: TCapability;
                              Var CapMask: TCapability;
                              Var Controllers: TCapability;
                              Var Info: TIntvControls);          Virtual;
    Procedure   GetAnalog(XMin,XMid,XMax,XValue,XM,
                          YMin,YMid,YMax,YValue,YM: LongInt;
                          Var Cap: TCapability;
                          Var CapMask: TCapability;
                          Var Controllers: TCapability;
                          Var Info: TIntvControls);              Virtual;
    Procedure   WriteToPort(B: Byte);                             Virtual;

    { Capability routines }

    Function    UsedCapability(Var Cap: TCapability): Boolean;
    Procedure   ReadCapability(F: TINIFile; Const Section,Ident: String; Var Cap: TCapability);
    Procedure   WriteCapability(F: TINIFile; Const Section,Ident: String; Var Cap: TCapability);
    Procedure   EraseCapability(F: TINIFile; Const Section,Ident: String);
    Procedure   SetAllCapability(Var Cap: TCapability);
    Procedure   AndCapabilities(Var Src,Dest: TCapability);
    Procedure   CopyTo(Intf: TInterface);                         Virtual;
  End; { TInterface }

  { Interface handler class: emulator non-specific }

  TInterfaceHandler = Class
  Public
    Stack : TStringList;
    Constructor Create;
    Destructor  Destroy;                                          Override;
    Procedure   LoadConfiguration(F: TINIFile);
    Procedure   SaveConfiguration(F: TINIFile);
    Procedure   GetControllerInfo(Controllers: TCapability;
                                  Var Info: TIntvControls);
    Procedure   AddInterface(Int: TInterface);
    Procedure   ActivateInterfaces;
    Procedure   DeactivateInterfaces;
    Procedure   SetReadMask(Mask: Byte);
    Procedure   WriteToPort(B: Byte);
  End; { TInterfaceHandler }

  { Subclasses of TInterface: partially emulator-specific }

  TMouseInterface = Class(TInterface)
  Private
    OldX      : Integer;
    OldY      : Integer;
  Public
    MouseX    : TAxis;
    MouseY    : TAxis;
    MouseBtn  : Array[1..3] Of TCapability;
    Constructor Create;
    Destructor  Destroy;                                          Override;
    Procedure   LoadConfiguration(F: TINIFile);                   Override;
    Procedure   SaveConfiguration(F: TINIFile);                   Override;
    Procedure   GetControllerInfo(Var Controllers: TCapability;
                                  Var Info: TIntvControls);       Override;
    Procedure   CopyTo(Intf: TInterface);                         Override;
  End; { TMouseInterface }

  TKeyboardInterface = Class(TInterface)
  Private
    ConfigKeyPressed : Boolean;
    ConfigKey        : Byte;
  Public
    KeyCap : Array[0..$FF] Of TCapability;
    Keys   : Array[0..$FF] Of Boolean;
    Constructor Create;
    Destructor  Destroy;                                          Override;
    Procedure   LoadConfiguration(F: TINIFile);                   Override;
    Procedure   SaveConfiguration(F: TINIFile);                   Override;
    Procedure   GetControllerInfo(Var Controllers: TCapability;
                                  Var Info: TIntvControls);       Override;
    Procedure   CopyTo(Intf: TInterface);                         Override;
  End; { TKeyboardInterface }

  TExternalInterface = Class(TInterface)
    LPTCap : Array[0..2] Of TCapability;
    Wrote  : Byte;
    Constructor Create;
    Destructor  Destroy;                                          Override;
    Procedure   Activate;                                         Override;
    Procedure   Deactivate;                                       Override;
    Procedure   LoadConfiguration(F: TINIFile);                   Override;
    Procedure   SaveConfiguration(F: TINIFile);                   Override;
    Procedure   GetControllerInfo(Var Controllers: TCapability;
                                  Var Info: TIntvControls);       Override;
    Procedure   CopyTo(Intf: TInterface);                         Override;
  End; { TExternalInterface }

  { TDigitalInterface abstract class }

  TCallbackRec = Record
    Slot  : GRIP_SLOT;
  End; { TCallbackRec }

  TDigitalInterface = Class(TInterface)
  Public
    Cap       : Array[1..Max_Digital_Con] Of TDigitalRec;
    ContInfo  : Array[1..Max_Digital_Con] Of TDigitalInfoRec;
    ConInfo   : Array[1..Max_Digital_Con] Of TDigitalInfoRec;
    Constructor Create(St: String);
    Destructor  Destroy;                                          Override;
    Procedure   LoadConfiguration(F: TINIFile);                   Override;
    Procedure   SaveConfiguration(F: TINIFile);                   Override;
    Procedure   GetControllerInfo(Var Controllers: TCapability;
                                  Var Info: TIntvControls);      Override;
    Procedure   RefreshControllers;                               Virtual;
    Function    GetVendorName(Slot: Integer): String;             Virtual;
    Function    GetProductName(Slot: Integer): String;            Virtual;
    Function    GetSlotMap: GRIP_BITFIELD;                        Virtual;
    Function    GetClassMap(S: GRIP_SLOT): GRIP_BITFIELD;         Virtual;
    Function    GetMaxIndex(S: GRIP_SLOT; C: GRIP_CLASS): GRIP_INDEX; Virtual;
    Function    GetMaxValue(S: GRIP_SLOT; C: GRIP_CLASS): GRIP_VALUE; Virtual;
    Function    GetPackedValues(S: GRIP_SLOT; C: GRIP_CLASS; IStart: GRIP_INDEX;
                                IEnd: GRIP_INDEX): GRIP_BITFIELD; Virtual;
    Function    GetValue(S: GRIP_SLOT; C: GRIP_CLASS;
                         I: GRIP_INDEX): GRIP_VALUE;              Virtual;
    Procedure   CopyTo(Intf: TInterface);                         Override;
  End; { TDigitalInterface }

Function PollIntv2PC: Boolean;

Var
  InterfaceHandler: TInterfaceHandler;
  _MouseB           : Integer;
  OldKey            : Packed Array[0..255] Of Byte;
  Intv2PC           : Boolean;   { True if "External" interface is active }
  Intv2PCPort1      : Word;      { LPT port for controllers 1 & 2         }
  Intv2PCPort2      : Word;      { LPT port for controllers 3 & 4         }

Implementation

Uses Dialogs,SysUtils,Forms,frmMainUnt,DXInput,Controls,Windows,Math,DirectX;

Type
  TXYPoint = Record
    X,Y: Integer;
  End;
  TJoystickDir = (jdLeft,jdRight,jdUp,jdDown);
  TKeyStr = String[5];
  TKeyRec = Record
    Name   : TKeyStr;
    X      : Integer;
    Y      : Integer;
  End;

Const
  KeyList : Array[0..$FF] Of TKeyRec =
   ((Name:'';      X:0;  Y:0),  (Name:'ESC';   X:0;  Y:0),  (Name:'1';     X:4;  Y:7),  (Name:'2';     X:6;  Y:7),  { 00 }
    (Name:'3';     X:8;  Y:7),  (Name:'4';     X:10; Y:7),  (Name:'5';     X:12; Y:7),  (Name:'6';     X:14; Y:7),
    (Name:'7';     X:16; Y:7),  (Name:'8';     X:18; Y:7),  (Name:'9';     X:20; Y:7),  (Name:'0';     X:22; Y:7),
    (Name:'-';     X:0;  Y:0),  (Name:'=';     X:0;  Y:0),  (Name:'BKSPC'; X:29; Y:7),  (Name:'TAB';   X:2;  Y:9),
    (Name:'Q';     X:6;  Y:9),  (Name:'W';     X:8;  Y:9),  (Name:'E';     X:10; Y:9),  (Name:'R';     X:12; Y:9),  { 10 }
    (Name:'T';     X:14; Y:9),  (Name:'Y';     X:16; Y:9),  (Name:'U';     X:18; Y:9),  (Name:'I';     X:20; Y:9),
    (Name:'O';     X:22; Y:9),  (Name:'P';     X:24; Y:9),  (Name:'[';     X:26; Y:9),  (Name:']';     X:28; Y:9),
    (Name:'ENTER'; X:29; Y:11), (Name:'CTL';   X:2;  Y:15), (Name:'A';     X:7;  Y:11), (Name:'S';     X:9;  Y:11),
    (Name:'D';     X:11; Y:11), (Name:'F';     X:13; Y:11), (Name:'G';     X:15; Y:11), (Name:'H';     X:17; Y:11), { 20 }
    (Name:'J';     X:19; Y:11), (Name:'K';     X:21; Y:11), (Name:'L';     X:23; Y:11), (Name:';';     X:25; Y:11),
    (Name:'''';    X:27; Y:11), (Name:'`';     X:2;  Y:7),  (Name:'SHIFT'; X:2;  Y:13), (Name:'\';     X:32; Y:9),
    (Name:'Z';     X:8;  Y:13), (Name:'X';     X:10; Y:13), (Name:'C';     X:12; Y:13), (Name:'V';     X:14; Y:13),
    (Name:'B';     X:16; Y:13), (Name:'N';     X:18; Y:13), (Name:'M';     X:20; Y:13), (Name:',';     X:22; Y:13), { 30 }
    (Name:'.';     X:24; Y:13), (Name:'/';     X:26; Y:13), (Name:'SHIFT'; X:29; Y:13), (Name:'*';     X:64; Y:7),
    (Name:'ALT';   X:9;  Y:15), (Name:'SPACE'; X:14; Y:15), (Name:'CAPS';  X:2;  Y:11), (Name:'F1';    X:0;  Y:0),
    (Name:'F2';    X:0;  Y:0),  (Name:'F3';    X:0;  Y:0),  (Name:'F4';    X:0;  Y:0),  (Name:'F5';    X:0;  Y:0),
    (Name:'F6';    X:0;  Y:0),  (Name:'F7';    X:0;  Y:0),  (Name:'F8';    X:0;  Y:0),  (Name:'F9';    X:0;  Y:0),  { 40 }
    (Name:'F10';   X:0;  Y:0),  (Name:'NUM';   X:55; Y:7),  (Name:'SCRL';  X:43; Y:3),  (Name:'7';     X:56; Y:9),
    (Name:'8';     X:60; Y:9),  (Name:'9';     X:64; Y:9),  (Name:'-';     X:68; Y:7),  (Name:'4';     X:56; Y:11),
    (Name:'5';     X:60; Y:11), (Name:'6';     X:64; Y:11), (Name:'+';     X:68; Y:10), (Name:'1';     X:56; Y:13),
    (Name:'2';     X:60; Y:13), (Name:'3';     X:64; Y:13), (Name:'0';     X:58; Y:15), (Name:'.';     X:64; Y:15), { 50 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'F11';   X:0;  Y:0),
    (Name:'F12';   X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  { 60 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  { 70 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  { 80 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  { 90 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'ENT';   X:67; Y:14), (Name:'CTL';   X:32; Y:15), (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  { A0 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'PRT';   X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  { B0 }
    (Name:'';      X:0;  Y:0),  (Name:'/';     X:60; Y:7),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'ALT';   X:21; Y:15), (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  { C0 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'HOME';  X:43; Y:7),
    (Name:'UP';    X:44; Y:13), (Name:'PGUP';  X:48; Y:7),  (Name:'';      X:0;  Y:0),  (Name:'LF';    X:39; Y:15),
    (Name:'';      X:0;  Y:0),  (Name:'RT';    X:49; Y:15), (Name:'';      X:0;  Y:0),  (Name:'END';   X:43; Y:9),
    (Name:'DN';    X:44; Y:15), (Name:'PGDN';  X:48; Y:9),  (Name:'INS';   X:38; Y:7),  (Name:'DEL';   X:38; Y:9),  { D0 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'WN';    X:6;  Y:15),
    (Name:'WN';    X:25; Y:15), (Name:'MNU';   X:28; Y:15), (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  { E0 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  { F0 }
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),
    (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0),  (Name:'';      X:0;  Y:0));
  _32BIT_OPERAND = $66;

Var
  Intv2PC_C1    : Byte;      { Intv2PC controller 1 info              }
  Intv2PC_C2    : Byte;      { Intv2PC controller 2 info              }
  Intv2PC_C3    : Byte;      { Intv2PC controller 1 info              }
  Intv2PC_C4    : Byte;      { Intv2PC controller 2 info              }
  Intv2PCTime   : LongInt;   { Time in milliseconds                   }
  Intv2PCRead   : Boolean;   { TRUE=reading, FALSE=writing            }
  Intv2PCNibble : Integer;   { Next nibble to retrieve                }
  Intv2PCInfo   : Array[1..4] of Byte; { Last controller info }

Function PollIntv2PC: Boolean;
Const
  ReadDelay  : Array[0..7] Of Integer = (1,1,1,1,1,1,1,1);
  WriteDelay : Array[0..7] Of Integer = (1,1,1,1,1,1,1,1);

Var
  T : LongInt;
  B : Boolean;

Begin
  B := False;

  { Make sure the "External" (i.e. Intv2PC) interface is active }

  If Intv2PC Then
  Begin

    { Make sure at least 1 ms has elapsed }

    T := GetTickCount;
    If Intv2PCRead Then
    Begin
      If T > Intv2PCTime + ReadDelay[Intv2PCNibble] Then
      Begin
        Case Intv2PCNibble Of
          0: Begin
               If Intv2PCPort1 <> 0
                Then Intv2PC_C1 := (Intv2PC_C1 And $0F) +
                                   (frmMain.SmallPort1.Port[Intv2PCPort1 + 1] And $F0) Xor $70
                Else Intv2PC_C1 := 0;
             End;
          1: Begin
               If Intv2PCPort1 <> 0
                Then Intv2PC_C1 := (Intv2PC_C1 And $F0) +
                                   (frmMain.SmallPort1.Port[Intv2PCPort1 + 1] Shr 4) Xor $07
                Else Intv2PC_C1 := 0;
               Intv2PCInfo[1] := Intv2PC_C1;
             End;
          2: Begin
               If Intv2PCPort1 <> 0
                Then Intv2PC_C2 := (Intv2PC_C2 And $0F) +
                                   (frmMain.SmallPort1.Port[Intv2PCPort1 + 1] And $F0) Xor $70
                Else Intv2PC_C2 := 0;
             End;
          3: Begin
               If Intv2PCPort1 <> 0
                Then Intv2PC_C2 := (Intv2PC_C2 And $F0) +
                                   (frmMain.SmallPort1.Port[Intv2PCPort1 + 1] Shr 4) Xor $07
                Else Intv2PC_C2 := 0;
               Intv2PCInfo[2] := Intv2PC_C2;
             End;
          4: Begin
               If Intv2PCPort2 <> 0
                Then Intv2PC_C3 := (Intv2PC_C3 And $0F) +
                                   (frmMain.SmallPort1.Port[Intv2PCPort2 + 1] And $F0) Xor $70
                Else Intv2PC_C3 := 0;
             End;
          5: Begin
               If Intv2PCPort2 <> 0
                Then Intv2PC_C3 := (Intv2PC_C3 And $F0) +
                                   (frmMain.SmallPort1.Port[Intv2PCPort2 + 1] Shr 4) Xor $07
                Else Intv2PC_C3 := 0;
               Intv2PCInfo[3] := Intv2PC_C3;
             End;
          6: Begin
               If Intv2PCPort2 <> 0
                Then Intv2PC_C4 := (Intv2PC_C4 And $0F) +
                                   (frmMain.SmallPort1.Port[Intv2PCPort2 + 1] And $F0) Xor $70
                Else Intv2PC_C4 := 0;
             End;
          7: Begin
               If Intv2PCPort2 <> 0
                Then Intv2PC_C4 := (Intv2PC_C4 And $F0) +
                                   (frmMain.SmallPort1.Port[Intv2PCPort2 + 1] Shr 4) Xor $07
                Else Intv2PC_C4 := 0;
               Intv2PCInfo[4] := Intv2PC_C4;
               B := True;
             End;
        End; { Case }
        Intv2PCNibble := (Intv2PCNibble + 1) And 7;
        Intv2PCRead   := Not Intv2PCRead;
        Intv2PCTime   := T;
      End;
    End
    Else
    Begin
      If T > Intv2PCTime + WriteDelay[Intv2PCNibble] Then
      Begin
        Case Intv2PCNibble Of
          0: If Intv2PCPort1 <> 0 Then frmMain.SmallPort1.Port[Intv2PCPort1] := 2;  { C1 hi }
          1: If Intv2PCPort1 <> 0 Then frmMain.SmallPort1.Port[Intv2PCPort1] := 3;  { C1 lo }
          2: If Intv2PCPort1 <> 0 Then frmMain.SmallPort1.Port[Intv2PCPort1] := 4;  { C2 hi }
          3: If Intv2PCPort1 <> 0 Then frmMain.SmallPort1.Port[Intv2PCPort1] := 5;  { C2 lo }
          4: If Intv2PCPort2 <> 0 Then frmMain.SmallPort1.Port[Intv2PCPort2] := 2;  { C3 hi }
          5: If Intv2PCPort2 <> 0 Then frmMain.SmallPort1.Port[Intv2PCPort2] := 3;  { C3 lo }
          6: If Intv2PCPort2 <> 0 Then frmMain.SmallPort1.Port[Intv2PCPort2] := 4;  { C4 hi }
          7: If Intv2PCPort2 <> 0 Then frmMain.SmallPort1.Port[Intv2PCPort2] := 5;  { C4 lo }
        End; { Case }
        Intv2PCRead := Not Intv2PCRead;
        Intv2PCTime := T;
      End;
    End;
  End;
  PollIntv2PC := B;
End; { PollIntv2PC }

(*
Procedure StayPopup(Const St: String);
Var I,J,X,Y : Integer;
Begin
  Screen.TextColor(Black);
  Screen.TextBackground(LightGray);
  I := Length(St);
  X := (MaxX Div 2) - ((I + 4) Div 2);
  Y := MaxY Div 2;
  _Win := New(PWindow,Init(X,Y - 2,X + I + 3,Y + 2,
            Black,LightGray,Black,LightGray,Black,LightGray,
            DoubleBorder,'','',True));
  _Win^.Display;
  Screen.GotoXY(2,2);
  Screen.Write(St);
End; { StayPopup }

Procedure StayPopup4(St1,St2,St3,St4: String);
Var I,J,X,Y : Integer;
Begin
  Screen.TextColor(Black);
  Screen.TextBackground(LightGray);
  I := Length(St1);
  If Length(St2) > I Then I := Length(St2);
  If Length(St3) > I Then I := Length(St3);
  If Length(St4) > I Then I := Length(St4);
  X := (MaxX Div 2) - ((I + 4) Div 2);
  Y := MaxY Div 2;
  _Win := New(PWindow,Init(X,Y - 2,X + I + 3,Y + 3,
            Black,LightGray,Black,LightGray,Black,LightGray,
            DoubleBorder,'','',True));
  _Win^.Display;
  Screen.FastWrite(Lo(Screen.WindMin) + 2,Hi(Screen.WindMin) + 1,Screen.TextAttr,St1);
  Screen.FastWrite(Lo(Screen.WindMin) + 2,Hi(Screen.WindMin) + 2,Screen.TextAttr,St2);
  Screen.FastWrite(Lo(Screen.WindMin) + 2,Hi(Screen.WindMin) + 3,Screen.TextAttr,St3);
  Screen.FastWrite(Lo(Screen.WindMin) + 2,Hi(Screen.WindMin) + 4,Screen.TextAttr,St4);
End; { StayPopup4 }
*)
Procedure Popup(St: String);
Begin
  ShowMessage(St);
End; { Popup }

{ ----------------- }
{ TInterfaceHandler }
{ ----------------- }

Constructor TInterfaceHandler.Create;
Begin
  Inherited Create;
  Stack := TStringList.Create;
End; { TInterfaceHandler.Create }

Destructor TInterfaceHandler.Destroy;
Begin
  Stack.Free;
  Inherited;
End; { TInterfaceHandler.Destroy }

Procedure TInterfaceHandler.LoadConfiguration(F: TINIFile);
Var I: Integer;
Begin
  I := 0;
  While I < Stack.Count Do
  Begin
    TInterface(Stack.Objects[I]).LoadConfiguration(F);
    Inc(I);
  End; { While }
End; { TInterfaceHandler.LoadConfiguration }

Procedure TInterfaceHandler.SaveConfiguration(F: TINIFile);
Var I: Integer;
Begin
  I := 0;
  While I < Stack.Count Do
  Begin
    TInterface(Stack.Objects[I]).SaveConfiguration(F);
    Inc(I);
  End; { While }
End; { TInterfaceHandler.SaveConfiguration }

Procedure TInterfaceHandler.GetControllerInfo(Controllers: TCapability;
                                              Var Info: TIntvControls);
Var
  I    : Integer;
  Intf : TInterface;

Begin
  I := Stack.Count - 1;
  While I >= 0 Do
  Begin
    Intf := TInterface(Stack.Objects[I]);
    If Intf.Enabled And Intf.Active Then
     Intf.GetControllerInfo(Controllers,Info);
    Dec(I);
  End; { While }
End; { TInterfaceHandler.GetControllerInfo }

Procedure TInterfaceHandler.AddInterface(Int: TInterface);
Begin
  Stack.AddObject('',Int);
End; { TInterfaceHandler.AddInterface }

Procedure TInterfaceHandler.ActivateInterfaces;
Var
  I    : Integer;
  Intf : TInterface;

Begin
  I := Stack.Count - 1;
  While I >= 0 Do
  Begin
    Intf := TInterface(Stack.Objects[I]);
    If Intf.Enabled Then Intf.Activate;
    Dec(I);
  End; { While }
End; { TInterfaceHandler.ActivateInterfaces }

Procedure TInterfaceHandler.DeactivateInterfaces;
Var
  I    : Integer;
  Intf : TInterface;

Begin
  I := Stack.Count - 1;
  While I >= 0 Do
  Begin
    Intf := TInterface(Stack.Objects[I]);
    If Intf.Enabled Then Intf.Deactivate;
    Dec(I);
  End; { While }
End; { TInterfaceHandler.DeactivateInterfaces }

Procedure TInterfaceHandler.SetReadMask(Mask: Byte);
Var
  I    : Integer;
  Intf : TInterface;

Begin
  I := Stack.Count - 1;
  While I >= 0 Do
  Begin
    Intf := TInterface(Stack.Objects[I]);
    Intf.ReadMask := Mask;
    Dec(I);
  End; { While }
End; { TInterfaceHandler.SetReadMask }

Procedure TInterfaceHandler.WriteToPort(B: Byte);
Var
  I    : Integer;
  Intf : TInterface;

Begin
  I := Stack.Count - 1;
  While I >= 0 Do
  Begin
    Intf := TInterface(Stack.Objects[I]);
    Intf.WriteToPort(B);
    Dec(I);
  End; { While }
End; { TInterfaceHandler.WriteToPort }

{ ---------- }
{ TInterface }
{ ---------- }

Constructor TInterface.Create(St: String);
Begin
  Inherited Create;
  Name     := St;
  Enabled  := False;
  Active   := False;
  ReadMask := $FF;  { All bits readable }
End; { TInterface.Create }

Destructor TInterface.Destroy;
Begin
  Inherited;
End; { TInterface.Destroy }

Function TInterface.UsedCapability(Var Cap: TCapability): Boolean;
Begin
  UsedCapability := ((Cap.Cont[1] Or Cap.Cont[2] Or Cap.Cont[3] Or Cap.Cont[4]) <> 0);
End; { TInterface.UsedCapability }

Procedure TInterface.ReadCapability(F: TINIFile; Const Section,Ident: String; Var Cap: TCapability);
Begin
  Cap.Cont[1] := LongWord(F.ReadInteger(Section,Ident + '1',0));
  Cap.Cont[2] := LongWord(F.ReadInteger(Section,Ident + '2',0));
  Cap.Cont[3] := LongWord(F.ReadInteger(Section,Ident + '3',0));
  Cap.Cont[4] := LongWord(F.ReadInteger(Section,Ident + '4',0));
End; { TInterface.ReadCapability }

Procedure TInterface.WriteCapability(F: TINIFile; Const Section,Ident: String; Var Cap: TCapability);
Begin
  F.WriteInteger(Section,Ident + '1',LongInt(Cap.Cont[1]));
  F.WriteInteger(Section,Ident + '2',LongInt(Cap.Cont[2]));
  F.WriteInteger(Section,Ident + '3',LongInt(Cap.Cont[3]));
  F.WriteInteger(Section,Ident + '4',LongInt(Cap.Cont[4]));
End; { TInterface.WriteCapability }

Procedure TInterface.EraseCapability(F: TINIFile; Const Section,Ident: String);
Begin
  F.DeleteKey(Section,Ident + '1');
  F.DeleteKey(Section,Ident + '2');
  F.DeleteKey(Section,Ident + '3');
  F.DeleteKey(Section,Ident + '4');
End; { TInterface.WriteCapability }

Procedure TInterface.SetAllCapability(Var Cap: TCapability);
Begin
  Cap.Cont[1] := imAllCaps;
  Cap.Cont[2] := imAllCaps;
  Cap.Cont[3] := imAllCaps;
  Cap.Cont[4] := imAllCaps;
End; { TInterface.SetAllCapability }

Procedure TInterface.AndCapabilities(Var Src,Dest: TCapability);
Begin
  Dest.Cont[1] := Dest.Cont[1] And Src.Cont[1];
  Dest.Cont[2] := Dest.Cont[2] And Src.Cont[2];
  Dest.Cont[3] := Dest.Cont[3] And Src.Cont[3];
  Dest.Cont[4] := Dest.Cont[4] And Src.Cont[4];
End; { TInterface.AndCapabilities }

Procedure TInterface.LoadConfiguration(F: TINIFile);
Begin
  Enabled := F.ReadBool(Name,'ENABLED',False);
End; { TInterface.LoadConfiguration }

Procedure TInterface.SaveConfiguration(F: TINIFile);
Begin
  F.EraseSection(Name);
  F.WriteBool(Name,'ENABLED',Enabled);
End; { TInterface.SaveConfiguration }

Procedure TInterface.Activate;
Begin
  Active := True;
End; { TInterface.Activate }

Procedure TInterface.Deactivate;
Begin
  Active := False;
End; { TInterface.Deactivate }

Procedure TInterface.GetDigitalAny(On: Boolean;
                                   Var Cap: TCapability;
                                   Var CapMask: TCapability;
                                   Var Controllers: TCapability;
                                   Var Info: TIntvControls);
Var Con,KP,Side,Disc: Integer;
Begin

  { Hand controllers }

  For Con := 1 To 4 Do
  Begin
    If ((Cap.Cont[Con]          <> 0)  And
       ((Controllers.Cont[Con]) <> 0)) Then
    Begin
      For KP := 1 To 12 Do
      Begin
        If ((Cap.Cont[Con]         And ifKeypad_Mask[KP]) <> 0) And
           ((Controllers.Cont[Con] And ifKeypad_Mask[KP]) <> 0) Then
        Begin
          If On Then Info.P[Con] := Info.P[Con] And ($FF Xor Keypad_Mask[KP]);
          CapMask.Cont[Con] := CapMask.Cont[Con] And (Not ifKeypad_Mask[KP]);
        End;
      End; { For KP }
      For Side := 1 To 3 Do
      Begin
        If ((Cap.Cont[Con]         And ifSide_Mask[Side]) <> 0) And
           ((Controllers.Cont[Con] And ifSide_Mask[Side]) <> 0) Then
        Begin
          If On Then Info.P[Con] := Info.P[Con] And ($FF Xor Side_Mask[Side]);
          CapMask.Cont[Con] := CapMask.Cont[Con] And (Not ifSide_Mask[Side]);
        End;
      End; { For Side }
      For Disc := 1 To 16 Do
      Begin
        If ((Cap.Cont[Con]         And ifDisc_Mask[Disc]) <> 0) And
           ((Controllers.Cont[Con] And ifDisc_Mask[Disc]) <> 0) Then
        Begin
          If On Then Info.P[Con] := Info.P[Con] And ($FF Xor Disc_Mask[Disc]);
          CapMask.Cont[Con] := CapMask.Cont[Con] And (Not ifDisc_Mask[Disc]);
        End;
      End; { For Disc }
    End;
  End; { For Con }

  { Console switches }

  If ((Cap.Cont[1]         And imConsole) <> 0) And
     ((Controllers.Cont[1] And imConsole) <> 0) Then
  Begin
    If ((Cap.Cont[1]         And ifPower) <> 0) And
       ((Controllers.Cont[1] And ifPower) <> 0) Then
    Begin
      If On Then Info.Console := Info.Console Or cfPower;
      CapMask.Cont[1] := CapMask.Cont[1] And (Not ifPower);
    End;
    If ((Cap.Cont[2]         And ifReset) <> 0) And
       ((Controllers.Cont[2] And ifReset) <> 0) Then
    Begin
      If On Then Info.Console := Info.Console Or cfReset;
      CapMask.Cont[2] := CapMask.Cont[2] And (Not ifReset);
    End;
  End;
End; { TInterface.GetDigitalAny }

Procedure TInterface.GetAnalog(XMin,XMid,XMax,XValue,XM,
                               YMin,YMid,YMax,YValue,YM: LongInt;
                               Var Cap: TCapability;
                               Var CapMask: TCapability;
                               Var Controllers: TCapability;
                               Var Info: TIntvControls);
Var
  DX    : Double;
  DY    : Double;
  Angle : Double;
  Disc  : LongInt;
  Mask  : Word;
  Con   : Integer;

Begin
  For Con := 1 To 4 Do
  Begin
    If ((Cap.Cont[Con]         And imDisc) <> 0) And
       ((Controllers.Cont[Con] And imDisc) <> 0) Then
    Begin
      DX := XValue - XMid;
//      DY := YValue - YMid;
      DY := YMid - YValue;  // Flip vertical axis  
      If Abs(DX) < ((XMax - XMin) / 4) Then DX := 0;
      If Abs(DY) < ((YMax - YMin) / 4) Then DY := 0;
      If (DX <> 0) Or (DY <> 0) Then
      Begin

        { Force a minimum threshhold }

        If DX <> 0
         Then Angle := ArcTan2(DY,DX)
         Else If DY > 0 Then Angle := Pi / 2 Else Angle := -Pi / 2;
        Disc := Round(Angle * (8 / Pi));
        If Disc < 0  Then Inc(Disc,16);
        If Disc > 15 Then Dec(Disc,16);
        Mask := 0;
        Case Disc Of
          0: Mask := Disc_E;
          1: Mask := Disc_ENE;
          2: Mask := Disc_NE;
          3: Mask := Disc_NNE;
          4: Mask := Disc_N;
          5: Mask := Disc_NNW;
          6: Mask := Disc_NW;
          7: Mask := Disc_WNW;
          8: Mask := Disc_W;
          9: Mask := Disc_WSW;
         10: Mask := Disc_SW;
         11: Mask := Disc_SSW;
         12: Mask := Disc_S;
         13: Mask := Disc_SSE;
         14: Mask := Disc_SE;
         15: Mask := Disc_ESE;
        End; { Case }
        Info.P[Con] := Info.P[Con] And ($FF Xor Mask);
      End;
      CapMask.Cont[Con] := CapMask.Cont[Con] And imNoDisc;
    End;
  End; { For Con }
End; { TInterface.GetAnalog }

Procedure TInterface.WriteToPort(B: Byte);
Begin
End; { TInterface.WriteToPort }

Procedure TInterface.CopyTo(Intf: TInterface);
Begin
  Intf.Name     := Name;
  Intf.Enabled  := Enabled;
  Intf.Active   := Active;
  Intf.ReadMask := ReadMask;
End; // TInterface.CopyTo

{ --------------- }
{ TMouseInterface }
{ --------------- }

Constructor TMouseInterface.Create;
//Var MX,MY,MB: Word;
Begin
  Inherited Create('Mouse');
  FillChar(MouseX,SizeOf(MouseX),#0);
  FillChar(MouseY,SizeOf(MouseY),#0);
  FillChar(MouseBtn,SizeOf(MouseBtn),#0);
  MouseX.Min := 0;
  MouseY.Min := 0;
  MouseX.Mid := 320;
  MouseY.Mid := 100;
  MouseX.Max := 640;
  MouseY.Max := 200;
  OldX       := -1;
  OldY       := -1;
//  GetMousePosition(MX,MY,MB);
End; { TMouseInterface.Create }

Destructor TMouseInterface.Destroy;
Begin
  Inherited;
End; { TMouseInterface.Destroy }

Procedure TMouseInterface.LoadConfiguration(F: TINIFile);
Begin
  Inherited LoadConfiguration(F);
  ReadCapability(F,Name,'X',MouseX.Cap);
  MouseX.Min   := F.ReadInteger(Name,'XMIN',0);
  MouseX.Mid   := F.ReadInteger(Name,'XMID',0);
  MouseX.Max   := F.ReadInteger(Name,'XMAX',0);

  ReadCapability(F,Name,'Y',MouseY.Cap);
  MouseY.Min   := F.ReadInteger(Name,'YMIN',0);
  MouseY.Mid   := F.ReadInteger(Name,'YMID',0);
  MouseY.Max   := F.ReadInteger(Name,'YMAX',0);

  ReadCapability(F,Name,'B1',MouseBtn[1]);
  ReadCapability(F,Name,'B2',MouseBtn[2]);
  ReadCapability(F,Name,'B3',MouseBtn[3]);
End; { TMouseInterface.LoadConfiguration }

Procedure TMouseInterface.SaveConfiguration(F: TINIFile);
Begin
  Inherited SaveConfiguration(F);

  WriteCapability(F,Name,'X',MouseX.Cap);
  F.WriteInteger(Name,'XMIN',MouseX.Min);
  F.WriteInteger(Name,'XMID',MouseX.Mid);
  F.WriteInteger(Name,'XMAX',MouseX.Max);

  WriteCapability(F,Name,'Y',MouseY.Cap);
  F.WriteInteger(Name,'YMIN',MouseY.Min);
  F.WriteInteger(Name,'YMID',MouseY.Mid);
  F.WriteInteger(Name,'YMAX',MouseY.Max);

  WriteCapability(F,Name,'B1',MouseBtn[1]);
  WriteCapability(F,Name,'B2',MouseBtn[2]);
  WriteCapability(F,Name,'B3',MouseBtn[3]);
End; { TMouseInterface.SaveConfiguration }

Procedure TMouseInterface.GetControllerInfo(Var Controllers: TCapability;
                                            Var Info: TIntvControls);
Var
  MX,MY,MB  : Integer;
  XM,YM     : Integer;
  CapMask   : TCapability;
  Pt        : TPoint;

Begin

  { Initialize the capability mask to include all capabliities }

  SetAllCapability(CapMask);

  { Read the mouse position and calculate the paddle "position" }

//  GetMouseMotion(XM,YM);
//  frmMain.GetMousePosition(MX,MY,MB);

  GetCursorPos(Pt);

  MB := _MouseB;
  MX := (Pt.X - frmMain.Left) * 2;
  MY := Pt.Y - frmMain.Top;
  If MX < MouseX.Min Then MX := 0;
  If MY < MouseY.Min Then MY := 0;
  If MX > MouseX.Max Then MX := MouseX.Max;
  If MY > MouseY.Max Then MY := MouseY.Max;
  If OldX >= 0 Then XM := MX - OldX Else XM := 0;
  If OldY >= 0 Then YM := MY - OldY Else YM := 0;

  { Left button }

  GetDigitalAny(((MB And 1) <> 0),MouseBtn[1],CapMask,Controllers,Info);

  { Right button }

  GetDigitalAny(((MB And 2) <> 0),MouseBtn[2],CapMask,Controllers,Info);

  { Middle button }

  GetDigitalAny(((MB And 4) <> 0),MouseBtn[3],CapMask,Controllers,Info);

  { X and Y axes }

  GetAnalog(MouseX.Min,MouseX.Mid,MouseX.Max,MX,XM,
            MouseY.Min,MouseY.Mid,MouseY.Max,MY,YM,
            MouseX.Cap,CapMask,Controllers,Info);

  { Remove capabilities that were used }

  AndCapabilities(CapMask,Controllers);

  // Save values for next time

  OldX := MX;
  OldY := MY;
End; { TMouseInterface.GetControllerInfo }

Procedure TMouseInterface.CopyTo(Intf: TInterface);
Begin
  Inherited CopyTo(Intf);
  TMouseInterface(Intf).MouseX    := MouseX;
  TMouseInterface(Intf).MouseY    := MouseY;
  TMouseInterface(Intf).MouseBtn  := MouseBtn;
End; // TMouseInterface.CopyTo

{ ------------------ }
{ TKeyboardInterface }
{ ------------------ }

Constructor TKeyboardInterface.Create;
Begin
  Inherited Create('Keyboard');
  FillChar(KeyCap,SizeOf(KeyCap),#0);

  { Predefine console switches - other function }
  { keys are handled in the main emulator.      }

  KeyCap[DIK_ESCAPE].Cont[1] := ifPower;
  KeyCap[DIK_F10].Cont[2]    := ifReset;
  ConfigKeyPressed           := False;
  ConfigKey                  := 0;
End; { TKeyboardInterface.Create }

Destructor TKeyboardInterface.Destroy;
Begin
  Inherited;
End; { TKeyboardInterface.Destroy }

Procedure TKeyboardInterface.LoadConfiguration(F: TINIFile);
Var I: Integer;
Begin
  Inherited LoadConfiguration(F);
  For I := 0 To 255 Do
   ReadCapability(F,Name,'K' + IntToStr(I),KeyCap[I]);
End; { TKeyboardInterface.LoadConfiguration }

Procedure TKeyboardInterface.SaveConfiguration(F: TINIFile);
Var I: Integer;
Begin
  Inherited SaveConfiguration(F);
  For I := 0 To 255 Do
   If UsedCapability(KeyCap[I]) Then WriteCapability(F,Name,'K' + IntToStr(I),KeyCap[I]);
End; { TKeyboardInterface.SaveConfiguration }

Procedure TKeyboardInterface.GetControllerInfo(Var Controllers: TCapability;
                                               Var Info: TIntvControls);
Var
  TheKey  : Integer;
  CapMask : TCapability;

Begin

  { Initialize the capability mask to include all capabliities }

  SetAllCapability(CapMask);

  { Get the key information }

  frmMain.DXInput1.Update;
  For TheKey := 0 To $FF Do
  Begin
    Keys[TheKey] := frmMain.DXInput1.Keyboard.DIKeys[TheKey];
    GetDigitalAny(Keys[TheKey],KeyCap[TheKey],CapMask,Controllers,Info);
  End;

  { Remove capabilities that were used }

  AndCapabilities(CapMask,Controllers);
End; { TKeyboardInterface.GetControllerInfo }

Procedure TKeyboardInterface.CopyTo(Intf: TInterface);
Begin
  Inherited CopyTo(Intf);
  TKeyboardInterface(Intf).KeyCap := KeyCap;
End; // TKeyboardInterface.CopyTo

{ ------------------ }
{ TDigitalInterface  }
{ ------------------ }

Constructor TDigitalInterface.Create(St: String);
Begin
  Inherited Create(St);
  FillChar(Cap,SizeOf(Cap),#0);
  FillChar(ContInfo,SizeOf(ContInfo),#0);
  FillChar(ConInfo,SizeOf(ConInfo),#0);
End; { TDigitalInterface.Create }

Destructor TDigitalInterface.Destroy;
Begin
  Inherited;
End; { TDigitalInterface.Destroy }

Procedure TDigitalInterface.LoadConfiguration(F: TINIFile);
Var
  Con,Btn,Axis,Hat,Thr,Ana : Integer;
  ConStr,ThrStr,AnaStr     : String[4];

Begin
  Inherited LoadConfiguration(F);
  FillChar(Cap,SizeOf(Cap),#0);
  For Con := 1 To Max_Digital_Con Do
  Begin
    ConStr := 'C' + IntToStr(Con);
    If F.ValueExists(Name,ConStr + 'LOADED') Then
    Begin
      Cap[Con].Loaded   := F.ReadBool(Name,ConStr + 'LOADED',False);
      Cap[Con].ClassMap := F.ReadInteger(Name,ConStr + 'CLASSMAP',0);
      Cap[Con].NumBtns  := F.ReadInteger(Name,ConStr + 'NUMBTNS',0);
      Cap[Con].NumAxes  := F.ReadInteger(Name,ConStr + 'NUMAXES',0);
      Cap[Con].NumHats  := F.ReadInteger(Name,ConStr + 'NUMHATS',0);
      Cap[Con].NumThrs  := F.ReadInteger(Name,ConStr + 'NUMTHRS',0);
      Cap[Con].NumAnas  := F.ReadInteger(Name,ConStr + 'NUMANAS',0);
      For Btn := 1 To Max_Digital_Btn Do
       ReadCapability(F,Name,ConStr + 'B' + IntToStr(Btn),Cap[Con].Btn[Btn]);
      For Axis := 1 To Max_Digital_Axis Do
       ReadCapability(F,Name,ConStr + 'D' + IntToStr(Axis),Cap[Con].Axis[Axis]);
      For Hat := 1 To Max_Digital_Hat Do
       ReadCapability(F,Name,ConStr + 'H' + IntToStr(Hat),Cap[Con].Hat[Hat]);
      For Thr := 1 To Max_Digital_Thr Do
      Begin
        ThrStr := ConStr + 'T' + IntToStr(Thr);
        ReadCapability(F,Name,ThrStr,Cap[Con].Thr[Thr].Cap);
        Cap[Con].Thr[Thr].Min   := F.ReadInteger(Name,ThrStr + 'MIN',0);
        Cap[Con].Thr[Thr].Mid   := F.ReadInteger(Name,ThrStr + 'MID',0);
        Cap[Con].Thr[Thr].Max   := F.ReadInteger(Name,ThrStr + 'MAX',0);
      End; { For Thr }
      For Ana := 1 To Max_Digital_Ana Do
      Begin
        AnaStr := ConStr + 'A' + IntToStr(Ana);
        ReadCapability(F,Name,AnaStr,Cap[Con].Ana[Ana].Cap);
        Cap[Con].Ana[Ana].Min   := F.ReadInteger(Name,AnaStr + 'MIN',0);
        Cap[Con].Ana[Ana].Mid   := F.ReadInteger(Name,AnaStr + 'MID',0);
        Cap[Con].Ana[Ana].Max   := F.ReadInteger(Name,AnaStr + 'MAX',0);
      End; { For Ana }
    End;  
  End; { For Con }
End; { TDigitalInterface.LoadConfiguration }

Procedure TDigitalInterface.SaveConfiguration(F: TINIFile);
Var
  Con,Btn,Axis,Hat,Thr,Ana : Integer;
  ConStr,ThrStr,AnaStr     : String[4];

Begin
  Inherited SaveConfiguration(F);
  For Con := 1 To Max_Digital_Con Do
  Begin
    If Cap[Con].ClassMap <> 0 Then
    Begin
      ConStr := 'C' + IntToStr(Con);
      F.WriteBool(Name,ConStr + 'LOADED',Cap[Con].Loaded);
      F.WriteInteger(Name,ConStr + 'CLASSMAP',Cap[Con].ClassMap);
      F.WriteInteger(Name,ConStr + 'NUMBTNS',Cap[Con].NumBtns);
      F.WriteInteger(Name,ConStr + 'NUMAXES',Cap[Con].NumAxes);
      F.WriteInteger(Name,ConStr + 'NUMHATS',Cap[Con].NumHats);
      F.WriteInteger(Name,ConStr + 'NUMTHRS',Cap[Con].NumThrs);
      F.WriteInteger(Name,ConStr + 'NUMANAS',Cap[Con].NumAnas);
      For Btn := 1 To Max_Digital_Btn Do
      Begin
        If UsedCapability(Cap[Con].Btn[Btn])
         Then WriteCapability(F,Name,ConStr + 'B' + IntToStr(Btn),Cap[Con].Btn[Btn]);
      End; { For Btn }
      For Axis := 1 To Max_Digital_Axis Do
      Begin
        If UsedCapability(Cap[Con].Axis[Axis])
         Then WriteCapability(F,Name,ConStr + 'D' + IntToStr(Axis),Cap[Con].Axis[Axis]);
      End; { For Axis }
      For Hat := 1 To Max_Digital_Hat Do
      Begin
        If UsedCapability(Cap[Con].Hat[Hat])
         Then WriteCapability(F,Name,ConStr + 'H' + IntToStr(Hat),Cap[Con].Hat[Hat]);
      End; { For Hat }
      For Thr := 1 To Max_Digital_Thr Do
      Begin
        If UsedCapability(Cap[Con].Thr[Thr].Cap) Then
        Begin
          ThrStr := ConStr + 'T' + IntToStr(Thr);
          WriteCapability(F,Name,ThrStr,Cap[Con].Thr[Thr].Cap);
          F.WriteInteger(Name,ThrStr + 'MIN',Cap[Con].Thr[Thr].Min);
          F.WriteInteger(Name,ThrStr + 'MID',Cap[Con].Thr[Thr].Mid);
          F.WriteInteger(Name,ThrStr + 'MAX',Cap[Con].Thr[Thr].Max);
        End;
      End; { For Thr }
      For Ana := 1 To Max_Digital_Ana Do
      Begin
        If UsedCapability(Cap[Con].Ana[Ana].Cap) Then
        Begin
          AnaStr := ConStr + 'A' + IntToStr(Ana);
          WriteCapability(F,Name,AnaStr,Cap[Con].Ana[Ana].Cap);
          F.WriteInteger(Name,AnaStr + 'MIN',Cap[Con].Ana[Ana].Min);
          F.WriteInteger(Name,AnaStr + 'MID',Cap[Con].Ana[Ana].Mid);
          F.WriteInteger(Name,AnaStr + 'MAX',Cap[Con].Ana[Ana].Max);
        End;
      End; { For Ana }
    End;
  End; { For Con }
End; { TDigitalInterface.SaveConfiguration }

Procedure TDigitalInterface.RefreshControllers;

Type BPtr = ^Byte;

Var
  I,J,K : Integer;
  Con   : Integer;
  Joy   : TJoystick2;
{
  JS    : DIJOYSTATE2;
  St    : String;
  A     : Array[0..1023] Of Byte;
}
Begin
  FillChar(ConInfo,SizeOf(ConInfo),#0);
  frmMain.DXInput1.Update;


//  BPtr(SDesc.lpSurface)^ := 15;


  For Con := 0 To frmMain.DXInput1.NumJoysticks - 1 Do
  Begin
    Joy := frmMain.DXInput1.Joystick[Con];
    J := Joy.ButtonCount - 1;
//    J := frmMain.DXInput1.Joystick.ButtonCount - 1;
    K := 0;
    If J > 31 Then J := 31;

    For I := 0 To J Do If Joy.Buttons[I] Then K := K Or (1 Shl I);
//    For I := 0 To J Do If frmMain.DXInput1.Joystick.Buttons[I] Then K := K Or (1 Shl I);

    ConInfo[Con + 1].Buttons := K;
(*
    If Joy.AxisCount >= 2 Then
    frmMain.Label1.Caption := 'NumJoys = ' + IntToStr(frmMain.DXInput1.NumJoysticks) + #13#10 +
                              'X = ' + IntToStr(Joy.X) + #13#10 +
                              'RangeX = ' + IntToStr(Joy.RangeX) + #13#10 +
                              'Y = ' + IntToStr(Joy.Y) + #13#10 +
                              'RangeY = ' + IntToStr(Joy.RangeY) + #13#10{ +
                              'Z = ' + IntToStr(frmMain.DXInput1.Joystick[2].Z) + #13#10 +
                              'RangeZ = ' + IntToStr(frmMain.DXInput1.Joystick[2].RangeZ)};
*)
    I := Joy.AxisCount;
    If I >= 1 Then ConInfo[Con + 1].Analog[1] := Joy.X + Joy.RangeX;
    If I >= 2 Then ConInfo[Con + 1].Analog[2] := Joy.Y + Joy.RangeY;
    If I >= 3 Then ConInfo[Con + 1].Analog[3] := Joy.Z + Joy.RangeZ;
    If I >= 4 Then ConInfo[Con + 1].Analog[4] := Joy.U + Joy.RangeX;
    If I >= 5 Then ConInfo[Con + 1].Analog[5] := Joy.V + Joy.RangeX;
    If I >= 6 Then ConInfo[Con + 1].Analog[6] := Joy.W + Joy.RangeX;

    For I := 0 To Joy.POVCount - 1 Do
    Begin
      J := Joy.POV[I];
      If J < 0 Then
      Begin
        ConInfo[Con + 1].Hat[I * 2 + 1] := 10;
        ConInfo[Con + 1].Hat[I * 2 + 2] := 10;
      End
      Else
      Begin
        // Horizontal direction

        Case J Of
          0,18000:           ConInfo[Con + 1].Hat[I * 2 + 1] := 10;
          4500,9000,13500:   ConInfo[Con + 1].Hat[I * 2 + 1] := 20;
          22500,27000,31500: ConInfo[Con + 1].Hat[I * 2 + 1] := 0;
        End; // Case

        // Vertical direction

        Case J Of
          0,4500,31500:      ConInfo[Con + 1].Hat[I * 2 + 2] := 0;
          9000,27000:        ConInfo[Con + 1].Hat[I * 2 + 2] := 10;
          13500,18000,22500: ConInfo[Con + 1].Hat[I * 2 + 2] := 20;
        End; // Case }
      End;
    End; // For I
(*
    // Display entire joystick status (testing purposes only)

    If Con = 0 Then
    Begin
      St := '';
      For I := 1 To 6 Do St := St + IntToStr(ConInfo[Con + 1].Analog[I]) + #13#10;
      St := St + IntToStr(ConInfo[Con + 1].Hat[1]) + #13#10;
      St := St + IntToStr(ConInfo[Con + 1].Hat[2]) + #13#10;
      St := St + BinLongInt(ConInfo[Con + 1].Buttons) + #13#10;
      JS := Joy.JoyState;
      Move(JS,A,SizeOf(JS));
      For I := 0 To SizeOf(JS) - 1 Do
      Begin
        St := St + HexByte(A[I]);
        If (I And $F) = 7 Then St := St + '-' Else St := St + ' ';
        If (I And $F) = $F Then St := St + #13#10;
      End; // For I
      frmMain.Label1.Caption := St;
    End;
*)
//    I := frmMain.DXInput1.Joystick.AxisCount;
//    If I >= 1 Then ConInfo.Analog[1] := frmMain.DXInput1.Joystick.X + frmMain.DXInput1.Joystick.RangeX;
//    If I >= 2 Then ConInfo.Analog[2] := frmMain.DXInput1.Joystick.Y + frmMain.DXInput1.Joystick.RangeY;
//    If I >= 3 Then ConInfo.Analog[3] := frmMain.DXInput1.Joystick.Z + frmMain.DXInput1.Joystick.RangeZ;

  {
    frmMain.Label1.Caption := 'ButtonCount=' + IntToStr(J) + '   Buttons=' + IntToStr(K) + #13#10 +
     'X=' + IntToStr(frmMain.DXInput1.Joystick.X) + #13#10 +
     'Y=' + IntToStr(frmMain.DXInput1.Joystick.Y) + #13#10 +
     'Name=' + frmMain.DXInput1.Joystick.ProductName + #13#10 +
     'Time=' + IntToStr(GetTickCount);
  }
  End; // For Con
End; { TDigitalInterface.RefreshControllers }

Function TDigitalInterface.GetVendorName(Slot: Integer): String;
Begin
  GetVendorName := '';
End; { TDigitalInterface.GetVendorName }

Function TDigitalInterface.GetProductName(Slot: Integer): String;
Begin
  If Slot < frmMain.DXInput1.NumJoysticks
   Then GetProductName := frmMain.DXInput1.Joystick[Slot].ProductName
   Else GetProductName := '';
(*
  Case Slot Of
    0: GetProductName := frmMain.DXInput1.Joystick.ProductName;
//    1: GetProductName := frmMain.DXInput2.Joystick.ProductName;
//    2: GetProductName := frmMain.DXInput3.Joystick.ProductName;
//    3: GetProductName := frmMain.DXInput4.Joystick.ProductName;
  Else
    GetProductName := '';
  End; // Case
*)  
End; { TDigitalInterface.GetProductName }

Function TDigitalInterface.GetSlotMap: GRIP_BITFIELD;
Var
  SMap : GRIP_BITFIELD;
  I    : Integer;

Begin
  SMap := 0;
  For I := 0 To Max_Digital_Con - 1 Do
   If frmMain.DXInput1.NumJoysticks > I Then SMap := SMap + (1 Shl I);
  GetSlotMap := SMap;
End; { TDigitalInterface.GetSlotMap }

Function TDigitalInterface.GetClassMap(S: GRIP_SLOT): GRIP_BITFIELD;
Var CMap: GRIP_BITFIELD;
Begin
  CMap := 0;
  If S < frmMain.DXInput1.NumJoysticks Then
  Begin
    If frmMain.DXInput1.Joystick[S].ButtonCount > 0 Then CMap := CMap + (1 Shl GRIP_CLASS_BUTTON);
    If frmMain.DXInput1.Joystick[S].POVCount    > 0 Then CMap := CMap + (1 Shl GRIP_CLASS_POV_HAT);
    If frmMain.DXInput1.Joystick[S].AxisCount   > 0 Then CMap := CMap + (1 Shl GRIP_CLASS_ANALOG);
  End;
  GetClassMap := CMap;
End; { TDigitalInterface.GetClassMap }

Function TDigitalInterface.GetMaxIndex(S: GRIP_SLOT; C: GRIP_CLASS): GRIP_INDEX;
Var MaxInd: Integer;
Begin
  MaxInd := 0;
  If S < frmMain.DXInput1.NumJoysticks Then
  Begin
    Case C Of
      GRIP_CLASS_BUTTON: MaxInd := frmMain.DXInput1.Joystick[S].ButtonCount - 1;
     GRIP_CLASS_POV_HAT: MaxInd := frmMain.DXInput1.Joystick[S].POVCount * 2 - 1;
      GRIP_CLASS_ANALOG: MaxInd := frmMain.DXInput1.Joystick[S].AxisCount   - 1;
    Else
      MaxInd := 0;
    End; // Case
  End;
  GetMaxIndex := MaxInd;
End; { TDigitalInterface.GetMaxIndex }

Function TDigitalInterface.GetMaxValue(S: GRIP_SLOT; C: GRIP_CLASS): GRIP_VALUE;
Var MaxVal: Integer;

//St : String;

Begin
  MaxVal := 0;
  If S < frmMain.DXInput1.NumJoysticks Then
  Begin
    Case C Of
     GRIP_CLASS_POV_HAT: MaxVal := 20;
      GRIP_CLASS_ANALOG: MaxVal := 2 * frmMain.DXInput1.Joystick[S].RangeX;
    Else
      MaxVal := 0;
    End; // Case
  End;
  GetMaxValue := MaxVal;
End; { TDigitalInterface.GetMaxValue }

Function TDigitalInterface.GetPackedValues(S: GRIP_SLOT; C: GRIP_CLASS; IStart: GRIP_INDEX;
                         IEnd: GRIP_INDEX): GRIP_BITFIELD;
Var
  I : Integer;
  L : GRIP_BITFIELD;

Begin
  L := 0;
  For I := IStart To IEnd Do L := L Or (1 Shl I);
  If (GetClassMap(S) And (1 Shl C)) <> 0 Then
  Begin
    If C = GRIP_CLASS_BUTTON
     Then GetPackedValues := ConInfo[S + 1].Buttons And L
     Else GetPackedValues := 0;
  End
  Else GetPackedValues := 0;
End; { TDigitalInterface.GetPackedValues }

Function TDigitalInterface.GetValue(S: GRIP_SLOT; C: GRIP_CLASS;
                                    I: GRIP_INDEX): GRIP_VALUE;
{Var
  J  : Integer;
  St : String;}

Begin
  If (GetClassMap(S) And (1 Shl C)) <> 0 Then
  Begin
    If I <= GetMaxIndex(S,C) Then
    Begin
      Case C Of
        GRIP_CLASS_AXIS: GetValue := ConInfo[S + 1].Axis[I + 1];
     GRIP_CLASS_POV_HAT: GetValue := ConInfo[S + 1].Hat[I + 1];
    GRIP_CLASS_THROTTLE: GetValue := ConInfo[S + 1].Throttle[I + 1];
      GRIP_CLASS_ANALOG: GetValue := ConInfo[S + 1].Analog[I + 1];
      Else
        GetValue := 0;
      End; { Case }
    End
    Else GetValue := 0;
  End
  Else GetValue := 0;
End; { TDigitalInterface.GetValue }

Procedure TDigitalInterface.GetControllerInfo(Var Controllers: TCapability;
                                              Var Info: TIntvControls);
Var
  SlotMap  : LongInt;
//  ClassMap : LongInt;
  Slot     : Integer;
  Cont     : Integer;
  Mask     : LongInt;
//  NumBtns  : Integer;
//  NumAxes  : Integer;
//  NumHats  : Integer;
//  NumThrs  : Integer;
//  NumAnas  : Integer;
  I        : Integer;
  CapMask  : TCapability;
  MaxThr   : Word;
  MaxAna   : Word;

Begin

  { Initialize the capability mask to include all capabliities }

  SetAllCapability(CapMask);

  { Get the slot map }

  SlotMap := GetSlotMap;
  Mask    := 1;
  Cont    := 0;

  { Refresh the controllers }

  RefreshControllers;
  For Slot := 0 To 31 Do
  Begin
    If (SlotMap And Mask) <> 0 Then
    Begin
      Inc(Cont);
      If Not Cap[Cont].Loaded Then
      Begin

        { Fill in the Cap entry }

        { Get the class map and find out what the controller has }

        Cap[Cont].ClassMap := GetClassMap(Slot);

        { Find out how much of each component the controller has }

        If (Cap[Cont].ClassMap And (1 Shl GRIP_CLASS_BUTTON)) <> 0
         Then Cap[Cont].NumBtns := GetMaxIndex(Slot,GRIP_CLASS_BUTTON) + 1
         Else Cap[Cont].NumBtns := 0;
        If (Cap[Cont].ClassMap And (1 Shl GRIP_CLASS_AXIS)) <> 0
         Then Cap[Cont].NumAxes := GetMaxIndex(Slot,GRIP_CLASS_AXIS) + 1
         Else Cap[Cont].NumAxes := 0;
        If (Cap[Cont].ClassMap And (1 Shl GRIP_CLASS_POV_HAT)) <> 0
         Then Cap[Cont].NumHats := GetMaxIndex(Slot,GRIP_CLASS_POV_HAT) + 1
         Else Cap[Cont].NumHats := 0;
        If (Cap[Cont].ClassMap And (1 Shl GRIP_CLASS_THROTTLE)) <> 0
         Then Cap[Cont].NumThrs := GetMaxIndex(Slot,GRIP_CLASS_THROTTLE) + 1
         Else Cap[Cont].NumThrs := 0;
        If (Cap[Cont].ClassMap And (1 Shl GRIP_CLASS_ANALOG)) <> 0
         Then Cap[Cont].NumAnas := GetMaxIndex(Slot,GRIP_CLASS_ANALOG) + 1
         Else Cap[Cont].NumAnas := 0;

        { Bound the components }

        If Cap[Cont].NumBtns > Max_Digital_Btn  Then Cap[Cont].NumBtns := Max_Digital_Btn;
        If Cap[Cont].NumAxes > Max_Digital_Axis Then Cap[Cont].NumAxes := Max_Digital_Axis;
        If Cap[Cont].NumHats > Max_Digital_Hat  Then Cap[Cont].NumHats := Max_Digital_Hat;
        If Cap[Cont].NumThrs > Max_Digital_Thr  Then Cap[Cont].NumThrs := Max_Digital_Thr;
        If Cap[Cont].NumAnas > Max_Digital_Ana  Then Cap[Cont].NumAnas := Max_Digital_Ana;
        If Cap[Cont].ClassMap <> 0 Then Cap[Cont].Loaded := True;
      End;
      If Cap[Cont].Loaded Then
      Begin

        { Get the raw info }

        ContInfo[Cont].Buttons := GetPackedValues(Slot,GRIP_CLASS_BUTTON,0,31);
        For I := 1 To Cap[Cont].NumAxes Do ContInfo[Cont].Axis[I]     := GetValue(Slot,GRIP_CLASS_AXIS    ,I - 1);
        For I := 1 To Cap[Cont].NumHats Do ContInfo[Cont].Hat[I]      := GetValue(Slot,GRIP_CLASS_POV_HAT ,I - 1);
        For I := 1 To Cap[Cont].NumThrs Do ContInfo[Cont].Throttle[I] := GetValue(Slot,GRIP_CLASS_THROTTLE,I - 1);
        For I := 1 To Cap[Cont].NumAnas Do ContInfo[Cont].Analog[I]   := GetValue(Slot,GRIP_CLASS_ANALOG  ,I - 1);

        { Get the maximum values }

        MaxThr := GetMaxValue(Slot,GRIP_CLASS_THROTTLE);
        MaxAna := GetMaxValue(Slot,GRIP_CLASS_ANALOG);

        { Process the buttons }

        For I := 1 To Cap[Cont].NumBtns Do
         GetDigitalAny(((ContInfo[Cont].Buttons And (1 Shl (I - 1))) <> 0),
          Cap[Cont].Btn[I],CapMask,Controllers,Info);

        { Process the digital axes }

        For I := 1 To Cap[Cont].NumAxes Do
        Begin

          { Assume that even-numbered axes have to be flipped }

          If (I And 1) <> 0 Then
          Begin
            GetAnalog(0,10,20,ContInfo[Cont].Axis[I],0,
                      0,10,20,ContInfo[Cont].Axis[I + 1],0,
                      Cap[Cont].Axis[I],
                      CapMask,Controllers,Info);
          End
          Else
          Begin
  {          GetAnalog(0,10,20,10 * (2 - ContInfo.Axis[I]),0,Cap[Cont].Axis[I],
             CapMask,Controllers,Info);}
          End;
        End; // For I  


        { Process the hats }

        For I := 1 To Cap[Cont].NumHats Do
        Begin

          { Assume that even-numbered hat axes have to be flipped }

          If (I And 1) <> 0 Then
          Begin
            GetAnalog(0,10,20,ContInfo[Cont].Hat[I],0,
                      0,10,20,ContInfo[Cont].Hat[I + 1],0,
                      Cap[Cont].Hat[I],CapMask,Controllers,Info);
          End
          Else
          Begin
{
            GetAnalog(0,10,20,10 * (2 - ContInfo[Cont].Hat[I]),0,Cap[Cont].Hat[I],
             CapMask,Controllers,Info);
}             
          End;
        End; { For I }

        { Process the throttles }

        For I := 1 To Cap[Cont].NumThrs Do
         GetAnalog(0,MaxThr Div 2,MaxThr,ContInfo[Cont].Throttle[I],0,
                   0,MaxThr Div 2,MaxThr,ContInfo[Cont].Throttle[I + 1],0,
                   Cap[Cont].Thr[I].Cap,CapMask,Controllers,Info);

        { Process the analogs }

        For I := 1 To Cap[Cont].NumAnas Do
         If (I And 1) <> 0 Then
          GetAnalog(0,MaxAna Div 2,MaxAna,ContInfo[Cont].Analog[I],0,
                    0,MaxAna Div 2,MaxAna,ContInfo[Cont].Analog[I + 1],0,
                    Cap[Cont].Ana[I].Cap,CapMask,Controllers,Info);
      End;
    End
    Else Cap[Cont].Loaded := False;
    Mask := Mask Shl 1;
  End; { For Slot }

  { Remove capabilities that were used }

  AndCapabilities(CapMask,Controllers);
End; { TDigitalInterface.GetControllerInfo }

Procedure TDigitalInterface.CopyTo(Intf: TInterface);
Begin
  Inherited CopyTo(Intf);
  TDigitalInterface(Intf).Cap      := Cap;
  TDigitalInterface(Intf).ContInfo := ContInfo;
  TDigitalInterface(Intf).ConInfo  := ConInfo;
End; // TDigitalInterface.CopyTo

{ ---------------------- }
{ TExternalInterface     }
{ ---------------------- }

Procedure TExternalInterface.Activate;
Begin
  Inherited;
  Intv2PC := Active;
End; // TExternalInterface.Activate

Procedure TExternalInterface.Deactivate;
Begin
  Inherited;
  Intv2PC := Active;
End; // TExternalInterface.Deactivate

Constructor TExternalInterface.Create;
Begin
  Inherited Create('Intv2PC');
  Wrote := 0;
  FillChar(LPTCap,SizeOf(LPTCap),#0);
  Deactivate;
End; // TExternalInterface.Create

Destructor TExternalInterface.Destroy;
Begin
  Deactivate;
  Inherited;
End; // TExternalInterface.Destroy

Procedure TExternalInterface.LoadConfiguration(F: TINIFile);
Var LPT: Integer;
Begin
  Inherited LoadConfiguration(F);
  For LPT := 0 To 2 Do
   ReadCapability(F,Name,'L' + IntToStr(LPT),LPTCap[LPT]);
  For LPT := 0 To 2 Do
  Begin
    If (LPTCap[LPT].Cont[1] <> 0) Or
       (LPTCap[LPT].Cont[2] <> 0) Then Intv2PCPort1 := LPTPorts[LPT];//MemW[0:$408 + LPT * 2];
    If (LPTCap[LPT].Cont[3] <> 0) Or
       (LPTCap[LPT].Cont[4] <> 0) Then Intv2PCPort2 := LPTPorts[LPT];//MemW[0:$408 + LPT * 2];
  End; { For LPT }
End; // TExternalInterface.LoadConfiguration

Procedure TExternalInterface.SaveConfiguration(F: TINIFile);
Var LPT: Integer;
Begin
  Inherited SaveConfiguration(F);
  For LPT := 0 To 2 Do
   WriteCapability(F,Name,'L' + IntToStr(LPT),LPTCap[LPT]);
End; // TExternalInterface.SaveConfiguration
(*
Procedure TExternalInterface.Configure;
Var
  Menu  : PMenu;
  I     : Integer;
  Ports : Integer;

Begin
  Inherited Configure;
  Menu := New(PMenu,InitWH(46,6,
              Black,LightGray,Black,LightGray,Blue,LightGray,
              Red,LightGray,Black,Green,Red,Green,
              DarkGray,LightGray,DarkGray,Green,
              SingleBorder,' External port configuration ',
              ' <Enter> selects, <Esc> exits ',True));
  Ports := 0;
  For I := 0 To 2 Do
  Begin
    If MemW[0:$408 + I * 2] <> 0 Then
    Begin
      Menu^.AddSelection(1,I + 1,44,6,False,UsedCapability(LPTCap[I]),'  LPT' + IntToStr(I + 1));
      Inc(Ports);
    End;
  End; { For I }
  Menu^.AddSelection(1, 4,44,3,False,Not (UsedCapability(LPTCap[0]) Or
                                          UsedCapability(LPTCap[1]) Or
                                          UsedCapability(LPTCap[2])),'  None');
  Repeat
    For I := 1 To Ports Do
     Menu^.GetSelection(I)^.Checked := UsedCapability(LPTCap[I - 1]);
    Menu^.GetSelection(Ports + 1)^.Checked := Not (UsedCapability(LPTCap[0]) Or
                                                   UsedCapability(LPTCap[1]) Or
                                                   UsedCapability(LPTCap[2]));

    Menu^.Execute;

    { If a port is enabled, set the capabliity, otherwise clear it }

    If (Menu^.CurSel > 0) Then
    Begin
      If Menu^.CurSel < Ports + 1 Then
      Begin
        Case Menu^.CurSel Of
       1..3: SelectPair('Select Intv2PC interface',LPTCap[Menu^.CurSel - 1]);
        End; { Case }
      End
      Else FillChar(LPTCap,SizeOf(LPTCap),#0);
    End;
  Until Menu^.CurSel = 0;
  Dispose(Menu,Done);

  { Copy information to local variables }

  Intv2PCPort1 := 0;
  Intv2PCPort2 := 0;
  For I := 0 To 2 Do
  Begin
    If (LPTCap[I].Cont[1] <> 0) Or
       (LPTCap[I].Cont[2] <> 0) Then Intv2PCPort1 := MemW[0:$408 + I * 2];
    If (LPTCap[I].Cont[3] <> 0) Or
       (LPTCap[I].Cont[4] <> 0) Then Intv2PCPort2 := MemW[0:$408 + I * 2];
  End; { For I }
End; { TExternalInterface.Configure }
*)
Procedure TExternalInterface.GetControllerInfo(Var Controllers: TCapability;
                                           Var Info: TIntvControls);
Var
  CapMask : TCapability;
  LPT     : Integer;
  OldCont : TCapability;

Begin

  { Copy the initial controller list }

  OldCont := Controllers;

  { Initialize the capability mask to include all capabliities }

  SetAllCapability(CapMask);

  { Process parallel port }

  For LPT := 0 To 2 Do
  Begin
    If UsedCapability(LPTCap[LPT]) Then
    Begin

      { Read each controller info as long as we have the entire byte }

      Info.P[1] := Intv2PCInfo[1] Xor $FF;
      Info.P[2] := Intv2PCInfo[2] Xor $FF;
      Info.P[3] := Intv2PCInfo[3] Xor $FF;
      Info.P[4] := Intv2PCInfo[4] Xor $FF;

      { Kill all remaining capabilities }

      FillChar(CapMask,SizeOf(CapMask),#0);
    End;
  End; { For LPT }

  { Remove capabilities that were used }

  AndCapabilities(CapMask,Controllers);
End; // TExternalInterface.GetControllerInfo 

Procedure TExternalInterface.CopyTo(Intf: TInterface);
Begin
  Inherited CopyTo(Intf);
  TExternalInterface(Intf).LPTCap := LPTCap;
  TExternalInterface(Intf).Wrote  := Wrote;
End; // TExternalInterface.CopyTo

{ ---------- }
{ Other code }
{ ---------- }

Initialization
  InterfaceHandler := TInterfaceHandler.Create;
  FillChar(OldKey,SizeOf(OldKey),#0);
  Intv2PCPort1 := 0;
  Intv2PCPort2 := 0;                         
Finalization
  InterfaceHandler.Free;
End.

// ----------------------------------------------------------------------
// IntvWin - Mattel Intellivision Emulator
// Copyright (C) 2000 John Dullea
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// ----------------------------------------------------------------------
