Files
vt1200/harness/udpstream/lance_bcast.c
T
arti e51c7b94cf NETWORK_REPORT: the broadcast payload works on the real VT1200 (TX 5, RX 5)
Panel read off the unit after loading lance_bcast.coff with the host sending
0x88B5 broadcasts: TXD=5, RXF=5, ET=0x88B5, CSR0=0x0073 (STRT|TXON|RXON|INEA)
and REST=0x0073. First exchange with a real peer using only our own driver
code.

Two display bugs found by reading it: the glyph table had no 'X' (so TXD/RXF
printed as 'T D'/'R F') and the three MAC words were drawn on top of each
other. Both fixed: X added, MAC on three rows, REST moved to y=878.
2026-10-30 16:24:06 +02:00

416 lines
13 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 0x018D0000 /* after .text (which now reaches 0x018CACD0) */
#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 unsigned int rb(bit)
unsigned int bit;
{
return *(unsigned char *)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;
}
/* ---- 5x7 text -------------------------------------------------------
* Glyphs are built at runtime: this toolchain never runs .cinit, so a
* statically initialised table would arrive as zeros (same reason
* harness/c-hello builds its font in code).
*/
static unsigned char glyph[36][7];
static unsigned char lab_csr0[5], lab_csr1[5], lab_tmd1[5], lab_rmd1[5];
static unsigned char lab_mcnt[5], lab_rest[5], lab_ok[3], lab_yes[4], lab_no[3];
static unsigned char lab_mac1[5], lab_mac2[5];
static unsigned int gi(ch)
unsigned int ch;
{
if (ch >= '0' && ch <= '9') {
return ch - '0';
}
return 10 + (ch - 'A');
}
static void fset(i, r0, r1, r2, r3, r4, r5, r6)
unsigned int i, r0, r1, r2, r3, r4, r5, r6;
{
glyph[i][0] = r0; glyph[i][1] = r1; glyph[i][2] = r2; glyph[i][3] = r3;
glyph[i][4] = r4; glyph[i][5] = r5; glyph[i][6] = r6;
}
static void build_font()
{
fset(0, 0x0E,0x11,0x13,0x15,0x19,0x11,0x0E); /* 0 */
fset(1, 0x04,0x0C,0x04,0x04,0x04,0x04,0x0E); /* 1 */
fset(2, 0x0E,0x11,0x01,0x02,0x04,0x08,0x1F); /* 2 */
fset(3, 0x1F,0x02,0x04,0x02,0x01,0x11,0x0E); /* 3 */
fset(4, 0x02,0x06,0x0A,0x12,0x1F,0x02,0x02); /* 4 */
fset(5, 0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E); /* 5 */
fset(6, 0x06,0x08,0x10,0x1E,0x11,0x11,0x0E); /* 6 */
fset(7, 0x1F,0x01,0x02,0x04,0x08,0x08,0x08); /* 7 */
fset(8, 0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E); /* 8 */
fset(9, 0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C); /* 9 */
fset(gi('A'), 0x0E,0x11,0x11,0x1F,0x11,0x11,0x11);
fset(gi('B'), 0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E);
fset(gi('C'), 0x0E,0x11,0x10,0x10,0x10,0x11,0x0E);
fset(gi('D'), 0x1E,0x11,0x11,0x11,0x11,0x11,0x1E);
fset(gi('E'), 0x1F,0x10,0x10,0x1E,0x10,0x10,0x1F);
fset(gi('F'), 0x1F,0x10,0x10,0x1E,0x10,0x10,0x10);
fset(gi('K'), 0x11,0x12,0x14,0x18,0x14,0x12,0x11);
fset(gi('M'), 0x11,0x1B,0x15,0x15,0x11,0x11,0x11);
fset(gi('N'), 0x11,0x19,0x19,0x15,0x13,0x13,0x11);
fset(gi('O'), 0x0E,0x11,0x11,0x11,0x11,0x11,0x0E);
fset(gi('R'), 0x1E,0x11,0x11,0x1E,0x14,0x12,0x11);
fset(gi('S'), 0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E);
fset(gi('T'), 0x1F,0x04,0x04,0x04,0x04,0x04,0x04);
fset(gi('X'), 0x11,0x11,0x0A,0x04,0x0A,0x11,0x11);
fset(gi('Y'), 0x11,0x11,0x0A,0x04,0x04,0x04,0x04);
}
static void put4(a, c0, c1, c2, c3)
unsigned char *a;
unsigned int c0, c1, c2, c3;
{
a[0] = c0; a[1] = c1; a[2] = c2; a[3] = c3; a[4] = 0;
}
static void draw_glyph(x, y, ch)
unsigned int x, y, ch;
{
unsigned int r, c, row, idx;
idx = gi(ch);
for (r = 0; r < 7; r++) {
row = glyph[idx][r];
for (c = 0; c < 5; c++) {
if (row & (1 << (4 - c))) {
rect(x + c * 8, y + r * 3, 8, 3, 0xFF);
}
}
}
}
static void draw_text(x, y, s)
unsigned int x, y;
unsigned char *s;
{
while (*s) {
if (*s != ' ') {
draw_glyph(x, y, *s);
}
x = x + 48;
s = s + 1;
}
}
static void hex4(x, y, v)
unsigned int x, y, v;
{
unsigned int d, nib;
for (d = 0; d < 4; d++) {
nib = (v >> ((3 - d) * 4)) & 0xF;
if (nib < 10) {
draw_glyph(x + d * 48, y, '0' + nib);
} else {
draw_glyph(x + d * 48, y, 'A' + nib - 10);
}
}
}
int main()
{
unsigned int i, r, idle, n, seen, lastet, txd, rxf, ok;
unsigned int m0, m1, m2, txlen;
cell(0x000, 1);
build_font();
put4(lab_csr0, 'T', 'X', 'D', ' ');
put4(lab_csr1, 'R', 'X', 'F', ' ');
put4(lab_tmd1, 'E', 'T', ' ', ' ');
put4(lab_rmd1, 'M', 'A', 'C', ' ');
put4(lab_mcnt, 'C', 'S', 'R', '0');
put4(lab_rest, 'R', 'E', 'S', 'T');
put4(lab_mac1, 'M', 'A', 'C', '1');
put4(lab_mac2, 'M', 'A', 'C', '2');
dot(0);
/* black panel */
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);
/* --- chip init: normal mode, our MAC, accept everything --- */
csr_w(0, CSR0_STOP);
w16(INITB + 0 * 16, 0x0000); /* MODE: normal, TX/RX enabled */
w16(INITB + 1 * 16, 0x0008); /* PADR 08:00:2b:16:4b:5a */
w16(INITB + 2 * 16, 0x162b);
w16(INITB + 3 * 16, 0x5a4b);
w16(INITB + 4 * 16, 0); /* LADRF: no multicast */
w16(INITB + 5 * 16, 0);
w16(INITB + 6 * 16, 0);
w16(INITB + 7 * 16, 0);
w16(INITB + 8 * 16, (RXR >> 3) & 0xFFFF);
w16(INITB + 9 * 16, 3 << 13); /* 8 RX descriptors */
w16(INITB + 10 * 16, (TXR >> 3) & 0xFFFF);
w16(INITB + 11 * 16, 3 << 13); /* 8 TX descriptors */
/* --- arm the 8 RX descriptors, each with its own 1600-byte buffer --- */
for (i = 0; i < 8; i++) {
unsigned int buf;
buf = 0x4000 + i * 1600;
w16(RXR + i * 64 + 0, buf & 0xFFFF);
w16(RXR + i * 64 + 16, RMD1_OWN | ((buf >> 16) & 0xFF));
w16(RXR + i * 64 + 32, 0xF9C0); /* -1600 */
w16(RXR + i * 64 + 48, 0);
}
/* TX descriptors: idle, length set at transmit time */
for (i = 0; i < 8; i++) {
w16(TXR + i * 64 + 0, (TXBUF >> 3) & 0xFFFF);
w16(TXR + i * 64 + 16, ((TXBUF >> 3) >> 16) & 0xFF);
w16(TXR + i * 64 + 32, 0xF000);
w16(TXR + i * 64 + 48, 0);
}
csr_w(1, (INITB >> 3) & 0xFFFF);
csr_w(2, ((INITB >> 3) >> 16) & 0xFF);
csr_w(3, 0);
csr_w(0, 0x7F41);
for (idle = 0; idle < 20000; idle++) {
if (csr_r(0) & CSR0_IDON) {
break;
}
}
csr_w(0, 0x0042); /* STRT | INEA */
dot(1);
/* --- build a raw Ethernet broadcast frame (60 bytes, ethertype 0x88B5) --- */
for (i = 0; i < 6; i++) {
wb(TXBUF + i * 8, 0xFF); /* dst: broadcast */
}
wb(TXBUF + 6 * 8, 0x08); wb(TXBUF + 7 * 8, 0x00);
wb(TXBUF + 8 * 8, 0x2b); wb(TXBUF + 9 * 8, 0x16);
wb(TXBUF + 10 * 8, 0x4b); wb(TXBUF + 11 * 8, 0x5a);
wb(TXBUF + 12 * 8, 0x88); wb(TXBUF + 13 * 8, 0xB5);
for (i = 14; i < 60; i++) {
wb(TXBUF + i * 8, i);
}
txlen = 60;
dot(2);
/* --- send 6 broadcasts, one every ~160k iterations --- */
txd = 0;
for (n = 0; n < 6; n++) {
wb(TXBUF + 14 * 8, n); /* sequence byte */
w16(TXR + n * 64 + 32, (0x10000 - txlen) & 0xFFFF); /* -len */
w16(TXR + n * 64 + 16,
TMD1_OWN | TMD1_STP | TMD1_ENP | (((TXBUF >> 3) >> 16) & 0xFF));
csr_w(0, 0x0042 | CSR0_TDMD); /* STRT | INEA | TDMD */
for (idle = 0; idle < 8000; idle++) {
if (!(r16(TXR + n * 64 + 16) & TMD1_OWN)) {
txd = txd + 1;
break;
}
}
for (idle = 0; idle < 20000; idle++) {
}
}
dot(3);
/* --- listen: count what arrives, remember the last ethertype and MAC --- */
rxf = 0; lastet = 0; m0 = 0; m1 = 0; m2 = 0;
for (idle = 0; idle < 400000000; idle++) {
if (idle & 1023) {
continue; /* cheap spin; poll every 1024 */
}
if (rxf >= 5) {
break; /* enough frames: stop early */
}
for (i = 0; i < 8; i++) {
unsigned int buf, len;
if (r16(RXR + i * 64 + 16) & RMD1_OWN) {
continue;
}
buf = 0x4000 + i * 1600;
len = r16(RXR + i * 64 + 48) & 0x0FFF;
if (len > 0) {
rxf = rxf + 1;
/* buf is a BYTE address, rb() takes a BIT address */
lastet = (rb((buf + 12) * 8) << 8) | rb((buf + 13) * 8);
m0 = (rb((buf + 6) * 8) << 8) | rb((buf + 7) * 8);
m1 = (rb((buf + 8) * 8) << 8) | rb((buf + 9) * 8);
m2 = (rb((buf + 10) * 8) << 8) | rb((buf + 11) * 8);
}
/* re-arm this descriptor */
w16(RXR + i * 64 + 16, RMD1_OWN | ((buf >> 16) & 0xFF));
}
/* publish live counters so a dump *during* the listen window is useful */
cell(0x160, txd); cell(0x180, rxf); cell(0x200, lastet); cell(0x220, m0);
}
dot(4);
/* --- panel --- */
ok = 1; cell(0x1A0, ok);
cell(0x160, txd); cell(0x180, rxf); cell(0x200, lastet); cell(0x220, m0);
draw_text(104, 654, lab_csr0); hex4(312, 654, txd); /* TXD */
draw_text(104, 686, lab_csr1); hex4(312, 686, rxf); /* RXF */
draw_text(104, 718, lab_tmd1); hex4(312, 718, lastet); /* ET */
draw_text(104, 750, lab_rmd1); hex4(312, 750, m0); /* MAC word 0 */
draw_text(104, 782, lab_mac1); hex4(312, 782, m1); /* MAC word 1 */
draw_text(104, 814, lab_mac2); hex4(312, 814, m2); /* MAC word 2 */
draw_text(104, 846, lab_mcnt); hex4(312, 846, csr_r(0)); /* CSR0 */
/* give the chip back to the OS */
{
unsigned int osblk, tries, csr0res;
osblk = *(unsigned int *)0x23E7A0;
csr0res = 0;
if (osblk != 0) {
for (tries = 0; tries < 2; tries++) {
csr_w(0, CSR0_STOP);
for (idle = 0; idle < 200; idle++) {
}
csr_w(1, osblk & 0xFFFF);
csr_w(2, (osblk >> 16) & 0xFF);
csr_w(3, 0);
csr_w(0, 0x7F41);
for (idle = 0; idle < 20000; idle++) {
if (csr_r(0) & CSR0_IDON) {
break;
}
}
csr_w(0, 0x0042);
for (idle = 0; idle < 2000; idle++) {
}
csr0res = csr_r(0);
if (csr0res & 0x0010) {
break;
}
}
}
draw_text(104, 878, lab_rest); hex4(312, 878, csr0res);
}
/* stay here: the panel and the marker cells stay valid for inspection
* (a returned payload lets the OS reuse this memory) */
for (;;) {
}
return 0;
}