ttydio.asm


;+
; **-udp-udp receive handler.
;
; return with ret.
;-


#define UDP_FUNC    UDP_DATA

import_udp:
	rcall pcode
#if defined(SHOW_UDP_REQUESTS)
    pprintr "UDP src",IP_SRCADDR,4
    pprintr ":",UDP_DP,2
#endif
    pr2s buf,UDP_DP,2           ; get destination port
    pcmpwi buf,BSWAP(UDP_CMD_PORT)          ; check if it matches
    pjumpne udp_bail

    pr2s buf,IP_SRCADDR,4
    pr2s buf+10,UDP_FUNC,1      ; get function code byte
    pandwi buf+10,0xff
    pjumpeq login_check
    pcmpn buf,client_ip,4
    pjumpne nak_request         ; sorry - you're not logged in
    pjump check_port
login_check:
    pandwi client_ip,0xffff     ; check client ip
    pjumpeq go_func             ; anything is OK if nobody's logged in
    pcmpn buf,client_ip,4       ; check against client
    pjumpne nak_request         ; sorry
check_port:

#if defined(CHECK_UDP_REQUESTOR_PORT)
    pr2s buf,UDP_SP,2           ; get requestor port
    pcmpwi buf,[client_port]    ; check port
    pjumpne nak_request         ; sorry
#endif

go_func:
    asm

    lds r20,buf+10
    movwi z,udp_functab
    andi r20,7
    add zl,r20
    clr r20
    adc zh,r20                  ; get jump address
    icall
    ret
udp_functab:
    rjmp loginout               ; 00 - loginout
    rjmp read_timer             ; 01 - read timer info
    rjmp set_clock_div          ; 02 - set clock divisor
    ret                         ; 03 - unused
    ret                         ; 04 - unused
    ret                         ; 05 - memory packet test
    rjmp async_poll             ; 06 - async poller
    rjmp udp_output_test        ; 07 - output tester

async_poll:
    rcall pcode
    pmovbi async_poll_active,1
    pjump ack_request
do_async_poll:
    pser_getc buf               ; get another char
    pjumpeq do_pret             ; none left
    pcall start_udp_output      ; condition output
    pputc [byte buf]            ; put first byte
next_async:
    pser_getc buf               ; get another char
    pjumpeq async_done          ; none left
    pputc [byte buf]            ; put char
    pjump next_async            ; go do next one
async_done:
    pcall udp_flush_and_send    ; transmit the packet
do_pret:
    pret
;+
; **-udp_output_test-output testor.
;
; inputs:
;   UDP_DATA+1  = packet count (word)
;   UDP_DATA+3  = packet length (word)
;
; outputs:
;   none, except the desire to blast packets is recorded, and packet
;   blasting begins when the next trip through the main loop happens.
;-

udp_output_test:
    rcall pcode
    pr2s udp_pkt_count,UDP_DATA+1,2
    pr2s udp_pkt_len,UDP_DATA+1+2,2
    pjump udp_bail
udp_loop_processing:
    rcall pcode
    pmovb buf,async_poll_active
    pandwi buf,0xff
    pjumpeq udp_loop1
    pcall do_async_poll
udp_loop1:
    pandwi udp_pkt_count,0xffff
    pjumpeq udp_bail

    pmovwi MEM_UDP_LEN,[udp_pkt_len]
    pf2x UDP_DATA,movedata,[udp_pkt_len]
    pcall mem_udp_send

    psubwi udp_pkt_count,1
    pjumpne udp_bail
    pjump ack_request           ; not right - uses reply and there's no req!
;+
; **-udp_flush_and_send-flush and send memory packet.
;
; inputs:
;   boardaddr   = current board address (word)
;   xmit_page   = current transmit page (byte)
;
; outputs:
;   packet is sent to original client
;-

udp_flush_and_send:
    bflush                      ; flush anything remaining
    pmovbi putc_b,0             ; regular putchar now
    pmovwi buf,[boardaddr]
    pmovb buf+3,xmit_page
    pmovbi buf+2,UDP_DATA
    psubwi buf,[buf+2]              ; length of data
    pi2x UDP_LEN,[buf]              ; store length
    pjump udp_send_client             ; send it
;+
; **-loginout-log in or out.
;
; inputs:
;   UDP_DATA+1 = 0-logout, else login (byte)
;
; outputs:
;   reply packet sent
;-

loginout:
    rcall pcode
    pr2s buf,UDP_DATA+1,1       ; get logon/logoff function code
    pandwi buf,0xff             ; check it
    pjumpeq log_out             ; logging out
    pcall set_client
ack_request:
    pi2x UDP_DATA,1             ; good ack
    pjump reply_request
log_out:
    pclrw client_ip
    pjump ack_request
;+
; **-set_client-remember requestor as client for future autonomous sends.
;
; inputs:
;   current receive packet contains client info
;
; outputs:
;   appropriate info copied to memory packet buffer.
;
; caveats:
;   called via pcall/pret
;-

set_client:
    pr2s client_ip,IP_SRCADDR,4                 ; remember client's IP
    pr2s client_port,UDP_SP,2                   ;  and port number
    pmemcpy MEM_ETH_DEST,ETH_SRC,6              ;   and ether

    pmemcpy MEM_IP_SRCADDR,my_ip,4              ; source ip == me
    pmovwi MEM_UDP_SP,BSWAP(UDP_CMD_PORT)       ;  and port
    pmemcpy MEM_ETH_SRC,my_ether,6              ;   and ether
    
    pmovwi MEM_ETH_PT,0x0008                    ; set PT=IP

    pmovwi MEM_IP_HVTOS,0x0045                  ; 4500
    pmovwi MEM_IP_TTLP,0x1101           ; UDP proto and TTL=1
    pret
;+
; **-start_udp_output-start output of UDP packet via pputc/putchar.
;
; inputs:
;   none
;
; outputs:
;   boardaddr/boardlen/putc_b set appropriately.
;-

start_udp_output:
    pmovb boardaddr+1,xmit_page         ; get transmit page
    pmovbi boardaddr,UDP_DATA       ; set offset
    pmovbi boardlen,0           ; nothing in buffer
    pmovbi putc_b,1             ; set output for board
    pret
;+
; **-nak_request-blow him off!
;-

nak_request:
    pi2x UDP_DATA,0
reply_request:
    ps2x UDP_DATA+1,client_ip,4
    pi2x UDP_LEN,5
    pcall udp_reply
    asm
    ret
;+
; 00
;-

read_timer:
    rcall pcode
send_timer_info:
;
; this needs work because the timer is being written at interrupt level
; and there needs to be an interlock.  alternative is to read it until two
; successive reads match.
;
#define TI_TIMER    UDP_DATA
#define TI_LOOPS    TI_TIMER+4
#define TI_CLOCKDIV TI_LOOPS+4
#define TI_ARPREQ   TI_CLOCKDIV+1
#define TI_LEN      TI_ARPREQ+2-TI_TIMER

    ps2x TI_TIMER,timer,4            ; store timer
    ps2x TI_LOOPS,loop_count,4          ; main loop count
    ps2x TI_CLOCKDIV,clock_div,1        ; and divisor
    ps2x TI_ARPREQ,arp_requests,2       ; # of arp requests

    pi2x UDP_LEN,TI_LEN
    pcall udp_reply
udp_bail:
    asm
	ret
;+
; **-udp_send_client-send UDP to client.
;
; inputs:
;   UDP_DATA, UDP_LEN valid
;-

udp_send_client:
    pi2x UDP_DP,[client_port]
    pi2x UDP_SP,BSWAP(UDP_CMD_PORT)     ; set source port
    ps2x IP_DSTADDR,client_ip,4     ; set dest ip
    ps2x IP_SRCADDR,my_ip,4         ; source ip
    pjump udp_send
;+
; **-movedata-move data to packet.
;
; inputs:
;   none
;
; outputs:
;   data moved
;
; notes:
;   demonstrates how to use pf2x with initial setup processing vs.
;   "during the loop" processing.
;-

movedata:
    movwi z,movedata_next
    clrw y
    clrw x
movedata_next:
    mov i0,yl
    mov i1,yh						; i0/i1 = next word of data

    f2x_checksum y,r10				; accumulate checksum - r10 destroyed

    incw y							; increment the "data"

    ret
;+
; 01 XX
;
; where XX = new clock divisor.
;-

set_clock_div:
    rcall pcode
    pr2s clock_div,UDP_DATA+1,1           ; get new divisor
    pjump send_timer_info
;+
; **-timer0ovf_isr-timer 0 overflow ISR.
;-

timer0ovf_isr:
    push r16
	push r17
	in r17,sreg
	push r17
    lds r16,clock_div
    out tcnt0,r16
	pushw y

	movwi y,timer
	ldi r17,4                       ; width of timer in bytes
    rcall incn
	popw y
	pop r17
	out sreg,r17
	pop r17
    pop r16
	reti
;+
; **-incn-increment N-byte counter.
;
; inputs:
;   y   -> counter
;   r17 = width in bytes
;-
incn:
    push r0
    clr r0
    sec
incn_next:
	ld r16,y
	adc r16,r0
	st y+,r16
	brcc incn_done
	dec r17
	brne incn_next
incn_done:
    pop r0
    ret
;+
; **-zeron-zero N bytes.
;
; inputs:
;   y   -> bytes
;   r18 = count (word)
;-

zeron:
    clr r0
znn:
    st y+,r0
    dec r18l
    brne znn
    dec r18h
    brpl znn
    ret
;+
; **-udp_send-send a UDP datagram.
;
; inputs:
;       transmit buffer:
;               UDP_DATA        = user data to be sent
;               UDP_LEN         = native byte order length of data to send
;-

udp_reply:
    ps2x IP_SRCADDR,my_ip,4         ; set source ip
    pr2x IP_DSTADDR,IP_SRCADDR,4        ; set requestor IP as dest

    pr2x UDP_DP,UDP_SP,2         ; swap source/destination ports
    pr2x UDP_SP,UDP_DP,2
udp_send:

    px2s buf,UDP_LEN,2
    paddwi buf,UDP_HEADER_LEN           ; total length
    pi2x UDP_LEN,[swap buf]             ; write swapped to UDP_LEN
    paddwi buf,IP_HEADER_LEN
    pi2x IP_LEN,[swap buf]              ; write swapped to IP_LEN
;
; build packet and send.
;

; this is bogus - you have to double-pump the TTLP values. !!!needs work!-SDF

    pi2x IP_TTLP,0x1101                 ; TTL=01, proto=UDP

    pcall ip_header_1
    pcall udp_checksum
    px2s buf,IP_LEN,2                   ; read back the length
    pmovwi buf,[swap buf]               ; swap bytes
    paddwi buf,6+6+2                    ; compute length with ether header
    xmit_frame [buf]                    ; out she goes
    pret
;+
; **-mem_udp_send-send a UDP datagram from ram.
;
; inputs:
;   transmit buffer:
;       MEM_UDP_DATA    = user data to be sent
;       MEM_UDP_LEN     = native byte order length of data to send
;
; checksum algorithm:
;
;   ip_chksum = 0 ;
;   ip_chksum = ~checksum(IPH,IPHLEN)
;   udp_chksum = 0
;   ph.src = ip.src
;   ph.dst = ip.dst
;   ph.mbzprot = 0|(ip.prot<<8)
;   ph.len = udp_len (UDP data only, not header)
;   ph_chksum = checksum(UDP_HDR,UDP_LEN+UDP_HEADER_LEN)
;   udp_chksum = ~checksum(PH_HDR,PH_HEADER_LEN)
;-

#define PH_HEADER  rcv_hdr+en_rbuf_nhdr
#define PH_SRCADDR PH_HEADER+0
#define PH_DSTADDR PH_HEADER+4
#define PH_MBZPROT PH_HEADER+8
#define PH_LEN     PH_HEADER+10
#define PH_CHKSUM  PH_HEADER+12
#define PH_HEADER_LEN      14

#define MOVW(x,y) pmovwi x,[y]

#define UDP_CHECKSUM_DONE

mem_udp_send:
#if defined(UDP_MEM_SEND_DEBUG)
    pprintv " MVCHK=",[chkacc]
    pclrw chkacc
    pmem_chksum MEM_UDP_DATA,[MEM_UDP_LEN]
    pprintv " UDPCHK=",[chkacc]
#endif

;
; compute UDP length and store in UDP_LEN field and PH_LEN field.
;
    MOVW(buf,MEM_UDP_LEN)              ; get length (native order)
    paddwi buf,UDP_HEADER_LEN           ; total length
    MOVW(MEM_UDP_LEN,swap buf)         ; set UDP length
;
; checksum UDP data, and store checksum (uncomplemented) in pseudoheader.
;
    pclrw MEM_UDP_CHKSUM                ; zero UDP checksum
#if defined(UDP_CHECKSUM_DONE)
    pmem_chksum MEM_UDP_HEADER,UDP_HEADER_LEN
#else
    pclrw chkacc                        ; reset accumulator
    pmem_chksum MEM_UDP_HEADER,[buf]    ; compute UDP checksum
#endif
    MOVW(PH_CHKSUM,chkacc)             ; store checksum in pseudoheader

    paddwi buf,IP_HEADER_LEN
    MOVW(MEM_IP_LEN,swap buf)          ; set IP length
;
; the IP header should be ready, zero its checksum, checksum it, and
; store complemented checksum back in header.
;
    pclrw chkacc
    pclrw MEM_IP_CHKSUM                 ; zero IP header checksum
    pmem_chksum MEM_IPH,IP_HEADER_LEN   ; checksum IP header
    pxorwi chkacc,-1                    ; complement the ip checksum
#if defined(LOG_MEM_CHKSUM)
    pprintv "IPCK ",[chkacc]
#endif
    MOVW(MEM_IP_CHKSUM,chkacc)          ; store in IP header
;
; finally, checksum the pseudoheader and store complemented checksum
; in UDP header.
;
    pmemcpy PH_SRCADDR,MEM_IP_SRCADDR,8 ; source/dest IP addrs
    pmovwi PH_MBZPROT,0x1100            ; UDP proto in PH
    MOVW(PH_LEN,MEM_UDP_LEN)            ; store net order length in ph

    pclrw chkacc
    pmem_chksum PH_HEADER,PH_HEADER_LEN ; checksum pseudoheader
    pxorwi chkacc,-1                    ; complement
    MOVW(MEM_UDP_CHKSUM,chkacc)         ; store in UDP header checksum
#if defined(LOG_MEM_CHKSUM)
    pprintv "PHCK ",[chkacc]
#endif
;
; compute total packet size and send.
;
    MOVW(buf,swap MEM_IP_LEN)           ; ip length total
    paddwi buf,6+6+2                    ; compute length with ether header

#if defined(UDP_CHECKSUM_DONE)
;
; if the checksum has already been computed, it also means the packet
; data is in the frame buffer, so just load the headers.
;
    ps2x 0,MEM_ETH_DEST,6+6+2+IP_HEADER_LEN+UDP_HEADER_LEN
#else
;
; load everything.
;
    ps2x 0,MEM_ETH_DEST,[buf]           ; copy packet to board
#endif

    xmit_frame [buf]                    ; out she goes

#if defined(LOG_MEM_CHKSUM)
    pprintv "PKLEN ",[buf]
#endif

    pret

Back