mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
dwt/tcpip: LAN TFTP client (100.103.255.254) and a verified self-contained fetch + load on the real unit
This commit is contained in:
1 parent
efef682b63
commit
09f8607466
3 files changed
+278
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 6.9 KiB |
@@ -282,3 +282,25 @@ The LANCE address ROM at bit `0x6800000` reads (low bytes of the words):
|
||||
|
||||
byte-identical to the MAME model. The `Ethernet Address ROM Test` passing is
|
||||
consistent with this.
|
||||
|
||||
### Self-contained TFTP loader on the real unit
|
||||
|
||||
Retargeted `tftpc.asm` to the LAN (`100.103.255.254:69`) as
|
||||
`dwt/tcpip/tftpl.asm`; the RRQ name stays `dwt/tcpip/probe.bin` because the
|
||||
server serves the chosen COFF for any requested name. The DOS 8.3 build name
|
||||
must be <= 8 characters: `tftpc_lan` silently truncates and GSPLNK produces no
|
||||
`TFTPC_LAN.OUT`; `tftpl` is safe.
|
||||
|
||||
```
|
||||
server: dwt/tcpip/serve_probe.py 100.103.255.254 --file dwt/loader/build/xhello.coff
|
||||
RRQ: 100.103.100.103:1036 requested 'dwt/tcpip/probe.bin' (774 bytes)
|
||||
fetch: sha256 a8b3014d27deb97adcebb418a5d14da51fc3c11559e84923ede72e260d3e7271
|
||||
loader: status 0, entry 0x01840000, pcb 0x00849a10
|
||||
app: xhello stage=6 strlen=4
|
||||
```
|
||||
|
||||
Evidence: `dwt/loader/evidence/real-unit-tftp/hostload_tftp_xhello.png`.
|
||||
|
||||
This is the first fully self-contained delivery on the real unit: the terminal
|
||||
fetched the COFF over TFTP into `malloc` memory and ran it in a created
|
||||
process, with only the rdebug jump used to start the fetch.
|
||||
@@ -0,0 +1,256 @@
|
||||
; tftpc.asm -- minimal TFTP read client built on the firmware's socket API.
|
||||
;
|
||||
; The firmware's own tftp_open() calls a resolver that does not return when
|
||||
; called from the rdebug process (dwt/notes/TCPIP.md), so this payload speaks
|
||||
; RFC 783 directly: RRQ, DATA/ACK, 512-byte blocks, one file.
|
||||
;
|
||||
; socket = 3F5C9A00h socket(2,2,0) -> fd
|
||||
; sendto = 3F5D3D70h sendto(fd, buf, len, flags, to, tolen) -> n
|
||||
; recvfrom = 3F5D7D50h recvfrom(fd, buf, len, flags, from, fromlen) -> n
|
||||
; close = 3F5E8930h close(fd)
|
||||
; malloc = 3F701230h malloc(bytes) -> bit pointer
|
||||
;
|
||||
; The transfer is 600 bytes, so DATA 1 is 512 (full) and DATA 2 is 88
|
||||
; (short); the short block ends the transfer. ACKs go to the sockaddr that
|
||||
; recvfrom reports, not to a hard-coded port.
|
||||
;
|
||||
; Evidence cells (32-bit, bit addresses):
|
||||
; 0187C000 stage (1 malloc, 2 socket, 3 receiving, 4 closed, 0FFh failed)
|
||||
; 0187C020 fd
|
||||
; 0187C040 destination buffer (malloc)
|
||||
; 0187C060 total bytes fetched
|
||||
; 0187C080 last recvfrom length
|
||||
; 0187C0A0 last DATA block word as read from the wire
|
||||
; 0187C0C0 marker 05A5A5A5h when the fetch completed
|
||||
; 0187C200 entry SP
|
||||
; 0187C220 entry A14
|
||||
;
|
||||
; Scratch (fixed bit addresses, above this image):
|
||||
; 01875000 argument area
|
||||
; 01876000 receive buffer (2048 bytes)
|
||||
; 0187A000 recvfrom source sockaddr
|
||||
; 0187A080 recvfrom sockaddr length
|
||||
; 0187A100 ACK packet (4 bytes)
|
||||
|
||||
.text
|
||||
_start:
|
||||
setf 16, 0, 0
|
||||
setf 32, 0, 1
|
||||
mmtm sp, a0,a1,a2,a3,a4,a5,a6,a7
|
||||
move sp, @0187C200h, 1
|
||||
move a14, @0187C220h, 1
|
||||
|
||||
movi 1, a0, L
|
||||
move a0, @0187C000h, 1
|
||||
|
||||
; ---- buf = malloc(010000h) ------------------------------------
|
||||
move a14, -*sp, 1
|
||||
movi 01875000h, a14, L
|
||||
movi 010000h, a0, L
|
||||
move a0, *a14+, 1
|
||||
calla 3F701230h
|
||||
move @0187C200h, sp, 1
|
||||
move @0187C220h, a14, 1
|
||||
setf 16, 0, 0
|
||||
setf 32, 0, 1
|
||||
move a8, @0187C040h, 1
|
||||
move a8, a11 ; dest cursor (callee-saved)
|
||||
move a8, a8
|
||||
jreq fail
|
||||
|
||||
movi 2, a0, L
|
||||
move a0, @0187C000h, 1
|
||||
|
||||
; ---- fd = socket(2,2,0) --------------------------------------
|
||||
movi 01875000h, a14, L
|
||||
movi 0, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi 2, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi 2, a0, L
|
||||
move a0, *a14+, 1
|
||||
calla 3F5C9A00h
|
||||
move @0187C200h, sp, 1
|
||||
move @0187C220h, a14, 1
|
||||
setf 16, 0, 0
|
||||
setf 32, 0, 1
|
||||
move a8, @0187C020h, 1
|
||||
move a8, a9 ; fd (callee-saved)
|
||||
move a8, a8
|
||||
jrlt fail
|
||||
|
||||
; ---- sendto(fd, RRQ, 28, 0, SADDR, 16) ------------------------
|
||||
movi 01875000h, a14, L
|
||||
movi 16, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi SADDR, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi 0, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi 28, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi RRQ, a0, L
|
||||
move a0, *a14+, 1
|
||||
move a9, *a14+, 1
|
||||
calla 3F5D3D70h
|
||||
move @0187C200h, sp, 1
|
||||
move @0187C220h, a14, 1
|
||||
setf 16, 0, 0
|
||||
setf 32, 0, 1
|
||||
move a8, a8
|
||||
jrlt fail
|
||||
|
||||
movi 3, a0, L
|
||||
move a0, @0187C000h, 1
|
||||
sub a0, a0
|
||||
move a0, @0187C060h, 1 ; total = 0
|
||||
|
||||
recv_loop:
|
||||
; ---- recvfrom(fd, BUF, 2048, 0, &FROM, &FROMLEN) -------------
|
||||
; arg5 is a POINTER to the socklen (as tftp_read passes it); the
|
||||
; value is initialised to 16 first.
|
||||
movi 16, a0, L
|
||||
move a0, @0187A080h, 1 ; fromlen = 16
|
||||
movi 01875000h, a14, L
|
||||
movi 0187A080h, a0, L
|
||||
move a0, *a14+, 1 ; arg5 = &fromlen
|
||||
movi 0187A000h, a0, L
|
||||
move a0, *a14+, 1 ; arg4 = &from
|
||||
movi 0, a0, L
|
||||
move a0, *a14+, 1 ; arg3 = flags
|
||||
movi 00800h, a0, L
|
||||
move a0, *a14+, 1 ; arg2 = 2048
|
||||
movi 01876000h, a0, L
|
||||
move a0, *a14+, 1 ; arg1 = receive buffer
|
||||
move a9, *a14+, 1 ; arg0 = fd
|
||||
calla 3F5D7D50h
|
||||
move @0187C200h, sp, 1
|
||||
move @0187C220h, a14, 1
|
||||
setf 16, 0, 0
|
||||
setf 32, 0, 1
|
||||
|
||||
move a8, @0187C080h, 1 ; last length
|
||||
move a8, a8
|
||||
jrle fail
|
||||
|
||||
; opcode: wire 00 03 -> little-endian word 0300h
|
||||
move @01876000h, a0, 0
|
||||
cmpi 0300h, a0
|
||||
jrne fail ; only DATA is accepted
|
||||
|
||||
move @01876010h, a4, 0 ; block number, wire order
|
||||
move a4, @0187C0A0h, 1
|
||||
|
||||
move a8, a3
|
||||
subk 4, a3 ; payload length
|
||||
|
||||
; ---- total += payload, dest += payload -----------------------
|
||||
move @0187C060h, a1, 1
|
||||
add a3, a1
|
||||
move a1, @0187C060h, 1 ; total += payload (bytes)
|
||||
move a11, a6 ; copy destination
|
||||
move a3, a2
|
||||
sll 3, a2 ; bytes -> bits: pointers are bit addresses
|
||||
add a2, a11 ; dest cursor advances now (do NOT add bytes)
|
||||
|
||||
; ---- copy payload from BUF+4 to a6, one byte at a time ------
|
||||
; Interrupts are disabled for the copy so a 512-byte copy is atomic
|
||||
; with respect to the firmware's ISRs.
|
||||
dint
|
||||
movi 01876020h, a5, L
|
||||
move a3, a2
|
||||
move a3, @0187C0E0h, 1 ; payload length
|
||||
move a2, @0187C140h, 1 ; copy count
|
||||
move a6, @0187C120h, 1 ; write start
|
||||
move a11, @0187C100h, 1 ; cursor after advance
|
||||
move a5, @0187C160h, 1 ; source
|
||||
jreq copy_done
|
||||
copy_bytes:
|
||||
movb *a5, a0
|
||||
movb a0, *a6
|
||||
addk 8, a5 ; one byte
|
||||
addk 8, a6
|
||||
dsjs a2, copy_bytes
|
||||
copy_done:
|
||||
eint
|
||||
|
||||
; ---- ACK: 00 04 <block as received> --------------------------
|
||||
movi 00400h, a0, L
|
||||
move a0, @0187A100h, 0
|
||||
move a4, @0187A110h, 0
|
||||
|
||||
movi 01875000h, a14, L
|
||||
movi 16, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi 0187A000h, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi 0, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi 4, a0, L
|
||||
move a0, *a14+, 1
|
||||
movi 0187A100h, a0, L
|
||||
move a0, *a14+, 1
|
||||
move a9, *a14+, 1
|
||||
calla 3F5D3D70h
|
||||
move @0187C200h, sp, 1
|
||||
move @0187C220h, a14, 1
|
||||
setf 16, 0, 0
|
||||
setf 32, 0, 1
|
||||
move a8, a8
|
||||
jrlt fail
|
||||
|
||||
; short block ends the transfer; full block means read again
|
||||
move @0187C080h, a2, 1
|
||||
subk 4, a2
|
||||
cmpi 512, a2
|
||||
jrlt close_it
|
||||
jruc recv_loop
|
||||
|
||||
close_it:
|
||||
movi 4, a0, L
|
||||
move a0, @0187C000h, 1
|
||||
movi 01875000h, a14, L
|
||||
move a9, *a14+, 1
|
||||
calla 3F5E8930h ; close(fd)
|
||||
move @0187C200h, sp, 1
|
||||
move @0187C220h, a14, 1
|
||||
setf 16, 0, 0
|
||||
setf 32, 0, 1
|
||||
movi 05A5A5A5h, a0, L
|
||||
move a0, @0187C0C0h, 1
|
||||
sub a8, a8
|
||||
jruc epilogue
|
||||
|
||||
fail:
|
||||
movi 0FFh, a0, L
|
||||
move a0, @0187C000h, 1
|
||||
movi 0FFFFFFFFh, a8, L
|
||||
epilogue:
|
||||
mmfm sp, a0,a1,a2,a3,a4,a5,a6,a7
|
||||
rets
|
||||
|
||||
RRQ:
|
||||
.word 00100h ; opcode 1 (00 01)
|
||||
.word 07764h ; "dw"
|
||||
.word 02F74h ; "t/"
|
||||
.word 06374h ; "tc"
|
||||
.word 06970h ; "pi"
|
||||
.word 02F70h ; "p/"
|
||||
.word 07270h ; "pr"
|
||||
.word 0626Fh ; "ob"
|
||||
.word 02E65h ; "e."
|
||||
.word 06962h ; "bi"
|
||||
.word 0006Eh ; "n\0"
|
||||
.word 0636Fh ; "oc"
|
||||
.word 06574h ; "te"
|
||||
.word 00074h ; "t\0"
|
||||
SADDR:
|
||||
.word 00002h ; sin_family = AF_INET
|
||||
.word 04500h ; sin_port = htons(69)
|
||||
.word 06764h ; 100.103
|
||||
.word 0FEFFh ; .255.254
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.end
|
||||
Reference in new issue
Block a user