Files
vt1200/harness/udpstream/tcpstream.c
T
arti 874d1fbbf5 networking RE report + LANCE payload variants
harness/mame/NETWORK_REPORT.md: how the VT1200 firmware drives the LANCE,
from ROM disassembly and MAME traces. 50 sections, each claim tied to a bit
address, a trace count or a quoted source line. Covers the driver device
struct (0x23CBF0), init (0x3F9D4BC0), descriptor/DMA encoding, the live
handler (0x3F9D5B40) with per-status-bit dispatch, the RX poll (0x3F9D62B0),
ring arming (0x3F9D36C0), TX completion (0x3F9D8AF0) and the single transmit
path every protocol uses (0x3F6084E0 -> 0x3F9DC890 -> 0x3F9D4BC0), plus the
IP protocol dispatch (0x3F6270F0), ICMP (343-count chain), TCP state tables
and live TIME_WAIT, the ARP responder (0x3F6119A0) and the socket 4-tuple
demux confirmed by a memory dump.

It also records what was withdrawn: five successive explanations for the
real-silicon CSR0=0x1BB / TMD1=0x8338 result, and the descriptor-length
encoding bug found by comparing Linux 7990.c and NetBSD am7990.c (length must
be 0xF000 | -len; MAME masks the difference in get_buf_length).

harness/udpstream: the LANCE loopback/takeover payload and the variants used
to test the init/kick orderings in MAME (bare TDMD, STOP before INIT, double
INIT, long wait with CSR0 sampling, IDON clear, corrected descriptor length).
build-lance.sh takes LANCE_SRC/LANCE_OUT so the variants build from one script.
2026-10-30 01:25:21 +02:00

122 lines
3.2 KiB
C

/*
* tcpstream.c -- measure the firmware's TCP receive throughput.
*
* The terminal is the server: socket(SOCK_STREAM) -> bind -> listen -> accept,
* then read() into a buffer and copy the bytes into video RAM in order. One
* read() can return a whole socket-buffer's worth of bytes, so this can beat
* the per-datagram recvfrom cost -- by how much is the question.
*
* Host-written cells (before the load):
* 0x018C9000 byte limit (0 = unlimited)
* 0x018C9040 TCP port (0 = 7009)
*
* Markers at 0x018C8000:
* +0x000 stage: 1 entered, 2 socket, 3 bound, 4 listening, 5 accepted,
* 0xEE socket failed, 0xEF bind failed, 0xF0 listen failed,
* 0xF1 accept failed
* +0x020 fd, +0x040 bind ret, +0x060 listen ret, +0x080 accept ret
* +0x0A0 reads, +0x0C0 bytes read, +0x0E0 last read length, +0x100 port
*/
#include "romapi.h"
#define VRAM_BASE 0x04000000
#define VRAM_BYTES 0x40000 /* 256 KiB, the real unit's framebuffer */
#define PORT 7009
#define MARK 0x018C8000
#define LIMIT_CELL 0x018C9000
#define PORT_CELL 0x018C9040
static unsigned char sa[16];
static unsigned char from[16];
static unsigned char buf[1472];
static void cell(off, v)
unsigned int off, v;
{
*(unsigned int *)(MARK + off) = v;
}
static unsigned int getcell(off)
unsigned int off;
{
return *(unsigned int *)(MARK + off);
}
int main()
{
int fd, cfd, n, r;
unsigned int limit, port, off, total, reads, i;
cell(0x000, 1);
limit = *(unsigned int *)LIMIT_CELL;
port = *(unsigned int *)PORT_CELL;
if (port == 0) {
port = PORT;
}
cell(0x100, port);
/* per-process user area is uninitialised (see udpstream.c) */
{
unsigned int proc, user, k;
proc = *(unsigned int *)0x1B3900;
user = *(unsigned int *)(proc + 0x100);
for (k = 0; k < 0x100; k++) {
*(unsigned char *)(user + k * 8) = 0;
}
cell(0x140, user);
}
fd = socket_(AF_INET, SOCK_STREAM, 0);
cell(0x020, fd);
if (fd < 0) { cell(0x000, 0xEE); return 1; }
cell(0x000, 2);
sa_any(sa, port);
r = bind_(fd, sa, SOCKADDR_LEN);
cell(0x040, r);
if (r < 0) { cell(0x000, 0xEF); close_(fd); return 2; }
cell(0x000, 3);
r = listen_(fd, 1);
cell(0x060, r);
if (r < 0) { cell(0x000, 0xF0); close_(fd); return 3; }
cell(0x000, 4);
cfd = accept_(fd, 0, 0);
cell(0x080, cfd);
if (cfd < 0) { cell(0x000, 0xF1); close_(fd); return 4; }
cell(0x000, 5);
cell(0x0A0, 0);
cell(0x0C0, 0);
off = 0;
total = 0;
reads = 0;
for (;;) {
n = read_(cfd, buf, sizeof(buf));
cell(0x0E0, (unsigned int)n);
if (n <= 0) {
close_(cfd);
close_(fd);
return 5;
}
for (i = 0; i < (unsigned int)n; i++) {
*(unsigned char *)(VRAM_BASE + off * 8) = buf[i];
off = off + 1;
if (off >= VRAM_BYTES) {
off = 0;
}
}
total = total + (unsigned int)n;
reads = reads + 1;
cell(0x0A0, reads);
cell(0x0C0, total);
if (limit != 0 && total >= limit) {
close_(cfd);
close_(fd);
return 0;
}
}
}