Files
vt1200/harness/udpstream/romapi.h
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

94 lines
3.9 KiB
C

/*
* romapi.h -- the VT1200 firmware entry points a C payload may call.
*
* Every address here is a TMS34010 BIT address (= Ghidra byte address x 8).
* The evidence for each is dwt/notes/TCPIP.md (sockets) and dwt/notes/HEAP.md
* (allocator); the ones marked "verified live" have been called from a payload
* on real hardware, not just read out of the ROM.
*
* K&R C only. This compiler rejects ANSI prototypes (`int f(int a)` is a
* syntax error), so the declarations below are old-style: the parameter lists
* live in the comments, and the compiler checks nothing but the name. That is
* the price of using the 1987 toolchain; keep the comments honest.
*
* Calling convention: the C compiler and the firmware agree -- arguments are
* widened to 32 bits and pushed upward on the program stack (A14), first
* argument last, and the result comes back in A8. The thunks in entry.asm
* forward with `calla`, so the caller's already-pushed arguments pass through
* untouched and only A8 has to survive.
*/
#ifndef ROMAPI_H
#define ROMAPI_H
/* ---- ROM entry points -------------------------------------------------- */
#define ROM_SOCKET 0x3F5C9A00 /* Ghidra 07EB9340 verified live */
#define ROM_BIND 0x3F5CAA80 /* Ghidra 07EB9550 BOOTP caller */
#define ROM_LISTEN 0x3F5CBCD0 /* Ghidra 07EB979A inetd caller */
#define ROM_ACCEPT 0x3F5CCB40 /* Ghidra 07EB9968 inetd caller */
#define ROM_SENDTO 0x3F5D3D70 /* Ghidra 07EBA7AE verified live */
#define ROM_RECVFROM 0x3F5D7D50 /* Ghidra 07EBAFAA verified live */
#define ROM_READ 0x3F5D8E70 /* Ghidra 07EBB1CE rdebug / Xlib */
#define ROM_CLOSE 0x3F5E8930 /* Ghidra 07EBD126 verified live */
#define ROM_SETSOCKOPT 0x3F5DD260 /* Ghidra 07EBBA4C BOOTP / Xlib */
#define ROM_DELAY 0x3F833A20 /* Ghidra 07F66744 sleep(ms), telnet caller */
#define ROM_SELECT 0x3F871CE0 /* Ghidra 07F0E39C Xlib caller, candidate */
#define ROM_MALLOC 0x3F701230 /* Ghidra 07EE0246 verified live */
#define ROM_FREE 0x3F702440 /* Ghidra 07EE0488 allocator */
#define ROM_CREATE_PROC 0x3F706A40 /* Ghidra 07EE0D48 GUI work */
/* ---- thunks implemented in entry.asm ----------------------------------- */
/*
* int socket_(domain, type, protocol) -> fd, or -1
* int bind_(fd, sockaddr *, len) -> 0, or -1
* int recvfrom_(fd, buf, len, flags, from *, fromlen *)
* -> bytes, or -1
* int close_(fd) -> 0, or -1
* unsigned int malloc_(bytes) -> BIT pointer, or 0
* int free_(bit pointer) -> 0
*
* `from` is a 16-byte sockaddr the stack fills in; `fromlen` points to an
* int that must be 16 on entry (the call writes back the real length).
*/
int socket_();
int bind_();
int recvfrom_();
int close_();
int listen_();
int accept_();
int read_();
int select_();
int delay_();
unsigned int malloc_();
int free_();
/* ---- socket constants -------------------------------------------------- */
#define AF_INET 2
#define SOCK_DGRAM 2
#define SOCK_STREAM 1
#define IPPROTO_UDP 0
#define SOCKADDR_LEN 16
/*
* struct sockaddr, 16 bytes, NETWORK byte order:
* +0 u16 family (AF_INET = 2 -> bytes 00 02)
* +2 u16 port (high byte first, e.g. 7000 = 0x1B58 -> 1B 58)
* +4 u32 addr (0 = INADDR_ANY)
* +8 u8 zero[8]
*/
/* Fill sa (an unsigned char[16]) with AF_INET, `port`, and INADDR_ANY.
* `port` is in host order so callers can write 7000 rather than 0x1B58. */
static void sa_any(sa, port)
unsigned char *sa;
unsigned int port;
{
sa[0] = 0; sa[1] = AF_INET;
sa[2] = (port >> 8) & 0xFF;
sa[3] = port & 0xFF;
sa[4] = 0; sa[5] = 0; sa[6] = 0; sa[7] = 0;
sa[8] = 0; sa[9] = 0; sa[10] = 0; sa[11] = 0;
sa[12] = 0; sa[13] = 0; sa[14] = 0; sa[15] = 0;
}
#endif /* ROMAPI_H */