emailler/client/ip65/ip.s

411 lines
6.2 KiB
ArmAsm

;originally from Per Olofsson's IP65 library - http://www.paradroid.net/ip65
.include "../inc/common.i"
;.import dbg_dump_ip_header
.export ip_init
.export ip_process
.export ip_calc_cksum
.export ip_create_packet
.export ip_send
.export ip_inp
.export ip_outp
.export ip_broadcast
.exportzp ip_cksum_ptr
.exportzp ip_ver_ihl
.exportzp ip_tos
.exportzp ip_len
.exportzp ip_id
.exportzp ip_frag
.exportzp ip_ttl
.exportzp ip_proto
.exportzp ip_header_cksum
.exportzp ip_src
.exportzp ip_dest
.exportzp ip_data
.exportzp ip_proto_icmp
.exportzp ip_proto_tcp
.exportzp ip_proto_udp
.import cfg_mac
.import cfg_ip
.import eth_tx
.import eth_set_proto
.import eth_inp
.import eth_inp_len
.import eth_outp
.import eth_outp_len
.importzp eth_dest
.importzp eth_src
.importzp eth_type
.importzp eth_data
.importzp eth_proto_ip
.importzp eth_proto_arp
.import arp_lookup
.import arp_mac
.import arp_ip
.import icmp_init
.import icmp_process
.import udp_init
.import udp_process
.importzp copy_src
.segment "IP65ZP" : zeropage
; checksum
ip_cksum_ptr: .res 2 ; data pointer
.bss
ip_cksum_len: .res 2 ; length of data
; ip packets start at ethernet packet + 14
ip_inp = eth_inp + eth_data
ip_outp = eth_outp + eth_data
; temp storage for size calculation
len: .res 2
; flag for incoming broadcast packets
ip_broadcast: .res 1
; ip packet offsets
ip_ver_ihl = 0
ip_tos = 1
ip_len = 2
ip_id = 4
ip_frag = 6
ip_ttl = 8
ip_proto = 9
ip_header_cksum = 10
ip_src = 12
ip_dest = 16
ip_data = 20
; ip protocols
ip_proto_icmp = 1
ip_proto_tcp = 6
ip_proto_udp = 17
; temp for calculating checksum
cksum: .res 3
; bad packet counters
bad_header: .res 2
bad_addr: .res 2
.code
; initialize ip routines
ip_init:
lda #0
sta bad_header
sta bad_header + 1
sta bad_addr
sta bad_addr + 1
jsr icmp_init
jsr udp_init
; jsr tcp_init
rts
; process an incoming packet
ip_process:
jsr verifyheader ; ver, ihl, len, frag, checksum
bcc @ok
@badpacket:
sec
rts
@ok:
jsr checkaddr ; make sure it's meant for us
bcs @badpacket
lda ip_inp + ip_proto
cmp #ip_proto_icmp
bne :+
jmp icmp_process ; jump to icmp handler
: cmp #ip_proto_tcp
bne :+
jmp tcp_process ; jump to tcp handler
: cmp #ip_proto_udp
bne :+
jmp udp_process ; jump to udp handler
:
tcp_process:
sec ; unknown protocol
rts
; verify that header contains what we expect
verifyheader:
lda ip_inp + ip_ver_ihl ; IPv4 and no IP options
cmp #$45
bne @badpacket
; lda ip_inp + ip_tos ; ignore ToS
lda ip_inp + ip_len + 1 ; ip + 14 bytes ethernet header
clc
adc #14
sta len
lda ip_inp + ip_len
adc #0
sta len + 1
lda eth_inp_len ; check if advertised length is shorter
sec ; than actual length
sbc len
lda eth_inp_len + 1
sbc len + 1
bmi @badpacket
lda ip_inp + ip_frag ; check for fragmentation
beq :+
cmp #$40
bne @badpacket
: lda ip_inp + ip_frag + 1
bne @badpacket
ldax #ip_inp ; verify checksum
stax ip_cksum_ptr
ldax #20
jsr ip_calc_cksum
cmp #0
bne @badpacket
cpx #0
bne @badpacket
clc
rts
@badpacket:
inc bad_header
bne :+
inc bad_header + 1
: sec
rts
; check that this packet was addressed to us
checkaddr:
lda #0
sta ip_broadcast
lda ip_inp + ip_dest ; compare ip address
cmp cfg_ip
bne @broadcast
lda ip_inp + ip_dest + 1
cmp cfg_ip + 1
bne @broadcast
lda ip_inp + ip_dest + 2
cmp cfg_ip + 2
bne @broadcast
lda ip_inp + ip_dest + 3
cmp cfg_ip + 3
bne @broadcast
@ok: clc
rts
@broadcast:
inc ip_broadcast
lda ip_inp + ip_dest ; check for broadcast
and ip_inp + ip_dest + 1
and ip_inp + ip_dest + 2
and ip_inp + ip_dest + 3
cmp #$ff
beq @ok
inc bad_addr
bne :+
inc bad_addr + 1
: sec
rts
; create a packet template
ip_create_packet:
lda #$45 ; set IP version and header length
sta ip_outp + ip_ver_ihl
lda #0 ; set type of service
sta ip_outp + ip_tos
; skip length
; skip ID
lda #$40 ; don't fragment - or should we not care?
sta ip_outp + ip_frag
lda #0
sta ip_outp + ip_frag + 1
lda #$40 ; set time to live
sta ip_outp + ip_ttl
; skip protocol
lda #0 ; clear checksum
sta ip_outp + ip_header_cksum
sta ip_outp + ip_header_cksum + 1
ldx #3 ; copy source address
: lda cfg_ip,x
sta ip_outp + ip_src,x
dex
bpl :-
; skip destination address
rts
; send an IP packet
;
; but first:
;
; call ip_create_packet
; set length
; set ID
; set protocol
; set destination address
ip_send:
ldx #3 ; get mac addr from ip
: lda ip_outp + ip_dest,x
sta arp_ip,x
dex
bpl :-
jsr arp_lookup
bcc :+
rts ; packet buffer nuked, fail
:
ldax #ip_outp ; calculate ip header checksum
stax ip_cksum_ptr
ldax #20
jsr ip_calc_cksum
stax ip_outp + ip_header_cksum
ldx #5
: lda arp_mac,x ; copy destination mac address
sta eth_outp + eth_dest,x
lda cfg_mac,x ; copy my mac address
sta eth_outp + eth_src,x
dex
bpl :-
lda #eth_proto_ip ; set type to IP
jsr eth_set_proto
lda ip_outp + ip_len + 1 ; set packet length
lsr
bcc @dontpad
rol ; pad with 0
;clc
adc #<ip_outp
sta copy_src ; borrow copymem zp...
lda ip_outp + ip_len
adc #>ip_outp
sta copy_src + 1
ldy #0
tya
sta (copy_src),y
sec ; round up to even number
@dontpad:
lda ip_outp + ip_len + 1
adc #eth_data
sta eth_outp_len
lda ip_outp + ip_len
adc #0
sta eth_outp_len + 1
;jsr dbg_dump_ip_header
jmp eth_tx ; send packet and return status
; calculate checksum for ip header
ip_calc_cksum:
sta ip_cksum_len ; save length
stx ip_cksum_len + 1
lda #0
sta cksum
sta cksum + 1
sta cksum + 2
cpx #0
beq @tail
: ldx #$80 ; number of bytes / 2
jsr @calc
inc ip_cksum_ptr + 1
dec ip_cksum_len + 1
bne :-
@tail:
lda ip_cksum_len ; divide length by 2
lsr
php ; save carry for odd size
tax
jsr @calc
plp
bcc @done
clc
lda (ip_cksum_ptr),y
adc cksum
sta cksum
bcc @done
inc cksum + 1
bne @done
inc cksum + 2
@done:
lda cksum + 2 ; add carries back in
clc
adc cksum
pha
lda cksum + 1
adc #0
eor #$ff ; return inverted result
tax
pla
eor #$ff
rts
@calc:
ldy #0 ; 1's complement 16-bit sum
@next:
clc
lda (ip_cksum_ptr),y
adc cksum
sta cksum
iny
lda (ip_cksum_ptr),y
adc cksum + 1
sta cksum + 1
bcc :+
inc cksum + 2
: iny
dex
bne @next
rts