#define ASM_FILE
#include <avr/io.h>
#include "lcd.h"


//sends an instruction to LCD
.global lcd_command
lcd_command:							//same as lcd_data but without RS
		clt
		rjmp lcd_write

//sends a databyte to LCD
.global lcd_data
lcd_data:
		set

lcd_write:
		push r18
		push r24						//Backup for sendig 2. nibble
		
		rcall lcd_wbf					//wait until LCD in ready	
		
		brtc lcd_write_1
		sbi _SFR_IO_ADDR(LCD_RS_PORT), LCD_RS			//set RS
		lcd_write_1:
	
		swap r24						//swap nibbles
		andi r24, LCD_DATAMSK			//clear upper nibble
		in r18, _SFR_IO_ADDR(LCD_D_PORT)		
		cbr r18, LCD_DATAMSK
		or r24, r18	

		out _SFR_IO_ADDR(LCD_D_PORT), r24			//output
		rcall lcd_enable				//pulse enable pin
										
		pop r24						//reload original
		andi r24, LCD_DATAMSK			//clear upper nibble 
		in r18, _SFR_IO_ADDR(LCD_D_PORT)
		cbr r18, LCD_DATAMSK
		or r24, r18	
	
		out _SFR_IO_ADDR(LCD_D_PORT), r24			//output
		rcall lcd_enable				//pulse enable pin

		brtc lcd_write_2
		cbi _SFR_IO_ADDR(LCD_RS_PORT), LCD_RS			//set RS
		lcd_write_2:

		pop r18
		ret								//return

//pulse enable pin
lcd_enable:		
		nop
		sbi _SFR_IO_ADDR(LCD_E_PORT), LCD_E 			//Enable high
		nop								//wait 4 cycles
		nop
		nop
		nop
		cbi _SFR_IO_ADDR(LCD_E_PORT), LCD_E			//Enable low
		nop
		ret

//Send command to clear display
.global lcd_clear
lcd_clear:
		push r24
		push r18
		rcall lcd_wbf					//wait until LCD in ready
		ldi r24, 0b00000001			//Clear Display
		rcall lcd_command
		pop r18
		pop r24
		ret


lcd_wbf:
	push r24
	
	wbf_loop:
	rcall lcd_read
	sbrc r24, 7
	rjmp wbf_loop
	
		ldi r24, 8					//wait 4µs after BF gets LOW				
		wait_t_add:					//refer to HR44780 Datasheet for details
			dec r24				//Time to update Address Counter (Tadd=4µs)
			tst r24				//Hitachi HD44780 Datasheet, page 25
			nop
			nop
			nop
			nop
			brne wait_t_add
		nop
	pop r24
	ret



//change line
.global lcd_setadr
lcd_setadr:
		sbr r24,0b10000000			//Write DDRAM Address 
		rcall lcd_command
		ret



.globl lcd_puts
lcd_puts:
		
		movw ZL, r24
lcd_puts_loop:
		ld 	r24,Z+					//load char from RAM
		tst r24
		breq lcd_puts_end
		
		rcall lcd_data				//write char to LCD
		rjmp lcd_puts_loop

		
lcd_puts_end:		
		ret

.global lcd_puts_P		
lcd_puts_P:
	movw ZL, r24
	
	lcd_puts_P_loop:
	lpm r24, Z+						//load a byte into iotmp						
	tst r24							//if thr read byte is zero
	breq lcd_puts_P_end					//we are ready

	rcall lcd_data

	rjmp lcd_puts_P_loop					//load next byte

lcd_puts_P_end:
	ret



.global lcd_init
//Initialisation: call at beginning of your programm
lcd_init:
		
		in r24, _SFR_IO_ADDR(LCD_D_DDR)
		sbr r24, LCD_DATAMSK			
		out _SFR_IO_ADDR(LCD_D_DDR),r24				//LCD_D_PORT as output

		in r24, _SFR_IO_ADDR(LCD_E_DDR)
		sbr r24, 1<<LCD_E			
		out _SFR_IO_ADDR(LCD_E_DDR),r24				//LCD Enable as output

		in r24, _SFR_IO_ADDR(LCD_RS_DDR)
		sbr r24, 1<<LCD_RS			
		out _SFR_IO_ADDR(LCD_RS_DDR),r24			//register Select as output

		in r24, _SFR_IO_ADDR(LCD_RW_DDR)
		sbr r24, 1<<LCD_RW		
		out _SFR_IO_ADDR(LCD_RW_DDR),r24			//Read / Write as output

		
		rcall lcd_delay5ms				//powerupwait=20ms
		rcall lcd_delay5ms
		rcall lcd_delay5ms
		rcall lcd_delay5ms

		
		in r24, _SFR_IO_ADDR(LCD_D_PORT)		
		cbr r24, LCD_DATAMSK
		ori r24, 0b00000011
		//ldi r24, 0b00000011			//in order to be HD44780 kompatible
		out _SFR_IO_ADDR(LCD_D_PORT), r24			//		>this has to be done
		rcall lcd_enable				//1		>refer to Datasheet for details
		rcall lcd_delay5ms
		rcall lcd_enable				//2
		rcall lcd_delay5ms
		rcall lcd_enable				//3
		rcall lcd_delay5ms
		
		
		in r24, _SFR_IO_ADDR(LCD_D_PORT)		
		cbr r24, LCD_DATAMSK
		ori r24, 0b00000011
		//ldi r24, 0b00000010			//Set Interface to be 4 bits long
		out _SFR_IO_ADDR(LCD_D_PORT), r24
		rcall lcd_enable
		rcall lcd_delay5ms

		
		#ifdef ks0073
			ldi r24, FUNCTION_SET_RE
			rcall lcd_command
			ldi r24, EXT_FUNCTION_SET
			rcall lcd_command
		#endif		
		
		ldi r24, FUNCTION_SET			//Function set
		rcall lcd_command
		ldi r24, DISPLAY_STATUS		//Display on, cursor off, blink off
		rcall lcd_command
		ldi r24, ENTRY_MODE			//Entry Mode set
		rcall lcd_command


		
		
		rcall lcd_clear					//clear screen

		ldi r24, LCD_LINE1
		rcall lcd_setadr		
		
		ret




//reads the actual DDRAM Address
.global lcd_readadr
lcd_readadr:
	rcall lcd_wbf
	rcall lcd_read
	andi r24, 0b01111111
	ret


lcd_read:
		push r18
	
		in r18, _SFR_IO_ADDR(LCD_D_DDR)
		cbr r18, LCD_DATAMSK
		out _SFR_IO_ADDR(LCD_D_DDR), r18			//set DB4..DB7 as input
		
		sbi _SFR_IO_ADDR(LCD_RW_PORT),LCD_RW			//set R/W to HIGH
		nop								//dont know if really needed :-)
	

		sbi _SFR_IO_ADDR(LCD_E_PORT),LCD_E			//enable LCD
		nop
		nop
		nop
		nop

		in r18, _SFR_IO_ADDR(LCD_D_PIN)				//read HIGH nibble
		cbi _SFR_IO_ADDR(LCD_E_PORT),LCD_E			//disable LCD

		
		andi r18, 0b00001111			//prepare for LOW Nibble
		swap r18
		mov r24,r18
		nop

		sbi _SFR_IO_ADDR(LCD_E_PORT),LCD_E			//enable LCD
		nop
		nop
		nop
		nop

		in r18,_SFR_IO_ADDR(LCD_D_PIN)			//read LOW nibble
	
		cbi _SFR_IO_ADDR(LCD_E_PORT),LCD_E
	
	
		andi r18,0b00001111			//combine HIGH and LOW Nibble
		or r24,r18
		
		cbi _SFR_IO_ADDR(LCD_RW_PORT),LCD_RW
		
		in r18, _SFR_IO_ADDR(LCD_D_DDR)
		sbr r18, LCD_DATAMSK
		out _SFR_IO_ADDR(LCD_D_DDR), r18				//set DB4..DB7 back output
						
		pop r18		
		
		ret

//5ms Delay
lcd_delay5ms:
	nop					
	push r18
	push r19

          ldi  R18, 0x41
WGLOOP00: ldi  R19, 0xCC
WGLOOP01: dec  R19
          brne WGLOOP01
          dec  R18
          brne WGLOOP00

          ldi  R18, 0x02
WGLOOP02: dec  R18
          brne WGLOOP02

          nop

	pop	r19
	pop r18
	ret
