Equivalent assembly code: 762 705 683 670 670 characters (Counting newline as 2 chars, CR LF)
org $1000x100
ttcp equ $610x61 ; NTCPDRV interrupt
xor ax,ax
mov bx,10
xor cx,cx
mov si,$810x81 ; [ds:81]-[ds:FF] = command line args
mov di,$800x80 ; [ds:80] = strlen(args)
mov cl,[di]
add di,cx
a
@@: inc si
mov cl,[si] ; get character
sub cl,'0' ; convert char to int
mul bx ; ax *= 10
add al,cl
cmp si,di
jb a@b
; now ax = port number
mov bx,ax ; source port (leaving this 0 doesn't work?)
mov cx,ax ; dest port
mov ax,$10010x1001 ; open TCP socket for listening
mov dx,-1 ; infinite timeout
xor si,si ; any dest IP
xor di,di
int ttcp
; ^ I think this call should block until a connection is established, but apparently it doesn't.
push bx ; bx = socket handle, save it for later
mov ax,$12000x1200 ; read from socket
mov di,$800x80 ; es:di = buffer (just reuse argument area to save space)
mov cx,1 ; one byte
mov dx,-1
int ttcp ; this will block until a client connects and sends one byte
mov ax,$14000x1400 ; get TCP session status, bx=handle
int ttcp
; now es:di points to a struct containing the source/dest IP addresses and ports
; the docs say it's two dwords for each IP address, then two bytes for "ip_prot" and "active" (whatever that means)
; ...but actually each IP address is followed by the port number (one word)
xor ax,ax
mov bx,10
add di,6 ; [es:di+6] = client IP
lea cx,[di+4]
b@@: add al,[es:di] ; add all bytes together
adc ah,0
inc di
cmp di,cx
jb b@b
; now ax contains the IP address sum
mov di,$840x84 ; recycle arguments area again
mov cx,$800x80
mov bx,10
c@@: dec di
xor dx,dx
div bx ; dl = ax mod 10
add dl,'0' ; convert int to char
mov [di],dl
cmp di,cx
ja c@b
; now [ds:80]-[ds:83] contains an ascii representation of the IP address sum
push ds
pop es
mov ax,$130e0x130e ; send data with newline, wait for ack
pop bx ; socket handle
mov di,$800x80 ; es:di = data
mov cx,4 ; sizeof data
mov dx,-1
int ttcp
mov ax,$11000x1100 ; close TCP socket
mov dx,1
int ttcp
mov ax,$4c000x4c00
int $210x21