unit PortIO;

{$DEFINE AUTOINIT}

{
  ALL IN ONE Unit/Component that enables direct access to IO ports in
  all MS-Windows OS versions, even in WinNT and Win2000 (supposing you
  have permission to load device drivers).

  Other file necessary for functioning in WinNT and Win2000 is GWIOPM.SYS -
  devicer driver file written by Graham Wideman. The file will be created
  automatically if not present, and loaded automatically as well.

  Usage:

    If AUTOINIT defined (default):
      there is no need to do any explicit initialization
      (PortIO component can not be used)

    You can use statements (asuming you have PortIO in USES section
    of your unit)

      Port[address] := byteValue;

        or

      byteValue := Port[address];

      (.. equivalent to Port.B[address] access;
          Port.W[address], Port.L[address] can be used for word and
          longint values )


    If not AUTOINIT defined, ports must be initialized:

      - just put the PortIO component on the main form (just one component allowed
        for application)

        or

      - place the following statement in the initialization part of your program:

         PortIO.Port := TPortIO.Create(nil);

           and

         PortIO.Port.Free;

         in deinitialization part.


  Author:

  Jindrich Jindrich

  Comments, bug reports send to: jindrich@jindrich.com

  You can use this unit for anything you want, but I am not responsible
  for anything.
}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  WinSvc;

const
  MaxPortAddr = $FFF;

{$IFDEF WIN32}

{gwiopm}

Const
  IOPM_SIZE = $2000;
Type
  TIOPM = array[0..IOPM_SIZE] of byte;
  PIOPM = ^TIOPM;

  TGWIOPM_Driver = class;
{/gwiopm}

{$ENDIF}

  TPortIO = class(TComponent)
  private
    { Private declarations }
    {$IFDEF WIN32}
    FIsNT: boolean;
    FGWIODriver: TGWIOPM_Driver;
    FPortsEnabled: array[0..MaxPortAddr] of boolean;
    FDriverInstalled: boolean;
    FDriverStarted: boolean;
    {$ENDIF}
  protected
    { Protected declarations }
    function GetPortB(Index: integer): byte;
    procedure SetPortB(Index: integer; AValue: byte);
    function GetPortW(Index: integer): word;
    procedure SetPortW(Index: integer; AValue: word);
    function GetPortL(Index: integer): longint;
    procedure SetPortL(Index: integer; AValue: longint);
    {$IFDEF WIN32}
    procedure PortEnable(Index: integer; Count: integer; OnOff: boolean);
    procedure CheckDriverFile;
    {$ENDIF}
  public
    { Public declarations }
    constructor Create(AOwner: TComponent);reintroduce;
    destructor Destroy;override;
    property Port[Index: integer]: byte read GetPortB write SetPortB;default;
    property B[Index: integer]: byte read GetPortB write SetPortB;
    property W[Index: integer]: word read GetPortW write SetPortW;
    property L[Index: integer]: longint read GetPortL write SetPortL;
  published
    { Published declarations }{modulu}
  end;


{$IFDEF WIN32}
{gwiopm}
  TGWIOPM_Driver = class
  private
    HomeDir         : string;
    DriverDir       : string;
    DriverName      : string;
    DriverPath      : string; // the whole thing
    hSCMan          : SC_HANDLE; // Service Control Manager
    hDevice         : SC_HANDLE; // Handle for device

    //---------------------------------------
    // private multi-func funcs called by public ones
    //---------------------------------------
    function IOCTL_IOPMD_Misc1(var RetVal: DWORD; Cmd: integer):  DWORD;
    function IOCTL_IOPMD_GET_SET_LIOPM(Addr: Word; var B: byte; cmd: integer): DWORD;

  public
    LastOutBuf      : longint;
    constructor Create;

    //---------------------------------------
    // Interact with Service Control Manager
    //---------------------------------------
    function OpenSCM: DWORD;
    function CloseSCM: DWORD;

    //---------------------------------------
    // Install/Start/Stop/Remove driver
    //---------------------------------------
    function Install(newdriverpath: string): DWORD; { use '' for default }
    function Start:   DWORD;
    function Stop:    DWORD;
    function Remove:  DWORD;

    //--------------------------------
    // Device Open/Close
    //--------------------------------
    function DeviceOpen: DWORD;   // get a valid hDevice
    function DeviceClose: DWORD;

    //--------------------------------
    // IO Permission Map functions
    //--------------------------------
    // Test functions
    function IOCTL_IOPMD_READ_TEST(var RetVal: DWORD): DWORD;
    function IOCTL_IOPMD_READ_VERSION(var RetVal: DWORD): DWORD;
    // Manipulate driver's local IOPM (LIOPM)
    function IOCTL_IOPMD_CLEAR_LIOPM:  DWORD;    // set "local" map to block perm for all I/O addr
    function IOCTL_IOPMD_SET_LIOPM(Addr: Word; B: byte): DWORD;  // set a byte (8 ports-worth) in LIOPM
    function IOCTL_IOPMD_GET_LIOPMB(Addr: Word; var B: byte): DWORD;  // get a byte from LIOPM (diagnostic)
    function IOCTL_IOPMD_GET_LIOPMA(var A: TIOPM): DWORD;  // get entire LIOPM array (diagnostic)

    function LIOPM_Set_Ports(BeginPort: word; EndPort: word; Enable: Boolean): DWORD;

    // Interact with kernel IOPM (KIOPM)
    function IOCTL_IOPMD_ACTIVATE_KIOPM: DWORD;              // copy LIOPM to be active map
    function IOCTL_IOPMD_DEACTIVATE_KIOPM: DWORD;            // tell kernel to forget map
    function IOCTL_IOPMD_QUERY_KIOPM: DWORD;                  // query KIOPM to LIOPM
    //--------------------------------
    function ErrorLookup(ErrorNum: integer): string;
  end;
{/gwiopm}

{$ENDIF}


procedure Register;

const
  Port: TPortIO = nil;

implementation

{$IFDEF WIN32}
const
  gwiopm: array[0..3903] of byte = (
   77, 90,144,  0,  3,  0,  0,  0,  4,  0,  0,  0,255,255,  0,  0,
  184,  0,  0,  0,  0,  0,  0,  0, 64,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,128,  0,  0,  0,
   14, 31,186, 14,  0,180,  9,205, 33,184,  1, 76,205, 33, 84,104,
  105,115, 32,112,114,111,103,114, 97,109, 32, 99, 97,110,110,111,
  116, 32, 98,101, 32,114,117,110, 32,105,110, 32, 68, 79, 83, 32,
  109,111,100,101, 46, 13, 13, 10, 36,  0,  0,  0,  0,  0,  0,  0,
   80, 69,  0,  0, 76,  1,  4,  0, 43,171,117, 53,  0,  8,  0,  0,
   32,  0,  0,  0,224,  0,  6,  1, 11,  1,  5,  0, 64,  5,  0,  0,
  128,  0,  0,  0,  0,  0,  0,  0, 22,  5,  0,  0, 32,  2,  0,  0,
  224,  5,  0,  0,  0,  0,  1,  0, 32,  0,  0,  0, 32,  0,  0,  0,
    4,  0,  0,  0,  4,  0,  0,  0,  4,  0,  0,  0,  0,  0,  0,  0,
  224,  7,  0,  0, 32,  2,  0,  0,128,179,  0,  0,  1,  0,  0,  0,
    0,  0, 16,  0,  0, 16,  0,  0,  0,  0, 16,  0,  0, 16,  0,  0,
    0,  0,  0,  0, 16,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  6,  0,  0, 40,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
  128,  7,  0,  0, 64,  0,  0,  0, 96,  2,  0,  0, 84,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0, 32,  2,  0,  0, 52,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0, 46,116,101,120,116,  0,  0,  0,
  180,  3,  0,  0, 32,  2,  0,  0,192,  3,  0,  0, 32,  2,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 32,  0,  0,104,
   46,100, 97,116, 97,  0,  0,  0,  4,  0,  0,  0,224,  5,  0,  0,
   32,  0,  0,  0,224,  5,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0, 64,  0,  0,200, 73, 78, 73, 84,  0,  0,  0,  0,
  126,  1,  0,  0,  0,  6,  0,  0,128,  1,  0,  0,  0,  6,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 32,  0,  0,226,
   46,114,101,108,111, 99,  0,  0, 92,  0,  0,  0,128,  7,  0,  0,
   96,  0,  0,  0,128,  7,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0, 64,  0,  0, 66,  0,  0,  0,  0,  0,  0,  0,  0,
  226,  6,  0,  0,114,  6,  0,  0,140,  6,  0,  0,162,  6,  0,  0,
  186,  6,  0,  0,208,  6,  0,  0, 92,  6,  0,  0,250,  6,  0,  0,
   18,  7,  0,  0, 42,  7,  0,  0, 66,  7,  0,  0, 84,  7,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0, 43,171,117, 53,  0,  0,  0,  0,  1,  0,  0,  0,
  201,  5,  0,  0,  0,  0,  0,  0,224,  7,  0,  0,  0,  0,  0,  0,
   43,171,117, 53,  0,  0,  0,  0,  4,  0,  0,  0, 16,  1,  0,  0,
    0,  0,  0,  0,172, 13,  0,  0,  0,  0,  0,  0, 43,171,117, 53,
    0,  0,  0,  0,  3,  0,  0,  0,112,  0,  0,  0,  0,  0,  0,  0,
  188, 14,  0,  0,106,  1,255, 21, 40,  2,  1,  0, 80,232,  6,  3,
    0,  0,255, 53,224,  5,  1,  0,106,  1,232,243,  2,  0,  0,195,
  106,  0,255, 21, 40,  2,  1,  0, 80,232,234,  2,  0,  0,195,204,
  255, 53,224,  5,  1,  0,106,  1,232,225,  2,  0,  0,195, 51,192,
  139, 13,224,  5,  1,  0,128, 12,  8,255, 64, 61,  0, 32,  0,  0,
  124,238,195,204, 83,139, 92, 36, 12, 51,192, 85,139, 75, 96, 86,
   87,190,  2,  0,  0,192, 15,182, 17, 43,208, 15,132, 22,  1,  0,
    0, 74, 74, 15,132, 14,  1,  0,  0,106, 12, 95, 43,215, 15,133,
    5,  1,  0,  0,139,105,  4,139, 73, 12,139,115, 12,186,  4, 36,
    0,241, 59,202,119, 43,116, 30,184,  0, 36,  0,241, 59,200,117,
   96,199,  6, 35,  1,  0,  0,199, 70,  4, 52, 18,  0,  0,137, 70,
    8,233,207,  0,  0,  0,199,  6,110,  0,  0,  0,233,183,  0,  0,
    0,187, 64, 36,  0,241, 59,203, 15,132,175,  0,  0,  0,186, 68,
   36,  0,241, 59,202, 15,132,132,  0,  0,  0,131,194,  4, 59,202,
  116,103,129,249, 76, 36,  0,241,116, 56,131,195, 64, 59,203,116,
   42,187,132, 36,  0,241, 59,203,116, 26,131,195,  4, 59,203,116,
   12,137, 78,  8,139,199,190, 16,  0,  0,192,235,124,232, 30,255,
  255,255,235,110,232,  7,255,255,255,235,103,232,228,254,255,255,
  235, 96,185,  0, 32,  0,  0, 59,233,115,  7,190, 13,  0,  0,192,
  235, 87, 51,192,139, 21,224,  5,  1,  0,138, 20, 16,136, 20,  6,
   64, 59,193,124,239,139,193,235, 62,139,  6,139, 13,224,  5,  1,
    0, 37,255, 31,  0,  0, 15,182,  4,  8,137, 70,  4,235, 25,139,
   70,  4,139, 14,139, 29,224,  5,  1,  0, 37,255,  0,  0,  0,129,
  225,255, 31,  0,  0,136,  4, 25,137, 86,  8,235,  8,232,188,254,
  255,255,137, 94,  8,139,199, 51,246,139, 76, 36, 24, 50,210,137,
  113, 24,137, 65, 28,255, 21, 48,  2,  1,  0,139,198, 95, 94, 93,
   91,194,  8,  0, 92,  0, 68,  0,111,  0,115,  0, 68,  0,101,  0,
  118,  0,105,  0, 99,  0,101,  0,115,  0, 92,  0,103,  0,119,  0,
  105,  0,111,  0,112,  0,109,  0,  0,  0, 85,139,236,131,236, 48,
   86, 87,106,  9,190, 84,  4,  1,  0, 89,141,125,208,243,165,161,
  224,  5,  1,  0,133,192,102,165,116, 12,104,  0, 32,  0,  0, 80,
  255, 21, 64,  2,  1,  0,141, 69,208, 80,141, 69,248, 80,255, 21,
   60,  2,  1,  0,141, 69,248, 80,255, 21, 32,  2,  1,  0,139, 69,
    8,255,112,  4,255, 21, 52,  2,  1,  0, 95, 94,201,194,  4,  0,
   92,  0, 68,  0,101,  0,118,  0,105,  0, 99,  0,101,  0, 92,  0,
  103,  0,119,  0,105,  0,111,  0,112,  0,109,  0,  0,  0,  0,  0,
   92,  0, 68,  0,111,  0,115,  0, 68,  0,101,  0,118,  0,105,  0,
   99,  0,101,  0,115,  0, 92,  0,103,  0,119,  0,105,  0,111,  0,
  112,  0,109,  0,  0,  0, 85,139,236,131,236, 92, 86, 87,106,  7,
  190,208,  4,  1,  0, 89,141,125,204,243,165,102,165,106,  9,190,
  240,  4,  1,  0, 89,141,125,164,243,165,104,  0, 32,  0,  0,102,
  165,255, 21, 76,  2,  1,  0, 51,255,163,224,  5,  1,  0, 59,199,
  117,  7,184,154,  0,  0,192,235, 99,232,144,253,255,255,139, 53,
   60,  2,  1,  0,141, 69,204, 80,141, 69,244, 80,255,214,141, 69,
  164, 80,141, 69,236, 80,255,214,139,117,  8,141, 69,252, 80, 87,
   87,141, 69,244,104,  0,241,  0,  0, 80, 87, 86,255, 21, 72,  2,
    1,  0, 59,199,124, 38,141, 69,244, 80,141, 69,236, 80,255, 21,
   68,  2,  1,  0, 59,199,124, 20,184,  4,  3,  1,  0,199, 70, 52,
  122,  4,  1,  0,137, 70, 56,137, 70,112, 51,192, 95, 94,201,194,
    8,  0,255, 37, 56,  2,  1,  0,255, 37, 36,  2,  1,  0,255, 37,
   44,  2,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   40,  6,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,112,  7,  0,  0,
   32,  2,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,226,  6,  0,  0,114,  6,  0,  0,
  140,  6,  0,  0,162,  6,  0,  0,186,  6,  0,  0,208,  6,  0,  0,
   92,  6,  0,  0,250,  6,  0,  0, 18,  7,  0,  0, 42,  7,  0,  0,
   66,  7,  0,  0, 84,  7,  0,  0,  0,  0,  0,  0, 76,  1, 75,101,
   51, 56, 54, 83,101,116, 73,111, 65, 99, 99,101,115,115, 77, 97,
  112,  0, 74,  1, 75,101, 51, 56, 54, 73,111, 83,101,116, 65, 99,
   99,101,115,115, 80,114,111, 99,101,115,115,  0, 12,  1, 73,111,
   71,101,116, 67,117,114,114,101,110,116, 80,114,111, 99,101,115,
  115,  0, 75,  1, 75,101, 51, 56, 54, 81,117,101,114,121, 73,111,
   65, 99, 99,101,115,115, 77, 97,112,  0, 69,  1, 73,111,102, 67,
  111,109,112,108,101,116,101, 82,101,113,117,101,115,116,  0,  0,
  251,  0, 73,111, 68,101,108,101,116,101, 68,101,118,105, 99,101,
    0,  0,252,  0, 73,111, 68,101,108,101,116,101, 83,121,109, 98,
  111,108,105, 99, 76,105,110,107,  0,  0,208,  2, 82,116,108, 73,
  110,105,116, 85,110,105, 99,111,100,101, 83,116,114,105,110,103,
    0,  0,220,  1, 77,109, 70,114,101,101, 78,111,110, 67, 97, 99,
  104,101,100, 77,101,109,111,114,121,  0,247,  0, 73,111, 67,114,
  101, 97,116,101, 83,121,109, 98,111,108,105, 99, 76,105,110,107,
    0,  0,243,  0, 73,111, 67,114,101, 97,116,101, 68,101,118,105,
   99,101,  0,  0,210,  1, 77,109, 65,108,108,111, 99, 97,116,101,
   78,111,110, 67, 97, 99,104,101,100, 77,101,109,111,114,121,  0,
  110,116,111,115,107,114,110,108, 46,101,120,101,  0,  0,  0,  0,
    0,  0,  0,  0, 64,  0,  0,  0,184, 50,196, 50,212, 50,226, 50,
  242, 50,230, 51,253, 51, 22, 52, 71, 52,133, 52,144, 52,162, 52,
  176, 52,186, 52,198, 52, 33, 53, 48, 53, 67, 53, 74, 53, 96, 53,
  142, 53,160, 53,169, 53,176, 53,196, 53,202, 53,208, 53,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
   32,  0,  0,  0, 32,  0,  0,  0,  0,  0,  0,  0, 32,  0,  0,  0,
   32,  2,  0,  0, 96,  7,  0,  0,224,  5,  0,  0,160,  2,  0,  0,
   36, 83, 71, 51, 49, 51, 49,  0, 84,  4,  0,  0,  1,  0,  0,  0,
    3,  0, 36, 83, 71, 51, 49, 52, 51,  0,240,  4,  0,  0,  1,  0,
    0,  0,  3,  0, 36, 83, 71, 51, 49, 52, 49,  0,208,  4,  0,  0,
    1,  0,  0,  0,  3,  0, 46,105,100, 97,116, 97, 36, 54,112,  7,
    0,  0,  3,  0,  0,  0,  3,  0,104,101, 97,100,101,114,  0,  0,
    0,  0,  0,  0,254,255,  0,  0,  2,  0,  0,  0,  0,  0, 55,  1,
    0,  0, 32,  2,  0,  0,  1,  0,  0,  0,  2,  0,  0,  0,  0,  0,
   38,  2,  0,  0, 36,  2,  0,  0,  1,  0,  0,  0,  2,  0,  0,  0,
    0,  0,205,  0,  0,  0, 40,  2,  0,  0,  1,  0,  0,  0,  2,  0,
    0,  0,  0,  0, 94,  2,  0,  0, 44,  2,  0,  0,  1,  0,  0,  0,
    2,  0,  0,  0,  0,  0,  3,  1,  0,  0, 48,  2,  0,  0,  1,  0,
    0,  0,  2,  0,  0,  0,  0,  0, 31,  1,  0,  0, 52,  2,  0,  0,
    1,  0,  0,  0,  2,  0,  0,  0,  0,  0,236,  1,  0,  0, 56,  2,
    0,  0,  1,  0,  0,  0,  2,  0,  0,  0,  0,  0, 85,  1,  0,  0,
   60,  2,  0,  0,  1,  0,  0,  0,  2,  0,  0,  0,  0,  0,115,  1,
    0,  0, 64,  2,  0,  0,  1,  0,  0,  0,  2,  0,  0,  0,  0,  0,
  146,  1,  0,  0, 68,  2,  0,  0,  1,  0,  0,  0,  2,  0,  0,  0,
    0,  0,176,  1,  0,  0, 72,  2,  0,  0,  1,  0,  0,  0,  2,  0,
    0,  0,  0,  0,201,  1,  0,  0, 76,  2,  0,  0,  1,  0,  0,  0,
    2,  0,  0,  0,  0,  0, 79,  3,  0,  0, 80,  2,  0,  0,  1,  0,
    0,  0,  2,  0,  0,  0,  0,  0, 19,  0,  0,  0,180,  2,  0,  0,
    1,  0, 32,  0,  2,  0,  0,  0,  0,  0, 42,  0,  0,  0,208,  2,
    0,  0,  1,  0, 32,  0,  2,  0,  0,  0,  0,  0, 67,  0,  0,  0,
  224,  2,  0,  0,  1,  0, 32,  0,  2,  0,  0,  0,  0,  0, 87,  0,
    0,  0,238,  2,  0,  0,  1,  0, 32,  0,  2,  0,  0,  0,  0,  0,
  107,  0,  0,  0,  4,  3,  0,  0,  1,  0, 32,  0,  2,  0,  0,  0,
    0,  0,126,  0,  0,  0,122,  4,  0,  0,  1,  0, 32,  0,  2,  0,
    0,  0,  0,  0,  4,  0,  0,  0, 22,  5,  0,  0,  1,  0, 32,  0,
    2,  0,  0,  0,  0,  0,155,  0,  0,  0,194,  5,  0,  0,  1,  0,
   32,  0,  2,  0,  0,  0,  0,  0,178,  0,  0,  0,200,  5,  0,  0,
    1,  0, 32,  0,  2,  0,  0,  0,  0,  0,234,  0,  0,  0,206,  5,
    0,  0,  1,  0, 32,  0,  2,  0,  0,  0,  0,  0,143,  0,  0,  0,
  224,  5,  0,  0,  2,  0,  0,  0,  2,  0,  0,  0,  0,  0,  9,  2,
    0,  0,  0,  6,  0,  0,  3,  0,  0,  0,  2,  0,  0,  0,  0,  0,
   54,  3,  0,  0, 20,  6,  0,  0,  3,  0,  0,  0,  2,  0,101,110,
  100,  0,  0,  0,  0,  0,224,  7,  0,  0,254,255,  0,  0,  2,  0,
  105,  3,  0,  0, 95, 68,114,105,118,101,114, 69,110,116,114,121,
   64, 56,  0, 95,100,105,115,112, 95, 65, 67, 84, 73, 86, 65, 84,
   69, 95, 75, 73, 79, 80, 77, 64, 48,  0, 95,100,105,115,112, 95,
   68, 69, 65, 67, 84, 73, 86, 65, 84, 69, 95, 75, 73, 79, 80, 77,
   64, 48,  0, 95,100,105,115,112, 95, 81, 85, 69, 82, 89, 95, 75,
   73, 79, 80, 77, 64, 48,  0, 95,100,105,115,112, 95, 67, 76, 69,
   65, 82, 95, 76, 73, 79, 80, 77, 64, 48,  0, 95,103,119,105,111,
  112,109, 95, 68,105,115,112, 97,116, 99,104, 64, 56,  0, 95,103,
  119,105,111,112,109, 95, 85,110,108,111, 97,100, 64, 52,  0, 95,
   73, 79, 80, 77, 95,108,111, 99, 97,108,  0, 95, 75,101, 51, 56,
   54, 83,101,116, 73,111, 65, 99, 99,101,115,115, 77, 97,112, 64,
   56,  0, 95, 75,101, 51, 56, 54, 73,111, 83,101,116, 65, 99, 99,
  101,115,115, 80,114,111, 99,101,115,115, 64, 56,  0, 95, 95,105,
  109,112, 95, 95, 73,111, 71,101,116, 67,117,114,114,101,110,116,
   80,114,111, 99,101,115,115, 64, 48,  0, 95, 75,101, 51, 56, 54,
   81,117,101,114,121, 73,111, 65, 99, 99,101,115,115, 77, 97,112,
   64, 56,  0, 95, 95,105,109,112, 95, 64, 73,111,102, 67,111,109,
  112,108,101,116,101, 82,101,113,117,101,115,116, 64, 56,  0, 95,
   95,105,109,112, 95, 95, 73,111, 68,101,108,101,116,101, 68,101,
  118,105, 99,101, 64, 52,  0, 95, 95,105,109,112, 95, 95, 73,111,
   68,101,108,101,116,101, 83,121,109, 98,111,108,105, 99, 76,105,
  110,107, 64, 52,  0, 95, 95,105,109,112, 95, 95, 82,116,108, 73,
  110,105,116, 85,110,105, 99,111,100,101, 83,116,114,105,110,103,
   64, 56,  0, 95, 95,105,109,112, 95, 95, 77,109, 70,114,101,101,
   78,111,110, 67, 97, 99,104,101,100, 77,101,109,111,114,121, 64,
   56,  0, 95, 95,105,109,112, 95, 95, 73,111, 67,114,101, 97,116,
  101, 83,121,109, 98,111,108,105, 99, 76,105,110,107, 64, 56,  0,
   95, 95,105,109,112, 95, 95, 73,111, 67,114,101, 97,116,101, 68,
  101,118,105, 99,101, 64, 50, 56,  0, 95, 95,105,109,112, 95, 95,
   77,109, 65,108,108,111, 99, 97,116,101, 78,111,110, 67, 97, 99,
  104,101,100, 77,101,109,111,114,121, 64, 52,  0, 95, 95,105,109,
  112, 95, 95, 75,101, 51, 56, 54, 83,101,116, 73,111, 65, 99, 99,
  101,115,115, 77, 97,112, 64, 56,  0, 95, 95, 73, 77, 80, 79, 82,
   84, 95, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 95,110,116,111,
  115,107,114,110,108,  0, 95, 95,105,109,112, 95, 95, 75,101, 51,
   56, 54, 73,111, 83,101,116, 65, 99, 99,101,115,115, 80,114,111,
   99,101,115,115, 64, 56,  0, 95, 73,111, 71,101,116, 67,117,114,
  114,101,110,116, 80,114,111, 99,101,115,115, 64, 48,  0, 95, 95,
  105,109,112, 95, 95, 75,101, 51, 56, 54, 81,117,101,114,121, 73,
  111, 65, 99, 99,101,115,115, 77, 97,112, 64, 56,  0, 64, 73,111,
  102, 67,111,109,112,108,101,116,101, 82,101,113,117,101,115,116,
   64, 56,  0, 95, 73,111, 68,101,108,101,116,101, 68,101,118,105,
   99,101, 64, 52,  0, 95, 73,111, 68,101,108,101,116,101, 83,121,
  109, 98,111,108,105, 99, 76,105,110,107, 64, 52,  0, 95, 82,116,
  108, 73,110,105,116, 85,110,105, 99,111,100,101, 83,116,114,105,
  110,103, 64, 56,  0, 95, 77,109, 70,114,101,101, 78,111,110, 67,
   97, 99,104,101,100, 77,101,109,111,114,121, 64, 56,  0, 95, 73,
  111, 67,114,101, 97,116,101, 83,121,109, 98,111,108,105, 99, 76,
  105,110,107, 64, 56,  0, 95, 73,111, 67,114,101, 97,116,101, 68,
  101,118,105, 99,101, 64, 50, 56,  0, 95, 77,109, 65,108,108,111,
   99, 97,116,101, 78,111,110, 67, 97, 99,104,101,100, 77,101,109,
  111,114,121, 64, 52,  0, 95, 95, 78, 85, 76, 76, 95, 73, 77, 80,
   79, 82, 84, 95, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82,  0,127,
  110,116,111,115,107,114,110,108, 95, 78, 85, 76, 76, 95, 84, 72,
   85, 78, 75, 95, 68, 65, 84, 65,  0,  0,  0,  0,  1,  0,  0,  0,
   16,  1,  0,  0,  0,  0,  0,  0, 46, 92,105, 51, 56, 54, 92,102,
  114,101,101, 92,103,119,105,111,112,109, 46,115,121,115,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,180,  2,  0,  0,
   28,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,208,  2,  0,  0,
   15,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,224,  2,  0,  0,
   14,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,238,  2,  0,  0,
   21,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  4,  3,  0,  0,
   80,  1,  0,  0,  0,  0,  0,  0,  2,  0,  0, 20,122,  4,  0,  0,
   86,  0,  0,  0, 13,  0,  0,  0,  1,  0,  8,210, 22,  5,  0,  0,
  172,  0,  0,  0, 24,  0,  0,  0,  2,  0,  8,210,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
  );

{gwportio}
//unit gwportio;
{-----------------------------------------------
Functions for accessing I/O Ports with Delphi 2 and 3.
These work as-is under Win95 (and presumably Win98).

However, use of I/O instructions by "user-mode" applications
under NT is blocked by by the IO Permissions mechanism.
To get around that, use gwiopm unit etc.

Revisions
----------
98-05-23 GW original

------------------------------------------------}
//interface
//uses windows, winsvc;

//-----------------------------------
// lowest-complexity I/O port functions
//-----------------------------------

//function  PortIn(  PortNum: word)  : byte;
//procedure PortOut( PortNum: word; a: byte);

//function  PortInW( PortNum: word)  : word;
//procedure PortOutW(PortNum: word; a: word);

//function  PortInL( PortNum: word)  : longint;
//procedure PortOutL(PortNum: word; a: longint);

//-------------------------------------------
//implementation
//-------------------------------------------
{$ENDIF WIN32}

//-----------------------------------------
function  PortIn( PortNum: word): byte;
//-----------------------------------------
Var a : byte;
Begin
  asm
    mov DX, PortNum;
    in  AL, DX;
    mov a, AL;
  end; {asm}
  PortIn := a;
end;

//-----------------------------------------
procedure PortOut( PortNum: word; a: byte);
//-----------------------------------------
Begin
  asm
    mov DX, PortNum;
    mov AL, a;
    out DX, AL;
  end; {asm}
end;

//-----------------------------------------
function  PortInW( PortNum: word): word;
//-----------------------------------------
Var a : word;
Begin
  asm
    mov DX, PortNum;
    in  AX, DX;
    mov a, AX;
  end; {asm}
  PortInW := a;
end;

//-----------------------------------------
procedure PortOutW( PortNum: word; a: word);
//-----------------------------------------
Begin
  asm
    mov DX, PortNum;
    mov AX, a;
    out DX, AX;
  end; {asm}
end;

//-----------------------------------------
function  PortInL( PortNum: word): longint;
//-----------------------------------------
Var a : longint;
Begin
  asm
    mov DX, PortNum;
    in  EAX, DX;
    mov a, EAX;
  end; {asm}
  PortInL := a;
end;

//-----------------------------------------
procedure PortOutL( PortNum: word; a: longint);
//-----------------------------------------
Begin
  asm
    mov DX, PortNum;
    mov EAX, a;
    out DX, EAX;
  end; {asm}
end;

//end.
{/gwportio}


{$IFDEF WIN32}
{gwiopm}
//unit gwiopm;
{-----------------------------------------------
Functions for interacting with gwiopm I/O-permissions-map "driver",
in order to give cpu I/O instructions permission to operate
via the I/O permissions mechanism under Win NT.

Revisions
---------
98-05-23 GW original

Copyright Graham Wideman
------------------------
This module is distributed as freeware, and may be freely used for any purpose.
I would appreciate a credit notice if this is useful in your work. Thanks.

Note that this work was greatly aided by demo code from:
Dale Roberts      (giveio.sys)
Paula Tomlinson   (LOADDRV)

------------------------------------------------}
//interface
//uses windows, winsvc;
(*
Const
  IOPM_SIZE = $2000;
Type
  TIOPM = array[0..IOPM_SIZE] of byte;
  PIOPM = ^TIOPM;

  TGWIOPM_Driver = class
  private
    HomeDir         : string;
    DriverDir       : string;
    DriverName      : string;
    DriverPath      : string; // the whole thing
    hSCMan          : SC_HANDLE; // Service Control Manager
    hDevice         : SC_HANDLE; // Handle for device

    //---------------------------------------
    // private multi-func funcs called by public ones
    //---------------------------------------
    function IOCTL_IOPMD_Misc1(var RetVal: DWORD; Cmd: integer):  DWORD;
    function IOCTL_IOPMD_GET_SET_LIOPM(Addr: Word; var B: byte; cmd: integer): DWORD;

  public
    LastOutBuf      : longint;
    constructor Create;

    //---------------------------------------
    // Interact with Service Control Manager
    //---------------------------------------
    function OpenSCM: DWORD;
    function CloseSCM: DWORD;

    //---------------------------------------
    // Install/Start/Stop/Remove driver
    //---------------------------------------
    function Install(newdriverpath: string): DWORD; { use '' for default }
    function Start:   DWORD;
    function Stop:    DWORD;
    function Remove:  DWORD;

    //--------------------------------
    // Device Open/Close
    //--------------------------------
    function DeviceOpen: DWORD;   // get a valid hDevice
    function DeviceClose: DWORD;

    //--------------------------------
    // IO Permission Map functions
    //--------------------------------
    // Test functions
    function IOCTL_IOPMD_READ_TEST(var RetVal: DWORD): DWORD;
    function IOCTL_IOPMD_READ_VERSION(var RetVal: DWORD): DWORD;
    // Manipulate driver's local IOPM (LIOPM)
    function IOCTL_IOPMD_CLEAR_LIOPM:  DWORD;    // set "local" map to block perm for all I/O addr
    function IOCTL_IOPMD_SET_LIOPM(Addr: Word; B: byte): DWORD;  // set a byte (8 ports-worth) in LIOPM
    function IOCTL_IOPMD_GET_LIOPMB(Addr: Word; var B: byte): DWORD;  // get a byte from LIOPM (diagnostic)
    function IOCTL_IOPMD_GET_LIOPMA(var A: TIOPM): DWORD;  // get entire LIOPM array (diagnostic)

    function LIOPM_Set_Ports(BeginPort: word; EndPort: word; Enable: Boolean): DWORD;

    // Interact with kernel IOPM (KIOPM)
    function IOCTL_IOPMD_ACTIVATE_KIOPM: DWORD;              // copy LIOPM to be active map
    function IOCTL_IOPMD_DEACTIVATE_KIOPM: DWORD;            // tell kernel to forget map
    function IOCTL_IOPMD_QUERY_KIOPM: DWORD;                  // query KIOPM to LIOPM
    //--------------------------------
    function ErrorLookup(ErrorNum: integer): string;
  end;
*)
Const
  DEVICE_NAME_STRING	= 'gwiopm'; // in application's home directory

  // Device type           -- in the "User Defined" range."
  IOPMD_TYPE = $F100;

  // The IOCTL function codes from 0x800 to 0xFFF are for non-Microsoft use.
  // Test functions
  IOCMD_IOPMD_READ_TEST        = $900;
  IOCMD_IOPMD_READ_VERSION     = $901;
  // Manipulate driver's local IOPM (LIOPM)
  IOCMD_IOPMD_CLEAR_LIOPM      = $910;
  IOCMD_IOPMD_SET_LIOPM        = $911;
  IOCMD_IOPMD_GET_LIOPMB       = $912;
  IOCMD_IOPMD_GET_LIOPMA       = $913;
  // Interact with kernel IOPM (KIOPM)
  IOCMD_IOPMD_ACTIVATE_KIOPM   = $920;
  IOCMD_IOPMD_DEACTIVATE_KIOPM = $921;
  IOCMD_IOPMD_QUERY_KIOPM      = $922;

//Var
//  GWIOPM_Driver: TGWIOPM_Driver;

//-------------------------------------------
//implementation
//-------------------------------------------
//uses sysutils;

Const // from ntddk
// Service Types (Bit Mask)
  SERVICE_KERNEL_DRIVER        =  $00000001;
  SERVICE_FILE_SYSTEM_DRIVER   =  $00000002;
  SERVICE_ADAPTER              =  $00000004;
  SERVICE_RECOGNIZER_DRIVER    =  $00000008;

  SERVICE_DRIVER               =  SERVICE_KERNEL_DRIVER OR
                                  SERVICE_FILE_SYSTEM_DRIVER OR
                                  SERVICE_RECOGNIZER_DRIVER;

  SERVICE_WIN32_OWN_PROCESS    =  $00000010;
  SERVICE_WIN32_SHARE_PROCESS  =  $00000020;
  SERVICE_WIN32                =  SERVICE_WIN32_OWN_PROCESS OR
                                  SERVICE_WIN32_SHARE_PROCESS;

  SERVICE_INTERACTIVE_PROCESS  =  $00000100;

  SERVICE_TYPE_ALL             =  SERVICE_WIN32   OR
                                  SERVICE_ADAPTER OR
                                  SERVICE_DRIVER  OR
                                  SERVICE_INTERACTIVE_PROCESS;
// Start Type
  SERVICE_BOOT_START           =  $00000000;
  SERVICE_SYSTEM_START         =  $00000001;
  SERVICE_AUTO_START           =  $00000002;
  SERVICE_DEMAND_START         =  $00000003;
  SERVICE_DISABLED             =  $00000004;

// Error control type
  SERVICE_ERROR_IGNORE         =  $00000000;
  SERVICE_ERROR_NORMAL         =  $00000001;
  SERVICE_ERROR_SEVERE         =  $00000002;
  SERVICE_ERROR_CRITICAL       =  $00000003;

Type
  TErrorMsg = record
    Num: integer;
    Msg: string;
  end;

Const
  ErrorMsgCt = 30;
  ERROR_SCM_CANT_CONNECT = 9998;
  ERROR_NO_DEVICE_HANDLE = 9997;
  ERROR_GW_BUFFER_TOO_SMALL = 9997;
  ERROR_UNEXPECTED = 9999;

  ErrorMsgs: array[1..ErrorMsgCt] of TErrorMsg = (
    (Num: ERROR_SUCCESS                   ; Msg: 'Operation was successful'),
    (Num: ERROR_INVALID_FUNCTION          ; Msg: 'Invalid Function'),
    (Num: ERROR_ACCESS_DENIED             ; Msg: 'Access denied'),
    (Num: ERROR_CIRCULAR_DEPENDENCY       ; Msg: 'Circular dependency'),
    (Num: ERROR_DATABASE_DOES_NOT_EXIST   ; Msg: 'Database doesn''t exist'),
    (Num: ERROR_DEPENDENT_SERVICES_RUNNING; Msg: 'Dependent services running'),
    (Num: ERROR_DUP_NAME                  ; Msg: 'Display name already exists'),
    (Num: ERROR_INVALID_HANDLE            ; Msg: 'Invalid handle'),
    (Num: ERROR_INVALID_NAME              ; Msg: 'Invalid service name'),
    (Num: ERROR_INVALID_PARAMETER         ; Msg: 'Invalid Parameter'),
    (Num: ERROR_INVALID_SERVICE_ACCOUNT   ; Msg: 'User account doesn''t exist'),
    (Num: ERROR_INVALID_SERVICE_CONTROL   ; Msg: 'Invalid service control code'),
    (Num: ERROR_PATH_NOT_FOUND            ; Msg: 'Path not found'),
    (Num: ERROR_SERVICE_ALREADY_RUNNING   ; Msg: 'Service already running'),
    (Num: ERROR_SERVICE_CANNOT_ACCEPT_CTRL; Msg: 'Service can''t accept control'),
    (Num: ERROR_SERVICE_DATABASE_LOCKED   ; Msg: 'The database is locked'),
    (Num: ERROR_SERVICE_DEPENDENCY_DELETED; Msg: 'Depends on nonexistant service'),
    (Num: ERROR_SERVICE_DEPENDENCY_FAIL   ; Msg: 'Depends on service that failed'),
    (Num: ERROR_SERVICE_DISABLED          ; Msg: 'Service has been disabled'),
    (Num: ERROR_SERVICE_DOES_NOT_EXIST    ; Msg: 'Service doesn''t exist'),
    (Num: ERROR_SERVICE_EXISTS            ; Msg: 'Service already exists'),
    (Num: ERROR_SERVICE_LOGON_FAILED      ; Msg: 'Service couldn''t be logged on'),
    (Num: ERROR_SERVICE_MARKED_FOR_DELETE ; Msg: 'Service marked for deletion'),
    (Num: ERROR_SERVICE_NO_THREAD         ; Msg: 'Couldn''t create thread'),
    (Num: ERROR_SERVICE_NOT_ACTIVE        ; Msg: 'Service hasn''t been started'),
    (Num: ERROR_SERVICE_REQUEST_TIMEOUT   ; Msg: 'Service timed out'),
    (Num: ERROR_GW_BUFFER_TOO_SMALL       ; Msg: 'Buffer too small'),
    (Num: ERROR_NO_DEVICE_HANDLE          ; Msg: 'No device handle'),
    (Num: ERROR_SCM_CANT_CONNECT          ; Msg: 'Can''t connect to Service Control Manager'),
    (Num: ERROR_UNEXPECTED                ; Msg: 'An unexpected error occured')
  );

//-----------------------------------------
function TGWIOPM_Driver.ErrorLookup(ErrorNum: integer): string;
//-----------------------------------------
Var
  S: string;
  N: integer;
label foundit;
Begin
  If Error <> ERROR_SUCCESS then
    S := 'Error: ' + IntToStr(ErrorNum) + ': ';
    
  For N := 1 to ErrorMsgCt do
  Begin
    if ErrorNum = ErrorMsgs[N].Num then
    Begin
      goto foundit;
    end;
  end;
foundit:
  If N > ErrorMsgCt then N := ErrorMsgCt;
  S := S + ErrorMsgs[N].Msg;
  result := S;
end;

//----------------------------------------------------------
// IOCTL codes
//----------------------------------------------------------
function CTL_CODE(DeviceType: integer; func: integer; meth: integer; access: integer): DWORD;
Begin
  result := (DeviceType shl 16) or (Access shl 14) or (func shl 2) or (meth);
end;

Const
  // Buffering method for user-mode app talking to drive
  METHOD_BUFFERED    = 0;
  METHOD_IN_DIRECT   = 1;
  METHOD_OUT_DIRECT  = 2;
  METHOD_NEITHER     = 3;

  // Define the access allowed
  FILE_ANY_ACCESS    = 0;
  FILE_READ_ACCESS   = 1;     // file & pipe
  FILE_WRITE_ACCESS  = 2;     // file & pipe


//-----------------------------------------
constructor TGWIOPM_Driver.Create;
//-----------------------------------------
Begin
  hSCMan  := 0;
  hDevice := INVALID_HANDLE_VALUE;
  HomeDir := ExtractFilePath(ParamStr(0));
  DriverName  := DEVICE_NAME_STRING;
    // default driver name needed by stop/remove if install wasn't executed
    // this run (ie: driver already installed
end;

//-------------------------------------------
function TGWIOPM_Driver.OpenSCM:  DWORD;
//-------------------------------------------
Begin
  result := ERROR_SUCCESS;
  hSCMan := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
  if hSCMan = 0 then result := ERROR_SCM_CANT_CONNECT;
end;

//-------------------------------------------
function TGWIOPM_Driver.CloseSCM:  DWORD;
//-------------------------------------------
Begin
  result := ERROR_SUCCESS;
  CloseServiceHandle(hSCMan);
  hSCMan := 0;
end;

//-----------------------------------------
function TGWIOPM_Driver.Install(newdriverpath: string): DWORD; { use '' for default }
//-----------------------------------------
Var
  hService: SC_HANDLE;
  dwStatus: DWORD;
Begin
  hService := 0;
  dwStatus := 0;

  If newdriverpath = '' then
  Begin
    DriverDir   := HomeDir;
    DriverName  := DEVICE_NAME_STRING;
  end else
  Begin
    DriverDir  := ExtractFilePath(driverpath);
    DriverName := ExtractFileName(driverpath);
  end;
  DriverPath  := DriverDir + DriverName + '.sys';

   // add to service control manager's database
   hService := CreateService(hSCMan, PChar(DriverName),PChar(DriverName),
              SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START,
              SERVICE_ERROR_NORMAL, PChar(DriverPath),
              nil, nil, nil, nil, nil);
   if (hService = 0) then
   Begin
     dwStatus := GetLastError();
   end else
   Begin
     CloseServiceHandle(hService);
   end;

   result := dwStatus;
end;

//-------------------------------------------
function TGWIOPM_Driver.Start:   DWORD;
//-------------------------------------------
Var
  hService: SC_HANDLE;
  dwStatus: DWORD;
  lpServiceArgVectors: PChar;
  temp: LongBool;
Begin
  hService := 0;
  dwStatus := 0;
  lpServiceArgVectors := nil;

   // get a handle to the service
   hService := OpenService(hSCMan, PChar(DriverName), SERVICE_ALL_ACCESS);
   if hService <> 0 then
   Begin
      // start the driver
      temp := StartService(hService, 0, PChar(lpServiceArgVectors));
      if not temp then dwStatus := GetLastError();
   end else dwStatus := GetLastError();

   if (hService <> 0) then CloseServiceHandle(hService);
   result := dwStatus;
end;

//-------------------------------------------
function TGWIOPM_Driver.Stop:    DWORD;
//-------------------------------------------
Var
  hService: SC_HANDLE;
  dwStatus: DWORD;
  serviceStatus: TServiceStatus;
  temp: LongBool;
Begin
  hService := 0;
  dwStatus := 0;

  // get a handle to the service
  hService := OpenService(hSCMan, PChar(DriverName), SERVICE_ALL_ACCESS);
  if hService <> 0 then
  Begin
     // stop the driver
     temp := ControlService(hService, SERVICE_CONTROL_STOP, serviceStatus);
     if not temp then dwStatus := GetLastError();
  end else dwStatus := GetLastError();

  if (hService <> 0) then CloseServiceHandle(hService);
  result := dwStatus;
end;

//-------------------------------------------
function TGWIOPM_Driver.Remove:  DWORD;
//-------------------------------------------
Var
  hService: SC_HANDLE;
  dwStatus: DWORD;
  temp: LongBool;
Begin
  hService := 0;
  dwStatus := 0;

  dwStatus := Stop;  // ignore result
  dwStatus := 0;

  // get a handle to the service
  hService := OpenService(hSCMan, PChar(DriverName), SERVICE_ALL_ACCESS);
  if hService <> 0 then
  Begin
     temp := DeleteService(hService);
     if not temp then dwStatus := GetLastError();
  end else dwStatus := GetLastError();

  if (hService <> 0) then CloseServiceHandle(hService);
  result := dwStatus;
end;

//=============================================================
// Device Open/Close functions
//=============================================================

//-------------------------------------------
function TGWIOPM_Driver.DeviceOpen:  DWORD;
//-------------------------------------------
Var
  dwStatus: DWORD;
Begin
  dwStatus := 0;

  if hDevice <> INVALID_HANDLE_VALUE then DeviceClose;

  // get a handle to the device
  hDevice := CreateFile(
             { lpFileName: PChar            } '\\.\'+ DEVICE_NAME_STRING,
             { dwDesiredAccess: integer     } GENERIC_READ or GENERIC_WRITE,
             { dwShareMode: Integer         } 0,
             { lpSecurityAttributes         } PSECURITY_DESCRIPTOR(nil),
             { dwCreationDisposition: DWORD } OPEN_EXISTING,
             { dwFlagsAndAttributes: DWORD  } FILE_ATTRIBUTE_NORMAL,
             { hTemplateFile: THandle       } 0);

  if hDevice = INVALID_HANDLE_VALUE then
  Begin
    dwStatus := GetLastError();
  end;

  result := dwStatus;
end;

//-------------------------------------------
function TGWIOPM_Driver.DeviceClose:  DWORD;
//-------------------------------------------
Var
  dwStatus: DWORD;
Begin
  dwStatus := 0;
  if (hDevice <> INVALID_HANDLE_VALUE) then CloseHandle(hDevice);
  hDevice := INVALID_HANDLE_VALUE;
  result := dwStatus; { assume that it went OK? }
end;

//=================================================================
// IO Permission Map functions
//=================================================================
Const
  GWIO_PARAMCOUNT     = 3;
  GWIO_BYTES_IN  = GWIO_PARAMCOUNT * 4;
  GWIO_BYTES_OUT = GWIO_PARAMCOUNT * 4;

Type
  TGWIO_PARAMS = array[0..GWIO_PARAMCOUNT-1] of longint;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_Misc1(var RetVal: DWORD; Cmd: integer):  DWORD;
//-------------------------------------------
Var
  dwStatus: DWORD;
  temp: LongBool;
  BytesReturned: DWORD;
  MyControlCode: DWORD;
  InBuf:  TGWIO_PARAMS;
  OutBuf: TGWIO_PARAMS;

Begin
  dwStatus := 0;
  RetVal := 0;
  InBuf[0] := 0;
  InBuf[1] := 0;
  InBuf[2] := 0;

  if hDevice = INVALID_HANDLE_VALUE then
  Begin
    dwStatus := ERROR_NO_DEVICE_HANDLE;
  end else
  Begin
    MyControlCode := CTL_CODE(IOPMD_TYPE, Cmd , METHOD_BUFFERED, FILE_ANY_ACCESS);

    BytesReturned := 0;
    temp := DeviceIoControl(hDevice, MyControlCode ,
            { in buffer  (to driver)   }  @InBuf,  GWIO_BYTES_IN,
            { out buffer (from driver) }  @OutBuf, GWIO_BYTES_OUT,
            BytesReturned, nil);
    if temp then
    Begin
      RetVal := OutBuf[0];
    end else
    Begin
      dwStatus := GetLastError();
    end;
  end;

  result := dwStatus;
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_READ_TEST(var RetVal: DWORD):  DWORD;
//-------------------------------------------
Begin
  result := IOCTL_IOPMD_Misc1(RetVal, IOCMD_IOPMD_READ_TEST);
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_READ_VERSION(var RetVal: DWORD):  DWORD;
//-------------------------------------------
Begin
  result := IOCTL_IOPMD_Misc1(RetVal, IOCMD_IOPMD_READ_VERSION);
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_CLEAR_LIOPM:  DWORD;
//-------------------------------------------
Var
  RetVal: DWORD;
Begin
  result := IOCTL_IOPMD_Misc1(RetVal, IOCMD_IOPMD_CLEAR_LIOPM );
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_GET_SET_LIOPM(Addr: Word; var B: byte; cmd: integer): DWORD;
//-------------------------------------------
Var
  dwStatus: DWORD;
  temp: LongBool;
  BytesReturned: DWORD;
  MyControlCode: DWORD;
  InBuf:  TGWIO_PARAMS;
  OutBuf: TGWIO_PARAMS;

Begin
  dwStatus := 0;

  if hDevice = INVALID_HANDLE_VALUE then
  Begin
    dwStatus := ERROR_NO_DEVICE_HANDLE;
  end else
  Begin
    MyControlCode := CTL_CODE(IOPMD_TYPE, cmd, METHOD_BUFFERED, FILE_ANY_ACCESS);

    InBuf[0] := Addr;
    InBuf[1] := B;

    BytesReturned := 0;
    temp := DeviceIoControl(hDevice, MyControlCode ,
            { in buffer  (to driver)   }  @InBuf,  GWIO_BYTES_IN,
            { out buffer (from driver) }  @OutBuf, GWIO_BYTES_OUT,
            BytesReturned, nil);
    if temp then
    Begin
      B := Lo(OutBuf[1]);
    end else dwStatus := GetLastError();
  end;

  result := dwStatus;
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_SET_LIOPM(Addr: Word; B: byte):  DWORD;
//-------------------------------------------
Var
  RetVal: DWORD;
Begin
  result := IOCTL_IOPMD_GET_SET_LIOPM(Addr, B, IOCMD_IOPMD_SET_LIOPM);
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_GET_LIOPMB(Addr: Word; var B: byte):  DWORD;
//-------------------------------------------
Var
  RetVal: DWORD;
Begin
  result := IOCTL_IOPMD_GET_SET_LIOPM(Addr, B, IOCMD_IOPMD_GET_LIOPMB);
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_GET_LIOPMA(var A: TIOPM): DWORD;
//-------------------------------------------
// get entire LIOPM array (diagnostic)
Var
  dwStatus: DWORD;
  temp: LongBool;
  BytesReturned: DWORD;
  MyControlCode: DWORD;
  InBuf:  TGWIO_PARAMS;
//  OutBuf: TGWIO_PARAMS;

Begin
  dwStatus := 0;

  if hDevice = INVALID_HANDLE_VALUE then
  Begin
    dwStatus := ERROR_NO_DEVICE_HANDLE;
  end else
  Begin
    MyControlCode := CTL_CODE(IOPMD_TYPE, IOCMD_IOPMD_GET_LIOPMA, METHOD_BUFFERED, FILE_ANY_ACCESS);

    InBuf[0] := 0;
    InBuf[1] := 0;
    InBuf[2] := 0;

    BytesReturned := 0;
    temp := DeviceIoControl(hDevice, MyControlCode ,
            { in buffer  (to driver)   }  @InBuf,  GWIO_BYTES_IN,
            { out buffer (from driver) }  @A,      IOPM_SIZE,
            BytesReturned, nil);
    if temp then
    Begin
      // do nothing
    end else dwStatus := GetLastError();
  end;
  result := dwStatus;
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_ACTIVATE_KIOPM:  DWORD;
//-------------------------------------------
Var
  RetVal: DWORD;
Begin
  result := IOCTL_IOPMD_Misc1(RetVal, IOCMD_IOPMD_ACTIVATE_KIOPM );
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_DEACTIVATE_KIOPM:  DWORD;
//-------------------------------------------
Var
  RetVal: DWORD;
Begin
  result := IOCTL_IOPMD_Misc1(RetVal, IOCMD_IOPMD_DEACTIVATE_KIOPM );
end;

//-------------------------------------------
function TGWIOPM_Driver.IOCTL_IOPMD_QUERY_KIOPM:  DWORD;
//-------------------------------------------
Var
  RetVal: DWORD;
Begin
  result := IOCTL_IOPMD_Misc1(RetVal, IOCMD_IOPMD_QUERY_KIOPM);
end;

//-------------------------------------------
function TGWIOPM_Driver.LIOPM_Set_Ports(BeginPort: word; EndPort: word; Enable: Boolean): DWORD;
//-------------------------------------------
Var
  PortNum: word;
  IOPM_Ix, IOPM_BitNum, Last_IOPM_Ix: integer;
  IOPM_Byte, Mask_Byte: byte;
  DriverResult: DWORD;
Label the_end;
Begin
  DriverResult := ERROR_SUCCESS;
  IOPM_Byte    := $FF;

  For PortNum := BeginPort to EndPort do
  Begin
    IOPM_Ix      := PortNum shr 3;  // 8 bits per byte;
    IOPM_BitNum  := PortNum and 7;  // lowest 3 bits;
    Mask_Byte     := 1 shl IOPM_BitNum;

    If (PortNum = BeginPort) or (IOPM_BitNum = 0) then
    Begin  // get IOPM byte
      DriverResult := IOCTL_IOPMD_GET_LIOPMB(IOPM_Ix, IOPM_Byte);
      if DriverResult <> ERROR_SUCCESS then goto the_end;
    end;

    If Enable then
    Begin   // set the bit to 0
      IOPM_Byte := IOPM_Byte and ($FF xor Mask_Byte);
    end else
    Begin   // set the bit to 1
      IOPM_Byte := IOPM_Byte or Mask_Byte;
    end;

    If (PortNum = EndPort) or (IOPM_BitNum = 7) then
    Begin    // Write out IOPM_Byte
      DriverResult := IOCTL_IOPMD_SET_LIOPM(IOPM_Ix, IOPM_Byte);
      if DriverResult <> ERROR_SUCCESS then goto the_end;
    end;
  end;
the_end:
  Result := DriverResult;
end;
{/gwiopm}
{$ENDIF WIN32}

{TPortIO}
function TPortIO.GetPortB(Index: integer): byte;
begin
  Result := 0;
  if (Index < 0) or (Index > MaxPortAddr) then
    raise Exception.Create('Invalid port addr ' + IntToStr(Index));
  PortEnable(Index, 1, true);
  Result := PortIn(Index);
end;

procedure TPortIO.SetPortB(Index: integer; AValue: byte);
begin
  if (Index < 0) or (Index > MaxPortAddr) then
    raise Exception.Create('Invalid port addr ' + IntToStr(Index));
  PortEnable(Index, 1, true);
  PortOut(Index, AValue);
end;

function TPortIO.GetPortW(Index: integer): word;
begin
  Result := 0;
  if (Index < 0) or (Index > MaxPortAddr) then
    raise Exception.Create('Invalid port addr ' + IntToStr(Index));
  PortEnable(Index, 2, true);
  Result := PortInW(Index);
end;

procedure TPortIO.SetPortW(Index: integer; AValue: word);
begin
  if (Index < 0) or (Index > MaxPortAddr) then
    raise Exception.Create('Invalid port addr ' + IntToStr(Index));
  PortEnable(Index, 2, true);
  PortOutW(Index, AValue);
end;

function TPortIO.GetPortL(Index: integer): longint;
begin
  Result := 0;
  if (Index < 0) or (Index > MaxPortAddr) then
    raise Exception.Create('Invalid port addr ' + IntToStr(Index));
  PortEnable(Index, 4, true);
  Result := PortInL(Index);
end;

procedure TPortIO.SetPortL(Index: integer; AValue: longint);
begin
  if (Index < 0) or (Index > MaxPortAddr) then
    raise Exception.Create('Invalid port addr ' + IntToStr(Index));
  PortEnable(Index, 4, true);
  PortOutL(Index, AValue);
end;

procedure TPortIO.PortEnable(Index: integer; Count: integer; OnOff: boolean);
var
  i: integer;
  diffnd: boolean;
begin
  {$IFDEF WIN32}
  if FGWIODriver <> nil then begin
    if Count = 0 then
      Count := 1;
    diffnd := false;
    for i := Index to Index + Count - 1 do begin
      if FPortsEnabled[i] <> OnOff then
        diffnd := true;
    end;
    if diffnd then begin
      if FGWIODriver.LIOPM_Set_Ports(Index, Index + Count - 1, OnOff) = ERROR_SUCCESS then begin
        FGWIODriver.IOCTL_IOPMD_ACTIVATE_KIOPM;
        for i := Index to Index + Count - 1 do
          FPortsEnabled[i] := OnOff
      end else begin
        raise Exception.Create('Failed to enable port ' + IntToStr(Index));
      end;
    end;
  end;
  {$ENDIF WIN32}
end;

{$IFDEF WIN32}
procedure TPortIO.CheckDriverFile;
var
  fn: string;
  f: TFileStream;
begin
  fn := DEVICE_NAME_STRING + '.sys';
  if not FileExists(fn) then begin
    f := TFileStream.Create(fn, fmCreate);
    f.Write(gwiopm, sizeof(gwiopm));
    f.Free;
  end;
end;
{$ENDIF WIN32}

constructor TPortIO.Create(AOwner: TComponent);
var
  r:integer;
  OSVersion:OSVERSIONINFO;
begin
  if PortIO.Port <> nil then
    raise Exception.Create('Only one instance of PortIO can be created');
  inherited Create(AOwner);
  {$IFDEF WIN32}
  OSVersion.dwOSVersionInfoSize := sizeof(OSVersion);
  GetVersionEx(OSVersion);
  FIsNT := OSVersion.dwPlatformId = VER_PLATFORM_WIN32_NT;
  if FIsNT then begin
    CheckDriverFile;
    FGWIODriver := TGWIOPM_Driver.Create;
    r := FGWIODriver.OpenSCM;
    if r <> 0 then
      raise Exception.Create('Failed to open Service Control Manager');
    r := FGWIODriver.Install('');
    if r <> 0 then begin
      if r <> ERROR_SERVICE_EXISTS then
        raise Exception.Create('Failed to install GWIOPM.SYS driver')
    end else
      FDriverInstalled := true;
    r := FGWIODriver.Start;
    if r <> 0 then begin
      if r <> ERROR_SERVICE_ALREADY_RUNNING then
        raise Exception.Create('Failed to start GWIOPM driver')
    end else
      FDriverStarted := true;
    if FGWIODriver.DeviceOpen <> 0 then
      raise Exception.Create('Failed to open GWIOPM driver');
  end;
  {$ENDIF}
  PortIO.Port := Self;
end;

destructor TPortIO.Destroy;
var
  i: integer;
  disCnt:integer;
begin
  PortIO.Port := nil;
  {$IFDEF WIN32}
  if FGWIODriver <> nil then begin
    disCnt := 0;
    for i := 0 to MaxPortAddr do begin
      if FPortsEnabled[i] then begin
        PortEnable(i, 1, false);
        inc(disCnt);
      end;
    end;
    if disCnt > 0 then 
      FGWIODriver.IOCTL_IOPMD_DEACTIVATE_KIOPM;
    FGWIODriver.DeviceClose;
    if FDriverStarted then begin
      FGWIODriver.Stop;
      if FDriverInstalled then
        FGWIODriver.Remove;
    end;
    FGWIODriver.CloseSCM;
    FGWIODriver.Free;
  end;
  {$ENDIF WIN32}
  {FGWIODriver.Free;}
  inherited Destroy;
end;
{/TPortIO}

procedure Register;
begin
  RegisterComponents('NonVis', [TPortIO]);
end;

{$IFDEF AUTOINIT}
initialization
  PortIO.Port := TPortIO.Create(nil);
finalization
  PortIO.Port.Free;
{$ENDIF}
end.
