Sub-pages
$JOY00A

Game Port Overview

The Amiga provides two game ports (DB-9 connectors) on the front or side of the machine, each capable of accepting a mouse, a digital joystick, or analog paddles. All three device types share the same physical connector but use different subsets of the port signals. The input hardware is implemented in the Denise and Paula custom chips, with button inputs routed through CIA-A.

Each game port carries four directional signals, two analog inputs, and a primary button line. The directional signals serve double duty: for a mouse, they carry quadrature-encoded pulses; for a joystick, they carry simple switch closures. Denise contains the counters and logic for both modes, accessible through the JOYxDAT registers.

Port Signal Assignment

The nine pins of each DB-9 connector are allocated as follows:

  • Pins 1–4: Four directional signals (up, down, left, right for joystick; V, VQ, H, HQ for mouse)
  • Pin 5: Analog pot Y (POTY)
  • Pin 6: Fire button (directly active on press)
  • Pin 7: +5V power supply
  • Pin 8: Ground
  • Pin 9: Analog pot X (POTX)
$JOY00A

Mouse Quadrature Signals

The mouse uses an optical encoding system to report movement. Inside the mouse, a rubber-coated ball drives two perpendicular axle shafts. Each shaft has a slotted disc at its end that interrupts a light beam as it rotates. Two photo-sensors per disc, offset by half a slot width, generate a pair of quadrature signals per axis:

  • H and HQ (horizontal pulse and horizontal quadrature pulse)
  • V and VQ (vertical pulse and vertical quadrature pulse)

The phase relationship between the pulse and quadrature pulse signals encodes the direction of movement. When the mouse moves right, H leads HQ; when it moves left, HQ leads H. The same principle applies vertically.

Signal Processing

Denise derives two internal signals from each pulse pair using simple logic:

  • X1 = inverse of HQ
  • X0 = H XOR HQ (equals 1 whenever H and HQ differ)

The truth table for horizontal signals:

H HQ X0 (H XOR HQ) X1 (HQ)
0000
0111
1010
1101

These derived signals drive an internal 8-bit counter that increments for rightward/downward movement and decrements for leftward/upward movement. The counter value represents the current relative position of the mouse. The mouse generates approximately 200 pulses per inch (about 79 per centimeter).

$JOY00A

JOYxDAT Registers

Two read-only registers in Denise hold the mouse/joystick counter values, one per game port:

Mouse / Joystick Data Registers
$DFF00AJOY0DAT — Game port 0 data (read only)
$DFF00CJOY1DAT — Game port 1 data (read only)

Each register packs two 8-bit counters into a single 16-bit word:

Bit 15–8 7–0 Access
FieldY7–Y0X7–X0Read only
Bit Name Description
15–8Y7–Y0Vertical movement counter (Y axis)
7–0X7–X0Horizontal movement counter (X axis)

Counter Overflow Handling

Since each counter is only 8 bits (0–255), it overflows after roughly 3–4 cm of mouse travel. To track absolute position, software must poll the counters frequently and detect overflows. The operating system samples JOYxDAT during the vertical blank interrupt, which guarantees that movement between two samples never exceeds 127 counter steps.

The direction of movement is determined by comparing the current counter value with the previous one:

  • Difference between -127 and +127: No overflow. Positive = right/down, negative = left/up.
  • Difference > +127: Underflow occurred. Actual movement = -(256 - difference).
  • Difference < -127: Overflow occurred. Actual movement = 256 + difference.

JOYTEST — Counter Preset

The counters can be preset by writing to the JOYTEST register. This sets both JOY0DAT and JOY1DAT simultaneously.

Counter Preset Register
$DFF036JOYTEST — Set mouse counter values (write only)
Bit Name Description
15–10Y7–Y2Vertical counter preset (upper 6 bits)
9–8Not used
7–2X7–X2Horizontal counter preset (upper 6 bits)
1–0Not used

Only the upper 6 bits of each counter can be preset. The two lowest bits of each counter come directly from the mouse signal lines and cannot be written by software.

$JOY00A

Joystick Reading

A digital joystick uses simple switch closures that produce the same signals as the mouse quadrature lines. Because the signals are static (not pulsed), Denise's counters settle to fixed values that encode the switch positions. Joystick direction is decoded directly from the JOYxDAT bits:

Direction Condition JOYxDAT Bits
RightX1 = 1Bit 1
LeftY1 = 1Bit 9
Backward (down)X0 XOR X1 = 1Bit 0 XOR Bit 1
Forward (up)Y0 XOR Y1 = 1Bit 8 XOR Bit 9

Left and right are simple single-bit tests. Forward and backward require an XOR operation between adjacent bits. Here is the standard joystick test routine for game port 1:

TestJoystick:
    move.w $DFF00C,d0          ; read JOY1DAT into d0
    btst   #1,d0               ; test bit 1 (X1)
    bne    right               ; if set, joystick is pushed right
    btst   #9,d0               ; test bit 9 (Y1)
    bne    left                ; if set, joystick is pushed left
    move.w d0,d1               ; copy to d1
    lsr.w  #1,d1               ; shift right: X1 aligns with X0, Y1 with Y0
    eor.w  d0,d1               ; XOR: d1.bit0 = X0^X1, d1.bit8 = Y0^Y1
    btst   #0,d1               ; test X0 XOR X1
    bne    backward            ; if set, joystick is pushed backward (down)
    btst   #8,d1               ; test Y0 XOR Y1
    bne    forward             ; if set, joystick is pushed forward (up)
    bra    center              ; joystick is in neutral position

This routine does not handle diagonal positions. To support diagonals, test each axis independently rather than using an if-else chain.

$JOY00A

Paddle / Analog Inputs

Each game port provides two analog inputs (POTX and POTY) for connecting proportional controllers such as paddles or analog joysticks. A paddle contains a variable resistor (potentiometer) whose position determines a resistance value. Analog joysticks work similarly, with one potentiometer per axis.

Analog Measurement Principle

The Amiga uses a capacitor-based timing method to measure resistance. Each of the four analog inputs is connected internally to a capacitor (47 nF) between the input pin and ground. The measurement cycle works as follows:

  1. Paula discharges all four capacitors by briefly pulling the inputs to ground, and resets the counter registers.
  2. The capacitors begin charging through the external potentiometers.
  3. A counter increments once per display line while charging continues.
  4. When the voltage on a capacitor crosses a threshold, the corresponding counter stops.

A low resistance charges the capacitor quickly (low counter value). A high resistance charges slowly (high counter value). The potentiometers must have a maximum resistance of 470 k-ohm (+/-10%).

POTxDAT Registers

Analog Input Registers
$DFF012POT0DAT — Game port 0 analog values (read only)
$DFF014POT1DAT — Game port 1 analog values (read only)
Bit Name Description
15–8Y7–Y0POTY counter value (right paddle / Y axis)
7–0X7–X0POTX counter value (left paddle / X axis)

POTGO / POTGOR — Control and Readback

The POTGO register controls the analog measurement cycle and can also configure the analog lines as general-purpose digital I/O.

Analog Control Registers
$DFF034POTGO — Analog port control (write only)
$DFF016POTGOR (POTINP) — Analog port status (read only)
Bit Name Function
15OUTRYGame port 1 POTY set as output
14DATRYGame port 1 POTY data bit
13OUTRXGame port 1 POTX set as output
12DATRXGame port 1 POTX data bit
11OUTLYGame port 0 POTY set as output
10DATLYGame port 0 POTY data bit
9OUTLXGame port 0 POTX set as output
8DATLXGame port 0 POTX data bit
7–1Unused
0STARTDischarge capacitors and start measurement

Writing to POTGO resets both POTxDAT counters. Normally, the START bit is set to 1 during the vertical blank. During the active display, the capacitors charge and the counters run. By the next vertical blank, the POTxDAT registers contain the final measured values.

Setting an OUTxx bit to 1 switches the corresponding line to digital output mode, disconnecting it from the capacitor measurement circuit. The DATxx bit value in POTGO is then driven on the pin. Reading DATxx from POTGOR always reflects the current logic level of the pin, regardless of mode.

Note: When using the analog ports as digital I/O, the 47 nF capacitors on the lines cause a settling delay of up to 300 microseconds after each signal transition.

$JOY00A

Fire Buttons

Each input device has one or more buttons. The primary fire button (or left mouse button) is active-low and directly connected to CIA-A.

Game Port 0 Buttons

Button Register Bit Active
Left mouse / FireCIA-A PRA ($BFE001)Bit 6Low (0 = pressed)
Right mousePOTGOR ($DFF016)DATLY (bit 10)Low (0 = pressed)
Third mouse buttonPOTGOR ($DFF016)DATLX (bit 8)Low (0 = pressed)
Left paddle buttonJOY0DAT ($DFF00A)Bit 9High (1 = pressed)
Right paddle buttonJOY0DAT ($DFF00A)Bit 1High (1 = pressed)

Game Port 1 Buttons

Button Register Bit Active
Left mouse / FireCIA-A PRA ($BFE001)Bit 7Low (0 = pressed)
Right mousePOTGOR ($DFF016)DATRY (bit 14)Low (0 = pressed)
Third mouse buttonPOTGOR ($DFF016)DATRX (bit 12)Low (0 = pressed)
Left paddle buttonJOY1DAT ($DFF00C)Bit 9High (1 = pressed)
Right paddle buttonJOY1DAT ($DFF00C)Bit 1High (1 = pressed)

Unless otherwise noted, buttons are active-low (0 = pressed). The exceptions are the paddle buttons, which are active-high (1 = pressed). To read the fire button on port 1 (standard joystick port):

    btst #7,$BFE001        ; test CIA-A PRA bit 7
    beq  fire_pressed      ; branch if zero (button pressed)

The right mouse button and third mouse button are read through the POTGOR register. To use them, the corresponding OUTxx bit in POTGO must be cleared (input mode), which is the default state.