;
; hello7.1.asm
; Neil Gershenfeld CBA MIT 12/14/05
; 16x2 44780 LCD hello-world
; master processor
;

.include "tn13def.inc"

.def txbyte = R17
.def lcdbyte = R18
.def bitcnt = R19
.def temp = R20
.def temp1 = R21
.def temp2 = R22
.def temp3 = R23

.equ E = PB2
.equ RS = PB1
.equ RS_bit = 7

.equ DB7 = PB2
.equ DB6 = PB1
.equ DB5 = PB0
.equ DB4 = PB3

.equ txpin = PB3
		
.cseg
.org 0
rjmp reset

;
; lcd_putchar
;
lcd_putchar:
   ;
   ; set RS
   ; 
   sbi PORTB, RS
   ;
   ; load high nibble
   ;
   clr txbyte
   sbrc lcdbyte, 7
      ori txbyte, (1 << DB7)
   sbrc lcdbyte, 6
      ori txbyte, (1 << DB6)
   sbrc lcdbyte, 5
      ori txbyte, (1 << DB5)
   sbrc lcdbyte, 4
      ori txbyte, (1 << DB4)
   ;
   ; send to processor #2
   ;
   rcall putchar
   ;
   ; strobe E after delay
   ;
   nop
   nop
   nop
   nop
   nop
   nop
   nop
   cbi PORTB, E
   nop
   nop
   nop
   nop
   nop
   nop
   nop
   sbi PORTB, E
   ;
   ; wait
   ;
   rcall lcd_delay
   ;
   ; load low nibble
   ;
   clr txbyte
   sbrc lcdbyte, 3
      ori txbyte, (1 << DB7)
   sbrc lcdbyte, 2
      ori txbyte, (1 << DB6)
   sbrc lcdbyte, 1
      ori txbyte, (1 << DB5)
   sbrc lcdbyte, 0
      ori txbyte, (1 << DB4)
   ;
   ; send to processor #2
   ;
   rcall putchar
   ;
   ; strobe E after delay
   ;
   nop
   nop
   nop
   nop
   nop
   nop
   nop
   cbi PORTB, E
   nop
   nop
   nop
   nop
   nop
   nop
   nop
   sbi PORTB, E
   ;
   ; wait
   ;
   rcall lcd_delay
   ret
;
; putchar
; assumes no line driver (doesn't invert bits)
;
.equ sb = 1; number of stop bits
putchar:
   ldi bitcnt, 9+sb; 1+8+sb
   com txbyte; invert everything
   sec; set start bit
   putchar0:
      brcc putchar1; if carry set
      sbi PORTB, txpin; send a '0'
      rjmp putchar2; else
   putchar1:
       cbi PORTB, txpin	; send a '1'
       nop ; even out timing
   putchar2:
       rcall bit_delay; one bit delay
       rcall bit_delay
       lsr txbyte; get next bit
       dec bitcnt; if not all bits sent
       brne putchar0; send next bit
   ret;
;
; bit delay
;
.equ b = 17 ; 9600 bps
bit_delay:
   ldi temp, b
   bitloop:
      dec temp
      brne bitloop
   ret
;
; lcd_long_delay
;
.equ lcd_long_delay_time = 100
lcd_long_delay:
   ldi temp1, lcd_long_delay_time
   lcd_long_delay1:
      ldi temp2, lcd_long_delay_time
      lcd_long_delay2:
         ldi temp3, lcd_long_delay_time
	 lcd_long_delay3:
	    dec temp3
	    brne lcd_long_delay3
	 dec temp2
	 brne lcd_long_delay2
      dec temp1
      brne lcd_long_delay1
   ret
;
; lcd_delay
;
.equ lcd_delay_time = 100
lcd_delay:
   ldi temp1, lcd_delay_time
   lcd_delay1:
      ldi temp2, lcd_delay_time
      lcd_delay2:
	 dec temp2
	 brne lcd_delay2
      dec temp1
      brne lcd_delay1
   ret
;
; lcd_putcmd
;
lcd_putcmd:
   ;
   ; clear RS
   ; 
   cbi PORTB, RS
   ;
   ; send data bits to processor #2
   ;
   rcall putchar
   ;
   ; strobe E after delay
   ;
   nop
   nop
   nop
   nop
   nop
   nop
   nop
   cbi PORTB, E
   nop
   nop
   nop
   nop
   nop
   nop
   nop
   sbi PORTB, E
   ;
   ; wait
   ;
   rcall lcd_delay
   ret
;
; lcd_init
;
lcd_init:
   ;
   ; power-up delay
   ;
   rcall lcd_delay
   sbi PORTB, E
   ;
   ; initialization sequence
   ;
   ldi txbyte, (1 << DB5) + (1 << DB4)
   rcall lcd_putcmd
   rcall lcd_long_delay
   ldi txbyte, (1 << DB5) + (1 << DB4)
   rcall lcd_putcmd
   ldi txbyte, (1 << DB5) + (1 << DB4)
   rcall lcd_putcmd
   ;
   ; 4-bit interface
   ;
   ldi txbyte, (1 << DB5)
   rcall lcd_putcmd
   ;
   ; two lines, 5x7 font
   ;
   ldi txbyte, (1 << DB5)
   rcall lcd_putcmd
   ldi txbyte, (1 << DB7)
   rcall lcd_putcmd
   ;
   ; display on
   ;
   clr txbyte
   rcall lcd_putcmd
   ldi txbyte, (1 << DB7) + (1 << DB6) + (1 << DB5)
   rcall lcd_putcmd
   ;
   ; entry mode
   ;
   clr txbyte
   rcall lcd_putcmd
   ldi txbyte, (1 << DB6) + (1 << DB5)
   rcall lcd_putcmd
   ret

;
; lcd_print
; prints a null-terminated string
;
lcd_print:
   lcd_print_loop:
      lpm
      mov lcdbyte,R0
      cpi lcdbyte,0
      breq return
      rcall lcd_putchar
      inc zl
      rjmp lcd_print_loop
   return:
      ret
;
; strings to print
;
line_1:
   .db "Hello to",0
line_2:
   .db "the world!",0
;
; reset routine
;
reset:
   ;
   ; set stack pointer to top of RAM
   ;
   ldi temp, low(RAMEND)
   out SPL, temp
   ;
   ; init I/O pins
   ;
   cbi PORTB, txpin
   sbi DDRB, txpin
   cbi PORTB, E
   sbi DDRB, E
   cbi PORTB, RS
   sbi DDRB, RS
   ;
   ; init LCD
   ;
   rcall lcd_init
   ;
   ; infinite main loop
   ;
   main:
      ;
      ; go to zero position
      ;
      clr txbyte
      rcall lcd_putcmd
      ldi txbyte, (1 << DB5)
      rcall lcd_putcmd
      ;
      ; print first line
      ;
      ldi zl,low(line_1*2)
      ldi zh,high(line_1*2)
      rcall lcd_print
      ;
      ; move to second line
      ;
      ldi txbyte, (1 << DB7) + (1 << DB6)
      rcall lcd_putcmd
      clr txbyte
      rcall lcd_putcmd
      ;
      ; print second line
      ;
      ldi zl,low(line_2*2)
      ldi zh,high(line_2*2)
      rcall lcd_print
      ;
      ; pause
      ;
      rcall lcd_long_delay
      ;
      ; clear display
      ;
      clr txbyte
      rcall lcd_putcmd
      ldi txbyte, (1 << DB4)
      rcall lcd_putcmd
      ;
      ; repeat
      ;
      rjmp main
