; ARP address resolution .include "../inc/common.i" .export arp_init .export arp_lookup .export arp_process .export arp_add .export arp_calculate_gateway_mask .export arp_ip .export arp_mac .export arp_cache .exportzp ac_size .import eth_inp .import eth_inp_len .import eth_outp .import eth_outp_len .import eth_tx .import eth_set_broadcast_dest .import eth_set_my_mac_src .import eth_set_proto .importzp eth_proto_arp .import cfg_mac .import cfg_ip .import cfg_netmask .import cfg_gateway .import timer_read .import timer_timeout .segment "IP65ZP" : zeropage ap: .res 2 .bss ; arp state machine arp_idle = 1 ; idling arp_wait = 2 ; waiting for reply arp_state: .res 1 ; current activity ; arguments for lookup and add arp: ; ptr to mac/ip pair arp_mac: .res 6 ; result is delivered here when arp_lookup returns with carry flag clear arp_ip: .res 4 ; set arp_ip before calling arp_lookup ; arp cache ac_size = 8 ; lookup cache ac_ip = 6 ; offset for ip ac_mac = 0 ; offset for mac arp_cache: .res (6+4)*ac_size ;cache of IP addresses and corresponding MAC addresses ; offsets for arp packet generation ap_hw = 14 ; hw type (eth = 0001) ap_proto = 16 ; protocol (ip = 0800) ap_hwlen = 18 ; hw addr len (eth = 06) ap_protolen = 19 ; proto addr len (ip = 04) ap_op = 20 ; request = 0001, reply = 0002 ap_shw = 22 ; sender hw addr ap_sp = 28 ; sender proto addr ap_thw = 32 ; target hw addr ap_tp = 38 ; target protoaddr ap_packlen = 42 ; total length of packet ; gateway handling gw_mask: .res 4 ; inverted netmask gw_test: .res 4 ; gateway ip or:d with inverted netmask gw_last: .res 1 ; netmask length - 1 ; timeout arptimeout: .res 2 ; time when we will have timed out .code ;initialize arp (including clearing the arp cache) ;inputs: none ;outputs: none arp_init: lda #0 ldx #(6+4)*ac_size - 1 ; clear cache : sta arp_cache,x dex bpl :- arp_calculate_gateway_mask: lda #$ff ; counter for netmask length - 1 sta gw_last ldx #3 @gw: lda cfg_netmask,x eor #$ff cmp #$ff bne :+ inc gw_last : sta gw_mask,x ora cfg_gateway,x sta gw_test,x dex bpl @gw lda #arp_idle ; start out idle sta arp_state rts ;lookup the mac address for an ip ;inputs: arp_ip should be set to ip address to be resolved ;outputs: ; if carry flag is clear, then arp_mac will be set to correct mac address ; if carry flag is set, then the correct mac address could not be found in ; the arp cache, so an arp request was sent. so the caller should wait a while ; (to allow time for an arp response message to arrive) and then call arp_lookup again. arp_lookup: lda arp_ip ; check for broadcast IP (255.255.255.255) and arp_ip + 1 and arp_ip + 2 and arp_ip + 3 cmp #$ff bne @notbroadcast ldx #6 ;copy ff:ff:ff:ff:ff:ff to ap_mac : sta arp_mac,x dex bpl :- clc rts @notbroadcast: ldx gw_last ; check if address is on our subnet : lda arp_ip,x ora gw_mask,x cmp gw_test,x bne @notlocal dex bpl :- bmi @local @notlocal: ldx #3 ; copy gateway's ip address : lda cfg_gateway,x sta arp_ip,x dex bpl :- @local: jsr findip bcs @cachemiss ldy #ac_ip - 1 ; copy mac : lda (ap),y sta arp,y dey bpl :- rts @cachemiss: lda arp_state ; are we already waiting for a reply? cmp #arp_idle beq @sendrequest ; yes, send request ldax arptimeout ; check if we've timed out jsr timer_timeout bcs @notimeout ; no, don't send @sendrequest: ; send out arp request jsr eth_set_broadcast_dest jsr eth_set_my_mac_src jsr makearppacket ; add arp, eth, ip, hwlen, protolen lda #0 ; set opcode (request = 0001) sta eth_outp + ap_op lda #1 sta eth_outp + ap_op + 1 ldx #5 : lda cfg_mac,x ; set source mac addr sta eth_outp + ap_shw,x lda #0 ; set target mac addr sta eth_outp + ap_thw,x dex bpl :- ldx #3 : lda cfg_ip,x ; set source ip addr sta eth_outp + ap_sp,x lda arp_ip,x ; set target ip addr sta eth_outp + ap_tp,x dex bpl :- lda #ap_packlen sta eth_outp_len + 1 jsr eth_tx ; send packet lda #arp_wait ; waiting for reply sta arp_state jsr timer_read ; read current timer value clc adc #<1000 ; set timeout to now + 1000 ms sta arptimeout txa adc #>1000 sta arptimeout + 1 @notimeout: sec ; set carry to indicate that rts ; no result is availble ; find arp_ip in the cache ; clc returns pointer to entry in (ap) findip: ldax #arp_cache stax ap ldx #ac_size @compare: ; compare cache entry ldy #ac_ip lda (ap),y beq @notfound : lda (ap),y cmp arp,y bne @next iny cpy #ac_ip + 4 bne :- clc ; return rts @next: ; next entry lda ap clc adc #10 sta ap bcc :+ inc ap + 1 : dex bne @compare @notfound: sec rts ;handle incoming arp packets ;inputs: eth_inp should contain an arp packet ;outputs: ; carry flag is set if there was an error processing the arp packet, clear otherwise ; the arp_cache will be updated with the mac & ip address (whether the inbound ; message was a request or a response). if the incoming packet was an arp ; request for this machine, then an arp response will be created (overwriting ; eth_outp) and sent out arp_process: lda eth_inp + ap_op ; should be 0 bne @badpacket lda eth_inp + ap_op + 1 ; check opcode cmp #1 ; request? beq @request cmp #2 ; reply? beq @reply @badpacket: sec rts @request: ldx #3 : lda eth_inp + ap_tp,x ; check if they're asking for cmp cfg_ip,x ; my address bne @done dex bpl :- ldax #eth_inp + ap_shw jsr ac_add_source ; add them to arp cache ldx #5 ; send reply : lda eth_inp + ap_shw,x sta eth_outp,x ; set sender packet dest sta eth_outp + ap_thw,x ; and as target lda cfg_mac,x ; me as source sta eth_outp + ap_shw,x dex bpl :- jsr eth_set_my_mac_src ; me as packet source jsr makearppacket ; add arp, eth, ip, hwlen, protolen lda #0 ; set opcode (reply = 0002) sta eth_outp + ap_op lda #2 sta eth_outp + ap_op + 1 ldx #3 : lda eth_inp + ap_sp,x ; sender as target addr sta eth_outp + ap_tp,x lda cfg_ip,x ; my ip as source addr sta eth_outp + ap_sp,x dex bpl :- lda #ap_packlen sta eth_outp_len + 1 jsr eth_tx ; send packet @done: clc rts @reply: lda arp_state cmp #arp_wait ; are we waiting for a reply? bne @badpacket ldax #eth_inp + ap_shw jsr ac_add_source ; add to cache lda #arp_idle sta arp_state rts ; add arp_mac and arp_ip to the cache ;inputs: ; arp_ip is ip address to add to cache ; arp_mac is corresponding mac address of specified ip ;outputs: ; arp_cache is updated arp_add: jsr findip ; check if ip is already in cache bcs @add ldy #9 ; update old entry : lda arp,y ; move to top as well? sta (ap),y dey bpl :- rts @add: ldax #arp ; add ;add source to cache ac_add_source: stax ap ldx #9 ; make space in the arp cache : lda arp_cache + 60,x sta arp_cache + 70,x lda arp_cache + 50,x sta arp_cache + 60,x lda arp_cache + 40,x sta arp_cache + 50,x lda arp_cache + 30,x sta arp_cache + 40,x lda arp_cache + 20,x sta arp_cache + 30,x lda arp_cache + 10,x sta arp_cache + 20,x lda arp_cache,x sta arp_cache + 10,x dex bpl :- ldy #9 : lda (ap),y ; copy source sta arp_cache,y dey bpl :- rts ; adds proto = arp, hw = eth, and proto = ip to outgoing packet makearppacket: lda #eth_proto_arp jsr eth_set_proto lda #0 ; set hw type (eth = 0001) sta eth_outp + ap_hw lda #1 sta eth_outp + ap_hw + 1 lda #8 ; set protcol (ip = 0800) sta eth_outp + ap_proto lda #0 sta eth_outp + ap_proto + 1 lda #6 ; set hw addr len (eth = 06) sta eth_outp + ap_hwlen lda #4 ; set proto addr len (eth = 04) sta eth_outp + ap_protolen rts