;******************************************************************** ; di2456.txt ; ; Assembly code for: ; ; "Add music to your next project," EDN, Dec 23, 1999, pg 112 ; http://www.ednmag.com/ednmag/reg/1999/122399/designideas.htm#26di4 ;******************************************************************** list p=16f84 include errorlevel -302 ; Application-specific #define BEATS_PER_MIN .120 #define CLOCK .4000000 #define PRESCALER .8 #define TICK (CLOCK / .4 / PRESCALER) ; Frequencies of the notes in Hz #define FreqC .523 #define FreqD .587 #define FreqE .659 #define FreqF .698 #define FreqG .784 #define FreqA .880 #define FreqB .988 ; Counts required to generate the notes. Two ticks are needed for one period. #define TICKS_NEEDED(Freq) (.256 - ((TICK/Freq/.2)-.1)) #define NoteC .137 ; TICKS_NEEDED( FreqC ) #define NoteD .151 ; TICKS_NEEDED( FreqD ) #define NoteE .162 ; TICKS_NEEDED( FreqE ) #define NoteF .167 ; TICKS_NEEDED( FreqF ) #define NoteG .177 ; TICKS_NEEDED( FreqG ) #define NoteA .186 ; TICKS_NEEDED( FreqA ) #define NoteB .194 ; TICKS_NEEDED( FreqB ) ; Duration for each eighth for each note. Quarter note = 1 beat. ;#define DURATION(Note) ((TICK/BEATS_PER_MIN/.8*.60*.4) / (.256-Note)) #define DURATION(Note) (.31250 / (.256-Note)) #define DurationC DURATION( NoteC ) #define DurationD DURATION( NoteD ) #define DurationE DURATION( NoteE ) #define DurationF DURATION( NoteF ) #define DurationG DURATION( NoteG ) #define DurationA DURATION( NoteA ) #define DurationB DURATION( NoteB ) ; Define length of emphasis pause - 1/64 note left for stop. ;#define STOP_DURATION(Note) ((TICK/BEATS_PER_MINUTE/.64*.60*.4) / (.256-Note)) #define STOP_DURATION(Note) (.3906 / (.256-Note)) #define StopC STOP_DURATION( NoteC ) #define StopD STOP_DURATION( NoteD ) #define StopE STOP_DURATION( NoteE ) #define StopF STOP_DURATION( NoteF ) #define StopG STOP_DURATION( NoteG ) #define StopA STOP_DURATION( NoteA ) #define StopB STOP_DURATION( NoteB ) #define STOP_LENGTH 1 ; Counts to pause after each note. ;#define STOP_TICKS ((TICK/BEATS_PER_MINUTE/.64*.60) * STOP_LENGTH) #define STOP_TICKS (.960 * STOP_LENGTH) ; Short cuts for defining durations #define CNote( Eighths ) Eighths * DurationC - StopC #define DNote( Eighths ) Eighths * DurationD - StopD #define ENote( Eighths ) Eighths * DurationE - StopE #define FNote( Eighths ) Eighths * DurationF - StopF #define GNote( Eighths ) Eighths * DurationG - StopG #define ANote( Eighths ) Eighths * DurationA - StopA #define BNote( Eighths ) Eighths * DurationB - StopB ; Global variables Wimage equ 0x20 TempStatus equ 0x21 DoingStop equ 0x22 NoteNumber equ 0x23 CurrentNoteTimeL equ 0x24 CurrentNoteTimeH equ 0x25 Temp1 equ 0x26 Temp2 equ 0x27 ; Reset Vector org 0x0000 clrf PCLATH goto Main ; Interrupt Vector org 0x0004 INT movwf Wimage swapf STATUS,W bcf STATUS,RP0 movwf TempSTATUS btfss INTCON,T0IF ; Timer0 interrupt? goto Pop bcf INTCON,T0IF ; Clear the Timer0 interrupt flag bcf STATUS,RP0 ; If not doing a stop bit movf DoingStop,F btfss STATUS,Z goto Check1 bcf STATUS,RP0 movf NoteNumber,W ; and not at end of song call Notes iorlw 0x00 btfsc STATUS,Z goto Check1 movlw 0x01 bcf STATUS,RP0 ; Then, xorwf PORTA,F ; flip the output port bit. Check1 bcf STATUS,RP0 ; if CurrentNoteTime is zero movf CurrentNoteTimeL,W iorwf CurrentNoteTimeH,W btfss STATUS,Z goto UpdateNoteTime bcf STATUS,RP0 ; if doing a stop bit, go to the next note. movf DoingStop,F btfsc STATUS,Z goto Check2 bcf STATUS,RP0 ; DoingStop = FALSE clrf DoingStop incf NoteNumber,F ; Increment NoteNumber movf NoteNumber,W ; Get next note duration movwf FSR bcf STATUS,C rlf FSR,F movf FSR,W call Durations bcf STATUS,RP0 movwf CurrentNoteTimeL incf FSR,F movf FSR,W call Durations bcf STATUS,RP0 movwf CurrentNoteTimeH goto UpdateNoteTime Check2 ; Pause for emphasis between notes movlw 0x01 ; DoingStop = TRUE bcf STATUS,RP0 movwf DoingStop movlw 0x01 ; Set CurrentNoteTime = STOP_LENGTH movwf CurrentNoteTimeL movlw 0x00 movwf CurrentNoteTimeH UpdateNoteTime bcf STATUS,RP0 ; Decrement CurrentNoteTime movf CurrentNoteTimeL,F btfsc STATUS,Z decf CurrentNoteTimeH,F bcf STATUS,RP0 decf CurrentNoteTimeL,F movf NoteNumber,W ; If durations = zero movwf FSR ; Start the song over again bcf STATUS,C rlf FSR,F movf FSR,W call Durations bcf STATUS,RP0 movwf Temp1 incf FSR,F movf FSR,W call Durations bcf STATUS,RP0 movwf Temp2 movf Temp1,F btfss STATUS,Z goto Check3 bcf STATUS,RP0 movf Temp2,F btfss STATUS,Z goto Check3 bcf STATUS,RP0 ; Set NoteNumber = 0 clrf NoteNumber ; Large pause between consecutive plays. movf NoteNumber,W ; Get duration for current note movwf FSR bcf STATUS,C rlf FSR,F movf FSR,W call Durations bcf STATUS,RP0 movwf CurrentNoteTimeL incf FSR,F movf FSR,W call Durations bcf STATUS,RP0 movwf CurrentNoteTimeH goto Pop Check3 ; If doing a stop bit bcf STATUS,RP0 ; Reset the timer for a pause movf DoingStop,F btfsc STATUS,Z goto Check4 movlw 0xC0 bcf STATUS,RP0 addwf TMR0,F goto Pop Check4 clrf PCLATH ; Otherwise bcf STATUS,RP0 ; Reset the timer for a note movf NoteNumber,W call Notes bcf STATUS,RP0 addwf TMR0,F Pop swapf TempSTATUS,W movwf STATUS swapf Wimage,F swapf Wimage,W retfie Initialize movlw 0x1E ; Set RA0 to output. bsf STATUS,RP0 movwf TRISA bcf STATUS,RP0 ; Initialize port A. clrf PORTA movlw 0x82 ; Set the timer prescaler. bsf STATUS,RP0 movwf OPTION_REG movlw 0x01 ; Initialize variables movwf DoingStop ; Pausing movlw 0xFF ; Set note number movwf NoteNumber clrf CurrentNoteTimeL; Clear duration clrf CurrentNoteTimeH bcf STATUS,RP0 ; Initialize the timer period. movlw 0x00 call Notes movwf TMR0 bsf INTCON,T0IE ; Enable the timer interrupt. bsf INTCON,GIE ; Enable global interrupts. return Main call Initialize goto $ ; Play the song forever. return org 0x0100 ; Table must start on an even 256 byte boundary ; Table of notes to play Notes addwf PCL,F dt NoteE, NoteE, NoteF, NoteG, NoteG, NoteF, NoteE, NoteD dt NoteC, NoteC, NoteD, NoteE, NoteE, NoteD, NoteD, NoteE dt NoteE, NoteF, NoteG, NoteG, NoteF, NoteE, NoteD, NoteC dt NoteC, NoteD, NoteE, NoteD, NoteC, NoteC, 0 org 0x0200 ; Table must start on an even 256 byte boundary ; Table of note durations for the Notes table Durations addwf PCL,F dt 0x46, 0x02, 0x46, 0x02, 0x68, 0x02 ;ENote(2), ENote(2), FNote(2) dt 0xB4, 0x02, 0xB4, 0x02, 0x68, 0x02 ;GNote(2), GNote(2), FNote(2) dt 0x46, 0x02, 0x08, 0x02, 0xCC, 0x01 ;ENote(2), DNote(2), CNote(2) dt 0xCC, 0x01, 0x08, 0x02, 0x46, 0x02 ;CNote(2), DNote(2), ENote(2) dt 0x69, 0x03, 0x04, 0x01, 0x10, 0x04 ;ENote(3), DNote(1), DNote(4) dt 0x46, 0x02, 0x46, 0x02, 0x68, 0x02 ;ENote(2), ENote(2), FNote(2) dt 0xB4, 0x02, 0xB4, 0x02, 0x68, 0x02 ;GNote(2), GNote(2), FNote(2) dt 0x46, 0x02, 0x08, 0x02, 0xCC, 0x01 ;ENote(2), DNote(2), CNote(2) dt 0xCC, 0x01, 0x08, 0x02, 0x46, 0x02 ;CNote(2), DNote(2), ENote(2) dt 0x0C, 0x03, 0xE6, 0x00, 0x98, 0x03 ;DNote(3), CNote(1), CNote(4) dt 0x00, 0x00 ;0 end