Import RIO board hardware docs from RIOjoy repo (history preserved)

Board photos, scanned schematics, and GAL fuse-map decode of the
physical RIO board set.

git-subtree-dir: restoration/rio-hardware
git-subtree-mainline: def5840e07
git-subtree-split: 42b3944ebe
This commit is contained in:
Cyd
2026-07-19 20:45:18 -05:00
20 changed files with 541 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
# RIO cockpit hardware — boards, schematics, GAL dump
Physical documentation of the VWE Tesla-cockpit **RIO** (Remote Input/Output)
board set, gathered during the 2026 restoration: board photos (Signal,
2026-07-15), a scan of the original paper wire schematics, and the fuse map
read out of the RIO board's GAL address decoder.
All boards are **Copyright 1994 V.W.E. Inc.** — the schematic title blocks
spell it out: **Virtual World Entertainment, 1100 W Cermak Suite B404,
Chicago, Illinois**, every sheet and silkscreen credited **"Design by M.C."**
Schematic plots are dated 1/11/1996 (peripheral boards) and 6/26/1996 (RIO
board), so these drawings post-date the Rev. 0/1 boards they describe.
## Board family
| P/N | Title | Rev photographed | Key silicon |
|-----|-------|------------------|-------------|
| **1407** | RIO board ("CPU board") | Rev. 3 | Toshiba **TMP68HC11** @ 8 MHz, GAL20V8A decoder, 27C512 EPROM, LH52B256 32K SRAM, 5× HCTL-2016 |
| **1401** | Buttons board | Rev. 0 | 2× UCN5821A lamp drivers, 2× 74HCT165, 74HCT164, 74LS00/05 |
| **1402** | Keypad board | Rev. 1 | 2× 74HCT164, CD74HCT165, 74LS00/05 |
| **1408** | Display board ("RIO external display") | Rev. 1 | Intersil **ICM7228B** 8-digit driver, 8× LTS313A 7-segment |
| **1413** | Quad Amplifier Board | Rev. 1 | **Pyramid PB-150P "Pro Plus"** car amp (bolt-on) + passive 3-way crossovers |
Plus the cockpit position sensors: **HP HEDS-5700** two-channel quadrature
optical encoders (option code **I** = 512 counts/rev; the photographed unit is
date-coded 9515, Singapore), one per analog axis, read by the five HCTL-2016
counters on the RIO board (`OPT1``OPT5` = stick X/Y, throttle, left/right
pedals).
## RIO board 1407 (schematic sheets 13, `RIO_1407`)
**Sheet 1 — CPU : Memory Decoder.** The TMP68HC11 runs in expanded
multiplexed mode: a 74HCT573 latches A[0:7] off the AD bus, and **U7 — drawn
as a 20L8, fitted as a GAL20V8A-15LP** — decodes A[8:15] into `ROM_SEL*`
(27C512 EPROM, U10), `RAM_SEL*` (LH52B256-70LL 32K SRAM, U11), `PH_SEL*`
(HCTL-2016 bank via two 74LS138s), `DSP_SEL*` (external display port) and
`BND_SEL` (I/O-interface latch, sheet 3). Each HCTL-2016 feeds a 5-pin
header (`J4``J8`) carrying CHA/CHB from a HEDS-5700. The DB9 COM port goes
through a MAX231/MAX232-class level shifter; the schematic's `IN_RESET*` from
the serial side is the hardware behind the **DTR reset pulse** described in
[PROTOCOL.md](../PROTOCOL.md) §1. The "display interface" block (ALS541
buffers, HEADER14) exports latched data lines `HD[00:04]` + digit address
`AS0-2` to the display board — this is a memory-mapped port, *not* the pod
bus. A 22V10 site (U31) is drawn mostly N/C and is **unpopulated** on the
photographed board.
**Sheet 2 — Power & Reset Interface.** A MAX690 supervisor provides
power-fail reset and a **watchdog (WDI)**; reset can also come from the
front-panel `RESET` toggle (SW1) or remotely through an **H11L1 optocoupler**
fed from an isolated 3-pin `+5 VDC` input (J8) — the game PC can hard-reset
the board. The `ABORT` toggle (SW2) pulls the HC11 `IRQ*`. A 74LS241 drives
the status LED bank: `POWER ON`, `FAULT`, `RESET`, `RUN`, `TX`, `RX`. Power
enters on J9 through 5 A/10 A fuses, LC filters and 1N5341B clamp zeners,
producing separate logic `VCC` and peripheral `VCC_EXT` rails.
**Sheet 3 — I/O Interface ("pod bus").** Eight identical 16-pin IDC ports
(`J11``J18`) fan out through ALS541 buffers and SIP resistor packs. All
ports share the outbound signals; each port has its own return line
(`DI[0..7]`, selected by a 74LS138 from `SEL0-2`) and its own buffered
brightness line (`BRT_CTR0-7`, all copies of one PWM signal). A 74HC574
latch written via `BND_SEL` holds the bus control bits (`SEL0-2`, `RD_SB`,
`WR_SB`, `D_OUT`, `CCK`) — so the firmware bit-bangs the bus by rewriting
this latch.
### Pod bus signal set (per port)
| Signal | Direction | Purpose |
|--------|-----------|---------|
| `CCK` | RIO → pod | shift clock |
| `DD_IN` | RIO → pod | serial data into the pod's 74HCT164s (address select + payload) |
| `WR_SB` | RIO → pod | write strobe (latch lamp/display data) |
| `RD_SB` | RIO → pod | read strobe (parallel-load the pod's 74HCT165) |
| `DD_OUT` | pod → RIO | serial readback, **open-collector 74LS05** so pods can share a cable |
| `BRT_CTR` | RIO → pod | global lamp-brightness PWM, gates the UCN5821A output enable |
| `VCC_EXT`, GND | — | peripheral power rail |
Every pod board carries a 74HCT164 whose parallel outputs go to an
**`ADR. SEL.` jumper block (`A-0``A-7`)**: the RIO shifts an address
pattern down the cable, the jumper picks which bit this board answers to,
and 74LS00 gates qualify `RD_SB`/`WR_SB` with that selection. This
hardware addressing is what the 0x000x6F logical input map in
[PROTOCOL.md](../PROTOCOL.md) ultimately resolves to.
## Peripheral boards
- **1401 Buttons board** (`PBE_1401`) — eight 4-pin connectors (`J3``J10`),
pinout `1 = buttons GND, 2 = button in, 3 = lamps +5V, 4 = lamps logic GND`.
Two UCN5821A serial-in latched sink drivers light the button lamps
(brightness = `BRT_CTR` PWM on their OE), two 74HCT165s read the buttons
back, one of them also echoing board status/address.
- **1402 Keypad board** (`KEY_1402`) — hosts the COTS 16-key hex pad (0F)
on an 8-line header (4×4 matrix). A second 74HCT164 drives the matrix
rows through open-collector 74LS05 inverters; the 74HCT165 reads the four
columns (`IN0-3`, 2.7K pull-ups) plus a board-detect line `BRD_DT*`.
- **1408 Display board** (`DSP_1408`) — ICM7228B driving eight LTS313A
digits (hex decode) from the RIO's dedicated 14-pin *external display*
port; write-only, plus an isolated 26-pin **"IBM printer port"** LED
monitor block. Silkscreen reads "P/N 140B" but the schematic title block
confirms **1408**. Full pin-level output definition — and what the
firmware actually displays (boot banner + the keypad-A-9-E test mode) —
in [display-board-1408.md](display-board-1408.md).
- **1413 Quad Amplifier Board** (`AMP_PLT`, silkscreen "Quad Amplifier
Board … P/N 1413 REV. 1") — the actual amplification is a stock
**Pyramid PB-150P "Pro Plus"** 4-channel 12 V car-audio amplifier bolted
to the board as a module; the schematic only drew the VWE-designed
passive circuitry around it. The board feeds the Pyramid's `B+12V / GND
/ REMOTE` screw terminals and speaker-level `HIGH INPUT` connectors, and
its four outputs (`1CH/L``4CH/R`) return through terminal strips into
per-corner 3-way passive crossovers: Dale IHB-3 inductors (820 µH woofer
low-pass, 180 µH midrange, date-coded 9531), MMP 100 VDC film capacitors
(2.2 µF/10 µF), and Dale CP-5 2 Ω 5 W pad resistors. Corner speaker
connectors J1J4 (front/back × left/right) are 6-pin: `1 woofer+,
2 midrange+, 3 tweeter+, 4 woofer, 5 midrange, 6 tweeter` (silkscreen,
matching sheet 5). Power enters on J5 (4-pin: `+12VDC ×2, GND ×2`) with a
green POWER ON LED; the per-channel red LED pairs across the outputs are
the schematic's "speaker outputs checking circuit". PCB etch NT 1-0A 3695
— the same fab batch series (week 36 '95) as the buttons board.
## GAL dump — [`gal/GAL20v8a_5764.JED`](gal/GAL20v8a_5764.JED)
Fuse map read 2023-03-09 from **U7 on the 1407 board** (GAL20V8A-15LP,
hand-labeled **"5764"** — which matches the JEDEC fuse checksum `*C5764`, the
source of the file name). Per sheet 1 it is the CPU **memory decoder**
(drawn as a 20L8). **Decoded** — equations, the recovered memory map
(`$2000-$9FFF` SRAM, `$A000/$A010/$A020` I/O windows, `$C000-$FFFF` EPROM)
and the firmware cross-check are in [`gal/README.md`](gal/README.md); the
chip's programmed signature even reads `U7`.
## Schematic scan — [`schematics/Scans_018-014.pdf`](schematics/Scans_018-014.pdf)
| PDF page | Sheet | Title block |
|----------|-------|-------------|
| 1 | RIO_1407 sheet 1/3 | CPU : Memory Decoder |
| 2 | RIO_1407 sheet 2/3 | Power & Reset Interface |
| 3 | RIO_1407 sheet 3/3 | I/O Interface |
| 4 | PBE_1401 1/1 | Buttons Board |
| 5 | AMP_PLT (P/N 1413) 1/1 | Amplifier Board |
| 6 | KEY_1402 1/1 | Keypad Board |
| 7 | DSP_1408 1/1 | Display Board |
## Photo index ([`photos/`](photos/))
Original Signal filenames preserved here for provenance (taken 2026-07-15;
amplifier board 2026-07-19).
| File | Original | Shows |
|------|----------|-------|
| `encoder-heds5700-control-assembly.jpeg` | `signal-2026-07-15-093011.jpeg` | HP HEDS-5700 I02 encoder (9515 A, Singapore) on a control-mechanism shaft |
| `display-board-1408-front-digits.jpeg` | `signal-2026-07-15-093017.jpeg` | Display board digit side, 8× LTS313A (U2U9), PCB etch NT 1-0 5094 |
| `display-board-1408-back-driver.jpeg` | `signal-2026-07-15-093023.jpeg` | Display board driver side: ICM7228BIPI (date 9438), 16-pin IDC, "RIO EXTERNAL DISPLAY / P/N 140B REV. 1" |
| `keypad-1402-front-keys.jpeg` | `signal-2026-07-15-093032.jpeg` | 16-key hex keypad module (keys 0F), front bezel |
| `keypad-board-1402-back-logic.jpeg` | `signal-2026-07-15-093038.jpeg` | Keypad board logic: DM74LS00N, 2× MM74HCT164N, SN74LS05N, CD74HCT165E, ADR. SEL. jumpers, "P/N 1402 REV. 1" |
| `buttons-board-1401-solder-side.jpeg` | `signal-2026-07-15-093045.jpeg` | Buttons board solder side, PCB etch NT 1-0 3695 |
| `buttons-board-1401-component-side.jpeg` | `signal-2026-07-15-093051.jpeg` | Buttons board: 2× CD74HCT165E, 74HCT00AP, 2× UCN5821A-type drivers, 74HCT164N, 8× lamp/button connectors, "P/N 1401 REV. 0" |
| `rio-board-1407-solder-side.jpeg` | `signal-2026-07-15-093058.jpeg` | RIO board solder side, PCB etch NT 3-0 4996 |
| `rio-board-1407-component-side.jpeg` | `signal-2026-07-15-093105.jpeg` | RIO board: TMP68HC11 QFP, GAL20V8A ("5764" sticker), AM27C512 EPROM, LH52B256 SRAM, 5× HCTL-2016 + OPT15, J11J18 pod ports, status LEDs, "P/N 1407 REV. 3" |
| `amp-board-1413-component-side.jpeg` | `signal-2026-07-19-134932.jpeg` | Quad Amplifier Board full view: Pyramid PB-150P module, corner crossovers, "P/N 1413 REV. 1", J5 pinout silkscreen |
| `amp-board-1413-solder-side.jpeg` | `signal-2026-07-19-134926.jpeg` | Amplifier board solder side, PCB etch NT 1-0A 3695 |
| `amp-board-1413-pyramid-inputs.jpeg` | `signal-2026-07-19-134921.jpeg` | Pyramid input face: HIGH INPUT connectors, LEVEL pots, LOW INPUT RCAs; front-corner crossovers |
| `amp-board-1413-pyramid-outputs.jpeg` | `signal-2026-07-19-134915.jpeg` | Pyramid output face: SPEAKERS terminals 1CH/L4CH/R, blade FUSE, B+12V/GND/REMOTE; check LEDs, J1J4 pinout silkscreen |
## Cross-references
- [`../PROTOCOL.md`](../PROTOCOL.md) — the 9600 8N1 wire protocol this
hardware speaks; its DTR reset pulse and 0x000x6F input map both have
their physical explanation above.
- [`../../rio-firmware/`](../../rio-firmware/) — v4.2 EPROM dump (from the
AM27C512 at U10) and 68HC11 disassembly/patches.
- [`../Win32RIO/`](../Win32RIO/) — FASA's original Windows driver for the
same hardware.
@@ -0,0 +1,148 @@
# Display Board 1408 — outputs, and what the RIO shows on it
Complete definition of the RIO External Display board (P/N 1408 Rev. 1,
`DSP_1408` sheet 7), from the schematic, the board photos, the GAL decode,
and the firmware disassembly (`RIOv4_2.disasm.asm`).
## Outputs: light only — the board is electrically write-only
No signal leaves the board toward the RIO or anything else. J1 has no
readback line, matching the GAL's write-only `DSP_SEL*` decode, and the
PC↔RIO protocol has no display command — only the firmware writes it.
### 1. Eight multiplexed 7-segment digits (U2U9, LTS313A, red, common-cathode)
The ICM7228B (U1) does all driving. `HEX/B/SHD` is strapped high via 2.7 K
(R1): **hexadecimal decode mode, always enabled** — each digit renders 0F
plus a decimal point; no raw-segment access exists as built.
- **Segment bus** — U1 pins 16, 17, 20, 18, 21, 22, 23, 15 drive
`SEGA``SEGG`, `SEGDP`, shared by all digits (digit pins AG, DP =
10, 9, 8, 5, 4, 2, 3, 7). Segments source the multiplexed current
(~20 mA peak).
- **Digit commons** — U1 outputs `DSP1``DSP8` (pins 4, 25, 3, 1, 26, 2,
27, 24) each sink one digit's tied-together commons (CC1+CC2, pins 6+1).
`DSP1`→U2 (leftmost) … `DSP8`→U9 (rightmost).
- Multiplex scanning is internal to the ICM7228; the host only writes
digit registers.
From the 68HC11 the digits are bytes at **`$A000``$A007`** (`AS0-2` =
A0A2, strobed by `DSP_SEL*` = `E & /R/W`-qualified, arriving as the
7228's `/WR` on J1 pin 7). Data format per write: bits 03 = hex value
(`MD[00:03]``ID0-3`), bit 4 = decimal point (`MD[04]``ID7`; DP lights
when the bit is **low** — the firmware always sets it, so the DP is never
used). **`$A000` = leftmost digit (U2), `$A007` = rightmost (U9)**; the
firmware's render buffer (`$2038`, routine `$CB49`) fills right-to-left so
multi-digit hex numbers read naturally.
### 2. Twelve passive monitor LEDs on the "IBM printer port" header (J2) — an input, not an output
J2 (26-pin IDC, standard LPT-ribbon mapping to a DB25) is electrically
isolated from the display circuit; its `DD` nets touch nothing but LEDs
and 1 K resistors referenced to the PC's own ground (`IBM_GND`). With a PC
parallel port plugged in, the LEDs show: **D0D7** = data bits (J2 pins
3, 5, 7, 9, 11, 13, 15, 17), **D8** = `/STROBE` (pin 1), **D9D11** =
`/AUTOFD`, `/INIT`, `/SELECTIN` (pins 2, 6, 8). The LPT status returns are
strapped to ground so a PC streams without waiting. It is a built-in
parallel-port activity monitor / bench tool, powered entirely by the PC.
### 3. Nothing else
J1 (14-pin, from the RIO's EXTERNAL DISPLAY port) is all input:
`MD[00:04]` on pins 2, 4, 6, 8, 10; `AS0-2` on 1, 3, 5; `BF_DSP_SEL*` on
7; GND on 9, 11, 13; VCC on 12, 14.
## What the RIO firmware displays (v4.2)
All display writes go through `$CB49` (buffer render) or five canned
patterns at `$CB7C$CC52`, driven from four contexts: initialization,
the built-in **test mode**, the **serial error/crash displays**, and
the **CheckRequest self-test** (all below). During normal, healthy
gameplay the display is static.
**Normal operation:** `F0000000` — written at cold boot (`$C068`) and warm
re-init (`$C64B`), then left alone.
**Test mode** — entered by typing the keypad sequence **A, 9, E** on a
cockpit keypad (`$C1CB` tracks the sequence at `$20F8`); entry sets flag
`$2421`, shows `F1000000`, and notifies the PC (`TestModeChange`, cmd
`0x8C` — see [PROTOCOL.md](../PROTOCOL.md)). Within test mode the next
keypad digit selects a sub-test; **F** ends a sub-test (back to
`F1000000`), **D** exits test mode (back to `F0000000`).
Display format convention: **left pair = test number, right = value**,
zeros as filler; the decimal points are never used.
| Key | Display | Test |
|-----|---------|------|
| *(entry)* | `F1000000` | test-mode banner |
| `0` | `00000000` | encoder-counter reset: presets the five 16-bit accumulators to `$FAFA` and re-reads all HCTL-2016s (`$C446`) |
| `1` | `0100hhll` | axis test, channel 01 — counter at `$A028/$A038` |
| `2` | `0200hhll` | axis test, channel 02 — `$A020/$A030` |
| `3` | `0300hhll` | axis test, channel 03 — `$A022/$A032` |
| `C` | `0C00hhll` | axis test, channel 0C — `$A024/$A034` |
| `4` | `0400hhll` | axis test, channel 04 — `$A026/$A036` |
| `5` | `050000nn` | button test — `nn` = index of the pressed input (scan at `$D004` over the pod input state at `$2152`), with lamp feedback |
| `6` | `06000003` | lamp test, pattern 3 (`$D34A`) — right digit is the pattern code |
| `7` | `07000001` | lamp test, pattern 1 (`$D370`) |
| `8` | `080000kk` | keypad test — `kk` = keypad status byte `$204B`, refreshed while scanning (`$CC7E`) |
In the axis tests `hhll` is the **live 16-bit quadrature count in hex**
(high:low = `$201D:$201E`, alternating with the raw latched bytes
`$2026:$2025` as the loop runs) — spin the stick/throttle/pedal encoder
and watch the number move. The channel IDs are literally the keypad keys
that start them, which is why the fourth axis reads `0C`.
## The CheckRequest self-test leftover: `04000000`
The host `CheckRequest` (`0x80`) triggers a full self-test in the
handler at `$C5A6`: it sets the same display flag test mode uses
(`$2421`), brackets itself with `TestModeChange 0x8C` notifications,
flashes a lamp pattern, scans the pods, and sweeps the five encoder
channels — rendering each channel's `cc00hhll` frame as it goes. It
clears the flag on exit but **never repaints the display**, so after
every status check the digits are left showing the last frame:
channel 4, usually `04000000` with idle encoders. It is a stale
snapshot (axis movement does not update it — bench-confirmed
2026-07-19), purely cosmetic, and the board keeps operating normally.
Any reset repaints `F0000000`.
## Error displays (the third use)
Two more display writers live in the serial machinery — the display is
also the RIO's **error annunciator**:
- **`E0` serial-diagnostics readout** (`$D5F2`) — `E0xxyyzz` where
`xx` = TX-ring overflow count (`$3187`, outbound byte dropped because
the ring was full, `$D66A`), `yy` = give-up count (`$3184`, `$D96A`
reply cycles whose 5 timeout retransmits all went unACKed), `zz` =
reply-teardown count (`$3185`, `$DA2F` — any reply cycle that needed
at least one retransmission; the counter behind a lone `E0000001`).
Semantics bench-confirmed 2026-07-19: display read `E0000105` after
one give-up + five teardowns. Each increment site immediately re-renders,
so the display live-updates as serial trouble accumulates. If a
cockpit's display reads `E0…` instead of `F0000000`, the serial link
has been dropping or retrying. Note there is **no threshold in stock
firmware** — the first event paints `E0000001` permanently; the
**RIO 4.3** firmware (`make_patch.py --e0thresh`, see
[RIOv4_2-ANALYSIS.md](../../rio-firmware/RIOv4_2-ANALYSIS.md)) keeps
the display quiet until a counter reaches 5, and repaints honestly
after every check self-test.
- **`dEAd` crash screen** (`$DAB8`, loop at `$DAB2`) — the TX-ISR ring
drain (`$D8C2`) looks each outgoing command byte up in an 8-entry
dispatch table at `$3158`; an unmatched byte means internal state
corruption, and the firmware jumps to an infinite loop that renders
`#$DE #$AD`**`dEAd`** followed by RAM bytes `$0000/$0001` (nothing
in this build writes them — vestigial debug data). The board is dead
until reset; there is no watchdog to rescue it (the MAX690's WDI is
not connected to the CPU and no strobe code exists in the firmware).
## Cross-references
- [README.md](README.md) — board family, photos, schematic page map
- [gal/README.md](gal/README.md) — `DSP_SEL*` decode (`$A000-$A00F`,
write-only) recovered from the GAL fuse map
- [`rio-firmware/RIOv4_2.disasm.asm`](../../rio-firmware/RIOv4_2.disasm.asm)
— display routines `$CB20/$CB36/$CB49` (buffer render), `$CB7C` (`F0`),
`$CBA7` (`F1`), `$CBD2`/`$CBFD` (lamp-test banners), `$CC28` (zeros),
test dispatcher `$C241`, entry-sequence tracker `$C1CB`
@@ -0,0 +1,38 @@

Device : GAL20V8A
Created By: http://www.autoelectric.cn
Date : 2023-03-09 19:02
*QP24
*QF2706
*G0
*F0
*L00000 11111111111111111111111111111111
*L00032 11111111111111011111111111111111
*L00064 11111111110111110000000000000000
*L00320 11111111111111111111111111111111
*L00352 11111111111111111111111111111111
*L00384 11111110111111110000000000000000
*L00640 11111111111111111111111111111111
*L00672 11111111100101111011101110111011
*L00704 10111011101101110000000000000000
*L00960 11111111111111111111111111111111
*L00992 11111111111001111111111111111111
+71
View File
@@ -0,0 +1,71 @@
# U7 GAL20V8A decode — the RIO memory map, recovered
[`GAL20v8a_5764.JED`](GAL20v8a_5764.JED) is the fuse map read out of **U7**
on the RIO board (P/N 1407 Rev. 3) — the chip the schematic draws as a
"20L8" memory decoder. Decoding the fuses (see below for how) yields the
complete address decode, which cross-checks perfectly against both the
schematic net names and the firmware disassembly.
Three provenance details line up: the chip's hand-written sticker reads
**"5764"** = the JED fuse checksum (`*C5764`); the fuse map is in **complex
(20L8-emulation) mode** exactly as the schematic symbol suggests; and the
programmed electronic signature (UES) literally reads **`U7`** — VWE
signed the chip with its own reference designator.
## Recovered equations
Inputs: `A[15:3]` (A3 arrives via the pin-16 feedback path), `E`, `R/W`.
All outputs are combinatorial, always enabled.
```
/ROM_SEL = A15 & A14 ; pin 15
/RAM_SEL = /A15 & A14 & E | /A15 & A13 & E | A15 & /A14 & /A13 & E ; pin 19
/PH_SEL = A15 & /A14 & A13 & /A12../A6 & A5 ; pin 20
/BRD_SEL = A15 & /A14 & A13 & /A12../A6 & /A5 & A4 & E & /RW ; pin 18
/DSP_SEL = A15 & /A14 & A13 & /A12../A6 & /A5 & /A4 & E & /RW ; pin 17
/OE = E & RW ; pin 22
NOT_A3 = /A3 ; pin 21
```
(`/A12../A6` abbreviates the run `/A12 & /A11 & /A10 & /A9 & /A8 & /A7 & /A6`;
full machine output comes from `galdecode.py`, below.)
## The memory map
| Address | Select | Device | Notes |
|---------|--------|--------|-------|
| `$0000-$1FFF` | *(none)* | 68HC11 internal | RAM `$0000-$00FF`, registers `$1000-$103F` |
| `$2000-$9FFF` | `RAM_SEL*` | LH52B256 32K SRAM | E-qualified, read/write; exactly 32K |
| `$A000-$A00F` | `DSP_SEL*` | External-display write port | write-only (`E & /RW`) |
| `$A010-$A01F` | `BRD_SEL` | Pod-bus control latch (74HC574, sheet 3) | write-only (`E & /RW`) |
| `$A020-$A03F` | `PH_SEL*` | HCTL-2016 encoder counters ×5 | `A3` picks high/low count byte via `NOT_A3`, `A[2:0]` picks chip via the 74LS138s |
| `$C000-$FFFF` | `ROM_SEL*` | 27C512 EPROM | top 16K of the 64K device |
`OE*` (`E & R/W`) is the shared read-side output enable for
EPROM/SRAM/counters; the write-only decodes double as write strobes.
**Firmware cross-check** — [`RIOv4_2.disasm.asm`](../../../rio-firmware/RIOv4_2.disasm.asm)
touches precisely these windows and nothing else in `$A0xx`: digit writes to
`$A000-$A007`, pod-bus latch writes to `$A010`, and encoder reads at
`$A021/$A023/$A025/$A027/$A029` + `$A028/$A030-$A038` (five chips × two count
bytes). It also explains the EPROM dump being `FF` below `$C000`: with
`ROM_SEL* = A15·A14` and all 16 address lines wired, only the top quarter of
the 27C512 is ever addressed.
## How the decode was done
[`galdecode.py`](galdecode.py) parses the JED and applies the GAL20V8
complex-mode fuse geometry — row/column-to-pin tables, product-term-disable
fuses, XOR polarity, SYN/AC0 mode bits — taken from MAME's `jedutil.cpp`
(the reference implementation for PLD fuse maps). Pin-to-net names come
from schematic sheet 1 (`RIO_1407`) and live in
[`u7-pin-names.txt`](u7-pin-names.txt). Reproduce with:
```sh
py docs/hardware/gal/galdecode.py docs/hardware/gal/GAL20v8a_5764.JED docs/hardware/gal/u7-pin-names.txt
```
The JED itself was read from the chip on 2023-03-09 (header says an
autoelectric.cn / XGecu-style programmer). Note `*F0` in the header: unlisted
fuse rows default to 0, i.e. unused product terms are all-connected
(always-false) rather than blown — the decoder handles this.
+91
View File
@@ -0,0 +1,91 @@
"""Decode a GAL20V8 JED (complex mode) into boolean equations.
Fuse-map semantics taken from MAME jedutil.cpp (config_gal20v8_pins,
get_pin_fuse_state, is_gal20v8_product_term_enabled,
does_output_enable_fuse_row_allow_output).
"""
import re
import sys
JEDPATH = sys.argv[1] if len(sys.argv) > 1 else r"C:\VWE\riojoy\docs\hardware\gal\GAL20v8a_5764.JED"
QF = 2706
fuses = [0] * QF # *F0 default
text = open(JEDPATH).read()
for m in re.finditer(r"\*L(\d+)\s+([01]+)", text):
start = int(m.group(1))
for i, ch in enumerate(m.group(2)):
fuses[start + i] = int(ch)
SYN, AC0 = fuses[2704], fuses[2705]
mode = {(1, 1): "complex", (1, 0): "simple", (0, 1): "registered", (0, 0): "registered"}[(SYN, AC0)]
print(f"SYN={SYN} AC0={AC0} -> {mode} mode")
assert mode == "complex", "decoder only handles complex mode"
# UES signature 2568-2631, 8 bytes MSB-first
ues = bytes(
int("".join(map(str, fuses[2568 + 8 * i : 2576 + 8 * i])), 2) for i in range(8)
)
print("UES signature:", ues, "->", ues.decode("ascii", "replace"))
# complex-mode column map: pin -> (lowfusecolumn, highfusecolumn)
COLS = {1: (3, 2), 2: (1, 0), 3: (5, 4), 4: (9, 8), 5: (13, 12), 6: (17, 16),
7: (21, 20), 8: (25, 24), 9: (29, 28), 10: (33, 32), 11: (37, 36),
13: (39, 38), 14: (35, 34), 16: (31, 30), 17: (27, 26), 18: (23, 22),
19: (19, 18), 20: (15, 14), 21: (11, 10), 23: (7, 6)}
# complex-mode rows: pin -> (OE row fuse, first PT fuse, last PT fuse)
ROWS = {22: (0, 40, 280), 21: (320, 360, 600), 20: (640, 680, 920),
19: (960, 1000, 1240), 18: (1280, 1320, 1560), 17: (1600, 1640, 1880),
16: (1920, 1960, 2200), 15: (2240, 2280, 2520)}
XOR = {15: 2567, 16: 2566, 17: 2565, 18: 2564, 19: 2563, 20: 2562, 21: 2561, 22: 2560}
# Optional net names for pins, filled after reading the schematic:
NAMES = {}
if len(sys.argv) > 2:
for kv in open(sys.argv[2]):
kv = kv.strip()
if kv and not kv.startswith("#"):
p, n = kv.split(None, 1)
NAMES[int(p)] = n
def name(pin):
return NAMES.get(pin, f"pin{pin}")
def pt_enabled(fuserow):
return fuses[2640 + fuserow // 40] == 1
def term(fuserow):
"""Return list of literals, or None if term is always-false, '1' if always-true."""
lits = []
for pin in sorted(COLS):
lo, hi = COLS[pin]
l, h = fuses[fuserow + lo], fuses[fuserow + hi]
if l == 0 and h == 1:
lits.append("/" + name(pin))
elif l == 1 and h == 0:
lits.append(name(pin))
elif l == 0 and h == 0:
return None # both intact -> always false
return lits if lits else ["1"]
for pin in sorted(ROWS, reverse=True):
oerow, first, last = ROWS[pin]
oe = term(oerow) if pt_enabled(oerow) else None
if oe is None:
print(f"\npin {pin} ({name(pin)}): OE never true -> used as INPUT")
continue
pol = "active-HIGH" if fuses[XOR[pin]] else "active-LOW"
pts = []
for f in range(first, last + 40, 40):
if not pt_enabled(f):
continue
t = term(f)
if t is not None:
pts.append(" & ".join(t))
lhs = name(pin) if fuses[XOR[pin]] else "/" + name(pin)
oestr = " & ".join(oe)
print(f"\npin {pin} ({name(pin)}): {pol}, OE = {oestr}")
if pts:
print(f" {lhs} = " + ("\n" + " " * (len(lhs) + 5) + "| ").join(pts))
else:
print(f" {lhs} = 0 (no active product terms)")
@@ -0,0 +1,23 @@
# U7 (GAL20V8A, "20L8" on schematic RIO_1407 sheet 1) pin -> net name
1 A15
2 A14
3 A13
4 A12
5 A11
6 A10
7 A9
8 A8
9 A7
10 A6
11 A5
13 A4
14 E
23 RW
16 A3
15 ROM_SEL
22 OE
21 NOT_A3
20 PH_SEL
19 RAM_SEL
18 BRD_SEL
17 DSP_SEL
Binary file not shown.

After

Width:  |  Height:  |  Size: 738 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 916 KiB