diff options
Diffstat (limited to 'src/socket.asm')
| -rw-r--r-- | src/socket.asm | 342 |
1 files changed, 321 insertions, 21 deletions
diff --git a/src/socket.asm b/src/socket.asm index f0d8444..ef9d4b9 100644 --- a/src/socket.asm +++ b/src/socket.asm @@ -35,6 +35,8 @@ wNetTx:: DS 256 ; tx build buffer wNetRxBuf:: DS 256 ; rx frame assembly / parse wNetRxLen:: DS 1 wNetRxEsc:: DS 1 +wNetUdpPort:: DS 2 ; scratch: UDP dst port being demuxed +wNetTO:: DS 3 ; recv timeout counter (net_pump clobbers registers) SECTION "socket", ROM0 @@ -123,9 +125,10 @@ net_slip_send: ret ; ----------------------------------------------------------------------------- -; net_cksum(HL = buf, B = len, DE = seed) -> HL = inverted 16-bit checksum. -; One's-complement sum (RFC 1071), carry-folded each add. len <= 255. -net_cksum: +; net_sum(HL = buf, B = len, DE = seed) -> DE = folded 16-bit one's-complement +; sum (RFC 1071), carry-folded each add. len <= 255. Used to chain a pseudo- +; header into a segment checksum. +net_sum: .loop ld a, b cp 2 @@ -147,15 +150,19 @@ net_cksum: .tail ld a, b or a - jr z, .fold + ret z ld a, [hl] ; last odd byte -> high half add d ld d, a - jr nc, .fold + ret nc inc e - jr nz, .fold + ret nz inc d -.fold + ret + +; net_cksum(HL, B, DE=seed) -> HL = inverted 16-bit checksum. +net_cksum: + call net_sum ld a, d cpl ld h, a @@ -201,11 +208,30 @@ sys_net:: jp z, net_op_recv cp NET_CLOSE jp z, net_op_close + cp NET_BIND + jp z, net_op_bind cp NET_POLL jp z, net_op_poll ld a, $FF ret +; ---- bind(sock, port) - set local port ----------------------------------- +net_op_bind: + ld a, [wNetReq + NR_SOCK] + call net_sock_ptr + ld a, l + add SK_LPORT + ld l, a + ld a, h + adc 0 + ld h, a + ld a, [wNetReq + NR_PORT] + ld [hl+], a + ld a, [wNetReq + NR_PORT + 1] + ld [hl], a + xor a + ret + ; ---- socket(type) -> A = sockid or $FF --------------------------------------- net_op_socket: ld c, 0 @@ -281,6 +307,8 @@ net_op_send: ld a, [hl] ; SK_TYPE cp SOCK_ICMP jp z, icmp_send + cp SOCK_UDP + jp z, udp_send ld a, $FF ret @@ -292,21 +320,27 @@ net_op_recv: ld [wNetSockPtr], a ld a, h ld [wNetSockPtr+1], a - ld b, 200 ; outer timeout -.outer - ld de, 0 -.inner + xor a ; 24-bit timeout counter in WRAM (net_pump + ld [wNetTO], a ; clobbers BC/DE/HL, so we can't count in regs) + ld [wNetTO+1], a + ld [wNetTO+2], a +.wait call net_pump - call sock_has_rx ; Z clear (nz) if HASRX set + call sock_has_rx ; nz if a datagram is buffered jr nz, .got - dec de - ld a, d - or e - jr nz, .inner - call SchedYield - dec b - jr nz, .outer - ld a, $FF ; timeout + ld hl, wNetTO + inc [hl] + jr nz, .wait ; low byte didn't wrap -> spin (no yield) + call SchedYield ; every 256 pumps, yield to the scheduler + inc hl + inc [hl] + jr nz, .wait + inc hl + inc [hl] + ld a, [hl] + cp 250 ; timeout after ~250*65536 pumps + jr c, .wait + ld a, $FF ret .got ; copy sock.RXBUF -> process NR_BUF, len = sock.RXLEN, NR_IP = sock.SRCIP @@ -493,6 +527,153 @@ icmp_send: ret ; ============================================================================= +; udp_send - build + transmit a UDP datagram from the current socket. +; ============================================================================= +udp_send: + ld a, $45 + ld [wNetTx+0], a + xor a + ld [wNetTx+1], a + ld [wNetTx+2], a + ld a, [wNetReq+NR_LEN] + add 28 ; 20 IP + 8 UDP + payload + ld [wNetTx+3], a + xor a + ld [wNetTx+4], a + ld [wNetTx+5], a + ld [wNetTx+6], a + ld [wNetTx+7], a + ld a, 64 + ld [wNetTx+8], a + ld a, 17 + ld [wNetTx+9], a ; proto UDP + xor a + ld [wNetTx+10], a + ld [wNetTx+11], a + ld a, [wNetOurIP+0] + ld [wNetTx+12], a + ld a, [wNetOurIP+1] + ld [wNetTx+13], a + ld a, [wNetOurIP+2] + ld [wNetTx+14], a + ld a, [wNetOurIP+3] + ld [wNetTx+15], a + ; dst IP = sock.RIP + call sock_ptr_hl + ld a, l + add SK_RIP + ld l, a + ld a, h + adc 0 + ld h, a + ld de, wNetTx+16 + ld b, 4 +.dip + ld a, [hl+] + ld [de], a + inc de + dec b + jr nz, .dip + ; UDP header at wNetTx+20: src port = SK_LPORT + call sock_ptr_hl + ld a, l + add SK_LPORT + ld l, a + ld a, h + adc 0 + ld h, a + ld a, [hl+] + ld [wNetTx+20], a + ld a, [hl] + ld [wNetTx+21], a + ; dst port = SK_RPORT + call sock_ptr_hl + ld a, l + add SK_RPORT + ld l, a + ld a, h + adc 0 + ld h, a + ld a, [hl+] + ld [wNetTx+22], a + ld a, [hl] + ld [wNetTx+23], a + ; length = 8 + payload + xor a + ld [wNetTx+24], a + ld a, [wNetReq+NR_LEN] + add 8 + ld [wNetTx+25], a + xor a + ld [wNetTx+26], a ; cksum = 0 + ld [wNetTx+27], a + ; payload from process buf + ld a, [wNetReq+NR_BUF] + ld l, a + ld a, [wNetReq+NR_BUF+1] + ld h, a + ld de, wNetTx+28 + ld a, [wNetReq+NR_LEN] + ld b, a + or a + jr z, .nopl +.cppl + ld a, [hl+] + ld [de], a + inc de + dec b + jr nz, .cppl +.nopl + ; UDP checksum with pseudo-header: sum(src+dst IP) + proto + udplen, then seg + ld hl, wNetTx+12 + ld b, 8 + ld de, 0 + call net_sum ; DE = IP-address sum + ld a, e + add 17 ; + protocol + ld e, a + ld a, d + adc 0 + ld d, a + ld a, [wNetReq+NR_LEN] + add 8 ; + udp length + add e + ld e, a + ld a, d + adc 0 + ld d, a ; DE = pseudo-header seed + ld a, [wNetReq+NR_LEN] + add 8 + ld b, a + ld hl, wNetTx+20 + call net_cksum ; HL = inverted checksum + ld a, h + or l + jr nz, .cknz + ld hl, $FFFF ; 0 means "no checksum" -> send all-ones +.cknz + ld a, h + ld [wNetTx+26], a + ld a, l + ld [wNetTx+27], a + ; IP checksum + ld hl, wNetTx + ld b, 20 + ld de, 0 + call net_cksum + ld a, h + ld [wNetTx+10], a + ld a, l + ld [wNetTx+11], a + ld a, [wNetReq+NR_LEN] + add 28 + ld b, a + ld hl, wNetTx + call net_slip_send + xor a + ret + +; ============================================================================= ; net_pump - read available serial bytes, assemble SLIP frames, process each. ; Non-blocking: returns when the link has no more bytes ready. ; ============================================================================= @@ -576,7 +757,9 @@ process_frame: ld [wNetIhl], a ; ihl in bytes ld a, [wNetRxBuf+9] ; protocol cp 1 - jr z, icmp_in + jp z, icmp_in + cp 17 + jp z, udp_in ret ; ----------------------------------------------------------------------------- @@ -736,6 +919,123 @@ icmp_reply: jr nz, .cp ret +; ----------------------------------------------------------------------------- +; udp_in - deliver an inbound UDP datagram to the socket bound to its dst port. +udp_in: + ld a, [wNetIhl] + add 2 + ld e, a + ld d, 0 + ld hl, wNetRxBuf + add hl, de ; HL = &udp.dstport + ld a, [hl+] + ld [wNetUdpPort], a + ld a, [hl] + ld [wNetUdpPort+1], a + call net_find_udp ; HL = &sock, CF if none + ret c + ; deliver: HASRX=1, SRCIP, RXLEN + RXBUF (payload after ihl+8) + push hl + ld a, l + add SK_HASRX + ld l, a + ld a, h + adc 0 + ld h, a + ld a, 1 + ld [hl], a + pop hl + push hl + ld a, l + add SK_SRCIP + ld l, a + ld a, h + adc 0 + ld h, a + ld de, wNetRxBuf+12 + ld b, 4 +.sip + ld a, [de] + ld [hl+], a + inc de + dec b + jr nz, .sip + pop hl + ; payload offset = ihl + 8, length = rxlen - ihl - 8 + ld a, [wNetIhl] + add 8 + ld e, a + ld d, 0 + ld a, [wNetRxLen] + sub e + ld c, a ; C = payload length + push hl + ld a, l + add SK_RXLEN + ld l, a + ld a, h + adc 0 + ld h, a + ld a, c + ld [hl], a + pop hl + ld a, l + add SK_RXBUF + ld l, a + ld a, h + adc 0 + ld h, a ; HL = &sock.RXBUF (dest) + push hl + ld hl, wNetRxBuf + add hl, de ; HL = &payload (src) + pop de ; DE = dest + ld a, c + or a + ret z +.cp + ld a, [hl+] + ld [de], a + inc de + dec c + jr nz, .cp + ret + +; net_find_udp -> HL = &UDP socket bound to wNetUdpPort, CF set if none +net_find_udp: + ld c, 0 +.l + ld a, c + call net_sock_ptr ; HL = &sock (clobbers B, DE) + ld a, [hl] + cp SOCK_UDP + jr nz, .nx + push hl + ld a, l + add SK_LPORT + ld l, a + ld a, h + adc 0 + ld h, a + ld a, [wNetUdpPort] + cp [hl] + jr nz, .nomatch + inc hl + ld a, [wNetUdpPort+1] + cp [hl] + jr nz, .nomatch + pop hl ; match + or a ; CF = 0 + ret +.nomatch + pop hl +.nx + inc c + ld a, c + cp MAX_SOCKS + jr c, .l + scf + ret + ; net_swap_ip - swap the src/dst IP fields of the frame in wNetRxBuf net_swap_ip: ld hl, wNetRxBuf+12 |
