;ICMP implementation ; .include "../inc/common.i" .export icmp_init .export icmp_process .export icmp_add_listener .export icmp_remove_listener .export icmp_callback .export icmp_inp .export icmp_outp .exportzp icmp_type .exportzp icmp_code .exportzp icmp_cksum .exportzp icmp_data .import ip_calc_cksum .import ip_inp .import ip_outp .import ip_broadcast .importzp ip_cksum_ptr .importzp ip_header_cksum .importzp ip_src .importzp ip_dest .importzp ip_data .import eth_tx .import eth_inp .import eth_inp_len .import eth_outp .import eth_outp_len .bss ; argument for icmp_add_listener icmp_callback: .res 2 ; icmp callbacks icmp_cbmax = 4 icmp_cbtmp: .res 3 ; temporary vector icmp_cbveclo: .res icmp_cbmax ; table of listener vectors (lsb) icmp_cbvechi: .res icmp_cbmax ; table of listener vectors (msb) icmp_cbtype: .res icmp_cbmax ; table of listener types icmp_cbcount: .res 1 ; number of active listeners ; icmp packet offsets icmp_inp = ip_inp + ip_data ;pointer to inbound icmp packet icmp_outp = ip_outp + ip_data ;pointer to outbound icmp packet icmp_type = 0 ;offset of 'type' field in icmp packet icmp_code = 1 ;offset of 'code' field in icmp packet icmp_cksum = 2 ;offset of 'checksum' field in icmp packet icmp_data = 4;offset of 'data' field in icmp packet ; icmp echo packet offsets icmp_echo_id = 4 ;offset of 'id' field in icmp echo request/echo response icmp_echo_seq = 6 ;offset of 'sequence' field in icmp echo request/echo response icmp_echo_data = 8 ;offset of 'data' field in icmp echo request/echo response .code ; initialize icmp ; inputs: none ; outputs: none icmp_init: lda #0 sta icmp_cbcount lda #$4c ; jmp addr sta icmp_cbtmp rts ;process incoming icmp packet ;inputs: ; eth_inp points to an ethernet frame containing an icmp packet ;outputs: ; carry flag - set on any error, clear if OK ; if inbound packet is a request (e.g. 'echo request') and an icmp listener ; has been installed, then an appropriate response message will be ; generated and sent out (overwriting the eth_outp buffer) icmp_process: lda icmp_inp + icmp_type cmp #8 ; ping beq @echo lda icmp_cbcount ; any installed icmp listeners? beq @drop ldx icmp_cbcount ; check listened types dex : lda icmp_cbtype,x cmp icmp_inp + icmp_type beq @handle ; found a match dex bpl :- @drop: sec rts @handle: lda icmp_cbveclo,x ; copy vector sta icmp_cbtmp + 1 lda icmp_cbvechi,x sta icmp_cbtmp + 2 jsr icmp_cbtmp ; call listener clc rts @echo: lda ip_broadcast ; check if packet is broadcast beq @notbc sec ; don't reply to broadcast pings rts @notbc: ldx #5 : lda eth_inp,x ; swap dest and src mac sta eth_outp + 6,x lda eth_inp + 6,x sta eth_outp,x dex bpl :- ldx #12 ; copy the packet : lda eth_inp,x sta eth_outp,x inx cpx eth_inp_len bne :- ldx #3 : lda ip_inp + ip_src,x ; swap dest and src ip sta ip_outp + ip_dest,x lda ip_inp + ip_dest,x sta ip_outp + ip_src,x dex bpl :- lda #0 ; change type to reply sta icmp_outp + icmp_type lda icmp_inp + icmp_cksum ; recalc checksum clc adc #8 sta icmp_outp + icmp_cksum bcc :+ inc icmp_outp + icmp_cksum + 1 : lda eth_inp_len ; copy length sta eth_outp_len lda eth_inp_len + 1 sta eth_outp_len + 1 lda #0 ; clear checksum sta ip_outp + ip_header_cksum sta ip_outp + ip_header_cksum + 1 ldax #ip_outp ; calculate ip header checksum stax ip_cksum_ptr ldax #20 jsr ip_calc_cksum stax ip_outp + ip_header_cksum jsr eth_tx ; send packet clc rts ;add an icmp listener ;inputs: ; A = icmp type ; icmp_callback: vector to call when an icmp packet of specified type arrives ;outputs: ; carry flag - set if error, clear if no error icmp_add_listener: ldx icmp_cbcount ; any listeners at all? beq @add cpx #icmp_cbmax ; max? beq @full ldx #0 : cmp icmp_cbtype,x ; check if type is already listened beq @busy inx cpx icmp_cbcount bne :- @add: inc icmp_cbcount ; increase counter sta icmp_cbtype,x ; add type lda icmp_callback ; and vector sta icmp_cbveclo,x lda icmp_callback + 1 sta icmp_cbvechi,x clc rts @full: @busy: sec rts ;add an icmp listener ;inputs: ; A = icmp type ;outputs: ; carry flag - set if error (i.e. no listner for this type exists), ; clear if no error icmp_remove_listener: ldx icmp_cbcount ; any listeners installed? beq @notfound : cmp icmp_cbtype,x ; check if type is listened beq @remove inx cpx icmp_cbcount bne :- @notfound: sec rts @remove: txa ; number of listeners below eor #$ff clc adc icmp_cbcount beq @done @move: tay ; number of items to move : lda icmp_cbtype + 1,x ; move type sta icmp_cbtype,x lda icmp_cbveclo + 1,x ; move vector lsb sta icmp_cbveclo,x lda icmp_cbvechi + 1,x ; move vector msb sta icmp_cbvechi,x inx dey bne :- @done: dec icmp_cbcount ; decrement counter clc rts