docs(input): mouse user guide, and the evidence that no titledb row should ship (#429) (#450)

* docs(input): user guide for the mouse, and why no titledb row ships

PR 4 of 4 of the input-devices track (epic #428), steps 8-9 of the design
spec.  Step 8 was "titledb: JagDoomEX rows gain
{virtualjaguar_p2_device, mouse_st} -- only if step 5 confirmed JagDoomEX
reads the mouse".  It does not, so no row ships, and the reason is
recorded where the next person will look for it.

JagDoomEX does not read port 2.  Verified by disassembly against every
JagDoomEX CRC in titledb (each reproduced by applying the matching IPS
patch to the verified retail dump 0x5E2CDBC0), plus the local
Doom (World) EX.j64 (0xEE7B84EB) and the newest patch archive
(JagDoomEX-ips-250318.7z, which reapplies to the existing 0x35743B9C
row).  Every build:

  - writes only the port-1 row selects $81FE/$81FD/$81FB/$81F7 -- the
    port-2 nibble is $F in all four, so every port-2 row is deselected;
  - ORs #$F0FFFFFC into the read before folding it into an 8-bit
    accumulator, so $F14000 bits 12-15 (the lines a mouse adapter
    drives) cannot reach the result;
  - accounts for all 11 absolute-long $F14000 references as 8 in that
    poll, one move.l #$100 at init and two lea $F14000,a0 in the EEPROM
    driver.  There is no third path to the register.

A row would therefore disconnect the port-2 RetroPad for a title that
reads nothing from port 2, in exchange for no function.  #429's
"auto-select the mouse for JagDoomEX by CRC" checklist item is not
applicable to any released build.

Elansar was the other candidate and it cannot have a row either, for a
structural reason rather than an evidential one: titledb is keyed on
cartridge CRCs and libretro.c deliberately never hashes a disc image, so
no CD title can auto-select a device.  That dissolves the "which wiring
case does Elansar's Atari/Amiga selector mean" question for this PR --
whichever it is, it stays a manual option choice.  Defender of the
Crown, Gridrunner and Star Wars Arcade are absent from the corpus, so
their CRCs are unverifiable and no row is guessed for them.

New docs/input-devices-user-guide.md covers the options, the three
wiring cases and how to tell you picked the wrong one, why raising
sensitivity past the game's poll rate buys lag rather than speed, the
mouse-capable titles, the reproduced-not-broken hardware quirks, and the
known limitation that real-frontend mouse routing is unverified
(RetroArch has historically bound mice to port index 0; #449
deliberately shipped no speculative port-0 fallback).

docs/jaguar-mouse-adapter-mapping.md section 8 F is updated: the
JagDoomEX half of "not attempted" is now done and negative; Elansar is
still not disassembled and item A is still open.

No behaviour change -- the joymatrix identity digests are asserted
unchanged.

Refs #429, #428.  Stacked on #446 and #449.

Co-Authored-By: Claude Opus 5 <[email protected]>

* docs(input): match the guide to what actually merged in #449

The user guide was written against the pre-review mouse snapshot.  Three
of its claims no longer describe the merged code:

- the port-2 pad is not dropped the moment a mouse is selected, only once
  the frontend has proven it routes mouse state (inputdev_live[]);
- a frontend setting JOYPAD/NONE releases its claim back to the core
  option rather than forcing a pad;
- the symptom of the unverified-routing limitation is therefore "mouse
  does nothing, pad still works", not "port 2 is dead".

---------

Co-authored-by: Claude Opus 5 <[email protected]>
This commit is contained in:
Joe MattielloandClaude Opus 5 authored and GitHub committed 2026-08-15 01:48:09 -04:00
1 parent a89dc194c1
commit ecfba369f5
4 files changed
+247 -5

No files matched your search

+2 -1
View File
@@ -34,6 +34,7 @@ This is a libretro core, so the whole frontend feature set applies — and the c
- **Jaguar CD, two ways.** CUE/BIN and CDI images boot through a high-level-emulated CD BIOS *or* through the real CD BIOS driving emulated BUTCH/DSA/FIFO hardware. Audio CDs play and the Virtual Light Machine visualizer works. ([v3.0.0 notes](docs/RELEASE_NOTES_v3.0.0.md) · [v3.1.0 notes](docs/RELEASE_NOTES_v3.1.0.md))
- **Damaged-rip repair.** CDI images from the old V2 ripper that lost each track's leading bytes used to be rejected outright. The loader now classifies the boot track and repairs slides or head-loss at read time; when bytes are provably gone it logs a precise diagnosis instead of a black screen. ([PR #342](https://github.com/libretro/virtualjaguar-libretro/pull/342))
- **JagLink / netlink link-cable networking.** The Jaguar's link-port serial hardware is emulated at the register level with a TCP transport and a multi-peer hub — Doom deathmatch over LAN validated end to end. ([user guide](docs/netlink-user-guide.md) · [design doc](docs/netlink-design.md))
- **ST/Amiga mouse adapter.** The port-2 mouse adapter sold by AtariAge and The Brewing Academy (and its PS/2 variant) is emulated from a sourced pin-level mapping, including the fact that the real adapter is *row-blind* — all three wiring cases ship as selectable devices. Opt-in: with no mouse selected the controller register is bit-identical to before, proven by a test. ([user guide](docs/input-devices-user-guide.md) · [mapping](docs/jaguar-mouse-adapter-mapping.md) · [#429](https://github.com/libretro/virtualjaguar-libretro/issues/429))
- **Jaguar GameDrive (JagGD).** Detection plus full 16 MB bank switching (6×1 MB pages over 16 banks), implemented from RetroHQ's published GDBIOS bindings, so GD-locked homebrew boots. ([interface notes](docs/jgd-interface-notes.md) · v3.1.0 #312, #320)
- **Memory Track saves.** The Jaguar CD's Memory Track cartridge is emulated — a flash window at `$900000` plus an HLE'd NVM BIOS module — so CD titles keep their saves, round-tripping through the frontend's `.srm`. ([`memory-track.md`](docs/memory-track.md) · [PR #259](https://github.com/libretro/virtualjaguar-libretro/pull/259))
- **A crash watchdog that names the bug.** A runtime watchdog recognises hang and crash signatures (GPU/DSP program-counter escape, wedges, video stalls, CD seek wedges) and writes the diagnosis into your frontend log at the moment of failure — so a bug report points at the subsystem, not just "black screen". ([`crash_detect.c`](src/core/crash_detect.c))
@@ -267,7 +268,7 @@ System clock 26.590906 MHz NTSC / 26.593900 MHz PAL. Full source map: [`docs/sou
- **[GitHub Discussions](https://github.com/libretro/virtualjaguar-libretro/discussions)** — questions and compatibility reports. Bring your frontend log.
- **[Issues](https://github.com/libretro/virtualjaguar-libretro/issues)** — confirmed bugs.
- **[Releases](https://github.com/libretro/virtualjaguar-libretro/releases)** — tagged builds for 16 platforms. The rolling **[`nightly` prerelease](https://github.com/libretro/virtualjaguar-libretro/releases/tag/nightly)** is rebuilt from every push to `develop`; it is gated on *compiling*, not on the test suite — treat it as bleeding edge.
- **[`docs/`](docs)** — the full document index: [network play setup](docs/netlink-user-guide.md), [file formats](docs/README), [source layout](docs/source-layout.md), [savestate compatibility](docs/savestate-compat.md), [ROM patches / soft patching](docs/rom-patches.md), [CD read speed](docs/cd-read-speed.md), [CD known issues](docs/cd-known-issues.md), [cartridge triage](docs/cart-issue-triage.md), [profiling](docs/profiling.md), [release process](docs/release-process.md), [changelog](docs/WHATSNEW), [TODO](docs/TODO), and the distilled JTRM hardware references (`docs/jtrm-*.md`).
- **[`docs/`](docs)** — the full document index: [network play setup](docs/netlink-user-guide.md), [input devices / mouse](docs/input-devices-user-guide.md), [file formats](docs/README), [source layout](docs/source-layout.md), [savestate compatibility](docs/savestate-compat.md), [ROM patches / soft patching](docs/rom-patches.md), [CD read speed](docs/cd-read-speed.md), [CD known issues](docs/cd-known-issues.md), [cartridge triage](docs/cart-issue-triage.md), [profiling](docs/profiling.md), [release process](docs/release-process.md), [changelog](docs/WHATSNEW), [TODO](docs/TODO), and the distilled JTRM hardware references (`docs/jtrm-*.md`).
- **[SECURITY.md](SECURITY.md)** — security policy and binary verification.
## Contributors
+176
View File
@@ -0,0 +1,176 @@
# Jaguar Input Devices — User Guide
Virtual Jaguar emulates the **ST/Amiga mouse adapter**, a real port-2
peripheral sold by AtariAge and The Brewing Academy (and, in a PS/2 flavour,
by AtariAge as well). A handful of Jaguar titles read one instead of a joypad.
This guide covers what to turn on, which wiring to pick, and the one
limitation you should know about before filing a bug.
**The mouse is opt-in.** With the options left alone the core behaves exactly
as it did before the feature existed — `$F14000` is bit-identical, proven by
`test/tools/joymatrix_identity`. Nothing here can affect a title you play
with a pad.
## Core options
All three live under **Port 2** in the core options menu.
| Option | Values | Default |
|---|---|---|
| *Port 2 > Controller Type* (`virtualjaguar_p2_device`) | Auto (per-title default), Standard Joypad, Atari ST / PS2 Mouse, Amiga Mouse (ST adapter), Amiga Mouse (Amiga adapter) | Auto |
| *Port 2 > Mouse Sensitivity* (`virtualjaguar_mouse_sensitivity`) | 25% – 400% | 100% |
*Mouse Sensitivity* only appears while a mouse is actually attached to port 2.
**Auto currently means Standard Joypad for every title.** See
[No per-title auto-select](#no-per-title-auto-select-and-why) below — that is
a deliberate result, not an unfinished switch.
Your frontend can also select the device directly (RetroArch: *Controls →
Port 2 → Device Type*), which offers the same four devices. A **mouse** set
that way outranks the core option. Setting the port back to *Joypad* or *None*
releases that claim rather than forcing a pad — the core option decides again,
immediately.
### Once a mouse is live, the port-2 RetroPad is disconnected
That is the hardware being faithful, not a bug. The adapter occupies the port;
there is no pad plugged into it. Port 1 is untouched — a mouse on port 2
cannot perturb port 1's bits, and there is a test asserting exactly that.
**The pad is not dropped until the mouse has actually moved.** Selecting a
mouse must never leave the port with no working input, and whether your
frontend routes host mouse deltas to port 2 is not something the core can
know in advance (see [the limitation below](#known-limitation-frontend-mouse-routing-is-unverified)).
So port 2 keeps its RetroPad until the frontend reports a non-zero delta or a
mouse button; from that moment the pad stays suppressed for the session, or
until you change the device type. Consequence worth knowing: with a mouse
selected but not routed, port 2 behaves exactly like a joypad.
## Which wiring do I pick?
Three real hardware combinations exist, and **they are not compatible with
each other** — the four direction lines carry different axes in each. Picking
the wrong one gives you motion coupled diagonally between the axes, or one
axis inverted, rather than a subtle difference.
| Option value | The hardware it models | Pick it when |
|---|---|---|
| **Atari ST / PS2 Mouse** (`mouse_st`) | An Atari ST mouse (or an ST-compatible trackball) in an ST-wired adapter — the AtariAge and Brewing Academy ST adapters. **The PS/2 adapter presents the same signals**, so use this for a PS/2 mouse too. | Default choice. This is what almost everyone owns. |
| **Amiga Mouse (ST adapter)** (`mouse_amiga`) | An **Amiga** mouse plugged into that same ST-wired adapter. The two mice differ in how their quadrature lines are pinned, so the axes land differently. | A game offers an in-game "Atari / Amiga" mouse-type selector and you set it to Amiga. That selector normally chooses between these first two rows. |
| **Amiga Mouse (Amiga adapter)** (`mouse_amiga_adapter`) | The rarer dedicated Amiga-wired adapter with an Amiga mouse. | You specifically have that adapter. If you are not sure, you do not. |
If motion comes out diagonal — moving the mouse left also moves the cursor up
— you have picked the wrong row above. Try the other two.
The pin-level evidence for all three is in
[`docs/jaguar-mouse-adapter-mapping.md`](jaguar-mouse-adapter-mapping.md)
§4d. Nothing in the table is guessed.
## Sensitivity, and why more is not always faster
The emulated adapter can only emit **one quadrature pulse per controller
poll**, because that is the constraint a real game's decoder imposes: a
two-state jump between consecutive polls is ambiguous and the decoder throws
it away. So the core rate-limits to one state per poll, always.
The practical consequence: raising *Mouse Sensitivity* past what the game's
poll rate can carry buys you **lag, not speed** — the extra motion queues up
and drains at the game's own pace (bounded, so it cannot drift for seconds
after your hand stops). If the pointer feels like it keeps gliding after you
stop, lower the sensitivity rather than raising it.
## Titles that read a mouse
Reported mouse-capable Jaguar titles:
- **Elansar** (CD) — has an in-game mouse-type selector, `*` = Atari,
`#` = Amiga
- **Philia — The Sequel to Elansar** (CD)
- **Defender of the Crown**
- **Gridrunner** (Jeff Minter Classics)
- **Star Wars Arcade** (ST port)
This list comes from issue #429 and community reports; the core has not
verified each of them against a dump. If you have one of these and the mouse
works (or does not), that is useful — please say so on
[#429](https://github.com/libretro/virtualjaguar-libretro/issues/429).
**JagDoomEX does not read a mouse** in any build released so far, despite
being the title that motivated the feature. See below.
## No per-title auto-select, and why
The core has a per-title defaults database (`src/core/titledb.c`, issue #368)
that can turn options on for a specific game by CRC. **No title gets the mouse
selected automatically**, for two separate reasons:
**1. JagDoomEX does not read port 2.** Its CRCs are in the database already
(nine of them), so the row was easy to add — but the ROM was disassembled
first, and every build reads port 1 only. It writes only the port-1 row
selects and then masks away the four bits a mouse adapter drives, so the mouse
could not reach the game even if the option were on. Selecting a mouse for it
would only disconnect the port-2 pad in exchange for nothing. If a future
JagDoomEX release adds mouse support it will hash differently from every CRC
we know, so it would need a fresh row anyway.
**2. The per-title database is cartridge-only.** It is keyed on the CRC of
cartridge content and deliberately never hashes a disc image (a disc CRC would
match nothing it knows while risking a collision with some cartridge's
settings). So **CD titles can never auto-select a device** — which is the
standing answer for Elansar and Philia, the two mouse titles most likely to
prompt the question. Select the mouse by hand for those.
## Known limitation: frontend mouse routing is unverified
The Jaguar mouse adapter is a **port 2** device — that is where the hardware
plugs in, that is where every known title reads it, and so that is the only
port the core offers it on.
RetroArch has historically bound the host mouse to **port index 0**, and we
have not verified on which frontends and versions a mouse selected on port 2
actually delivers its deltas there. The core deliberately ships **no
speculative port-0 fallback**: silently reading port 1's mouse into a port-2
device would be a guess, and a wrong guess would be invisible until it
misbehaved.
If you select a mouse on port 2 and nothing responds to the mouse — while the
port-2 **pad still works normally** — that is the symptom of this limitation
rather than of the emulation. (The pad still working is the deliberate
fallback described above, not a second bug.) Please
report it with your frontend and version on
[#429](https://github.com/libretro/virtualjaguar-libretro/issues/429) — real
frontend data is exactly what is needed to settle it.
## Behaviour that looks wrong but is not
The real adapter is wired straight onto the port's input lines and **cannot
see the row select**. It presents the mouse's state identically in all four
row scans. We reproduce that, so three odd-looking behaviours are faithful:
- while the mouse moves, a title that scans all four rows sees **phantom
keypad digits** (the direction lines are the same wires as `*`/7/4/1,
2/5/8/0, 3/6/9/`#`);
- the **left button reads as A, B, C and Option** at once;
- holding the **right button** perturbs the controller-type field, so a title
that re-probes what is plugged in while you hold it will misidentify the
device.
All three happen on real hardware with a real adapter. They are commented as
such at the code sites so nobody "fixes" them later.
## Not yet implemented
The **Tempest rotary** controller (#436) is a separate device on the same
track and is not covered here. Lightgun (#437), analog/driving controllers
(#438) and per-axis tuning (#439) are also open.
## See also
- [`docs/jaguar-mouse-adapter-mapping.md`](jaguar-mouse-adapter-mapping.md) —
the sourced pin → register-bit mapping this is built from
- [#428](https://github.com/libretro/virtualjaguar-libretro/issues/428) —
input-devices epic
- [#429](https://github.com/libretro/virtualjaguar-libretro/issues/429) —
ST/Amiga mouse
+44 -3
View File
@@ -563,6 +563,15 @@ empirically — drive synthetic quadrature per case and observe which produces c
axis-aligned cursor motion. Expect case 3; a mismatch shows up as motion coupled
diagonally between axes, which is unmistakable.
*Still open.* The 3-entry table shipped (PR #449) but Elansar has not been run against
it. Note also that whichever way it lands, it cannot become a per-title default:
Elansar and Philia are **CD** titles, and `src/core/titledb.c` is keyed on cartridge
CRCs and deliberately never hashes a disc image (`libretro.c`, the
`info->data && !is_cd_content` guard). Elansar's wiring case will always be a manual
option choice, so the answer belongs in the user guide
([`docs/input-devices-user-guide.md`](input-devices-user-guide.md)), not in the
database.
### B. Y-axis sign between case 1 and case 2
The dedicated Amiga adapter (§4b) lands the Amiga's `V` (Y phase A) on bit 15 and `VQ`
@@ -615,9 +624,41 @@ at netlist level (and hence Domin's `$81xx` writes).
- Physical measurement of a real adapter (none on hand).
- Contacting AtariAge / The Brewing Academy for a schematic — worth doing for the PS/2
question if it ever becomes load-bearing.
- Disassembling Elansar or JagDoomEX to read their mouse routine directly. **This is the
highest-value unexplored lead for item A** and needs no external source: the row-select
write and bit tests are visible in the ROM.
- ~~Disassembling Elansar or JagDoomEX to read their mouse routine directly.~~
**JagDoomEX: DONE, and the answer is negative** (see F.1). **Elansar: still not
attempted** — it is a CD title, so its executable has to be pulled out of the disc
image first, and item A remains open until someone does that or runs the game against
each option value.
#### F.1. JagDoomEX does NOT read a mouse — settled by disassembly
Checked against every JagDoomEX CRC carried in `src/core/titledb.c`
(`0x754096DB`, `0x4643E9DB`, `0x35743B9C`, `0xAD6B68BA`, `0xC4F4CACF`, `0x1F4EE4A5`,
`0x013A5359`, `0xB92D1CA3`, `0xEA12E234`), each reproduced by applying the matching IPS
patch to the verified retail dump (`Doom - Evil Unleashed (1994).jag`, `0x5E2CDBC0`),
plus the local `Doom (World) EX.j64` (`0xEE7B84EB`) and the newest patch archive
(`JagDoomEX-ips-250318.7z`, which reapplies to `0x35743B9C`).
Every one of them is **port 1 only**, by two independent mechanisms:
1. The pad poll writes only the port-1 row selects `$81FE`, `$81FD`, `$81FB`, `$81F7`.
The port-2 nibble is `$F` in all four, i.e. **every port-2 row is deselected**.
2. The poll reads `move.l $F14000` and immediately does `or.l #$F0FFFFFC,d0` before
folding into an 8-bit accumulator. That mask clears everything except `$F14000`
bits 8-11 and `$F14002` bits 0-1 — the port-1 columns. `$F14000` bits 12-15, the
four lines a mouse adapter drives, **cannot reach the result**.
And there is no third path: all **11** absolute-long `$F14000` references in the image
are accounted for — 8 in that poll (4 writes + 4 reads), one `move.l #$100,$F14000` at
init, and two `lea $F14000,a0` in the EEPROM driver (which reads bit 0 and `$F15000`).
A driver reading the register through a register-indirect base would still need one of
these to load the address.
Consequence, recorded at the site in `src/core/titledb.c`: **no
`virtualjaguar_p2_device` row ships for JagDoomEX**, and #429's "auto-select the mouse
for JagDoomEX by CRC" checklist item is not applicable to any released build. If a
future release adds mouse support it will hash differently from all nine CRCs above, so
it needs its own row and its own re-check.
---
+25 -1
View File
@@ -119,7 +119,31 @@ static const TitleDBEntry titledb_table[] = {
/* Doom EX romhack family — alias rows inheriting Doom (0x5E2CDBC0)'s
* pairs. CRC = IPS patch applied to the verified retail dump;
* boot-verified via cart_boot_probe (issue #409). */
* boot-verified via cart_boot_probe (issue #409).
*
* NO virtualjaguar_p2_device ROW HERE, and it is not an oversight
* (#429, #428). #429 proposed auto-selecting the ST/Amiga mouse for
* JagDoomEX by CRC; the design spec made that row conditional on
* first confirming the title actually reads port 2. It does not.
* Disassembled against every CRC in this block (plus the 250318
* archive build, which hashes to 0x35743B9C above):
*
* - the pad poll writes only the port-1 row selects $81FE, $81FD,
* $81FB and $81F7. The port-2 nibble is $F in all four, i.e.
* every port-2 row is deselected;
* - the poll then does or.l #$F0FFFFFC before and.l into an 8-bit
* accumulator, so $F14000 bits 12-15 -- the four lines a mouse
* adapter drives -- cannot reach the result;
* - all 11 absolute-long $F14000 references in the image are
* accounted for: 8 in that poll, one move.l #$100 at init, and
* two lea $F14000,a0 in the EEPROM driver (which reads bit 0).
* There is no other path to the register.
*
* A row here would therefore disconnect the port-2 RetroPad for a
* title that reads nothing from port 2, in exchange for no function.
* Do not add one without re-running that check against the build in
* hand -- a future JagDoomEX release may add mouse support, and it
* will hash differently from every CRC listed here anyway. */
{
0x754096DB, "Doom EX (JagDoomEX)",
{