Phase 2 progress: boot handshake + i860 download solved; FIFO path mapped
The VPX responder device now drives production BattleTech v4.10 through the full transputer/i860 boot, far past the Phase 1 wall: - iserver boot handshake solved: feeds 3 well-formed 'version' (tag 42) iserver requests, satisfying startup_handshake()'s 3 transactions. - i860 download solved: parses the outbound framed renderer messages (vr_860args/code/data/bss) and absorbs them, staying byte-aligned. - echo-action reply model validated against board source (VRENDER/VR_REMOT.C reply() echoes the received action); device tracks and echoes the last outbound action generally. Remaining sub-protocol identified and characterized: after i860 boot the host switches to a FIFO fast path (OUTSW.ASM: 0x40 tag + REP OUTSW 16-bit words to FIFO port 0x154/0x155, gated by ok_to_fifo at 0x160). The current build stops at velocirender_sync because the device only implements the slow byte path. PHASE2-PROGRESS.md documents the exact next steps (FIFO transport + render loop -> Phase 3 OpenGL backend). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# Phase 2 — Boot Protocol Emulation: Progress
|
||||
|
||||
**Status: substantial progress; boot handshake + i860 download solved; render
|
||||
transport (FIFO fast path) identified as the remaining sub-protocol.**
|
||||
|
||||
The VPX device (`vpx-device/vpxlog.cpp`, env `VPX_RESPOND=1`) now drives the
|
||||
production **BattleTech v4.10** (ALPHA_1 image) through the entire transputer/i860
|
||||
boot sequence — far past the Phase 1 wall — and reaches the live renderer
|
||||
protocol. Each layer below was solved by reading the normative sources on the
|
||||
drive (`sda4/DPL3/VR_COMMS.C`, `DPL_HOST.C`, `VRENDER/VR_REMOT.C`, `LINKIO.C`,
|
||||
`OUTSW.ASM`) and iterating against the running binary.
|
||||
|
||||
## Solved
|
||||
|
||||
### 1. iserver boot handshake
|
||||
After the monitor (`VRENDMON.BTL`) download, `boot_xputer()` calls
|
||||
`startup_handshake()`, which does **3 iserver transactions** (Phil's comment in
|
||||
`VR_COMMS.C`: "the C run-time boots up and does 3 iserver transactions"). Each
|
||||
is a `receive_protocol()` read of a 4-byte little-endian length/route word
|
||||
(bit31=iserver, low16=payload length) + payload, answered by `iserver_action()`;
|
||||
only success is checked. The device feeds **3 well-formed iserver "version"
|
||||
requests** (tag 42, no request-content dependency) and the handshake completes.
|
||||
|
||||
### 2. i860 renderer download
|
||||
`boot_860()` then streams the i860 image (`VRNOSTEX.MNG`) as framed renderer
|
||||
messages on the slow byte path: `vr_860args` (21), `vr_860code` (18, ×118),
|
||||
`vr_860data` (19, ×37), `vr_860bss` (20, ×9). The device parses the outbound
|
||||
frame stream (`[length_word:4][action:4][data]`, `nb = length_word & 0xffff`)
|
||||
and absorbs them, staying byte-aligned throughout.
|
||||
|
||||
### 3. The echo-action reply model (validated against board source)
|
||||
Renderer request/reply calls (`velocirender_create`/`delete`/`flush`/`sync` in
|
||||
`DPL_HOST.C`) send an action and require the **same action echoed back**. The
|
||||
board side confirms this exactly: `VRENDER/VR_REMOT.C remote_velocirender()`
|
||||
replies with its `data` buffer whose first word is still the received action
|
||||
(`reply()` → `dN_send`). The device tracks the last outbound action and echoes
|
||||
it; this is the general model for every render call, not a one-off.
|
||||
|
||||
## The remaining sub-protocol: the FIFO fast path
|
||||
|
||||
After the i860 boots, the host stops using the slow byte path and switches to a
|
||||
**FIFO fast path** for the render protocol. This is why the current build still
|
||||
stops at `velocirender_sync` — the device only implements the slow path
|
||||
(0x150–0x153).
|
||||
|
||||
Discovered mechanics (from the capture + `OUTSW.ASM` + `LINKIO.C`):
|
||||
|
||||
- The VPX board is **dual-register**: beyond the C012 slow link
|
||||
(0x150 inputData / 0x151 outputData / 0x152 inputStatus / 0x153 outputStatus),
|
||||
there is a **FIFO data port** reached at **0x154/0x155** (observed) and an
|
||||
`ok_to_fifo` status at **0x160**.
|
||||
- `send_FIFO_protocol()` / `velocirender_transmit()` (when `use_fifo`) call the
|
||||
assembly `outsw()` (`OUTSW.ASM`): it writes a `0x40` "use fifo" tag byte, then
|
||||
`add dx,3` and `REP OUTSW` — pumping the message as **16-bit words** into the
|
||||
FIFO port. In the capture these land as alternating byte writes to 0x154/0x155
|
||||
and decode to the init/args string (e.g. `/device~0x150~/video~svga~…`).
|
||||
- The host switches to `use_fifo` once the board asserts `ok_to_fifo`
|
||||
(`fifo_ok_status()` reads 0x160 bit0); see the commented wait loop in
|
||||
`boot_xputer()`.
|
||||
|
||||
### Next step (concrete)
|
||||
Extend the device to implement the FIFO transport:
|
||||
1. Answer `ok_to_fifo` (0x160 bit0) at the right time so the host enables `use_fifo`.
|
||||
2. Accept FIFO writes: the `0x40` tag, then word-stream data at the FIFO port
|
||||
(0x154/0x155). Reassemble into `[length_word][action][data]` messages
|
||||
(same framing as the slow path).
|
||||
3. Serve FIFO reads for replies: `velocirender_receive()` in FIFO mode reads the
|
||||
reply (length_word + `[action][node]`) from the FIFO/status ports. Echo the
|
||||
sent action (model already validated), returning meaningful node handles for
|
||||
`vr_create` so later `vr_delete`/geometry ops have valid remotes.
|
||||
4. Then the render loop (`vr_draw_scene` + frame-ack, `velocirender_frameack()`)
|
||||
— the Phase 3 boundary, where geometry/material/texture commands can be
|
||||
decoded into the OpenGL backend (formats already implemented in
|
||||
`restoration/divformats.py`).
|
||||
|
||||
## Reproduce
|
||||
|
||||
```
|
||||
set VPXLOG=C:\VWE\TeslaRel410\emulator\vpxresp.txt
|
||||
set VPX_RESPOND=1
|
||||
emulator\src\src\dosbox-x.exe -conf emulator\respond.conf
|
||||
```
|
||||
|
||||
Console progression now: `BattleTech v4.10` → `BTL4Application` → RIO check →
|
||||
(handshake, i860 download succeed) → `unexpected action N in velocirender_sync`
|
||||
(the FIFO-path render handshake, the next target). The log's `#` note lines
|
||||
trace the device's decisions (handshake requests fed, frames parsed, actions
|
||||
echoed, mystery ports resolved to 0x154/0x155).
|
||||
@@ -0,0 +1,26 @@
|
||||
[sdl]
|
||||
output=opengl
|
||||
[dosbox]
|
||||
memsize=32
|
||||
machine=svga_s3
|
||||
[cpu]
|
||||
core=normal
|
||||
cputype=pentium
|
||||
cycles=20000
|
||||
[serial]
|
||||
serial1=disabled
|
||||
serial2=disabled
|
||||
[autoexec]
|
||||
mount c "image"
|
||||
c:
|
||||
set L4CONTROLS=RIO,KEYBOARD
|
||||
set L4TIMER=
|
||||
set BLASTER=A220 I5 D1 H5 P330 T6
|
||||
set VIDEOFORMAT=svga
|
||||
set TEMP=c:\
|
||||
set DPLARG=/tranny~.\vrendmon.btl~/i860~.\vrnostex.mng~/device~0x150~/video~svga~/pipes~1~/qual~0x14~/system_tex~0~
|
||||
32rtm.exe -x
|
||||
btl4opt.exe -egg test.egg
|
||||
32rtm.exe -u
|
||||
echo RESPOND-DONE
|
||||
pause
|
||||
+224
-65
@@ -1,26 +1,33 @@
|
||||
/* VPX link-adapter logging device (Tesla Rel 4.10 emulation, Phase 1)
|
||||
/* VPX link-adapter device (Tesla Rel 4.10 emulation)
|
||||
*
|
||||
* Impersonates the INMOS C012 transputer link adapter the Division VPX board
|
||||
* hung off the ISA bus, at its documented register map (host source:
|
||||
* sda4/DPL3/LINKIO.C, setLA):
|
||||
* hung off the ISA bus (host source: sda4/DPL3/LINKIO.C, setLA):
|
||||
*
|
||||
* base+0 (0x150) inputData R byte board->host
|
||||
* base+1 (0x151) outputData W byte host->board
|
||||
* base+2 (0x152) inputStatus R bit0 = inbound byte available
|
||||
* base+3 (0x153) outputStatus R bit0 = ready to accept outbound byte
|
||||
* base+16 (0x160) resetRoot W board reset strobe / ok_to_fifo
|
||||
* base+16 (0x160) resetRoot W board reset strobe
|
||||
* base+17 (0x161) analyseRoot W board analyse strobe
|
||||
*
|
||||
* Phase 1 goal is DISCOVERY, not emulation: log every access so we can capture
|
||||
* the game's outbound boot conversation (reset -> transputer monitor download
|
||||
* -> i860 code/data segments) from the shipped binary. Status reads are
|
||||
* answered so the game keeps writing:
|
||||
* - outputStatus -> always ready (bit0=1) so OUTBYTE proceeds
|
||||
* - inputStatus -> no data (bit0=0) so the game doesn't read phantom input
|
||||
* - inputData -> 0xFF (logged) if the game reads anyway
|
||||
* Two modes, selected by environment variables (device inert unless VPXLOG set):
|
||||
*
|
||||
* Enabled only when the environment variable VPXLOG is set (harmless when off).
|
||||
* Log file: $VPXLOG if it names a path, else "vpxlog.txt" in the CWD.
|
||||
* VPXLOG=<path> Phase 1: log every access to <path>.
|
||||
* VPX_RESPOND=1 Phase 2: also answer as the transputer would, so the
|
||||
* game gets past boot_xputer()'s startup_handshake().
|
||||
* VPX_HANDSHAKES=N number of iserver transactions to feed (default 3;
|
||||
* Phil's note in VR_COMMS.C: the transputer C runtime
|
||||
* does 3 iserver transactions at startup).
|
||||
*
|
||||
* Protocol reference: sda4/DPL3/VR_COMMS.C.
|
||||
* - receive_protocol(): host reads a 4-byte little-endian length/route word
|
||||
* (bit31=iserver, bits16-23=sender, low16=payload length nb<=1040) then nb
|
||||
* payload bytes. startup_handshake() does N such reads; each is answered by
|
||||
* iserver_action() and only success is checked -- so feeding N well-formed
|
||||
* iserver "version" requests (tag 42, no request-content dependency)
|
||||
* satisfies the handshake.
|
||||
* - The monitor (VRENDMON.BTL) and i860 (VRNOSTEX.MNG) segment downloads are
|
||||
* pure host->board writes; the device just absorbs them.
|
||||
*/
|
||||
|
||||
#include "dosbox.h"
|
||||
@@ -33,17 +40,105 @@
|
||||
|
||||
static const io_port_t VPX_BASE = 0x150;
|
||||
|
||||
static FILE *vpx_fp = NULL;
|
||||
static bool vpx_on = false;
|
||||
static FILE *vpx_fp = NULL; /* log file, or NULL */
|
||||
static bool vpx_respond = false;
|
||||
static int vpx_max_handshakes = 3;
|
||||
|
||||
/* ---- board->host FIFO (what the emulated board sends the game) ---------- */
|
||||
static unsigned char in_fifo[64];
|
||||
static int in_len = 0, in_pos = 0;
|
||||
|
||||
/* ---- boot state machine ------------------------------------------------- */
|
||||
enum Phase { P_INIT, P_HANDSHAKE, P_POSTBOOT };
|
||||
static Phase phase = P_INIT;
|
||||
static bool saw_write = false; /* game has written at least one byte */
|
||||
static int handshakes_fed = 0;
|
||||
|
||||
/* ---- outbound frame parser (renderer messages, post-handshake) ---------- *
|
||||
* Wire format (VR_COMMS.C velocirender_transmit, little-endian PC path):
|
||||
* [length_word:4][action:4][data:(nb-4)] where nb = length_word & 0xffff.
|
||||
* iserver responses keep the same nb = length_word & 0xffff, so byte-count
|
||||
* alignment is preserved even when one slips through. We track the action of
|
||||
* the last complete message so the reply can echo it (the renderer echoes the
|
||||
* action it received: see velocirender_create/delete/sync in DPL_HOST.C). */
|
||||
static void flush_run(void); /* fwd decl (logging, defined below) */
|
||||
|
||||
static bool parse_frames = false; /* true once handshake requests done */
|
||||
static int wf_need_len = 4; /* bytes still needed for length_word */
|
||||
static unsigned wf_lenbuf = 0, wf_lenshift = 0;
|
||||
static int wf_payload_left = -1; /* -1 => currently reading length_word */
|
||||
static int wf_action_pos = 0;
|
||||
static unsigned wf_action = 0;
|
||||
static int wf_last_nb = 0;
|
||||
static unsigned last_action = 0;
|
||||
|
||||
static void parse_out_byte(unsigned char v) {
|
||||
if (wf_payload_left < 0) { /* accumulating length_word */
|
||||
wf_lenbuf |= ((unsigned)v) << wf_lenshift;
|
||||
wf_lenshift += 8;
|
||||
if (--wf_need_len == 0) {
|
||||
int nb = (int)(wf_lenbuf & 0xffff);
|
||||
wf_lenbuf = 0; wf_lenshift = 0; wf_need_len = 4;
|
||||
wf_payload_left = nb > 0 ? nb : 0;
|
||||
wf_last_nb = nb;
|
||||
wf_action_pos = 0; wf_action = 0;
|
||||
if (wf_payload_left == 0) wf_payload_left = -1; /* empty msg */
|
||||
}
|
||||
} else if (wf_payload_left > 0) { /* reading payload */
|
||||
if (wf_action_pos < 4) {
|
||||
wf_action |= ((unsigned)v) << (wf_action_pos * 8);
|
||||
wf_action_pos++;
|
||||
}
|
||||
if (--wf_payload_left == 0) {
|
||||
last_action = wf_action; /* message complete */
|
||||
/* diagnostic: log small control frames (skip 512B i860 chunks) */
|
||||
if (vpx_fp && wf_last_nb <= 64) {
|
||||
flush_run();
|
||||
fprintf(vpx_fp, "# FRAME action=%u nb=%d\n", wf_action, wf_last_nb);
|
||||
fflush(vpx_fp);
|
||||
}
|
||||
wf_payload_left = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* A well-formed iserver "version" request (VR_COMMS.C iserver_action case 42):
|
||||
* length_word = nb | (sender<<16) | 0x80000000, little-endian; payload = tag.
|
||||
* nb is padded even for iserver; use nb=2, payload {0x2a,0x00}. sender=0. */
|
||||
static const unsigned char VERSION_REQUEST[6] = {
|
||||
0x02, 0x00, 0x00, 0x80, /* length_word 0x80000002 LE */
|
||||
0x2a, 0x00 /* tag 42 (version) + pad */
|
||||
};
|
||||
|
||||
static void queue_version_request(void) {
|
||||
memcpy(in_fifo, VERSION_REQUEST, sizeof VERSION_REQUEST);
|
||||
in_len = (int)sizeof VERSION_REQUEST;
|
||||
in_pos = 0;
|
||||
}
|
||||
|
||||
/* A minimal non-iserver renderer reply for velocirender_receive() (VR_COMMS.C):
|
||||
* top bit of length_word clear (non-iserver), nb>=8, first int32 of payload =
|
||||
* action code. The post-boot vr_init reply is ignored by the caller
|
||||
* (DPL_HOST.C), so any action != vr_draw_scene_action satisfies it. */
|
||||
static int postboot_acks = 0;
|
||||
static int vpx_max_postboot_acks = 40; /* safety cap; tune via env */
|
||||
static void queue_render_ack(unsigned char action) {
|
||||
unsigned char m[12] = {
|
||||
0x08, 0x00, 0x00, 0x00, /* length_word 0x00000008 (nb=8) */
|
||||
action, 0x00, 0x00, 0x00, /* payload[0..3] = action (LE) */
|
||||
0x00, 0x00, 0x00, 0x00 /* payload[4..7] = node (unused) */
|
||||
};
|
||||
memcpy(in_fifo, m, sizeof m);
|
||||
in_len = (int)sizeof m;
|
||||
in_pos = 0;
|
||||
}
|
||||
|
||||
/* ---- logging (run-length coalesced) ------------------------------------- */
|
||||
static unsigned long vpx_seq = 0;
|
||||
static io_port_t last_port = 0xFFFF; static unsigned last_val = ~0u;
|
||||
static bool last_write = false; static unsigned long last_run = 0;
|
||||
|
||||
/* Coalesce runs of identical status polls so the log shows the conversation,
|
||||
* not millions of "read status = ready" lines. */
|
||||
static io_port_t last_port = 0xFFFF;
|
||||
static unsigned last_val = 0xFFFFFFFF;
|
||||
static bool last_write = false;
|
||||
static unsigned long last_run = 0;
|
||||
|
||||
static char reg_name_buf[16];
|
||||
static const char *reg_name(io_port_t port) {
|
||||
switch (port - VPX_BASE) {
|
||||
case 0: return "inputData";
|
||||
@@ -52,55 +147,115 @@ static const char *reg_name(io_port_t port) {
|
||||
case 3: return "outputStatus";
|
||||
case 16: return "resetRoot";
|
||||
case 17: return "analyseRoot";
|
||||
default: return "?";
|
||||
default:
|
||||
snprintf(reg_name_buf, sizeof reg_name_buf, "port_%03X", (unsigned)port);
|
||||
return reg_name_buf;
|
||||
}
|
||||
}
|
||||
|
||||
static void vpx_flush_run(void) {
|
||||
static void flush_run(void) {
|
||||
if (last_run == 0 || vpx_fp == NULL) return;
|
||||
if (last_run == 1) {
|
||||
fprintf(vpx_fp, "%8lu %s %-13s 0x%02X\n",
|
||||
vpx_seq++, last_write ? "W" : "R", reg_name(last_port),
|
||||
last_val & 0xFF);
|
||||
} else {
|
||||
fprintf(vpx_fp, "%8lu %s %-13s 0x%02X x%lu\n",
|
||||
vpx_seq++, last_write ? "W" : "R", reg_name(last_port),
|
||||
last_val & 0xFF, last_run);
|
||||
}
|
||||
fflush(vpx_fp);
|
||||
last_run = 0;
|
||||
if (last_run == 1)
|
||||
fprintf(vpx_fp, "%8lu %s %-13s 0x%02X\n", vpx_seq++,
|
||||
last_write ? "W" : "R", reg_name(last_port), last_val & 0xFF);
|
||||
else
|
||||
fprintf(vpx_fp, "%8lu %s %-13s 0x%02X x%lu\n", vpx_seq++,
|
||||
last_write ? "W" : "R", reg_name(last_port), last_val & 0xFF, last_run);
|
||||
fflush(vpx_fp); last_run = 0;
|
||||
}
|
||||
|
||||
static void vpx_record(io_port_t port, unsigned val, bool write) {
|
||||
static void record(io_port_t port, unsigned val, bool write) {
|
||||
if (vpx_fp == NULL) return;
|
||||
/* Collapse identical consecutive accesses (typically status polls). */
|
||||
if (port == last_port && val == last_val && write == last_write) {
|
||||
last_run++;
|
||||
return;
|
||||
}
|
||||
vpx_flush_run();
|
||||
if (port == last_port && val == last_val && write == last_write) { last_run++; return; }
|
||||
flush_run();
|
||||
last_port = port; last_val = val; last_write = write; last_run = 1;
|
||||
/* Payload writes and reset/analyse strobes are the interesting events;
|
||||
* emit them immediately (don't wait for a differing access to flush). */
|
||||
io_port_t off = port - VPX_BASE;
|
||||
if (write && (off == 1 || off == 16 || off == 17))
|
||||
vpx_flush_run();
|
||||
if (write && (off == 1 || off == 16 || off == 17)) flush_run();
|
||||
}
|
||||
static void note(const char *msg) {
|
||||
if (vpx_fp) { flush_run(); fprintf(vpx_fp, "# %s\n", msg); fflush(vpx_fp); }
|
||||
}
|
||||
|
||||
/* ---- I/O handlers ------------------------------------------------------- */
|
||||
static Bitu vpx_read(Bitu port, Bitu /*iolen*/) {
|
||||
io_port_t off = (io_port_t)port - VPX_BASE;
|
||||
Bitu ret;
|
||||
Bitu ret = 0xFF;
|
||||
|
||||
switch (off) {
|
||||
case 2: ret = 0x00; break; /* inputStatus: no inbound data */
|
||||
case 3: ret = 0x01; break; /* outputStatus: always ready to send */
|
||||
default: ret = 0xFF; break; /* inputData / unknown: open-bus */
|
||||
case 3: /* outputStatus: always ready to accept a byte */
|
||||
ret = 0x01;
|
||||
break;
|
||||
|
||||
case 2: /* inputStatus: bit0 = inbound byte available */
|
||||
ret = 0x00;
|
||||
if (vpx_respond) {
|
||||
if (in_pos < in_len) {
|
||||
ret = 0x01; /* still draining a queued message */
|
||||
} else {
|
||||
/* FIFO empty: decide whether to start a new transaction. */
|
||||
if (phase == P_INIT && saw_write) phase = P_HANDSHAKE;
|
||||
if (phase == P_HANDSHAKE) {
|
||||
if (handshakes_fed < vpx_max_handshakes) {
|
||||
queue_version_request();
|
||||
handshakes_fed++;
|
||||
note("feeding iserver version request");
|
||||
if (handshakes_fed >= vpx_max_handshakes)
|
||||
parse_frames = true; /* i860 dl + init now framed */
|
||||
ret = 0x01;
|
||||
} else {
|
||||
phase = P_POSTBOOT;
|
||||
note("handshake complete; entering post-boot");
|
||||
}
|
||||
}
|
||||
if (phase == P_POSTBOOT && in_pos >= in_len) {
|
||||
/* game is reading -> expects a renderer reply
|
||||
* (first is the vr_init ack after i860 boot). */
|
||||
if (postboot_acks < vpx_max_postboot_acks) {
|
||||
queue_render_ack((unsigned char)(last_action & 0xff));
|
||||
postboot_acks++;
|
||||
if (vpx_fp) { flush_run();
|
||||
fprintf(vpx_fp, "# post-boot: echo action %u\n",
|
||||
last_action); fflush(vpx_fp); }
|
||||
ret = 0x01;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0: /* inputData: next byte from board->host FIFO */
|
||||
if (vpx_respond && in_pos < in_len) ret = in_fifo[in_pos++];
|
||||
else ret = 0xFF;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = 0xFF;
|
||||
break;
|
||||
}
|
||||
vpx_record((io_port_t)port, (unsigned)ret, false);
|
||||
record((io_port_t)port, (unsigned)ret, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vpx_write(Bitu port, Bitu val, Bitu /*iolen*/) {
|
||||
vpx_record((io_port_t)port, (unsigned)val, true);
|
||||
static bool warned_iolen = false;
|
||||
static void vpx_write(Bitu port, Bitu val, Bitu iolen) {
|
||||
io_port_t off = (io_port_t)port - VPX_BASE;
|
||||
if (iolen != 1 && !warned_iolen && vpx_fp) {
|
||||
warned_iolen = true; flush_run();
|
||||
fprintf(vpx_fp, "# NOTE: non-byte write iolen=%u port=0x%03X val=0x%X\n",
|
||||
(unsigned)iolen, (unsigned)port, (unsigned)val);
|
||||
fflush(vpx_fp);
|
||||
}
|
||||
record((io_port_t)port, (unsigned)val, true);
|
||||
|
||||
if (off == 16 && (val & 1)) {
|
||||
/* resetRoot asserted: board reset -> clear all state. */
|
||||
phase = P_INIT; saw_write = false; handshakes_fed = 0;
|
||||
postboot_acks = 0; in_len = in_pos = 0;
|
||||
parse_frames = false; wf_need_len = 4; wf_lenbuf = 0; wf_lenshift = 0;
|
||||
wf_payload_left = -1; wf_action_pos = 0; wf_action = 0; last_action = 0;
|
||||
note("board reset");
|
||||
} else if (off == 1) {
|
||||
saw_write = true; /* outputData: a download/response byte */
|
||||
if (parse_frames) parse_out_byte((unsigned char)val);
|
||||
}
|
||||
}
|
||||
|
||||
void VPXLOG_Init(void) {
|
||||
@@ -110,18 +265,22 @@ void VPXLOG_Init(void) {
|
||||
const char *path = (strchr(env, '/') || strchr(env, '\\') ||
|
||||
strchr(env, '.')) ? env : "vpxlog.txt";
|
||||
vpx_fp = fopen(path, "w");
|
||||
if (vpx_fp == NULL) {
|
||||
LOG_MSG("VPXLOG: could not open log file '%s'", path);
|
||||
return;
|
||||
}
|
||||
vpx_on = true;
|
||||
if (vpx_fp == NULL) { LOG_MSG("VPXLOG: cannot open '%s'", path); }
|
||||
|
||||
const char *r = getenv("VPX_RESPOND");
|
||||
vpx_respond = (r && r[0] && r[0] != '0');
|
||||
const char *h = getenv("VPX_HANDSHAKES");
|
||||
if (h && atoi(h) > 0) vpx_max_handshakes = atoi(h);
|
||||
|
||||
/* Cover 0x150..0x153 and 0x160..0x161 with one 18-port range. */
|
||||
IO_RegisterReadHandler(VPX_BASE, vpx_read, IO_MB, 18);
|
||||
IO_RegisterWriteHandler(VPX_BASE, vpx_write, IO_MB, 18);
|
||||
|
||||
fprintf(vpx_fp, "# VPX link-adapter access log, base 0x%03X\n", VPX_BASE);
|
||||
fprintf(vpx_fp, "# seq dir register value [run-length]\n");
|
||||
fflush(vpx_fp);
|
||||
LOG_MSG("VPXLOG: logging VPX link adapter at 0x%03X to '%s'", VPX_BASE, path);
|
||||
if (vpx_fp) {
|
||||
fprintf(vpx_fp, "# VPX link adapter, base 0x%03X, respond=%d handshakes=%d\n",
|
||||
VPX_BASE, vpx_respond ? 1 : 0, vpx_max_handshakes);
|
||||
fprintf(vpx_fp, "# seq dir register value [run]\n");
|
||||
fflush(vpx_fp);
|
||||
}
|
||||
LOG_MSG("VPXLOG: base 0x%03X respond=%d handshakes=%d log='%s'",
|
||||
VPX_BASE, vpx_respond ? 1 : 0, vpx_max_handshakes, vpx_fp ? path : "(none)");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user