;
; i0.14.packet.asm
;
; Neil Gershenfeld CBA MIT 11/25/06
; (c) Massachusetts Institute of Technology 2006
; Permission granted for experimental and personal use;
; license for commercial use available from MIT
;
.include "tn45def.inc"
.equ buttonbit = 2 ; pushbutton bit
.equ buttonpin = PB2 ; pushbutton pin
.equ lightpin = PB0 ; light pin
.equ clickbit = 4 ; click bit
.equ clickpin = PB4 ; click pin
.equ settle_count = 10 ; counts to wait for click to settle
.equ Web_port = 80 ; Web server port
.equ END = 192 ; SLIP definitions
.equ ESC = 219 ; "
.equ ESC_END = 220 ; "
.equ ESC_ESC = 221 ; "
.equ RAMSTART = 0x60 ; beginning of SRAM
.equ source_address_byte_1 = 10 ; first source address byte
.equ source_address_byte_2 = 0 ; second source address byte
.equ source_address_byte_3 = 0 ; third source address byte
.equ source_address_byte_4 = 3 ; fourth source address byte
.equ destination_address_byte_1 = 10 ; first destination address byte
.equ destination_address_byte_2 = 0 ; second destination address byte
.equ destination_address_byte_3 = 0 ; third destination address byte
.equ destination_address_byte_4 = 1 ; fourth destination address byte
.def zero = R1 ; 0
.def one = R2 ; 1
.def bytecount = R3 ; byte counter
.def doublecount = R4 ; double loop count
.def triplespace = R5 ; triple click spacing
.def check_lo = R6 ; lo checksum accumulator
.def check_hi = R7 ; hi checksum accumulator
.def check_carry = R8 ; checksum carry accumulator
.def temp = R16 ; temporary register
.def temp1 = R17 ; temporary register
.def txbyte = R18 ; byte to transit
.def rxbyte = R19 ; receive byte
.def bitcount = R20 ; bit counter
.def clickspace = R21 ; click spacing
.def count = R26 ; loop counter
.def count_hi = R27 ; loop counter
; R28, 29 used for Y pointer
; R30, 31 used for Z pointer
;
; compare
; compare two memory locations
;
.macro compare
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
; compare memory location with a constant
;
.macro compare_immediate
ldi zh, high(@0)
ldi zl, low(@0)
ld temp, z
cpi temp, @1
.endmacro
;
; store_immediate
; store immediate constant to memory
;
.macro store_immediate
ldi temp, @1
sts @0, temp
.endmacro
;
; set_data
; set Y registers to point to data
;
.macro set_data
ldi yh, high(@0*2)
ldi yl, low(@0*2)
.endmacro
;
; putslip
; click char in txbyte, with SLIP mapping
;
.macro putslip
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
;
; begin code segment
;
.cseg
.org 0
rjmp reset
;
; click_duration
;
click_duration:
ldi temp, 2
click_duration_loop:
dec temp
brne click_duration_loop
ret
;
; click_delay
;
click_delay:
ldi temp, 15
click_delay_loop:
dec temp
brne click_delay_loop
ret
;
; char_delay
; delay after sending a character
;
char_delay:
ldi temp, 10
char_delay_loop:
dec temp
brne char_delay_loop
ret
;
; blink_delay
; light blink delay
;
blink_delay:
ldi temp, 255
blink_delay_loop:
ldi temp1, 255
blink_delay_loop1:
dec temp1
brne blink_delay_loop1
dec temp
brne blink_delay_loop
ret
;
; 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 = RAMSTART
.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
ldi temp, source_address_byte_1 ; source address
st z+, temp
ldi temp, source_address_byte_2 ;
st z+, temp
ldi temp, source_address_byte_3 ;
st z+, temp
ldi temp, source_address_byte_4 ;
st z+, temp
ldi temp, destination_address_byte_1 ; destination address
st z+, temp
ldi temp, destination_address_byte_2 ;
st z+, temp
ldi temp, destination_address_byte_3 ;
st z+, temp
ldi temp, 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_I0_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 bytecount
get_I0_packet_mainloop:
;
; wait for last click to settle
;
ldi count, settle_count
get_I0_packet_settle:
dec count
nop ; to even out timing for breq
brne get_I0_packet_settle
;
; wait for next click
;
clr count
get_I0_packet_waitloop:
inc count
breq get_I0_packet_timeout ; time-out if loop count overflows
sbic PINB, clickpin ; check for click
rjmp get_I0_packet_waitloop
;
; read next byte
;
rcall getclick
;
; return if byte count overflows
;
inc bytecount
breq get_I0_packet_timeout
;
; check for SLIP escape
;
cpi rxbyte, ESC
brne get_I0_packet_store_byte
;
; found escape, wait for last click to settle
;
ldi count, settle_count
get_I0_packet_escape_settle:
dec count
nop ; to even out timing for breq
brne get_I0_packet_escape_settle
;
; found escape, wait for next click
;
get_I0_packet_waitloop1:
sbic PINB, clickpin ; check for click
rjmp get_I0_packet_waitloop1
rcall getclick
cpi rxbyte, ESC_ESC
brne get_I0_packet_END
;
; store an ESC
;
ldi rxbyte, ESC
st z+, rxbyte
rjmp get_I0_packet_mainloop
get_I0_packet_END:
;
; store an END
;
ldi rxbyte, END
st z+, rxbyte
rjmp get_I0_packet_mainloop
;
; store byte
;
get_I0_packet_store_byte:
st z+, rxbyte
;
; go back for next byte if not END
;
ldi temp, END
cpse rxbyte, temp
rjmp get_I0_packet_mainloop
ret
get_I0_packet_timeout:
clr bytecount
ret
;
; send the Web page
;
send_Web_page:
;
; 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)
sbis PORTB, lightpin
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
;
; 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 for traffic before sending
;
print_packet_CSMA:
ldi temp, 255
print_packet_CSMA_loop:
sbis PINB, clickpin ; check for click during packet delay
rjmp print_packet_CSMA
dec temp
brne print_packet_CSMA_loop
;
; 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
;
; putclick
; send char in txbyte clicks
;
putclick:
ldi bitcount, 8
sec; set start bit
;
; get click pin ready for output
;
cbi PORTB, clickpin
;
; send start clicks
;
sbi DDRB, clickpin
rcall click_duration
cbi DDRB, clickpin
rcall click_delay
sbi DDRB, clickpin
rcall click_duration
cbi DDRB, clickpin
rcall click_delay
;
; send data clicks
;
putclick0:
lsr txbyte; get next bit
brcc putclick1 ; if carry set, send a 1 click
sbi DDRB, clickpin
rcall click_duration
cbi DDRB, clickpin
rcall click_delay
cbi DDRB, clickpin
rcall click_duration
cbi DDRB, clickpin
rcall click_delay
rjmp putclick2; otherwise ...
putclick1:
cbi DDRB, clickpin ; ... send a 0 click
rcall click_duration
cbi DDRB, clickpin
rcall click_delay
sbi DDRB, clickpin
rcall click_duration
cbi DDRB, clickpin
rcall click_delay
putclick2:
dec bitcount; if not all bits sent
brne putclick0; send next bit
;
; send stop clicks
;
sbi DDRB, clickpin
rcall click_duration
cbi DDRB, clickpin
rcall click_delay
sbi DDRB, clickpin
rcall click_duration
cbi DDRB, clickpin
rcall click_delay
;
; return to pull-up on click pin and return
;
sbi PORTB, clickpin
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 clickspace, settle_count
getclick_time_start:
inc clickspace
breq getclick_timeout ; check for overflow
sbic PINB, clickpin ; check for click
rjmp getclick_time_start
mov triplespace, clickspace
add triplespace, clickspace
add triplespace, clickspace
;
; decode data clicks
;
clr rxbyte
ldi bitcount, 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 PINB, clickpin ; check for click
rjmp getclick_time
;
; determine bit delay
;
mov doublecount, count
add doublecount, count
cp doublecount, triplespace
brsh getclick_zero
;
; one bit
;
sec ; set carry
ror rxbyte ; shift in carry
;
; even out 0/1 timing
;
mov count, clickspace
getclick_space:
dec count
nop ; to even out timing for breq
brne getclick_space
;
; decrement counter and output if byte received
;
dec bitcount
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 bitcount
brne getclick_bitloop
getclick_end:
;
; delay for stop clicks
;
mov count, clickspace
getclick_stop1:
dec count
nop ; to even out timing for breq
brne getclick_stop1
mov count, clickspace
getclick_stop2:
dec count
nop ; to even out timing for breq
brne getclick_stop2
ret
getclick_timeout:
ldi rxbyte, 0
ret
;
; toggle_light
; toggle the light
;
toggle_light:
sbis PORTB, lightpin
rjmp toggle_light_off
toggle_light_on:
cbi PORTB, lightpin
ret
toggle_light_off:
sbi PORTB, lightpin
ret
;
; reset
; reset routine
;
reset:
;
; set clock divider
;
ldi temp, (1 << CLKPCE)
ldi temp1, (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0) ; /1
out CLKPR, temp
out CLKPR, temp1
;
; init microcontroller
;
ldi temp, high(RAMEND) ; set stack pointer to top of RAM
out SPH, temp ; "
ldi temp, low(RAMEND) ; "
out SPL, temp ; "
sbi PORTB, lightpin ; init light pin for output
sbi DDRB, lightpin ; "
sbi PORTB, buttonpin ; init pushbutton pin for input with pull-up
cbi DDRB, buttonpin ; "
sbi PORTB, clickpin ; init click pin for input with pull-up
cbi DDRB, clickpin ; "
;
; init the packet
;
rcall init_packet
;
; main loop
;
mainloop:
;
; wait for the button to be pressed or a click to arrive
;
waitloop:
sbis PINB, buttonbit ; check for button press
rjmp button_pressed
sbic PINB, clickpin ; check for click
rjmp waitloop
;
; click received
;
click_received:
;
; 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_I0_packet
;
; return to mainloop if bytecount is zero
;
tst bytecount
breq 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
rcall toggle_light
;
; address matches, check port
;
address_matches:
check_Web_port:
compare_immediate incoming_destination_port, high(Web_port)
brne port_not_assigned
compare_immediate incoming_destination_port+1, low(Web_port)
brne port_not_assigned
;
; port matches, send Web page and blink light
;
Web_port_matches:
rcall send_Web_page
rcall toggle_light
rcall blink_delay
rcall toggle_light
rjmp mainloop
;
; port doesn't match, return to main loop
;
port_not_assigned:
rjmp mainloop
;
; button pressed
;
button_pressed:
;
; toggle light
;
rcall toggle_light
;
; wait for button to be released
;
button_pressed_loop:
sbis PINB, buttonbit
rjmp button_pressed_loop
;
; return to main loop
;
rjmp mainloop