# Tesla Rel 4.10 on Windows 10 — VPX Emulation Implementation Plan

## Goal

Run the unported DOS versions of **Tesla:BattleTech** and **Tesla:Red Planet**
(release 4.10) inside the surviving cockpits, whose computers now run
Windows 10. The games are complete on disk (`sda4/BTLIVE`, `sda4/RPLIVE` —
executables, resources, content), but they render through **Division Ltd.'s
VPX board**, hardware that no longer exists in the pods. Everything else the
games need still exists or is emulatable.

The approach: a customized DOS emulator whose main addition is a
**high-level-emulation (HLE) device that impersonates the VPX board** at its
host interface and renders with modern GPU APIs. The cockpit's real RIO
(COM1) and plasma display (COM2) are passed through to the physical serial
ports so the game's own drivers operate the actual cockpit hardware.

```
┌─ Windows 10 cockpit PC ─────────────────────────────────────────────┐
│  ┌─ DOSBox-X (custom fork) ─────────────────────────────────────┐   │
│  │  BTL4 game EXE (DOS, Borland 32-bit, unmodified)             │   │
│  │   ├─ I/O port 0x150..0x153 ──► VPX HLE device ── OpenGL ─────┼─► main display
│  │   ├─ COM1 ── serial passthrough ─────────────────────────────┼─► RIO (cockpit controls)
│  │   ├─ COM2 ── serial passthrough ─────────────────────────────┼─► plasma display
│  │   ├─ SB16 emulation ── host audio ───────────────────────────┼─► cockpit speakers
│  │   ├─ NE2000 emulation ── pcap/slirp ─────────────────────────┼─► pod-to-pod network
│  │   └─ emulated VGA (DOS console; later: gauges) ──► window    │   │
│  └───────────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────────┘
```

## Why this is feasible — the evidence

Every load-bearing fact below was verified against files in this repository
and the Glaze drive dump (`sda4/`):

| Fact | Evidence |
|---|---|
| Host↔board link is an INMOS **C012 link adapter, IMS B004-style**: polled byte FIFO on 4–6 I/O ports, no host interrupts, no host DMA | `sda4/DPL3/LINKIO.C` ("simple polled PC / DOS interface, a la IMS B004", `setLA()`, `INPUT_READY`/`OUTPUT_READY` status bits) |
| The link adapter sits at **I/O port 0x150** in the shipped install | `sda4/BTLIVE/SETENV.BAT`: `DPLARG=... /device~0x150~ ...` |
| Boot sequence: host loads a transputer monitor, then i860 code segments, over the link | `DPLARG=/tranny~.\vrendmon.btl~/i860~.\vrnostex.mng~...`; `sda4/DPL3/VR_COMMS.C` (`i860_segment`, `vr_860code_action`, `vr_860data_action`) |
| Message protocol on the link: ≤1023-byte packets, 32-bit length/route word (sender/target id, iserver flag, broadcast id 0xff) | `sda4/DPL3/VR_COMMS.C` header comment + `send_protocol()` |
| Frame sync is **polled over the link** ("frame ack"), not interrupt-driven | `sda4/DPL3/VR_COMMS.C` (`frame ack` handling, `velocirender_inputstatus`) |
| The board-side renderer's behavior is documented **in source** | `sda4/DPL3/VRENDER/` (C + i860 asm, `PXPL5SUP/`, `DMA.TXT` tile-loop description, register maps `DIVPXMAP.H`, `DMAENGN.H`) |
| Triangle/command encodings | `CODE/*/MUNGA_L4/LIBDPL/dpl/vpx/TRICODER.H`, `VR_PROT.H`, `DPL_VPX.H` |
| The game natively drives the **RIO on COM1** | `CODE/RP/MUNGA_L4/L4RIO.CPP` + `L4RIO.HPP` (complete driver source); `L4CTRL.CPP` accepts `L4CONTROLS=RIO` / `RIO:COM1` |
| Plasma display on COM2, optional subsystems | `sda4/VGL_LABS/BOOTPOD.*` (`L4PLASMA=com2`, `L4SOUND`, `L4GAUGE`, `L4INTERCOM` all optional) |
| Keyboard fallback for controls exists (dev "office/cart" config) | `BOOTPOD.*`: `L4CONTROLS=THRUSTMASTER,KEYBOARD` |
| Complete runnable game installs | `sda4/BTLIVE/`, `sda4/RPLIVE/` (EXEs, `BTL4.RES`, content, `BT.BAT`/`BTGO.BAT`/`BTNET.BAT` launch modes) |
| Renderer configuration is data-driven from files we have | `sda4/BTLIVE/BTDPL.INI` (fog/lights per arena/time/weather, particle defs, cache lists) |
| All geometry/material/texture formats already decoded and re-rendered | `restoration/divformats.py`, `restoration/vwe-archive.html` |
| Audio: HMI SOS on SB16 | `sda4/VGL_LABS/AUTOEXEC.POD` (`SET BLASTER=A220 I5 D1 H5 P330 T6`) |
| Networking: WATTCP/BOOTP over a packet driver | `CODE/*/MUNGA_L4/NETNUB/` |

Note the pods ran **two** sound cards (`AWE_FRONT` at A220, `AWE_REAR` at
A240 per `AUTOEXEC.POD`). Phase 6 targets one emulated SB16; rear-channel
audio is deferred alongside the gauges.

## Platform choice

**Fork DOSBox-X** (Windows builds, active project, GPL):

- Built-in **serial passthrough** to real COM ports (`serial1=directserial realport:COM1`) — the RIO and plasma work through the game's own drivers with no new code.
- Built-in SB16, game port, NE2000 (pcap + slirp backends).
- Runs Borland DPMI/32RTM protected-mode DOS apps well; cycle throttling for a game that expects a Pentium Pro.
- Clean I/O-port device API for the custom VPX device; access to host OpenGL.

Alternative considered: **86Box** (full PCI/Pentium machine emulation).
Better absolute fidelity, but heavier, harder to bolt a GL-rendering device
into, and per-cycle emulation costs more than we need. DOSBox-X is the right
tradeoff; nothing in the plan precludes porting the VPX device to 86Box
later.

## Phases

### Phase 0 — Groundwork (repo + environment)
- [ ] Create `emulator/` build tree; vendor a pinned DOSBox-X source snapshot
      (submodule or subtree) and stand up a Windows build (Visual Studio).
- [ ] Assemble a clean game image: `BTLIVE` copied into a DOSBox-X mounted
      directory; replicate `AUTOEXEC.POD` environment (`SET BLASTER=…`,
      `L4CONTROLS=KEYBOARD` initially, `VIDEOFORMAT=svga`).
- [ ] Boot the game unmodified and record how far it gets (expected: DPL
      init fails at the missing link adapter).
- **Acceptance:** documented baseline failure mode; build reproducible.

### Phase 1 — Interface discovery (logging stub)
- [ ] Add a DOSBox-X device claiming I/O ports 0x150–0x157 that logs every
      read/write with timestamps and returns configurable status bits.
- [ ] Drive the game through init; capture the full port trace. Compare
      against `LINKIO.C` semantics (C012 register order: input data, input
      status, output data, output status, + reset/analyse) and identify the
      exact register map and any block-transfer (`outsw`) usage.
- [ ] Verify the trace against `VR_COMMS.C`'s expected send sequence
      (reset → tranny boot → i860 code/data segments → version handshake).
- **Acceptance:** annotated trace of the complete boot conversation; the
      register map and framing confirmed from the *shipped binary*, not
      just the DPL3 sources (guards against protocol drift between the
      DPL3 snapshot and the LIBDPL.LIB actually linked into Rel 4.10).

### Phase 2 — Link + boot protocol emulation
- [ ] Implement the C012 FIFO state machine (status bits, byte in/out,
      reset/analyse behavior) with correct "always ready" pacing.
- [ ] Implement transputer bootstrap acceptance: consume `VRENDMON.BTL`,
      emit whatever readiness bytes the monitor protocol expects (normative
      reference: `VR_COMMS.C` boot path; the `.BTL` content itself is
      discarded — we impersonate, not execute).
- [ ] Accept i860 code/data segment uploads (`vrnostex.mng` /
      `vrendmon`-loaded `vrend*.btl` variants); discard content; ACK.
- [ ] Implement the message layer: 32-bit length/route word, ≤1023-byte
      payloads, iserver vs. renderer messages, broadcast id 0xff.
- [ ] Answer version/status queries with values captured from `VR_PROT.H` /
      `DIVVERS.H` conventions; iterate until DPL init returns success.
- **Acceptance:** game completes `dpl` startup with `/device 0x150` and
      proceeds to resource loading; its console shows the same startup
      banner sequence the INI/logs imply.

### Phase 3 — Frame-stream renderer (the core)
- [ ] Decode the per-frame message stream: matrices/viewpoint, object and
      geometry references, material/texture state, light/fog/clip setup,
      and the `TRICODER.H` triangle encoding. Normative references:
      `VR_PROT.H`, `DPL_VPX.H`, `DPL.H`/`DPL_HOST.C` (what the host sends),
      `VRENDER/*.c` (how the board consumed it).
- [ ] Texture uploads: SVT/BSL texel formats are already decoded in
      `restoration/divformats.py`; port that logic to C++ (incl. 4-bit
      mono bit-slice textures with material color modulation).
- [ ] OpenGL backend in the device: double-buffered FBO, Division shading
      model (ambient/diffuse/emissive, RGB shading ramps, per-vertex
      "cooked" colors, linear fog with per-material fog immunity) —
      semantics already validated visually by `restoration/vwe-archive.html`.
- [ ] Present in a dedicated borderless window (target: the cockpit's main
      projector output), independent of the DOSBox-X VGA window.
- [ ] **Capture/replay harness:** every session can dump the raw link
      byte-stream; a standalone tool replays dumps into the renderer
      without DOSBox-X. This makes renderer work testable in isolation and
      lets us regression-test against golden frames.
- **Acceptance:** attract mode / mission renders recognizably; golden-frame
      comparisons against the scene-archive renderer for the same content.

### Phase 4 — Frame pacing and sync
- [ ] Implement the frame-ack poll exactly as the game expects (respond as
      the board would at ~30 Hz; configurable). `RETRACE n` in scene data
      implies frame-rate divisors — honor them.
- [ ] Tune DOSBox-X cycle settings for Pentium-Pro-class throughput; verify
      game simulation speed against the 30 Hz assumption in game code.
- **Acceptance:** stable 30 fps, no spiral-of-death when the host GPU is
      momentarily slow; timing feels correct in motion.

### Phase 5 — Cockpit I/O: RIO and plasma
- [ ] `serial1=directserial realport:COM1` (RIO), `serial2=…COM2` (plasma);
      set `L4CONTROLS=RIO:COM1`, `L4PLASMA=com2`.
- [ ] Validate against `L4RIO.CPP` expectations (framing, polling rate,
      shared-data messages). Risk is low — the game's own driver does the
      talking — but latency through the emulated UART must be measured;
      if polled-UART overhead hurts, add a fast path.
- [ ] Keyboard fallback (`L4CONTROLS=KEYBOARD`) retained for bench testing.
- **Acceptance:** cockpit sticks/pedals/buttons drive the game; plasma
      shows its normal content.

### Phase 6 — Audio and network
- [ ] SB16 at A220/I5/D1 (matches `AUTOEXEC.POD` front card); confirm HMI
      SOS drivers initialize and stream. Rear card (A240) deferred.
- [ ] NetNub: NE2000 emulation + packet driver, bridged via pcap so two
      cockpits (or a cockpit and a test PC) see each other; stand up the
      BOOTP expectations WATTCP has (`NetNub/include/BOOTP.H`).
- **Acceptance:** sound effects + MIDI play; two instances complete a
      networked mission handshake.

### Phase 7 — Deployment hardening
- [ ] Per-pod configuration file (COM mapping, display selection, pod id,
      game selection BT/RP) mirroring the old `VGL_LABS` per-pod setup.
- [ ] Autostart: boot-to-game like the original pods (`BOOTPOD.BAT`
      equivalent as a Windows service/scheduled task), watchdog restart.
- [ ] Error logging (`PREFMODE=ERRORLOG` existed in the original scripts —
      keep the spirit: never strand a pod on a DOS prompt).
- **Acceptance:** power-on → attract mode with no operator interaction.

### Phase 8 — Deferred (tracked, not in critical path)
- **Gauge displays:** original pods drove six instrument displays from the
  S3 card while the Division board drove the main view. Options, in
  ascending fidelity: (a) run with `L4GAUGE=` empty (supported by the game);
  (b) present the emulated VGA/VESA output in a window on a secondary
  monitor; (c) split/route to the physical gauge displays. Decide after
  Phase 5 when the real display topology is measured.
- **Rear audio channel** (second SB16/AWE at A240).
- **Intercom** (`L4INTERCOM`).
- **NTSC video mode** (`/video~ntsc`): only if a pod's projector chain
  still wants NTSC; SVGA path is primary.

## Risks and mitigations

| Risk | Likelihood | Mitigation |
|---|---|---|
| Protocol drift: Rel 4.10's LIBDPL differs from the DPL3 sources | Medium | Phase 1 derives the real protocol from the shipped binary's port trace; DPL3RLS + dated `VREND/` drops bracket the era |
| Undocumented bulk-transfer mode (`outsw`, `ok_to_fifo`) | Medium | Still plain port I/O; the trace will show it; implement what's observed |
| Game validates renderer version/capabilities strictly | Medium | Answer with values from `VR_PROT.H`/`DIVVERS.H`; iterate from the game's error messages (it prints useful diagnostics per `VR_COMMS.C` style) |
| Hidden second link adapter / interrupt use | Low | `LINKIO.C` is fully polled; the trace will falsify this cheaply |
| RIO protocol quirks under emulated UART timing | Low–Medium | Driver source is in-repo (`L4RIO.CPP`); real hardware available for testing |
| DPMI/32RTM incompatibility in DOSBox-X | Low | Known-good app class; 86Box as fallback platform |
| Performance (parsing + GL per frame) | Very low | 1996 scene complexity; thousands of triangles |

## Test strategy

1. **Unit:** protocol framing round-trip tests against `VR_COMMS.C`
   semantics; TRICODER decode against hand-built vectors.
2. **Replay:** captured link-stream dumps replayed into the standalone
   renderer; golden-image diffs (allowing rasterizer tolerance).
3. **Cross-check:** the same models rendered by `restoration/` tools vs.
   the emulator renderer must agree on geometry/material interpretation.
4. **Hardware-in-loop:** bench PC + real RIO on COM1 before cockpit time;
   cockpit sessions scripted with a checklist (controls, plasma, audio,
   network, endurance run).

## Sequencing and effort

Phases 0–2 are the discovery-heavy half; 3 is the largest single block of
work; 4–6 are integration. For one experienced developer:

- Phase 0–1: ~1–2 weeks
- Phase 2: ~2–4 weeks
- Phase 3: ~4–8 weeks
- Phase 4–6: ~3–5 weeks combined
- Phase 7: ~1–2 weeks

Total: roughly **3–5 months** of focused work to "playable in a cockpit,"
with the gauge displays intentionally out of scope until after that
milestone.

## Appendix A — C012/B004 register model (from `LINKIO.C`)

Base address `LA` (0x150 in the shipped configuration):

| Offset | Register | Behavior |
|---|---|---|
| +0 | input data | read one byte from board→host FIFO |
| +1 | output data | write one byte host→board |
| +2 | input status | bit 0 = byte available |
| +3 | output status | bit 0 = ready to accept |
| +4/+5 | reset / analyse | board reset & debug lines (per B004 convention; exact offsets to be confirmed in Phase 1 trace) |

## Appendix B — message framing (from `VR_COMMS.C`)

Every message begins with a 32-bit length/route word:
- bit 31 set → iserver-class message (boot/loader); clear → renderer message
- bits 23–16 → sender id (to host) / target id (from host); 0xff = broadcast
- low bits → payload byte count (≤1023 for renderer messages)

Boot order (from `DPLARG` and `VR_COMMS.C`): board reset → transputer
bootstrap (`VRENDMON.BTL`) → i860 code segment → i860 data segment →
version handshake → texture/geometry environment setup → frame loop
(commands + triangles, then frame-ack poll).

## Appendix C — primary sources

- Host link layer: `sda4/DPL3/LINKIO.C`, `sda4/DPL3/VR_COMMS.C`
- Protocol constants: `CODE/*/MUNGA_L4/LIBDPL/dpl/vpx/VR_PROT.H`,
  `TRICODER.H`, `DPL_VPX.H`, `DPL_MEM.H`
- Host library behavior: `sda4/DPL3/DPL.C`, `DPL_HOST.C`, `DPL_LOAD.C`
- Board-side renderer (normative for HLE behavior): `sda4/DPL3/VRENDER/`
- Shipped boot config: `sda4/BTLIVE/SETENV.BAT` (`DPLARG`), `BTDPL.INI`
- Cockpit I/O: `CODE/RP/MUNGA_L4/L4RIO.CPP`/`.HPP`, `L4CTRL.CPP`,
  `sda4/VGL_LABS/BOOTPOD.*`, `AUTOEXEC.POD`
- Data formats (already reimplemented): `restoration/divformats.py`
