; Source code for the Micro Marquee v2

; This is the source code (in Parallax format) for the Micro Marquee, an electronic
; hobby project that appeared in the November issue of ComputerCraft magazine. 
; The program described in the article was very straightforward, and the user
; interface was pretty crude. During the time between submission of the article
; and its publication, I completely rewrote the code. 

; The program serves as an example for those wishing to interface an LCD panel, 
; handle buttons (with delay before auto-repeat), or use the PIC's indirect 
; addressing mode. 

; This version holds part of the message being edited in a queue in the PIC's memory. 
; When the user backs up to make a correction, the program looks up the previously 
; entered letters and puts them on the display. This seems more natural than the 
; original, which overwrote old data with the currently selected character. 
; In order to manage the queue, this program uses the PIC's indirect addressing
; mode. To examine the contents of, say, file register 10, write 10 to the file selection
; register (fsr, location 4) and look in the indirect register (location 0). Fortunately, 
; nothing affects fsr other than a direct write or math operation on it, so it's safe to
; use it to keep track of the current location. 

; Questions may be addressed to the author, Scott Edwards at CompuServe 
; 72037,2612, or regular mail: 964 Cactus Wren Lane, Sierra Vista, AZ 85635. 

; Variables and equates

RS	=	ra.3	; 0 = instruction, 1 = data
E	=	ra.2	; 0 = disable,  1 = enable
LED1	=	ra.0
LED2	=	ra.1
S1	=	rb.0	; increment ASCII character
S2	=	rb.1	; decrement ASCII character
S3	=	rb.2	; enter character
S4	=	rb.3	; backspace (without erase)

; Set origin for variables above special registers. 

	org	7	; origin for variables above control registers

low	ds	1	; delay subroutines
temp	ds	1	; blip_E and advance: temporary storage
index	ds	1	; advance and wait: loop counter
flag	ds	1	; twokey: controls meaning of 2-key
position	ds	1	; display: controls LCD position
queue	ds	19

; reset code origin

	org	0

; Device data

	device	pic16c54,rc_osc,wdt_off,protect_off
	reset	start

; Begin program: 
; Initialize ports and variables while LCD automatically resets

start 	mov	ra, #0
	call	wait	; wait for LCD reset. 
	mov	!ra,#0h ; set control lines to output
	mov	!rb,#00001111b	; set data lines to output

; Send initialization commands to LCD per Hitachi instruction book, page 254

	clrb	RS
	mov	rb,#00100000b
	setb	E
	call	wait
	clrb	E
	call	wait
	mov	temp,#00100100b
	call	blip_E
	mov	temp,#00001100b
	call	blip_E
	mov	temp,#00000110b
	call	blip_E
	mov	temp,#00000001b	;clear LCD
	call	blip_E
	call	longwait
	mov	fsr,#queue
	mov	position,#10000111b
	mov	indirect,#'A'
	call	display
	clr	flag

; Poll the switches. This is the main loop of the program 
; when the user is entering a message. 

:switches	jb	S1, abcde
	jb	S2, edcba
	jb	S3, forward
	jb	S4,back
	goto	:switches

; Delay subroutines used throughout program

wait	mov	index,#2
:loop	djnz	low,:loop
	djnz	index,:loop
	ret

medwait	mov	index,#11
:loop	djnz	low,:loop
	djnz	index,:loop
	ret

longwait	mov	index,#32
:loop	djnz	low,:loop
	djnz	index,:loop
	ret

; Subroutine to send a character or command to the LCD in 4-bit mode. 
; Before calling this routine, load the variable temp with the character
; to send to the LCD and set or clear bit RS (0 for command, 1 for text). 

blip_E 	mov	rb,temp
	setb	E
	clrb	E
	swap	temp
	mov	rb,temp
	setb	E
	clrb	E
	ret

; Subroutine to display a character from the queue (fsr points to an address, 
; and the contents show up in the indirect register. 
; This routine also looks for the letters g, j, p, q, and y, and 
; substitutes versions that display with tails (descenders) on the LCD. 

display	clrb	RS
	mov	temp,position
	call	blip_E
	setb	RS
	mov	temp, indirect
	cje	temp, #'g', tails
	cje	temp, #'j', tails
	cje	temp, #'p', tails
	cje	temp, #'q', tails
	cje	temp, #'y', tails
:blip	call	blip_E
	call	medwait
	ret

; Setting bit 7 of the addresses for the tailless versions of g, j, p, q, and y
; changes them into the versions with tails. 

tails	or	temp,#10000000b
	jmp	display:blip

; Subroutine to move stored characters through a queueÑcalled whenever the 
; enter button is pressed to advance the display. 

advance	mov	temp,fsr
	mov	index, #19
	mov	fsr,#queue + 18
:loop	mov	w, indirect
	inc	fsr
	mov	indirect,W
	dec	fsr
	dec	fsr
	djnz	index, :loop
	mov	fsr, temp
:return	ret

; Increment character code and call display to display it. 
; Note that all of the routines that respond to switches have 
; two modes, one for the intial press, and another, faster one
; for a held-down key. 

abcde	call	medwait
	inc	indirect
	snb	S2
	call	twokey
	call	display
	call	longwait	; delay before auto-repeat
	mov	temp, #200
:switch	sb	S1
	goto	start:switches
	djnz	temp, :switch
:still_down	inc	indirect
	call	display
	sb	S1
	goto	start:switches
	goto	:still_down

; Subroutine to handle simultaneous press of S1 and S2 to
; display special characters: <space>, <A>, <a>, and <0>.
; This is to speed navigation around the ASCII character set.

twokey	cje	flag, #0, :blank
	cje	flag, #1, :cap_A
	cje	flag, #2, :lo_a
	cje	flag, #3, :nums
:blank	mov	indirect, #32
	inc	flag
	goto	:return
:cap_A	mov	indirect, #'A'
	inc	flag
	goto	:return
:lo_a	mov	indirect, #'a'
	inc	flag
	goto	:return
:nums	mov	indirect, #'0'
	inc	flag
:return	csbe	flag, #3
	clr	flag
	ret

; Decrease character code by one and call display to display it.
; Pass 2-key press to twokey. 

edcba	call	medwait
	dec	indirect
	snb	S1
	call	twokey
	call	display
	call	longwait	; delay before auto-repeat
	mov	temp, #200
:switch	sb	S2
	goto	start:switches
	djnz	temp, :switch
:still_down	dec	indirect
	call	display
	sb	S2
	goto	start:switches
	goto	:still_down

; Handle the enter key. If we're at the front of the queue, forging ahead
; (not backed into previously entered text), call subroutine advance. If we're
; somewhere back in the queue, just move forward in the queue (decrement
; fsr) and show the contents of the new location. 

forward	inc	position
	cjae	position, #208, run_msg
	clrb	RS
	mov	temp,#00011000b
	call	blip_E
	cje	fsr, #queue + 224, :call_adv	; 128 + 64 +32 = 224
	dec	fsr
	goto	:disp
:call_adv	call	advance
:disp	call	display
	call	longwait	; delay before auto-repeat
:switch	sb	S3
	goto	start:switches
:still_down	inc	position
	cjae	position, #208, run_msg
	clrb	RS
	mov	temp,#00011000b
	call	blip_E
	cje	fsr, #queue + 224, :call_adv2	; 128 + 64 +32 = 224
	dec	fsr
	goto	:disp2
:call_adv2	call	advance
:disp2	call	display
	call	wait
	goto	:switch

; Handle the backup key. Prevent backing up beyond the end of the queue, or 
; before the beginning of the initial display position. Flash the LEDs if the user
; tries to back up too far (:trap). 

back	cjbe	position,#135,:trap
	cjae 	fsr, #queue + 243, :trap
	inc	fsr
	dec	position
	clrb	RS
	mov	temp,#00011100b
	call	blip_E
	call	display
	call	longwait	; delay before auto-repeat
:switch	sb	S4
	goto	start:switches
:still_down	cjbe	position,#135,:trap
	cjae 	fsr, #queue + 243, :trap
	inc	fsr
	dec	position
	clrb	RS
	mov	temp,#00011100b
	call	blip_E
	call	display
	call	wait
	sb	S4
	goto	start:switches
	goto	:still_down

:trap	setb	LED1
	setb	LED2
	call	longwait
	clrb	LED1
	clrb	LED2
	jb	S4, :trap
	goto	start:switches

; Circulate the message in the LCD memory and flash the LEDs. 

run_msg	clrb	RS
	mov	temp,#00001100b
	call	blip_E
	setb	LED1
	setb	LED2
	call	longwait
:loop	mov	temp,#00011000b
	call	blip_E
	setb	LED1
	clrb	LED2
	call	longwait
	mov	temp,#00011000b
	call	blip_E
	clrb	LED1
	setb	LED2
	call	longwait
	goto	:loop

