;************************************************************************** ; FILE: mmm.asm * ; CONTENTS: 'Mad Music Machine' * ; COPYRIGHT: MadLab Ltd. 1993-99 * ; AUTHOR: James Hutchby * ; UPDATED: 02/10/99 * ;************************************************************************** ; list p=16C54A list p=16C54C include "p16c5x.inc" ; __config _RC_OSC & _WDT_ON & _CP_OFF __config _RC_OSC & _WDT_ON & _CP_ON MIX equ 1 ; mix, 1 to 6 __idlocs h'AA50'+MIX-1 errorlevel -305 ;************************************************************************** ; * ; Specification * ; * ;************************************************************************** ; function select + keyboard 1 => octave 1 ; function select + keyboard 2 => octave 2 ; function select + keyboard 3 => octave 3 ; function select + keyboard 4 => octave 4 ; function select + keyboard 5 => tune 1 ; function select + keyboard 6 => tune 2 ; function select + keyboard 7 => tune 3 ; function select + keyboard 8 => tune 4 ; goes into sleep mode after ~2 minutes inactivity ;************************************************************************** ; * ; Port assignments * ; * ;************************************************************************** PORTA_IO equ b'0010' ; port A I/O status PORTB_IO equ b'11111111' ; port B I/O status #define SPK1 PORTA,0 ; speaker #define SPK2 PORTA,2 #define LED PORTA,3 ; LED indicator #define FUNC PORTA,1 ; function select SPK_MASK equ b'0101' ; speaker mask ;************************************************************************** ; * ; File register usage * ; * ;************************************************************************** cblock h'07' flags ; various flags clock:2 ; system clock keyboard ; keyboard state keyboard_ ; previous keyboard state key ; key pressed function ; function select counter spk_time:2 ; speaker task despatch time spk_period:2 ; speaker task despatch period cur_octave ; current octave, 1 to 4 tune_note ; tune note tune_pnt ; tune pointer tune_ret ; tune return sleep_count ; sleep mode counter work1, work2 ; work registers endc ; flags SPK_ENABLE equ 0 ; set if speaker enabled POLL_TASK equ 1 ; set if poll task already despatched WAIT_RELEASE equ 2 ; set if waiting for release TUNE_ENABLE equ 3 ; set if tune enabled TUNE_TASK equ 4 ; set if tune task already despatched TEMPO_FAST equ 5 ; set if tempo fast SELECT equ 6 ; function select state ;************************************************************************** ; * ; Constants and timings * ; * ;************************************************************************** CLOCK equ d'3500000' ; processor clock frequency in Hz PRESCALE equ b'000001' ; prescale RTCC 1:4 BEEP_PITCH equ d'50' ; beep pitch BEEP_PERIOD equ d'200' ; beep period POLL_PERIOD equ b'00001111' ; poll task despatch mask (~32 ms) FUNC_TIMEOUT equ b'0001111' ; function select timeout mask (~15/2 s) TUNE_PERIOD0 equ b'01111111' ; tune task despatch mask (~256 ms) TUNE_PERIOD1 equ b'00111111' ; tune task despatch mask (~128 ms) KEY_BASE equ 1 ; key base octave TUNE_BASE equ 2 ; tune base octave DEF_OCTAVE equ 2 ; default octave (middle C) C1_PERIOD equ d'511' ; ~130.8 Hz C1S_PERIOD equ d'482' ; ~138.6 Hz D1_PERIOD equ d'455' ; ~146.9 Hz D1S_PERIOD equ d'430' ; ~155.6 Hz E1_PERIOD equ d'406' ; ~164.8 Hz F1_PERIOD equ d'383' ; ~174.6 Hz F1S_PERIOD equ d'361' ; ~185.0 Hz G1_PERIOD equ d'341' ; ~196.0 Hz G1S_PERIOD equ d'322' ; ~207.7 Hz A1_PERIOD equ d'304' ; ~220.0 Hz A1S_PERIOD equ d'287' ; ~233.1 Hz B1_PERIOD equ d'271' ; ~247.0 Hz C2_PERIOD equ d'256' ; ~261.6 Hz C2S_PERIOD equ d'242' ; ~277.2 Hz D2_PERIOD equ d'228' ; ~293.7 Hz ;************************************************************************** ; * ; Macros * ; * ;************************************************************************** routine macro label ; routine label endm table macro label ; define lookup table label addwf PCL endm entry macro value ; define table entry retlw value endm index macro label ; index lookup table call label endm jump macro label ; jump through table goto label endm tstw macro ; test w register iorlw 0 endm movff macro f1,f2 ; move file to file movfw f1 movwf f2 endm movlf macro n,f ; move literal to file movlw n movwf f endm org 0 ;************************************************************************** ; * ; Lookup tables * ; * ;************************************************************************** ;-------------------------------------------------------------------------- ; Tune definitions ;-------------------------------------------------------------------------- ; rrttnnnn = note: ; rr = rest duration (00 = none ... 11 = max) ; tt = tone duration (00 = min ... 11 = max) ; nnnn = tone value (0001 = C ... 1111 = D') ; xxxx0000 = escape: ; xx000000 = end of tune or sub-tune ; xx010000 = play sub-tune (followed by address) ; rr100000 = long rest (00 = min ... 11 = max) ; oo110000 = octave shift (00 = -2, 01 = -1, 10 = +1, 11 = +2) C1 equ b'0001' C1S equ b'0010' D1 equ b'0011' D1S equ b'0100' E1 equ b'0101' F1 equ b'0110' F1S equ b'0111' G1 equ b'1000' G1S equ b'1001' A1 equ b'1010' A1S equ b'1011' B1 equ b'1100' C2 equ b'1101' C2S equ b'1110' D2 equ b'1111' start macro label ; start of tune or sub-tune label endm tempo macro t ; tempo entry t<>1 entry C1S_PERIOD>>1 entry D1_PERIOD>>1 entry D1S_PERIOD>>1 entry E1_PERIOD>>1 entry F1_PERIOD>>1 entry F1S_PERIOD>>1 entry G1_PERIOD>>1 entry G1S_PERIOD>>1 entry A1_PERIOD>>1 entry A1S_PERIOD>>1 entry B1_PERIOD>>1 entry C2_PERIOD>>1 entry C2S_PERIOD>>1 entry D2_PERIOD>>1 table tune_esc ; tune task escape table goto tesc00 ; end of tune or sub-tune goto tesc01 ; play sub-tune goto tesc10 ; long rest goto tesc11 ; octave shift ;************************************************************************** ; * ; Procedures * ; * ;************************************************************************** lookup movwf PCL ; look up table ;-------------------------------------------------------------------------- ; updates the system clock ;-------------------------------------------------------------------------- routine clock_task rlf TMR0,w ; increment high byte if btfss clock+1,7 ; low byte rolled over goto ctask1 skpc incf clock+0 ctask1 movwf clock+1 rrf clock+1 retlw 0 ;-------------------------------------------------------------------------- ; plays a note ;-------------------------------------------------------------------------- routine play_note movff cur_octave,work1 ; adjust for octave goto playn2 playn1 clrc rrf spk_period+0 rrf spk_period+1 playn2 decfsz work1 goto playn1 call clock_task ; initialise task despatch time movff clock+0,spk_time+0 movff clock+1,spk_time+1 bsf flags,SPK_ENABLE ; enable the speaker bcf SPK1 bsf SPK2 retlw 0 ;-------------------------------------------------------------------------- ; sounds a beep ;-------------------------------------------------------------------------- beep macro bcf SPK1 bsf SPK2 movlf BEEP_PERIOD&~1,work1 beep1 movlw SPK_MASK ; toggle speaker output xorwf PORTA movlf BEEP_PITCH,work2 ; half-cycle delay beep2 clrwdt decfsz work2 goto beep2 decfsz work1 goto beep1 ; bcf SPK1 bcf SPK2 endm ;-------------------------------------------------------------------------- ; speaker task ;-------------------------------------------------------------------------- routine spk_task movlw PORTA_IO ; re-initialise port tris PORTA btfsc flags,SPK_ENABLE ; speaker enabled ? goto stask1 bcf SPK1 ; speaker off if not bcf SPK2 goto sexit stask1 call clock_task movfw clock+0 ; task to be despatched ? subwf spk_time+0,w movwf work1 movfw clock+1 subwf spk_time+1,w skpc decf work1 btfsc work1,7 goto stask3 tstf work1 bnz sexit andlw -h'40' bnz sexit ; branch if not clrwdt stask2 movfw TMR0 ; wait for task despatch time subwf spk_time+1,w andlw h'80' bz stask2 stask3 movlw SPK_MASK ; toggle speaker output xorwf PORTA movfw spk_period+1 ; next despatch time addwf spk_time+1 skpnc incf spk_time+0 movfw spk_period+0 addwf spk_time+0 sexit goto clock_task ;-------------------------------------------------------------------------- ; main entry point ;-------------------------------------------------------------------------- routine main_entry movlw PORTA_IO ; initialise ports tris PORTA movwf PORTA movlw PORTB_IO tris PORTB movwf PORTB movfw STATUS ; wake-up from sleep ? andlw (1<