mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
256 lines
6.2 KiB
NASM
256 lines
6.2 KiB
NASM
; tftpr.asm -- tftpc.asm retargeted for the real unit at 100.103.100.103.
|
|
; Server: 100.103.255.254:69; RRQ name: "xtext.coff".
|
|
;
|
|
; 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 20, 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 07478h ; "xt"
|
|
.word 07865h ; "ex"
|
|
.word 02E74h ; "t."
|
|
.word 06F63h ; "co"
|
|
.word 06666h ; "ff"
|
|
.word 06F00h ; "\0o" (NUL ends the name, mode starts)
|
|
.word 07463h ; "ct"
|
|
.word 07465h ; "et"
|
|
.word 00000h ; "\0" + pad
|
|
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
|