//Demo of assembly code in C -- //Both ISRs and some of the task code has been converted to ASM // //Note the pragma to turn off automatic register allocation //in the compiler config dialog so that C variables are //available to the assembler #include #include //for debugging using printf, etc #include //I like these definitions #define begin { #define end } //timer 1 constants #define prescale1 1 #define clear_on_match 8 //the subroutines void initialize(void); //all the usual mcu stuff #pragma regalloc- //following variables must not be registers unsigned char reload; //timer 0 reload to set 1 mSec unsigned char note, noteplaying; //current note to be played... //task 1 sets note to 0 to start song // State Variables for reading in the midi data packets #define Status 1 #define Key 2 #define Velocity 3 #define Program 4 #define AfterTouch 5 unsigned char State; //State of the midi data unsigned char data; //the data being read in unsigned char Sound; //the "instrument" chosen //variables to determine which note to play int i; unsigned char spot; unsigned char currentnote[10]; unsigned char currentpos; unsigned char sample; unsigned char counter; unsigned int frequency; #define macVel = 0x7F #pragma regalloc+ //**************************************************** //table of periods for 1 cycle of each musical note in 1/4 microsec ticks // // midi note freq period in // Hz 1/4 microsec // // 0x30 C3 131 30534 // 0x31 C3# 139 28776 // 0x32 D3 147 27210 // 0x33 D3# 156 25641 // 0x34 E3 165 24242 // 0x35 F3 175 22857 // 0x36 F3# 185 21621 // 0x37 G3 196 20408 // 0x38 G3# 208 19230 // 0x39 A3 220 18181 // 0x3A A3# 233 17167 // 0x3B B3 247 16194 // 0x3C C4 262 15267 // 0x3D C4# 277 14440 // 0x3F D4 294 13605 // 0x40 D4# 311 12861 // 0x41 E4 330 12121 // 0x42 F4 349 11461 // 0x43 F4# 370 10810 // 0x44 G4 392 10204 // 0x45 G4# 415 9638 // 0x46 A4 440 9090 // 0x47 A4# 466 8583 // 0x48 B4 494 8097 // 0x49 C5 523 7648 // put the notes in flash to save RAM //notetable for the all the keys on the Studio 610 Plus //Each entry is a different frequency covering 5 octives of music ranging from C2 to C6 and //then looping back around to C4 thru C5 for the top octive flash int notetable[61] = {65, 69, 73, 78, 82, 87, 92, 98, 104, 110, 116, 123, 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 544, 588, 622, 660, 698, 740, 784, 830, 880, 932, 988, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494,523}; //Instruments //These are predefined waves which will produce unique sounds when all the bytes in the // table are played at one of the notetable frequencies flash unsigned char sinetable[16]={0x80, 0xb1, 0xda, 0xf6, 0xff, 0xf6, 0xda, 0xb1, 0x80, 0x4f, 0x26, 0x0a, 0x00, 0x0a, 0x26, 0x4f}; flash unsigned char squaretable[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,0xff, 0xff, 0xff, 0xff}; flash unsigned char sawtable[16] = {0xff, 0xdf, 0xbf, 0x9f, 0x6f, 0x4f, 0x2f, 0x0f, 0x0d, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00}; flash unsigned char tritable[16] = {0x00, 0x1f, 0x3f, 0x5f, 0x7f, 0x9f, 0xbf, 0xdf, 0xff, 0xdf, 0xbf, 0x9f, 0x7f, 0x5f, 0x3f, 0x1f}; flash unsigned char wave[16] = {0x00, 0x4f, 0x8f, 0xcf, 0xff, 0xcf, 0xaf, 0x8f, 0x6f, 0x8f, 0xaf, 0xcf, 0xff, 0xcf, 0x8f, 0x4f}; #pragma savereg- //********************************************************** //timer 1 compare-match A ISR interrupt [TIM1_COMPA] void cmpA_overflow(void) begin //determines the sound and plays the corresponding table if (Sound == 0) PORTC = sinetable[sample++]; else if (Sound == 1) PORTC = squaretable[sample++]; else if (Sound == 2) PORTC = sawtable[sample++]; else if (Sound == 3) PORTC = tritable[sample++]; else PORTC = wave[sample++]; if (sample == 16) begin sample=0; end end #pragma savereg+ //********************************************************** //Entry point and task scheduler loop void main(void) begin initialize(); //main task scheduler loop -- never exits! while(1) begin if (UCSRA.7) begin data = UDR; //midi data being read in // check to make sure we aren't receiving a status bit // when we're not in the Status state if (((data & 0x80) == 0x80) && !(State == Status)) begin State = Status; end if (State == Status) begin // Status = NoteOn signal, determine note to play if ((data & 0x90) == 0x90) begin State = Key; end else // Status = ControlPressure signal, next data packet is Velocity if ((data & 0xd0) == 0xd0) begin State = AfterTouch; end else // Status = Program, determine new instrument based on next piece of data if ((data & 0xc0) == 0xc0) State = Program; else // Status = unused status byte // remain in state Status until new status byte received if ((data & 0x80) == 0x80) begin State = Status; end end // end of state Status else // Key determines which note to play with a base note being 0x24 (C2) // next byte is the velocity if (State == Key) begin note = data - 0x24; //determine not State = Velocity; end // end state key else // Velocity determines if we should play the note or not // if velocity is 0, remove that note from list of notes to play // if list is empty, turn off the sound // always return to State = Status to look for next status byte if (State == Velocity) begin State = Status; // note is being played if (data != 0x00) begin noteplaying = note; currentnote[currentpos] = note; spot = currentpos++; counter++; if (counter >6) counter = 6; TCNT1=0; frequency = notetable[currentnote[spot]]; OCR1A = 1000000L/frequency ; //and load the correct time out for timer 1 TCCR1B = prescale1 + clear_on_match ; //start timer 2 end else begin counter--; for (i = 0; i < 10; i++) begin if (currentnote[i] == note) begin note ==100; currentnote[i] = 100; end end //nothing being played, turn off sound if (counter <= 0) begin spot = 0; currentpos = 0; TCCR1B = 0; //turn off timer 1 end // note was released, still other notes to play else begin while(currentnote[currentpos] == 100) begin currentpos--; end spot = currentpos; TCCR1B = 0; TCNT1=0; frequency = notetable[currentnote[currentpos]]; OCR1A = 1000000L/frequency ; //and load the correct time out for timer 1 TCCR1B = prescale1 + clear_on_match ; //start timer 2 end // else end //statevelocity end else // Program determines which sound to play if (State == Program) begin State = Status; Sound = Sound +1; if (Sound >4) begin Sound = 0; end end else if (State == AfterTouch) begin State = Status; end end //end receive flag end //end the endless while end //end main //Set it all up void initialize(void) begin //set up the ports DDRD=0x00; // PORT D is an input DDRC=0xff; // PORT C is an ouput PORTC=0x00; //serial setop for debugging using printf, etc. UCSRB = 0x10 + 0x08 ; UCSRC = 0b01000000; UBRRL = 31 ; //baud rate of 31.25K //set up timer 0 //62.5 x (64x.25) microSec = 1.0 mSec, so prescale 64, and count 62 times. reload=256-250; //value for 1 Msec TCNT0=reload; //preload timer 1 so that is interrupts after 1 mSec. TCCR0=3; //prescalar to 64 //TIMSK=0x01; //turn on timer 0 overflow ISR //set up timer 1 TIMSK=TIMSK | 0x10; //turn on timer 1 compare match interrupt TCCR1B = 0; //disable timer 1 until song starts TCNT1 = 0; //and zero the timer //init the current note 8 => all done //song starts when task 1 sets note to 0 note=100; counter = 0; State = Status; Sound = 5; // determine the initial sound // initialize the first note being played to nothing noteplaying = 100; currentnote[0] = currentnote[1] = currentnote[2] = currentnote[3] = currentnote[4] = currentnote[5] = 100; currentpos = 0; //crank up the ISRs #asm sei #endasm end