emailler/client/ip65/icmp.s

244 lines
4.8 KiB
ArmAsm

;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