Files
vt1200/harness/mame/LANCE_REPORT.md
T
SDD 634246ecb1 harness: LANCE Ethernet -- register file, 8-byte address ROM, INT1 and byte<->bit DMA
Milestone 1 of docs/superpowers/plans/2026-10-16-lance-ethernet.md.

Modelled:
  * MAME am79c90 (AM79C90) at the firmware's two windows -- bit 0x5900000 is
    RAP, 0x5800000 is RDP; every other offset in those windows still falls
    through to the board model.
  * the Ethernet address ROM at bit 0x6800000, as EIGHT bytes.  The plan said
    six; the firmware's reader (0x3FDD7520) actually loops i = 0..7 and then
    validates a folded ones-complement checksum over bytes 0..5 against
    bytes 6..7 (0x3FDD78B0).  For the MAC 08:00:2b:16:4b:5a that is 0xC186, so
    the ROM is 08 00 2b 16 4b 5a c1 86.  A six-byte ROM returns expected = 0
    and fails LEVEL1/2 ERROR -> ETHERNET ROM CHECKSUM - 23.  The checksum
    bytes are derived from the firmware's own algorithm (documented in
    LANCE_REPORT.md); they are not read off a real PROM.
  * the interrupt on INT1, matching the ISR 0x3FA93400 and INTENB bit 1.
  * bus-master DMA, translating the LANCE's byte address to the TMS34010's
    bit-addressed main RAM (byte B -> word B>>1), cross-checked against the
    firmware's own CSR1 = 0xFF60 = RAM word 0x7FB0.
  * VT1200_LANCELOG to trace the register/ROM accesses.

Observed with the firmware's own diagnostics menu (driven by the real LK401
model; new menu_keys.lua): the M -- ETHERNET ROM TEST now PASSES.  The
C -- LANCE TEST runs the chip for real -- the firmware's init block
(mode 0x004C = LOOP|INTL|DTCR, PADR 08:00:2b:16:4b:5a) is read via the new DMA,
INIT completes (CSR0 = 0x6FE3, IDON), the chip transmits and receives -- but its
internal-loopback receive still reports a CRC error under MAME (RCV descriptor
1 = 0x4B00, message count 0).  Cause: MAME's receive() runs the FCS check when
MODE_LOOP && MODE_DTCR (am79c90.cpp:263) even though DTCR means no FCS was
appended.  No MAME change was made; quoted and reported in LANCE_REPORT.md.

The OS (skip gate) reads the ROM MAC, programs CSR1/2/3, INITs, sees IDON,
STRTs and polls CSR0; the Terminal Manager now shows Ethernet Address
08-00-2B-16-4B-5A (was 00-00-00-00-00-00) and LAT_08002B164B5A.

Baselines unchanged: no-env samples=198 (3 PCs), VT1200_IO_PATH_STUB samples=53
(75 PCs).  ROM md5 unchanged.  No MAME tree changes.
2026-10-16 16:39:21 +03:00

9.8 KiB
Raw Blame History

LANCE Ethernet — milestone 1 report

Date: 2026-10-16. Plan: docs/superpowers/plans/2026-10-16-lance-ethernet.md. Firmware vt1200_v22.bin md5 b46a3e442ccb86134d2356bd70d22fd2 (unmodified). MAME tree unmodified (see section 6). Configuration: bare run unless stated.

Milestone 1 target was the register interface and the address ROM. This report covers those, plus three things the firmware forced beyond them: the address ROM turned out to be eight bytes with a checksum, the interface is only half the Ethernet test (it also needs bus-master DMA), and running the firmware's own diagnostics menu was the only way to see the tests at all.


1. What was modelled

piece where how
LANCE register file bit 0x5900000 = RAP, 0x5800000 = RDP MAME am79c90 (AM79C90), offset 0 of each window; every other offset still falls through to the board model
bus-master DMA dma_in/dma_out LANCE byte address B → main-RAM word B >> 1
Ethernet address ROM bit 0x6800000 (byte 0xD00000) eight bytes, read as words with the byte in the low lane
interrupt intr_out → INT1 (line 0), .invert() matches the ISR 0x3FA93400 and INTENB bit 1 (0x3FA8C340)
logging VT1200_LANCELOG=<path> every RAP/RDP/ROM access with PC and time

The register map is confirmed by the firmware itself. io5800_5900_init (L3FDDCBE0) and lance_stop (L3FDDCA90) produce exactly the LANCE protocol, and the srl 0x3 on a bit address before loading CSR1/CSR2 proves the chip is byte addressed (plan section 1.1).

The DMA unit, cross-checked

The plan's own cross-check holds. The firmware programs the init block at CSR1 = 0xFF60; a RAM dump taken while its LANCE test runs shows the init block at byte 0xFF60 (main-RAM word 0x7FB0 = bit 0x7fb00). So dma_in(o) = m_mainram[o >> 1] is right, not o >> 2 or o >> 3.


2. The address ROM is eight bytes, with a checksum

The plan modelled six bytes (the MAC) and flagged the guide's "format and checksum" caveat as an open risk. That risk is real, and it is now resolved.

0x3FDD74B0 (called by the ROM test) is the reader:

3fdd7520: move a9,a11 ; sll 0x4,a11 ; add a13,a11   ; dest = local[i]
3fdd7560: move a9,a7  ; sll 0x4,a7
3fdd7590: add a5,a7                                  ; a7 = 0x6800000 + i*16
3fdd75a0: move *a7,a7,0 ; andi 0xff,a7               ; byte i (low lane)
3fdd7600: cmpi 0x7,a9 ; jrle L3FDD7520               ; i = 0..7  -> EIGHT bytes

Then a folded ones-complement checksum over bytes 0..5, compared to bytes 6..7:

3fdd7630: sub a10,a10                                ; sum = 0, i = 0
L3FDD7650:
3fdd7650: sll 0x1,a10 ; (>=0x10000 ? -=0xffff)       ; fold(sum<<1)
3fdd7710: move *a11,a11,0 ; sll 0x8,a11 ; add a10,a11
3fdd7780: move *a7,a10,0 ; add a10,a11 ; move a11,a10 ; fold
3fdd7820: addk 2,a9 ; cmpi 0x5,a9 ; jrle L3FDD7650   ; pairs (0,1),(2,3),(4,5)
3fdd7860: cmpi 0xffff,a10 ; (== ? =0)
3fdd78b0: expected = (rom[6]<<8)|rom[7]
3fdd7920: cmp a10,a9 ; jreq pass ... else a8 = 1

For the MAC 08 00 2b 16 4b 5a this yields 0xC186, so the ROM is:

08 00 2b 16 4b 5a c1 86

A six-byte ROM returns expected = 0 and fails LEVEL1/2 ERROR -> ETHERNET ROM CHECKSUM - 23 (string at bit 0x3FDCA688). With the eight-byte ROM the test passes (section 4).

Caveat, stated plainly. Bytes 6–7 are derived from the firmware's own algorithm; they are not read off a physical PROM. A real PROM carries the same value because it must validate, so this is a self-consistent ROM, not a fabricated verdict — the firmware still does the read, the sum and the compare. But the test passing is a consistency check of this model, not independent evidence that a real VT1200 PROM holds c1 86.


3. The OS programs the chip (skip gate)

VT1200_SKIP_LEVEL1=1, VT1200_LANCELOG. Excerpt (PC, access):

3F9D2880  RAP=0                 lance_stop
3F9D28D0  RDP=0004              CSR0 STOP
3F9D2A20  read 08 00 2B 16 4B 5A   OS copies the ROM MAC into its own buffer
3F9D4380  RAP=0 / RDP=0004      lance_stop
3F9D4420  RAP=1 / RDP=0F80      CSR1 = init block (byte 0xFF80)
3F9D44E0  RAP=2 / RDP=0000      CSR2
3F9D45E0  RAP=3 / RDP=0000      CSR3
3F9D4680  RAP=0 / RDP=0001      CSR0 = INIT
3F9D4720  RAP=0 / RDP=0181      read CSR0: IDON|INTR|INIT  -> INIT completed
3F9D4900  RDP=0042              CSR0 = STRT|INEA
...

So the OS completes INIT (IDON set), starts the chip and runs its interrupt path. What it does with the interface is therefore: read the MAC ROM → program CSRs 1/2/3 → INIT → wait for IDON → STRT → poll CSR0 in its ISR. With no network peer it prints Could not determine IP address after 5 retries / TCP/IP is now down. (a missing address, which the plan allows) and the Terminal Manager now shows the real hardware address:

Ethernet Address   08-00-2B-16-4B-5A
LAT Host Name      LAT_08002B164B5A

(Previously 00-00-00-00-00-00 / LAT_000000000000.)


4. The firmware's own Ethernet tests

The level-0 diagnostics menu (VERSION 2.08 - SELECTED TEST TYPE:) is reached in a bare run and offers C - LANCE TEST and M - ETHERNET ROM TEST. It is driven by the real LK401 model, so the tests run in the firmware's own context — no jumping into routines. harness/mame/menu_keys.lua presses the keycaps via the keyboard I/O ports:

M=/home/arti/code/k-space/mame0289/vt1200
MENU_KEYS=M,Z MENU_AT=300 MENU_LAST=2600 VT1200_DUMP_FRAMES=1200 \
VT1200_DUMP_DIR=. $M vt1200 -rompath harness/mame/roms -video none \
  -sound none -nothrottle -debug -debugger none \
  -autoboot_script harness/mame/menu_keys.lua -autoboot_delay 0
test before after
M — Ethernet ROM test LEVEL1/2 ERROR -> ETHERNET ROM CHECKSUM - 23 passes; menu returns, no error banner
C — LANCE test LEVEL1/2 ERROR -> LANCE - 24/34, ERROR RESULT 20000032, CSR REG VALUE 00000000, RCV DESCR 1 00008000 still fails, but far deeper: ERROR RESULT 00000050, CSR REG VALUE 00006FE3, RCV DESCR 1 00004B00, XMIT DESCR 1 00000301

The ROM test now reads all eight bytes and validates the checksum.

The LANCE test's register/INIT stage passes; the loopback does not

With DMA bound, the C - LANCE TEST runs the chip for real. The init block the firmware builds (RAM byte 0xFF60, from a VT1200_DUMP_RAM dump) is:

mode    = 0x004C          MODE_LOOP | MODE_INTL | MODE_DTCR
PADR    = 08:00:2b:16:4b:5a         <- the ROM MAC, copied in
LADRF   = 0
RX ring = 0x00FEE0 (len 8)   TX ring = 0x00FF20 (len 8)

The chip reads that block through the (new) DMA, completes INIT (CSR0 = 0x6FE3, IDON set), transmits (XMIT DESCR 1 = 0x0301, OWN cleared, no transmit errors) and receives — but the receive descriptor is RCV DESCR 1 = 0x4B00 (ERR | CRC | STP | ENP) with RCV DESCR 3 (MCNT) = 0, so the test reports a failure. The failure is the loopback CRC.


5. The remaining failure: MAME's FCS check under MODE_DTCR

The firmware runs internal loopback with CRC generation disabled (MODE_DTCR, mode bit 3). MAME's receive() then does the FCS check:

// src/devices/machine/am79c90.cpp:263
if (!(m_mode & MODE_LOOP) || (m_mode & MODE_DTCR))
{
    u32 const crc = util::crc32_creator::simple(buf, length);
    if (~crc != FCS_RESIDUE) { ... m_rx_md[1] |= RMD1_ERR | RMD1_CRC; }
}

Because DTCR means no FCS was appended (transmit(), same file: append_fcs = !(m_mode & MODE_DTCR)), the receiver's CRC can never match and the descriptor always comes back ERR|CRC with message count 0. On the real terminal this self-test passes, so the real chip must not fail the receive CRC in internal loopback when it generated none — i.e. the check should be skipped whenever the frame carries no FCS, not only when MODE_LOOP && !MODE_DTCR.

No MAME change was made. Per the project conventions this is reported for a second reader, with the quote above, rather than edited. It is the next blocker for the C - LANCE TEST; the byte↔bit DMA itself is done and verified.

Also open (from the plan)

  • AM79C90 vs AM7990 was not settled by evidence. Both were tried and behave identically on this test, so AM79C90 is kept (it is the part in DEC's own kn01, the template the plan cites). get_buf_length is the only behavioural difference that matters, and the firmware's descriptor here does not distinguish them. A board photograph or a datasheet would settle it.
  • The level-0 "LANCE not stopped" check (0x3F201320, error 33) is behind DUART1 input-port bit 2 = high; the harness holds IP2 low (the value the blit-status test at 0x3F204AA0 needs), so that path is not taken and the check neither runs nor fails. A reset chip reads CSR0 = 0x0004 (STOP set), which is what the check tests, so it would pass if taken.

6. Reproduction and scope

cd /home/arti/code/k-space/vt1200
JOBS=8 harness/mame/build.sh

# register file + ROM + DMA, live:
M=/home/arti/code/k-space/mame0289/vt1200
VT1200_SKIP_LEVEL1=1 VT1200_LANCELOG=lance.log $M vt1200 \
  -rompath harness/mame/roms -video none -sound none -nothrottle \
  -debug -debugger none -autoboot_script harness/mame/progress.lua

# the firmware's own ROM test (passes):
MENU_KEYS=M,Z MENU_AT=300 VT1200_DUMP_FRAMES=1200 VT1200_DUMP_DIR=. \
  $M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \
  -debug -debugger none -autoboot_script harness/mame/menu_keys.lua

Regression baselines are unchanged by this work (the LANCE is only touched by lance_stop before the OS phase): progress.lua at 200 frames is still samples=198 (3 distinct PCs) with no env and samples=53 with VT1200_IO_PATH_STUB=1; the skip-gate Terminal Manager is unchanged except for the now-correct MAC. harness/mame/build.sh test passes. Nothing in vt1200_board.* or the ROM was touched.