Appendix A
SIMON Project Source Code


Note: To save this code as an .asm file, click on the hyperlink and save the file to disk; otherwise, it opens in Notepad.

; The game of Simon

.include "c:\users\geiger\4414def.inc"	; Will likely have to change
					; this file name

.device AT90S4414	; Specify chip to the assembler

.def	save 	=r1	; SREG save location
.def	maxnum	=r2	; Maximum wait time for mode 2
			; Corresponds to level of difficulty
.def	minnum	=r3	; Minimum wait time for mode 2
			; Corresponds to level of difficulty
.def	waitval	=r4	; Current number of T1 interrupts
			; for which I want to wait
.def	level	=r5	; Level of difficulty (not actually
			; used in Mode 2, but used in Mode 1)
.def	r30save	=r6	; Save registers used in outstr
.def	r31save	=r7	; subroutine to avoid writing over
			; registers
.def	lcdstat	=r8	; LCD status
.def	Temp	=r16	; Temporary register
.def	numtones=r17	; Register to keep track
			; of the number of tones stored in RAM
			; (for Mode 1)
.def	reload	=r18	; Holds Timer0 reload value
.def	toggle	=r19	; Register used to toggle output bit (A6)
.def	outreg	=r20	; Register used to hold output values to Port A
.def	count	=r21	; Interrupt count
.def	wcount	=r22	; Timer1 interrupt count--T1 will be used to
			; count wait times (Mode 2)
.def	tcount	=r23	; Count of number of tones played--used both when
			; playing tones and when counting 
			; user button presses
.def	rand	=r24	; Pseudorandom value taken from T0
.def	charcnt	=r25	; Count of characters sent to LCD
.def	rtemp1	=r26	; Temporary reload register used to test 
			; user button presses
.def	rndnum	=r27	; The number of tones to play each time, based
			; on the level

.equ	PreScal0=3	; Set timer0 prescale to 64 (use CK/64 
			; for counter/timer0)
.equ	PreScal1=2	; Set timer1 prescale to 8

; Counter reload values corresponding to the frequencies to be played 
; for each button
.equ	reload3	=0xb0	; 392 Hz (G4, switch3)
.equ	reload2	=0xb9	; 440 Hz (A4, switch2)
.equ	reload1	=0xc1	; 494 Hz (B4, switch1)
.equ	reload0	=0xc4	; 523 Hz (C5, switch0)

; Interrupt count values corresponding to the frequencies to be 
; played for each button
.equ	count3	=196	; 392 Hz (G4, switch3)
.equ	count2	=220	; 440 Hz (A4, switch2)
.equ	count1	=247	; 494 Hz (B4, switch1)
.equ	count0	=255	; 523 Hz (C5, switch0)
			; Note:  count0 should actually be
			; 261, but in order to only use
			; one register, we have to sacrifice
			; a few counts so that a tone produced
			; using that button is slightly shorter
			; than the others--.2448 seconds rather
			; than .25 seconds

.equ	maxL1	=30	; Maximum and minimum numbers of T1 interrupts
.equ	maxL2	=25	; for which the program will wait for a user
.equ	maxL3	=20	; to press a button, depending on the level of
.equ	minL1	=10	; difficulty
.equ	minL2	=5
.equ	minL3	=2

;***** Other equates 
.equ    lcdrs   =6	; LCD rs pin connected to PC6 
.equ    lcdrw   =5 	; LCD r/w pin connected to PC5 
.equ    lcde    =4  	; LCD enable pin connected to PC4 
 
; Timer/Counter prescaler values 
.equ    TSTOP	=0      ; Stop Timer/Counter 
.equ    TCK8 	=2      ; Timer/Counter runs from CK / 8 
.equ    TCK256	=4      ; Timer/Counter runs from CK / 256 
.equ    TCK1024	=5      ; Timer/Counter runs from CK / 1024 

.dseg
tones:	.byte	100	; Tone list to hold up to 100 tones in RAM
			; There is no code to handle an overflow of
			; this array because finding a person who can 
			; remember 100 tones is a highly unlikely
			; occurrence

; ***** Initialization *****
.cseg
.org $0000
	rjmp	RESET	; reset entry vector
	reti		; external interrupt 0 not used
	reti		; external interrupt 1 not used
	reti		; timer1 capture not used
	reti		; timer1 compare match A not used
	reti		; timer1 compare match B not used
	rjmp	T1ovfl	; timer1 overflow not used
	rjmp	T0ovfl	; timer0 overflow vector
	reti		; SPI transfer complete not used
	reti		; Rx complete vector not used
	reti		; Tx empty not used
	reti		; Tx complete not used
	reti		; analog comparator not used

; NOTE:  All leading spaces on the following strings are included for the
; purpose of centering the strings on the LCD
simon:	.db	"     SIMON", 0x00	; opening string
simon2:	.db	" EE 476 Project", 0x00	; sequence
simon3:	.db	" by Mike Geiger", 0x00	;
modsel:	.db	"  Select mode", 0x00	; mode select
modsel2:.db	" S1: Memory mode", 0x00; sequence
modsel3:.db	" S2: React mode", 0x00	
level1:	.db	"  Choose level", 0x00	; Level select sequence
level2a:.db	" S1: L1, S2: L2",0x00
level2b:.db	"S1:1, S2:2, S3:3",0x00
ready:	.db	"  Get ready ...", 0x00	; String used to fill the pause 
					; at the start of each game
mode1s:	.db	"     Mode 1", 0x00	; Mode 1 string
					; Displayed while playing in Mode 1
mode2s:	.db	"     Mode 2", 0x00	; Mode 2 string
					; Displayed while playing in Mode 2
end:	.db	"   Game Over", 0x00	; Game over
plagain:.db	"  Play again?", 0x00	; Play again
plagn2:	.db	"  S1: Y, S2: N", 0x00	; sequence
bye:	.db	"    Goodbye", 0x00	; To be shown if user decides
					; not to play again

RESET:	ldi	Temp, low(RAMEND)	; set stack pointer
	out	SPL, Temp
	ldi	Temp, high(RAMEND)
	out	SPH, Temp

	; LCD setup
	ldi     Temp, TSTOP      	; Timer 0 off (just in case) 
        out     TCCR0, Temp      	; Stop timer 
        ldi     Temp, 0b00000010 	; Enable Timer 0 interrupt 
        out     TIMSK, Temp
        sei                     	; Enable interrupts

       	; power down the LCD
       	ldi     Temp, 0xff      	; LCD power connection
       	out     DDRD, Temp		; power it down after reset
       	ldi     Temp, 0x00
       	out     PORTD, Temp
       	ldi     Temp, 100    		; then Wait 1.5 second with 
	mov	count, Temp		; LCD power off 
       	clt
	ldi	Temp, TCK256
	out	TCCR0, Temp 

offwait:brtc	offwait			; wait for interrupt to set 
	clt				; T bit
	tst	Count			; Count == 0?
        breq	pwrup
;	dec	Count			; Not needed here--Count is 
        rjmp	offwait			; decremented in T0ovfl ISR

; now power up LCD
pwrup:	ldi     Temp, 0xff
       	out     PORTD, Temp
       	ldi     Temp, 100    		; Wait 1.5 second for LCD 
	mov	count, Temp		; power up  
puwait: brtc	puwait 
        clt
	tst	Count
	breq	init
;	dec	Count			; As above--dec not needed here
	rjmp	puwait			; since T0ovfl ISR takes care of it

; hopefully by now the LCD has rebooted 
init:   rcall   lcdinit         	; Initialize LCD module 
	rcall	lcdclr			; clear lcd	
	
	; Output opening string sequence
	ldi	ZH, high(simon*2)
	ldi	ZL, low(simon*2)
	rcall	outstr
	rcall	wait2s
	
	ldi	ZH, high(simon2*2)
	ldi	ZL, low(simon2*2)
	rcall	outstr
	rcall	wait2s
	
	ldi	ZH, high(simon3*2)
	ldi	ZL, low(simon3*2)
	rcall	outstr
	rcall	wait2s
		
	ser	Temp			; set Port A to be
	out	DDRA, Temp		; all outputs
	out	DDRB, Temp		; set Port B to be all outputs
	out	PORTB, Temp		; and clear LEDs
	out	PORTD, Temp		; Turn on Port D pullups
	ldi	Temp, 0b10000000	; Set Port D to all inputs except 
	out	DDRD, Temp		; for bit 7--LCD power

	clr	reload			; clear reload register
	out	TCNT0, reload		; and preset counter to 0
	
	ldi	Temp, 0b10000010	; enable timer0 and timer1
	out	TIMSK, Temp		; interrupts
	
	ldi	Temp, PreScal0		; prescale timer0 by 64
	out	TCCR0, Temp		; turn on timer0

	ldi	toggle, 0x40		; set bit 6 of toggle register

main:	clr	outreg		; clear output register
	clr	numtones	; clear number of tones
	clr	count		; clear T0 interrupt count	
	clr	wcount		; clear T1 interrupt count
	clr	tcount		; clear tone count
	
	ldi	ZL, low(tones)		; set Z register to point to
	ldi	ZH, high(tones)		; RAM string
	
	; Save the address of tones
	mov	r30save, r30
	mov	r31save, r31
	
	; Display mode select sequence
	ldi	ZH, high(modsel*2)	; Display "Select mode"
	ldi	ZL, low(modsel*2)
	rcall	outstr
	rcall	wait2s

	ldi	ZH, high(modsel2*2)	; Display "S1: Memory mode"
	ldi	ZL, low(modsel2*2)
	rcall	outstr
	rcall	wait2s
	
	ldi	ZH, high(modsel3*2)	; Display "S2: React mode"
	ldi	ZL, low(modsel3*2)
	rcall	outstr
	rcall	wait2s

	ldi	ZH, high(modsel*2)	; Now return display to
	ldi	ZL, low(modsel*2)	; "Select mode" and
	rcall	outstr			; wait for button press
	
mpoll:	sbis	PIND, 0		; Pick mode
	rjmp	mode1
	sbis	PIND, 1
	rjmp	mode2
	rjmp	mpoll

mode1:	ldi	Temp, 0x01	; Set T1 to CK
	out	TCCR1B, Temp
	ldi	rndnum, 1
	sbis	PIND, 0		; First, wait for switch 0 to
	rjmp	mode1		; be released

	ldi	ZH, high(level1*2)	; Display "Choose level"
	ldi	ZL, low(level1*2)
	rcall	outstr
	rcall	wait2s
	
	ldi	ZH, high(level2a*2)	; Display "S1: L1, S2: L2"
	ldi	ZL, low(level2a*2)
	rcall	outstr
	
mode1a:	clr	Temp		; Set the level of difficulty
	sbis	PIND, 0		; Switch 0 => level 1
	ldi	Temp, 1		; (Only adds one new tone 
				; to the sequence each time)
	sbis	PIND, 1		; Switch 1 => level 2
	ldi	Temp, 2		; (Adds a random number 
				; (between 1 and 4) of new
				; tones to the sequence each time)
	tst	Temp
	breq	mode1a
	subi	Temp, 1		; reduce level by 1 (represent 
				; level 1 as a 0 in the 
	mov	level, Temp	; level register so that the 
				; level can be determined 
				; by a simple tst operation, 
				; rather than a set of
				; cpi operations)
	
	; Wait 
	ldi	ZH, high(ready*2)	; Display "Get
	ldi	ZL, low(ready*2)	; ready ..."
	rcall	outstr
	rcall	wait2s		; Wait for two seconds before 
				; starting game

	; Now display "Mode 1"
	ldi	ZH, high(mode1s*2)
	ldi	ZL, low(mode1s*2)
	rcall	outstr

	mov	r30, r30save	; Restore Z reg.--holds address 
	mov	r31, r31save	; of tones array
	
mode1b:	; First, wait .25 seconds
	clr	reload
	ldi	Count, 63
pause:	tst	Count
	brne	pause
	
	ldi	YL, low(tones)	; set Y register to point to
	ldi	YH, high(tones)	; RAM string
	
mode1c:	andi	rand, 0x03	; Convert rand to 2-bit value
				; (in range 0-3)
	cpi	rand, 0		; Check possible values for rand--
	breq	tone0		; set tone accordingly
	cpi	rand, 1
	breq	tone1
	cpi	rand, 2
	breq	tone2
	rjmp	tone3

tone0:	ldi	reload, reload0		; Load appropriate 
	rjmp	st_tone			; reload values
tone1:	ldi	reload, reload1
	rjmp	st_tone
tone2:	ldi	reload, reload2
	rjmp	st_tone
tone3:	ldi	reload, reload3

st_tone:st	Z+, reload	; and store them in tones buffer
	inc	numtones	; increment number of stored tones
	clr	reload		; clear the reload value to prevent
				; unintended tones

	dec	rndnum
	tst	rndnum
	breq	next
	in	rand, TCNT1L	; Get another random value
	rjmp	mode1c
	
next:	clr	tcount		; Clear tcount before entering into loop
				; where tones are played

pltone:	ld	reload, Y+
	inc	tcount
	cpi	reload, reload0	; Another jump table--compare reload values
	breq	pl0		; loaded from array of saved notes
	cpi	reload, reload1	; and jump to code which loads appropriate
	breq	pl1		; interrupt count value
	cpi	reload, reload2
	breq	pl2
	cpi	reload, reload3
	breq	pl3

pl0:	ldi	Count, count0	; Preload interrupt count values
	cbi	PORTB, 0
	rjmp	plback			
pl1:	ldi	Count, count1
	cbi	PORTB, 1
	rjmp	plback			
pl2:	ldi	Count, count2
	cbi	PORTB, 2
	rjmp	plback
pl3:	ldi	Count, count3
	cbi	PORTB, 3

plback:	tst	Count
	brne	plback		; spin until Count == 0
	ser	Temp		; Turn off LEDs
	out	PORTB, Temp

; Now wait .25 seconds
	clr	reload
	ldi	Count, 63
wloop:	tst	Count
	brne	wloop	
	
; Check to see what tones have been played
	cp	tcount, numtones 	; Have all tones in RAM 
					; been played?
	brne	pltone			; if not, play next tone

; Now, wait and see if the user can match the buttons pressed
	clr	tcount
	clr	reload
	ldi	YL, low(tones)
	ldi	YH, high(tones)

tstlp:	ld	rtemp1, Y+	; Get tone from RAM
	inc	tcount

poll:	sbis	PIND, 0			; Test for user 
	ldi	reload, reload0		; button press
	sbis	PIND, 1
	ldi	reload, reload1
	sbis	PIND, 2
	ldi	reload, reload2
	sbis	PIND, 3
	ldi	reload, reload3
	tst	reload
	breq	poll
	cp	reload, rtemp1
	brne	error
	
; Light appropriate LED corresponding to button pressed
	sbis	PIND, 0
	cbi	PORTB, 0
	sbis	PIND, 1
	cbi	PORTB, 1
	sbis	PIND, 2
	cbi	PORTB, 2
	sbis	PIND, 3
	cbi	PORTB, 3

; Have the program play the tone for as long as you press the button
waitA:	sbis	PIND, 0
	rjmp	waitA
	sbis	PIND, 1
	rjmp	waitA
	sbis	PIND, 2
	rjmp	waitA
	sbis	PIND, 3
	rjmp	waitA

	ser	Temp		; Turn off LEDs
	out	PORTB, Temp
	in	rand, TCNT0	; Get next random value
	tst	level		; if level==0
	breq	m1L1		; We're in level 1
	
	mov	rndnum, rand
	andi	rndnum, 0x0C	; Actually take the random		
	lsr	rndnum		; counter off of the 3rd
	lsr	rndnum		; and 4th bits of TCNT0
	subi	rndnum, -1
	rjmp	stopton
m1L1:	ldi	rndnum, 1	; Add only one tone to the
				; sequence
stopton:clr	reload		; Stop the current tone
	ldi	Count, 63
waitlp:	tst	Count		; Play silence for .25 sec
	brne	waitlp
	cp	tcount, numtones	; Has the whole sequence
					; been replicated?
	brlo	tstlp			; If not, check next tone
	rjmp	mode1b

error:	ldi	reload, 10		; Make some awful tone
	ldi	ZH, high(end*2)		; Display "Game over"
	ldi	ZL, low(end*2)
	rcall	outstr
	ldi	Count, 255		; Play the tone for a somewhat
					; unspecified amount of time

errlp:	tst	Count
	brne	errlp
	clr	reload
	
; Now the game is over--display game over message, and ask if player
; wants to play again
gmover:	ldi	Temp, 0xF0
	out	PORTB, Temp
	
	ldi	ZH, high(plagain*2)	; Display "Play again?"
	ldi	ZL, low(plagain*2)
	rcall	outstr
	rcall	wait2s
	
	ldi	ZH, high(plagn2*2)	; Display "S1: Y, S2: N"
	ldi	ZL, low(plagn2*2)
	rcall	outstr
	
gmover2:sbis	PIND, 0		; if Port D, pin 0 is clear, play again
	rjmp	replay
	sbis	PIND, 1		; if Port D, pin 1 clear, game's over
	rjmp	endg
	rjmp	gmover2

replay:	ser	Temp		; Clear LEDs
	out	PORTB, Temp	
	sbic	PIND, 0		; and wait for switch 0 to
	rjmp	main		; be released before returning 
	rjmp	replay		; to main menu

endg:	ldi	ZH, high(bye*2)	; Display "Goodbye"
	ldi	ZL, low(bye*2)
	rcall	outstr
	ser	Temp		; Turn off LEDs since blue LED 
	out	PORTB, Temp	; is a little too bright to 
				; leave on for any long amount
				; of time without hurting 
				; someone's eyes
endg2:	rjmp	endg2

; Reaction mode--mode 2
mode2:	sbis	PIND, 1 	; first, must wait for switch 1 to
	rjmp	mode2		; be released
	
	ldi	ZH, high(level1*2)	; Display "Choose level"
	ldi	ZL, low(level1*2)
	rcall	outstr
	rcall	wait2s
	
	ldi	ZH, high(level2b*2)	; Display "S1:1, S2:2, S3:3"
	ldi	ZL, low(level2b*2)
	rcall	outstr

mode2b:	clr	Temp		; Test for user button press
	sbis	PIND, 0		; and set level accordingly
	ldi	Temp, maxL1
	sbis	PIND, 1
	ldi	Temp, maxL2
	sbis	PIND, 2
	ldi	Temp, maxL3
	tst	Temp
	breq	mode2b
	
	mov	maxnum, Temp
	cpi	Temp, maxL1
	brne	L2
	ldi	Temp, minL1
	mov	minnum, Temp
	rjmp	setT1
L2:	cpi	Temp, maxL2
	brne	L3
	ldi	Temp, minL2
	mov	minnum, Temp
	rjmp	setT1
L3:	ldi	Temp, minL3
	mov	minnum, Temp

setT1:	ldi	Temp, PreScal1	; Set Timer1 to CK/8
	out	TCCR1B, Temp
	mov	waitval, maxnum	; Start waitval at whatever the
				; maximum number of interrupts is
				; for that level

	; Wait
	ldi	ZH, high(ready*2)	; Display "Get ready ... "
	ldi	ZL, low(ready*2)
	rcall	outstr
	rcall	wait2s

	ldi	ZH, high(mode2s*2)	; Display "Mode 2"
	ldi	ZL, low(mode2s*2)
	rcall	outstr

mode2a:	; First, wait .25 seconds
	clr	reload
	ldi	Count, 63
pause2:	tst	Count
	brne	pause2

	andi	rand, 0x03
	cpi	rand, 0		; Check possible values for rand & 0x03
	breq	tone02		; set tone accordingly
	cpi	rand, 1
	breq	tone12
	cpi	rand, 2
	breq	tone22
	rjmp	tone32

tone02:	ldi	reload, reload0		; Load appropriate reload values
	ldi	Count, count0
	cbi	PORTB, 0
	rjmp	plbck2
tone12:	ldi	reload, reload1
	ldi	Count, count1
	cbi	PORTB, 1
	rjmp	plbck2
tone22:	ldi	reload, reload2
	ldi	Count, count2
	cbi	PORTB, 2
	rjmp	plbck2
tone32:	ldi	reload, reload3
	ldi	Count, count3
	cbi	PORTB, 3

plbck2:	tst	Count		; spin until Count == 0
	brne	plbck2
	mov	rtemp1, reload	; store reload value in reload
				; temp register
	ser	Temp		; Turn off LEDs
	out	PORTB, Temp
	mov	wcount, waitval
	clr	reload

poll2:	tst	wcount		; Has time run out?
	breq	gerror		; If so, game over
	sbis	PIND, 0		; Test for user button press
	ldi	reload, reload0
	sbis	PIND, 1
	ldi	reload, reload1
	sbis	PIND, 2
	ldi	reload, reload2
	sbis	PIND, 3
	ldi	reload, reload3
	tst	reload
	breq	poll2
	cp	reload, rtemp1	; Did user replicate correct tone?
	brne	gerror		; If not, game over
	rjmp	light

gerror:	rjmp	error

; Light appropriate LED corresponding to button pressed
light:	sbis	PIND, 0
	cbi	PORTB, 0
	sbis	PIND, 1
	cbi	PORTB, 1
	sbis	PIND, 2
	cbi	PORTB, 2
	sbis	PIND, 3
	cbi	PORTB, 3

; Have the program play the tone for as long as you press the button
waitA2:	sbis	PIND, 0
	rjmp	waitA2
	sbis	PIND, 1
	rjmp	waitA2
	sbis	PIND, 2
	rjmp	waitA2
	sbis	PIND, 3
	rjmp	waitA2

	ser	Temp		; Turn off LEDs
	out	PORTB, Temp
	in	rand, TCNT0	; Get next random value
	clr	reload		; Turn off speaker
	ldi	Count, 63	; and wait .25 sec in silence
wtlp2:	tst	Count
	brne	wtlp2
	dec	waitval		; wait for 1 less interrupt
	cp	waitval, minnum
	brsh	gmode2a
	mov	waitval, minnum	; ensure that we never go below
				; the minimum number of interrupts
				; for a given level of difficulty
gmode2a:rjmp	mode2a

; ================================================ 
;       Clear entire LCD and delay for a bit 
lcdclr:	ldi     Temp, 1         ; Clear LCD command 
        rcall   lcdcmd 
	ldi	Temp, TCK256	; Wait for 15 ms
	out	TCCR0, Temp      
cwait:	brtc	cwait
	clt
	ldi	Temp, PreScal0
	out	TCCR0, Temp
	clr	Temp
	out	TCNT0, Temp
	ret 

;================================================ 
;       Initialize LCD module 
lcdinit:ldi     Temp,0          ; Setup port pins 
        out     PORTC,Temp	; Pull all pins low 
        ldi     Temp, 0xff      ; All pins are outputs 
        out     DDRC, Temp
	ldi	Temp, TCK256	; Wait for 15 ms
	out	TCCR0, Temp      
iwait:	brtc	iwait
	clt

; LCD specs call for 3 repetitions as follows 
	ldi     Temp, 3         ; Function set 
        out     PORTC, Temp	; to 8-bit mode
        nop                    	; nop is data setup time 
        sbi     PORTC, lcde     ; Toggle enable line 
        cbi     PORTC,lcde
 
        clr	Temp
	out	TCNT0, Temp
iwait2:	brtc	iwait2
	clt

	ldi     Temp, 3         ; Function set 
        out     PORTC, Temp	; to 8-bit mode
        nop                     ; nop is data setup time 
        sbi     PORTC, lcde     ; Toggle enable line 
        cbi     PORTC,lcde
 
        clr	Temp
	out	TCNT0, Temp
iwait3:	brtc	iwait3
	clt

	ldi     Temp, 3         ; Function set 
        out     PORTC, Temp	; to 8-bit mode
        nop                     ; nop is data setup time 
        sbi     PORTC, lcde     ; Toggle enable line 
        cbi     PORTC,lcde
 
        clr	Temp
	out	TCNT0, Temp
iwait4:	brtc	iwait4
	clt

 	ldi     Temp, 0b11110000 ; Make 4 data lines inputs 
       	out     DDRC, Temp
	ldi	Temp, TSTOP	; Clear timer0 and turn it off
	out	TCCR0, Temp
	out	TCNT0, Temp
        
;       Finally, 
;       At this point, the normal 4 wire command routine can be used 

        ldi     Temp, 0b00100000 ; Function set, 4 wire, 
        rcall   lcdcmd		; 1 line, 5x7 font 
 
       	ldi     Temp, 0b00001100 	; Display on, no cursor, 
        rcall   lcdcmd			; no blink 
 
       	ldi     Temp, 0b00000110 	; Address increment, no 
       	rcall   lcdcmd 			; scrolling 
 	ret 

; ============================================ 
;       Wait for LCD to go unbusy 
lcdwait:ldi     Temp, 0xF0      ; Make 4 data lines inputs 
        out     DDRC, Temp
        sbi     PORTC, lcdrw	; Set r/w pin to read 
        cbi     PORTC, lcdrs    ; Set register select to command 
waitloop: 
        sbi     PORTC, lcde     ; Toggle enable line 
        cbi     PORTC, lcde 
        in      lcdstat, PINC   ; Read busy flag 
        
	; Read, and ignore lower nibble 
        sbi     PORTC, lcde     ; Toggle enable line 
        cbi     PORTC, lcde
 
	sbrc    lcdstat, 3      ; Loop until done 
        rjmp    waitloop 
        ret 

; ============================================= 
;       Send command in Temp to LCD 
lcdcmd:	push    Temp		; Save character 
        rcall   lcdwait         ; Wait for LCD to be ready 
        ldi     Temp, 0xFF      ; Make all port D pins outputs 
        out     DDRC, Temp
        pop     Temp		; Get character back 
        push    Temp		; Save another copy 
        swap    Temp 		; Get upper nibble 
        andi    Temp, 0x0F      ; Strip off upper bits 
        out     PORTC, Temp	; Put on port
        nop                     ; wait for data setup time 
       	sbi     PORTC, lcde     ; Toggle enable line 
        cbi     PORTC, lcde
 
	pop     Temp		; Recall character 
        andi    Temp, 0x0F      ; Strip off upper bits 
        out     PORTC, Temp	; Put on port
        nop 
       	sbi     PORTC, lcde     ; Toggle enable line 
        cbi     PORTC, lcde
 
       	ldi     Temp, 0xF0      ; Make 4 data lines inputs 
        out     DDRC, Temp
        ret 

; ============================================= 
;       Send character data in Temp to LCD 
lcdput: 
       	push    Temp		; Save character 
        rcall   lcdwait         ; Wait for LCD to be ready 
        ldi     Temp, 0xFF      ; Make all port D pins outputs 
       	out     DDRC, Temp
 
       	pop     Temp		; Get character back 
       	push    Temp		; Save another copy 
       	swap    Temp		; Get upper nibble 
       	andi    Temp, 0x0F      ; Strip off upper bits 
       	out     PORTC, Temp	; Put on port 
       	sbi     PORTC, lcdrs    ; Register select set for data
       	nop 
       	sbi    	PORTC, lcde     ; Toggle enable line 
       	cbi     PORTC, lcde
 
	pop     Temp		; Recall character 
       	andi    Temp, 0x0F      ; Strip off upper bits 
       	out     PORTC, Temp	; Put on port 
       	sbi     PORTC, lcdrs    ; Register select set for data
       	nop 
       	sbi    	PORTC, lcde     ; Toggle enable line 
       	cbi     PORTC, lcde
       	
	ldi     Temp, 0xF0      ; Make 4 data lines inputs 
        out     DDRC, Temp
       	ret 


; Subroutine to output a string to the LCD
; The address of the first character in the string should be in 
; the Z register (R30-31) when the subroutine is called
outstr:	clr	charcnt
	rcall	lcdclr
strlp:	lpm
	tst	R0
	breq	strend
	mov	Temp, R0
	rcall	lcdput
	adiw	ZL, 1
	inc	charcnt
	cpi	charcnt, 8
	brne	strlp
	ldi	Temp, 0xC0	; Set address to last 8 chars
	rcall	lcdcmd
	rjmp	strlp
strend:	ret


; Cheesy way of waiting about 2 seconds (2.097152 seconds, to be precise)	
wait2s:	ldi	Count, 255
wait1:	tst	Count
	brne	wait1
	ldi	Count, 255
wait2:	tst	Count
	brne	wait2
	ret


; Timer1 overflow ISR
T1ovfl:	in	save, SREG
	dec	wcount		; decrement wait count
	out	SREG, save
	reti			

; Timer0 overflow ISR
T0ovfl:	in	save, SREG
	dec	Count		; decrement interrupt count
	tst	reload		; if reload = 0, 
	breq	no_tone
	eor	outreg, toggle	; toggle output bit
	out	TCNT0, reload	; reload counter
	out	PORTA, outreg	; send output to PORTA
	rjmp	t0_end
no_tone:clr	outreg		; clear output and 
				; play nothing
	out	PORTA, outreg	; this is the case 
				; where no button has
				; been pushed yet
t0_end:	out	SREG, save
	set
	reti

Back to top of page.
Back to SIMON Project Home Page.