[CODE] ;---------------------------------------------------------------------; ; MAESTRO.ASM for AVR BUTTERFLY ; ; AUTHOR: DANIEL J. DOREY (RETRODAN) ; ; CREATED: 07-NOV-09 UPDATED: 09-NOV-09 ; ; ; ; THE PROGRAM COMPOSES A SHORT MELODY THEN AS TIME GOES ON THE MELODY ; ; SLOWLY CHANGES; USES TWO TIMERS, ONE TO CREATE TONES & THE OTHER TO ; ; CHANGE NOTES. THE TIMERS RUN IN BACKGROUND USING INTERUPTS. IF YOU ; ; HAVE LEDS ON PORTB YOU CAN WATCH THE NOTES. USES MY RANDOM NUMBER ; ; GENERATOR & MY SYNTHETIC DIVISION METHOD TO MULT BY 2/3 & 3/4. ; ; FOR IMPROVED SOUND HOOK 8 OHM SPEAKER ON BIT 5 OF PORTB TO GROUND ; ; THROUGH A 220 OHM RESISTOR. ; ;---------------------------------------------------------------------; .INCLUDE "M169DEF.INC" ;(BUTTERFLY MCU DEFINITIONS)
.SET MINOTE = 10 ;SMALLEST (HIGHEST) NOTE ALLOWED .SET MAXNOT = 239 ;START NOTE 239=(LOW C) .SET BARLEN = 8 ;NOTES IN A BAR OF MUSIC .SET BARS = 4 ;BARS BEFORE CHANGES .SET TEMPO = 110 ;OVERALL SPEED
;-------------------------; ; DEFINE REGISTERS TO USE ; ;-------------------------; .DEF A = R16 ;GENERAL PURPOSE ACCUMULATOR .DEF B = R17 ;USED BY RANDOM NUMBER GENERATOR... .DEF C = R18 ; .DEF D = R19 ; .DEF E = R20 ; .DEF F = R21 ; .DEF I = R22 ;INDEX .DEF M = R23 ;BAR/NOTE INDEX .DEF N = R24 ;HOLDS CURRENT NOTE TO PLAY
;-----------------------------; ; FIRST WE SETUP A STACK AREA ; ;-----------------------------; ON_RESET: LDI A,LOW(RAMEND) ;SETUP STACK POINTER... OUT SPL,A ;SO CALLS TO SUBROUTINES... LDI A,HIGH(RAMEND) ;WORK CORRECTLY OUT SPH,A RCALL SET_MCU_SPEED ;SET MCU SPEED LDI A,255 ;OPEN PORTS B FOR OUTPUT OUT DDRB,A ;IF YOU HAVE LEDS ON PORT B
;--------------------------------------------------; ; MAIN ROUTINE: REAL WORK IS DONE DURING INTERUPTS ; ;--------------------------------------------------; MAIN: RCALL TIM0_MATCH_MODE ;SETUP TIMER0 FOR SOUND RCALL TIM2_32K_CTC_MODE ;SETUP TIMER2 TO PICK NOTES RCALL RND_INIT ;INITIALIZA RANDOM GENERATOR RCALL INITNOTES ;INITIALIZE NOTES OF SONG LDI M,BARS ;INITIALIZE BARS PER PHRASE SEI ;ACTIVATE INTERUPTS
MAINL:RCALL RND_BYTE ;RANDOMIZE THINGS RJMP MAINL ;GO ROUND, ENDLESS LOOP
;--------[ I N T E R U P T R O U T I N E S ]-----------
;-------------------------------------------; ; TIMER2 INTERUPT ROUTINE USED TO MAKE TONE ; ;-------------------------------------------; ON_INTERUPT: PUSH A ;SAVE FOR LATER RESTORE IN A,SREG ;SAVE CPU STATUS PUSH A RCALL SPEAKER ;SEND BIT TO SPEAKER NOSEC: POP A OUT SREG,A ;RESTORE CPU STATUS POP A ;RESTORE CONTENTS OF A RETI
;----------------------------------; ; FLIP THE SPEAKER BIT 5 ON PORT B ; ;----------------------------------; SPEAKER: SBI DDRB,5 ;SET SPKR PORT B FOR OUTPUT PUSH F LDI F,0b0010_0000 IN A,PORTB ;READ PORT WITH SPEAKER EOR A,F ;FLIP THE SPEAKER BIT OUT PORTB,A ;WRITE OUT TO SPEAKER POP F RET
;-----------------------------------------------; ; TIMER0 INTERUPT ROUTINE GRAB A NOTE & PLAY IT ; ;-----------------------------------------------; ON_INTERUPT2: PUSH A ;SAVE A FOR LATER RESTORE IN A,SREG ;SAVE CPU STATUS PUSH A RCALL FETCHNOTE ;GRAB A NOTE TO PLAY OUT OCR0A,N ;SAVE IT FOR TIMER2 OUT PORTB,N ;IF YOU HAVE LEDS ON PORTB POP A ;RESTORE CPU STATUS OUT SREG,A POP A ;RESTORE A RETI
;--------------------------------------------------------; ; PUT TIMER0 INTO COMPARE (CTC) MODE & SET THE PRESCALER ; ; 0=OFF 1=/1 2=/8 3=/64 4=/256 5=/1024 ; ;--------------------------------------------------------; TIM0_MATCH_MODE: LDI A,2 ;1=OVF, 2=OCF0A MATCH - SET TIMER MODE STS TIMSK0,A LDI A,0b0011_1011 ;PRESCALE 1=/1 10=/8 11=/64 100=/256 101=/1024 OUT TCCR0A,A ;XXXX_1XXX=MATCH ON RET
;-----------------------------------------------------------------; ; SET TIMER2 TO 32.768KHz CLOCK & COMPARE(CTC) MODE ; ; SET PRESCALER 0=OFF 1=/1 2=/8 3=/32 4=/64 5=/128 6=/256 7=/1024 ; ;-----------------------------------------------------------------; TIM2_32K_CTC_MODE: LDI A,TEMPO ;SET COMPARE VALUE (TEMPO) STS OCR2A,A LDI A,8 ;SWITCH TIMER2 CLOCK TO 32.768KHz STS ASSR,A LDI A,2 ;1=OVF, 2=OCF0A MATCH - SET TIMER MODE STS TIMSK2,A ;SET TIMER INTO OVF (OVERFLOW) MODE LDI A,0b0011_1100 ;SET PRESCALE & INTERUPT MODE STS TCCR2A,A RET
;---------[ S U B R O U T I N E S ]------------
;---------------------------; ; INITIALIZE NOTES IN MDATA ; ;---------------------------; INITNOTES: RCALL INPOINT ;SET POINTER LDI I,BARLEN ;GET LENGTH OF A BAR INLUPE: LD N,Y ;GRAB A NOTE RCALL CHNGNOTE ;RANDOM NOTE CHANGE ST Y+,N ;SAVE NEW NOTE DEC I ;DONE YET? BRNE INLUPE ST Y,I ;MARK THE END WITH A ZERO RCALL INPOINT ;RESET POINTER RET
;-------------------------; ; FETCH A NOTE FROM MDATA ; ;-------------------------; FETCHNOTE: LD N,Y+ ;GET NOTE & INCREMENT POINTER CPI N,0 ;CHECK IF WE HIT A ZERO BRNE FNXIT ;IF SO THEN... RCALL INPOINT ;RE-SET NOTE POINTER... DEC M ;GET A NEW STRUC VALUE BRNE FNXIT ;IF ZERO... LDI M,BARS ;RESET BARS RCALL PIKNOTE ;PICK A NOTE & CHANGE IT FNXIT: RET
;-----------------------------; ; RESET THE MDATA POINTER (Y) ; ;-----------------------------; INPOINT: LDI YH,HIGH(MDATA) LDI YL,LOW(MDATA) RET
;---------------------------; ; PICK A NOTE AND CHANGE IT ; ;---------------------------; PIKNOTE: RCALL INPOINT ;RESET POINTER RCALL RND_BYTE ;RANDOM NUMBER 0-7 ANDI A,7 ADD YL,A ;ADD TO POINTER AS OFFEST LD N,Y ;FETCH THAT NOTE RCALL CHNGNOTE ;RANDOMIZE IT AND... ST Y,N ;SAVE IT BACK RCALL INPOINT ;RESET POINTER RET
;-----------------------------------------------------; ; CHANGE A NOTE IN N BY EITHER MULTIPLY BY 2/3 OR 3/4 ; ; RESETS NOTE IF TOO HIGH ; ;-----------------------------------------------------; CHNGNOTE: RCALL RND_BYTE ;DO EITHER 2/3 OR 3/4 BRPL CNSKIP RCALL MUL3DIV4 ;MUL N BY 2/3 RJMP CHKIT CNSKIP: RCALL MUL2DIV3 ;MUL N BY 3/4 CHKIT: CPI N,MINOTE ;CHECK IF TOO HIGH... BRSH CNXIT LDI N,MAXNOT ;IF SO RESET... RJMP CHNGNOTE ;THEN RANDOMIZE CNXIT: RCALL RND_BYTE BRPL CHNGNOTE RET
;-------------------; ; MULTIPLY N BY 2/3 ; ;-------------------; MUL2DIV3: LDI A,170 MUL N,A MOV N,R0 RET
;-------------------; ; MULTIPLY N BY 3/4 ; ;-------------------; MUL3DIV4: LDI A,192 MUL N,A MOV N,R0 RET
;------------------------; ; SET THE MAIN MCU SPEED ; ;------------------------; SET_MCU_SPEED: LDI A,128 ;SET THE MCU CLOCK SPEED STS CLKPR,A LDI A,2 ;0=8MHz 1=4MHz 2=2MHz 3=1MHz STS CLKPR,A ;BUTTERFLY RUNS AT 2MHZ OUT-OF-THE-BOX RET
;-------------------------------------; ; RANDOM: USING A SLIGHTLY MODIFIED ; ; LINEAR CONGRUENCY GENERATOR ; ;-------------------------------------; RND_INIT: LDI A,97 ;USE PRIME NUMBERS AS SEED LDI B,89 LDI C,101 LDI D,193 RND_BYTE: MUL A,C ;MULTIPLY PRIME MUL B,D MOVW A:B,R0:R1 ADD A,E ;ADD IN PRIMES ADC B,F MOVW E:F,C:D ;MIXEM UP MOVW C:D,A:B RET
;-------------[ D A T A A R E A ]------------- .DSEG .ORG $100
;----------------------; ; SONG NOTES DATA AREA ; ;----------------------;