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.
275 lines
7.2 KiB
C
275 lines
7.2 KiB
C
/*
|
|
* lance.c -- LANCE loopback probe using LOW-MEMORY rings.
|
|
*
|
|
* The OS's own init block (pointer at bit 0x23E7A0 -> byte 0xF80 on a live
|
|
* boot) puts its rings at byte 0x0A80 / 0x0E80 and stores only the LENGTH in
|
|
* the ring words' bits 13-15 (0xE000 / 0xA000), with no address bits above 16.
|
|
* MAME's model packs address bits 16+ into that word; if the real C-LANCE does
|
|
* not, a high ring address like 0x380028 is seen as 0x0028. This payload
|
|
* therefore places the block, rings and buffers below 64 KB.
|
|
*
|
|
* Memory used (BYTE addresses, well below 64 KB):
|
|
* 0x3000 init block (24 B) 0x3020 RX ring (8 * 8 B) 0x3060 TX ring (8 B)
|
|
* 0x3100 TX frame (24 B) 0x3200 RX buffer (2048 B)
|
|
*
|
|
* Result cells at 0x018C8000 (32-bit): 0x160/0x180 CSR0 samples,
|
|
* 0x200 RX RMD1, 0x220 RX MCNT, 0x240 TX TMD1, 0x1A0 payload-found flag.
|
|
*
|
|
* Panel rows (real unit, 256-byte stride): y 670/700 = CSR0, 730 = TMD1,
|
|
* 760 = RMD1, 790 = MCNT; blocks y 850: left = payload returned, right = TX done.
|
|
*/
|
|
#define RAP 0x05900000
|
|
#define RDP 0x05800000
|
|
#define MARK 0x018C8000
|
|
#define VRAM_BASE 0x04000000
|
|
|
|
#define MODE_LOOP 0x0004
|
|
#define MODE_INTL 0x0040
|
|
#define CSR0_STOP 0x0004
|
|
#define CSR0_STRT 0x0002
|
|
#define CSR0_INIT 0x0001
|
|
#define CSR0_TDMD 0x0008
|
|
#define CSR0_IDON 0x0100
|
|
#define RMD1_OWN 0x8000
|
|
#define TMD1_OWN 0x8000
|
|
#define TMD1_ENP 0x0100
|
|
#define TMD1_STP 0x0200
|
|
|
|
#define B(byte) ((unsigned int)(byte) * 8) /* bit address of a byte */
|
|
#define INITB B(0x3000)
|
|
#define RXR B(0x3020)
|
|
#define TXR B(0x3060)
|
|
#define RXBUF B(0x3200)
|
|
#define TXBUF B(0x3180)
|
|
|
|
#define RING_ENTRIES 8
|
|
|
|
static void cell(off, v)
|
|
unsigned int off, v;
|
|
{
|
|
*(unsigned int *)(MARK + off) = v;
|
|
}
|
|
|
|
static void w16(bit, v)
|
|
unsigned int bit, v;
|
|
{
|
|
*(unsigned short *)bit = v;
|
|
}
|
|
|
|
static unsigned int r16(bit)
|
|
unsigned int bit;
|
|
{
|
|
return *(unsigned short *)bit;
|
|
}
|
|
|
|
static void wb(bit, v)
|
|
unsigned int bit, v;
|
|
{
|
|
*(unsigned char *)bit = v;
|
|
}
|
|
|
|
static void rect(x, y, w, h, v)
|
|
unsigned int x, y, w, h, v;
|
|
{
|
|
unsigned int row, col;
|
|
for (row = 0; row < h; row++) {
|
|
for (col = 0; col < w; col = col + 8) {
|
|
*(unsigned char *)(VRAM_BASE + (y + row) * 2048 + x + col) = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void bits16(x, y, v)
|
|
unsigned int x, y, v;
|
|
{
|
|
unsigned int b;
|
|
for (b = 0; b < 16; b++) {
|
|
rect(x + b * 30, y, 24, 24, (v & (1 << b)) ? 0xFF : 0x00);
|
|
}
|
|
}
|
|
|
|
static void dot(n)
|
|
unsigned int n;
|
|
{
|
|
rect(60, 650 + n * 44, 24, 24, 0xFF);
|
|
}
|
|
|
|
static void settle()
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < 30; i++) {
|
|
}
|
|
}
|
|
|
|
static void csr_w(reg, v)
|
|
unsigned int reg, v;
|
|
{
|
|
*(unsigned short *)RAP = reg;
|
|
settle();
|
|
*(unsigned short *)RDP = v;
|
|
settle();
|
|
}
|
|
|
|
static unsigned int csr_r(reg)
|
|
unsigned int reg;
|
|
{
|
|
unsigned int v;
|
|
*(unsigned short *)RAP = reg;
|
|
settle();
|
|
v = *(unsigned short *)RDP;
|
|
settle();
|
|
return v;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
unsigned int i, r, idle, ok, rxmd1, txmd1, mcnt;
|
|
unsigned int s0[2];
|
|
|
|
cell(0x000, 1);
|
|
dot(0);
|
|
csr_w(0, CSR0_STOP);
|
|
|
|
rect(100, 650, 512, 256, 0x00);
|
|
rect(100, 650, 512, 4, 0xFF);
|
|
rect(100, 902, 512, 4, 0xFF);
|
|
rect(100, 650, 8, 256, 0xFF);
|
|
rect(608, 650, 8, 256, 0xFF);
|
|
|
|
/* init block: mode, PADR, LADRF, RX addr/len, TX addr/len */
|
|
w16(INITB + 0 * 16, MODE_LOOP | MODE_INTL);
|
|
w16(INITB + 1 * 16, 0x0008);
|
|
w16(INITB + 2 * 16, 0x162b);
|
|
w16(INITB + 3 * 16, 0x5a4b);
|
|
w16(INITB + 4 * 16, 0);
|
|
w16(INITB + 5 * 16, 0);
|
|
w16(INITB + 6 * 16, 0);
|
|
w16(INITB + 7 * 16, 0);
|
|
w16(INITB + 8 * 16, RXR >> 3 & 0xFFFF); /* ring addr low 16 */
|
|
w16(INITB + 9 * 16, 3 << 13); /* length only: 2^3 = 8 */
|
|
w16(INITB + 10 * 16, TXR >> 3 & 0xFFFF);
|
|
w16(INITB + 11 * 16, 3 << 13);
|
|
cell(0x040, INITB >> 3);
|
|
cell(0x060, RXR >> 3);
|
|
cell(0x080, TXR >> 3);
|
|
|
|
/* RX descriptors: all point at the same buffer */
|
|
for (i = 0; i < RING_ENTRIES; i++) {
|
|
w16(RXR + i * 64 + 0, (RXBUF >> 3) & 0xFFFF);
|
|
w16(RXR + i * 64 + 16, RMD1_OWN | (((RXBUF >> 3) >> 16) & 0xFF));
|
|
w16(RXR + i * 64 + 32, 0x0A0A); /* 1526 bytes */
|
|
w16(RXR + i * 64 + 48, 0);
|
|
}
|
|
|
|
/* one 24-byte frame (MAME's loopback accepts 8..32 bytes) */
|
|
wb(TXBUF + 0 * 8, 0x08); wb(TXBUF + 1 * 8, 0x00);
|
|
wb(TXBUF + 2 * 8, 0x2b); wb(TXBUF + 3 * 8, 0x16);
|
|
wb(TXBUF + 4 * 8, 0x4b); wb(TXBUF + 5 * 8, 0x5a);
|
|
wb(TXBUF + 6 * 8, 0x02); wb(TXBUF + 7 * 8, 0x00);
|
|
wb(TXBUF + 8 * 8, 0x00); wb(TXBUF + 9 * 8, 0x00);
|
|
wb(TXBUF + 10 * 8, 0x00); wb(TXBUF + 11 * 8, 0x01);
|
|
wb(TXBUF + 12 * 8, 0x88); wb(TXBUF + 13 * 8, 0xB5);
|
|
wb(TXBUF + 14 * 8, 0xDE); wb(TXBUF + 15 * 8, 0xAD);
|
|
wb(TXBUF + 16 * 8, 0xBE); wb(TXBUF + 17 * 8, 0xEF);
|
|
for (i = 18; i < 24; i++) {
|
|
wb(TXBUF + i * 8, i);
|
|
}
|
|
|
|
w16(TXR + 0, (TXBUF >> 3) & 0xFFFF);
|
|
w16(TXR + 16, TMD1_OWN | TMD1_STP | TMD1_ENP | (((TXBUF >> 3) >> 16) & 0xFF));
|
|
w16(TXR + 32, 0x0FE8); /* 24 bytes */
|
|
w16(TXR + 48, 0);
|
|
|
|
csr_w(1, (INITB >> 3) & 0xFFFF);
|
|
csr_w(2, ((INITB >> 3) >> 16) & 0xFF);
|
|
csr_w(3, 0);
|
|
csr_w(0, CSR0_INIT);
|
|
dot(1);
|
|
|
|
r = 0;
|
|
for (idle = 0; idle < 20000; idle++) {
|
|
r = csr_r(0);
|
|
if (r & CSR0_IDON) {
|
|
break;
|
|
}
|
|
}
|
|
cell(0x140, r);
|
|
if (!(r & CSR0_IDON)) {
|
|
cell(0x000, 0xEE);
|
|
return 1;
|
|
}
|
|
dot(2);
|
|
for (idle = 0; idle < 50000; idle++) {
|
|
}
|
|
|
|
csr_w(0, CSR0_STRT);
|
|
for (idle = 0; idle < 50000; idle++) {
|
|
}
|
|
/* own the TX descriptor now, then kick with STRT|TDMD in ONE write:
|
|
* CSR0 is a command shadow, a bare TDMD write clears STRT. */
|
|
w16(TXR + 16, TMD1_OWN | TMD1_STP | TMD1_ENP | (((TXBUF >> 3) >> 16) & 0xFF));
|
|
csr_w(0, CSR0_TDMD); /* BARE TDMD: no STRT, no INEA */
|
|
dot(3);
|
|
s0[0] = csr_r(0);
|
|
|
|
for (idle = 0; idle < 100000; idle++) {
|
|
if (!(r16(RXR + 16) & RMD1_OWN)) {
|
|
dot(4);
|
|
break;
|
|
}
|
|
if (!(r16(TXR + 16) & TMD1_OWN)) {
|
|
dot(4);
|
|
}
|
|
}
|
|
s0[1] = csr_r(0);
|
|
|
|
rxmd1 = r16(RXR + 16);
|
|
mcnt = r16(RXR + 48) & 0x0FFF;
|
|
txmd1 = r16(TXR + 16);
|
|
ok = 0;
|
|
if (*(unsigned char *)(RXBUF + 12 * 8) == 0x88 &&
|
|
*(unsigned char *)(RXBUF + 13 * 8) == 0xB5 &&
|
|
*(unsigned char *)(RXBUF + 14 * 8) == 0xDE) {
|
|
ok = 1;
|
|
}
|
|
cell(0x160, s0[0]);
|
|
cell(0x180, s0[1]);
|
|
cell(0x200, rxmd1);
|
|
cell(0x220, mcnt);
|
|
cell(0x240, txmd1);
|
|
cell(0x1A0, ok);
|
|
|
|
bits16(120, 670, s0[0]);
|
|
bits16(120, 700, s0[1]);
|
|
bits16(120, 730, txmd1);
|
|
bits16(120, 760, rxmd1);
|
|
bits16(120, 790, mcnt);
|
|
if (ok) {
|
|
rect(120, 850, 24, 24, 0xFF);
|
|
}
|
|
if (!(txmd1 & TMD1_OWN)) {
|
|
rect(160, 850, 24, 24, 0xFF);
|
|
}
|
|
|
|
/* best effort: give the chip back to the OS */
|
|
{
|
|
unsigned int osblk;
|
|
osblk = *(unsigned int *)0x23E7A0;
|
|
if (osblk != 0) {
|
|
csr_w(0, CSR0_STOP);
|
|
csr_w(1, osblk & 0xFFFF);
|
|
csr_w(2, (osblk >> 16) & 0xFF);
|
|
csr_w(3, 0);
|
|
csr_w(0, CSR0_INIT);
|
|
for (idle = 0; idle < 10000; idle++) {
|
|
r = csr_r(0);
|
|
if (r & CSR0_IDON) {
|
|
break;
|
|
}
|
|
}
|
|
csr_w(0, 0x0042);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|