/* * 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, tdmd_seen, samples; 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_STRT | CSR0_TDMD); dot(3); /* long wait (600k iterations) with sparse CSR0 sampling: 600 polls */ tdmd_seen = 0; samples = 0; for (idle = 0; idle < 600000; idle++) { if ((idle % 1000) == 0) { r = csr_r(0); samples = samples + 1; if (r & CSR0_TDMD) { tdmd_seen = tdmd_seen + 1; } } } cell(0x1C0, csr_r(0)); /* CSR0 after the long wait */ cell(0x1E0, tdmd_seen); /* polls that still saw TDMD set (of `samples`) */ 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; }