Game port

From OSDev.wiki
(Redirected from Joystick)
Jump to navigation Jump to search

Game port is an old and obsolete parallel communication port. Used mostly for analog joysticks, but also for another devices (like MIDI devices or paddles). Game port uses an DB-15 port. It has eight analog connections for four buttons and four for the axis.

History

The port was introduced in the IBM Game Control Adapter, an optional add-on board for the original IBM PC. Creative Labs included the game port in their soundcards (SoundBlaster) to provide an "all-in-one" solution for gamers, because sound cards were mostly used for gaming. Since then it was very popular among gamers. Joysticks aren't limited to four buttons as you may think. Some joysticks (like Microsoft Sidewinder with nine buttons) used multiplexation techniques to get up to 15 buttons or even 20.

However, there's a serious drawback; old analog joysticks (that use the game port) use an potentiometer (variable resistor) with a resistance of usually 100kΩ and a capacitor. The controller board calculates the charge threshold of the capacitor, and then converts it to numbers (axis). The problem is that the joystick resistor starts to heat up with time (10-15 mins.), and the resistance changes, leading to incorrect values. Analog joysticks need constant calibration.


Programming the game port

Game port uses port 201h. It's a read and write port. Nothing much to say, it is very simple. Any axis read must be preceded by an write, it can be any data, it doesn't matter.

IO Port Access Type Purpose
0x201 Read/Write Joystick status
Bits Purpose
0 X joystick axis (A). If bit is set, timing isn't active. Same for all.
1 Y joystick axis (A)
2 X joystick axis (B)
3 Y joystick axis (B)
4 Status of (A) button 1. If bit is not set, button has been pressed. Same for all.
5 Status of (A) button 2
6 Status of (B) button 1
7 Status of (B) button 2

There's also and BIOS call for joystick; Int 0x15, AH 0x84. But it is poorly supported, and most BIOSes have an buggy implementation.


Example code

Example code, to get button status. Uses polling to detect status. You can also use IRQ 9-11, they are free for peripherals, but it is a waste for such a simple device.

WARNING! this code has not been tested!

/* Simple joystick port controller. hextakatt 2019, public domain. Improvements are welcome, send me an PM (in the forum) if you have any. */
#include <stdint.h>
#include <stdbool.h>

/* Joystick 200h - 201h. */
#define JOYSTICK_PORT      0x201
#define GARBAGE_DATA       0xAA55
#define MAX_TIME_ATTEMPTS  1000

/* Joystick values: axis and buttons */
enum JoystickValues
{
    JoystickButtonA = 0x10,
    JoystickButtonB = 0x20,
    JoystickButtonC = 0x40,
    JoystickButtonD = 0x80,
    JoystickAxisX   = 0x01,
    JoystickAxisY   = 0x02,
    JoystickDeltaX  = 0x04,
    JoystickDeltaY  = 0x08,
};

/* A nice struct which contains the joystick status */
struct JoystickStatus
{
    int16_t AxisX;
    int16_t AxisY;
    int16_t DeltaX;
    int16_t DeltaY;
    bool ButtonA;
    bool ButtonB;
    bool ButtonC;
    bool ButtonD;
    bool JoystickFlag;
};

struct JoystickStatus *jst;
/* If joystick is present, flag is true. */
jst->JoystickFlag = false;

uint8_t joystick_values[] = {
    0x01, 0x02, 0x04, 0x08, /* Axis */
    0x10, 0x20, 0x40, 0x80, /* Buttons */
}

bool joystick_button(uint8_t buttonnum)
{
    for (int i = 4; i <= 8; ++i) { 
        if (joystick_values[i] == buttonnum) {
 	           return ((inb(JOYSTICK_PORT) & buttonnum) == 0);
        }
    }
    kputs("Invalid value: %x; Out of bounds.\n", buttonnum);
    return 0;
}

/* Dump all joystick values to an structure */
struct JoystickStatus joystick_dump(void)
{
    struct JoystickStatus jst;
    jst.AxisX   = joystick_status(JoystickAxisX);
    jst.AxisY   = joystick_status(JoystickAxisY);
    jst.DeltaX  = joystick_status(JoystickDeltaX);
    jst.DeltaY  = joystick_status(JoystickDeltaY);
    jst.ButtonA = joystick_button(joystick_status(JoystickButtonA));
    jst.ButtonB = joystick_button(joystick_status(JoystickButtonB));
    jst.ButtonC = joystick_button(joystick_status(JoystickButtonC));
    jst.ButtonD = joystick_button(joystick_status(JoystickButtonD));
    return jst;
}

/* 
 * There's no IRQ for joystick port, so we poll instead.
 * However isn't a problem, because very few data is transmitted
 * through game port.
 */
uint16_t joystick_status(uint8_t byte)
{
    int timeout = 0;
    /* Disable interrupts, so they don't affect timing */
    asm volatile("cli");

    uint16_t stat = inb(JOYSTICK_PORT);
    /* Any read needs to be with a write. The byte that we send 
     * can be garbage, it doesn't matters.
     */
    outb(JOYSTICK_PORT, GARBAGE_DATA);

    /* Poll until we get our requested value */
    while (1) {
        if (stat & byte) {
            jst->JoystickFlag = true;
            break;
        }
      	 else if (timeout == MAX_TIME_ATTEMPTS) {
            jst->JoystickFlag = false;
            return 0;
    	}
        ++timeout;
    }
    /* Now we can enable interrupts again */
    asm volatile("sti");
    return (stat & byte);
}

See Also

External Links

Linux gameport implementation

Nondot OSRC joystick article

IBM Game Control Adapter manual