;-----------------------------------------------------------------------------------------------;
; LICENSE AGREEMENT:                                                                            ;
; This program is free software. You can redistribute it and/or modify it under the terms of    ;
; the GNU General Public License as published by the Free Software Foundation, either version   ;
; 2 of the License, or (at your option) any later version. No part of this code may be used     ;
; in any commercial application without prior written permission.                               ;
;                                                                                               ;
; This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY,     ;
; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     ;
; See the GNU General Public License for more details.                                          ;
;                                                                                               ;
; Copyright (c) September 5, 2005  David J. Brown                                               ;
;                                                                                               ;
;-----------------------------------------------------------------------------------------------;
;
; TinyDisplay.asm
; developed for the ATTINY13
;
; written by David J. Brown
; includes Ap Note AVR304 half duplex interrupt driven software uart code
; email: davebr@earthlink.net
; web: http://modularsynthesis.com
;
; created: September 5, 2005
; revision 0.0
;
; history:
; rev 0.0 initial code release
;
;------------------------
; hardware
;------------------------
;
; pb0			mosi / rx (not used)
; pb1			miso
; pb2			sck
; pb3			serial-0 to display
; pb4			blink-0
; pb5			reset
;
;------------------------
; registers
;------------------------
;
;				=r0					;r0 not used
;				=r1					;r1 not used
;				=r2					;r2 not used
;				=r3					;r3 not used
;				=r4					;r4 not used
;				=r5					;r5 not used
;				=r6					;r6 not used
;				=r7					;r7 not used
;				=r8					;r8 not used
;				=r9					;r9 not used
;				=r10				;r10 not used	
;				=r11				;r11 not used
;				=r12				;r12 not used
;				=r13				;r13 not used
; 				=r14 to r20 		;r14 - r20 are defined for uart global registers 
.def tmp		=r21				;r21 temp local register
;				=r22				;r22 not used
;				=r23				;r23 not used
;				=r24				;r24 delay counter
;				=r25				;r25 delay counter
;				=r26				;r26/xl not used
;				=r27				;r27/xh not used
;				=r28				;r28/yl not used
;				=r29				;r29/yh not used
;				=r30				;r30/zl not used
;				=r31				;r31/zh not used
;
;------------------------
; assembler directives
;------------------------
;
.nolist
.include "tn13def.inc"
.list
.listmac
;
;------------------------
; ram
;------------------------
;
.dseg
.org	sram_start
;
;
;------------------------
; interrupt vectors
;------------------------
;
	.cseg
	.org 0x000
;
	rjmp start						;0x000	reset
	rjmp ext_int0					;0x001	int0
	rjmp irq_none					;0x002	pcint0
	rjmp tim0_ovf					;0x003	timer0 ovf
	rjmp irq_none					;0x004	ee rdy
	rjmp irq_none					;0x005	analog comp
	rjmp irq_none					;0x006	timer0 comp-a
	rjmp irq_none					;0x007	timer0 comp-b
	rjmp irq_none					;0x008	wdt ovf
	rjmp irq_none					;0x009	adc conversion
;
irq_none:
	reti
;
;**** START OF  APPLICATION NOTE AVR304 ***********************************
;*
;* Title:		half duplex interrupt driven software uart 
;* Version:		1.0
;* Last update:	Nov 5 2002, by ŘE
;* Target:		AT90Sxxxx (All AVR Devices)
;*
;* Modified to ATTINY13 by Dave Brown
;* Sept 5, 2005
;*
;* Interrupts used:	external interrupt and timer/vounter0 overflow interrupt
;*
;* This application note describes how to make a half duplex software UART
;* on any AVR device with the 8-bit timer/counter0 and external interrupt.
;*
;* The constants N and R determine the data rate. R selects clock frequency as
;* described in the T/C Prescaler in the AVR databook. If the T/C prescaling
;* factor is denoted C, the following expression yields the data rate:
;*
;*			 XTAL
;*	 BAUD = ------		min. N*C = 17
;*			 N*C		max. N   = 170
;*
;* Absolute minimum value for N*C is 17 (which causes the interrupt flag to be 
;* set again before the interrupt is finished). Absolute maximum is 170 (due
;* to the 1.5 bit-length necessary for receive bits).
;*
;* Since the uart is half duplex, it can either send or recieve data. It can't
;* do both simoutaneausly. When idle it will automatically receive incoming
;* data, but if it is transmitting data while incoming data arrives, it will
;* ignore it. Also, if u_transmit is called without waiting for the 'BUSY' bit
;* in the 'u_status' register to become cleared, it will abort any pending 
;* reception or transmission.
;*
;* initialization
;*  1. call uart_init
;*  2. enable global interrupts (with 'sei')
;*
;* receive
;*  1. wait until RDR in 'u_status' becomes set
;*  2. read 'u_buffer'
;*
;* transmit
;*  0. initialize the usty (by executing uart_init and sei)
;*  1. wait until BUSY in 'u_status' becomes clear
;*  2. set 'u_buffer'
;*  3. call 'u_transmit'
;*
;* BAUD-rate settings
;*
;* @1MHz XTAL AND R=1
;*.equ	N=104						; 9600
;*.equ	N=52						;19200 
;*.equ	N=26						;38400
;*.equ	C=1							;Divisor
;*.equ	R=1							;R=1 when C=1
;*
;* 9600 @9.6MHz OSC					;N*C=1000
;*
.equ	N=131
.equ	C=1
.equ	R=0x02						;prescale by 8
;*
;* uart pins
;*
.equ	rx_pin=0					;Receive data pin (must be INT0)
.equ	tx_pin=3					;Transmit data pin
;*
;* uart global registers
;*
.def	u_buffer	=r14			;Serial buffer
.def	u_sr		=r15			;Status-register storage
.def	u_tmp		=r16			;Scratchregister
.def	u_bit_cnt	=r17			;Bit counter
.def	u_status	=r18			;Status buffer
.def	u_reload	=r19			;Reload-register (internal - do not use)
.def	u_transmit	=r20			;Data to transmit
;*
;* bit positions in the uart status-register
;*
.equ	RDR=0						;receive data ready bit
.equ	TD=6						;transmitting data (internal - read-only)
.equ	BUSY=7						;busy-flag (internal - read-only)
;*
;**************************************************************************
;*
;* external interrupt routine 0
;*
;* This routine is executed when a negative edge on the incoming serial
;* signal is detected. It disables further external interrupts and enables
;* timer interrupts (bit-timer) because the uart must now receive the
;* incoming data. 
;*
;* This routine sets bits in the GIMSK, TIFR0 and TIMSK0 registers. In this
;* code when the bits are set, it overwrites all other bits. This is done
;* because of the lack of available cycles when it operates at low clock
;* rate and high baud rates.
;*
;**************************************************************************
;*
ext_int0:
	in	u_sr,SREG					;save status register
	ldi	u_status,1<<BUSY			;set busy-flag and clear all others
	ldi	u_tmp,(256-(N+N/2)+(29/C))	;set timer reload-value (to 1.5 bit length.
	out	TCNT0,u_tmp					; 29 = time delay that has already been used
									; in this interrupt plus the time that will
									; be used by the time delay between timer
									; interrupt request and the actual sampling
									; of the first data bit.
	ldi	u_tmp,1<<TOV0				;clear T/C0 overflow flag
	out	TIFR0,u_tmp					; 
	ldi	u_tmp,1<<TOIE0				;enable T/C0 overflow interrupt
	out	TIMSK0,u_tmp 
	clr	u_bit_cnt					;clear bit counter
	out	GIMSK,u_bit_cnt				;disable external interrupt to now use timer
	ldi	u_reload,(256-N+(8/C))		;set timer reload-value.
	out	SREG,u_sr					;restore status register
	reti
;*
;**************************************************************************
;*
;* timer/counter 0 overflow interrupt
;*
;* This routine coordinates the transmition and reception of bits. This 
;* routine is automatically executed at a rate equal to the baud-rate. When
;* transmitting, this routine shifts the bits and sends it. When receiving,
;* it samples the bit and shifts it into the buffer.
;*
;* The serial routines uses a status register (u_status): READ-ONLY.
;*		BUSY	This bit indicates whenever the UART is busy
;*		TD		Transmit Data. Set when the UART is transmitting
;*		RDR		Receive Data Ready. Set when new data has arrived
;*				and it is ready for reading.
;*
;* When the RDR flag is set, the new data can be read from u_buffer.
;*
;* This routine also sets bits in TIMSK0. See ext_int0 description
;* 
;**************************************************************************
;*
tim0_ovf:
	in	u_sr,SREG					;save status register
	out	TCNT0,u_reload				;reload timer
	inc	u_bit_cnt					;increment bit counter
	sbrs u_status,TD				;check if transmit-bit set
	rjmp tim0_receive				;set so go to receive
	sbrc u_bit_cnt,3				;check if bit 3 in u_bit_cnt (>7) is set
	rjmp tim0_stopb					;set so stop-bit-part
	sbrc u_buffer,0					;check if lsb in buffer is 1
	sbi	PORTB,tx_pin				;1 so set transmit to 1
	sbrs u_buffer,0					;check if lsb in buffer is 0
	cbi	PORTB,tx_pin				;0 so set transmit to 0
	lsr	u_buffer					;shift buffer right
	ldi u_tmp,INTF0 
	out GIFR,u_tmp 					;clear pending external interrupt 
	out	SREG,u_sr					;restore SREG
	reti
;*
tim0_stopb:
	sbi	PORTB,tx_pin				;generate stop-bit
	sbrs u_bit_cnt,0				;check if u_bit_cnt=8 (stop-bit)
	rjmp tim0_ret					;yes so exit
tim0_complete:
	ldi u_tmp,1<<INTF0 
	out GIFR,u_tmp 					;clear pending external interrupt 
	ldi	u_tmp,1<<INT0
	out	GIMSK,u_tmp					;enable external interrupt
	clr	u_tmp 
	out	TIMSK0,u_tmp				;disable timer interrupt
	cbr	u_status,(1<<BUSY)|(1<<TD)  ;clear busy-flag and transmit-flag
tim0_ret:
	out	SREG,u_sr					;restore status register
	reti
;*
tim0_receive:
	sec								;assume high so set carry
	sbis PINB,rx_pin				;check if rx_pin is low (sample rx data)
	clc								;low so clear carry
	ror	u_buffer					;shift carry into data
	in u_tmp,SREG					;save status register
	cpi	u_bit_cnt,9					;check if u_bit_cnt=9 (verifying stop-bit)
	brne tim0_ret					;no so exit
	out	SREG,u_tmp					;yes so restore old status register
	rol	u_buffer					;rotate back data to get rid of the stop-bit
	sbr	u_status,1<<RDR				;clear busy-flag
	rjmp tim0_complete				;clear timer and re-enable external interrupt
;*
;**************************************************************************
;*
;* uart initialization
;*
;* This routine initializes the uart. It sets the timer and enables the
;* external interrupt to receive. To enable the uart the global interrupt
;* flag must be set.
;* 
;**************************************************************************
;*
uart_init:	
	ldi	u_tmp,R						;prescale 8
	out	TCCR0B,u_tmp				;start timer and set clock source
	ldi	u_tmp,1<<INT0
	out	GIMSK,u_tmp					;enable external interrupt 0
	ldi	u_tmp,1<<ISC01
	out	MCUCR,u_tmp					;interrupt on falling edge
	clr	u_status					;clear status byte
	ret
;*
;**************************************************************************
;*
;* uart transmit
;*
;* This routine initialize the uart to transmit data. The data to be sent
;* must be located in u_transmit. 
;*
;* Warning: This routine cancels all other transmissions or receptions.
;* So be careful. If a byte is being received and the timer interrupt is
;* currently running, the transmission may be corrupted. By checking the
;* BUSY-bit and/or TD-bit in the u_status register for safe transmissions.
;*
;* This routine also sets bits in TIMSK0. See ext_int0 description.
;*
;**************************************************************************
;*
uart_transmit:
	ldi	u_status,(1<<BUSY)|(1<<TD)  ;set only busy- and transmit flag
	clr	u_tmp
	out	GIMSK,u_tmp					;disable external interrupt
	ser	u_bit_cnt					;erase bit-counter (set all bits)
	mov	u_buffer,u_transmit			;copy transmit-data to buffer
	ldi	u_reload,(256-N+(8/C))		;set reload-value
	ldi	u_tmp,(256-N+(14/C))		;set timer delay to first bit
	out	TCNT0,u_tmp					;set timer reload-value (1 bit)
	ldi	u_tmp,1<<TOV0				;clear T/C0 overflow flag
	out	TIFR0,u_tmp					; 
	ldi	u_tmp,1<<TOIE0				;enable T/C0 overflow interrupt
	out	TIMSK0,u_tmp 
	cbi	PORTB,tx_pin				;clear output (start-bit)
	ret
;*
;*	END OF APPLICATION NOTE AVR304
;*
;**************************************************************************
;
;------------------------
; utility subroutines
;------------------------
;
;clears display, sends message s l o w l y
;registers used:
; z=message pointer; message terminated with 0x00
slow_msgc:
	ldi u_transmit,0x0c				;clear display
	rcall wait_transmit
slow_msg:
	clt								;clear duplicate space flag
sm_1:
	lpm u_transmit,z+				;get character
	tst u_transmit					;check for message end
	breq sm_3
	rcall wait_transmit				;send character
	cpi u_transmit,0x20				;check for space
	brne sm_2						;character
	brts sm_1						;duplicate
	set								;set duplicat space flag
	rcall wait_100m					;wait some
	rjmp sm_1
sm_2:
	clt								;clear flag on non-space
	rcall wait_100m					;wait some
	rjmp sm_1
sm_3:
	ret
;
;clears display, sends message
;registers used:
; z=message pointer; message terminated with 0x00
disp_msgc:
	ldi u_transmit,0x0c				;clear display
	rcall wait_transmit
disp_msg:
	lpm u_transmit,z+				;get character
	tst u_transmit					;check for message end
	breq dm_1
	rcall wait_transmit				;send character
	rjmp disp_msg
dm_1:
	ret
;
;transmits data and waits until done
wait_transmit:
	sbrc u_status,BUSY				;wait until transmitter ready
	rjmp wait_transmit
	rcall uart_transmit				;transmit the data
wt_1:
	sbrc u_status,TD				;wait until tranmission done
	rjmp wt_1						;redundant since waits until ready
	ret
;
;wait 1 mS @ 9.6 MHz clock
;registers used
; r25:24=temp
wait_1m:
	push r25
	push r24
	ldi r25,0xf6
	ldi r24,0xb4
wt_1m:
	adiw r25:r24,1
	brne wt_1m
	pop r24
	pop r25
	ret
;
;wait 100 mS
;registers used
; r24=temp
wait_100m:
	push r24
	ldi r24,100
wt_100m:
	rcall wait_1m
	dec r24
	brne wt_100m
	pop r24
	ret
;
;wait 500 mS
wait_500m:
	rcall wait_100m
	rcall wait_100m
	rcall wait_100m
	rcall wait_100m
	rcall wait_100m
	ret
;
;wait 1 S
wait_1s:
	rcall wait_500m
	rcall wait_500m
	ret
;
;wait 5 S
wait_5s:
	rcall wait_1s
	rcall wait_1s
	rcall wait_1s
	rcall wait_1s
	rcall wait_1s
	ret
;
;wait 10 S
wait_10s:
	rcall wait_5s
	rcall wait_5s
	ret
;
;blink 1 S
blink_1s:
	rcall wait_100m
	rcall wait_100m
	rcall wait_100m
	rcall wait_100m
	cbi portb,4						;display off
	rcall wait_100m
	rcall wait_100m
	sbi portb,4						;display on
	rcall wait_100m
	rcall wait_100m
	rcall wait_100m
	rcall wait_100m
	ret
;
;blink 5 S
blink_5s:
	rcall blink_1s
	rcall blink_1s
	rcall blink_1s
	rcall blink_1s
	rcall blink_1s
	ret
;
;blink 10 S
blink_10s:
	rcall blink_5s
	rcall blink_5s
	ret
;
;------------------------
; main program
;------------------------
;
start:
	;initialize stack and pins
	ldi tmp, low(ramend)			;initialize stack
	out spl, tmp
	ldi tmp,0b00011000				;0=input
	out ddrb,tmp					;set directions
	ldi tmp,0b11111111				;1=pullup
	out portb,tmp					;set pullups, tx high, & blink high
	;stop watchdog
	wdr								;reset watchdog timer
	in tmp,mcusr
	andi tmp,(0xff & (0<<wdrf))		;clear wdrf
	out	mcusr,tmp
	in tmp,wdtcr					;get prescalar settings
	ori	tmp,(1<<wdce)|(1<<wde)		;keep settings to prevent unintentional time out
	out	wdtcr,tmp
	ldi tmp,(0<<wde)				;turn off watch dog timer
	out wdtcr,tmp
	;initialize uart
	rcall uart_init
	sei
	rcall wait_500m					;let display initialize
	;initialize vfd
	ldi u_transmit,0x12				;scroll mode
	rcall wait_transmit
	ldi u_transmit,0x14				;cursor off
	rcall wait_transmit
	ldi u_transmit,0x03				;50% brightness
	rcall wait_transmit
start_msg:
	;display message 1
	ldi zh,high(msg_c1*2)			;1949
	ldi zl,low(msg_c1*2)
	rcall slow_msgc
	rcall wait_1s
	rcall blink_5s
	rcall wait_1s
	;display message 2
	ldi zh,high(msg_c2*2)			;55k
	ldi zl,low(msg_c2*2)
	rcall disp_msgc
	rcall wait_5s
	;display message 3
	ldi zh,high(msg_c3*2)			;1954
	ldi zl,low(msg_c3*2)
	rcall disp_msgc
	rcall wait_5s
	;display message 4
	ldi zh,high(msg_c4*2)			;original
	ldi zl,low(msg_c4*2)
	rcall disp_msgc
	rcall wait_5s
	;display message 5
	ldi zh,high(msg_c5*2)			;owner
	ldi zl,low(msg_c5*2)
	rcall disp_msgc
	rcall wait_5s
	;display message 6
	ldi zh,high(msg_c6*2)			;signoff
	ldi zl,low(msg_c6*2)
	rcall disp_msgc
	rcall wait_5s
	;blank display
	ldi u_transmit,0x0c				;clear display
	rcall wait_transmit
	rcall wait_1s
	rcall wait_1s
	;start over
	rjmp start_msg
;
msg_c1: .db " ",0x86," 1949 Chevrolet ",0x87," "
		.db "  Styleline Deluxe ",0x00
msg_c2:	.db "       55,000       "
		.db "   original miles  ",0x00
msg_c3:	.db "1954 235 engine with"
		.db " full oil pressure ",0x00
msg_c4:	.db	"  Original chrome,  "
		.db " wiring, & interior",0x00
msg_c5:	.db " Owner: Dave Brown  "
		.db " Sherwood,  Oregon ",0x00
msg_c6:	.db "Keep on crusing ...",0x00
;
;------------------------
; end of program
;------------------------
