; ; i0.3.IO.44.thtpv4.asm ; i0 three-wire I/O node demonstration code ; IPv4 version ; 2-16-2 encoding ; ; Neil Gershenfeld ; CBA MIT 11/30/08 ; ; (c) Massachusetts Institute of Technology 2008 ; Permission granted for experimental and personal use; ; license for commercial sale available from MIT. ; .include "tn44def.inc" ; ; definitions ; .equ Web_port = 80 ; Web server port .equ set_source_address_port = 1000 ; port to set board's source address .equ set_destination_address_port = 1001 ; port to set board's destination address .equ toggle_port = 1002 ; port to toggle the light .equ set_destination_flag = 1 ; status flag for setting destination address .equ set_source_flag = 2 ; status flag for setting source address .equ click_pin = PA0 ; i0 click pin .equ click_count = 5 ; loop count to wait during click .equ settle_count = 5 ; loop count to wait for click to settle .equ delay_count = 10 ; loop count to wait between clicks .equ char_delay_count = 15 ; loop count for character delay .equ csma_count = 20 ; loop count for CSMA check .equ button_bit = 5 ; button bit .equ button_pin = PA5 ; button pin .equ button_gnd = PA4 ; button ground .equ mosfet_pin = PA7 ; MOSFET pin .equ LED_pin = PA2 ; LED pin .equ LED_gnd = PA1 ; LED ground .equ END = 192 ; SLIP definitions .equ ESC = 219 ; " .equ ESC_END = 220 ; " .equ ESC_ESC = 221 ; " .equ eeprom_source_address = 0 ; EEPROM addresses .equ eeprom_destination_address = eeprom_source_address + 4 ; " ; ; registers ; .def zero = R1 ; 0 .def one = R2 ; 1 .def double_count = R3 ; double loop count .def triple_space = R4 ; triple click spacing .def check_lo = R5 ; lo checksum accumulator .def check_hi = R6 ; hi checksum accumulator .def check_carry = R7 ; checksum carry accumulator .def bit_count = R16 ; bit counter .def byte_count = R17 ; byte counter .def txbyte = R18 ; transmit byte .def rxbyte = R19 ; receive byte .def temp = R20 ; temporary storage .def temp1 = R21 ; temporary storage .def temp2 = R22 ; temporary storage .def temp3 = R23 ; temporary storage .def flags = R24 ; status flags .def click_space = R25 ; click spacing .def count = R26 ; loop counter (X low) .def count_hi = R27 ; loop counter (X high) ; R28, 29 used for Y pointer ; R30, 31 used for Z pointer ; ; macros ; ; copy ; .macro copy ; ; copy bytes between two memory locations ; copy destination, source ; ldi zh, high(@1) ldi zl, low(@1) ld temp, z ldi zh, high(@0) ldi zl, low(@0) st z, temp .endmacro ; ; compare ; .macro compare ; ; compare two memory locations ; ldi zh, high(@1) ldi zl, low(@1) ld temp, z ldi zh, high(@0) ldi zl, low(@0) ld temp1, z cp temp, temp1 .endmacro ; ; compare_immediate ; .macro compare_immediate ; ; compare memory location with a constant ; ldi zh, high(@0) ldi zl, low(@0) ld temp, z cpi temp, @1 .endmacro ; ; store_immediate ; .macro store_immediate ; ; store immediate constant to memory ; ldi temp, @1 sts @0, temp .endmacro ; ; set_data ; .macro set_data ; ; set Y registers to point to data ; ldi yh, high(@0*2) ldi yl, low(@0*2) .endmacro ; ; putslip ; .macro putslip ; ; putslip ; click char in txbyte, with SLIP mapping ; ldi temp, END cpse txbyte, temp rjmp putslipchar ; ; END char ; mov temp, txbyte ldi txbyte, ESC rcall putclick rcall char_delay ldi txbyte, ESC_END rcall putclick rcall char_delay mov txbyte, temp rjmp endputslip ldi temp, ESC cpse txbyte, temp rjmp putslipchar ; ; ESC char ; mov temp, txbyte ldi txbyte, ESC rcall putclick rcall char_delay ldi txbyte, ESC_ESC rcall putclick rcall char_delay mov txbyte, temp rjmp endputslip putslipchar: ; ; ordinary char, no escape needed ; rcall putclick rcall char_delay endputslip: .endmacro ; ; read_eeprom ; .macro read_eeprom ; ; read EEPROM location to register ; read_eeprom_loop: ; make sure EEPROM is ready for writing sbic EECR, EEPE rjmp read_eeprom_loop ldi temp, high(@1) out EEARH, temp ldi temp, low(@1) out EEARL, temp sbi EECR, EERE in @0, EEDR .endmacro ; ; write_eeprom ; .macro write_eeprom ; ; write register to EEPROM ; write_eeprom_loop: ; make sure EEPROM is ready for writing sbic EECR, EEPE rjmp write_eeprom_loop cbi EECR, EEPM1 cbi EECR, EEPM0 ldi temp, high(@0) out EEARH, temp ldi temp, low(@0) out EEARL, temp out EEDR, @1 sbi EECR, EEMPE sbi EECR, EEPE .endmacro ; ; write_sram_to_eeprom ; .macro write_sram_to_eeprom ; ; write SRAM location to EEPROM ; write_sram_to_eeprom_loop: ; make sure EEPROM is ready for writing sbic EECR, EEPE rjmp write_sram_to_eeprom_loop cbi EECR, EEPM1 cbi EECR, EEPM0 ldi temp, high(@0) out EEARH, temp ldi temp, low(@0) out EEARL, temp ldi zh, high(@1) ldi zl, low(@1) ld temp, z out EEDR, temp sbi EECR, EEMPE sbi EECR, EEPE .endmacro ; ; code segment ; .cseg .org 0 rjmp reset ; ; init_packet ; initialize outgoing packet in SRAM ; init_packet: .equ ip_header_size = 20 .equ ip_header_size_before_checksum = 10 .equ ip_header_size_after_checksum = 8 .equ udp_header_size = 8 .equ outgoing_ip_start = SRAM_START; .equ outgoing_ip_length = outgoing_ip_start + 2 .equ outgoing_ip_checksum = outgoing_ip_start + 10 .equ outgoing_source_address = outgoing_ip_start + 12 .equ outgoing_destination_address = outgoing_ip_start + 16 .equ outgoing_udp_start = outgoing_ip_start + 20 .equ outgoing_source_port = outgoing_udp_start .equ outgoing_destination_port = outgoing_udp_start + 2 .equ outgoing_udp_length = outgoing_udp_start + 4 ldi zl, low(outgoing_ip_start) ldi zh, high(outgoing_ip_start) ; ; IP ; ldi temp, 0x45 ; version = 4 (4 bits), header length = 5 32-bit words (4 bits) st z+, temp ldi temp, 0 ; type of service st z+, temp ldi temp, 0 ; packet length high byte (to be calculated) st z+, temp ldi temp, 0 ; packet length low byte (to be calculated) st z+, temp ldi temp, 0 ; identification (high byte) st z+, temp ldi temp, 0 ; identification (low byte) st z+, temp ldi temp, 0 ; flag (3 bits), fragment offset (13 bits) (high byte) st z+, temp ldi temp, 0 ; flag (3 bits), fragment offset (13 bits) (low byte) st z+, temp ldi temp, 255 ; time to live st z+, temp ldi temp, 17 ; protocol = 17 for UDP st z+, temp ldi temp, 0 ; header checksum (to be calculated) st z+, temp ldi temp, 0 ; header checksum (to be calculated) st z+, temp read_eeprom temp, eeprom_source_address ; source address byte 1 st z+, temp read_eeprom temp, eeprom_source_address+1 ; source address byte 2 st z+, temp read_eeprom temp, eeprom_source_address+2 ; source address byte 3 st z+, temp read_eeprom temp, eeprom_source_address+3 ; source address byte 4 st z+, temp read_eeprom temp, eeprom_destination_address ; destination address byte 1 st z+, temp read_eeprom temp, eeprom_destination_address+1 ; destination address byte 2 st z+, temp read_eeprom temp, eeprom_destination_address+2 ; destination address byte 3 st z+, temp read_eeprom temp, eeprom_destination_address+3 ; destination address byte 4 st z+, temp ; ; UDP ; ldi temp, 0 ; source port st z+, temp ldi temp, 0 ; source port st z+, temp ldi temp, 0 ; destination port st z+, temp ldi temp, 0 ; destination port st z+, temp ldi temp, 0 ; payload length high byte (to be calculated) st z+, temp ldi temp, 0 ; payload length low byte (to be calculated) st z+, temp ldi temp, 0 ; payload checksum (not used) st z+, temp ldi temp, 0 ; payload checksum (not used) st z+, temp ; ; null-terminated data messages ; empty_message: .db 0 on_message: .db "HTTP/1.1 200 OK",13,10,"Content-Type: text/html",13,10,13,10,"

light is on",13,10,0 off_message: .db "HTTP/1.1 200 OK",13,10,"Content-Type: text/html",13,10,13,10,"

light is off",13,10,0 ret ; ; get_I0_packet ; read an I0 packet to SRAM following starting SLIP END character, ; removing SLIP mapping ; get_packet: .equ incoming_start = outgoing_ip_start + ip_header_size + udp_header_size .equ incoming_source_address = incoming_start + 12 .equ incoming_destination_address = incoming_source_address + 4 .equ incoming_destination_port = incoming_destination_address + 6 ; ; set Z to point to start of SRAM ; ldi zl, low(incoming_start) ldi zh, high(incoming_start) clr byte_count get_packet_mainloop: ; ; wait for a click ; clr count ldi count_hi, 1 get_packet_waitloop: add count, one adc count_hi, zero breq get_packet_timeout ; time-out if loop count overflows sbic PINA, click_pin ; check i0 pin for click rjmp get_packet_waitloop ; ; read next byte ; rcall getclick ; ; return if byte count overflows ; inc byte_count breq get_packet_timeout ; ; check for SLIP escape ; cpi rxbyte, ESC brne get_packet_store_byte ; ; found escape, read next character after next click ; get_packet_waitloop1: sbic PINA, click_pin ; check i0 pin for click rjmp get_packet_waitloop1 rcall getclick cpi rxbyte, ESC_ESC brne get_packet_END ; ; store an ESC ; ldi rxbyte, ESC st z+, rxbyte rjmp get_packet_mainloop get_packet_END: ; ; store an END ; ldi rxbyte, END st z+, rxbyte rjmp get_packet_mainloop ; ; store byte ; get_packet_store_byte: st z+, rxbyte ; ; go back for next byte if not END ; ldi temp, END cpse rxbyte, temp rjmp get_packet_mainloop ret get_packet_timeout: clr byte_count ret ; ; click_duration ; delay during click ; click_duration: ldi temp, click_count click_duration_loop: dec temp brne click_duration_loop ret ; ; click_delay ; delay between clicks ; click_delay: ldi temp, delay_count click_delay_loop: dec temp brne click_delay_loop ret ; ; putclick ; send char in txbyte clicks ; putclick: ldi bit_count, 8 sec; set start bit ; ; set click pin to output ; sbi DDRA, click_pin ; ; send start clicks ; cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay ; ; send data clicks ; putclick0: lsr txbyte; get next bit brcc putclick1 ; if carry set, send a 1 click cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay sbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay rjmp putclick2; otherwise ... putclick1: sbi PORTA, click_pin ; ... send a 0 click rcall click_duration sbi PORTA, click_pin rcall click_delay cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay putclick2: dec bit_count; if not all bits sent brne putclick0; send next bit ; ; send stop clicks ; cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay cbi PORTA, click_pin rcall click_duration sbi PORTA, click_pin rcall click_delay ; ; set click pin to input with pull-up ; cbi DDRA, click_pin ; ; return ; ret ; ; getclick ; input an i0 byte following first click ; getclick: ; ; delay for first click to settle ; ldi count, settle_count getclick_settle_start: dec count nop ; to even out timing for breq brne getclick_settle_start ; ; time arrivial of second start click ; ldi click_space, (settle_count+1) ; +1 for overhead getclick_time_start: inc click_space breq getclick_timeout ; check for overflow sbic PINA, click_pin ; check for click rjmp getclick_time_start mov triple_space, click_space add triple_space, click_space add triple_space, click_space ; ; decode data clicks ; clr rxbyte ldi bit_count, 8 getclick_bitloop: ; ; delay for click to settle ; ldi count, settle_count getclick_settle: dec count nop ; to even out timing for breq brne getclick_settle ; ; time arrivial of next click ; ldi count, settle_count getclick_time: inc count breq getclick_timeout ; check for overflow sbic PINA, click_pin ; check for click rjmp getclick_time ; ; determine bit delay ; mov double_count, count add double_count, count cp double_count, triple_space brsh getclick_zero ; ; one bit ; sec ; set carry ror rxbyte ; shift in carry ; ; even out 0/1 timing ; mov count, click_space getclick_space: dec count nop ; to even out timing for breq brne getclick_space ; ; decrement counter and output if byte received ; dec bit_count brne getclick_bitloop rjmp getclick_end getclick_zero: ; ; zero bit ; clc ; clear carry ror rxbyte ; shift in carry ; ; decrement counter and output if byte received ; dec bit_count brne getclick_bitloop getclick_end: ; ; wait for stop clicks and return ; getclick_end_0_up: sbis PINA, click_pin rjmp getclick_end_0_up getclick_end_1_down: sbic PINA, click_pin rjmp getclick_end_1_down getclick_end_1_up: sbis PINA, click_pin rjmp getclick_end_1_up getclick_end_2_down: sbic PINA, click_pin rjmp getclick_end_2_down getclick_end_2_up: sbis PINA, click_pin rjmp getclick_end_2_up ret getclick_timeout: ldi rxbyte, 0 ret ; ; char_delay ; delay between characters ; char_delay: ldi temp, char_delay_count char_delay_loop: dec temp brne char_delay_loop ret ; ; packet_delay ; delay between packets ; packet_delay: ldi temp, 255 packet_delayloop: ldi temp1, 255 packet_delayloop1: dec temp1 brne packet_delayloop1 dec temp brne packet_delayloop ret ; ; print_packet ; send a packet, calculating header checksum and doing SLIP encoding ; Y registers point to data ; print_packet: ; ; count data length and store in packet ; mov zl, yl mov zh, yh clr count clr count_hi count_data_loop: adiw count, 1 lpm temp, z+ cpi temp, 0 brne count_data_loop sbiw count,1 ; don't count the null termination adiw count, udp_header_size sts outgoing_udp_length, count_hi sts outgoing_udp_length+1, count adiw count, ip_header_size sts outgoing_ip_length, count_hi sts outgoing_ip_length+1, count ; ; find the IP header checksum and store in packet ; ldi zh, high(outgoing_ip_start) ldi zl, low(outgoing_ip_start) ldi count, ip_header_size clr check_lo clr check_hi clr check_carry ip_checksum_loop: adiw zl, 1 ld temp, z dec count sbiw zl, 1 ld temp1, z add check_lo, temp adc check_hi, temp1 adc check_carry, zero adiw zl, 2 dec count brne ip_checksum_loop add check_lo, check_carry adc check_hi, zero com check_lo com check_hi sts outgoing_ip_checksum, check_hi sts outgoing_ip_checksum+1, check_lo ; ; CSMA check ; print_packet_CSMA: ldi temp, csma_count print_packet_CSMA_loop: sbis PINA, click_pin rjmp print_packet_CSMA_delay dec temp brne print_packet_CSMA_loop rjmp print_packet_CSMA_continue print_packet_CSMA_delay: ldi temp, csma_count print_packet_CSMA_delay_loop: dec temp brne print_packet_CSMA_delay_loop rjmp print_packet_CSMA print_packet_CSMA_continue: ; ; send the packet ; ldi txbyte, END ; SLIP start rcall putclick rcall char_delay ldi zh, high(outgoing_ip_start) ldi zl, low(outgoing_ip_start) ldi count, (ip_header_size + udp_header_size) print_header_loop: ; IP + UDP header ld txbyte, z putslip adiw zl, 1 dec count brne print_header_loop mov zl, yl mov zh, yh print_data_loop: ; data lpm txbyte, z+ cpi txbyte, 0 breq print_data_continue putslip rjmp print_data_loop print_data_continue: ldi txbyte, END ; SLIP end rcall putclick rcall char_delay ret ; ; handle button actions ; button_pressed: ; ; is there a pending set source packet? ; sbrc flags, set_source_flag rjmp button_pressed_set_source_address ; ; is there a pending set destination packet? ; sbrc flags, set_destination_flag rjmp button_pressed_set_destination_address ; ; is the destination address not set? ; compare_immediate outgoing_destination_address, 255 breq button_pressed_send_set_destination rjmp button_pressed_send_packet ; ; otherwise send a set destination packet ; button_pressed_send_set_destination: ; ; blink the light to acknowledge ; rcall blink rcall blink ; ; set up and send set destination packet ; store_immediate outgoing_source_port, high(set_destination_address_port) store_immediate outgoing_source_port+1, low(set_destination_address_port) store_immediate outgoing_destination_port, high(set_destination_address_port) store_immediate outgoing_destination_port+1, low(set_destination_address_port) set_data(empty_message) rcall print_packet ret button_pressed_set_source_address: ; ; blink the light to acknowledge ; rcall blink rcall blink ; ; set source address to destination address from last received packet ; copy outgoing_source_address, incoming_destination_address copy outgoing_source_address+1, incoming_destination_address+1 copy outgoing_source_address+2, incoming_destination_address+2 copy outgoing_source_address+3, incoming_destination_address+3 ; ; save source address to EEPROM ; write_sram_to_eeprom eeprom_source_address, outgoing_source_address write_sram_to_eeprom eeprom_source_address+1, outgoing_source_address+1 write_sram_to_eeprom eeprom_source_address+2, outgoing_source_address+2 write_sram_to_eeprom eeprom_source_address+3, outgoing_source_address+3 ; ; clear flags and return ; clr flags ret button_pressed_set_destination_address: ; ; blink the light to acknowledge ; rcall blink rcall blink ; ; set destination address to source address from last received packet ; copy outgoing_destination_address, incoming_source_address copy outgoing_destination_address+1, incoming_source_address+1 copy outgoing_destination_address+2, incoming_source_address+2 copy outgoing_destination_address+3, incoming_source_address+3 ; ; save destination address to EEPROM ; write_sram_to_eeprom eeprom_destination_address, outgoing_destination_address write_sram_to_eeprom eeprom_destination_address+1, outgoing_destination_address+1 write_sram_to_eeprom eeprom_destination_address+2, outgoing_destination_address+2 write_sram_to_eeprom eeprom_destination_address+3, outgoing_destination_address+3 ; ; clear flags and return ; clr flags ret button_pressed_send_packet: ; ; blink the light to acknowledge ; rcall blink ; ; set up and send toggle packet ; store_immediate outgoing_source_port, high(toggle_port) store_immediate outgoing_source_port+1, low(toggle_port) store_immediate outgoing_destination_port, high(toggle_port) store_immediate outgoing_destination_port+1, low(toggle_port) set_data(empty_message) rcall print_packet ret ; ; blink the light ; blink: in temp, PORTA ldi temp1, ((1 << LED_pin) | (1 << mosfet_pin)) eor temp, temp1 out PORTA, temp rcall packet_delay in temp, PORTA ldi temp1, ((1 << LED_pin) | (1 << mosfet_pin)) eor temp, temp1 out PORTA, temp rcall packet_delay ret ; ; send the Web page ; send_Web_page: ; ; blink the light to acknowledge ; rcall blink rcall blink ; ; set up and send Web page ; store_immediate outgoing_source_port, high(Web_port) store_immediate outgoing_source_port+1, low(Web_port) store_immediate outgoing_destination_port, high(Web_port) store_immediate outgoing_destination_port+1, low(Web_port) sbic PORTA, LED_pin rjmp Web_send_on Web_send_off: set_data(off_message) rjmp Web_send_continue Web_send_on: set_data(on_message) Web_send_continue: rcall print_packet ret ; ; toggle the light ; toggle_light: in temp, PORTA ldi temp1, ((1 << LED_pin) | (1 << mosfet_pin)) eor temp, temp1 out PORTA, temp ret ; ; main program ; reset: ; ; set fuse low byte to 0x7E for 20 MHz resonator ; ; set clock divider to /1 ; ldi temp, (1 << CLKPCE) ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0) out CLKPR, temp out CLKPR, temp1 ; ; set stack pointer to top of RAM ; ldi temp, high(RAMEND) out SPH, temp ldi temp, low(RAMEND) out SPL, temp ; ; init click pin for input with pull-up ; sbi PORTA, click_pin cbi DDRA, click_pin ; ; init button for input with pull-up ; sbi PORTA, button_pin cbi DDRA, button_pin cbi PORTA, button_gnd sbi DDRA, button_gnd ; ; init LED for output ; cbi PORTA, LED_pin sbi DDRA, LED_pin cbi PORTA, LED_gnd sbi DDRA, LED_gnd ; ; init MOSFET pin for output ; cbi PORTA, mosfet_pin sbi DDRA, mosfet_pin ; ; init registers ; clr zero ; 0 clr one ; 1 inc one ; " ; ; set source address if not assigned ; read_eeprom temp, eeprom_source_address cpi temp, 255 brne source_address_assigned source_address_not_assigned: ; ; turn on light, start counter, and wait for button to be pressed ; sbi PORTA, LED_pin clr temp1 clr temp2 clr temp3 source_address_not_assigned_loop: add temp1, one adc temp2, zero adc temp3, zero sbic PINA, button_bit rjmp source_address_not_assigned_loop ; ; wait for button to be released ; source_address_not_button_release: sbis PINA, button_bit rjmp source_address_not_button_release ; ; move address to EEPROM, acknowledge and turn off light ; write_eeprom eeprom_source_address+3, temp1 write_eeprom eeprom_source_address+2, temp2 write_eeprom eeprom_source_address+1, temp3 ldi temp1, 169 write_eeprom eeprom_source_address, temp1 rcall blink rcall blink cbi PORTA, LED_pin source_address_assigned: ; ; init the packet ; rcall init_packet ; ; start event loop ; clr flags ; clear status flags mainloop: ; ; wait for the button to be pressed or a click to arrive ; waitloop: sbis PINA, click_pin ; check i0 pin for click rjmp click sbic PINA, button_bit ; check for button rjmp waitloop ; ; button pressed ; button: ; ; wait for button to be released ; button_release: sbis PINA, button_bit rjmp button_release rcall button_pressed rjmp mainloop ; ; click arrived ; click: ; ; check for i0 SLIP start of packet (END character) ; rcall getclick ldi temp, END cpse rxbyte, temp ; ; not start of packet, go back and wait for next char ; rjmp mainloop ; ; found start, get rest of i0 packet ; rcall get_packet ; ; return to mainloop if byte_count is zero ; tst byte_count breq mainloop ; ; valid packet received, check for address assignment packets ; clr flags ; clear status flags check_set_destination_address_port: compare_immediate incoming_destination_port, high(set_destination_address_port) brne check_set_source_address_port compare_immediate incoming_destination_port+1, low(set_destination_address_port) brne check_set_source_address_port sbr flags, (1 << set_destination_flag) rcall blink rcall blink rjmp mainloop check_set_source_address_port: compare_immediate incoming_destination_port, high(set_source_address_port) brne check_address compare_immediate incoming_destination_port+1, low(set_source_address_port) brne check_address sbr flags, (1 << set_source_flag) rcall blink rcall blink rjmp mainloop ; ; check to see if incoming destination address matches ; check_address: mainloop_test_1: compare outgoing_source_address, incoming_destination_address breq mainloop_test_2 rjmp mainloop mainloop_test_2: compare outgoing_source_address+1, incoming_destination_address+1 breq mainloop_test_3 rjmp mainloop mainloop_test_3: compare outgoing_source_address+2, incoming_destination_address+2 breq mainloop_test_4 rjmp mainloop mainloop_test_4: compare outgoing_source_address+3, incoming_destination_address+3 breq address_matches rjmp mainloop ; ; address matches, check port ; address_matches: check_Web_port: compare_immediate incoming_destination_port, high(Web_port) brne check_toggle_port compare_immediate incoming_destination_port+1, low(Web_port) brne check_toggle_port rcall send_Web_page rjmp mainloop check_toggle_port: compare_immediate incoming_destination_port, high(toggle_port) brne port_not_assigned compare_immediate incoming_destination_port+1, low(toggle_port) brne port_not_assigned rcall toggle_light rjmp mainloop port_not_assigned: rjmp mainloop