USBJoystick - API reference
===========================

This module lets a modern USB HID joystick/gamepad stand in for four
different RISC OS joystick interfaces. It also provides some new SWIs
(section 5) for new software - to enumerate devices, inspect their axes and
buttons, and control mapping directly, without touching any of the classic
APIs at all.

This note documents all of them: the classic APIs being emulated, the
native SWI API, and the *USBJoystick_ command set.

Three of the four classic APIs - Serial Port, Joy/RTFM, and ADC - can each
be turned on or off independently at run time with Joystick_Emulate
(section 5), or the equivalent *USBJoystick_Emulate command (section 6).
The standard Joystick_Read API cannot be turned off, and never answers "SWI
not known" - see section 1 for why.

A physical joystick only answers a given classic API once it has been
mapped onto it (via the binding table and Joystick_SetStick, section 5).
auto_map() does this automatically with sensible defaults as soon as a
device is recognised, so in practice most joysticks "just work" without
anyone running any mapping commands by hand.

-----------------------------
1. Standard Joystick_Read API
---------------------------------

SWI chunk: &43F40 (this module's own registered chunk).

  Joystick_Read                   &43F40
  Joystick_CalibrateTopRight      &43F41
  Joystick_CalibrateBottomLeft    &43F42

Joystick_CalibrateTopRight and Joystick_CalibrateBottomLeft are accepted
as no-ops: USB devices are unlikely to need this, compared to the original
Acorn design for the PC-compatible 15-pin 'game port', which had analogue
input capability.


Joystick_Read (SWI &43F40)

Reads the position and buttons of one joystick

On entry
  R0 = stick number (bits 0-7) and reason code (bits 8-15)

On exit
  Reason 0 - 8-bit position and buttons:
    R0 bits 0-7   = Y position, -127..127
    R0 bits 8-15  = X position, -127..127
    R0 bits 16-31 = one bit per button

  Reason 1 - 16-bit position and buttons:
    R0 bits 0-15  = Y position, 0..65535
    R0 bits 16-31 = X position, 0..65535
    R1            = one bit per button

Use
  The stick number is the legacy stick number assigned by
  Joystick_SetStick (or *USBJoystick_SetStick). If no stick is mapped to
  that number, reason 0 returns 0 (no position, no buttons), and reason 1
  returns 32768 (centred) for both X and Y with R1 = 0.

  X and Y are mapped independently: if a stick is mapped but one axis is
  not, that axis alone reads its centre value, and the buttons still
  report. This lets a one-axis device (a paddle or throttle) work.

  8-bit and 16-bit values are produced by linearly rescaling the
  joystick's native HID axis range into the standard range (a pre-computed
  slope, not a per-read float calculation), then clamping. A binding's
  Invert flag reverses an axis's sense (see the command reference).

  A HAT switch or D-pad, if present, is exposed as two extra synthetic
  digital axes (values -1/0/1) alongside any real analogue axes, and can be
  mapped onto the 8-bit and 16-bit APIs exactly like a real axis.

  These two reasons are the existing standard API. They are not "legacy" in
  the sense of being superseded: to date they are the only interface an
  actual RISC OS game can use to read a joystick, and that is not changing.

  Unlike the other three classic APIs this one cannot be turned off with
  Joystick_Emulate or *USBJoystick_Emulate.

  The newer SWIs described in section 5 are a complementary, lower-level
  tier (full axis and button access, real device info) for software that
  wants more than two pre-scaled sticks - for example a fancy in-game
  controller setup screen. They do not have to replace Joystick_Read for
  simple use.

Related SWIs
  Joystick_SetStick, Joystick_Status

Related commands
  *USBJoystick_SetStick


2. "Serial Port" joystick emulation
-----------------------------------

Joystick_Status (SWI &43F43)

Reads the digital state of the two legacy sticks

On entry
  -

On exit
  R0 bits 0-4   = stick 1 status (legacy stick number 0)
  R0 bits 8-12  = stick 2 status (legacy stick number 1)

  Each 5-bit status field has this layout:
    bit 0 = Right
    bit 1 = Left
    bit 2 = Down
    bit 3 = Up
    bit 4 = any fire button pressed

Use
  This SWI is reachable two ways: as the named module SWI
  Joystick_Status (&43F43), or via raw SWI number &81540 for older
  software that calls it directly rather than by name. Both do exactly
  the same thing.

  The status is a digital (on/off) view of the mapped X/Y axis. Each axis
  is compared against a dead-zone (an eighth of its range either side of
  centre) to decide whether it currently counts as "pushed" in that
  direction. A binding's Invert flag swaps left/right or up/down as
  appropriate.

  If no stick is mapped to a given legacy number, that status field reads
  as 0 (centred, no buttons).

  This API can be turned on and off with Joystick_Emulate (which = 0).

Related SWIs
  Joystick_Emulate, Joystick_SetStick


3. Teque/Krisalis "Joy" module API
----------------------------------

These SWIs are reachable only by raw SWI number, which is fine for the legacy
compiled code which may use them.

  &CFFC0  Joy_Read0     read legacy stick 0
  &CFFC1  Joy_Read1     read legacy stick 1
  &CFFC2  Joy_Test      is an RTFM-style interface present?
  &CFFC3  Joy_ReadPipe  not implemented (returns without error and does
                        nothing - the original behaviour is unknown)


Joy_Read0 (SWI &CFFC0)
Joy_Read1 (SWI &CFFC1)

Reads the digital state of legacy stick 0 or 1

On entry
  -

On exit
  R0 = %000FUDLR, or &1F if that stick number isn't mapped

  The encoding is the same 5-bit one as Joystick_Status:
    bit 0 = Right
    bit 1 = Left
    bit 2 = Down
    bit 3 = Up
    bit 4 = fire

Use
  Differs from Joystick_Status only in packing (one stick per call here,
  both packed into one word there) and in how "nothing mapped" is
  signalled (&1F here, 0 there).

  This API can be turned on and off with Joystick_Emulate (which = 1).

Related SWIs
  Joy_Test, Joystick_Status, Joystick_Emulate


Joy_Test (SWI &CFFC2)

Tests whether an RTFM-style interface is present

On entry
  -

On exit
  R0 = 255 if neither stick 0 nor stick 1 is mapped ("nothing present")
  R0 = 0   if either is mapped ("RTFM present")

Use
  This module never reports 128 ("Econet present"), since it isn't
  emulating Econet hardware.

Related SWIs
  Joy_Read0, Joy_Read1


Joy_ReadPipe (SWI &CFFC3)

Not implemented

On entry
  -

On exit
  -

Use
  Returns without error and does nothing.


4. I/O podule ADC emulation
---------------------------

The module intercepts the classic OS_Byte calls that BASIC's ADVAL()
function (and any direct OS_Byte caller) uses to talk to an I/O podule's
analogue-to-digital converter.

A HID axis or button only becomes readable through this API once a binding
targets an ADC channel (adc:<n>) or fire button (adcbtn:<n>). Unlike the
other three classic APIs, ADC channels are not auto-mapped, since there is
no way to guess which of up to 4 podule channels an application expects a
given axis on.

This API can be turned on and off with Joystick_Emulate (which = 2).

Pointer-position ADVAL calls (ADVAL(-1), ADVAL(-2)) are NOT implemented
and pass straight through to the real OS.


OS_Byte 16 (SWI &06)

Selects the number of ADC channels to sample

On entry
  R0 = 16
  R1 = number of channels

On exit
  R0 preserved
  R1 preserved

Use
  The module remembers how many channels to sample. Read it back with
  OS_Byte 189.


OS_Byte 17 (SWI &06)

Selects the ADC channel to convert

On entry
  R0 = 17
  R1 = channel number (1-4)

On exit
  R0 preserved
  R1 preserved

Use
  The module remembers which channel is "current". Read it back with
  OS_Byte 188.


OS_Byte 128 (SWI &06)

Reads fire-button status or one ADC channel, as ADVAL()

On entry
  R0 = 128
  R1 = 0 for fire buttons (ADVAL(0)), or 1-4 for a channel (ADVAL(1..4))

On exit
  R0 preserved
  For R1 = 0:
    R1 bits 0-1 = mapped fire buttons
    R2 = 0

  For R1 = 1-4:
    R1 = low 8 bits of the value
    R2 = remaining high bits

Use
  The value range is 0-65520, or snapped to a HAT's min/mid/max if that
  channel is mapped to a HAT-derived axis.


OS_Byte 188 (SWI &06)

Reads the current ADC channel

On entry
  R0 = 188

On exit
  R1 = the channel set by OS_Byte 17


OS_Byte 189 (SWI &06)

Reads the number of ADC channels

On entry
  R0 = 189

On exit
  R1 = the channel count set by OS_Byte 16


OS_Byte 190 (SWI &06)

Reads the ADC conversion resolution

On entry
  R0 = 190

On exit
  R1 = 12 (the resolution is hardcoded to 12 bits)


5. Native slot-based API
------------------------

This is the module's own, new API - the proposed way to enumerate
connected joysticks, inspect their axes and buttons, and control mapping,
without needing any of the classic APIs above turned on.

C programs get the SWI numbers, constants and structures from the exported
header Joystick.h (h.Joystick in this source). It also defines the
standard SWIs, but only if they are not already defined.

Every SWI here addresses a device by its raw slot (0..Joystick_MaxSlots-1,
the order USB attached it in; Joystick_MaxSlots is in h.Joystick). Do not
confuse this with an index, which means an axis or button's position
within one device's own arrays.

A device's axis/button index assignment is a deterministic function of its
HID report descriptor's own byte content. It is stable across replug and
reboot for a given model and firmware. A device with a hardware mode switch
that changes its report descriptor could show a different index assignment
per mode despite an identical vendor/product id.

Every SWI below has one fixed response shape. The one exception,
Joystick_Emulate, uses a selector register to choose which classic API to
toggle, but every value of that selector gets exactly the same
request/response shape back.

Errors: where a SWI says "or an error", V is set and R0 points to the
error block, as usual.


5.1 Device information
----------------------

Joystick_Enumerate (SWI &43F44)

Lists the slots currently in use

On entry
  -

On exit
  R0 = bitmask of in-use slots (bit N set = slot N is in use)
  R1 = generation counter

Use
  The generation counter is bumped on every attach and detach. It lets a
  poller (a Configure plug-in, an SDL backend) cheaply notice that the
  device list has changed, without re-enumerating on every call.

Related SWIs
  Joystick_DeviceInfo


Joystick_DeviceInfo (SWI &43F45)

Returns descriptive information about one device

On entry
  R0 = slot

On exit
  R0 = 0, or an error if the slot isn't a currently open joystick
  R1 = size of the device-info record
  R2 = pointer to the record

Use
  R1 allows for forward compatibility: new fields are only ever added at
  the end of the record.

  The record contains:
    manufacturer string
    product string
    serial string ("" if the device has no real iSerialNumber)
  Any string is "" if the device will not give it: some stall every string
  request (the PlayStation USB adaptor does, and *USBDevInfo shows it blank
  too). In the slot heading of *USBJoystick_List, _Debug and _Read a missing
  string is shown as its USB id, "vendor 0x0b43" or "product 0x0003".
  Strings are read as USBDriver reads them: the language once, then for each
  string its length (2 bytes) and then the whole descriptor, and a request
  that fails is tried up to three times. Reporter shows each attempt, for
  example "attempt 1 of 3 failed: Bad request" then "attempt 2 of 3
  succeeded".
    vendor_id
    product_id
    axis, button and hat counts
    capabilities bitfield
    GUID (16 bytes)
    legacy stick number (see Joystick_SetStick), or Joystick_NotMapped if none

  The capabilities bitfield is:
    bit 0 = rumble
    bit 1 = LED
    bit 2 = battery
    bit 3 = touchpad
    bit 4 = sensors
  Bits 0 and 1 are populated, for devices the driver can rumble and can set
  a player LED on (see doc.Rumble). The others are reserved and 0 until
  future work populates them.

  The GUID is SDL-inspired (bus type + vendor/product/version) but not
  byte-identical to SDL's own encoding - there is no need for an exact 1:1
  fit.

Related SWIs
  Joystick_Enumerate, Joystick_AxisInfo


Joystick_AxisInfo (SWI &43F46)

Returns descriptive information about a device's axes

On entry
  R0 = slot

On exit
  R0 = 0, or an error
  R1 = size of one axis-info record
  R2 = number of axes
  R3 = pointer to an array of records

Use
  Each record contains:
    raw HID usage page and usage
    this module's own semantic type
    name
    min, mid and max
    whether it is a synthesised HAT/D-pad digital axis

  The HID usage page/usage is transport-agnostic HID vocabulary. It is not
  a USB-specific concept, and is reused verbatim by (for example)
  Bluetooth HID, so it stays meaningful if a non-USB transport is ever
  added.

  The semantic type handles multiple axes sharing one HID usage, such as
  dual analogue sticks both being "X".

Related SWIs
  Joystick_AxisValues


Joystick_AxisValues (SWI &43F47)

Reads the current value of every axis and button

On entry
  R0 = slot

On exit
  R0 = 0, or an error
  R1 = size of one value (4 bytes)
  R2 = number of axes
  R3 = pointer to an array of current int32_t values, one per axis
  R4 = full button bitmask

Related SWIs
  Joystick_AxisInfo


5.2 Mapping
-----------

The whole mapping - which classic-read axes and buttons, mouse movement
and buttons, keys and ADC channels each physical axis or button drives -
is one flat binding table per slot.

A binding is a fixed-size "this source drives that target" record, with an
invert flag.
  source: an axis or a button
  target: a stick 8/16-bit axis, a stick button, mouse move X/Y, a mouse
          button, a key, or an ADC channel/fire button

Analogue targets (stick axis, mouse move, ADC channel) take one source
each. Digital targets (stick button, mouse button, key, ADC fire) may be
driven by several.

A button, being an on/off source, may only drive a digital target. A
button->analogue binding is rejected, since the engine records the driving
source as an axis index.

See h.binding for the record layout.


SWI &43F48 is reserved. It was Joystick_GetMapping, which returned a
snapshot of a device's mapping; the legacy stick number is now in
Joystick_DeviceInfo and everything else is in Joystick_ReadBindings.


Joystick_SetStick (SWI &43F49)

Assigns a device to a legacy stick number

On entry
  R0 = slot
  R1 = stick number (0..Joystick_MaxSlots-1)

On exit
  R0 = 0, or an error

Use
  The stick number is used by all three classic read APIs. It is a device
  attribute, not a binding.

Related SWIs
  Joystick_Read, Joystick_Status

Related commands
  *USBJoystick_SetStick


Joystick_ReadBindings (SWI &43F4E)

Reads a slot's binding table

On entry
  R0 = slot

On exit
  R0 = number of bindings
  R1 = size of one binding record (bytes)
  R2 = pointer to the slot's live binding table

Use
  This points at the module's own table. It is valid only until the next
  mapping change.

Related SWIs
  Joystick_WriteBindings, Joystick_AddBinding,
  Joystick_ReadDefaultBindings

Related commands
  *USBJoystick_Mappings


Joystick_WriteBindings (SWI &43F4F)

Replaces a slot's whole binding table

On entry
  R0 = slot
  R1 = pointer to an array of binding records
  R2 = number of records

On exit
  R0 = 0, or an error

Use
  The whole table is validated, then the slot's mapping is replaced with
  it. This is all-or-nothing: a bad record leaves the current mapping
  untouched.

Related SWIs
  Joystick_ReadBindings, Joystick_AddBinding


Joystick_AddBinding (SWI &43F50)

Adds one binding to a slot's table

On entry
  R0 = slot
  R1 = pointer to one binding record

On exit
  R0 = 0, or an error

Use
  The binding is validated against the current table (range checks and the
  singular-target rule) and then appended.

Related SWIs
  Joystick_ReadBindings, Joystick_WriteBindings

Related commands
  *USBJoystick_Map


Joystick_ReadDefaultBindings (SWI &43F51)

Previews the default binding table for a slot

On entry
  R0 = slot

On exit
  R0 = number of bindings
  R1 = size of one binding record (bytes)
  R2 = pointer to a computed (not stored) binding table
  R3 = legacy stick number auto-mapping would assign right now

Use
  This is a read-only preview of what the revert-to-default operation
  (*USBJoystick_RevertToDefaultMap) would produce. Unlike that operation
  it never touches live state, the config store, or any other slot, so a
  caller can use it to reset a not-yet-applied draft mapping and still let
  the user Cancel out of it.

  The returned pointer is valid until the next call to this SWI.

Related SWIs
  Joystick_ReadBindings

Related commands
  *USBJoystick_RevertToDefaultMap


5.3 Config store
----------------

These persist mappings to the config store - see doc.Config.


Joystick_SaveMapping (SWI &43F4B)

Saves a slot's binding table to the config store

On entry
  R0 = slot
  R1 = reserved (profile; must be 0 for now)

On exit
  R0 = 0, or an error

Use
  The saved mapping re-applies when the device next attaches. Only the
  default profile exists today, so R1 is accepted and ignored.

Related SWIs
  Joystick_RevertToDefaultMap, Joystick_ReloadConfig

Related commands
  *USBJoystick_SaveMappings


Joystick_RevertToDefaultMap (SWI &43F4C)

Discards a slot's saved mapping and restores the default

On entry
  R0 = slot

On exit
  R0 = 0, or an error

Use
  The saved section is removed from the config store and the slot is
  re-mapped with the automatically-generated default mapping.

Related SWIs
  Joystick_SaveMapping, Joystick_ReadDefaultBindings

Related commands
  *USBJoystick_RevertToDefaultMap


Joystick_ReloadConfig (SWI &43F4D)

Re-reads the config store

On entry
  -

On exit
  R0 = 0

Use
  Saved mappings are re-applied to all attached devices.

Related SWIs
  Joystick_SaveMapping

Related commands
  *USBJoystick_ReloadConfig


5.4 Global switches and settings
--------------------------------

Joystick_Emulate (SWI &43F4A)

Turns emulation of a classic joystick API on or off

On entry
  R0 = which API:
         0 = SerialPort
         1 = Joy
         2 = ADC
  R1 = -1 to query, 0 for off, 1 for on

On exit
  R0 = resulting current state (0 or 1) for that API

Use
  One SWI covers all three APIs. See h.joyswis for the Joystick_Emulate_*
  selector values. There is no value for the standard API - see section 1 for
  why Joystick_Read can't be turned off.

  When an API's emulation is off, an unrecognised SWI or OS_Byte for it
  passes through as if this module weren't present.

  *USBJoystick_Emulate mirrors this same selector as its first argument,
  rather than having three separate command names.

Related SWIs
  Joystick_Control, Joystick_SaveSettings

Related commands
  *USBJoystick_Emulate


Joystick_Control (SWI &43F52)

Turns the module's native mouse or keyboard output on or off

On entry
  R0 = which output:
         0 = Mouse
         1 = Keyboard
  R1 = -1 to query, 0 for off, 1 for on

On exit
  R0 = resulting current state (0 or 1)

Use
  These are global output gates, independent of the binding table, and OFF
  by default. A device can have mouse and key bindings configured (ready
  to go) without them driving anything until the matching gate here is on.

  Turning a gate off cleanly releases anything it was currently holding
  (see c.mouse, c.keys) rather than leaving it stuck.

  This is deliberately a separate SWI from Joystick_Emulate. Emulate is
  about impersonating a classic pre-USB joystick API for old software;
  this is about the module's own native mouse/keyboard output, which isn't
  emulating any RISC OS API, just directly driving the pointer/keyboard
  the way a real mouse or keyboard does.

  The selector values are Joystick_Control_* (h.joyswis) - a separate enum
  from Joystick_Emulate_* even though the numbers overlap, since the two
  SWIs have independent R0 namespaces.

Related SWIs
  Joystick_Emulate, Joystick_SaveSettings

Related commands
  *USBJoystick_Control


Joystick_SaveSettings (SWI &43F53)

Saves the module-wide settings

On entry
  -

On exit
  R0 = 0, or an error

Use
  Persists the module-wide settings (the Emulate and Control gates above)
  to the config store's [global] section - see doc.Config.

  This touches no device's own section, so it can never clash with
  Joystick_SaveMapping.

Related SWIs
  Joystick_Emulate, Joystick_Control

Related commands
  *USBJoystick_SaveSettings


6. *USBJoystick_ command reference
----------------------------------

The module's own commands, mainly for interactive inspection and editing.
Every command that changes state shares its implementation with the
equivalent SWI in section 5.

Inspection
----------

*USBJoystick_List [<slot>]
    List all connected joysticks, or details for just one, including what
    can be sent to each (Rumble, ...).

*USBJoystick_Read <slot>
    Show one joystick's current live axis values, buttons, and invert state.

*USBJoystick_Mappings <slot>
    List a slot's binding table - each "source -> target" pair, marked
    (inverted) where set.

*USBJoystick_Debug [<slot>]
    Detailed internal diagnostics: USB path, buffer/file handles, raw
    report bytes, upcall and good/bad-read counters (reading resets the
    "delta" counters), per-axis scaling detail, and what the driver table
    and descriptors gave the slot (output transport, OUT endpoint, report
    id, data offset, validator, start-up routine).

*USBJoystick_Fingerprint <slot>
    Print the config-store fingerprint key for one slot.

Mapping
-------

*USBJoystick_SetStick <slot> <stick number>
    Assign a device to legacy stick number 0..Joystick_MaxSlots-1, used by all three
    classic read APIs. A device attribute, not a binding.

*USBJoystick_Map <slot> <source> <target> [Invert]
    Add one binding (a physical input driving an output).

      source: axis:<name|n> | btn:<n>
      target: stick:{8bitX,8bitY,16bitX,16bitY} | stickbtn:<n> |
              mouse:{X,Y,S,M,A} | key:<keyno> | adc:<n> | adcbtn:<n>

    "stick" is the classic Joystick_Read output (the "Stick N" screen).
    Mouse buttons S/M/A are Select/Menu/Adjust. Keycodes are numeric RISC
    OS internal key numbers. Invert reverses direction.

    A device drives the mouse once it has mouse-move bindings. ADC
    channels and fire buttons feed the ADC API.

*USBJoystick_Unmap <slot> [<source> [<target>]]
    No source: clear the whole device.
    Source only: clear all of that source's bindings.
    Source and target: clear just that one binding.

Config store
------------

*USBJoystick_SaveMappings <slot>
    Save the slot's binding table to the config store, so it re-applies
    when the device next attaches.

*USBJoystick_RevertToDefaultMap <slot>
    Discard the slot's saved mapping and return it to the default,
    automatically-generated mapping.

*USBJoystick_ReloadConfig
    Re-read the config store and apply saved mappings to all attached
    devices.

*USBJoystick_SaveSettings
    Save the module-wide settings (Emulate and Control) to the config
    store, independent of any device's own saved mapping - see
    Joystick_SaveSettings in section 5.

Emulation toggles
-----------------

*USBJoystick_Emulate <SerialPort|Joy|ADC> [<on|off>]
    Turn emulation of one classic API on/off globally (an unrecognised
    SWI/OSBYTE passes through as if this module weren't present when its
    emulation is off). Mirrors Joystick_Emulate's R0 selector 1:1. With no
    on/off argument, reports that API's current state.

    There is no equivalent for the standard API - Joystick_Read is always
    active, see section 1.

Output control toggles
----------------------

*USBJoystick_Control <Mouse|Keyboard> [<on|off>]
    Turn a joystick's native mouse or keyboard output on/off globally,
    independent of any device's bindings. Mirrors Joystick_Control's R0
    selector 1:1. Off by default. With no on/off argument, reports the
    current state.

    Not the same thing as Emulate above: this doesn't impersonate a
    classic API, it gates the module's own direct pointer/keyboard output.

Device output
-------------

*USBJoystick_Rumble <slot> <low> <high> [<ms>]
    Run a joystick's rumble motors. low and high are 0..65535 strengths for
    the low- and high-frequency motors; both 0 stops them. ms (1..65535,
    default 1000) is how long to run before the module stops them again.
    See doc.Rumble.

*USBJoystick_SendOut <slot> <byte> [<byte>...]
    Send raw bytes (hexadecimal by default, up to 64) to a joystick's OUT
    endpoint. For finding out what a device that isn't supported yet needs.

*USBJoystick_SendReport <slot> <report-id> [<byte>...]
    Send a HID output report over the control pipe, for devices with no OUT
    endpoint. report-id 0 = the device uses no report ids.
