Make the in-fork glass cockpit fully interactive and finish the lamp model. - Click-to-press: clicking a button's region in a VDB head window presses that RIO address into the game (lights white-hot while held, releases on mouse-up). vpxlog rt_wndproc hit-tests the click against the bezel geometry (MFD 4 top / 4 bottom, radar 6 left / 6 right; the radar bottom indicators are display-only, not clickable); head windows are tagged via GWLP_USERDATA. Clicks arrive on the VPX render thread, so the seam serialrio RIO_HostButton() queues them under a mutex and pollInput() drains them on the emu tick through the same incHold/ decHold path as the keyboard/pad -- so a click also lights the bezel. - Flash blink: decode the lamp byte's flash bits (RioLampState 1 slow / 2 med / 3 fast) -- an unpressed flashing lamp now toggles between its brightness level and off (half-periods 500/250/125ms); solid lamps unchanged, a press still wins. - Focus: head windows are WS_EX_NOACTIVATE so a button click never pulls foreground off DOSBox (which would demote the emu thread and stop SDL polling the pad); a click uses MA_NOACTIVATE too. Do NOT enable SDL background joystick events -- that raced the emu-thread controller poll against SDL's main-thread pump and crashed the process. Explode layout only, and only when a serial=rio port is present. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
162 lines
9.2 KiB
Markdown
162 lines
9.2 KiB
Markdown
# VPX + AWE32 devices — DOSBox-X integration
|
||
|
||
Our original source for the emulated pod hardware. Kept here under
|
||
version control because the DOSBox-X source tree itself
|
||
(`emulator/src/`, ~490 MB) is git-ignored.
|
||
|
||
- **`vpxlog.cpp`** — the Division VPX link adapter (INMOS C012 at I/O base
|
||
`0x150`), grown through Phase 3: iserver handshake responder, FIFO scene
|
||
decode, GL Division renderer (gallery shading model), VDB video-head
|
||
splitter windows. **RIO lamp/button bezel (B2, 2026-07-24, explode layout):**
|
||
`pal_draw` rings each mono-MFD head with its 8 RIO buttons (red, 4 top / 4
|
||
bottom, tucked under a 640x500 display so a 10px lip shows) and the radar
|
||
with the amber Secondary/Screen side columns + a centered bottom indicator
|
||
strip, lit from `serialrio`'s `RIO_GetPanelState` (off/dim/bright + white-hot
|
||
press; flashing lamps blink at the RioLampState rate). No labels -- the
|
||
display shows each button's function. **Click-to-press:** `rt_wndproc`
|
||
hit-tests a click against the bezel and presses that RIO address (thread-safe
|
||
via serialrio's queued `RIO_HostButton`); head windows are `WS_EX_NOACTIVATE`
|
||
so a click never pulls foreground off DOSBox (which would demote the emu
|
||
thread and stop the SDL pad poll). No-op when no `serial=rio` port is present.
|
||
- **`vweawe.cpp`** — the dual-AWE32 sound device: two vendored EMU8000
|
||
cores at 0x620/0x640 (+0x400/+0x800 triplets), rear-card DSP/mixer stub
|
||
at 0x240, autonomous render thread with direct winmm output. Needs the
|
||
GM ROM (`emulator/roms/awe32.raw`) via `VWE_AWE_ROM` or SoundFont
|
||
uploads are refused (banks declare `irom=1MGM`). See
|
||
`emulator/SOUND-NOTES.md`.
|
||
- **`emu8k.cpp` / `emu8k.h` / `emu8k_shim.h`** — EMU8000 wavetable core
|
||
vendored from 86Box (GPL-2.0-or-later, same license as DOSBox-X), with
|
||
a minimal shim; local changes are listed in the emu8k.cpp header.
|
||
- **`serialnamedpipe.cpp` / `serialnamedpipe.h`** — `serial<n>=namedpipe
|
||
pipe:<name>` backend (2026-07-12): serial-over-named-pipe for vRIO/vPLASMA,
|
||
replacing com0com. DOSBox is the pipe CLIENT with 500ms background retry;
|
||
typed frames carry data (`0x00 len bytes`) and DTR/RTS line state (`0x01
|
||
bits`); the full wire contract is in the header comment (pinned with the
|
||
vRIO session). Supports the directserial `rxpollus`/`rxburst`/`rxdelay`
|
||
low-latency knobs. Smoke-tested end-to-end 2026-07-12.
|
||
- **`serialrio.cpp` / `serialrio.h`** — `serial<n>=rio` backend (2026-07-21,
|
||
keyboard field 2026-07-22): the cockpit RIO board emulated **in-fork**. It
|
||
speaks the device side of the RIO 9600-8N1 protocol straight to the game's
|
||
UART (no external process, no pipe) and sources pilot input from an **SDL
|
||
game controller + the host keyboard** inside the emulator. Because the
|
||
game↔board round trip is in-process at the emulated UART's own cadence —
|
||
like the real ISA UART on a pod — the serial round-trip dropouts (livelock,
|
||
`TXMAXIDLE`, `rxburst`, 15s retry) can't occur. The protocol state machine +
|
||
input mapping are transcribed from the validated vRIO app (`C:\VWE\vrio`
|
||
VRio.Core: `VRioDevice`, the `Protocol` codec, `Input/InputRouter` +
|
||
`BindingProfileFormat`); the transport, wire pacer, panel UI and thread
|
||
locks are dropped (single emulator thread). RX byte pacing reuses
|
||
directserial's state machine (same `rxpollus`/`rxburst` knobs), draining a
|
||
queue the board itself fills.
|
||
**Keyboard field (B1):** sdlmain offers every SDL key to `RIO_HostKeyEvent()`
|
||
first; while capture is ON (default) bound keys press RIO addresses or drive
|
||
axes and are swallowed; unbound keys and Ctrl/Alt/Gui chords pass through
|
||
(host hotkeys keep working; modifier keys BOUND as inputs are exempt from
|
||
the chord rule). Default field: number/QWERTY rows = upper MFD bank,
|
||
home/bottom rows = lower bank, F1–F12 = Secondary/Screen, arrows+Space =
|
||
hat+main, and keyboard FLIGHT controls (operator 2026-07-22): numpad
|
||
8/2/4/6 = stick, 7/9 = pedals, LShift/LCtrl = throttle slew ±0.7/s,
|
||
numpad-5 = throttle zero; the internal keypad is unbound (mission-review
|
||
plumbing only). **PAUSE or SCROLL LOCK toggles capture** panel↔DOS
|
||
(toggling off releases everything; needed at the netnub loop / DOS
|
||
prompts); `togglekey:<sdl-name>` adds a third toggle key for keyboards
|
||
lacking both. Focus loss releases held keys (`RIO_HostFocusLost` in
|
||
`GFX_LosingFocus`). A bindings file in vRIO's grammar REPLACES the default
|
||
profile (conf `bindings:<path>`, else env `VWE_RIO_BINDINGS`; key-axis
|
||
modes `deflect|slew|rate|set`, sign may be a separate token; vRIO's .NET
|
||
key names are aliased so a vRIO `bindings.txt` ports over).
|
||
Conf: `serial1=rio [pad:<n>] [inverty] [bindings:<path>]
|
||
[togglekey:<key>] [rxpollus:<us>] [rxburst:<n>]`. **Additive** — real pods keep `directserial realport:COM1`;
|
||
vRIO-over-pipe keeps `namedpipe pipe:vrio`. Confs: `net_loop_rio.conf` /
|
||
`net_rp_rio.conf`; `pod-launch --rio`. B0 (pad) LIVE-CONFIRMED 2026-07-22
|
||
from the repacked dist; keyboard-field live test pending.
|
||
|
||
## Applying to a DOSBox-X source checkout
|
||
|
||
Tested against DOSBox-X `v2026.06.02`, MSYS2 mingw64.
|
||
|
||
1. Copy the devices in:
|
||
```
|
||
cp emulator/vpx-device/vpxlog.cpp emulator/src/src/hardware/
|
||
cp emulator/vpx-device/emu8k.cpp emulator/src/src/hardware/
|
||
cp emulator/vpx-device/emu8k.h emulator/src/src/hardware/
|
||
cp emulator/vpx-device/emu8k_shim.h emulator/src/src/hardware/
|
||
cp emulator/vpx-device/vweawe.cpp emulator/src/src/hardware/
|
||
```
|
||
2. Add them to the hardware build — in `src/src/hardware/Makefile.am`, append
|
||
`vpxlog.cpp emu8k.cpp vweawe.cpp` to `libhardware_a_SOURCES` (we inserted
|
||
them after `glide.cpp`; if you edit the generated `Makefile`/`Makefile.in`
|
||
by hand instead of re-running automake, also mirror the `$(OBJEXT)` list,
|
||
the `.Po` depfile list, and the depfile include markers — grep for how
|
||
`vpxlog` appears and copy the pattern).
|
||
3. Call the inits — in `src/src/gui/sdlmain.cpp`, declare `void VPXLOG_Init();`
|
||
and `void VWEAWE_Init();` next to the other `*_Init()` prototypes; call
|
||
`VPXLOG_Init();` right after `GLIDE_Init();` and `VWEAWE_Init();` right
|
||
after `SBLASTER_Init();` (it needs the mixer initialized).
|
||
3b. The namedpipe serial backend:
|
||
```
|
||
cp emulator/vpx-device/serialnamedpipe.cpp emulator/src/src/hardware/serialport/
|
||
cp emulator/vpx-device/serialnamedpipe.h emulator/src/src/hardware/serialport/
|
||
```
|
||
plus four small stock edits:
|
||
- `src/src/hardware/serialport/Makefile.am`: append
|
||
`serialnamedpipe.cpp serialnamedpipe.h` to `libserial_a_SOURCES`
|
||
(same generated-Makefile caveat as step 2).
|
||
- `src/include/serialport.h`: add `SERIAL_TYPE_NAMED_PIPE` to
|
||
`SerialTypesE` under `#if defined(WIN32)` (before
|
||
`SERIAL_TYPE_DIRECT_SERIAL`).
|
||
- `src/src/hardware/serialport/serialport.cpp`: `#include
|
||
"serialnamedpipe.h"`; add the `type=="namedpipe"` case to BOTH dispatch
|
||
switches (SERIALPORTS ctor + the SERIAL command) and `"namedpipe"` to
|
||
the `serialTypes[]` string table — grep for how `"file"` appears and
|
||
copy the pattern.
|
||
- `src/src/dosbox.cpp`: add `"namedpipe"` to the `serials[]` allowed-values
|
||
list (without this the config parser silently falls back to `dummy`).
|
||
3c. The in-fork RIO serial backend (needs SDL2 — the whole-program link already
|
||
pulls SDL2 in, and the vendored SDL2 include dir is on the global compile
|
||
flags, so no per-subdir cflag edits):
|
||
```
|
||
cp emulator/vpx-device/serialrio.cpp emulator/src/src/hardware/serialport/
|
||
cp emulator/vpx-device/serialrio.h emulator/src/src/hardware/serialport/
|
||
```
|
||
plus the same four stock edits as the namedpipe backend:
|
||
- `src/src/hardware/serialport/Makefile.am`: append
|
||
`serialrio.cpp serialrio.h` to `libserial_a_SOURCES` (same generated-
|
||
Makefile caveat as step 2).
|
||
- `src/include/serialport.h`: add `SERIAL_TYPE_RIO` to `SerialTypesE`
|
||
(UNguarded — SDL is cross-platform, unlike the WIN32-only namedpipe;
|
||
placed right after `SERIAL_TYPE_MOUSE`).
|
||
- `src/src/hardware/serialport/serialport.cpp`: `#include "serialrio.h"`;
|
||
add the `type=="rio"` case to BOTH dispatch switches and `"rio"` to the
|
||
`serialTypes[]` string table (copy the `namedpipe` pattern; keep it
|
||
unguarded).
|
||
- `src/src/dosbox.cpp`: add `"rio"` to the `serials[]` allowed-values list.
|
||
- `src/src/gui/sdlmain.cpp` (the B1 keyboard field, three small edits):
|
||
(a) declare the hooks above `GFX_LosingFocus`:
|
||
`bool RIO_HostKeyEvent(int sdl_scancode, bool pressed, bool repeat,
|
||
unsigned int sdl_mods); void RIO_HostFocusLost(void);`
|
||
(b) call `RIO_HostFocusLost();` inside `GFX_LosingFocus` right after
|
||
`MAPPER_LosingFocus();`
|
||
(c) in GFX_Events' SDL2 `case SDL_KEYDOWN: case SDL_KEYUP:` block, right
|
||
after the SCREEN_GAMELINK early-out, offer the event to
|
||
`RIO_HostKeyEvent((int)event.key.keysym.scancode, event.type ==
|
||
SDL_KEYDOWN, event.key.repeat != 0, event.key.keysym.mod)` and `break;`
|
||
when it returns true (consumed keys must never reach the mapper).
|
||
4. Build:
|
||
```
|
||
cd emulator/src
|
||
./build-mingw-sdl2 --enable-debug=heavy
|
||
```
|
||
Output: `src/src/dosbox-x.exe`.
|
||
|
||
## Running
|
||
|
||
```
|
||
set VPXLOG=C:\VWE\TeslaRel410\emulator\vpxlog.txt
|
||
emulator\src\src\dosbox-x.exe -conf emulator\capture.conf
|
||
python emulator\analyze_capture.py
|
||
```
|
||
|
||
With `VPXLOG` unset the device is inert and the build behaves like stock
|
||
DOSBox-X.
|