Full Version : Roggman & Rhomas Hangman! (ASM)
avr >>GAME & VIDEO PROJECTS >>Roggman & Rhomas Hangman! (ASM)


Admin5- 04-22-2006
Hangman!

Jonathan Roffman & Theresa Thomas

Introduction

For our final project, we used an Atmel AT90S8535 microprocessor to create a hangman game. The letters are displayed on a 16-character LCD, and are input ("guessed") using a 16-button keypad. The 8 LED's on the Atmel development board are used as our "hanging man" - one lights up for each wrong guess. We also attached a speaker to our game which adds sounds to our game.

We programmed our game to randomly select from among nearly 150 different puzzles, although there is enough room in Program Memory to hold thousands of puzzles. A puzzle can consist of any combination of 1-15 characters including spaces.
.

Reset:

At reset, we initialize the three counters and the LCD. The LCD initialization and control code is the same as that used in previous labs.

State 0: Wait for "New Game"

Our code then goes into a keyboard polling loop that waits for a button press from the keypad. The keypad decoding is handled by the same code used in previous labs to decode button presses. The only valid button press at this time is a '1' which is the signal to start a new game. All other key presses leave the game in state 0.

State 1: Read in a Random Puzzle from Program Memory

Due to the vast amount of storage available in Program Memory (8 kilobytes) and the compact size of our code(~1 kilobyte), we chose to store our puzzles in Program Memory. They begin with the label "message:" and consist of a series of ASCII characters (1 byte per character). Puzzles are seperated by a terminating zero (0).

To randomly select a puzzle, we read the value of TIMER1 at the moment the user presses "New Game." We manipulate that value with a series of shifts and logical functions until it is a sufficiently random number that is no larger than the number of bytes we use to store puzzles. We then add that random number to the location in memory of the label "message:" and access that memory location.

State 2: Find the start of a puzzle

Since the random number added onto the location of "message:" may point to any letter in any puzzle, we need to find a terminating zero (0) to know that we have found the beggining of a new puzzle. Therefore, our code goes through a loop where it loads a byte from program memory and compares it to zero. Once it finds a zero, the next letter will be the start of a new puzzle.

State 3: Load the puzzle from memory into registers

We now store the contents of the puzzle in registers so that they can be easily and quickly accessed and manipulated. For this task, we use the Y-pointer to point to registers r1 through r15. We increment this pointer as we read each new letter in and loop until we find a terminating zero. At the same time, we increment a counter to hold the length of the puzzle.

State 4: Print dashes on LCD in place of letters

Next, we print dashes (actually underscores) on the LCD. We print the same number of dashes as we counted in State 3. At this point, we added an immediate guess of the "space" character to our code. This causes the LCD to print all the spaces in the puzzle and output a tone to signal to the user that a new game has begun.

State 5: Guessing letters

The game has now begun. Our code returns to the keyboard polling loop where it waits for button presses. In order to have the capability of 26 possible guesses (one for each letter in the alphabet) on a 16-button keypad, we use the A,B, and C keys on the keypad as function keys, selecting the 1st, 2nd, and 3rd letters assigned to each number respectively. The number represent letters the same way a telephone keypad has letter-number associations. For example, pressing "B" and then "3" would result in a guess of the letter E since 3 corresponds to the letters DEF on a telephone keypad. There were two exceptions - Q and Z - which do not appear on the numbers 2 through 9, so we use the 0 button to implement those.

This state therefore contains 2 polling loops. One to wait for the function key (A,B, or C) and another to wait for the number key (2-9 and 0). In the first loop, we store the value 0 for A, 1 for B, or 2 for C into a register called "abc". Once the number is pressed, we store the ASCII value of the first letter that is associated with that number into a register, and add to it the value of "abc". This gives the proper ASCII value for each letter.

State 6: Check if the letter guessed is in the puzzle

This state consists of comparing the "guessed" letter one by one to every letter in the puzzle. After each compare we check to see if we have reached the end of the puzzle yet, and increment the location of the cursor on the LCD. If the "guessed" letter matches at least one of the letters in the puzzle, we output that letter to the LCD, and set a flag. Once the end of the puzzle is reached, we test that flag to determine if the guess was right or wrong. Our code now branches off into two possible scenarios.

State 7a: The letter was in the puzzle - A correct guess!

If the letter appeared in the puzzle at least once, then it has already been written to the LCD in place of the dash that once was in its place. We now play a "correct" tone to signal that it was indeed a correct guess. We use TIMER2 preloaded with a certain value to play the tone. This value corresponds to twice the frequency of the tone we wish to play. This code is similar to that from lab 3. We preload the value for a high tone and use the TIMER2 overflow interrupt to toggle the speaker output at that frequncy. The preload value is also stored in Program Memory under the label "right:".

With each correct guess, it is necessary to check if the game has been won by the player. So we perform a compare between the number of letters remaining unseen and zero. If they are equal, the game has been won and we proceed to state 8a. If not, we return to state 5 and wait for the next guess.

State 8a: The last letter has been guessed correctly- A win!

If there are no more letters left to be guessed, the game is over. To reward the winner, we play a winning song. The musical notes in the song can be seen in figure 2. The preload values for TIMER2 that correspond to the notes in this song are stored in Program Memory under the label "winsong:". They are read from memory one at a time, and played the same as the the "correct" tone described above.

Once the song has completed playing, the game returns to state 0 where it waits for a "new game" button press.

State 7b: The letter was not in the puzzle - A wrong guess!

If the letter did not appear in the puzzle at all, there is no change to the LCD. Instead, we light up one of the LED's on the development board. Then we play a "wrong" tone to signal that it was a wrong guess. Again, we use TIMER2, but this time, preload it with a low tone that is stored in program memory under the label "wrong:".

With each incorrect guess, it is necessary to check if the game has been lost by the player. So we compare the number of LED's unlit to zero. If all the LED's are lit, the game has been lost and we proceed to state 8b. If not, we return to state 5 and wait for the next guess.

State 8b: Eight wrong guesses have occured - The player loses the game!

If there are no more LED's left to be lit, the game is over. To show that the player has lost, we play a losing song. The musical notes for this cong can also be seen in figure 2. The preload values for TIMER2 that correspond to the notes in this song are stored in Program Memory under the label "losesong:". They are read from memory one at a time, and played the same as the the "correct" tone described above.

Once the song has completed playing, the game returns to state 0 where it waits for a "new game" button press.



Results

Here is a list of our design goals and how we met them:

Full Funcionality - All of the functions we wanted to have in our game work correctly. These include:
Variable length puzzles
Multiple word puzzles
Interactive interface (including audio)
Random puzzle selection
Large puzzle database

Stand-Alone Interface - One of our big goals was to design a "portable" game so that hypothetically, it would be possible to package and sell our game. By using the keypad and LCD (instead of the UART with input from the computer keyboard and output to a terminal window), we were able to acheive this goal.
Fun and Interesting Game - This was our foremost concern in designing this game. We didn't want a Hangman game with easy puzzles that you could learn by heart after playing a few games. The random puzzle selection and large puzzle database helped us achieve this goal. Since there are currently over 150 puzzles and there is room for thousands more, we felt the game was sufficiently interesting. There are so many puzzles that even we can't always win even though we programmed in each puzzle!
What We Would Do Differently Next Time...

Multi-player games - A new function that would allow one person to create a puzzle for someone else to guess.
Graphical LCD display - This would let us make cool violent images of a person actually hanging instead of using the LEDs.
Longer puzzle length - Perhaps add a 2nd LCD or get a larger LCD so that puzzles are not limited to 16 characters.

Link: http://instruct1.cit.cornell.edu/courses/e...mas/HANGMAN.HTM


CODE

; Jonathan Roffman and Theresa Thomas
; EE 476
; March 29, 1999
; Lab 6

.include "c:\users\jonther\8535def.inc"

.device AT90S8535; specifies to the assembler which chip we are using

.def    save    =r16   ;SREG temp reg
.def    temp    =r17   ;temporary register
.def key =r18;temp register for key press value
.def butnum =r19;hold button press value
.def abc =r20;hold abc value
.def letter =r21;register to hold ascii value of input letter
.def led =r22;register to hold LED values
.def inpuzzle=r23;register to tell whether guess is in puzzle or not
;***** global variables for lcd2 code
.def    timeout =R24           ;Timeout value passed to subroutine
.def    lcdstat =R25           ;LCD busy/wait status
.def    longtime=R26           ;Long timer for powerup
.def    charcnt =r27           ;Char position on the display


.equ    azero   ='0'   ;0x30 ascii '0'
.equ    prescal =2     ;value to prescale clock to get 2us per tick

;***** Other equates
.equ    lcdrs   =PD6           ;LCD rs pin connected to PD6
.equ    lcdrw   =PD5           ;LCD r/w pin connected to PD5
.equ    lcde    =PD4           ;LCD enable pin connected to PD4
;       Timer/Counter prescaler values
.equ    TSTOP   =0             ;Stop Timer/Counter
.equ    TCK1    =1             ;Timer/Counter runs from CK
.equ    TCK8    =2             ;Timer/Counter runs from CK / 8
.equ    TCK64   =3             ;Timer/Counter runs from CK / 64
.equ    TCK256  =4             ;Timer/Counter runs from CK / 256
.equ    TCK1024 =5             ;Timer/Counter runs from CK / 1024
.equ    TEXF    =6             ;Timer/Counter runs from external falling edge
.equ    TEXR    =7             ;Timer/Counter runs from external rising edge


;***** Initialization


;**************************************

.cseg

.org $0000

       rjmp    RESET  ;reset entry vector
       reti            
       reti
       reti
       rjmp t2int   ;timer 2 interrupt for songs
       reti
       reti    
       reti
       reti    
       rjmp    t0int  ;timer0 interrupt :timer 0 interrupt for lcd stuff
       reti    
       reti            
       reti
       reti
       reti
       reti
       reti

;The following table is used to convert raw button-press high/low
;values to a sequence number. Invalid codes caused by multiple
;button presses are ignored.

keytbl: .db 0b11101110, 0b11101101, 0b11101011, 0b11100111
       .db 0b11011110, 0b11011101, 0b11011011, 0b11010111
       .db 0b10111110, 0b10111101, 0b10111011, 0b10110111
       .db 0b01111110, 0b01111101, 0b01111011, 0b01110111



RESET: ;***setup stack pointer***
       ldi     temp, LOW(RAMEND)
       out     SPL, temp
       ldi     temp, HIGH(RAMEND)
       out     SPH, temp
       clr     r31
       clr     butnum


;***start timer1 for randomization routine****
ldi temp, 0b00000001  ;prescale timer 1 by 1 for randomization
out TCCR1B, temp
clr  temp


;***setup for lcd***
       ldi     temp,TSTOP     ;Timer 0 off (just in case)
       out     TCCR0,temp     ;Stop timer
       ldi     temp, 0b01000001       ; enable capture interrupt and timer0 interrupt
       out     TIMSK, temp
       sei    ;enable all interrupts
 

       **power down the LCD***

       ldi     temp, 0xff     ;LCD power connection
       out     DDRA, temp     ;power it down after reset
       ldi     temp, 0x00
       out     PORTA,temp
       ldi     longtime,100   ; then Wait 1.5 second with LCD power off
       ldi     timeout,0      ;Delay 15 mS

offwait:rcall   delay
       dec     longtime
       brne    offwait


      ;***now power up LCD***

       ldi     temp, 0xff
       out     PORTA, temp
       ldi     longtime,100   ;Wait 1.5 second for LCD power up
       ldi     timeout,0      ;Delay 15 mS  
puwait: rcall   delay
       dec     longtime
       brne    puwait

      ;***hopefully by now the LCD has rebooted, so initialize and clear***
main:   rcall   lcdinit        ;Initialize LCD module
       rcall   lcdclr         ;Clear LCD screen
       

;***scan the keyboard for a button press***

keybd:  clr butnum
clr letter
ldi     temp, 0x0f     ;set lower four lines to output
       out     DDRC, temp
       ldi     temp, 0xf0     ;and turn on the pullups on the inputs
       out     PORTC, temp
       nop                    ;Need some time for the pullups to
       nop                    ;charge the port pins
       nop
       nop
       in      temp, PINC     ;read the high nibble
       mov     key, temp      ;and store it (with zeros in the low nibble)
       ldi     temp, 0xf0     ;set upper four lines to outputs
       out     DDRC, temp
       ldi     temp, 0x0f     ;and turn on pullups on the inputs
       out     PORTC, temp
       nop                    ;As before wait for the pin to charge
       nop    
       nop
       nop
       in      temp, PINC     ;read the low nibble
       or      key, temp      ;combine to make key code

       
      ;At the point the raw key code should have exactly one zero each in
      ;the lower and upper nibbles. Any other number of zeros indicates
      ;either no-button pressed or multiple-button pressed.

      ;Now search the table for a match to the raw key code
      ;and exit with a button number

       ldi     ZL, low(keytbl*2)      ;table pointer in FLASH
       ldi     ZH, high(keytbl*2)     ;so convert from word to byte addr

tbllp:  lpm                    ;get the table entry
       cp      key, r0        ;match?
       breq    foundit
       inc     butnum         ;if not, have we exhaused the
       cpi     butnum, 0x10   ;table
       breq    illegal
       adiw    ZL, 1          ;if not, get the next table entry
       rjmp    tbllp

foundit:cpi     butnum, 0      ;User presses '1' key to start a new game
       breq    newgame
cpi butnum, 12;at end of game, wait until new game button is pressed
breq keybd
cpi butnum, 1;abc key
breq two1
cpi butnum, 2;def key
breq three1
cpi butnum, 3;A shift key
breq alpha1
cpi butnum, 4;ghi key
breq four1
cpi butnum, 5;jkl key
breq five1
cpi butnum, 6;mno key
breq six1
cpi butnum, 7 :B shift key
breq beta1
cpi butnum, 8;prs key
breq seven1
cpi butnum, 9;tuv key
breq eight1
cpi butnum, 10;wxy key
breq nine1
cpi butnum, 11;C shift key
breq gamma1
cpi butnum, 13;q and z key
breq ten1
       rjmp    keybd

illegal:ser     temp           ;no table match means show
       clr     butnum
       rjmp    keybd

alpha1: rjmp alpha ;jumps because rjmps were out of reach
beta1: rjmp beta
gamma1: rjmp gamma
two1: rjmp two
three1: rjmp three
four1: rjmp four
five1: rjmp five
six1: rjmp six
seven1: rjmp seven
eight1: rjmp eight
nine1: rjmp nine
ten1: rjmp ten

newgame:rcall lcdclr ;clear the lcd
clr charcnt ;clear character count
ser abc ;set the shift tracker to reset it
ser led ;clear the led's
out DDRB,led;make port b output to led's
out PORTB,led
ldi     ZH,high(message*2) ;load pointer to first puzzle
       ldi     ZL,low(message*2)
in butnum, TCNT1L  ;get a random number (lower byte) by reading counter1
lsr butnum   ;shift the byte
add ZL, butnum  ;add it to the lower pointer
in  butnum, TCNT1H  ;get the upper byte of counter 1
add ZL, butnum  ;add to lower pointer for further randomization
andi butnum, 0b00000011 ;take only lower two bits of upper nibble counter
adc ZH, butnum  ;add that to high byte of pointer
 
findzero: lpm     ; check for new word
adiw ZL, 1    ; by looking for trailing zero
tst r0
brne  findzero  
   
;******Now read the random word****; once we reach trailing zero, get entire word
nextchar:lpm                       ;Get next character from ROM
       push r0   ;push it on to stack
tst     r0               ;See if at end of message
       breq    savelength       ;If so save letters to reg 1-15
       adiw    ZL,1      ;Increment Z-pointer
inc charcnt   ;keep track of chars on display
rjmp    nextchar           ;Loop for more

savelength:mov inpuzzle,charcnt ;save character count into another register
inc charcnt   ;inc character count to point to highest register
mov YL, charcnt  ;move into Y pointer to start storing into reg 1-15
clr YH   ;clear high byte of pointer
loadlet:cpi charcnt, 0  ;see if character count is down to zero
breq restore   ;if so, branch to restore character count
pop temp   ;pop letter one at a time
st Y, temp   ;and store it into registers
sbiw YL,1   ;inc register pointer
dec charcnt   ;dec character count to count letters in word
rjmp loadlet   ;keep loading letters

restore: mov  charcnt, inpuzzle ;restore the character count
printdash:clr r0   ;clear r0
again: cp charcnt,r0  ;check to see if the pointer is at the end of word
breq restlength  ;if so, then go to automatically guessing a space
ldi temp, 8   ;check to see if need to readdress the lcd
cp r0, temp
brne not8   ;if not, continue writing
ldi     temp,0xC0        ;Set address to last 8 chars
       rcall   lcdcmd      
not8: inc r0   ;go to next letter
ldi temp, 0b01011111  ;value for underscore
rcall lcdput   ;put it on lcd
rjmp again   ;keep going through puzzle

jump1: rjmp keybd

restlength:mov charcnt,inpuzzle  
ldi letter, 0x20  ;automatically guess "space" character before starting game
rjmp updatelcd

 
alpha: ldi abc, 0   ;first shift key, choses first letter on number key
rjmp keybd

beta: ldi abc,1   ;second shift key, choses second letter on number key
rjmp keybd

gamma: ldi abc,2   ;third shift key, choses third letter on number key
rjmp keybd

two: cpi abc, 0xff  ;if abc is not set, go back to keyboard poll
breq jump1
ldi letter, 'a'  ;load first letter of number key
add letter, abc  ;and increment according to what shift key was pressed
rjmp updatelcd  ;update lcd

three: cpi abc, 0xff  ;same as above for each key
breq jump1
ldi letter, 'd'
add letter, abc
rjmp updatelcd

four: cpi abc, 0xff
breq jump1
ldi letter, 'g'
add letter, abc
rjmp updatelcd

five: cpi abc, 0xff
breq jump1
ldi letter, 'j'
add letter, abc
rjmp updatelcd

six: cpi abc, 0xff
breq jump1
ldi letter, 'm'
add letter, abc
rjmp updatelcd

seven: cpi abc, 0xff
breq jump1
cpi abc, 0
breq letterp
inc abc
letterp:ldi letter, 'p'
add letter, abc
rjmp updatelcd

eight: cpi abc, 0xff
breq jump1
ldi letter, 't'
add letter, abc
rjmp updatelcd

nine: cpi abc, 0xff
breq jump1
ldi letter, 'w'
add letter, abc
rjmp updatelcd

ten: cpi abc, 0xff  ;this is for q and z
breq jump1
cpi abc, 1
brne letterz
ldi letter, 'q'
rjmp updatelcd
letterz:ldi letter, 'z'
rjmp updatelcd

updatelcd:ser abc   ;set abc to clear it
clr YL   ;clear the Y pointer
clr YH  
clr inpuzzle  
ldi     temp,0x80         ;Set LCD address to 0
       rcall   lcdcmd  
adiw YL,1   ;increment the pointer
nextl: ld temp, Y    
cpi temp, 0   ;are we at end of the word?
breq testin   ;if so, jump to code which checks if letter ever appeared in puzzle
cpi YL, 9   ;do we need to readdress lcd?
brne hop   ;jump if characters 0-7, otherwise, need to set lcd to address last 8 chars
ldi     temp,0xC0         ;Set address to last 8 chars
       rcall   lcdcmd  
hop: ld temp, Y   ;get Y pointer
cp temp, letter  ;check to see if letter guessed is in the word
brne skip   ;if letter not a match, skip over space on lcd
mov temp, letter  ;else, found a correct letter, so write it
rcall lcdput
ser inpuzzle  ;set inpuzzle to mark a correct guess
adiw YL, 1   ;increment Y pointer
dec charcnt   ;decrement character count to keep track of legth of word
rjmp nextl  
skip: ldi temp, 0b00010100 ;value to move cursor right by 1 without overwriting
rcall lcdcmd
adiw YL, 1   ;increment Y pointer
rjmp nextl

testin: cpi letter, 0x20  ; check if this is the beggining of a game (guessed "space" character)
brne reglet   ;if not, go to regular letter check
ldi inpuzzle, 1  ;if yes, space was guessed, so play game start tone.
reglet: cpi inpuzzle, 0  ;was the letter in the puzzle
brne wingame   ;if so, check to see if they won the game
lsl led   ;if not, light another led
out PORTB, led
ldi     ZH,high(wrong*2) ;"wrong" guess tone addr to Z-ptr  
ldi     ZL,low(wrong*2)    
rcall   playgo   ;call the play song code
cpi led,0   ;if no more led's left, then go to lose game
breq losegame
rjmp keybd   ;otherwise, wait for another letter guess

wingame:cpi charcnt,0  ;is it at the end of the word
breq won   ;if so, then they won
ldi     ZH,high(right*2) ;"right" guess tone addr to Z-ptr
       ldi     ZL,low(right*2)    
rcall playgo   ;call code to play song
rjmp keybd   ;then go back to keyboard so user can start a new game

won: ldi     ZH,high(winsong*2) ;winsong addr to Z-ptr  
       ldi     ZL,low(winsong*2)
rcall playgo   ;code to play song
ldi key, 0b01111110  ;set key to blank key
rjmp  keybd   ;goto keyboard

losegame:clr YL   ;clear Y pointer to print out puzzle they didn't guess
clr YH
adiw YL, 1   ;inc Y pointer
ldi     temp,0x80        ;Set LCD address to 0
       rcall   lcdcmd  
nextlet:ld temp, Y   ;load letter into temp
cpi temp, 0   ;check to see if at end of word
breq playlose  ;if so, play losing song
cpi YL, 9   ;see if need to readdress lcd
brne hop1   ;jump if characters 0-7, otherwise, need to set lcd to address last 8 chars
ldi     temp,0xC0       ;Set address to last 8 chars
     rcall   lcdcmd  
hop1: ld temp, Y   ;write next letter
rcall lcdput
adiw YL, 1   ;inc Y pointer
rjmp nextlet  
playlose:ldi     ZH,high(losesong*2) ;losesong addr to Z-ptr
       ldi     ZL,low(losesong*2)
rcall playgo   ;code for playing song
ldi key, 0b01111110  ;set key to blank key
rjmp  keybd   ;go to keyboard poll

jump:   rjmp    jump1

playgo: ldi temp, 0b00000100 ;prescale timer2 by 64 for playing notes
out TCCR2, temp
lpm    ;load song or tone
adiw ZL, 1   ;increment program memory
tst r0   ;are we at the end of the song?
breq end   ;if so, end
mov save, r0  ;if not, move note into save
cpi save, 0x75  ;this is a low tone for "wrong"
brne notwrong
ldi temp, 0x83  ;load in appropriate number of interrupts for each tone into temp
rjmp wait
notwrong:cpi save, 0x01  ;lowest note
brne notlow
ldi temp, 61
rjmp wait
notlow: cpi save, 0x03  ;low b
brne notb0
ldi temp, 62
rjmp wait
notb0: cpi save, 0x10  ;low c
brne notc0  
ldi temp, 65
rjmp wait
notc0: cpi save, 0x2a  ;low d
brne notd0
ldi temp, 73
rjmp wait
notd0: cpi save, 0x42  ;low e
brne note0
ldi temp, 83
rjmp wait
note0: cpi save, 0x4c  ;low f
brne notb1
ldi temp, 87
rjmp wait
notb1: cpi save, 0x89    ;C?
brne notc2
ldi temp, 0x83    ;131 interrupts
rjmp    wait
notc2: cpi save, 0x95    ;D?
brne notd2
ldi temp, 0x93    ;147 interrupt
rjmp    wait
notd2:  cpi  save, 0xa1    ;E?
brne note2
ldi temp, 0xa6    ;166 interrupts
rjmp    wait
note2: cpi  save, 0xa6      ;F?
brne notf2
ldi temp, 0xb0    ;176 interrupts
rjmp    wait
notf2: cpi save, 0xb0    ;G?
brne notg2
ldi   temp, 0xc6    ;198 interrupts
rjmp    wait
notg2: cpi save, 0xb9      ;A?
brne nota2
ldi temp, 0xdc    ;220 interrupts
rjmp    wait
nota2: cpi save, 0xc1    ;B?
brne wait
ldi temp, 0xf8    ;248 interrupts

wait: cpi temp, 0   ;wait for tone to be finished playing
brne wait
rjmp playgo

end: ldi temp, 0   ;clr temp to stop timer
out TCCR2, temp
ret    ;and return to code
===============================================
;       Clear entire LCD and delay for a bit
lcdclr:
       ldi     temp,1         ;Clear LCD command
       rcall   lcdcmd
       ldi     timeout,256    ;Delay 15 mS for clear command
       rcall   delay
       ret


================================================
;       Initialize LCD module
lcdinit:
       ldi     temp,0         ;Setup port pins
       out     PORTD,temp     ;Pull all pins low
       ldi     temp,0xff      ;All pins are outputs
       out     DDRD,temp
       ldi     timeout,256    ;Wait at least 15 mS at power up
       rcall   delay

;LCD specs call for 3 repetitions as follows
       ldi     temp,3         ;Function set
       out     PORTD,temp     ;to 8-bit mode
       nop                    ;nop is data setup time
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,lcde
       ldi     timeout,256    ;Wait at least 15 mS
       rcall   delay
       ldi     temp,3         ;Function set
       out     PORTD,temp
       nop
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,lcde
       ldi     timeout,256    ;Wait at least 15 ms
       rcall   delay
       ldi     temp,3         ;Function set
       out     PORTD,temp
       nop
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,lcde
       ldi     timeout,256    ;Wait at least 15 ms
       rcall   delay
       ldi     temp,2         ;Function set, 4 line interface
       out     PORTD,temp
       nop
;       rcall   strobe         ;Toggle enable line
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,lcde
       ldi     temp,0b11110000;Make 4 data lines inputs
       out     DDRD,temp
       
;Finally,
;At this point, the normal 4 wire command routine can be used


       ldi     temp,0b00100000;Function set, 4 wire, 1 line, 5x7 font
       rcall   lcdcmd


       ldi     temp,0b00001100;Display on, no cursor, no blink
       rcall   lcdcmd
       ldi     temp,0b00000110;Address increment, no scrolling
       rcall   lcdcmd
       ret


============================================
;       Wait for LCD to go unbusy
lcdwait:
       ldi     temp,0xF0      ;Make 4 data lines inputs
       out     DDRD,temp
       sbi     PORTD,lcdrw    ;Set r/w pin to read
       cbi     PORTD,lcdrs    ;Set register select to command
waitloop:
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,lcde
       in      lcdstat,PIND   ;Read busy flag
      ;Read, and ignore lower nibble
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,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     DDRD,temp
       pop     temp           ;Get character back
       push    temp           ;Save another copy
       swap    temp           ;Get upper nibble
       andi    temp,0x0F      ;Strip off upper bits
       out     PORTD,temp     ;Put on port
       nop                    ;wait for data setup time
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,lcde
       pop     temp           ;Recall character
       andi    temp,0x0F      ;Strip off upper bits
       out     PORTD,temp     ;Put on port
       nop
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,lcde
       ldi     temp,0xF0      ;Make 4 data lines inputs
       out     DDRD,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     DDRD,temp
       pop     temp           ;Get character back
       push    temp           ;Save another copy
       swap    temp           ;Get upper nibble
       andi    temp,0x0F      ;Strip off upper bits
       out     PORTD,temp     ;Put on port
       sbi     PORTD,lcdrs    ;Register select set for data
       nop
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,lcde
       pop     temp           ;Recall character
       andi    temp,0x0F      ;Strip off upper bits
       out     PORTD,temp     ;Put on port
       sbi     PORTD,lcdrs    ;Register select set for data
       nop
       sbi     PORTD,lcde     ;Toggle enable line
       cbi     PORTD,lcde
      ;cbi    PORTD,lcdrs    ;--

       ldi     temp,0xF0      ;Make 4 data lines inputs
       out     DDRD,temp
       ret


=============================================
;*****************************

;interrupt routines

;***** Timer 0 overflow interrupt handler
t0int:
set                    ;Set T flag
       ldi     save,TSTOP     ;Timer 0 off
       out     TCCR0,save     ;Stop timer
       reti                   ;Done, return

;***** Timer 2 overflow interrupt handler

t2int:  in save,SREG
ldi key, 0xff
cp r0, key ;dont' toggle if value is 0xff
breq notone
sbic PINA, 6   ;skip if PINA6 is low
rjmp setlo1   ;if not low, jump to set low
ldi butnum, 0b11000000
out PORTA, butnum    ;set PIND6 to high
rjmp load1          ;jump to load
setlo1: ldi butnum, 0b10000000
out  PORTA, butnum;set PIND6 to low
load1: out TCNT2, r0    ;set timer reload to set freq
out SREG, save
notone: dec temp
reti

;***** Delay n*64 microseconds using timer 0, delay time passed in timeout

; weird construction, interrupt is called like a subroutine


delay:  in      save,SREG      ;Save status register
       push    save
       out     TCNT0,timeout
       clt                    ;Clear T
       ldi     save,TCK256    ;Timer 0 prescaler, CK / 256
       out     TCCR0,save     ;Run timer
dwait:  brtc    dwait          ;Wait for timer 0 interrupt to set T
       pop     save           ;Restore status register
       out     SREG,save
       ret


=============================================

; text data

winsong: .db 0xb0, 0xff, 0x89, 0xff, 0x42, 0xff, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xff, 0x89, 0xff,0xb0,0xff,0x89,0x89,0x89,0x89,0x89,0x89,0x89,0xff,0x4d,0xff,0x42,0xff,0x89,0x00
losesong: .db 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0xff, 0x4c, 0x4c, 0xff, 0x42, 0xff, 0x2a, 0x2a, 0xff, 0x10, 0xff, 0x03, 0x03, 0xff, 0x01, 0xff, 0x10, 0x10, 0x10, 0x00
wrong: .db 0x75, 0x00
right: .db 0xc1, 0x00
message: .db 0x00, "robot", 0x00, "hamburger", 0x00, "store", 0x00, "hydroxide", 0x00, "smell", 0x00
 .db "tower", 0x00, "hangman", 0x00, "super man",0x00, "cat", 0x00, "rain", 0x00, "dog", 0x00
 .db "snow", 0x00, "significant", 0x00, "university", 0x00, "home run", 0x00, "computer", 0x00
 .db "cornell", 0x00, "graduate", 0x00, "bake sale", 0x00, "curious", 0x00, "cat in the hat", 0x00
 .db "brownie", 0x00, "hard drive", 0x00, "sunburn", 0x00, "barbecue", 0x00, "friends", 0x00
 .db "football", 0x00, "children", 0x00, "die hard", 0x00, "talking heads", 0x00, "terminate", 0x00
 .db "door", 0x00, "shirt", 0x00, "helping hands", 0x00, "america", 0x00, "paper doll", 0x00
 .db "enter key", 0x00, "aggrivate", 0x00, "roof top", 0x00, "once upon a time", 0x00, "philadelphia", 0x00
 .db "california", 0x00, "radio shack", 0x00, "happy birthday", 0x00, "new york", 0x00, "talk", 0x00
 .db "hello world", 0x00, "one two three", 0x00, "microsoft sucks", 0x00, "macintosh sucks", 0x00
 .db "bruce land", 0x00, "clarinet", 0x00, "trumpet", 0x00, "saxiphone", 0x00, "telephone", 0x00
 .db "television", 0x00, "hollywood video", 0x00, "star wars", 0x00, "les miserables", 0x00, "broadway", 0x00
 .db "hollywood", 0x00, "windowshade", 0x00, "cable", 0x00, "hair dryer", 0x00, "dishwasher", 0x00
 .db "knight rider", 0x00, "ruloffs", 0x00, "clubhouse", 0x00, "chapter", 0x00, "indiana jones", 0x00
 .db "cameron diaz", 0x00, "brooke shields", 0x00, "harrison ford", 0x00, "hangover", 0x00
 .db "tequila sunrise", 0x00, "sex on a beach", 0x00, "sesame street", 0x00, "whiskey sour", 0x00
 .db "teletubbies", 0x00, "butter", 0x00, "girl scouts",0x00,"star trek",0x00,"pentium",0x00
 .db "nectarine",0x00,"tangerine",0x00,"photosynthesis",0x00,"black and white", 0x00, "black and blue", 0x00
 .db "merry go round", 0x00, "snoop doggy dog", 0x00, "austin powers",0x00,"notebook",0x00,"psychedelic",0x00
 .db "blue sky",0x00,"wheres the beef",0x00,"dominos",0x00,"pizza",0x00,"chinese takeout",0x00
 .db "tortellini",0x00,"sunglasses",0x00,"neptune",0x00,"bathing suit",0x00,"beach ball",0x00, "volleyball", 0x00
 .db "baseball",0x00,"basketball", 0x00, "hockey stick",0x00, "miss america",0x00,"engineering",0x00
 .db "workbench",0x00,"capitalism",0x00,"democracy",0x00, "smart", 0x00, "goodbye", 0x00





Forumer™ is Voted #1 Free Forum Hosting provider
Build your own community today with the largest message board hosting company.