mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +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.
94 lines
3.9 KiB
C
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 */
|