Amiga® RKM Devices: 5 Gameport Device

The gameport device manages access to the Amiga gameport connectors for
the operating system.  It enables the Amiga to interface with various
external pointing devices like mice (two and three button), joysticks,
trackballs and light pens.  There are two units in the gameport device,
unit 0 and unit 1.

                       AMIGA GAMEPORT CONNECTORS

                Model       Unit 0              Unit 1
                -----   ---------------     --------------
                A3000   Front Connector     Back Connector
                A2000   Left Connector      Right Connector
                A1000   1                   2
                A500    1 JOYSTICK          2 JOYSTICK

 Gameport Device Commands and Functions 
 Device Interface 
 Gameport Events 
 Setting and Reading the Controller Type 
 Joystick Example Program 
 Additional Information on the Gameport Device 


5 Gameport Device / Gameport Device Commands and Functions

Command         Operation
-------         ---------
CMD_CLEAR       Clear the gameport input buffer.

GPD_ASKCTYPE    Return the type of gameport controller being used.

GPD_ASKTRIGGER  Return the conditions that have been preset for triggering.

GPD_READEVENT   Read one or more gameport events.

GPD_SETCTYPE    Set the type of the controller to be used.

GPD_SETTRIGGER  Preset the conditions that will trigger a gameport event.


Exec Functions as Used in This Chapter
--------------------------------------
AbortIO()       Abort a command to the gameport device.

CheckIO()       Return the status of an I/O request.

CloseDevice()   Relinquish use of the gameport device.  All requests must
                be complete before closing.

DoIO()          Initiate a command and wait for completion (synchronous
                request).

OpenDevice()    Obtain shared use of one unit of the gameport device. The
                unit number specified is placed in the I/O request
                structure for use by gameport commands.

SendIO()        Initiate a command and return immediately (asynchronous
                request).

WaitIO()        Wait for the completion of an asynchronous request.  When
                the request is complete the message will be removed from
                reply port.


Exec Support Functions as Used in This Chapter
----------------------------------------------
CreateExtIO()   Create an extended I/O request structure of type IOStdReq.
                This structure will be used to communicate commands to the
                gameport device.

CreatePort()    Create a signal message port for reply messages from the
                gameport device.  Exec will signal a task when a message
                arrives at the port.

DeleteExtIO()   Delete an I/O request structure created by CreateExtIO().

DeletePort()    Delete the message port created by CreatePort().

   Who Runs The Mouse?
   -------------------
   When the input device or Intution is operating, unit 0 is usually
   dedicated to gathering mouse events.  The input device uses the
   gameport device to read the mouse events.  (For applications that
   take over the machine without starting up the input device or
   Intuition, unit 0 can perform the same functions as unit 1.)  See the
   "Input Device" chapter for more information on the input device.


5 Gameport Device / Device Interface

The gameport device operates like the other Amiga devices.  To use it, you
must first open the gameport device, then send I/O requests to it, and
then close it when finished. See the "Introduction to Amiga System Devices"
chapter for general information on device usage.

The I/O request used by the gameport device is called IOStdReq.

    struct IOStdReq
    {
        struct  Message io_Message;
        struct  Device  *io_Device; /* device node pointer  */
        struct  Unit    *io_Unit; /* unit (driver private)*/
        UWORD   io_Command;       /* device command */
        UBYTE   io_Flags;
        BYTE    io_Error;         /* error or warning num */
        ULONG   io_Actual;        /* actual number of bytes transferred */
        ULONG   io_Length;        /* requested number bytes transferred*/
        APTR    io_Data;          /* points to data area */
        ULONG   io_Offset;        /* offset for block structured devices */
    };

See the include file exec/io.h for the complete structure definition.

 Opening The Gameport Device 
 Gameport Device Controllers 
 Closing The Gameport Device 


5 / Device Interface / Opening The Gameport Device

Three primary steps are required to open the gameport device:

   *  Create a message port using CreatePort(). Reply messages from the
      device must be directed to a message port.

   *  Create an I/O request structure of type IOStdReq. The IOStdReq
      structure is created by the CreateExtIO() function. CreateExtIO()
      will initialize the I/O request with your reply port.

   *  Open the gameport device.  Call OpenDevice(), passing the I/O request
      and and indicating the unit you wish to use.

   struct MsgPort *GameMP;   /* Message port pointer */
   struct IOStdReq *GameIO;  /* I/O request pointer */

     /* Create port for gameport device communications */
   if (!(GameMP = CreatePort("RKM_game_port",0)))
       cleanexit(" Error: Can't create port\n",RETURN_FAIL);

     /* Create message block for device I/O */
   if (!(GameIO = CreateExtIO(GameMP,sizeof(struct IOStdReq))))
       cleanexit(" Error: Can't create I/O request\n",RETURN_FAIL);

     /* Open the right/back (unit 1, number 2) gameport.device unit */
   if (error=OpenDevice("gameport.device",1,GameIO,0))
       cleanexit(" Error: Can't open gameport.device\n",RETURN_FAIL);

The gameport commands are unit specific.  The unit number specified in the
call to OpenDevice() determines which unit is acted upon.


5 / Device Interface / Gameport Device Controllers

The Amiga has five gameport device controller types.

                 GAMEPORT DEVICE CONTROLLERS

         Controller Type         Description
         ---------------         -----------
         GPCT_MOUSE              Mouse controller
         GPCT_ABSJOYSTICK        Absolute (digital) joystick
         GPCT_RELJOYSTICK        Relative (digital) joystick
         GPCT_ALLOCATED          Custom controller
         GPCT_NOCONTROLLER       No controller

To use the gameport device, you must define the type of device connected
to the gameport and define how the device is to respond. The gameport
device can be set up to return the controller status immediately or only
when certain conditions have been met.

When a gameport device unit reponds to a request for input, it creates an
input event. The contents of the input event will vary based on the type
of device and the trigger conditions you have declared.

   *  A mouse controller can report input events for one, two, or three
      buttons and for positive or negative (x,y) movements. A trackball
      controller or car-driving controller is generally of the same type
      and can be declared as a mouse controller.

   *  An absolute joystick reports one single event for each change of its
      current location. If, for example, the joystick is centered and the
      user pushes the stick forward and holds it in that position, only one
      single forward-switch event will be generated.

   *  A relative joystick, on the other hand, is comparable to an absolute
      joystick with "autorepeat" installed. As long as the user holds the
      stick in a position other than centered, the gameport device
      continues to generate position reports.

   *  There is currently no system software support for proportional
      joysticks or proportional controllers (e.g., paddles). If you write
      custom code to read proportional controllers or other controllers
      (e.g., light pen) make certain that you issue GPD_SETCTYPE (explained
      below) with controller type GPCT_ALLOCATED to insure that other
      applications know the connector is being used.

   GPCT_NOCONTROLLER.
   ------------------
   The controller type GPCT_NOCONTROLLER is not a controller at all, but
   a flag to indicate that the unit is not being used at the present
   time.


5 / Device Interface / Closing The Gameport Device

Each OpenDevice() must eventually be matched by a call to CloseDevice().

All I/O requests must be complete before CloseDevice().  If any requests
are still pending, abort them with AbortIO() and remove them with WaitIO().

    if (!(CheckIO(GameIO)))
        {
        AbortIO(GameIO);  /* Ask device to abort request, if pending */
        }
    WaitIO((GameIO);   /* Wait for abort, then clean up */
    CloseDevice(GameIO);


5 Gameport Device / Gameport Events

A gameport event is an InputEvent structure which describes the following:

   * The class of the event - always set to IECLASS_RAWMOUSE for the
     gameport device.

   * The subclass of the event - 0 for the left port; 1 for the right port.

   * The code - which button and its state.  (No report = 0xFF)

   * The qualifier - only button and relative mouse bits are set.

   * The position - either a data address or mouse position count.

   * The time stamp - delta time since last report, returned as frame
     count in tv_secs field.

   * The next event - pointer to next event.


    struct InputEvent GameEV
    {
        struct InputEvent *ie_NextEvent;  /* next event */
        UBYTE    ie_Class;                /* input event class */
        UBYTE    ie_SubClass;             /* subclass of the class */
        UWORD    ie_Code;                 /* input event code */
        UWORD    ie_Qualifier;            /* event qualifiers in effect */
           union
           {
             struct
             {
              WORD   ie_x;                /* x position for the event */
              WORD   ie_y;                /* y position for the event */
             }  ie_xy;
             APTR ie_addr;
           } ie_position;
        struct timeval ie_TimeStamp;      /* delta time since last report
    }

See the include file devices/inputevent.h for the complete structure
definition and listing of input event fields.

 Reading Gameport Events 
 Setting Gameport Event Trigger Conditions 
 Determining The Trigger Conditions 


5 / Gameport Events / Reading Gameport Events

You read gameport events by passing an I/O request to the device with
GPD_READEVENT set in io_Command, the address of the InputEvent structure
to store events set in io_Data and the size of the structure set in
io_Length.

   struct InputEvent  GameEV;
   struct IOStdRequest *GameIO;  /* Must be initialized prior to using */

   void send_read_request()
   {
   GameIO->io_Command = GPD_READEVENT;  /* Read events */
   GameIO->io_Length = sizeof (struct InputEvent);
   GameIO->io_Data = (APTR)&GameEV;     /* put events in GameEV*/
   SendIO(GameIO);                      /* Asynchronous */
   }


5 / Gameport Events / Setting Gameport Event Trigger Conditions

You set the conditions that can trigger a gameport event by passing an I/O
request to the device with GPD_SETTRIGGER set in io_Command and the
address of a GamePortTrigger structure set in io_Data.

The information needed for gameport trigger setting is placed into a
GamePortTrigger data structure which is defined in the include file
devices/gameport.h.

    struct GamePortTrigger
    {
        UWORD    gpt_Keys;      /* key transition triggers */
        UWORD    gpt_Timeout;   /* time trigger (vertical blank units) */
        UWORD    gpt_XDelta;    /* X distance trigger */
        UWORD    gpt_YDelta;    /* Y distance trigger */
    }

A few points to keep in mind with the GPD_SETTRIGGER command are:

    *  Setting GPTF_UPKEYS enables the reporting of upward transitions.
       Setting GPTF_DOWNKEYS enables the reporting of downward transitions.
       These flags may both be specified.

    *  The field gpt_Timeout specifies the time interval (in vertical blank
       units) between reports in the absence of another trigger condition.
       In other words, an event is generated every gpt_Timeout ticks.
       Vertical blank units may differ from country to country (e.g 60 Hz
       NTSC, 50 Hz PAL.)  To find out the exact frequency use this code
       fragment:

       #include 
       extern struct ExecBase *SysBase;

       UBYTE get_frequency(void)
       {
       return((UBYTE)SysBase->VBlankFrequency);
       }

    *  The gpt_XDelta and gpt_YDelta fields specify the x and y distances
       which, if exceeded, trigger a report.

For a mouse controller, you can trigger on a certain minimum-sized move in
either the x or y direction, on up or down transitions of the mouse
buttons, on a timed basis, or any combination of these conditions.

For example, suppose you normally signal mouse events if the mouse moves
at least 10 counts in either the x or y directions. If you are moving the
cursor to keep up with mouse movements and the user moves the mouse less
than 10 counts, after a period of time you will want to update the
position of the cursor to exactly match the mouse position. Thus the timed
report of current mouse counts would be preferred. The following structure
would be used:

    #define XMOVE 10
    #define YMOVE 10

    struct GamePortTrigger GameTR =
    {
        GPTF_UPKEYS | GPTF_DOWNKEYS, /* trigger on all key transitions */
        1800,                  /* and every 36(PAL) or 30(NTSC) seconds */
        XMOVE,                 /* for any 10 in an x or y direction */
        YMOVE
    };

For a joystick controller, you can select timed reports as well as
button-up and button-down report trigger conditions. For an absolute
joystick specify a value of one (1) for the GameTR_XDelta and
GameTR_YDelta fields or you will not get any direction events. You set the
trigger conditions by using the following code or its equivalent:

    struct IOStdReq *GameIO;

    void set_trigger_conditions(struct GamePortTrigger *GameTR)
    {
    GameIO->io_Command = GPD_SETTRIGGER;    /* set trigger conditions */
    GameIO->io_Data = (APTR)GameTR;         /* from GameTR */
    GameIO->io_Length = sizeof(struct GamePortTrigger);
    DoIO(GameIO);
    }

   Triggers and Reads.
   -------------------
   If a task sets trigger conditions and does not ask for the position
   reports the gameport device will queue them up anyway. If the trigger
   conditions occur again and the gameport device buffer is filled, the
   additional triggers will be ignored until the buffer is read by a
   device read request (GPD_READEVENT) or a system CMD_CLEAR command
   flushes the buffer.


5 / Gameport Events / Determining The Trigger Conditions

You determine the conditions required for triggering gameport events by
passing an I/O request to the device with GPD_ASKTRIGGER set in
io_Command, the length of the GamePortTrigger structure set in io_Length
and the address of the structure set in io_Data.  The gameport device will
respond with the event trigger conditions currently set.

    struct IOStdReq *GameIO;  /* Must be initialized prior to using */

    struct GamePortTrigger GameTR;

    void get_trigger_conditions(struct GamePortTrigger *GameTR)
    {
    GameIO->io_Command = GPD_ASKTRIGGER;    /* get type of triggers */
    GameIO->io_Data = (APTR)GameTR;         /* place data here */
    GameIO->io_Length= sizeof(GameTR);
    DoIO(GameIO);
    }


5 Gameport Device / Setting and Reading the Controller Type


 Determining The Controller Type 
 Setting The Controller Type 


5 / Setting and Reading Controller Type / Determining The Controller Type

You determine the type of controller being used by passing an I/O request
to the device with GPD_ASKCTYPE set in io_Command, 1 set in io_Length and
the number of the unit set in io_Unit. The gameport device will respond
with the type of controller being used.

   struct IOStdReq *GameIO;  /* Must be initialized prior to using */

   BYTE GetControllerType()
   {
   BYTE controller_type = 0;

   GameIO->io_Command = GPD_ASKCTYPE;         /* get type of controller */
   GameIO->io_Data = (APTR)&controller_type;  /* place data here */
   GameIO->io_Length = 1;
   DoIO(GameIO);
   return (controller_type);
   }

The BYTE value returned corresponds to one of the five controller types
noted above.


5 / Setting and Reading the Controller Type / Setting The Controller Type

You set the type of gameport controller by passing an I/O request to the
device with GPD_SETCTYPE set in io_Command, 1 set in io_Length and the
address of the byte variable describing the controller type set in io_Data.

The gameport device is a shared device; many tasks may have it open at any
given time. Hence, a high level protocol has been established to prevent
multiple tasks from reading the same unit at the same time.

 Three Step Protocol for Using the Gameport Device 


5 / Setting Controller Type /Three Step Protocol for Using Gameport Device

Step 1:
    Send GPD_ASKCTYPE to the device and check for a GPCT_NOCONTROLLER
    return. Never issue GPD_SETCTYPE without checking whether the desired
    gameport unit is in use.

Step 2:
    If GPCT_NOCONTROLLER is returned, you have access to the gameport.
    Set the allocation flag to GPCT_MOUSE, GPCT_ABSJOYSTICK or
    GPCT_RELJOYSTICK if you use a system supported controller, or
    GPCT_ALLOCATED if you use a custom controller.

    struct IOStdReq *GameIO;  /* Must be initialized prior to using */

    BOOL set_controller_type(type)
    BYTE type;
    {

    BOOL success = FALSE;
    BYTE controller_type = 0;

    Forbid();                           /*critical section start */
    GameIO->io_Command = GPD_ASKCTYPE;  /* inquire current status */
    GameIO->io_Length = 1;
    GameIO->io_Flags = IOF_QUICK;
    GameIO->io_Data = (APTR)&controller_type; /* put answer in here */
    DoIO(GameIO);

    /* No one is using this device unit, let's claim it */
    if (controller_type == GPCT_NOCONTROLLER)
        {
        GameIO->io_Command = GPD_SETCTYPE;/* set controller type */
        GameIO->io_Length = 1;
        GameIO->io_Data = (APTR)&type;  /* set to input param */
        DoIO( GameIO);
        success = TRUE;
        UnitOpened = TRUE;
        }
    Permit(); /* critical section end */

    /* success can be TRUE or FALSE, see above */
    return(success);
    }

Step 3:
    The program must set the controller type back to GPCT_NOCONTROLLER
    upon exiting your program:

    struct IOStdReq *GameIO;  /* Must be initialized prior to using */

    void free_gp_unit()
    {
    BYTE type = GPCT_NOCONTROLLER;
    GameIO->io_Command = GPD_SETCTYPE;  /* set controller type */
    GameIO->io_Length = 1;
    GameIO->io_Data = (APTR)&type;      /* set to unused */
    DoIO( GameIO);
    }

This three step protocol allows applications to share the gameport device
in a system compatible way.

    A Word About The Functions.
    ---------------------------
    The functions shown above are designed to be included in any
    application using the gameport device.  The first function,
    set_controller_type(), would be the first thing done after opening
    the gameport device. The second function, free_gp_unit(), would be
    the last thing done before closing the device.


5 Gameport Device / Additional Information on the Gameport Device

Additional programming information on the gameport device can be found in
the include files and the Autodocs for the gameport and input devices.
Both are contained in the Amiga ROM Kernel Reference Manual: Includes and
Autodocs.

                       Gameport Device Information
                   -----------------------------------
                   INCLUDES       devices/gameport.h
                                  devices/gameport.i
                                  devices/inputevent.h
                                  devices/inputevent.i

                   AUTODOCS       gameport.doc


Converted on 22 Apr 2000 with RexxDoesAmigaGuide2HTML 2.1 by Michael Ranner.