;All Keyboard Code written by George Hotz
;The LCD Code was "Adapted" from Ubicom's VP

device sx28l,osc4mhz,turbo,stackx_optionx

reset main2

;watch key,8,uhex

key equ $0C
temp equ $0D
temp2 equ $0E
stat equ $0F
lcd_temp	equ	$08	
lcd_count	equ	$09	
dlycnt1		equ	$0A	
dlycnt2		equ	$0B	

int_period	equ	217

lcd_control	=	ra

lcd_		=	ra.0		; This ra output could be used
					; for backlight or contrast.
lcd_RS		=	rb.0		; 0 = instruction, 1 = data
lcd_RW		=	ra.2		; 0 = write, 1 = read
lcd_E		=	ra.3		; 1,1-->0 is the LCD enable

lcd_data	= 	rb

lcd_DB4		=	rb.4		; DB4 = Data bus line 4 (LSB)
lcd_DB5		=	rb.5
lcd_DB6		=	rb.6
lcd_DB7		=	rb.7		; DB7 = Data bus line 7 (MSB)

; LCD Definitions
; LCD_Interface Constants.
;  Commands
LCD_Clear		equ	%00000001
LCD_Cursor_Home		equ	%00000010
LCD_Entry_Mode		equ	%00000100
LCD_Display_control	equ	%00001000
LCD_Cursor		equ	%00010000
LCD_Function		equ	%00100000
LCD_Set_CGRam		equ	%01000000
LCD_Set_DDRam		equ	%10000000
;  Control Bits
LCD_S			equ	%00000001	; Display Shift
LCD_B			equ	%00000000	; Cursor Blink
LCD_ID			equ	%00000010	; Cursor Position Decrement
LCD_C			equ	%00000000	; Cursor off/ on
LCD_RL			equ	%00000100	; Cursor direction (Right = 1 left =0)
LCD_F			equ	%00000100	; Character resolution 1= 5x10, 0= 5x7
LCD_D			equ	%00000100	; Display 0n = 1 off = 0
LCD_SC			equ	%00001000	; Shift 0=cursor 1=diaplay
LCD_N			equ	%00001000	; Number od lines 0 for 1 line 
LCD_DL			equ	%00010000	; Data lines 0 for 4 data lines 1 for 8 data lines
LCD_BF			equ	%10000000	; Busy Flag,  0 = not busy


LCD_Delay	equ	255

main2
mode $0f
mov stat,#0
call keyreset
mov !rb,#%11110000

		call 	@lcd_init
		mov 	W, #LCD_Clear		; Clear the screen
		call 	@lcd_write_command	
		call 	@lcd_wait_busy		; Wait for the LCD

main
call scankey		;Gets one key

		mov 	W, #LCD_Clear		; Clear the screen
		call 	@lcd_write_command	
		call 	@lcd_wait_busy		; Wait for the LCD
call NCS		;Sets Num, Caps, or Scroll lock in var stat
call LCD		;Prints to LCD
jmp main












;-----------------Start of Subroutines--------------------

;---------For LCD----------------
LCD
		mov 	W, #36
		call 	@lcd_write_data
		call 	@lcd_wait_busy		; Wait
		mov	w,#LCD_Delay

		
mov temp,key
rr temp
rr temp
rr temp
rr temp
and temp,#$F
cja temp,#9,ok
add temp,#48
jmp ok2
ok
cjbe temp,#9,ok2
add temp,#55
ok2
		mov 	W, temp	
		call 	@lcd_write_data
		call 	@lcd_wait_busy		; Wait
		mov	w,#LCD_Delay
mov temp,key
and temp,#$F
cja temp,#9,ok3
add temp,#48
jmp ok4
ok3
cjbe temp,#9,ok4
add temp,#55
ok4
		mov 	W, temp	
		call 	@lcd_write_data
		call 	@lcd_wait_busy		; Wait
		mov	w,#LCD_Delay
jnb stat.3,erend;
		mov 	W, #32	
		call 	@lcd_write_data
		call 	@lcd_wait_busy		; Wait
		mov	w,#LCD_Delay
		mov 	W, #69	
		call 	@lcd_write_data
		call 	@lcd_wait_busy		; Wait
		mov	w,#LCD_Delay
		mov 	W, #88	
		call 	@lcd_write_data
		call 	@lcd_wait_busy		; Wait
		mov	w,#LCD_Delay
		mov 	W, #84	
		call 	@lcd_write_data
		call 	@lcd_wait_busy		; Wait
		mov	w,#LCD_Delay

erend
ret

;-------End For LCD--------------
NCS
mov temp2,key
mov temp,stat
cjne temp2,#$58,Caps
jb stat.2,Caps2
setb stat.2
jmp Caps
Caps2
snb stat.2
clrb stat.2
Caps
cjne temp2,#$77,Num
jb stat.1,Num2
setb stat.1
jmp Num
Num2
snb stat.1
clrb stat.1
Num
cjne temp2,#$7E,Scroll
jb stat.0,Scroll2
setb stat.0
jmp Scroll
Scroll2
snb stat.0
clrb stat.0
Scroll
cje temp,stat,ng
mov key,#$ED
call tbyte 
mov w,stat
and w,#%00000111
mov key,w
call tbyte
ng
ret

keyreset
mov key,#$FF
call tbyte 
call rbyte
cje key,#$AA,go
jmp main2
go
ret

scankey		;Carry on for ext, Byte in key
call rbyte
cjne key,#$E0,noext
call rbyte
call rbyte
call rbyte

call rbyte
setb stat.3
ret
noext
call rbyte


call rbyte

clrb stat.3
ret

rbyte
call ignore

mov temp,#8

again 
jb ra.0,again
stc
jb ra.1,noclc
clc
noclc

rr key
again2 
jnb ra.0,again2
djnz temp,again

call ignore
call ignore
ret




ignore
jb ra.0,ignore   ;After Falling Edge
ignore2 
jnb ra.0,ignore2 ;After Rising Edge
ret



tbyte
		
mov temp2,#0
mov !RA,#%00000001
mov RA,#0
fall
jb ra.0,fall   ;After Falling Edge

mov temp,#8

send1
jnb ra.0,send1    ;After Rising Edge
movb ra.1,key.0
snb key.0
inc temp2
rr key
send2 
jb ra.0,send2     ;After Falling Edge
djnz temp,send1


send3
jnb ra.0,send3     ;After Rising Edge
not temp2
movb ra.1,temp2.0
send4
jb ra.0,send4      ;After Falling Edge
clrb ra.1
mov !ra,#%00000011
	
call ignore
call ignore
call rbyte


ret

org	$200
;*****************************************************************************************
; Jump table for page 1
; Enables CALLs to functions in the second half of the page 
;*****************************************************************************************
function_label_2	jmp	function_label_2_

;*****************************************************************************************
; Subroutines
;*****************************************************************************************

	;*********************************************************************************
	; Function: 			lcd_init
	; 
	; Inputs:			None
	;	
	; Outputs:			None
	;	
	; Registers affected:		W
	;
	; Functions Called:		lcd_wait_busy,  lcd_write_command
	;
	;*********************************************************************************
	;*************************************************************************
	; LCD initialization code.
	; This code should be called at the beginning of the program to 
	; initialize the LCD display.  It only needs to be called once.
	;*************************************************************************


lcd_init
		mov 	W,#0			; Delays for 5.1ms at 50MIPS
		call 	@delay
	; First, set the data length, number of display lines, and character font.
	;------------------------------------------------------------------------------------------------------
	;	RS-RA2 R/!W-RA3 DB7-RB7	DB6-RB6	DB5-RB5	DB4-RB4	DB3-RB3	DB2-RB2	DB1-RB1	DB0-RB0	Execution Time
	;	0	0	0	0	1	DL	N	F	*	*	40us
	;------------------------------------------------------------------------------------------------------
	; DL--Interface Data Length  		0 = 4-bit interface		1 = 8-bit interface
	; N --Number of Display Lines		0 = 1 line			1 = 2 lines
	; F --Character Font			0 = 5*7 dots			1 = 5*10 dots

		call	@lcd_wait_busy		; wait until the LCD is done initializing.
		mov 	W, #LCD_Function | LCD_C; to be sure, duplicate command to change to 4 bits on lower 4.
		call	@lcd_write_command
		call	@nopdel
		call	@nopdel
		setb 	lcd_E		; Pulse LCD's enable pin.
		call	@nopdel
		call	@nopdel
		clrb 	lcd_E		; Force LCD to latch the data present on the data bus.


		call 	@lcd_wait_busy		; Wait until the LCD is finished processing.

		mov 	W, #LCD_Function | LCD_N		; 
		call 	@lcd_write_command 	; set for for 4 bits, 2 lines, and 5*7 dots
		call 	@lcd_wait_busy		; Wait until the LCD is finished processing.


	; Next, turn the display on, turn the cursor on, and turn cursor blink on (so we know LCD is alive)
	;------------------------------------------------------------------------------------------------------
	;	RS-RA2 R/!W-RA3 DB7-RB7	DB6-RB6	DB5-RB5	DB4-RB4	DB3-RB3	DB2-RB2	DB1-RB1	DB0-RB0	Execution Time
	;	0	0	0	0	0	0	1	D	C	B	40us
	;------------------------------------------------------------------------------------------------------
	; D --Display ON/OFF control		0 = Display OFF			1 = Display ON
	; C --Cursor ON/OFF control		0 = Cursor OFF			1 = Cursor ON
	; B --Blink ON/OFF control		0 = Blink OFF			1 = Blink ON

		clr	W
		call 	@lcd_write_command
		call	@lcd_wait_busy		; Display off

		mov 	W, #LCD_Display_Control | LCD_D | LCD_C | LCD_B 

		call 	@lcd_write_command 	; turn display on, cursor on, and blink on..
		call 	@lcd_wait_busy		; Wait until the LCD is finished processing.

	; Next, set display so that the cursor moves as characters are entered.
	;------------------------------------------------------------------------------------------------------
	;	RS-RA2 R/!W-RA3 DB7-RB7	DB6-RB6	DB5-RB5	DB4-RB4	DB3-RB3	DB2-RB2	DB1-RB1	DB0-RB0	Execution Time
	;	0	0	0	0	0	1	S/C	R/L	*	*	40us
	;------------------------------------------------------------------------------------------------------
	; S/C--Cursor move/Display Shift	0 = Cursor Move			1 = Shift Display 
	; R/L--Shift Direction			0 = Shift left			1 = Shift right

		mov 	W, #LCD_Cursor
		call 	@lcd_write_command 	; set for cursor move and display shift.
		call 	@lcd_wait_busy		; Wait until the LCD is finished processing.
	
	; Next, set entry mode (cursor move direction, shift or no shift).
	;------------------------------------------------------------------------------------------------------
	;	RS-RA2 R/!W-RA3 DB7-RB7	DB6-RB6	DB5-RB5	DB4-RB4	DB3-RB3	DB2-RB2	DB1-RB1	DB0-RB0	Execution Time
	;	0	0	0	0	0	0	0	1	I/D	S	40us ~ 1.64ms
	;------------------------------------------------------------------------------------------------------
	; I/D--Increment/Decrement address	0 = Decrement Cursor Address	1 = Increment Cursor Address 
	; S  --Display shift			0 = No shift			1 = Shift

		mov 	W, #LCD_Entry_Mode | LCD_ID
		call 	@lcd_write_command 	; set for incrementing address and no shift..
		call 	@lcd_wait_busy		; Wait until the LCD is finished processing.
	

		retp	; Return fron lcd_init

	;*********************************************************************************
	; Function:			lcd_write_command 
	; 
	; Inputs:			Command to write in W
	;	
	; Outputs:			None
	;	
	; Registers affected:		W ,  lcd_temp , lcd_count
	;
	; Functions Called:		nopdel,    NOTE: shared body with lcd_write_data  
	;
	;*********************************************************************************
lcd_write_command
	;------------------------------------------------------------------------------------------------------
	; This function writes the command in W to the LCD display, using the 8-bit interface.  The procedure is:
	; 1.  Clear RS
	; 2.  Set up R/!W
	; 3.  Write the data to the port
	;------------------------------------------------------------------------------------------------------
		clrb 	lcd_RS		; Drive RS low so LCD knows to write COMMAND.
		jmp 	lcd_write	; goto WRITE code

	;*********************************************************************************
	; Function: 			lcd_write_data
	; 
	; Inputs:			Data byte to write in W
	;	
	; Outputs:			None
	;	
	; Registers affected:		W,  lcd_temp , lcd_count
	;
	; Functions Called:		nopdel, NOTE BODY shared with lcd_write_command
	;
	;*********************************************************************************
lcd_write_data
	;------------------------------------------------------------------------------------------------------
	; This function writes the data in W to the LCD display, using the 8-bit interface.
	; 1.  Set RS
	; 2.  Set up R/!W
	; 3.  Write the data to the port
	;------------------------------------------------------------------------------------------------------

		setb 	lcd_RS		; Drive RS high so LCD knows to write DATA.

lcd_write	

		mov	lcd_temp,W	
		mov	lcd_count,#2 
		mov	w,#00h
		mov	!lcd_data,w	; Switch the data pins to outputs
		clrb 	lcd_RW 		; Drive R/!W low so LCD knows to WRITE.

lcd_write_loop
		and	lcd_data,#0Fh	; don't destroy the data in the lower 4 latches
		mov	W,lcd_temp
		and	W,#0f0h
		or	lcd_data,W	; Write the data in W to the port latches.
		call	@nopdel
		call	@nopdel
		setb 	lcd_E		; Pulse LCD's enable pin.
		call	@nopdel
		call	@nopdel
		clrb 	lcd_E		; Force LCD to latch the data present on the data bus.
		call	@nopdel
		call	@nopdel
		swap	lcd_temp
		decsz	lcd_count
		jmp	lcd_write_loop


		retp			; Return from lcd_write_command and lcd_write_data

	;*********************************************************************************
	; Function: 			lcd_wait_busy
	; 
	; Inputs:			None
	;	
	; Outputs:			None
	;	
	; Registers affected:		W
	;
	; Functions Called:		nopdel
	;
	;*********************************************************************************
lcd_wait_busy

	; waits until the LCD is ready to accept a command.
	;------------------------------------------------------------------------------------------------------
	;	RS-RA2 R/!W-RA3 DB7-RB7	DB6-RB6	DB5-RB5	DB4-RB4	DB3-RB3	DB2-RB2	DB1-RB1	DB0-RB0	Execution Time
	;	0	1	BF	* ------------------DDRAM Address-------------- *	1us
	;------------------------------------------------------------------------------------------------------

		or	W,#0f0h
		mov	!lcd_data,W
		clrb 	lcd_RS		; clear RS for instruction
		setb 	lcd_RW		; set for READ.	
		call	@nopdel
		call	@nopdel
		setb 	lcd_E		; set enable high to read busy flag
		call	@nopdel	
		call	@nopdel		; wait for the LCD to tx data.	
		mov	W,lcd_data	; move the contents of the port to the W register
		clrb 	lcd_E		; clear LCD enable
		call	@nopdel
		call	@nopdel
		call	@nopdel		; wait for the LCD to tx data.	
		setb 	lcd_E		; set enable high to read busy flag
		call	@nopdel	
		call	@nopdel		; wait for the LCD to tx data.	
		clrb 	lcd_E		; clear LCD enable
		call	@nopdel		; Give the LCD some time to itself.
		call	@nopdel
		call	@nopdel
		call	@nopdel
		call	@nopdel
		and 	W, #LCD_BF	; test W for zero (Z is cleared if LCD is busy)
		sb	Z		; Busy Flag is MSB of data bus
		jmp	lcd_wait_busy	; loop through again if the LCD is still busy
		setb	lcd_RW
		retp

	;*********************************************************************************
	; Function: 			nopdel
	; 
	; Inputs:			None
	;	
	; Outputs:			None
	;	
	; Registers affected:		None
	;
	; Functions Called:		None
	;
	;*********************************************************************************
nopdel	;	returns to main program in 11 cycles (11us@1MIPS) from call
		nop
		nop 
		nop
		nop
		nop
		nop
		nop
		nop
		retp		; return from nopdel

	;*********************************************************************************
	; Function: 			delay
	; 
	; Inputs:			value passed in W
	;	
	; Outputs:			None
	;	
	; Registers affected:		W, dlycnt1, dlycnt2
	;
	; Functions Called:		None
	;
	;*********************************************************************************
delay		; (delays for [((w-1) * 1ms )] at 1MIPS, or [((w-1) * 20us)] at 50MIPS ... 0<=W<=255) 
	;****************************************************************************************************** 
	; This function delays for ((W-1)*20us), plus/minus a few ns 
	;****************************************************************************************************** 

		mov 	dlycnt1,W 
 
delay1 
		decsz 	dlycnt1; 
		jmp 	loop1; 
		retp	; return from delay	 
 
loop1 
		mov 	w,#166; 
		mov 	dlycnt2,W; 

loop; 
		nop 
		nop 
		nop 
		decsz 	dlycnt2; 
		jmp 	loop; 
		jmp 	delay1; 

	;*********************************************************************************
	; Function: 			function_label_2_
	; 
	; Inputs:			None
	;	
	; Outputs:			None
	;	
	; Registers affected:		None
	;
	; Functions Called:		Dummy Stub function for jump table
	;
	;*********************************************************************************

function_label_2_
		retp
;*****************************************************************************************
END		;End of program code
;*****************************************************************************************
