mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-31 12:05:05 +02:00
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.
122 lines
3.2 KiB
C
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;
|
|
}
|
|
}
|
|
}
|