$TITLE(File MUSIC.ASM - 87C750 music box - M. Covington 1994) $MOD750 $DEBUG ; Music-box program for the 87C750 with 3.58-MHz crystal. ; (4-MHz crystal plays same tune faster and in a higher key.) ; Michael A. Covington mcovingt@ai.uga.edu ; Artificial Intelligence Center ; The University of Georgia ; Athens, Georgia 30602-7415 ; +--------+ ; 330 ohms \ 8-ohm / ; P0.0 ------/\/\/\/--------|spkr |------ +5V ; +-----+ ; Because of the clock interrupts 8 times per second, there is an ; 8-Hz clicking superimposed on the music. Because it is in time ; with the musical rhythm, it is not obtrusive. RTCLOCK DATA 3FH ; Data location 3FH (top byte of data memory) ; will be used as a real-time clock. ORG 0 LJMP MAIN ; long jump to main program ; Timer interrupt service routine used to keep track of ; length of each note. Takes 3 cpu cycles every 1/8 second. ORG 0BH ISR: DEC RTCLOCK RETI ; Subprograms ;---------------------------------------------------------------- MED_WAIT: ; Delays A+60 CPU cycles, including the ACALL that calls it. ; A ranges from 3 to 255 inclusive. For timing audio frequencies. PUSH ACC ; yes, push it twice PUSH ACC ANL A,#1 ; isolate bottom bit of A JZ MW1 ; branch depending on whether A is even POP ACC ; 2 cycles \ DEC A ; 1 cycle | 5 cycles if A was odd SJMP MW2 ; 2 cycles / MW1: POP ACC ; 2 cycles \ 4 cycles if A was even SJMP MW2 ; 2 cycles / MW2: RR A ; rotate A 1 bit right thereby dividing by 2 MW3: DJNZ ACC,MW3 ; 2*(A/2) cycles MOV A,#20 MW4: DJNZ ACC,MW4 ; 40 more cycles POP ACC RET ;---------------------------------------------------------------- SOUND: ; Produces audio on P0.0, preceded by a 5-msec silence. ; Registers: ; A = total duration in eighths of a second, including ; the initial silence; ; R0 = 0 if you want silence, or ; (duration of low half-cycle in CPU cycles) - 65 ; R1 = (duration of high half-cycle in CPU cycles) - 65 ; R0, R1 range from 3 to 255 inclusive. ; Uses RTCLOCK data area and timer interrupt service ; routine elsewhere in this program. PUSH ACC ; Set up RTCLOCK to count down from A at 1/8-sec intervals. MOV RTH,#6EH MOV RTL,#5AH ; Count from 6E5A to 10000H MOV TH,RTH MOV TL,RTL ; Preload timer for first cycle MOV RTCLOCK,A ; Preload RTCLOCK, will count down to 0 SETB ET0 ; Enable timer 0 interrupt SETB EA ; Enable interrupts in general SETB TR ; Start timer running ; Moment of silence to separate adjacent musical notes MOV A,#238 ACALL MED_WAIT ACALL MED_WAIT ACALL MED_WAIT ACALL MED_WAIT ACALL MED_WAIT ; If R0=0, make silence, not sound MOV A,R0 JNZ SN1 SN0: MOV A,RTCLOCK ; keep checking timer JZ SN2 SJMP SN0 ; Main loop. Check timer after every half cycle of audio. SN1: CLR P0.0 ; (1 cycle) ; low half cycle MOV A,R0 ; (1 cycle) ACALL MED_WAIT ; (R0+60 cycles) MOV A,RTCLOCK ; (1 cycle) ; bail out if time is up JZ SN2 ; (2 cycles) SETB P0.0 ; (1 cycle) ; high half cycle MOV A,R1 ; (1 cycle) ACALL MED_WAIT ; (R1+60 cycles) MOV A,RTCLOCK ; (1 cycle) ; repeat if time is not up JNZ SN1 ; (2 cycles) SN2: SETB P0.0 ; Final state of output bit is 1 CLR EA ; Turn off interrupts CLR ET0 ; Clear timer interrupt bit CLR TR ; Stop timer POP ACC RET ; All done ;---------------------------------------------------------------- TUNE: ; Plays a tune by reading sets of (R0,R1,A) values beginning ; at @DPTR, in *code* memory (not data memory), until a set ; with A=0 is found. No limit on tune length. ; Calling sequence: MOV DPH,#HIGH(MYDATA) ; MOV DPL,#LOW(MYDATA) ; ACALL TUNE ; Uses regs A, R0, R1, DPTR. Does not preserve any of them. CLR A MOVC A,@A+DPTR MOV R0,A ; got R0 INC DPTR CLR A MOVC A,@A+DPTR MOV R1,A ; got R1 INC DPTR CLR A MOVC A,@A+DPTR ; got A INC DPTR JZ TN1 ; exit if A=0 ACALL SOUND SJMP TUNE TN1: RET ;---------------------------------------------------------------- MAIN: MOV B,3 ; loop counter MOV DPH,#HIGH(MYTUNE) MOV DPL,#LOW(MYTUNE) ACALL TUNE DJNZ B,MAIN ; play it again, Sam... ORL PCON,#2 ; shut down MYTUNE: ; Irish folk song "Morning Has Broken" ; (public domain) DB 225,225,4 ; C, qtr note DB 166,166,4 ; E, qtr note DB 130,130,4 ; G, qtr note DB 82, 83,12 ; C', dotted half note DB 67, 67,12 ; D', dotted half note DB 91, 91,4 ; B, qtr note DB 109,110,4 ; A, qtr note DB 130,130,4 ; G, qtr note DB 109,110,12 ; A, dotted half note DB 130,130,12 ; G, dotted half note DB 225,225,4 ; C, qtr note DB 194,194,4 ; D, qtr note DB 166,166,4 ; E, qtr note DB 130,130,12 ; G, dotted half note DB 109,110,12 ; A, dotted half note DB 130,130,4 ; G, qtr note DB 166,166,4 ; E, qtr note DB 225,225,4 ; C, qtr note DB 194,194,16 ; D, whole note DB 0, 0,4 ; half rest DB 130,130,4 ; G, qtr note DB 166,166,4 ; E, qtr note DB 130,130,4 ; G, qtr note DB 82, 83,12 ; C', dotted half note DB 109,110,12 ; A, dotted half note DB 130,130,4 ; G, qtr note DB 166,166,4 ; E, qtr note DB 225,225,4 ; C, qtr note DB 225,225,12 ; C, dotted half note DB 194,194,12 ; D, dotted half note DB 166,166,4 ; E, qtr note DB 194,194,4 ; D, qtr note DB 166,166,4 ; E, qtr note DB 130,130,12 ; G, dotted half note DB 109,110,12 ; A, dotted half note DB 194,194,4 ; D, qtr note DB 166,166,4 ; E, qtr note DB 194,194,4 ; D, qtr note DB 225,225,16 ; C, whole note DB 0, 0,8 ; half rest (relevant only if repeating tune) DB 0, 0,0 ; end of tune END