Appendix 1
Code Listing
download the code here




/* SIMON!! */

// Jorel Luyando - 391959
// Senaida San Miguel - 392059

#asm
    .equ __lcd_port=0x15 ;port c
#endasm

#include lcd.h 	// LCD driver routines
#include 90s8515.h 	
#include Stdio.h   
#include delay.h

   
#define prescale1 1		
#define clear_on_match 8    
#define OffT 2000		
#define OnT 2000       		
#define userOnT 1000		

#define LCDwidth 16 		//number of characters across length of LCD
#define mask 0b00011000   	//mask for random number generator

#define t1 50        		//task 1 timeout value
#define t2 50        		//task 2 timeout value
	
// state, mode, level, and color variables
enum gameStates {ModeStart, ModeSelect, LevelStart, LevelSelect, Playback, ButtonPress, Check, EndGame, RoundCheck, GameCont} gameState;
enum debounceStates {Released, MaybePressed, ButtonPressed, Pressed, MaybeReleased} debounceState;
enum modes {Simon, FFA} mode;
enum levels {easy, medium, hard} level;    
enum colors {red, blue, yellow, green, grey} color;

//various variables for FSM and button debounce
unsigned char noteArray[99];
unsigned char reload, time1, time2;
unsigned char index, round, userNoteCount, noteCount;          
unsigned char OffTimer, OnTimer, userOnTimer, timertemp;
unsigned char butnum, maybe;
unsigned char num, gameLength, speedup;   

//flags needed
unsigned char bttnpressFlag, playbackFlag, userplaybackFlag, startFlag, selectFlag;

char lcd_buffer[17];    // LCD display buffer


flash int noteTable[8]={15267/2, 12121/2, 10204/2, 8097/2, 30534/2, 3824/2, 2000/2, 9090/2}; 
// must have first 4 values be red, blue, yellow, and green note values
//**********************************************************

/* Use an 1x16 alphanumeric LCD connected
   to PORTA as follows:
   
  [LCD pin]  [8515]
     1 GND - GND    
     2 +5V - Vcc    
     3 VLC 10k trimpot wiper (trimpot ends go to +Vcc and GND) 
     4 RS  - PA0    
     5 RD  - PA1    
     6 EN  - PA2    
    11 D4  - PA4    
    12 D5  - PA5    
    13 D6  - PA6    
    14 D7  - PA7    
*/
//**********************************************************

void soundOn(void);	//turn speaker on 
void soundOff(void);	//turn speaker off 
void ledsOn(void);	//turn specified color leds on 
void ledsOff(void);	//turn all leds off

void wingame(void);     //game is won, blink "win" light/sound pattern
void wrong(void);	//game is lost, blink "lose" light/sound pattern

void modeLCD(void);	//initializes LCD to display mode
void levelLCD(void);	//initializes LCD to display level
void gameLCD(void);	//initializes LCD to display game info
void updateLCD(void);	//updates the number of rounds the user has won on LCD
void endgameLCD(void);	//display losing msg, mode, level, rounds to user
void wingameLCD(void);	//display winning msg, mode, level, rounds to user

void toggleMode(void);	//toggles mode and updates LCD when select is pressed
void toggleLevel(void);	//toggles level and updates LCD when select is pressed

void init(void);    	//initializes needed game variables
void randNote(void);	//creates random note, inc(noteCount), stores note in noteArray
void playback(void);	//runs through noteArray, turning on sound and led for a period of time
void userPush(void);	//inc(userNoteCount), turns on sound and led of button pressed

void initialize(void);  //initialize ports and timers
void task1(void);	//FSM
void task2(void);	//debounce buttons

//********************************************************** 
//timer 0 overflow ISR
interrupt [TIM0_OVF] void timer0_overflow(void)
{   
    
  //reload to force 1 mSec overflow
  TCNT0=reload;  
	
  //num is running for the randnum generator
  num++;
  
  //Decrement the three times if they are not already zero
  if (time1>0)	--time1;		//task1 timer
  if (time2>0) 	--time2;		//task2 timer
  if (OnTimer>0)	--OnTimer;	//time that LED/sound is on for playback
  if (OffTimer>0)	--OffTimer;   	//time that LED/sound is off for playback
  if (userOnTimer>0)	--userOnTimer;	//time that LED/sound is on for user
  
}
//********************************************************** 
//timer 1 compare-match A ISR
interrupt [TIM1_COMPA] void cmpA_overflow(void)  
{     
  PORTA = ~PORTA ; 	//toggle the port to make a sound
} 
//********************************************************** 
void init(void)
{                       
	//initialize needed game variables
	noteCount = 0;
	userNoteCount = 0;
	index = 0;
	round = 0;
	bttnpressFlag = 0;
	playbackFlag = 0;
	userplaybackFlag=0;
	startFlag = 0;
	selectFlag = 0;   
	gameLength = 99;
	num = 0;
	color = grey;
	mode = Simon;
	level = easy;
	gameState = ModeStart;
	debounceState = Released;
	OnTimer = 0;
	OffTimer = 0;   
	userOnTimer = 0;
	timertemp = OnT;     
	modeLCD();        
} 
//------------------------------------------------------------
void soundOn(void)
{                                       
	//zero the T1 counter
	TCNT1 = 0;														
	//begin timer
	TCCR1B = prescale1 + clear_on_match;	
}   
                                                                
void soundOff(void)
{                                       
	//turn off timer
	TCCR1B = 0;														
}

void ledsOn(void)
{
	if (color == red)                     
		//light up red
		PORTB = 0x01;	
										
	else if (color == blue)
		//light up blue	
		PORTB = 0x02;											

	else if (color == yellow)             
		//light up yellow
		PORTB = 0x04;											

	else if (color == green)              
		//light up green
		PORTB = 0x08;											
}

void ledsOff(void)
{                                       
	//turn off all leds
	PORTB = 0x00; 												
}

//------------------------------------------------------------
void wrong(void)
{                                               
	//turn all the leds on
	PORTB = 0xff;			

	//load the "wrong" note into OCR1A
	OCR1A = noteTable[4];             

	//turn speaker "on" - turn on T1
	soundOn();      

	//keep sound on a little longer         
	delay_ms(300);	
}

void wingame(void)
{
	//flash lights and play tune

	//red
	PORTB = 0x01;   
	OCR1A = noteTable[0];
	soundOn();	//turn sound on
	delay_ms(200);	//hold sound out 

	//blue
	PORTB = 0x02;	
	OCR1A = noteTable[1];
	delay_ms(200);	//hold sound/LED out 

	//yellow
	PORTB = 0x04;	
	OCR1A = noteTable[2];	
	delay_ms(200);	//hold sound/LED out 

	//green
	PORTB = 0x08;   
	OCR1A = noteTable[3];	
	delay_ms(200);	//hold sound/LED out 
 	soundOff();	//turn sound off
	PORTB = 0x00;	//turn LEDS off
}
//------------------------------------------------------------
void modeLCD(void)
{                                 
	//clear the display
	lcd_clear();                 		

	//display current mode to user
	lcd_gotoxy(0,0);
	lcd_putsf("Simon     FFA   " );
	lcd_gotoxy(0,1);
	lcd_putsf("  ^             ");     
}

void levelLCD(void)
{                                 
	//clear the display
	lcd_clear();             
    		
	//display current level to user
	lcd_gotoxy(0,0);
	lcd_putsf("Easy  Med  Hard " );
	lcd_gotoxy(0,1);
	lcd_putsf(" ^              ");
}

void toggleMode(void)
{
	//toggle modes
	if (mode == Simon)	mode = FFA;
	else			mode = Simon;

	//display new mode
	lcd_gotoxy(0,1);
	if (mode==Simon)
		lcd_putsf("  ^             ");
	else	lcd_putsf("          ^     ");	   

	//load start/select "beep"
	OCR1A = noteTable[6];
	soundOn();
}

void toggleLevel(void)
{
	//toggle levels
	if (level == easy)	
		level = medium;
	else if (level == medium)
		level = hard;
	else if (level == hard)
		level = easy;

	//display new level
	lcd_gotoxy(0,1);
	if (level == easy)
		lcd_putsf(" ^              ");
	else if (level == medium)
		lcd_putsf("       ^        ");
	else 	lcd_putsf("            ^   ");	  

	//load start/select "beep"
	OCR1A = noteTable[6];
	soundOn();
}
//------------------------------------------------------------
void gameLCD(void)
{
	//clear the display
	lcd_clear();       
          		
	//display mode to user
	lcd_gotoxy(0,0);
	if (mode == Simon)
		lcd_putsf("Simon    ");
	else 	lcd_putsf("FFA      ");

	//display level to user
	if (level == easy)
		lcd_putsf("Easy");
	else if (level == medium)
		lcd_putsf("Med");
	else	lcd_putsf("Hard");

	//display rounds won
	lcd_gotoxy(0,1);
	lcd_putsf("Rounds: 0       ");
}

void updateLCD(void)
{	
	//display current rounds won
	lcd_gotoxy(0,1);
	sprintf(lcd_buffer, "Rounds: %-i", round);
	lcd_puts(lcd_buffer);
}

void endgameLCD(void)
{   
	//initialize the display
	lcd_init(LCDwidth);          	
	lcd_clear();

	//display losing message and current round
	lcd_gotoxy(0,0);
	sprintf(lcd_buffer, "Loser! %-i rounds", round);
	lcd_puts(lcd_buffer);

	//display mode
	lcd_gotoxy(0,1);
	lcd_putsf("on ");                              
	if (mode == Simon)
		lcd_putsf("Simon ");
	else 	lcd_putsf("FFA ");

	//display level
	if (level == easy)
		lcd_putsf("Easy");
	else if (level == medium)
		lcd_putsf("Med");
	else	lcd_putsf("Hard");
}

void wingameLCD(void)
{
	//clear display
	lcd_clear();

	//display winning message and rounds won
	lcd_gotoxy(0,0);
	sprintf(lcd_buffer, "Winner %-i rounds", round);
	lcd_puts(lcd_buffer);

	//display mode
	lcd_gotoxy(0,1);       
	lcd_putsf("on ");                              
	if (mode == Simon)
		lcd_putsf("Simon ");
	else 	lcd_putsf("FFA ");

	//display level
	if (level == easy)
		lcd_putsf("Easy");
	else if (level == medium)
		lcd_putsf("Med");
	else	lcd_putsf("Hard");
}
//------------------------------------------------------------
void randNote(void)
{
	//variables used in randNote()
	unsigned char newColor;
	unsigned char randnum;

	//generate a random number
	randnum = num & mask;

	//create a new color by using random number
	if (randnum == 0x00)
		newColor = red;
	else if (randnum == 0x08)
		newColor = blue;
	else if (randnum == 0x10)
		newColor = yellow;
	else if (randnum == 0x18)
		newColor = green;

	//append new color to end of sequence
	noteCount++;
	noteArray[noteCount-1] = newColor;
} 

void playback(void)
{        
	//load the current color's note value into OCR1A
	color = noteArray[index];
	OCR1A = noteTable[color];
	index++;   

	//speed up in FFA-medium and FFA-hard levels only
  	if (mode == FFA)	
	{
		if (level == medium)
		{
			//speed up every 5 rounds
			if (round%5 == 0)	timertemp = timertemp - speedup;
		}
		else if (level == hard)
		{	
			//speed up every 3 rounds	
			if (round%3 == 0)	timertemp = timertemp - speedup;
		}
	}                         

	//note/LED is on for the amount of time in timertemp
	OnTimer = timertemp;
	soundOn();	//turn on sound
	ledsOn();	//turn on LED
} 

void userPush(void)
{
	//set flag low
	bttnpressFlag = 0;

	//load current color pressed into OCR1A
	OCR1A = noteTable[color];

	//user's current note count incremented
	userNoteCount++;

	//hold sound/LED on for period of time
	userOnTimer = userOnT;   
	soundOn();	//turn sound on
	ledsOn();	//turn LED on
}
//********************************************************** 
// TASK 1 - FSM for Simon game
void task1(void)
{
	time1 = t1;           
        
	//turn off start/select "beep"
        if ((playbackFlag==0)&&(userplaybackFlag==0) )
        {	soundOff();
       		ledsOff();
        }

	// Run FSM for Simon
	switch(gameState)
	{
		case ModeStart:
		if (startFlag == 1)	
		{
			//init LCD to level
			levelLCD();
			gameState = LevelStart;
			startFlag = 0;      

			//play start/select "beep"
			OCR1A = noteTable[6];
			soundOn();
		}
		else	gameState = ModeSelect;
		break;

		case ModeSelect:
		if (selectFlag == 1)
		{	
			//toggle mode and update LCD
			toggleMode();
			gameState = ModeSelect;
			selectFlag = 0;
		}
		else	gameState = ModeStart;
		break;

		case LevelStart:
		if (startFlag == 1)
		{
			//play start/select "beep"
			OCR1A = noteTable[6];
			soundOn();
			
			//init length of game and speed up
			if (mode==FFA)	
			{	
				//game is practically infinite
				gameLength = 99;                         

				//easy has no speedup
				if ( (level == medium)||(level == hard) )
					speedup = 2;                         				
			}
			else    // in Simon mode - no speed up at all                     
			{	// speed up is initialized in init() to OnT

				if (level == easy)		gameLength = 7;
				else if (level == medium)	gameLength = 15;
				else gameLength = 31;		//hard is 31 rounds
			}

			randNote();		//create new random note
			gameLCD();		//get LCD ready for game
			gameState = Playback; 	//go to Playback state   	
			startFlag = 0;   
			delay_ms(100);
	  		soundOff();		//turn off sound
			delay_ms(700);		//let user get ready for game
		}
		else	gameState = LevelSelect;
		break;

		case LevelSelect:
		if (selectFlag == 1)
		{	
			//toggle level and display
			toggleLevel();
			gameState = LevelSelect;
			selectFlag = 0;
		}
		else	gameState = LevelStart;
		break;

		case Playback:
		if (playbackFlag == 0)			//ready to play notes 
		{
			//cycle through sequence
			if (index < noteCount)
			{	
				playbackFlag = 1;	//we're going into playback mode now
				playback();		//play note in noteArray
				gameState = Playback;
			}
			else	gameState = ButtonPress;	//played all the notes, wait for user button press
		}	
		//else do nothing - have to wait for end of playback mode
		break;

		case ButtonPress:
		if (userplaybackFlag ==0)		//ready to check if user pressed anything
		{
			if (bttnpressFlag == 1)		//user pressed a colored button
			{	
				userPush();    			//play note/color that user pressed 
				userplaybackFlag =1;   		//go into userplayback mode
				gameState = ButtonPress;     	
				bttnpressFlag = 0;
                                       
			//this line is set after userOnTimer has run out in the "main" function
		   		//gameState = Check;		//check if the button pressed is correct

			}
		}
		
		else	gameState = ButtonPress;	//wait for user button press
		break;

		case Check:
		if (color == noteArray[userNoteCount - 1])
			gameState = GameCont;
		else
		{  	//game is over, user pressed wrong button
			wrong();
			endgameLCD();
			gameState = EndGame;
		}
		break;
	
		case EndGame:
		if (startFlag == 1)
		{	
			init();				//reinitialize game vars for next game
			modeLCD();            		//reinitialize LCD 
			gameState = ModeStart;		//begin new game
			startFlag = 0;
		}
		else	gameState = EndGame;		//wait for user to press start
		break;

		case RoundCheck:
		if (round < gameLength)
	   	{	
			//user entered in the correct sequence
	   		gameState = Playback;
			randNote();			//create new note
			index = 0;			//reinitialize for new round	
	   		userNoteCount = 0;		//reinitialize for new round
			updateLCD();			//update rounds won
	   	    	ledsOff();			
	   	    	soundOff();
	   	   	delay_ms(800);			//let user get ready for next round
	   	}
		else
		{	//user won game
			//blink winning lights, play winning sounds, update winning msg on LCD
			wingame();
			wingameLCD();
			gameState = EndGame;
		}
		break;

		case GameCont:
		if (userNoteCount < noteCount)
			gameState = ButtonPress;	//wait for user to enter in the next color 
		else
		{	
			round++;			//user just won the round
			gameState = RoundCheck; 	
		}
		break;

	}  // End FSM

}// end task 1
//------------------------------------------------------------
// TASK 2                  
void task2(void)            
{              
	time2 = t2;

	//get butnum from PIND
	butnum = PIND;
          
	switch(debounceState)
	{
		case Released:
		if (butnum == 0)	debounceState = Released;  
		else
		{	maybe = butnum;
			debounceState = MaybePressed;
		}
		break;

		case MaybePressed:
		if (butnum == maybe)		
			debounceState = ButtonPressed; 
		else	debounceState = Released;
		break;
	
		case ButtonPressed:     
	     	if (butnum == 0x01)		
	     	{	color = red;     
			bttnpressFlag =1;     
		}
	     	else if (butnum == 0x02)	
	     	{	color = blue;       
	     		bttnpressFlag =1;     
	     	}
	     	else if (butnum == 0x04)	
	     	{ color = yellow;
			bttnpressFlag =1; 
		}
	     	else if (butnum == 0x08)	
	     	{	color = green;              
			bttnpressFlag =1;	 	
		}              
	     	else if (butnum == 0x80)	startFlag = 1;
	     	else if (butnum == 0x40)	selectFlag = 1;

	     	debounceState = Pressed;
		break;

		case Pressed:
		if (butnum == maybe)	debounceState = Pressed; 
		else			debounceState = MaybeReleased;
		break;

		case MaybeReleased:
		if (butnum==maybe)	debounceState = Pressed;
		else			debounceState = Released;
		break;

	} //end debounce
}// end task 2


//********************************************************** 
void main(void)
{           
  initialize();
  init();
                    
  while(1)
  {           
		if (time1==0) 	task1();   	//FSM  
		if (time2==0)	task2();	//debounce button
 	
		//check if we're still in playback mode
 		if (playbackFlag == 1)
		{
			if (OnTimer ==1)		//catch it right before it zeros out
			{	soundOff();		//turn off sound
				ledsOff();		//turn off LED
				OffTimer = timertemp;	//set "off time"
			}

			//check if out of playback mode
			if (OffTimer == 1)		playbackFlag = 0;	

		}       
		else
		{   	//check if we're in user playback
			if (userplaybackFlag==1)
			{
				if (userOnTimer ==1)
				{
					ledsOff();       	//turn off LED
 					soundOff();		//turn off sound
 					userplaybackFlag =0;   	//out of userplayback mode
 					gameState = Check;	//check if color entered is correct
 				}
 			}
 		}
			
		
  } //end while
  
} //end main
  
//********************************************************** 
//Set it all up
void initialize(void)
{
  //set up the ports
  DDRD=0x00;	// PORT D is an input for buttons
  // PORT C is an ouput for lcd
  DDRB=0xff;	// PORT B is an output for leds
  DDRA=0xff;	// PORT A is an output for speaker

  PORTA=0; 
  PORTB=0x00;  	//all LEDs off       
        
  //set up timer 0     
  //62.5 x (64x.25) microSec = 1.0 mSec, so prescale 64, and count 62 times.
  reload=256-125; 		//value for 1 Msec  
  TCNT0=reload;	 		//preload timer 1 so that is interrupts after 1 mSec.
  TCCR0=3;			//prescalar to 64
  TIMSK=2;	   		//turn on timer 0 overflow ISR  
  
  //set up timer 1
  TIMSK=TIMSK | 0x40;		//turn on timer 1 compare match interrupt
  TCCR1B = 0;			//disable timer 1 
  TCNT1 = 0;     		//zero the timer 
  
  //init the task timers
  time1=t1;
  time2=t2;            

  //initialize and clear the display
  lcd_init(LCDwidth);          	
  lcd_clear();
               
  //crank up the ISRs
  #asm
  	sei
  #endasm 
}