mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
networking RE report + LANCE payload variants
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.
This commit is contained in:
1 parent
41b0979aef
commit
874d1fbbf5
24 files changed
+6365
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,6 @@
|
||||
# DOSBox build scratch: extracted 1987 SDK binaries, not project source
|
||||
work/
|
||||
|
||||
# generated listings/maps (the .coff payloads are committed)
|
||||
build/*.lst
|
||||
build/*.map
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/bin/sh
|
||||
# Build the raw LANCE receive probe.
|
||||
#
|
||||
# harness/udpstream/build.sh
|
||||
#
|
||||
# .text at bit 0x018C0000, .bss at 0x01C00000 (both inside the firmware's large
|
||||
# free block), entry _start, no .cinit (the runtime's autoinit pass does not
|
||||
# run -- see harness/c-hello/README.md).
|
||||
set -eu
|
||||
|
||||
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
WORK="$DIR/work"
|
||||
OUT="$DIR/build"
|
||||
IMG="$ROOT/tools/tms34010-sdk/1987-05 TMS34010 SDK/1987-11-06 TMS34010 Simulator-C Tools-ASM Tools.img"
|
||||
DOSBOX_CONF="$ROOT/harness/sdk-scratch/dosbox-bench.conf"
|
||||
|
||||
TEXT=018C0000
|
||||
BSS=01C00000
|
||||
|
||||
mkdir -p "$WORK" "$OUT"
|
||||
|
||||
if [ ! -x "$WORK/GSPCG.EXE" ]; then
|
||||
mcopy -s -n -i "$IMG" "::/A2/*" "$WORK/"
|
||||
mcopy -s -n -i "$IMG" "::/C2/*" "$WORK/"
|
||||
fi
|
||||
|
||||
SRC=${LANCE_SRC:-$DIR/lance.c}
|
||||
cp "$SRC" "$WORK/LANCE.C"
|
||||
cp "$DIR/romapi.h" "$WORK/ROMAPI.H"
|
||||
cp "$DIR/entry.asm" "$WORK/ENTRY.ASM"
|
||||
|
||||
{
|
||||
printf '%s\n' '-a'
|
||||
printf '%s\n' '-o LANCE.out'
|
||||
printf '%s\n' '-m LANCE.map'
|
||||
printf '%s\n' '-e _start'
|
||||
printf '%s\n' 'LANCE.obj'
|
||||
printf '%s\n' 'ENTRY.obj'
|
||||
printf '%s\n' '-l RTS.LIB'
|
||||
printf '%s\n' 'MEMORY'
|
||||
printf '%s\n' '{'
|
||||
printf '%s\n' " prog: origin = ${TEXT}h, length = 20000h"
|
||||
printf '%s\n' " ram: origin = ${BSS}h, length = 40000h"
|
||||
printf '%s\n' '}'
|
||||
printf '%s\n' 'SECTIONS'
|
||||
printf '%s\n' '{'
|
||||
printf '%s\n' ' .text: {} > prog'
|
||||
printf '%s\n' ' .data: {} > prog'
|
||||
printf '%s\n' ' .cinit: {} > prog'
|
||||
printf '%s\n' ' .bss: {} > ram'
|
||||
printf '%s\n' '}'
|
||||
} > "$WORK/LANCE.CMD"
|
||||
sed -i 's/$/\r/' "$WORK/LANCE.CMD"
|
||||
|
||||
rm -f "$WORK"/LANCE.OBJ "$WORK"/LANCE.OUT "$WORK"/LANCE.LST \
|
||||
"$WORK"/LANCE.MAP "$WORK"/LANCE.ASM "$WORK"/LANCE.CPP \
|
||||
"$WORK"/LANCE.IF "$WORK"/ENTRY.OBJ "$WORK"/ENTRY.LST
|
||||
|
||||
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
|
||||
timeout 240 dosbox -conf "$DOSBOX_CONF" -noprimaryconf -nolocalconf \
|
||||
-c "mount c $WORK" -c "c:" \
|
||||
-c "gspcpp LANCE" \
|
||||
-c "gspcc LANCE" \
|
||||
-c "gspcg LANCE" \
|
||||
-c "gspa LANCE LANCE.obj LANCE.lst -l" \
|
||||
-c "gspa ENTRY ENTRY.obj ENTRY.lst -l" \
|
||||
-c "gsplnk LANCE.cmd" \
|
||||
-c "exit" -exit > "$WORK/dosbox.log" 2>&1
|
||||
|
||||
if [ ! -f "$WORK/LANCE.OUT" ]; then
|
||||
echo "build.sh: GSPLNK produced no LANCE.OUT" >&2
|
||||
sed 's/\r$//' "$WORK/LANCE.MAP" 2>/dev/null | tail -20 >&2 || true
|
||||
for f in LANCE.LST ENTRY.LST; do
|
||||
sed 's/\r$//' "$WORK/$f" 2>/dev/null | grep -i 'error' | grep -vi 'no errors' >&2 || true
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
for f in LANCE.LST ENTRY.LST; do
|
||||
if sed 's/\r$//' "$WORK/$f" | grep -i 'error' | grep -qvi 'no errors'; then
|
||||
echo "build.sh: $f diagnostics:" >&2
|
||||
sed 's/\r$//' "$WORK/$f" | grep -in 'error' | grep -vi 'no errors' >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
OUTNAME=${LANCE_OUT:-lance.coff}
|
||||
cp "$WORK/LANCE.OUT" "$OUT/$OUTNAME"
|
||||
sed 's/\r$//' "$WORK/LANCE.MAP" > "$OUT/lance.map"
|
||||
sed 's/\r$//' "$WORK/LANCE.LST" > "$OUT/lance.lst"
|
||||
sed 's/\r$//' "$WORK/ENTRY.LST" > "$OUT/entry.lst"
|
||||
echo "build.sh: $OUT/$OUTNAME ($(wc -c < "$OUT/lance.coff") bytes)"
|
||||
grep -E "ENTRY POINT|^\.(text|cinit|bss)" "$OUT/lance.map" | head -8
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/bin/sh
|
||||
# Build the TCP throughput probe (C + ROM-API thunks).
|
||||
#
|
||||
# harness/udpstream/build.sh
|
||||
#
|
||||
# .text at bit 0x018C0000, .bss at 0x01C00000 (both inside the firmware's large
|
||||
# free block), entry _start, no .cinit (the runtime's autoinit pass does not
|
||||
# run -- see harness/c-hello/README.md).
|
||||
set -eu
|
||||
|
||||
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
WORK="$DIR/work"
|
||||
OUT="$DIR/build"
|
||||
IMG="$ROOT/tools/tms34010-sdk/1987-05 TMS34010 SDK/1987-11-06 TMS34010 Simulator-C Tools-ASM Tools.img"
|
||||
DOSBOX_CONF="$ROOT/harness/sdk-scratch/dosbox-bench.conf"
|
||||
|
||||
TEXT=018C0000
|
||||
BSS=01C00000
|
||||
|
||||
mkdir -p "$WORK" "$OUT"
|
||||
|
||||
if [ ! -x "$WORK/GSPCG.EXE" ]; then
|
||||
mcopy -s -n -i "$IMG" "::/A2/*" "$WORK/"
|
||||
mcopy -s -n -i "$IMG" "::/C2/*" "$WORK/"
|
||||
fi
|
||||
|
||||
cp "$DIR/tcpstream.c" "$WORK/TCPSTRM.C"
|
||||
cp "$DIR/romapi.h" "$WORK/ROMAPI.H"
|
||||
cp "$DIR/entry.asm" "$WORK/ENTRY.ASM"
|
||||
|
||||
{
|
||||
printf '%s\n' '-a'
|
||||
printf '%s\n' '-o TCPSTRM.out'
|
||||
printf '%s\n' '-m TCPSTRM.map'
|
||||
printf '%s\n' '-e _start'
|
||||
printf '%s\n' 'TCPSTRM.obj'
|
||||
printf '%s\n' 'ENTRY.obj'
|
||||
printf '%s\n' '-l RTS.LIB'
|
||||
printf '%s\n' 'MEMORY'
|
||||
printf '%s\n' '{'
|
||||
printf '%s\n' " prog: origin = ${TEXT}h, length = 20000h"
|
||||
printf '%s\n' " ram: origin = ${BSS}h, length = 40000h"
|
||||
printf '%s\n' '}'
|
||||
printf '%s\n' 'SECTIONS'
|
||||
printf '%s\n' '{'
|
||||
printf '%s\n' ' .text: {} > prog'
|
||||
printf '%s\n' ' .data: {} > prog'
|
||||
printf '%s\n' ' .cinit: {} > prog'
|
||||
printf '%s\n' ' .bss: {} > ram'
|
||||
printf '%s\n' '}'
|
||||
} > "$WORK/TCPSTRM.CMD"
|
||||
sed -i 's/$/\r/' "$WORK/TCPSTRM.CMD"
|
||||
|
||||
rm -f "$WORK"/TCPSTRM.OBJ "$WORK"/TCPSTRM.OUT "$WORK"/TCPSTRM.LST \
|
||||
"$WORK"/TCPSTRM.MAP "$WORK"/TCPSTRM.ASM "$WORK"/TCPSTRM.CPP \
|
||||
"$WORK"/TCPSTRM.IF "$WORK"/ENTRY.OBJ "$WORK"/ENTRY.LST
|
||||
|
||||
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
|
||||
timeout 240 dosbox -conf "$DOSBOX_CONF" -noprimaryconf -nolocalconf \
|
||||
-c "mount c $WORK" -c "c:" \
|
||||
-c "gspcpp TCPSTRM" \
|
||||
-c "gspcc TCPSTRM" \
|
||||
-c "gspcg TCPSTRM" \
|
||||
-c "gspa TCPSTRM TCPSTRM.obj TCPSTRM.lst -l" \
|
||||
-c "gspa ENTRY ENTRY.obj ENTRY.lst -l" \
|
||||
-c "gsplnk TCPSTRM.cmd" \
|
||||
-c "exit" -exit > "$WORK/dosbox.log" 2>&1
|
||||
|
||||
if [ ! -f "$WORK/TCPSTRM.OUT" ]; then
|
||||
echo "build.sh: GSPLNK produced no TCPSTRM.OUT" >&2
|
||||
sed 's/\r$//' "$WORK/TCPSTRM.MAP" 2>/dev/null | tail -20 >&2 || true
|
||||
for f in TCPSTRM.LST ENTRY.LST; do
|
||||
sed 's/\r$//' "$WORK/$f" 2>/dev/null | grep -i 'error' | grep -vi 'no errors' >&2 || true
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
for f in TCPSTRM.LST ENTRY.LST; do
|
||||
if sed 's/\r$//' "$WORK/$f" | grep -i 'error' | grep -qvi 'no errors'; then
|
||||
echo "build.sh: $f diagnostics:" >&2
|
||||
sed 's/\r$//' "$WORK/$f" | grep -in 'error' | grep -vi 'no errors' >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
cp "$WORK/TCPSTRM.OUT" "$OUT/tcpstream.coff"
|
||||
sed 's/\r$//' "$WORK/TCPSTRM.MAP" > "$OUT/tcpstream.map"
|
||||
sed 's/\r$//' "$WORK/TCPSTRM.LST" > "$OUT/tcpstream.lst"
|
||||
sed 's/\r$//' "$WORK/ENTRY.LST" > "$OUT/entry.lst"
|
||||
echo "build.sh: $OUT/tcpstream.coff ($(wc -c < "$OUT/tcpstream.coff") bytes)"
|
||||
grep -E "ENTRY POINT|^\.(text|cinit|bss)" "$OUT/tcpstream.map" | head -8
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/bin/sh
|
||||
# Build the UDP frame-stream receiver (C + ROM-API thunks).
|
||||
#
|
||||
# harness/udpstream/build.sh
|
||||
#
|
||||
# .text at bit 0x018C0000, .bss at 0x01C00000 (both inside the firmware's large
|
||||
# free block), entry _start, no .cinit (the runtime's autoinit pass does not
|
||||
# run -- see harness/c-hello/README.md).
|
||||
set -eu
|
||||
|
||||
ROOT=$(cd "$(dirname "$0")/../.." && pwd)
|
||||
DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
WORK="$DIR/work"
|
||||
OUT="$DIR/build"
|
||||
IMG="$ROOT/tools/tms34010-sdk/1987-05 TMS34010 SDK/1987-11-06 TMS34010 Simulator-C Tools-ASM Tools.img"
|
||||
DOSBOX_CONF="$ROOT/harness/sdk-scratch/dosbox-bench.conf"
|
||||
|
||||
TEXT=018C0000
|
||||
BSS=01C00000
|
||||
|
||||
mkdir -p "$WORK" "$OUT"
|
||||
|
||||
if [ ! -x "$WORK/GSPCG.EXE" ]; then
|
||||
mcopy -s -n -i "$IMG" "::/A2/*" "$WORK/"
|
||||
mcopy -s -n -i "$IMG" "::/C2/*" "$WORK/"
|
||||
fi
|
||||
|
||||
cp "$DIR/udpstream.c" "$WORK/UDPSTRM.C"
|
||||
cp "$DIR/romapi.h" "$WORK/ROMAPI.H"
|
||||
cp "$DIR/entry.asm" "$WORK/ENTRY.ASM"
|
||||
|
||||
{
|
||||
printf '%s\n' '-a'
|
||||
printf '%s\n' '-o UDPSTRM.out'
|
||||
printf '%s\n' '-m UDPSTRM.map'
|
||||
printf '%s\n' '-e _start'
|
||||
printf '%s\n' 'UDPSTRM.obj'
|
||||
printf '%s\n' 'ENTRY.obj'
|
||||
printf '%s\n' '-l RTS.LIB'
|
||||
printf '%s\n' 'MEMORY'
|
||||
printf '%s\n' '{'
|
||||
printf '%s\n' " prog: origin = ${TEXT}h, length = 20000h"
|
||||
printf '%s\n' " ram: origin = ${BSS}h, length = 40000h"
|
||||
printf '%s\n' '}'
|
||||
printf '%s\n' 'SECTIONS'
|
||||
printf '%s\n' '{'
|
||||
printf '%s\n' ' .text: {} > prog'
|
||||
printf '%s\n' ' .data: {} > prog'
|
||||
printf '%s\n' ' .cinit: {} > prog'
|
||||
printf '%s\n' ' .bss: {} > ram'
|
||||
printf '%s\n' '}'
|
||||
} > "$WORK/UDPSTRM.CMD"
|
||||
sed -i 's/$/\r/' "$WORK/UDPSTRM.CMD"
|
||||
|
||||
rm -f "$WORK"/UDPSTRM.OBJ "$WORK"/UDPSTRM.OUT "$WORK"/UDPSTRM.LST \
|
||||
"$WORK"/UDPSTRM.MAP "$WORK"/UDPSTRM.ASM "$WORK"/UDPSTRM.CPP \
|
||||
"$WORK"/UDPSTRM.IF "$WORK"/ENTRY.OBJ "$WORK"/ENTRY.LST
|
||||
|
||||
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
|
||||
timeout 240 dosbox -conf "$DOSBOX_CONF" -noprimaryconf -nolocalconf \
|
||||
-c "mount c $WORK" -c "c:" \
|
||||
-c "gspcpp UDPSTRM" \
|
||||
-c "gspcc UDPSTRM" \
|
||||
-c "gspcg UDPSTRM" \
|
||||
-c "gspa UDPSTRM UDPSTRM.obj UDPSTRM.lst -l" \
|
||||
-c "gspa ENTRY ENTRY.obj ENTRY.lst -l" \
|
||||
-c "gsplnk UDPSTRM.cmd" \
|
||||
-c "exit" -exit > "$WORK/dosbox.log" 2>&1
|
||||
|
||||
if [ ! -f "$WORK/UDPSTRM.OUT" ]; then
|
||||
echo "build.sh: GSPLNK produced no UDPSTRM.OUT" >&2
|
||||
sed 's/\r$//' "$WORK/UDPSTRM.MAP" 2>/dev/null | tail -20 >&2 || true
|
||||
for f in UDPSTRM.LST ENTRY.LST; do
|
||||
sed 's/\r$//' "$WORK/$f" 2>/dev/null | grep -i 'error' | grep -vi 'no errors' >&2 || true
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
for f in UDPSTRM.LST ENTRY.LST; do
|
||||
if sed 's/\r$//' "$WORK/$f" | grep -i 'error' | grep -qvi 'no errors'; then
|
||||
echo "build.sh: $f diagnostics:" >&2
|
||||
sed 's/\r$//' "$WORK/$f" | grep -in 'error' | grep -vi 'no errors' >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
cp "$WORK/UDPSTRM.OUT" "$OUT/udpstream.coff"
|
||||
sed 's/\r$//' "$WORK/UDPSTRM.MAP" > "$OUT/udpstream.map"
|
||||
sed 's/\r$//' "$WORK/UDPSTRM.LST" > "$OUT/udpstream.lst"
|
||||
sed 's/\r$//' "$WORK/ENTRY.LST" > "$OUT/entry.lst"
|
||||
echo "build.sh: $OUT/udpstream.coff ($(wc -c < "$OUT/udpstream.coff") bytes)"
|
||||
grep -E "ENTRY POINT|^\.(text|cinit|bss)" "$OUT/udpstream.map" | head -8
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,136 @@
|
||||
; entry.asm -- C process entry + call-throughs to the firmware socket API.
|
||||
;
|
||||
; _start replaces boot.obj (whose exit() spins and whose _c_int00 ends in
|
||||
; `reti`): set up the C program stack, call main, return with `rets` to the
|
||||
; create_process trampoline.
|
||||
;
|
||||
; The socket wrappers are NOT a bare `calla`/`rets`. The firmware's wrappers
|
||||
; end in `RETS 2`: they pop the return address *and* two argument words, and
|
||||
; they restore A14 from a caller-pushed slot. A plain thunk therefore returns
|
||||
; with SP one word off, and its own `rets` pops the saved-A14 slot (a bit
|
||||
; pointer into .bss) as the return address -- the CPU then executes data and
|
||||
; the board lands in its reset/delay path. dwt/tcpip/tftpc.asm avoids this by
|
||||
; saving SP and A14 in cells around every ROM call; these thunks do the same.
|
||||
;
|
||||
; socket = 3F5C9A00h socket(domain, type, protocol) -> fd
|
||||
; bind = 3F5CAA80h bind(fd, sockaddr*, len) -> 0 / -1
|
||||
; recvfrom = 3F5D7D50h recvfrom(fd, buf, len, flags, &from, &fromlen) -> n
|
||||
; close = 3F5E8930h close(fd)
|
||||
; malloc = 3F701230h malloc(bytes) -> bit pointer
|
||||
; free = 3F702440h free(bit pointer)
|
||||
;
|
||||
; See dwt/notes/TCPIP.md.
|
||||
|
||||
.text
|
||||
.globl _start
|
||||
.globl _main
|
||||
_start:
|
||||
setf 32, 0, 1
|
||||
movi _cstack, a14
|
||||
move a14, a13
|
||||
calla _main
|
||||
rets
|
||||
|
||||
.globl _socket_
|
||||
_socket_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F5C9A00h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _bind_
|
||||
_bind_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F5CAA80h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _recvfrom_
|
||||
_recvfrom_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F5D7D50h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _close_
|
||||
_close_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F5E8930h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _malloc_
|
||||
_malloc_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F701230h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _delay_
|
||||
_delay_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F833A20h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _listen_
|
||||
_listen_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F5CBCD0h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _accept_
|
||||
_accept_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F5CCB40h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _read_
|
||||
_read_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F5D8E70h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _select_
|
||||
_select_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F871CE0h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.globl _free_
|
||||
_free_:
|
||||
move sp, @_sys_sp, 1
|
||||
move a14, @_sys_a14, 1
|
||||
calla 3F702440h
|
||||
move @_sys_sp, sp, 1
|
||||
move @_sys_a14, a14, 1
|
||||
rets 2
|
||||
|
||||
.bss _cstack, 32768, 16 ; 32768 bits = 4096 bytes
|
||||
.bss _sys_sp, 32, 16
|
||||
.bss _sys_a14, 32, 16
|
||||
.end
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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_STRT | CSR0_TDMD);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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, 0xFA0A); /* 0xF000 | -1526: top nibble must be ones */
|
||||
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, 0xFFE8); /* 0xF000 | -24: top nibble must be ones */
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
/* acknowledge IDON (write 1 to clear it) before starting, as Linux's
|
||||
* drivers/net/ethernet/amd/7990.c does */
|
||||
csr_w(0, CSR0_IDON);
|
||||
for (idle = 0; idle < 2000; idle++) {
|
||||
}
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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);
|
||||
csr_w(0, CSR0_INIT); /* second INIT before IDON: does the chip keep INIT set? */
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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_STOP); /* STOP immediately before INIT (datasheet order) */
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Host sender for the VT1200 udpstream spike.
|
||||
|
||||
Sends full 1280x1024 frames as 8-row datagrams:
|
||||
|
||||
+0 u16 start row (LE) +2 u16 row count +4 rows*160 bytes
|
||||
|
||||
Usage:
|
||||
python3 harness/udpstream/send.py --host 100.103.100.103 --fps 5
|
||||
python3 harness/udpstream/send.py --host 100.103.100.103 --fps 0 --seconds 5
|
||||
|
||||
The frame is a black field with a white bar (encoding the frame number), a
|
||||
moving box, and a border, so a screenshot proves the whole frame arrived and
|
||||
which frame it was.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
|
||||
W, H = 1280, 1024
|
||||
ROW_BYTES = W // 8 # 160
|
||||
ROWS_PER_PKT = 8
|
||||
PORT = 7000
|
||||
|
||||
|
||||
def frame(n: int) -> bytes:
|
||||
"""One 1-bpp frame, packed rows of ROW_BYTES, LSB-first."""
|
||||
rows = bytearray(H * ROW_BYTES)
|
||||
# border: rows 0,1,H-2,H-1 and columns 0..3 / W-4..W-1 white
|
||||
for y in (0, 1, H - 2, H - 1):
|
||||
rows[y * ROW_BYTES:(y + 1) * ROW_BYTES] = b"\xff" * ROW_BYTES
|
||||
for y in range(H):
|
||||
r = rows[y * ROW_BYTES:(y + 1) * ROW_BYTES]
|
||||
r[0] = 0xFF
|
||||
r[ROW_BYTES - 1] = 0xFF
|
||||
rows[y * ROW_BYTES:(y + 1) * ROW_BYTES] = r
|
||||
|
||||
# moving 128x128 box
|
||||
x0 = (n * 37) % (W - 128)
|
||||
y0 = (n * 23) % (H - 128)
|
||||
for y in range(y0, y0 + 128):
|
||||
base = y * ROW_BYTES
|
||||
for xb in range(x0 // 8, (x0 + 128) // 8):
|
||||
rows[base + xb] = 0xFF
|
||||
|
||||
# frame-number bar on row 64: 16 pixels of white per 16-frame step
|
||||
y = 64
|
||||
base = y * ROW_BYTES
|
||||
for i in range(n % 64):
|
||||
x = 32 + i * 16
|
||||
rows[base + (x // 8)] |= 0xFF
|
||||
rows[base + (x // 8) + 1] |= 0xFF
|
||||
|
||||
return bytes(rows)
|
||||
|
||||
|
||||
def main(argv: list[str]) -> int:
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("--host", default="100.103.100.103")
|
||||
ap.add_argument("--port", type=int, default=PORT)
|
||||
ap.add_argument("--fps", type=float, default=5.0,
|
||||
help="target frames/s; 0 = as fast as possible")
|
||||
ap.add_argument("--pkt-gap-ms", type=float, default=0.0,
|
||||
help="sleep between datagrams; gives the cooperative OS a "
|
||||
"chance to run (a continuous burst starves it)")
|
||||
ap.add_argument("--seconds", type=float, default=0,
|
||||
help="stop after N seconds (0 = run until Ctrl-C)")
|
||||
a = ap.parse_args(argv[1:])
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1 << 20)
|
||||
dst = (a.host, a.port)
|
||||
|
||||
n = sent_bytes = sent_pkts = 0
|
||||
t0 = last = time.monotonic()
|
||||
frames_at_last = 0
|
||||
|
||||
try:
|
||||
while True:
|
||||
f = frame(n)
|
||||
for r in range(0, H, ROWS_PER_PKT):
|
||||
payload = f[r * ROW_BYTES:(r + ROWS_PER_PKT) * ROW_BYTES]
|
||||
pkt = struct.pack("<HH", r, ROWS_PER_PKT) + payload
|
||||
sock.sendto(pkt, dst)
|
||||
sent_bytes += len(pkt)
|
||||
sent_pkts += 1
|
||||
if a.pkt_gap_ms:
|
||||
time.sleep(a.pkt_gap_ms / 1000.0)
|
||||
n += 1
|
||||
|
||||
now = time.monotonic()
|
||||
if now - last >= 1.0:
|
||||
rate = (n - frames_at_last) / (now - last)
|
||||
print(f"{n:6d} frames {rate:5.2f} fps "
|
||||
f"{sent_bytes / (now - t0) / 1e6:5.3f} MB/s "
|
||||
f"{sent_pkts / (now - t0):6.0f} pkt/s", flush=True)
|
||||
last = now
|
||||
frames_at_last = n
|
||||
if a.seconds and now - t0 >= a.seconds:
|
||||
break
|
||||
if a.fps > 0:
|
||||
sleep = t0 + n / a.fps - time.monotonic()
|
||||
if sleep > 0:
|
||||
time.sleep(sleep)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
dt = time.monotonic() - t0
|
||||
print(f"sent {n} frames, {sent_bytes} bytes in {dt:.1f}s "
|
||||
f"({sent_bytes / dt / 1e6:.2f} MB/s, {sent_pkts / dt:.0f} pkt/s)",
|
||||
flush=True)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main(sys.argv))
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* tcpstream.c -- measure the firmware's TCP receive throughput.
|
||||
*
|
||||
* The terminal is the server: socket(SOCK_STREAM) -> bind -> listen -> accept,
|
||||
* then read() into a buffer and copy the bytes into video RAM in order. One
|
||||
* read() can return a whole socket-buffer's worth of bytes, so this can beat
|
||||
* the per-datagram recvfrom cost -- by how much is the question.
|
||||
*
|
||||
* Host-written cells (before the load):
|
||||
* 0x018C9000 byte limit (0 = unlimited)
|
||||
* 0x018C9040 TCP port (0 = 7009)
|
||||
*
|
||||
* Markers at 0x018C8000:
|
||||
* +0x000 stage: 1 entered, 2 socket, 3 bound, 4 listening, 5 accepted,
|
||||
* 0xEE socket failed, 0xEF bind failed, 0xF0 listen failed,
|
||||
* 0xF1 accept failed
|
||||
* +0x020 fd, +0x040 bind ret, +0x060 listen ret, +0x080 accept ret
|
||||
* +0x0A0 reads, +0x0C0 bytes read, +0x0E0 last read length, +0x100 port
|
||||
*/
|
||||
#include "romapi.h"
|
||||
|
||||
#define VRAM_BASE 0x04000000
|
||||
#define VRAM_BYTES 0x40000 /* 256 KiB, the real unit's framebuffer */
|
||||
#define PORT 7009
|
||||
#define MARK 0x018C8000
|
||||
#define LIMIT_CELL 0x018C9000
|
||||
#define PORT_CELL 0x018C9040
|
||||
|
||||
static unsigned char sa[16];
|
||||
static unsigned char from[16];
|
||||
static unsigned char buf[1472];
|
||||
|
||||
static void cell(off, v)
|
||||
unsigned int off, v;
|
||||
{
|
||||
*(unsigned int *)(MARK + off) = v;
|
||||
}
|
||||
|
||||
static unsigned int getcell(off)
|
||||
unsigned int off;
|
||||
{
|
||||
return *(unsigned int *)(MARK + off);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int fd, cfd, n, r;
|
||||
unsigned int limit, port, off, total, reads, i;
|
||||
|
||||
cell(0x000, 1);
|
||||
limit = *(unsigned int *)LIMIT_CELL;
|
||||
port = *(unsigned int *)PORT_CELL;
|
||||
if (port == 0) {
|
||||
port = PORT;
|
||||
}
|
||||
cell(0x100, port);
|
||||
|
||||
/* per-process user area is uninitialised (see udpstream.c) */
|
||||
{
|
||||
unsigned int proc, user, k;
|
||||
proc = *(unsigned int *)0x1B3900;
|
||||
user = *(unsigned int *)(proc + 0x100);
|
||||
for (k = 0; k < 0x100; k++) {
|
||||
*(unsigned char *)(user + k * 8) = 0;
|
||||
}
|
||||
cell(0x140, user);
|
||||
}
|
||||
|
||||
fd = socket_(AF_INET, SOCK_STREAM, 0);
|
||||
cell(0x020, fd);
|
||||
if (fd < 0) { cell(0x000, 0xEE); return 1; }
|
||||
cell(0x000, 2);
|
||||
|
||||
sa_any(sa, port);
|
||||
r = bind_(fd, sa, SOCKADDR_LEN);
|
||||
cell(0x040, r);
|
||||
if (r < 0) { cell(0x000, 0xEF); close_(fd); return 2; }
|
||||
cell(0x000, 3);
|
||||
|
||||
r = listen_(fd, 1);
|
||||
cell(0x060, r);
|
||||
if (r < 0) { cell(0x000, 0xF0); close_(fd); return 3; }
|
||||
cell(0x000, 4);
|
||||
|
||||
cfd = accept_(fd, 0, 0);
|
||||
cell(0x080, cfd);
|
||||
if (cfd < 0) { cell(0x000, 0xF1); close_(fd); return 4; }
|
||||
cell(0x000, 5);
|
||||
|
||||
cell(0x0A0, 0);
|
||||
cell(0x0C0, 0);
|
||||
off = 0;
|
||||
total = 0;
|
||||
reads = 0;
|
||||
|
||||
for (;;) {
|
||||
n = read_(cfd, buf, sizeof(buf));
|
||||
cell(0x0E0, (unsigned int)n);
|
||||
if (n <= 0) {
|
||||
close_(cfd);
|
||||
close_(fd);
|
||||
return 5;
|
||||
}
|
||||
for (i = 0; i < (unsigned int)n; i++) {
|
||||
*(unsigned char *)(VRAM_BASE + off * 8) = buf[i];
|
||||
off = off + 1;
|
||||
if (off >= VRAM_BYTES) {
|
||||
off = 0;
|
||||
}
|
||||
}
|
||||
total = total + (unsigned int)n;
|
||||
reads = reads + 1;
|
||||
cell(0x0A0, reads);
|
||||
cell(0x0C0, total);
|
||||
if (limit != 0 && total >= limit) {
|
||||
close_(cfd);
|
||||
close_(fd);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* udpstream.c -- spike: receive full frames over UDP and blit them into VRAM.
|
||||
*
|
||||
* The terminal is the client; the host sends self-describing datagrams:
|
||||
*
|
||||
* +0 u16 start row (little endian)
|
||||
* +2 u16 row count
|
||||
* +4 row_count * 160 bytes of 1-bpp pixels (1280 px per row)
|
||||
*
|
||||
* Each row lands at bit 0x04000000 + row * 2048 (the real unit's 256-byte row
|
||||
* pitch). Only the 160 visible bytes of a row are sent; the remaining 96 mask
|
||||
* bytes are left as they are. 8 rows per datagram = 1284 bytes, under the
|
||||
* 1500-byte Ethernet MTU.
|
||||
*
|
||||
* Markers at 0x018C8000 (32-bit cells):
|
||||
* +0x000 stage 1 entered main, 2 socket ok, 3 bound, 4 receiving
|
||||
* 0xEE socket failed, 0xEF bind failed
|
||||
* +0x020 fd
|
||||
* +0x040 bind() result
|
||||
* +0x060 datagrams received
|
||||
* +0x080 last datagram length
|
||||
* +0x0A0 rows written
|
||||
* +0x0C0 bytes blitted
|
||||
*
|
||||
* K&R C only; the ROM entry points and the sockaddr layout are in romapi.h.
|
||||
*/
|
||||
#include "romapi.h"
|
||||
|
||||
#define VRAM_BASE 0x04000000
|
||||
#define ROW_BITS 2048 /* 256-byte row pitch on the real unit */
|
||||
#define ROW_BYTES 160 /* 1280 visible pixels per row */
|
||||
#define MAX_ROWS 8
|
||||
#define PORT 7000
|
||||
#define MARK 0x018C8000
|
||||
#define LIMIT_CELL 0x018C9000 /* host-written datagram limit, 0 = unlimited */
|
||||
#define PORT_CELL 0x018C9040 /* host-written UDP port, 0 = 7000 */
|
||||
|
||||
static unsigned char sa[16];
|
||||
static unsigned char from[16];
|
||||
static int fromlen;
|
||||
static unsigned char buf[4 + MAX_ROWS * ROW_BYTES];
|
||||
static unsigned int rset[8];
|
||||
static unsigned int tv[2];
|
||||
|
||||
static void cell(off, v)
|
||||
unsigned int off, v;
|
||||
{
|
||||
*(unsigned int *)(MARK + off) = v;
|
||||
}
|
||||
|
||||
static unsigned int getcell(off)
|
||||
unsigned int off;
|
||||
{
|
||||
return *(unsigned int *)(MARK + off);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int fd, r, n;
|
||||
unsigned int limit, count, port;
|
||||
unsigned int start, rows, row, i, dst, len;
|
||||
|
||||
cell(0x000, 1);
|
||||
limit = *(unsigned int *)LIMIT_CELL;
|
||||
count = 0;
|
||||
port = *(unsigned int *)PORT_CELL;
|
||||
if (port == 0) {
|
||||
port = PORT;
|
||||
}
|
||||
|
||||
/* The per-process user area (process+0x100 bits) is not initialised by
|
||||
* create_process: on both the emulator and real hardware it reads as the
|
||||
* heap fill 0x55. The syscall layer keeps its file table and syscall
|
||||
* state there, so clear it before the first call. */
|
||||
{
|
||||
unsigned int proc, user, k;
|
||||
proc = *(unsigned int *)0x1B3900;
|
||||
cell(0x140, proc);
|
||||
user = *(unsigned int *)(proc + 0x100);
|
||||
cell(0x160, user);
|
||||
for (k = 0; k < 0x100; k++) {
|
||||
*(unsigned char *)(user + k * 8) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fd = socket_(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
cell(0x020, fd);
|
||||
if (fd < 0) {
|
||||
cell(0x000, 0xEE);
|
||||
return 1;
|
||||
}
|
||||
cell(0x000, 2);
|
||||
|
||||
sa_any(sa, port);
|
||||
cell(0x120, port);
|
||||
r = bind_(fd, sa, SOCKADDR_LEN);
|
||||
cell(0x040, r);
|
||||
if (r < 0) {
|
||||
cell(0x000, 0xEF);
|
||||
close_(fd);
|
||||
return 2;
|
||||
}
|
||||
cell(0x000, 3);
|
||||
|
||||
/* limit == 0xDEAD: bind and return without reading, so the host can test
|
||||
* whether traffic to an unread socket is what kills the machine. */
|
||||
if (limit == 0xDEAD) {
|
||||
cell(0x000, 0xDD);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cell(0x060, 0);
|
||||
cell(0x0A0, 0);
|
||||
cell(0x0C0, 0);
|
||||
|
||||
for (;;) {
|
||||
fromlen = SOCKADDR_LEN;
|
||||
n = recvfrom_(fd, buf, sizeof(buf), 0, from, &fromlen);
|
||||
cell(0x0E0, (unsigned int)n);
|
||||
if (n <= 0) {
|
||||
close_(fd);
|
||||
return 3;
|
||||
}
|
||||
if (n < 4) {
|
||||
close_(fd);
|
||||
return 4;
|
||||
}
|
||||
len = (unsigned int)n;
|
||||
start = (unsigned int)buf[0] | ((unsigned int)buf[1] << 8);
|
||||
rows = (unsigned int)buf[2] | ((unsigned int)buf[3] << 8);
|
||||
if (rows > MAX_ROWS || start + rows > 1024) {
|
||||
close_(fd);
|
||||
return 5;
|
||||
}
|
||||
if (len < 4 + rows * ROW_BYTES) {
|
||||
close_(fd);
|
||||
return 6;
|
||||
}
|
||||
|
||||
for (row = 0; row < rows; row++) {
|
||||
dst = VRAM_BASE + (start + row) * ROW_BITS;
|
||||
for (i = 0; i < ROW_BYTES; i++) {
|
||||
*(unsigned char *)(dst + i * 8) =
|
||||
buf[4 + row * ROW_BYTES + i];
|
||||
}
|
||||
}
|
||||
|
||||
cell(0x080, len);
|
||||
cell(0x060, getcell(0x060) + 1);
|
||||
cell(0x0A0, getcell(0x0A0) + rows);
|
||||
cell(0x0C0, getcell(0x0C0) + rows * ROW_BYTES);
|
||||
cell(0x000, 4);
|
||||
|
||||
count = count + 1;
|
||||
if (limit != 0 && count >= limit) {
|
||||
cell(0x100, count);
|
||||
close_(fd);
|
||||
return 0; /* bounded run: leave the OS to it */
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user