;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;9090 firmware ; ; ;Copyright Trevor Page 2001,2002 ; ;http://introspectiv.org/ ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; D E V E L O P M E N T H I S T O R Y ; ; This is VERSION 1.2 ; ; November 2002: Problems with velocity ; problems have been solved. ; ; December: stupid crappy running status prob solved. ; ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Code contains a lot of crappy comments I left for myself, which ;are probably better off just being ignored. The code has now ;been well-tested and this is the final version of it. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Port Usage: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; RB0/INT: MIDI input ; RB1 to RB6 - data lines ; RB7: sync output - to Q87 ; RA0: DAC clock ; RA1: first 40174 (U43) latch ; RA2: second 40174 (U41) latch ; RA3: enable line for BOTH 4051's ; RA4: MIDI LED (sink; 0V = LED on) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Mapping of PortB to latches and R-2R array ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 4174(0) 4174(1) DAC-R2R/BIT ;RB1 ride hh 10K bit4 ;RB2 hhsel ?midtom 40K bit2 ;RB3 sync ?lowtom 160K bit0 ;RB4 hitom ?snaredrum 80K bit1 ;RB5 h'clap ?bassdrum 20K bit3 ;RB6 crash r'shot 5K bit5 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #define trig_rimshot bsf triggers1,6 ; #define trig_handclap bsf triggers0,5 ; #define trig_bassdrum bsf triggers1,3 ; #define trig_snaredrum bsf triggers1,4 ; #define trig_lowtom bsf triggers1,5 ; #define trig_midtom bsf triggers1,2 ; #define trig_hightom bsf triggers0,4 ; #define trig_hihat bsf triggers1,1 ; #define trig_crash bsf triggers0,6 ; #define trig_ride bsf triggers0,1 ; #define sel_openhat bcf flags,hhsel ;;;nop ; :-) #define sel_closedhat bsf flags,hhsel ;;;;bsf triggers0,2 ; OPT_reg equ h'01' ;Option register (BANK 1) RBPU equ 7 ;Port B weak pull-ups enable: 0 = enable. INTEDG equ 6 ;Interrupt edge selection bit: 0 = interrupt on falling edge on RB0/INT pin. T0CS equ 5 ;TMR0 clock source selection: 0 = internal instruction cycle clock , 1 = RA4 T0SE equ 4 ;TMR0 source edge select bit PSA equ 3 ;Prescaler assignment : 1 = WDT, 0 = TMR0 PS2 equ 2 ;Prescaler rate selection bits PS1 equ 1 ; " " PS0 equ 0 ; " " ; INTCON equ h'0B' ;Interrupt Control Register GIE equ 7 ;Global Interrupt Enable: Set to enable all un-masked interrupts EEIE equ 6 ;EE write complete timer interrupt enable : 1 = enable T0IE equ 5 ;Timer 0 overflow interrupt enable : 1 = enable INTE equ 4 ;External interrupt RB0/INT pin enable : 1 = enable RBIE equ 3 ;Port B pin change interrupts (RB7:RB4) enable :1 = enable T0IF equ 2 ;Timer 0 overflow interrupt flag :must be cleared in software INTF equ 1 ;External interrupt RB0/INT flag :must be cleared in software??? RBIF equ 0 ;Port B pin change interrupt flag :must be cleared in software key_bassdrum: equ h'48' key_snaredrum: equ h'49' key_lowtom: equ h'50' key_midtom: equ h'51' key_hightom: equ h'40' key_handclap: equ h'3e' key_rimshot: equ h'3c' key_crash: equ h'41' key_ride: equ h'43' key_openhat: equ h'45' key_closedhat: equ h'47' ;40174 inputs / outputs: pins 6 to 7, 11 to 10, 13 to 12, 4 to 5, 3 to 2, 14 to 15. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Multiplexer settings for setting ;velocity of each drum circuit ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; mpx0 mpx1 ;Rimshot y7 y4(n/c) ;handclap y4 y4(n/c) ;bassdrum y2(n/c) y3? ;snaredrum y2(n/c) y0? ;lowtom y2(n/c) y1? ;midtom y2(n/c) y2? ;hightom y0 y4(n/c) ;hihat y2(n/c) y5 ;crash y2(n/c) y6 ;ride y2(n/c) y7 ; ;These are defined in macro definitions that follow ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Mapping of PortB to MPX address lines ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;addr# mpx0 mpx1 ;a0 rb6 rb1 ;a1 rb5 rb2 ;a2 rb4 rb3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; EEPROM MEMORY USAGE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;00h to 14h : 21 locations for mapping data ;15h ; Sustain ;16h ; CHECKSUM of locations 00h:15h ;17h ; Complimented checksum of 00h:15h ;18h ; Channel ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list p=16f84 r=dec __config 0x3ff1 ; 3ff1 = not code protected mpx0a0: equ 6 mpx0a1: equ 5 mpx0a2: equ 4 mpx1a0: equ 1 mpx1a1: equ 2 mpx1a2: equ 3 dac_en: equ 0 latch0: equ 1 ;DATA LOADED ON LOW-TO-HIGH TRANSITION latch1: equ 2 mpx_en: equ 3 led: equ 4 #define PAGE0 bcf STATUS,5 #define PAGE1 bsf STATUS,5 PORTB: equ h'06' ;Port B data register PORTA: equ h'05' ;Port A data register TRISB: equ h'06' ;Port B Data Direction Register [DDR] TRISA: equ h'05' ;Port A Data Direction Register [DDR] STATUS: equ h'03' ;Microcontroller Status Register byte C: equ h'0' ;Carry flag bit W: equ h'0' ;Working Register F: equ h'1' ;File Z: equ h'2' ;Zero flag bit TMR0: equ h'01' OPTIONREG: equ h'01' INTCON: equ h'0B' PCL equ h'02' ;program counter INDF equ h'00' FSR equ h'04' EEDATA equ 0x08 ; EEADR equ 0x09 ; EECON1 equ 0x88 ; EECON2 equ 0x89 ; EEIF equ 4 WREN equ 2 RD equ 0 WR equ 1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; USER GPR DEFINITIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BYTE: equ 0x0c ;Temporary MIDI data storage TEMP1: equ 0x0e ;temp TEMP0: equ 0x0f ;temp used inside interrupt service routine triggers0: equ h'10' ;triggers 0 to 5 xttttttx triggers1: equ h'11' ;triggers 6 to 11 xttttttx VELOCITY: equ h'14' ; PITCH: equ h'15' ; statusbyte: equ h'16' currentbytenum: equ h'17' flags: equ h'18' ; various special flags and associated bits sync_clock: equ 1 ; state of sync clock output (probably won't be used) sync_runstop: equ 2 ; state of sync run/stop line vel_is_zero: equ 3 ; hhsel: equ 4 ; chan_match: equ 5 ; chksum: equ 0x1a ; sustain: equ 0x1b ; clk_tmr: equ h'19' ; used to time width of sync clock pulse led_tmr: equ h'20' ; used to count on-time for MIDI activity LED led_tmr_prev: equ h'1c' drum_number: equ h'21' dac_out: equ h'28' ; CHANNEL: equ h'29' ; button: equ 1 ;Pin on port B connected to pushbutton triggers0d: equ 0x2a triggers1d: equ 0x2b ; GPR LOCATIONS 0x30 to 0x3F (+few more) ARE RESERVED FOR DRUM TO NOTE VALUE MAPPING total 21 ; these locations are also used to load in sysex bytes before storage to e2prom ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; MACRO DEFINITIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mpx_rimshot macro ;set up multiplexers to set rimshot velocity movlw b'01111000' ; ;bsf PORTB,mpx0a0 ; ;bsf PORTB,mpx0a1 ; ;bsf PORTB,mpx0a2 ; ;bsf PORTB,mpx1a2 ; endm ; mpx_handclap macro ;set up multiplexers to set handclap velocity movlw b'00011000' ; ;bsf PORTB,mpx0a2 ; ;bsf PORTB,mpx1a2 ; endm ; mpx_bassdrum macro ;bassdrum movlw b'00100110' ; ;bsf PORTB,mpx0a1 ;y2 and y3 ;bsf PORTB,mpx1a0 ; ;bsf PORTB,mpx1a1 ; endm ; mpx_snaredrum macro ;snaredrum movlw b'00100000' ; ;bsf PORTB,mpx0a1 ;y2 and y0 endm ; mpx_lowtom macro ;lowtom movlw b'00100010' ; ;bsf PORTB,mpx0a1 ;y2 and y1 ;bsf PORTB,mpx1a0 ; endm ; mpx_midtom macro ;midtom movlw b'00100100' ; ;bsf PORTB,mpx0a1 ;y2 and y2 ;bsf PORTB,mpx1a1 ; endm ; mpx_hightom macro ;hightom movlw b'00001000' ; ;bsf PORTB,mpx1a2 ; endm ; mpx_hihat macro ;hihat movlw b'00101010' ; ;bsf PORTB,mpx0a1 ; ;bsf PORTB,mpx1a0 ; ;bsf PORTB,mpx1a2 ; endm ; mpx_crash macro ;crash movlw b'00101100' ; ;bsf PORTB,mpx0a1 ; ;bsf PORTB,mpx1a1 ; ;bsf PORTB,mpx1a2 ; endm ; mpx_ride macro ;ride movlw b'00101110' ; ;bsf PORTB,mpx0a1 ; ;bsf PORTB,mpx1a0 ; ;bsf PORTB,mpx1a1 ; ;bsf PORTB,mpx1a2 ; endm ; org h'0000' goto begin org h'0004' ;interrupt vector. goto midi_rx org h'0005' ; default_lookup addwf PCL,F ; retlw .35 ;bassdrum retlw .36 retlw .37 ;rimshot retlw .38 ;snaredrum retlw .40 retlw .39 ;handclap retlw .39 retlw .41 ;lowtom retlw .43 retlw .42 ;closedhat retlw .44 retlw .45 ;midtom retlw .47 retlw .46 ;openhat retlw .46 retlw .48 ;hitom retlw .50 retlw .49 ;crash retlw .49 retlw .51 ;ride retlw .51 retlw 0 ;sustain = 0 retlw 0xff ;eolut main bcf INTCON,1 ;Reset RB0 flag bsf INTCON,7 ;Global interrupt enable mainloop btfsc INTCON,2 goto refresh ;Call routine refresh when tmr0 interrupt flag is high. Set to occur every 1.024mS. btfsc clk_tmr,7 clrf clk_tmr movf clk_tmr,f btfsc STATUS,Z bsf PORTB,7 goto mainloop ; refresh ;*** Refresh Triggers *** update_clk ;*** Refresh Sync Clock pulse according to clk_tmr *** bcf PORTA,latch0 ;ensure 74174(0) clock line is low btfsc flags,hhsel ; bsf triggers0,2 ; btfsc flags,sync_runstop; bsf triggers0,3 ; bsf PORTA,mpx_en ;Multiplexers MUST be inhibited before messing with PortB at all! movlw 0x80 ; andwf PORTB,F ; movf triggers0,W ; iorwf triggers0d,W ; iorwf PORTB,F ; bsf PORTA,latch0 ; bcf PORTA,latch1 ;ensure 74174(1) clock line is low movlw 0x80 ; andwf PORTB,F ; movf triggers1,W ; iorwf triggers1d,W ; iorwf PORTB,F ; bsf PORTA,latch1 ; movf triggers0,W ;Okay for this bit to be interrupted I think, since movwf triggers0d ;it does not cause drum to expire early. movf triggers1,W ; movwf triggers1d ; bcf INTCON,GIE ;Disable interrupts clrf triggers0 ; Disabling interrupts for this bit should prevent drums expiring too soon. clrf triggers1 ; bcf INTCON,2 ; decf clk_tmr,f ; bsf INTCON,GIE ;Enable interrupts update_led ; movf led_tmr,f ; btfss STATUS,Z ; decf led_tmr,f ; btfsc STATUS,Z ; bsf PORTA,4 ; goto mainloop ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SERVICE RB0 INTERRUPT - BITBASH RB0 TO GET MIDI BYTE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ;Asynch. MIDI data rate is 32 instruction cycles with 4MHz xtal. ;First bit needs to be polled 48 cycles after RB0 interrupt, ;Then consecutive bits are polled every 32 cycles. ;Byte is received LSB first. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; midi_rx: ; ;originally 5 nops. ;with 9 - vel problem even worse nop nop nop nop ; *** DETERMINE A GOOD NUMBER OF NOP'S TO GO HERE *** nop nop nop nop nop nop nop movf led_tmr,W movwf led_tmr_prev movlw h'ff' ;Reset MIDI Activity LED timer movwf led_tmr ; movlw 0x30 ; 9th cycle movwf FSR ; movlw b'10000000' ; movwf BYTE ;[12nd cycle since entry] loop: ;MIDI BIT POLL LOOP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MUST BE 22 CYCLES BETWEEN THESE POINTS donkeywork: ;'donkeywork' performs some misc. number crunching during the MIDI ;bitbash routine - specifically, it is placed within the MIDI byte ;polling loop. The routine must be 22 cycles long to maintain a delay ;of exactly 32us between polling. btfss BYTE,0 ;Decide what to do. If newbyte LSB is 1 then this is the 8th pass goto find_drum_number ;do drum_number mapping ;;;;; VERY MISC. STUFF! - PASS 8 ONLY ;;;;;; clrf dac_out btfsc PORTB,7 ;Protect the sync clock state on RB7 bsf dac_out,7 ; ; movf statusbyte,W ;Convert statusbyte to note-off message ; andlw 0xf0 ;if it's a note-on with velocity = 0. ; xorlw 0x90 ;Doing here saves time in handler routines. ; btfss STATUS,Z ;*** SIDE-EFFECT: KILLS NOTES ON KEY 0!!! *** ; goto cleverbit ; [8] cycles for this section ; decf BYTE,W ;If BYTE currently = 00000001, then it will likely be 0 in final pass. ;cleverbit: ; ; btfsc STATUS,Z ; ; bcf statusbyte,4 ; nop nop nop nop nop nop nop nop swapf drum_number,W ;Some further drum_number processing: drum_number needs to be addlw 0xd1 ;swapf'd, and have 0x2f subtracted from it. Then find_drum_number movwf drum_number ;is skipped. goto bitbash ; [5] cycles ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; find_drum_number ;DO THIS ON PASS 1 TO 7 movf INDF,W ; xorwf PITCH,W ; swapf FSR,W ; btfsc STATUS,Z ; movwf drum_number ; incf FSR,F ; movf INDF,W ; xorwf PITCH,W ; swapf FSR,W ; btfsc STATUS,Z ; movwf drum_number ; incf FSR,F ; movf INDF,W ; xorwf PITCH,W ; swapf FSR,W ; btfsc STATUS,Z ; movwf drum_number ; incf FSR,F ; bsf PORTA,mpx_en ; INHIBIT MULTIPLEXERS ;drum number needs to be initialised with some null value e.g. zero = no drum. or FF. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MUST BE 22 CYCLES BETWEEN THESE POINTS bitbash: bcf STATUS,C ; rrf BYTE,F ; btfsc PORTB,0 ;Bit is tested here - 25th cycle. bsf BYTE,7 ; bcf PORTA,dac_en ;Clear DAC strobe line - do here since there was a spare nop sitting around movf BYTE,W ;Remember that the final BYTE value will be written to dac out. Hence, iorwf dac_out,F ;set dac_out to 10000000 within the loop on the final pass, and ;ior the BYTE data here. MSB of BYTE will be 0. LSB doesn't matter. btfss STATUS,C goto loop ;32 cycles inc. goto ;bcf INTCON,1 ;Reset RB0 flag. ;*** THINK ABOUT WHERE TO PUT THIS *** ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;* MIDI BYTE PROCESSING * ;* This routine called if flags,data_waiting = 1, as a result of last * ;* RB0 interrupt routine. * ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;TIME TAKEN TO EXECUTE MIDI PROCESSING starting at process_midi, inc return back to main: ;------------------------------------- ;Note-on Status Byte: 29 inc return ; ;Note-on pitch data: 26 total. ; ;Note-on velocity data: about 33 total. ; ; process_midi: ; ;********************* Status Byte Check ****************************** ;* * ;************************************************************************ check_for_status: ;Check for a new status byte (MSB of BYTE = 1) btfss BYTE,7 ; goto chk_running_status ;moving swiftly on if not movlw h'f8' ;This bit checks for system realtime message (bits f8 are high) subwf BYTE,W ; btfsc STATUS,C ; goto sys_realtime ; bcf PORTA,4 ;Turn on MIDI Activity LED ** But what if running status? ** movf BYTE,W ; movwf statusbyte ; clrf currentbytenum ;must reset currentbytenum to 0 since this indicates last byte received ;was a status byte. ;********************* Extract channel number ************************* movlw B'00001111' ;Extract channel number from Status Byte. andwf statusbyte,W ; xorwf CHANNEL,W ;Compare and clear/set chan_match accordingly bsf flags,chan_match ; btfsc STATUS,Z ; goto lookup ; bcf flags,chan_match ; goto main ; ;********************* Running Status Handler ************************** ;* If data byte received when status byte was expected (currentbytenum * ;* =0) then byte is running status. If true, simply increase * ;* currentbytenum to 1 and continue as normal using previous known * ;* status byte. In unlikely event of there being no status byte ever * ;* received (e.g. shortly after powerup) then ignore and exit. * ;************************************************************************ chk_running_status: ;Check for a data byte when a status byte is expected. movf currentbytenum,F ;See if currentbytenum = 0 (if it is, a status byte is expected) btfss STATUS,Z ; goto chk_channel_match ; incf currentbytenum,F ;Otherwise increase currentbytenum to one, therefore the system uses the last status byte. chk_channel_match: ; btfss flags,chan_match ; goto main ; ;********************* Message type lookup ************************* ;* Using computed-goto (lookup table) branch to appropriate handler * ;* depending on the message type. * ;************************************************************************ ;*** IDEA: IF VERY FEW MESSAGE TYPES ARE ACTUALLY SUPPORTED, IT MIGHT JUST BE FASTER TO USE ;SEVERAL XORWF / BTFSX ETC ETC. *** - extra speed just needed for note-on messages (?) lookup: ;This routine takes 6 cycles for note on / note off etc. swapf statusbyte,W ; andlw 0x0f ; addlw 0xf8 ; ;Branch to appropriate message handling addwf PCL,F ;routine according to the type of message. goto handlenoteoff ;1000 note off ; goto handlenoteon ;1001 note on ; goto main;return ;1010 poly key pressure ; goto main;return ;1011 control change ; goto main;return ;1100 program change ; goto main;return ;1101 overall key pressure ; goto main;return ;1110 pitch wheel ; goto main;return ;1111 system message ; *won't actually happen* handlenoteoff: ;[force appropriate velocity to zero] incf currentbytenum,F movf currentbytenum,W ;Lookup table branches according to the byte number (current_byte_no) addwf PCL,F ; nop goto main;return goto noteoff_pitch ; goto noteoff_velocity ; noteoff_pitch ; movf BYTE,W ; movwf PITCH ; movlw 0xf2 movwf drum_number goto main;return ;back to main noteoff_velocity ; clrf currentbytenum ; btfsc sustain,6 ;Don't do velocity silencing if sustain is less than 64(dec) goto main;return ; noteoff_drum_lookup: ;There are 7*3=21 possible drums movf drum_number,W ; addwf PCL,F ; goto main;return ;drum_number is zero if no match is found in map goto main;return;goto noteoff_bassdrum ; goto main;return;goto noteoff_bassdrum ; goto main;return;goto noteoff_rimshot ; goto main;return;goto noteoff_snaredrum ; goto main;return;goto noteoff_snaredrum ; goto main;return;goto noteoff_handclap ; goto main;return;goto noteoff_handclap ; goto main;return;goto noteoff_lowtom ; goto main;return;goto noteoff_lowtom ; goto noteoff_closedhat ; goto noteoff_closedhat ; goto main;return;goto noteoff_midtom ; goto main;return;goto noteoff_midtom ; goto noteoff_openhat ; goto noteoff_openhat ; goto main;return;goto noteoff_hightom ; goto main;return;goto noteoff_hightom ; goto noteoff_crashcymbal; goto noteoff_crashcymbal; goto noteoff_ridecymbal ; goto noteoff_ridecymbal ; handlenoteon: ;**Process Note-On Messages** incf currentbytenum,F ; movf currentbytenum,W ;Lookup table branches according to the byte number (current_byte_no) addwf PCL,F ; nop goto main ; goto noteon_pitch ; goto noteon_velocity ; noteon_pitch: ; movf BYTE,W ; movwf PITCH ;Correct drum_number will be magically present in drum_number on next pass!! movlw 0xf2 ;will be swapped round to 2f, then 2f subtracted movwf drum_number ; goto main ;back to main noteon_velocity: ; movf BYTE,F ; *** ADDED 8.12.02 *** btfsc STATUS,Z ; goto noteoff_velocity ; clrf currentbytenum ; drum_lookup: ;There are 7*3=21 possible drums movf drum_number,W ; addwf PCL,F ; goto main;return ;drum_number is zero if no match is found in map goto do_bassdrum ; goto do_bassdrum ; goto do_rimshot ; goto do_snaredrum ; goto do_snaredrum ; goto do_handclap ; goto do_handclap ; goto do_lowtom ; goto do_lowtom ; goto do_closedhat ; goto do_closedhat ; goto do_midtom ; goto do_midtom ; goto do_openhat ; goto do_openhat ; goto do_hightom ; goto do_hightom ; goto do_crashcymbal ; goto do_crashcymbal ; goto do_ridecymbal ; goto do_ridecymbal ; do_bassdrum: ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_bassdrum ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_bassdrum ; goto main ; do_snaredrum: ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_snaredrum ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_snaredrum ; goto main;eturn ; do_lowtom: ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_lowtom ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_lowtom ; goto main;return ; do_midtom: ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_midtom ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_midtom ; goto main;return ; do_hightom: ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_hightom ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_hightom ; goto main;return ; do_rimshot: ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_rimshot ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_rimshot ; goto main;return ; do_handclap: ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_handclap ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_handclap ; goto main;return ; do_closedhat: ; sel_closedhat ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_hihat ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_hihat ; goto main;return ; do_openhat: ; sel_openhat movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_hihat ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_hihat ; goto main;return ; do_crashcymbal: ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_crash ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_crash ; goto main;return ; do_ridecymbal: ; movf dac_out,W ;dac_out already contains correct setting for RB7. movwf PORTB ;so put it onto PORTB bsf PORTA,dac_en ;and load onto DAC. DAC is now ready. movlw 0x80 ;Now clear RB1 to RB6, preserving state of RB7 andwf PORTB,F ; mpx_ride ;this macro moves a value into W iorwf PORTB,F ; bcf PORTA,mpx_en ;Enable multiplexers trig_ride ; goto main;return ; noteoff_closedhat: ; movlw 0x80 ; andwf PORTB,F ; bsf PORTA,dac_en ; mpx_hihat ; iorwf PORTB,F ; bcf PORTA,mpx_en ; goto main;return ; noteoff_openhat: ; movlw 0x80 ; andwf PORTB,F ; bsf PORTA,dac_en ; mpx_hihat ; iorwf PORTB,F ; bcf PORTA,mpx_en ; goto main;return ; noteoff_crashcymbal: ; movlw 0x80 ; andwf PORTB,F ; bsf PORTA,dac_en ; mpx_crash ; iorwf PORTB,F ; bcf PORTA,mpx_en ; goto main;return ; noteoff_ridecymbal: ; movlw 0x80 ; andwf PORTB,F ; bsf PORTA,dac_en ; mpx_ride ; iorwf PORTB,F ; bcf PORTA,mpx_en ; goto main;return ; sys_realtime: ;***handle realtime messages for midi>sync conversion*** movf led_tmr_prev,W ;Don't want the MIDI activity LED to respond to realtime messages, movwf led_tmr ;so restore original led_tmr value check_start ;See if realtime message is 'start' movlw h'FA' ;Compare with 0xFA xorwf BYTE,W ; btfss STATUS,Z ; goto check_stop ; bcf flags,sync_runstop ;External sync run/stop line will go high goto main;return ;Back to main check_stop ;See if realtime message is 'stop' movlw h'FC' ;Compare with 0xFC xorwf BYTE,W ; btfss STATUS,Z ; goto check_clock ; bsf flags,sync_runstop ;External sync run/stop line will go low goto main;return ;Back to main check_clock ;See if realtime message is 'clock' movlw h'F8' ;Compare with 0xF8 xorwf BYTE,W ; btfss STATUS,Z ; goto main;return ;Back to main bcf PORTB,7 ;Set the clock line high, movlw 0x05;06 ;and reset clock pulse timer. movwf clk_tmr ; goto main;return ;Back to main begin: clrf triggers0 ; clrf triggers1 ; clrf triggers0d ; clrf triggers1d ; clrf flags ; clrf currentbytenum ; clrf clk_tmr ; bsf flags,sync_runstop ; clrf PORTA ; clrf PORTB ; PAGE1 ; movlw b'00000001' ;All of port B = outputs except for RB0 is MIDI input. movwf TRISB ; clrf TRISA ;All of port A = outputs. PAGE0 ; bsf PORTA,latch0 ; movlw 0xff ; bsf PORTA,latch1 ; movwf led_tmr ;led_tmr = ff so that MIDI LED lights briefly upon startup bcf PORTA,4 ; PAGE1 ; movlw b'00000011' ;All of port B = outputs except for RB0 is MIDI input, RB1 for button input movwf TRISB ; movlw b'00000001' ;Enable PORTB pullups, set prescaler to 1:4, assign prescaler to TMR0, movwf OPTIONREG ;and RB0 interrupt upon high-to-low edge. bcf EECON1,EEIF ;Clear eeprom write interrupt flag PAGE0 ; bcf INTCON,2 ;clear T0IF. (must be done in software-see datasheet, INTCON register) bsf INTCON,4 ;enable RB0 interrupt bcf INTCON,1 ;Reset RB0 flag bsf PORTA,mpx_en ;inhibit demultiplexers bcf INTCON,7 ;global interrupt disabled for now bsf PORTA,dac_en ;DAC write disabled (latching) movlw b'10100000' ; movwf statusbyte ; btfss PORTB,1 ;See if the learn/sysex button is being pressed on startup. If so, goto sysex ;go to sysex routines. done_sysex clrf PORTB ; PAGE1 ; movlw b'00000001' ;All of port B = outputs except for RB0 is MIDI input. movwf TRISB ; PAGE0 ; goto restore ; sysex btfsc led_tmr,7 ;Flash LED at high rate to indicate waiting for data bcf PORTA,4 ; btfss led_tmr,7 ; bsf PORTA,4 ; btfsc INTCON,1 ; goto getstat ; btfss INTCON,2 ; goto sysex ; bcf INTCON,2 ; decf led_tmr,F ; goto sysex ; getstat call rx ;Go and get the first byte, which should be a status byte ;Now want to determine what was received xorlw 0xf0 ;compare with F0 - start sysex indicator btfsc STATUS,Z ; goto get_identifier ;jump to receive further sysex data bytes if true andlw 0xf0 ; xorlw b'01100000' ; btfsc STATUS,Z ;If it's a note-on message, jump to learn_channel goto learn_channel ; goto sysex ;Otherwise go back, carry on flashing LED get_identifier ;look for 00 90 90 call rx ; xorlw 0x00 ; btfss STATUS,Z ; goto sysex ; call rx ; xorlw 0x90 ; btfss STATUS,Z ; goto sysex ; call rx ; xorlw 0x90 ; btfss STATUS,Z ; goto sysex ; get_sysex ;come here if F0 00 90 90 sequence recieved bcf PORTA,4 ;Keep the active LED illuminated movlw 0x30 ; movwf FSR ; sysex_wait ;Need to implement a timeout feature?? call rx ; movlw h'f8' ;Trap and ignore realtime clock messages. subwf BYTE,W ;FA and FC (start /stop) messages are not caught; these btfsc STATUS,C ;will cause the sysex process to abort and restart goto sysex_wait ; btfsc BYTE,7 ;check that it's not a statusbyte goto sysex ;restart if error movf BYTE,W ; movwf INDF ; incf FSR,F ; movf FSR,W ; xorlw 0x46 ;Need to receive 22(dec) data bytes. 22dec + 30h = 46h --> exit when reached this value btfss STATUS,Z ; goto sysex_wait ; store movlw 0x30 ;///// STORE DATA HELD IN 0X30 UPWARDS TO EEPROM ///// movwf FSR ; clrf EEADR ;Start at location 00h clrf chksum ; stloop movf INDF,W ; movwf EEDATA ; addwf chksum,F ; call eeprom_wr ; incf EEADR,F ; incf FSR,F ; movf FSR,W ; xorlw 0x46 ;Need to store 22(dec) data bytes. 22dec + 30h = 46h --> exit when reached this value btfss STATUS,Z ; goto stloop ; movf chksum,W ;Store checksum into 16h movwf EEDATA ; call eeprom_wr ; comf chksum,W ;Store comf'd checksum into 17h movwf EEDATA ; incf EEADR,F ; call eeprom_wr ; call led_ok ; goto done_sysex ; learn_channel ; movlw 0x18 ; movwf EEADR ; movlw 0x0f ; andwf BYTE,W ; movwf EEDATA ; call eeprom_wr ; call led_ok ; goto done_sysex ; led_ok ; movlw 0x04 ; movwf TEMP0 ; clrf led_tmr ; led_ok_loop ; movf led_tmr,W ; subwf TMR0,W ; btfsc TEMP0,2 ; bsf STATUS,C ; btfss STATUS,C ; bcf PORTA,4 ; btfsc STATUS,C ; bsf PORTA,4 ; btfss INTCON,2 ; goto led_ok_loop ; bcf INTCON,2 ; decf led_tmr,F ; btfsc STATUS,Z ; decf TEMP0,F ; btfss STATUS,Z ; goto led_ok_loop ; bsf PORTA,4 ; return rx btfss INTCON,1 ; goto rx ;[2] if interrupt happened here or otherwise [5 or 6] movlw 0x03 ; \ movwf TEMP1 ; 10 cycles delay1 decfsz TEMP1,f ; goto delay1 ; / movlw b'10000000' ; movwf BYTE ;[14th cycle since RB0 edge... approx] loop1 movlw 0x08 ; \ delay is initial 2, then this value-1 x 3, then + 2 movwf TEMP1 ; i.e. value*3 + 1 delay2 decfsz TEMP1,f ; need 25 goto delay2 ; / bcf STATUS,C ; rrf BYTE,F ; btfsc PORTB,0 ;Bit is tested here - 42nd cycle. bsf BYTE,7 ; btfss STATUS,C ; goto loop1 ;32 cycles inc. goto bcf INTCON,1 ; movf BYTE,W ; return ; eeprom_wr ;/// write data to EEPROM /// PAGE1 ; bsf EECON1,WREN ; movlw 0x55 ;Special EECON2 sequence movwf EECON2 ; movlw 0xaa ; movwf EECON2 ; bsf EECON1,WR ; eifwait btfss EECON1,EEIF ; goto eifwait ; bcf EECON1,EEIF ; bcf EECON1,WREN ; PAGE0 ; return ; eeprom_rd ;/// read data from EEPROM /// PAGE1 ; bsf EECON1,RD ; PAGE0 ; return ; restore ; ;/// drum map information is stored in 21 eeprom locations, starting at eeprom 00h /// clrf EEADR ; movlw 0x30 ; movwf FSR ; clrf chksum ; restore_loop call eeprom_rd ; movf EEDATA,W ; addwf chksum,F ; movwf INDF ; incf EEADR,F ; incf FSR,F ; movf FSR,W ; xorlw 0x45 ;doing locations 30 to 44 h (21 locations), hence if FSR=45h then exit. btfss STATUS,Z ; goto restore_loop ; call eeprom_rd ; movf EEDATA,W ; movwf sustain ; addwf chksum,F ; movf chksum,W ; incf EEADR,F ; call eeprom_rd ; xorwf EEDATA,W ; btfss STATUS,Z ; goto default ; comf chksum,W ; incf EEADR,F ; call eeprom_rd ; xorwf EEDATA,W ; btfss STATUS,Z ; goto default ; incf EEADR,F ; call eeprom_rd ; movf EEDATA,W ;Read channel from this location... movwf CHANNEL ;..and store to CHANNEL. goto main ; default ; movlw 0x18 ; movwf EEADR ; movlw 0x09 ;09h = channel 10 movwf EEDATA ; call eeprom_wr ; movlw 0x30 ; movwf FSR ; default_loop ; movlw 0x30 ; subwf FSR,W ; call default_lookup ; movwf INDF ; xorlw 0xff ; btfsc STATUS,Z ; goto store ; incf FSR,F ; goto default_loop ; end ;That's all folks.