.nolist
.include "m168def.inc" ; Same family as 328P
.list
.include "macros.inc"
;; Load a 16-bit *word* address into the given register a a byte address
.macro loadp_16_i
ldi @0, high(2 * @2)
ldi @1, low(2 * @2)
.endm
.macro load_16_i
ldi @0, high(@2)
ldi @1, low(@2)
.endm
;; Data
.dseg
buffer: .byte 0
;; Interrupt Vector
.cseg
.org 0x0000
rjmp Main
;; Libraries
.include "div.inc" ; Division routines
;; Serial
.set SERIAL_BAUD = 103 ; 9.6k @ 16Mhz
;; Initialize the UART for
Serial_Init:
; Set up control registers
; Single-speed
poke [UCSR0A, r16, (0 << U2X0)]
; Async
; n parity
; 1 stop
; 8 bits
poke [UCSR0C, r16, (0b00 << UMSEL00) | (0b00 << UPM00) | (0 << USBS0) | (0b11 << UCSZ00)]
; Baud rate
poke [UBRR0L, r16:r17, SERIAL_BAUD]
; Enable RX
; Enable TX
; 8 bits
poke [UCSR0B, r16, (1 << RXEN0) | (1 << TXEN0) | (0 << UCSZ02)]
; Done
ret
;; Send a single byte on serial port
; Input byte in r16
Serial_Send:
; Wait for idle
lds r0, UCSR0A
sbrs r0, UDRE0
rjmp Serial_Send
; Copy byte to buffer
sts UDR0, r16
; Done
ret
;; Read a single byte from serial port
; Output byte in r16
Serial_Recv:
; Wait for recv
lds r0, UCSR0A
sbrs r0, RXC0
rjmp Serial_Recv
; Copy byte from buffer
lds r16, UDR0
; Done
ret
;; Write nul-terminated string from program mem to serial port
; Input string in Z
Serial_pprint:
; Load byte to r16
lpm r16, Z+
; Quit if nul
tst r16
breq _serial_pprint_end
; Write it
rcall Serial_Send
; Continue
rjmp Serial_pprint
_serial_pprint_end:
ret
;; Write nul-terminated string from sram and \r\n to serial port
; Input string in Z
Serial_sprintln:
; Load byte to r16
ld r16, Z+
; Quit if nul
tst r16
breq _serial_sprintln_end
; Write it
rcall Serial_Send
; Continue
rjmp Serial_sprintln
_serial_sprintln_end:
; \r\n
ldi r16, 13
rcall Serial_Send
ldi r16, 10
rcall Serial_Send
; Done
ret
;; Write char to serial port
; Input byte in r16
Serial_bprint:
; ASCII offset for '0'
ldi r17, 48
mov r4, r17
; Convert
ldi r17, 100
call div8u
add r16, r4
rcall Serial_Send
mov r16, r15
ldi r17, 10
call div8u
add r16, r4
rcall Serial_Send
mov r16, r15
add r16, r4
rcall Serial_Send
ret
;; Program
message: .db "Hello World", 13, 10, 0
Main:
; Initialization
; Stack
poke [SPL, r16:r17, RAMEND]
; Enable interrupts
sei
; Init
rcall Serial_Init
; Main program
ldi ZH, high(message * 2)
ldi ZL, low(message * 2)
rcall Serial_pprint
; Echo out
sbi DDRB, PORTB5
ldi r20, 0
ldi r21, 0
ldi r22, 0
; pointer to memory buffer
ldi XH, high(buffer)
ldi XL, low(buffer)
st X, r22 ; '\0'
Main_Echo:
; blink LED
out PORTB, r20
; read
rcall Serial_Recv
push r16
; display
rcall Serial_bprint
ldi r16, 32
rcall Serial_Send
pop r16
; test for control chars
cpi r16, 127 ; DEL
breq backspace
cpi r16, 13 ; '\r'
breq cr
cpi r16, 10 ; '\n'
breq Main_Echo ; ignore
rjmp char ; buffer char
; Erase last char
backspace:
; ignore if already at zero
cpi XL, low(buffer)
ldi r23, high(buffer)
cpc XH, r23
breq echo
; use pre-decrement to store nul
st -X, r22 ; '\0'
rjmp echo
; Erase line
cr:
; seek to start
ldi XH, high(buffer)
ldi XL, low(buffer)
; store nul
st X, r22 ; '\0'
rjmp echo
char:
; buffer char
st X+, r16
st X, r22 ; '\0'
echo:
; running counter
inc r21
mov r16, r21
rcall Serial_bprint
; ' '
ldi r16, 32
rcall Serial_Send
; length
mov r16, XL
rcall Serial_bprint
; ' '
ldi r16, 32
rcall Serial_Send
; output buffer
ldi ZH, high(buffer)
ldi ZL, low(buffer)
rcall Serial_sprintln
; toggle
com r20
; continue
rjmp Main_Echo