; Dick Cappels' project pages projects@cappels.org 
;
; You should be able to copy and paste this text into an assembler file.
;
; The lcd initialization and cotnrol code is owned by Richard Hosking
; http://members.iinet.net.au/~richardh/avrlcd.htm and this code is for personal use only.

; Code other than the LCD initilzation code is in the public domain.
;
; No liability for use of this code is assumed. See full disclaimer on web site at
; www.projects.cappels.org.
;
;The AT90S8515 version of this code was created by Rafael Alcocer.
;Please direct questions about this impliemntation to Mr. Alcocer via his website,
; http://ralcocer-avr-projects.netfirms.com/
; http://ralcocer.netfirms.com
;
; Please email comments or corrections to projectsxcappels.org where x is the @ sign.
; (the above is to mask the address from email address gathering robots).
;
;HOME
;
; Begin assembly souce.
;
;;TLCD309B.asm
AT90S8515 version created by Rafael Alcocer, ralcocer@imedicalpr.com
.nolist
.include "8515def.inc"
.list
.def charcount =r1 ;Number of characters displayed on line being written
.def delay2 =r16 ;Temporary register.
.def temp =r17 ;Temporary register.
.def temp1 =r18
.def outchar =r19 ;Char to send by UART.
.def inchar =r20 ;Char received by UART
.def flagreg =r21 ;Flags
.def charbuf =r22 ;Char read from circular buffer
; YL ;UART circular buffer write pointer.
.def gpcount =r23 ;General purpose counter
; ZL ;UART circular buffer read pointer
; XL ;16 char line buffer pointer
.equ lbufsiz =$10
.equ circbufsiz =$0F
.equ OE =8 ;Bit 3 port B display enable (also directly addressed).
.equ RS =4 ;Bit 2 in port B display register select (also directly
;adressed)
.equ cbufbot =$60 ;Bottom of circular UART receive buffer.
.equ cbuftop =$67 ;Top of circular UART receive buffer.
.equ lbufbot =$70 ;Bottom of display line buffer.
.equ lbuftop =$7F ;Top of display line buffer.

;Baudrate Calculation
.equ clock = 4000000 ;clock frequency
.equ baudrate = 9600 ;choose a baud rate
.equ baudconstant = (clock/(16*baudrate))-1

;Flagreg bit assignments
; bit 0 Pending linefeed if high.
; bit 1
; bit 2
; bit 3
; bit 4
; bit 5
; bit 6
; bit 7
;Memory usage:
;Ring buffer from $60 through $67
;Line buffer from $70 to $7F
;******************************
.cseg
.org $00


rjmp start ; Reset
rjmp start
rjmp start
rjmp start
rjmp start
rjmp start
rjmp start
rjmp start
rjmp UartRecInt ;UART interrupt
rjmp UartRecInt ;interrupt
;rjmp UartRecInt ;UART interrupt


HelloString: ;TEXT TO BE TYPED ON FIRST LINE WHEN POWER IS APPLIED
.db "8515 LCD 1002031B"
.db 00,00
crlf:
.db $0A,$0D
.db 00,00
message1:
.db "Press any key",0
message2:
.db "please (1-16)",0
message3:
.db "You pressed",0
message4:
.db "key:",0
start:
clr XH
clr YH
clr ZH

ldi temp,low(RAMEND) ;Init Stack Pointer
out SPL,temp
ldi temp,high(RAMEND)
out SPH,temp
ldi temp,0b00000011 ;Weak pullups on inputs
out PORTB,temp
ldi temp,0b11111100
out DDRB,temp ;PORTB = all outputs except bits 0,1
ldi temp,0b11111111 ;Weak pullups on inputs
out PORTD,temp
ldi temp,0b00000000 ;PORTD - all inputs
ldi flagreg,$00 ;Set all flags to zero.
ldi temp,$00
mov charcount,temp
rcall ClearLineBuffer ;Initialize line buffer.
rcall DisplayInit
ldi temp,baudconstant
out ubrr,temp ;load baudrate
sbi ucr,txen ;Enable the UART transmitter
sbi ucr,rxen ;Enable the receiver..
rcall sendhello ;write line 1 power-up information
rcall Hometwo ;Position cursor for input on line two.
ldi YL,cbufbot ;Set Y and Z circ buff pointers to bottom.
ldi ZL,cbufbot
sbi UCR,7 ;Emable UART Interrupt.
sei ;Global interrupt flag set (enabled).

forever: ;Waiting for new data from circular buffer

;Get waiting char from circular buffer if there is one.
cp YL,ZL ;Is circular buffer read pointer already pointing to latest entry?
breq Buffempty ;If so, there is no new data in the buffer.
ld charbuf,Z+ ;If pointers are not equal, then read next char in buffer.
cpi ZL,cbuftop + 1 ;Advance circular buffer read pointer to next value.
brne NoZeroZL ;If end of buffer, wrap around to start of buffer.
ldi ZL,cbufbot

NoZeroZL: ;Handle the new character as either a control char or a displayable char.
;If bufchar is a control char,test for CR and LF
cpi charbuf,$1F ;If not a control char branch to displayable char routine..
brpl ItsDisplayable
cpi charbuf,$0D ;If this is a carriage return character,
brne noCR ;Set cursor to start of bottom line.
ldi XL,lbufbot ;Its a CR so set ponters back to start of line.
ldi temp,$00
mov charcount,temp

rcall hometwo ;Put cursor back in first column of line two.

noCR:

cpi charbuf,$0A ;If its a linfeed char then
brne NotALineFeed ;set linfeed pending flag.
ori flagreg,0b00000001

NotALineFeed:
rjmp Buffdone

ItsDisplayable: ;If not a control char then do line feed if pending then
;write to display and to line buffer.
sbrc flagreg,0 ;If linefeed i spending, then do it
rcall linefeed
cpi XL,lbuftop+1 ;Don't store if buffer at limit.
breq Xfull
st X+,charbuf
mov temp,charbuf
rcall DATA_DISPLAY

Xfull:

Buffdone:

Buffempty:
rjmp forever

SendHello: ;Send HelloString

rcall Homeone
ldi ZH,high(2*HelloString) ; Load high part of byte address into ZH
ldi ZL,low(2*HelloString) ; Load low part of byte address into ZL

moretosend:
lpm ; Load byte from program memory into r0
tst r0 ; Check if we've reached the end of the message
breq finishsendstering ; If so, return
mov temp,r0
rcall DATA_DISPLAY
adiw ZL,1 ; Increment Z registers
rjmp moretosend

finishsendstering:
ret

linefeed: ;Handle a linefeed char
;Clear line 1 (top line), copy line two to line one, clear
;line two, the position the cursor in first column of line two.
push gpcount ;*****
push YL ;******
push ZL ;*******
rcall hometwo ;Put cursor at start of line 2 so it can be cleared.
ldi gpcount,$10 ;Number of chars in line.****

clearmore:
ldi temp,$20 ;Fill line with spaces (erase).

rcall DATA_DISPLAY
dec gpcount
brne clearmore

rcall homeone ;Copy line buffer to line 1, reset XL to bottom.
ldi XL,lbufbot
ldi temp,$00
mov charcount,temp

MoreToCopy:
ld temp,X+
rcall DATA_DISPLAY
cpi XL,lbuftop + 1
brne MoreToCopy
rcall ClearLineBuffer
ldi flagreg,$0b11111100 ;Clear linefeed pending and enable flagsb.
;*************************************************
;Added to reset the buffers ,counter and flag
;*************************************************
rcall hometwo
ldi gpcount,$00 ;********
pop ZL ;*****
POP YL ;*****
pop gpcount ;****

ldi flagreg,$00 ;******Set all flags to zero.
ldi temp,$00 ;******
mov charcount,temp
ret ;Done
;*************************************************

ClearLineBuffer: ;Fill line buffer with spaces, set XL to bottom.
ldi XL,lbufbot
ldi temp,$00
mov charcount,temp
ldi temp,$20

MoreToSpace:
st X+,temp
cpi XL,lbuftop + 1
brne MoreToSpace
ldi XL,lbufbot
ret

recchar:
;sbi ucr,rxen ; set receiver bit
sbis usr,rxc ;Wait for a char.
rjmp recchar
in inchar,udr ;Read the char.
rjmp emitchar
;cbi ucr,rxen ;clear register
ret



emitchar:
mov outchar,inchar
sbis usr,udre ;wait until the register is cleared
rjmp emitchar
out udr,outchar ;send the byte
ret ;go back


HomeOne: ;Home cursor to start of first top line of display.
ldi temp,$80
rcall COMMAND_DISPLAY
ret

HomeTwo: ;Home cursor to start of bottom line of display
ldi temp,$C0
rcall COMMAND_DISPLAY
ret

;************************************************
;
; Write command to display
; command word in temp
; built in 50 usec delay; by Richard Hosking
;************************************************

COMMAND_DISPLAY:
mov temp1,temp ;Copy data to temp1
andi temp,0b11110000 ;mask off lower 4 bits, make OE and RS low
sbr temp,OE ;OE bit high
out PORTB,temp ;write upper 4 bits to display
nop ;wait 1 usec total command
nop ; Stabilize line
nop ;Added by DC to bring up to 1 us for 4 mHz clock
nop ;Added by DC to bring up to 1 us for 4 mHz clock
cbi PORTB,3 ;OE low to clock in data
swap temp1 ;get lower 4 bits
andi temp1,0b11110000 ; mask off lower 4 bits, make OE and RS low
sbr temp1,OE ;OE high
out PORTB,temp1 ;write lower 4 bits to LCD ;
nop ;
nop
nop ;Added by DC to bring up to 1 us for 4 mHz clock
nop ;Added by DC to bring up to 1 us for 4 mHz clock
;
cbi PORTB,3 ;OE low to clock in data
ldi temp,100 ;Wait about 50 usec
D4:
dec temp
brne D4
ret ; return
;******************************************************
;
; Writes data to display; incorporates 50 usec delay; data in temp
; by Richard Hosking
;********************************************************
DATA_DISPLAY:
mov temp1,temp ; Copy data to temp1
andi temp,0b11110000 ; mask off lower 4 bits
sbr temp,OE ; OE bit high
sbr temp,RS ; data/command bit high
out PORTB,temp ; write upper 4 bits to display
nop ; wait 1 usec total command
nop ; Stabilize line

cbi PORTB,3 ; OE low to clock in data;
swap temp1 ; get lower 4 bits
andi temp1,0b11110000 ; mask off lower 4 bits
sbr temp1,OE ; OE high
sbr temp1,RS
out PORTB,temp1 ; write lower 4 bits to LCD
nop ;
nop
cbi PORTB,3 ; OE low to clock in data;
ldi temp,100 ; Wait about 50 usec ;
ldi temp,200 ; Wait about 50 usec
D5:
dec temp
brne D5
ret ; return

;*********************************************************************
; Delay 2 msec with a 4MHz clock
;*********************************************************************
WAIT_2msec:
ldi temp1,$0B

D24002:
ldi Delay2,$FF

D24001:
dec Delay2
brne D24001
dec temp1
brne D24002
ret
;*************************************************************************
; Display setup
; Port B bit 3 OE Enable - clocks on neg transition
; 2 RS Register Select
; command=0, data=1

; bits 4-7 data 4 bits, high nibble first
; ASCII format
; RW grounded (write)

; Note that some commands take up to 1.6 msec for display to implement
; at a nominal display clock rate of 250 KHz; Display initialize routine after power up
; by Richard
;************************************************************************
DisplayInit: ;Initialize Display
ldi temp,50
;Wait at least 15msec after
D1: ;powerup before writing
rcall WAIT_2msec ;to display
dec temp
brne D1
ldi temp,0b00111000 ;System set
out PORTB,temp
nop
nop ;Data write cycle must be >1000usec
cbi PORTB,3 ;OE low to clock in data;
rcall WAIT_2msec ;Wait 4 msec rcall WAIT_2msec
;
ldi temp,0b00111000 ;System set
out PORTB,temp
nop
nop ; Data write cycle must be >1000usec
cbi PORTB,3 ; OE low to clock in data;
rcall WAIT_2msec ; wait at least 100usec
ldi temp,0b00111000 ;System set
out PORTB,temp
nop ;
nop ;
cbi PORTB,3 ; OE low to clock in data
rcall WAIT_2msec ; Wait at least 40usec (2 msec)
ldi temp,0b00101000 ; Function set 4 bit mode, 2 lines 5X7 pixels
rcall COMMAND_DISPLAY ; write to display -first write sets 4 bit
; Second function set added by DC
ldi temp,0b00101000 ; Function set 4 bit mode, 2 lines 5X7 pixels
rcall COMMAND_DISPLAY ; write to display -second write to set N and F
ldi temp,0b00001000 ;Display off, cursor off
; blink off
rcall COMMAND_DISPLAY ;
ldi temp,0b00000001 ; Display clear
rcall COMMAND_DISPLAY
rcall WAIT_2msec ; Need to wait 1.6 msec after clear;
ldi temp,0b00000110 ; Entry mode set
; Increment RAM, dont shift display
rcall COMMAND_DISPLAY ;
ldi temp,0b00001110 ; Display on, cursor on ,blink off
rcall COMMAND_DISPLAY;
rcall WAIT_2msec ; Need to wait 1.6 msec after clear
ret

UartRecInt:
;Uart interrupt service -Write received char into a circular buffer
push temp
rcall recchar ;Get the char from the UART.
st Y+,inchar
cpi YL,cbuftop + 1
brne NoZeroYL
ldi YL,cbufbot

NoZeroYL:
pop temp
reti




;HOME


;http://projects.cappels.org/