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:def5840e07git-subtree-split:42b3944ebe
@@ -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 1–3, `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 0x00–0x6F 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 (0–F)
|
||||
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 J1–J4 (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 (U2–U9), 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 0–F), 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 + OPT1–5, J11–J18 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/L–4CH/R, blade FUSE, B+12V/GND/REMOTE; check LEDs, J1–J4 pinout silkscreen |
|
||||
|
||||
## Cross-references
|
||||
|
||||
- [`../PROTOCOL.md`](../PROTOCOL.md) — the 9600 8N1 wire protocol this
|
||||
hardware speaks; its DTR reset pulse and 0x00–0x6F 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 (U2–U9, 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 0–F
|
||||
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 A–G, 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` =
|
||||
A0–A2, strobed by `DSP_SEL*` = `E & /R/W`-qualified, arriving as the
|
||||
7228's `/WR` on J1 pin 7). Data format per write: bits 0–3 = 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: **D0–D7** = data bits (J2 pins
|
||||
3, 5, 7, 9, 11, 13, 15, 17), **D8** = `/STROBE` (pin 1), **D9–D11** =
|
||||
`/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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 738 KiB |
|
After Width: | Height: | Size: 637 KiB |
|
After Width: | Height: | Size: 647 KiB |
|
After Width: | Height: | Size: 587 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 858 KiB |
|
After Width: | Height: | Size: 844 KiB |
|
After Width: | Height: | Size: 712 KiB |
|
After Width: | Height: | Size: 614 KiB |
|
After Width: | Height: | Size: 828 KiB |
|
After Width: | Height: | Size: 987 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 916 KiB |