/*	MIDI Sequencer
	EE476 Final Project
	Nicholas Kisler
*/
#include <90s8515.h>
#include <stdio.h>
#include <String.h>
#include <delay.h>
#asm
.equ __lcd_port=0x1b
#endasm
#include <lcd.h>

//constants
#define NTRK 4
#define NMEAS 2

//keypad modes
#define M_NORM 0
#define M_PITCH 1
#define M_VEL 2
#define M_LENGTH 3
#define M_TEMPO 4

//keypad commands for M_NORM mode
#define K_BEAT_D 0
#define K_BEAT_U 1
#define K_STOP 2
#define K_PLAY 3
#define K_MEAS_D 4
#define K_MEAS_U 5
#define K_EDIT_PITCH 6
#define K_EDIT_VELOCITY 7
#define K_TRACK_D 8
#define K_TRACK_U 9
#define K_EDIT_LENGTH 10
#define K_EDIT_TEMPO 11
#define K_LOOP 12
#define K_TRIG 13
#define K_PRESET 14

//keypad commands for other modes
#define K_OCT_D 12
#define K_OCT_U 13
#define K_CANCEL 14
#define K_ENTER 15

flash unsigned char keytbl[16] = {
0xee,0xed,0xeb,0xe7,
0xde,0xdd,0xdb,0xd7,
0xbe,0xbd,0xbb,0xb7,
0x7e,0x7d,0x7b,0x77
};

//externally modified variables
unsigned char pch[NTRK][NMEAS*16];	//contains pitches of notes in each track, if the high bit is set then 
												//the note is held over (ie. no new note is started in that beat)
unsigned char vel[NTRK][NMEAS*16];	//contains velocities of notes in each track
unsigned char loop[NTRK];				//the loop pointer for each track, sequence will loop when it gets
												//to this beat
unsigned char tempo;						//the tempo in beats per minute (bpm)

//internally modified variables
unsigned char beat[NTRK];				//the next beat of the sequence to play for each track
unsigned char buffer[16];				//used to buffer outgoing MIDI bytes
unsigned char buf_idx;					//used to determine the length of data to send

unsigned char cur_track,cur_meas,cur_beat;	//the track/measure/beat currently selected by the user

unsigned char field[3];					//used to hold digits entered by the user
unsigned char field_count;				//used to index field[] 
unsigned char* fieldstr[4];			//a string used to output field[] to the display

float flt_temp;							//temporary floating point variable used in tempo calculations

unsigned char i1;							//loop index for timer1 interrupt handler
unsigned char prepare;					//flag signalling that the main method should prepare the buffer
unsigned char play_flag;				//flag signalling the main method to start playing

unsigned char command;					//command from keypad
unsigned char state;						//state of keypad state machine
unsigned char stateFlag;				//flag signalling the main method to enter the keypad state machine
char key2;									//saved key from the keypad state machine

char* outstr[7];							//temporary string used by the display

unsigned char mode;						//the mode of operation
unsigned char saved_val;				//used by the function that executes keypad commands to save some value
												//before the user starts editing

unsigned char trigger;					//flag used by the main method to determine when to turn on/off a note
												//triggered by the user
unsigned char trig_timer;				//controls duration of the triggered note


void initialize(void);
void prepBuffer(void);						//used by the main method to prepare the buffer to be output in 
													//the timer1 interrupt handler
void formatNote(unsigned char note);	//formats a note from a byte into a readable value (ex. "C#4")
void refresh(void);							//refreshes the LCD screen
char getButton(void);						//returns the button currently being pressed by the user
void stateMachine(void);					//keypad scanning state machine
void doCommand(void);						//executes command output from the keypad state machine
char getLength(void);						//returns the length of the note currently selected by the user


//timer1 interrupt handler: outputs buffer to MIDI, increments and loops the beat for each track,
//signals the main method upon completion so that the next values can be entered into the buffer
interrupt [TIM1_COMPA] void tim1_cmpA(void) {

	for(i1=0;i1<buf_idx;i1++) {
		putchar(buffer[i1]);
	}
	
	for(i1=0;i1<NTRK;i1++) {
		if (loop[i1] != beat[i1])
			beat[i1] = (beat[i1] + 1) & ((NMEAS<<4) - 1);
		else beat[i1] = 0;
	}
	
	prepare = 1;
}


//sets stateFlag every 30ms (stateFlag is cleared by main method)
interrupt [TIM0_OVF] void t0ovf(void) {
	TCNT0 = 139;
	stateFlag = 1;
}


void prepBuffer(void) {
	unsigned char trk;
	unsigned char last_beat;
	buf_idx = 0;
	buffer[buf_idx++] = 0x90;		//status byte for note on message
	for(trk=0;trk<NTRK;trk++) {
		//if the note is not a held note, then sequencer may have to perform an operation
 		if (pch[trk][beat[trk]] < 0x80) {
	 		if (beat[trk] == 0) last_beat = loop[trk];
	 		else last_beat = beat[trk] - 1;
	 		
	 		//cut old note, need to make sure "old" note is not cut on first time
			if ((vel[trk][last_beat] > 0) && (play_flag == 0)) {
  				buffer[buf_idx++] = pch[trk][last_beat];
				buffer[buf_idx++] = 0x00;
			}
		
 			//play new note
			if (vel[trk][beat[trk]] > 0) {
	  			buffer[buf_idx++] = pch[trk][beat[trk]];
		  		buffer[buf_idx++] = vel[trk][beat[trk]];
		 	}
	  	}
	}
}


void formatNote(unsigned char note) {
	unsigned char oct,rem;
	note = note & 0x7f;			//the high bit is used to keep track of non-MIDI information
	oct = (char)(note / 12);	//each octave is 12 notes
	rem = note % 12;				//the remainder determines which pitch
	
	switch(rem) {
		case 0: 
			sprintf(outstr,"C %-1d",oct);
			break;
		case 1:
			sprintf(outstr,"C#%-1d",oct);
			break;
		case 2:
			sprintf(outstr,"D %-1d",oct);
			break;
		case 3:
			sprintf(outstr,"D#%-1d",oct);
			break;
		case 4:
			sprintf(outstr,"E %-1d",oct);
			break;
		case 5:
			sprintf(outstr,"F %-1d",oct);
			break;
		case 6:
			sprintf(outstr,"F#%-1d",oct);
			break;
		case 7:
			sprintf(outstr,"G %-1d",oct);
			break;
		case 8:
			sprintf(outstr,"G#%-1d",oct);
			break;
		case 9:
			sprintf(outstr,"A %-1d",oct);
			break;
		case 10:
			sprintf(outstr,"A#%-1d",oct);
			break;
		case 11:
			sprintf(outstr,"B %-1d",oct);
			break;
		default:
			sprintf(outstr,"xx%-1d",oct);
			break;
	}
}	


void refresh(void) {
	unsigned char i,j,temp;
	
	lcd_clear();
	
	//position field
 	lcd_gotoxy(0,0);
  	sprintf(outstr,"%2d:",(char)(cur_track+1));
   lcd_puts(outstr);
   sprintf(outstr,"%2d:",(char)(cur_meas+1));
   lcd_puts(outstr);
   if (cur_beat < 9) lcd_putsf(" ");   	
   sprintf(outstr,"%-2d",(char)(cur_beat+1));
   lcd_puts(outstr);
   
   //tempo field 
   lcd_gotoxy(11,0);
   if (mode == M_TEMPO) {
   	lcd_putsf("@");
    	lcd_puts(fieldstr);
    	lcd_putsf("bpm");
	} else {
    	sprintf(outstr," %-3dbpm",tempo);
		lcd_puts(outstr);
	}           
   
   //note info field 
	lcd_gotoxy(0,1);
   if (mode == M_PITCH) lcd_putsf("@");
   else lcd_putsf(" ");
   formatNote(pch[cur_track][cur_beat + (cur_meas<<4)]);
   lcd_puts(outstr);
    
   lcd_gotoxy(5,1);
   if (mode == M_VEL) {
   	lcd_putsf("@V=");
    	lcd_puts(fieldstr);
   } else {
    	lcd_putsf(" V=");
    	sprintf(outstr,"%-3d",vel[cur_track][(char)(cur_beat + (cur_meas<<4))]);
    	lcd_puts(outstr);
   }
    
   lcd_gotoxy(12,1);
   if (mode == M_LENGTH) {
   	lcd_putsf("@L=");
   	lcd_puts(fieldstr);
   } else {
    	if (pch[cur_track][cur_beat + (cur_meas<<4)] >= 0x80) {
    		lcd_putsf(" L=--");
    	} else {
    		lcd_putsf(" L=");
    		temp = getLength();
    		sprintf(outstr,"%-2d",temp);
    		lcd_puts(outstr);
    	}
	}
   
   //measure overview field
	for(i=0;i<16;i++) {
		lcd_gotoxy(i,2);
		//if note is not a held note
		if (pch[cur_track][i + (cur_meas<<4)] < 0x80) {
			//if note is on
			if (vel[cur_track][i + (cur_meas<<4)] > 0x00) {
				lcd_putsf("X");
			} else {
				lcd_putsf("-");
			}
		//else note is a held note
		} else {
			//if note is on
			if (vel[cur_track][i + (cur_meas<<4)] > 0x00) {
				lcd_putsf("x");
			} else {
				lcd_putsf("=");
			}
		}
	}
	
	//track overview field
	lcd_gotoxy(16,3);
	temp = 0;
	//checks to see if track is empty or not
	for(i=0;i<NTRK;i++) {
		for(j=0;j<(NMEAS<<4);j++) {
			if (vel[i][j] != 0) temp = 1;
		}
		if (temp == 1) lcd_putsf("+");
		else lcd_putsf("-");
		temp = 0;
	}
	
	//loop pointer portion of measure overview field
	lcd_gotoxy(loop[cur_track] & 0x0f,3);		//go to the position corresponding to the loop pointer
	
	//if the loop pointer should be displayed for this measure, display it
	if ((cur_meas == 0) && ((loop[cur_track]>>4) == 0)) lcd_putsf("|");	
	else if ((cur_meas == 1) && ((loop[cur_track]>>4) == 1)) lcd_putsf("|");
}

//returns length of currently selected note
char getLength(void) {
	unsigned char i,temp;
    temp = 1;
    i = (cur_beat + (cur_meas<<4) + 1) & ((NMEAS<<4) - 1);	//start on beat after current beat
    
    //while beat is part of held note, advance to next beat (max length of 32)
    while((pch[cur_track][i] >= 0x80) && (temp < 32)) {
    	temp++;
    	i = i+1;
    	if (i==32) i=0;
    }
    return temp;
}


void doCommand(void) {
	unsigned char pitch,velocity;
	unsigned char value;
	unsigned char i;
	
	//if a new command is ready, execute it
	if (command != 0xff) {
		switch(mode) {
		case M_NORM:
			switch(command) {
			case K_PLAY:
				play_flag = 1;
				break;
			case K_STOP:
				TCCR1B = 0x00;		//stop timer1
				
				//turn notes off
				putchar(0x90);
				for(i=0;i<NTRK;i++) {
					if (beat[i] == 0) value = loop[i];
					else value = beat[i] - 1;
					if (vel[i][value] > 0) {
						putchar(pch[i][value] & 0x7f);
						putchar(0x00);
					}
				}
				break;
			case K_BEAT_U:
				cur_beat = (cur_beat + 1) & 0x0f;
				refresh();
				break;
			case K_BEAT_D:
				cur_beat = (cur_beat - 1) & 0x0f;
				refresh();
				break;
			case K_MEAS_U:
				cur_meas = (cur_meas + 1) & (NMEAS-1);
				refresh();
				break;
			case K_MEAS_D:
				cur_meas = (cur_meas - 1) & (NMEAS-1);
				refresh();
				break;
			case K_TRACK_U:
				cur_track = (cur_track + 1) & (NTRK-1);
				refresh();
				break;
			case K_TRACK_D:
				cur_track = (cur_track - 1) & (NTRK-1);
				refresh();
				break;
			case K_LOOP:
				loop[cur_track] = cur_beat + (cur_meas<<4);
				refresh();
				break;
			case K_EDIT_PITCH:
				//if note is not held, then command is valid
				if (pch[cur_track][cur_beat + (cur_meas<<4)] < 0x80) {
					saved_val = pch[cur_track][cur_beat + (cur_meas<<4)];		//save current pitch value
					mode = M_PITCH;
			  		lcd_gotoxy(0,1);
			   	refresh();
			  	}
				break;
			case K_EDIT_VELOCITY:
				//if note is not held, then command is valid
				if (pch[cur_track][cur_beat + (cur_meas<<4)] < 0x80) {
					saved_val = vel[cur_track][cur_beat + (cur_meas<<4)];		//save current velocity value
					
					//initialize values for display
					field_count = 0;
					sprintf(fieldstr,"___");
					
					mode = M_VEL;
					refresh();
				}
				break;
			case K_EDIT_LENGTH:
				//if note is not held, then command is valid
				if (pch[cur_track][cur_beat + (cur_meas<<4)] < 0x80) {
				
					//initialize values for display
					field_count = 0;
					sprintf(fieldstr,"__");
					
					mode = M_LENGTH;
					refresh();
				}
				break;
			case K_EDIT_TEMPO:
				saved_val = tempo;		//save current tempo
				
				//initialize values for display
				field_count = 0;
				sprintf(fieldstr,"___");
				
				mode = M_TEMPO;
				refresh();
				break;
			case K_TRIG:
				trig_timer = 10;		//start timer for triggered note, main method will handle from here on
				break;
			} //end switch(command)
			break;

		case M_PITCH:
			//parse command as according to specification for keypad in pitch mode
			if (command <= 11) {
				pitch = (char)(pch[cur_track][cur_beat + (cur_meas<<4)] % 12);		//get the current pitch value
				
				//if the pitch selected is in the proper range, modify the pch array
				if ((pch[cur_track][cur_beat + (cur_meas<<4)] + (command-pitch)) <= 127) {
					pch[cur_track][cur_beat + (cur_meas<<4)] += (command-pitch);	//add/sub the difference
					refresh();
				}
			} else if (command == K_OCT_U) {
				//if the octave selected is in the proper range, modify the pch array
				if (pch[cur_track][cur_beat + (cur_meas<<4)] <= 115) {
					pch[cur_track][cur_beat + (cur_meas<<4)] += 12;
					refresh();
				}
			} else if (command == K_OCT_D) {
				//if the octave selected is in the proper range, modify the pch array
				if (pch[cur_track][cur_beat + (cur_meas<<4)] >= 12) {
					pch[cur_track][cur_beat + (cur_meas<<4)] -= 12;
					refresh();
				}
			} else if (command == K_ENTER) {
				value = getLength();
				// if part of held note, have to modify all notes held
				if (value > 1) {
					pitch = pch[cur_track][cur_beat + (cur_meas<<4)];
					for(i=1;i<value;i++) {
						pch[cur_track][((cur_beat + (cur_meas<<4)) + i) & ((NMEAS<<4) - 1)] = (pitch | 0x80);
					}
				}	
					
				mode = M_NORM;
				refresh();
			} else if (command == K_CANCEL) {
				pch[cur_track][cur_beat + (cur_meas<<4)] = saved_val;		//restore previous value
				mode = M_NORM;
				refresh();
			}
			break;
			
		case M_VEL:
			//parse command as numerical keypad with enter, cancel
			if (command <= 2) value = command + 1;
			else if ((command >= 4) && (command <= 6)) value = command;
			else if ((command >= 8) && (command <= 10)) value = command-1;
			else if (command == 13) value = 0;
			else if (command == K_ENTER) {
				value = getLength();
				// if part of held note, have to modify all notes held
				if (value > 1) {
					velocity = vel[cur_track][cur_beat + (cur_meas<<4)];
					for(i=1;i<value;i++) {
						vel[cur_track][((cur_beat + (cur_meas<<4)) + i) & ((NMEAS<<4) - 1)] = velocity;
					}
				}
				mode = M_NORM;
				refresh();
			} else if (command == K_CANCEL) {
				vel[cur_track][cur_beat + (cur_meas<<4)] = saved_val;		//restore previous value
				mode = M_NORM;
				refresh();
			}
			
			//if value entered is a digit		
			if (value <= 9) {
				field[field_count] = value;
				
				//format fieldstr to display the digits as they are entered by the user
				if (field_count == 0) sprintf(fieldstr,"%-d__",field[0]);
				else if (field_count == 1) sprintf(fieldstr,"%-d%-d_",field[0],field[1]);
				else if (field_count == 2) sprintf(fieldstr,"%-d%-d%-d",field[0],field[1],field[2]);
				
				field_count++;
				refresh();
				
				//if 3 digits have been entered, calculate the numerical value, and perform a range check
				if (field_count == 3) {
					value = (char)((100 * field[0]) + (10 * field[1]) + field[2]);
					if ((field[0] > 1) || ((field[0] == 1) && (field[1] > 2)) || 
										((field[0] == 1) && (field[1] == 2) && (field[2] > 7)))
						vel[cur_track][cur_beat + (cur_meas<<4)] = 127;			//out of range, use maximum
					else vel[cur_track][cur_beat + (cur_meas<<4)] = value;	//in range, use the value
					
					//refresh the display, and reinitialize fieldstr so that the user can input 
					//another value if desired
					field_count = 0;
					refresh();
					sprintf(fieldstr,"___");
				}
			}
			break;
			
		case M_LENGTH:
			//parse command as numerical keypad with enter, cancel
			if (command <= 2) value = command + 1;
			else if ((command >= 4) && (command <= 6)) value = command;
			else if ((command >= 8) && (command <= 10)) value = command-1;
			else if (command == 13) value = 0;
			else if (command == K_ENTER) {
				value = getLength();
				pitch = pch[cur_track][cur_beat + (cur_meas<<4)];
				velocity = vel[cur_track][cur_beat + (cur_meas<<4)];
				
				//copies current values of pitch and velocity to all held notes
				for(i=1;i<saved_val;i++) {
					pch[cur_track][((cur_beat + (cur_meas<<4)) + i) & ((NMEAS<<4) - 1)] = (pitch | 0x80);
					vel[cur_track][((cur_beat + (cur_meas<<4)) + i) & ((NMEAS<<4) - 1)] = velocity;
				}
				
				//if length input is smaller than previous length, must remove old values
				if (saved_val < value) {
					for(i=saved_val;i<value;i++) {
						pch[cur_track][((cur_beat + (cur_meas<<4)) + i) & ((NMEAS<<4) - 1)] = (pitch & 0x7F);
						vel[cur_track][((cur_beat + (cur_meas<<4)) + i) & ((NMEAS<<4) - 1)] = 0;
					}
				}
				
				mode = M_NORM;
				refresh();
			} else if (command == K_CANCEL) {
				mode = M_NORM;
				refresh();
			}
			
			//format for display, same as in velocity edit mode, except with 2 digits instead of 3
			if (value <= 9) {
				field[field_count] = value;
				if (field_count == 0) sprintf(fieldstr,"%-d_",field[0]);
				else if (field_count == 1) sprintf(fieldstr,"%-d%-d",field[0],field[1]);
				field_count++;
				refresh();
				if (field_count == 2) {
					value = (char)((10 * field[0]) + field[1]);
					if ((field[0] > 3) || ((field[0] == 3) && (field[1] > 2)))
						saved_val = 32;
					else saved_val = value;
					field_count = 0;
					refresh();
					sprintf(fieldstr,"__");
				}
			}
			break;

		case M_TEMPO:
			//parse command as numerical keypad with enter, cancel
			if (command <= 2) value = command + 1;
			else if ((command >= 4) && (command <= 6)) value = command;
			else if ((command >= 8) && (command <= 10)) value = command-1;
			else if (command == 13) value = 0;
			else if (command == K_ENTER) {
				//calculate the period of a sixteenth note from the tempo entered in bpm
				flt_temp = (float)tempo;
				flt_temp = 60 / flt_temp;
				flt_temp = flt_temp * 15625;
				OCR1A = (int)flt_temp;
				
				mode = M_NORM;
				refresh();
			} else if (command == K_CANCEL) {
				tempo = saved_val;  		//restore previous value
				mode = M_NORM;
				refresh();
			}
			
			//format for display, same as in velocity edit mode		
			if (value <= 9) {
				field[field_count] = value;
				if (field_count == 0) sprintf(fieldstr,"%-d__",field[0]);
				else if (field_count == 1) sprintf(fieldstr,"%-d%-d_",field[0],field[1]);
				else if (field_count == 2) sprintf(fieldstr,"%-d%-d%-d",field[0],field[1],field[2]);
				field_count++;
				refresh();
				if (field_count == 3) {
					value = (char)((100 * field[0]) + (10 * field[1]) + field[2]);
					if ((field[0] > 2) || ((field[0] == 2) && (field[1] > 4)) || 
										((field[0] == 2) && (field[1] == 4) && (field[2] > 0)))
						tempo = 240;
					else if (value < 60) tempo = 60;
					else tempo = value;
					field_count = 0;
					refresh();
					sprintf(fieldstr,"___");
				}
			}
			break;
		}// end switch(mode)
				
		command = 0xff;	//command is done being executed, don't want to execute again
	}
}


void main(void) {
	unsigned char i;

	initialize();
	
	while(1) {
		//every 30ms
		if (stateFlag == 1) {
			stateFlag = 0;
			stateMachine();	//enter keypad scanning state machine
			doCommand();		//perform selected command
         
         //note has been triggered, decrement counter   
   		if (trig_timer > 0) {
     			trig_timer--;
        		trigger = 1;
        	}
		}
		
		//if trig_timer has been modified
		if (trigger == 1) {
			trigger = 0;
			//if timer was just started, play note
			if (trig_timer == 9) {
				putchar(0x90);
				putchar(pch[cur_track][cur_beat + (cur_meas<<4)]);
				putchar(vel[cur_track][cur_beat + (cur_meas<<4)]);
			//else if count reaches 0 (about 270ms), stop note
			} else if (trig_timer == 0) {
				putchar(0x90);
				putchar(pch[cur_track][cur_beat + (cur_meas<<4)]);
				putchar(0x00);
			}
		}
		
		//play_flag is 1 only when the sequence was just started
		if (play_flag == 0) {
			if (prepare == 1) {
				prepare = 0;
				prepBuffer();
			}
		} else {
			// need to buffer values before timer1 starts
			for(i=0;i<NTRK;i++) beat[i] = 0;
			prepare = 0;
			prepBuffer();
			play_flag = 0;
			
			//start timer1, set to interrupt right away
			TCNT1 = OCR1A-1;
			TCCR1B = 0x0b;
		}		
	}
}


//scans the keypad and returns the button currently pressed by the user, or 0xff if nothing is pressed
char getButton(void) {
	char key,butnum;
	
	DDRC = 0x0f;
	PORTC = 0xf0;
	delay_us(5);
	key = PINC;
	
	DDRC = 0xf0;
	PORTC = 0x0f;
	delay_us(5);
	key = key | PINC;
	
	if (key != 0xff) {
		for(butnum=0;butnum<16;butnum++) {
			if (keytbl[butnum]==key) break;
		}
		if (butnum==16) butnum=-1;
	}
	else butnum = -1;
	
	return butnum;
}


//debounces the keypad, sets command variable to the key pressed
void stateMachine(void) {
	char key;
	key = getButton();
	
	switch(state) {
	case 0:
		if (key != 0xff) {
			key2 = key;
			state = 1;
		}
		break;
	case 1:
		if (key==key2) {
			state = 2;
			command = key;
		}
		else state = 0;
		break;
	case 2:
		if (key!=key2) state = 3;
		break;
	case 3:
		if (key==key2) state = 2;
		else state = 0;
		break;
	}	
}


//initializes MCU, and enters preset song if preset button detected
void initialize(void) {
	int i,j;
	char select;
	
	DDRD = 0xff; 		//serial port
	UBRR = 7;	  		//31.25kBaud
	UCR = 0x08;   		//UART transmit enable
	TCCR1B = 0x00; 	//timer1 stopped, to start use 0x0b
	TIMSK = 0x40; 		//enable timer1 compare match A interrupt
	TCNT1 = 0x00; 		//reset timer1
	OCR1A = 7812; 		// 120 bpm
	
	TCNT0 = 139;		//reload timer0
	TCCR0 = 5;	  		//scale timer0 by 1024
	TIMSK = TIMSK | 2; //enable timer0 overflow interrupt
	
	//initialize arrays, variables
	for(i=0;i<NTRK;i++) {
		for(j=0;j<(NMEAS<<4);j++) {
			pch[i][j] = 0x00;
			vel[i][j] = 0x00;
		}
	}
		
	tempo = 120;
	cur_track = 0;
	cur_meas = 0;
	cur_beat = 0;
	
	loop[0] = 31;
	loop[1] = 31;
	loop[2] = 31;
	loop[3] = 31;
		
	prepare = 0;

	state = 0;	
	stateFlag = 0;

	trigger = 0;
	
	mode = M_NORM;
	
	//initialize LCD
	lcd_init(20);
	lcd_clear();
	refresh();
	
	//if preset button is pushed, load the preset sequence
	select = getButton();
	if (select == K_PRESET) {
		//C#5 = bassdrum
		pch[0][0] = 61;
		vel[0][0] = 90;
		pch[0][4] = 61;
		vel[0][4] = 90;
		pch[0][8] = 61;
		vel[0][8] = 90;
		pch[0][12] = 61;
		vel[0][12] = 90;
		pch[0][16] = 61;
		vel[0][16] = 90;
		pch[0][20] = 61;
		vel[0][20] = 90;
		pch[0][24] = 61;
		vel[0][24] = 90;
		pch[0][28] = 61;
		vel[0][28] = 90;
		pch[0][30] = 61;
		vel[0][30] = 90;
		
		//D5 = snaredrum
		pch[1][4] = 62;
		vel[1][4] = 80;
		pch[1][12] = 62;
		vel[1][12] = 80;
		pch[1][20] = 62;
		vel[1][20] = 80;
		pch[1][23] = 62;
		vel[1][23] = 80;
		pch[1][28] = 62;
		vel[1][28] = 80;
		
		//bassline
		pch[2][2] = 36;
		vel[2][2] = 50;
		pch[2][6] = 36;
		vel[2][6] = 50;
		pch[2][7] = 36;
		vel[2][7] = 80;
		pch[2][8] = 36 | 0x80;
		vel[2][8] = 80;
		pch[2][9] = 36 | 0x80;
		vel[2][9] = 80;
		pch[2][10] = 36;
		vel[2][10] = 50;
		pch[2][14] = 36;
		vel[2][14] = 50;
		pch[2][18] = 36;
		vel[2][18] = 50;
		pch[2][22] = 36;
		vel[2][22] = 50;
		pch[2][23] = 36;
		vel[2][23] = 80;
		pch[2][24] = 36 | 0x80;
		vel[2][24] = 80;
		pch[2][25] = 36 | 0x80;
		vel[2][25] = 80;
		pch[2][26] = 36;
		vel[2][26] = 50;
		pch[2][28] = 42;
		vel[2][28] = 90;
		pch[2][30] = 42;
		vel[2][30] = 90;
		
	}
	
	//enable all interrupts
	#asm
		sei
	#endasm
}