/*** WAVMUSIC.ASM *********************************************************** * * * AD1836/ADSP-21161N EZKit Music Demo Program * * * * This routine generates a song using tones generated from a sine * * function approximation routine. * * * * The sin routine is implemented using 32-bit fixed-point math with the * * Taylor Series Function Approximation. * * * * The song is then passed through an ADT (Automatic Double Tracking) * * digital delay effect, and then thickened with a slight pitch shift * * * * The audio data is sent out to the AD1836 DAC0 L & R (headphone) jack * * * * * *********************************************************************************/ /* ADSP-21161N System Register bit definitions */ #include "def21161.h" .GLOBAL Play_Song; .GLOBAL Init_Music_DAGs; .GLOBAL start_music; .EXTERN Left_Channel; .EXTERN Right_Channel; .section /dm dm_data; .VAR input_x; .VAR Count_Down_Zero; .VAR tones[4]; .VAR Mix_Tones; /* Taylor Series Sine Function Approximation 4.28 Format */ .VAR sin_coeff[5] = 0x32570A3D, /* 3.140625 */ 0x0052FFFF, /* 0.02026367 */ 0xAACBFF47, /* -5.325196 */ 0x08B70011, /* 0.5446778 */ 0x1CCE0008; /* 1.800293 */ .VAR SIN_Result; #define num_notes 535 .VAR music[num_notes] = "music.dat"; /*---------------------------------------------------------------------------*/ .section /pm pm_code; start_music: /* now that we are here, we will forever stay in the start_over loop */ bit clr mode1 ALUSAT; /* disable ALU saturation mode for this demo !!! */ /* otherwise, the Count_Down_Zero will not work properly */ bit set ustat2 FLG9O|FLG8O|FLG7O|FLG6O|FLG5O|FLG4O; dm(IOFLAG)=ustat2; start_over: R0 = 0; LCNTR=4, DO zero_out until LCE; zero_out: DM(I2,1) = R0; /* start all freq. pointers at 0 */ R1 = DM(I0,1); /* read duration and point to first freq.*/ R1 = PASS R1; /* put duration in R1 */ DM(Count_Down_Zero) = R1; tone_loop: R1 = DM(Count_Down_Zero); R1 = PASS R1; /* check for zero count */ IF NE JUMP tone_loop; /* loop and wait for interrupt */ MODIFY(I0,4); /* if duration over, point to next duration and start again */ ustat1=dm(IOFLAG); bit tgl ustat1 FLG9|FLG8|FLG7|FLG6|FLG5|FLG4; /* toggle flag 4-9 LEDs so that when a note changes, the LEDs will flicker */ dm(IOFLAG)=ustat1; JUMP start_over; start_music.end: /*----------------------------------------------------------------------------------*/ Init_Music_DAGs: /* I3 used by sine routine */ B0 = music; L0 = @music; M0 = 4; B2 = tones; L2 = 4; M2 = -4; M1 = 0; M3 = 1; RTS; Init_Music_DAGs.end: /******************************************************************************************* INTERRUPT SERVICE ROUTINES ********************************************************************************************/ Play_Song: R1 = DM(Count_Down_Zero); /* get value for note sustain */ R2 = 0x00002000; /* decrement value for sustain of musical note */ R1 = R1 - R2; /* decrement register counter R1 by R2 */ R2 = 0xFFFFE000; /* count down register with 19 bits precision */ R1 = R1 AND R2; /* mask out top 19 bits in register for count down */ DM(Count_Down_Zero) = R1; /* store remainder of tone duration */ R0 = 0; DM(Mix_Tones) = R0; /* clear the mixer */ I2 = tones; LCNTR = 4; /* load counter for 4 tones */ DO tone_gen until LCE; R3 = DM(I0,1); /* read frequency and point to next freq. */ R3 = ASHIFT R3 by -3; R4 = DM(I2,0); /* get pointer from mem */ R5 = R3 + R4; /* add delta t to get new pointer */ DM(I2,1) = R5; /* save pointer for next time */ DM(input_x) = R5; /* put value in X for use by sine */ call sine_routine; /* calculate sine of pointer */ R5 = DM(SIN_Result); /* get result of sin(x) computation */ R6 = ASHIFT R5 BY -4; /* divide sine by 4 */ R7 = DM(Mix_Tones); /* get partial sum from R7 register */ R8 = R6 + R7; /* add new sin value to partial sum */ tone_gen: DM(Mix_Tones) = R8; /* store new sum into mixer */ MODIFY(I0,-4); /* point back to first freq */ /* send signal output to left & right codec channels, or next filter stage */ r8 = ASHIFT r8 by -5; DM(Left_Channel) = r8; DM(Right_Channel) = r8; rts; Play_Song.end: /* -------------------------------------------------------------------------------------------- * * * * SIN(X) - Sine Calculation Subroutine * * * * This module will calculate the value of SIN(X). The scaled input should be placed in R9 * * and the result will be returned in R7. * * * * The approximation is accurate for any input value from 0 to 90 Degrees (First Quadrant). * * This routine takes advantage of the following trigonometric properties to calculate the * * sine for the other three quadrants: * * * * 1) sin(-x) = -sin(x) * * 2) sin(x) = sin(180 Deg - x) * * * * I3 is used as a pointer in this routine * * * * * * --------------------------------------------------------------------------------------------- */ sine_routine: R9 = dm(input_x); I3 = sin_coeff; /* Pointer to sine coefficients buffer */ M3 = 1; L3 = 0; R10 = 0x40000000; R11 = R9; R8 = R9 AND R10; /* Check to see if in 2nd or 4th quadrant */ IF NE R11 = -R9; /* If Yes, then negate input */ R12 = 0x7FFFFFFF; R11 = R11 AND R12; /* Remove sign bit */ R13 = R11; MRB = R11*R13 (SSFR), R14 = DM(I3,M3); /* MRB = x*x - square input, and get first sin coeff from buffer */ MRF = R14*R13 (SSF), R14 = DM(I3,M3); /* MRF = C1*x ... loop unroll first sin coeff and data */ LCNTR = 4, DO sine_approx UNTIL LCE; R15 = MR1B; MRF = MRF + R14*R15 (SSF); /* MRF = C1*x + C2*x^^2 + C3*x^^3 + C4*x^^4 + C5*x^^5 */ MRB = R11*R15 (SSFR); sine_approx: R14 = DM(I3,M3); R7 = MR1F; R8 = MR0F; R7 = ASHIFT R7 BY 3; /* Convert to 1.31 format */ R7 = R7 OR LSHIFT R8 BY -29; /* OR in the upper 3 bits in MR0B as LSBs for R7 */ R7 = PASS R7; IF LT R7 = PASS R12; /* Saturate if needed */ R9 = PASS R9; IF LT R7 = -R7; /* Negate output if needed */ DM(SIN_Result) = R7; RTS; /*-----------------------------------------------------------------------------------------------*/