;-----------------------------------------------------------------------------------------------; ; Program: Arpeggiate It ; ; Developed by David J. Brown ; ; Copyright (c) April 13, 2004 David J. Brown ; ; Email: davebr@earthlink.net ; ; Web site: http://modularsynthesis.com ; ;-----------------------------------------------------------------------------------------------; ; LICENSE AGREEMENT: ; ; This program is free software. You can redistribute it and/or modify it. ; ; ; ; This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, ; ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ;-----------------------------------------------------------------------------------------------; ; ; Description: ; ; This program will begin an arpeggiator sequence ; on the rising edge of gate and will continue the ; sequence until the gate goes low or cv in changes ; to a new value (trigger in is not used as the PSIM ; is not fast enough to catch a narrow ~sub-1 mS ; pulse). Gate in is passed through to gate out ; and a trigger is generated for each note. In-1 ; determines the note tempo and in-4 selects the ; arpeggiator sequence from five preset patterns. ; A high on Aux disables the arpeggiator. The notes ; are also sent to MIDI out. ; ; Sequence number 5 is user-definable. The program ; waits for the start or stop button to be pressed. ; If the start button is pressed the program uses ; the five preset patterns. If the stop button is ; pressed the program enters a sequence number 5 ; definition mode and is indicated by a steady on of ; the run light. The first note is defined as the ; root of the sequence. Each subsequent note is added ; to the sequence. Definition mode is ended by pressing ; the start button or reaching the maximum note count ; in the buffer. ; ; If the stop switch is pressed when no keys are down then ; the program begins a MIDI program change select mode. ; Each press of the start switch increments program change ; by 10 and each press of the stop switch incrments program ; change by 1. A440 is output with the selected program ; change. This mode continues until a key is detected ; (e.g. gate high). ; ; inputs: ; start = start program ; stop = enter definition mode ; in-1 = note tempo ; in-2 = gate in ; in-3 = cv in (sampled on rising edge of gate) ; in-4 = arpeggiator sequence select ; aux = bypass arpeggiator (disable) ; midi-in = n/a ; ; outputs: ; out-1 = 5 mS trigger out (0, 5 volts) ; out-2 = gate out (0, 5 volts) ; out-3 = cv out ; out-4 = cv out shifted up one octave ; midi-out= note shifted up two octaves ; speakjet= ; ; run led = toggles @ 0.5 S (steady on indicates definition mode) ; stop led = trigger hi (gate hi in definition mode) ; ; written by David J. Brown ; revision: 0.5S2 ; April 29.2010 ; history: 0.5S2 changed serial for Studio 2 ; 0.5X changed shiftout for Studio, removed serial speakjet ; 0.5 updated hex2ascii to eliminiate push/pop ; 0.4 converted to herout, changed ' to ;, added ; display messages throughout, moved arpeggiation ; sequence selection from start_seq to wait_gate ; for real-time display when gate is false ; 0.3 added display of program name ; 0.2 changed sequence termination from 255 ; to 128 (to allow -1 in sequence ; 0.11 added MIDI program change selection. ; 0.1 added MIDI out capability ; 0.0 initial code for arpeggiate-it program ; date: June 2, 2004 ; ;########################################### ; ; ### CONDITIONAL COMPILING OPTION ### ; set MIDI INTERRUPT mode ; ; comment out next line for polled mode midi_int_en con 1 ;define to enable midi-in interrupts ; comment to set polled midi-in mode ; ;########################################### ; ; ### CONDITIONAL COMPILING OPTION ### ; set TIMER INTERRUPT mode ; note: Run led will blink if timer_int_en defined ; ; comment out next line to disable timer interrupts timer_int_en con 1 ;define to enable timer interrupts ; comment to disable timer interrupts ; ;########################################### ; ; ### CONDITIONAL COMPILING OPTION ### ; set INPUT INTERRUPT mode ; note: Requires timer_int_en to be defined ; ; comment out next line to disable input interrupts ; input_int_en con 1 ;define to enable input interrupt mode ; comment to set polled input mode ; ;########################################### ; ;djb template written by David J. Brown ;based on code from Brice Hornback and Grant Richter ;revision: 0.41 ;history: 0.41 upper case conversion to lower, variables and ; labels renamed ; 0.4 added averaged input input driver (polled or ; interrupt mode), display_led driver (for debug), ; speakjet driver, renamed program to djb template ; 0.3 added program header information, modified ; pin initialization ; 0.2 initialize aux output low and speakjet pins ; 0.1 added DAC & led initialization, 1 mS timestamp, ; stop led blinks when midi data received, run ; led toggles @ 0.5 S for health indication ; 0.0 initial release of midi input & output program ;date: April 13, 2004 ;******************************************* ; module: PSIM-1 REV1b ; processor Type: Basic Micro - Basic Atom Pro24M ;******************************************* ; ; Basic Micro Atom Pro-24M Configuration ; ; P0 - in-1 ; P1 - in-2 ; P2 - in-3 ; P3 - in-4 ; P4 - start button (momentary normally open switch) ; P5 - stop button (momentary normally open switch) ; P6 - j3 pin 2 (speakjet buffer half full) ; P7 - j3 pin 1 (serial data to speakjet) ; P8 - aux digital I/O ; P9 - stop led ; P10 - run led ; P11 - load_dacs ; P12 - ser_data ; P13 - clock ; P14 - j5 pin 1 (midi-in) ; P15 - j5 pin 2 (midi-out) ; ;******************************************* ; ;pin constants pin_j1 con 0 ;in-1 pin pin_j2 con 1 ;in-2 pin pin_j3 con 2 ;in-3 pin pin_j4 con 3 ;in-4 pin start_j var in4 ;start jack and switch stop_j var in5 ;stop jack and switch aux_j con 8 ;aux jack (output mode) aux_in var in8 ;aux jack (input mode) stop_led con 9 ;stop led (red) stop_out var out9 ;alias for output run_led con 10 ;run led (green) run_out var out10 ;alias for output load_dacs con 11 ;load dac pin ser_data con 12 ;serial data to dac pin clock con 13 ;dac clock pin midi_sdata con 15 ;midi data serial output ;i/o variable declarations in_j1 var word ;in-1 value: get_inputs, get_inputs_avg, avg1 in_j2 var word ;in-2 value: get_inputs, get_inputs_avg, avc2 in_j3 var word ;in-3 value: get_inputs, get_inputs_avg, avg3 in_j4 var word ;in-4 value: get_inputs, get_inputs_avg, avg4 out_j1 var word ;out-1 value: load_outputs out_j2 var word ;out-2 value: load_outputs out_j3 var word ;out-3 value: load_outputs out_j4 var word ;out-4 value: load_outputs ;midi variable declarations note_off con $80 ;midi note-off command: send_note_on, send_note_off note_on con $90 ;midi note-on command: send_note_on, send_note_off pgm_chg con $c0 ;midi program change command:send_prog_change midi_chan var nib ;midi channel (0 - 15): send_note_on, send_note_off midi_note var byte ;midi note value: send_note_on, send_note_off midi_vel var byte ;midi velocity value: send_note_on, send_note_off midi_pgm var byte ;midi program change ;midi driver variable declarations midi_data var byte ;variable: check_midi, send_midi, get_rcx_bfr, send_note_on, send_note_off midi_data_isr var byte ;variable: midi_in_isr tx_status var byte ;variable: send_midi rcx_status var byte ;variable: check_midi, send_midi rcx_ptr_strt var byte ;received data start pointer (first data in buffer): get_rcx_bfr rcx_ptr_end var byte ;received data end pointer (last data in buffer+1): check_midi, midi_in_isr rcx_bfr_num var byte ;number of bytes in data buffer: check_midi, get_rcx_bfr, midi_in_isr rcx_data_flg var bit ;received data flag (1=data, 0=no data): get_rcx_bfr rcx_bfr_len con 8 ;midi-in buffer length: rcx_bfr rcx_bfr var byte(rcx_bfr_len) ;midi-in data buffer: check_midi, get_rcx_bfr, midi_in_isr ;timer variable declarations #ifdef timer_int_en time_count var long ;timer count value: tm_isr #endif ;input variable declarations in_jx_ptr var byte ;in-x buffer pointer: get_inputs_avg in_j1_tmp var long ;in-1 temporary variable: get_inputs_avg in_j2_tmp var long ;in-2 temporary variable: get_inputs_avg in_j3_tmp var long ;in-3 temporary variable: get_inputs_avg in_j4_tmp var long ;in-4 temporary variable: get_inputs_avg in_j1_bfr var long(2) ;in-1 last 4 samples buffer: get_inputs_avg in_j2_bfr var long(2) ;in-2 last 4 samples buffer: get_inputs_avg in_j3_bfr var long(2) ;in-3 last 4 samples buffer: get_inputs_avg in_j4_bfr var long(2) ;in-4 last 4 samples buffer: get_inputs_avg ;display declarations num_data var byte ;data for ascii conversion: hex2ascii huns var byte ;hundreds digit: hex2ascii tens var byte ;tens digit: hex2ascii, display_in, display_out ones var byte ;ones digit: hex2ascii, display_in, display_out ;misc declarations tempb var byte ;temp byte led_data var byte ;variable: display_led fivevolts con 1920 ;5 volt output value ;arpeggiator variable declarations trig_end var long ;trigger off time value: wait_gate, next_seq note_end var long ;next note time value: wait_gate, next_seq root_note var word ;root note to arpeggiate: wait_gate, next_seq, check_cv note_out var bit ;flag: 1=output next note, 0=skip: wait_gate, next_seq, end_seq, tm_isr trig_out var bit ;flag: 1=set trigger low, 0=skip: check_gate, end_seq, tm_isr seq_off var word ;arpeggiator sequence offset: wait_gate, next_seq seq_ptr var word ;arpeggiator sequence counter: wait_gate, next_seq ;arpeggiator sequence declarations ;values are +/- offset in semitones from root note ;terminate sequence with 128 ;pad with extra byte if necessary to make the sequence length even len1 con 6 ;length of sequence 1 seq1 bytetable 7,12,10,7,0,128 ; len2 con 8 ;length of sequence 2 seq2 bytetable -8,-5,-3,2,0,-3,0,128 ; len3 con 6 ;length of sequence 3 seq3 bytetable 12,24,19,7,128,128 ; len4 con 6 ;length of sequence 4 seq4 bytetable -2,3,-5,7,0,128 ; len5 con 6 ;length of sequence 5 seq5 bytetable 8,5,3,0,128,128 ;offset declarations off1 con 0 ;offset to seq1 off2 con off1+len1 ;offset from seq1 to seq2 off3 con off2+len2 ;offset from seq1 to seq3 off4 con off3+len3 ;offset from seq1 to seq4 off5 con off4+len4 ;offset from seq1 to seq5 ;ram sequence buffer ; arp_buf_len must be equal to or greater than len1+len2+len3+len4+len5 arp_buf_len con 100 ;buffer length arp_buf var byte(arp_buf_len) ;arpeggiator buffer ; ;******************************************* ; ;initialize pins ;note: setting midi-out, i2c_clock, and i2c_data to outputs can send glitches so initialize as inputs dirs=%0011111000000000 ;configure pin direction (1=output, 0=input) ;inputs: midi-in, aux, start, stop, in-4, in-3, in-2, in-1 ;outputs: load_dacs, ser_data, clock, stop led, run led, i2c clock & data low run_led ;set run led off low stop_led ;set stop led off high load_dacs ;set dac load-0 high low ser_data ;set dac data low low clock ;set dac clock low let out_j1=0 ;set out-1 low let out_j2=0 ;set out-2 low let out_j3=0 ;set out-3 low let out_j4=0 ;set out-4 low gosub load_outputs ; #ifndef midi_int_en ;initialize midi hardware ;these next 6 commands need to be in this order let scr3=%00000000 ;reset Serial Control Register let smr=%00000000 ;set Serial Mode Register ; asynchronous ; 8 bits ; parity disabled ; even parity (disabled) ; 1 stop bit ; multiprocessor mode disabled ; brr clock source direct let brr=15 ;set Bit Rate Register for 31500 baud pauseus 100 ;let brr settle for 50 uS let scr3=%00110000 ;set Serial Control Register ; transmit or receive interrupts disabled ; transmit and receive enabled ; multiprocessor interrupt disabled ; transmit end interrupt disabled ; internal baud rate generator let pmr1=%00001110 ;set Port Mode Register ; P17 general I/O port ; P16 general I/O port ; P15 general I/O port ; P14 general I/O port ; txd output ; P10 general I/O port #endif ; #ifdef timer_int_en ;initialize timer hardware for 10 mS interrupts let tmrw=%10001000 ;set Timer Mode Register to enable count let tcrw=%10110000 ;set Timer Control Register ; TCNT cleared by compare match ; /8 internal clock let tierw=%01110000 ;set Timer Interrupt register to disable overflow interrupt let tsrw=%01110000 ;set Timer Status Register to default let tior0=%10001000 ;set Timer I/O Regiseter 0 to default let tior1=%10001000 ;set Timer I/O Register 1 to default let gra=2000 ;set General Register A ; 16 MHz clock /8 = 2 MHz ; 2000 counts = 1 mS interrupt #endif ; ;initialize misc variables let rcx_ptr_strt=0 ;set receive start pointer let rcx_ptr_end=0 ;set receive end pointer let rcx_bfr_num=0 ;set receive buffer to empty let midi_chan=0 ;set midi channel to 0 let midi_note=128 ;set midi note to invalid let midi_vel=$40 ;set default velocity let led_data=0 ;set led data to 0 let in_jx_ptr=0 ;set in-x pointer to 0 let in_j1_bfr(0)=0 ;set in-1 buffer data to 0 let in_j1_bfr(1)=0 let in_j2_bfr(0)=0 ;set in-2 buffer data to 0 let in_j2_bfr(1)=0 let in_j3_bfr(0)=0 ;set in-3 buffer data to 0 let in_j3_bfr(1)=0 let in_j4_bfr(0)=0 ;set in-4 buffer data to 0 let in_j4_bfr(1)=0 let trig_end=0 ;set trigger flag false let note_end=0 ;set note flag false let trig_out=0 ;reset trigger time value let note_out=0 ;reset note time value ; ;enable midi interrupts #ifdef midi_int_en sethserial1 h31200,h8databits,hnoparity,h1stopbits pause 300 ;assume display initializaton may take longer hserout [$f0,$7d,$0a,$18,"ArpeggIt",$f7] ;clear, overwrite, name #endif ; let midi_data=$b0+midi_chan ;send all notes off gosub send_midi let midi_data=$7b gosub send_midi let midi_data=0 gosub send_midi ; ;initialize ram-based sequence buffer init_seq: for seq_ptr=0 to (len1+len2+len3+len4+len5) let arp_buf(seq_ptr)=seq1(seq_ptr) ;copy sequences to ram next ; ;determine if create user sequence hserout [$f0,$7d,$0b,"Start? ",$f7] ;display start message check_stop: if stop_j=0 then check_start hserout [$f0,$7d,$0b,"Def Seq ",$f7] ;display user sequence message high run_led ;ram sequence create mode let seq_ptr=off5 get_first: adin pin_j2,in_j2 ;get gate if in_j2<256 then get_first ;wait for hi high stop_led ;indicate note adin pin_j3,in_j3 ;get cv let root_note=((in_j3+4)*2)/17 ;shift half semitone and quantize let out_j1=fivevolts ;echo trigger, gate, and cv let out_j2=fivevolts let out_j3=root_note*32 gosub load_outputs let midi_note=root_note+24 gosub send_note_on ;also send midi note pause 5 let out_j1=0 ;end trigger gosub load_outputs first_done: adin pin_j2,in_j2 ;get gate if in_j2>255 then first_done ;wait for low let out_j2=0 ;end gate gosub load_outputs gosub send_note_off low stop_led get_next: if start_j=1 then start ;see if sequence complete adin pin_j2,in_j2 ;get gate if in_j2<256 then get_next ;wait for hi high stop_led ;indicate note adin pin_j3,in_j3 ;get cv let in_j3=((in_j3+4)*2)/17 ;shift half semitone and quantize let out_j1=fivevolts ;echo trigger, gate, and cv let out_j2=fivevolts let out_j3=in_j3*32 gosub load_outputs let midi_note=in_j3+24 gosub send_note_on ;also send midi note pause 5 let out_j1=0 ;end trigger gosub load_outputs let in_j3=in_j3-root_note ;calculate note offset let arp_buf(seq_ptr)=in_j3 ;store in buffer let seq_ptr=seq_ptr+1 ;increment pointer let arp_buf(seq_ptr)=128 ;terminate sequence if seq_ptr=(arp_buf_len-1) then start ;check if buffer full next_done: adin pin_j2,in_j2 ;get gate if in_j2>255 then next_done ;wait for low low stop_led let out_j2=0 ;end trigger gosub load_outputs gosub send_note_off goto get_next ; check_start: if start_j=0 then check_stop start: ; ;enable timer interrupts #ifdef timer_int_en let time_count=0 ;set real time counter to 0 oninterrupt timerwint_imiea, tm_isr enable timerwint_imiea ;enable timer interrupt #endif ; goto wait_gate ; bypass: if trig_out=1 then gosub load_outputs ;end trigger let trig_out=0 low stop_led endif if aux_in=0 then start_seq ;check if sequence enabled adin pin_j2,in_j2 if in_j2<256 then end_seq ;end if gate has gone low adin pin_j3,in_j3 ;get cv let in_j3=((in_j3+4)*2)/17 ;shift half semitone and quantize if root_note=in_j3 then bypass ;end if cv is different pause 5 ;let cv settle some due to slight portamento end_seq: disable timerwint_imiea let trig_end=time_count ;reset trigger off let note_end=time_count ;reset note off delay let trig_out=0 ;reset trigger time value let note_out=0 ;reset note time value enable timerwint_imiea let out_j1=0 ;end trigger let out_j2=0 ;end gate gosub load_outputs gosub send_note_off let midi_note=128 low stop_led wait_gate: ;moved arp selection here for real-time display hserout [$f0,$7d,$0b,"ArpSeq "] ;display message & do not terminate sysex wait_gate1: if stop_j=1 then select_pc ;stop switch enters program change mode adin pin_j4,in_j4 ;determine offset to sequence if in_j4 > 410 then ;4+ volts hserout [$08,"5"] ;display 5 let seq_off=off5 elseif in_j4 > 308 ;3 to 4 volts hserout [$08,"4"] ;display 4 let seq_off=off4 elseif in_j4 > 204 ;2 to 3 volts hserout [$08,"3"] ;display 3 let seq_off=off3 elseif in_j4 > 102 ;1 to 2 volts hserout [$08,"2"] ;display 2 let seq_off=off2 else hserout [$08,"1"] ;display 1 let seq_off=off1 ;0 to 1 volt endif ;end of moved arp selection adin pin_j2,in_j2 ;get gate if in_j2<256 then wait_gate1 ;check for hi transition on gate hserout [$f7] ;terminate sysex adin pin_j3,in_j3 ;get cv let root_note=((in_j3+4)*2)/17 ;shift half semitone and quantize adin pin_j1,in_j1 ;get delay let in_j1=(in_j1^$03ff)+50 ;reverse scale and set minimum let out_j1=fivevolts ;set trigger to hi let out_j2=fivevolts ;set gate to hi let out_j3=root_note*32 ;set cv to current let out_j4=(root_note+12)*32 ;set out-4 up an octave high stop_led gosub load_outputs let midi_note=root_note+24 gosub send_note_on disable timerwint_imiea let trig_end=time_count+5 ;set trigger off in 5 mS let note_end=time_count+in_j1 ;set note off delay enable timerwint_imiea if aux_in=1 then bypass ;bypass if aux hi start_seq: ;arp selection was here let seq_ptr=seq_off ;initialize sequence pointer check_gate: if trig_out=1 then gosub load_outputs ;end trigger let trig_out=0 low stop_led endif adin pin_j2,in_j2 if in_j2<256 then end_seq ;check if gate has gone low check_cv: ;check if cv is still same as root note adin pin_j3,in_j3 ;get cv let in_j3=((in_j3+4)*2)/17 ;shift half semitone and quantize if root_note<>in_j3 then ;check if cv has changed pause 5 ;let cv settle some due to slight portamento goto end_seq ;end sequence endif if note_out=0 then check_gate ;check if time for next note in sequence next_seq: ;comment next line for bypass mode to not terminate current seqence if aux_in=1 then end_seq ;check if sequence is enabled let note_out=0 ;set flag false let out_j1=fivevolts ;set trigger to hi let out_j3=root_note+arp_buf(seq_ptr) ;calculate next note in sequence let out_j4=(out_j3+12)*32 ;set out-4 up an octave if midi_note<128 then gosub send_note_off endif let midi_note=out_j3+24 gosub send_note_on let out_j3=out_j3*32 ;scale out-3 for output adin pin_j1,in_j1 ;get delay let in_j1=(in_j1^$03ff)+50 ;reverse scale and set minimum disable timerwint_imiea let trig_end=time_count+5 ;set trigger off in 5 mS let note_end=time_count+in_j1 ;set note off delay enable timerwint_imiea let seq_ptr=seq_ptr+1 ;increment point to next note if arp_buf(seq_ptr)=128 then ;check if at end of sequence let seq_ptr=seq_off ;repeat sequence endif gosub load_outputs high stop_led goto check_gate ; ;select and set program change ;starts with program change 0 ;start increments by 10 and stop increments by 1 ;mode terminated on gate high select_pc: hserout [$f7] ;terminate sysex high stop_led select_pc1: pause 20 if stop_j=1 then select_pc1 ;wait for switch release let midi_pgm=0 ;start with program change 0 goto send_pc ; inc_10: adin pin_j2,in_j2 ;get gate if in_j2>256 then wait_gate ;terminate if gate high if start_j=0 then inc_1 ;check start let midi_pgm=midi_pgm+10 ;increment by 10 goto send_pc ; inc_1: if stop_j=0 then inc_10 ;check stop let midi_pgm=midi_pgm+1 ;increment by 1 send_pc: if midi_pgm>127 then let midi_pgm=0 ;wrap at maximum endif gosub send_prog_change let midi_note=$45 ;output A440 gosub send_note_on let num_data=midi_pgm ;get program change gosub hex2ascii ;convert to ascii hserout [$f0,$7d,$0b,"PrCh=",huns,tens,ones,$f7] ;display program change message pause 200 release: pause 20 ;wait for switch release if (stop_j=1) or (start_j=1) then release gosub send_note_off goto inc_10 ; ;******************************************* ; subroutines ;******************************************* ; #ifndef midi_int_en ;poll midi-in ;puts data into buffer and sets rcx_bfr_num to number of entries ;written by David J. Brown check_midi: let rcx_status=ssr if rcx_status&%01000000 then ;data has been received let midi_data=rdr ;read Receiver Data Register if midi_data<>$FE then ;ignore if active status ;put midi_data into receive buffer ;check to see if buffer full if rcx_bfr_num=rcx_bfr_len then ;buffer is full ;simply loose data else ;buffer has space let rcx_bfr(rcx_ptr_end)=midi_data let rcx_bfr_num=rcx_bfr_num+1 let rcx_ptr_end=rcx_ptr_end+1 if rcx_ptr_end=rcx_bfr_len then rcx_ptr_end=0 ;wrap pointer at max value endif endif endif else ;no data received ;check if error if rcx_status&%00111000 then ;error condition let rcx_status=rcx_status&%10000111 let ssr=rcx_status ;reset error bits endif endif return #endif ; ;gets midi_data from buffer ;rcx_data_flg=1 if successful, rcx_data_flg=0 if no data in buffer ;written by David J. Brown get_rcx_bfr: #ifdef midi_int_en disable sci3int_rdrf ;isr uses the same variables #endif ;check to see if buffer empty if rcx_bfr_num=0 then ;buffer is empty rcx_data_flg=0 ;set empty flag else ;buffer has data let midi_data=rcx_bfr(rcx_ptr_strt) let rcx_bfr_num=rcx_bfr_num-1 let rcx_ptr_strt=rcx_ptr_strt+1 if rcx_ptr_strt=rcx_bfr_len then let rcx_ptr_strt=0 ;wrap pointer at max value endif let rcx_data_flg=1 ;set data returned flag endif #ifdef midi_int_en enable sci3int_rdrf #endif return ; ;output 3 byte midi note-on command send_note_on: let midi_data=note_on+midi_chan gosub send_midi let midi_data=midi_note gosub send_midi let midi_data=midi_vel gosub send_midi return ; ;output 3 byte midi note-off command send_note_off: let midi_data=note_off+midi_chan gosub send_midi let midi_data=midi_note gosub send_midi let midi_data=midi_vel gosub send_midi return ; ;output 3 byte program change command send_prog_change: let midi_data=pgm_chg+midi_chan gosub send_midi let midi_data=midi_pgm gosub send_midi return ; ;send midi_data ;waits until transmitter ready if polled ;puts data in buffer if interrupt enabled ;written by David J. Brown send_midi: #ifndef midi_int_en ;enable midi-out on timer interrupts let tx_status=ssr ;poll midi-out if tx_status&%10000000 then ;check transmitter ready ;ready to transmit let tdr=midi_data ;send data to tdr return endif goto send_midi #else hserout [midi_data] ;put data in transmit buffer return #endif ; ;convert byte num_data to three ascii digits ;updates huns, tens, ones variables ;use to generate leading zeros for fixed column display ;written by David J. Brown hex2ascii: let tempb=num_data ;save data if num_data >99 then let huns=num_data/100 ;calculate hundreds let num_data=num_data-(huns*100) ;prepare for tens let huns=huns+$30 ;convert to ascii else let huns=$30 ;leading 0 in ascii endif if num_data >9 then let tens=num_data/10 ;calculate tens let num_data=num_data-(tens*10) ;prepare for ones let tens=tens+$30 ;convert to ascii else let tens=$30 ;leading 0 in ascii endif let ones=num_data+$30 ;convert to ascii let num_data=tempb ;restore data return ; #ifndef input_int_en ;sample in-1 to in-4 ;injx is input value (0 - 1023) ;150 uS execution time get_inputs: adin pin_j1,in_j1 adin pin_j2,in_j2 adin pin_j3,in_j3 adin pin_j4,in_j4 return ; ;sample and average in-1 to in-4 ;injx is input value averaged over last four samples (0 - 1023) ;injx_bfr(0) & injx_bfr(1) are last four word samples ;600 uS execution time ;written by David J. Brown get_inputs_avg: adin pin_j1, in_j1_bfr.word1(in_jx_ptr) ;get in-1 into buffer let in_j1_tmp=in_j1_bfr(0)+in_j1_bfr(1) ;add two high words together and low words together let in_j1=(in_j1_tmp.word1+in_j1_tmp.word0)/4 adin pin_j2, in_j2_bfr.word1(in_jx_ptr) ;get in-2 into buffer let in_j2_tmp=in_j2_bfr(0)+in_j2_bfr(1) ;add two high words together and low words together let in_j2=(in_j2_tmp.word1+in_j2_tmp.word0)/4 adin pin_j3, in_j3_bfr.word1(in_jx_ptr) ;get in-3 into buffer let in_j3_tmp=in_j3_bfr(0)+in_j3_bfr(1) ;add two high words together and low words together let in_j3=(in_j3_tmp.word1+in_j3_tmp.word0)/4 adin pin_j4, in_j4_bfr.word1(in_jx_ptr) ;get in-4 into buffer let in_j4_tmp=in_j4_bfr(0)+in_j4_bfr(1) ;add two high words together and low words together let in_j4=(in_j4_tmp.word1+in_j4_tmp.word0)/4 let in_jx_ptr=(in_jx_ptr+1)&$03 ;increment pointer and wrap at 3 return #endif ; ;routine to display led_data to out leds ;note: sets outjx values to 0 ;written by David J. Brown display_led: let out_j1=0 ;output high nibble of data let out_j2=0 let out_j3=0 let out_j4=0 let out_j1.bit11=led_data.bit7 ;sets output value to 2048 if bit is 1 let out_j2.bit11=led_data.bit6 let out_j3.bit11=led_data.bit5 let out_j4.bit11=led_data.bit4 gosub load_outputs pause 750 ;display high nibble let out_j1.bit11=led_data.bit3 ;output low nibble of data let out_j2.bit11=led_data.bit2 let out_j3.bit11=led_data.bit1 let out_j4.bit11=led_data.bit0 gosub load_outputs pause 750 ;display low nibble let out_j4=0 let out_j3=0 let out_j2=0 let out_j1=0 gosub load_outputs ;turn all leds off return ; ;output outjx values to dacs ;based on code written by Grant Richter ;750 uS execution time load_outputs: ;add addresses to values ;shift out 16 bits mode 4 shiftout ser_data,clock,fastmsbpre,[(out_j1+49152)\16] pulsout load_dacs,1 ;pulse loaddacs for each channel shiftout ser_data,clock,fastmsbpre,[(out_j2+32768)\16] pulsout load_dacs,1 ;pulse loaddacs for each channel shiftout ser_data,clock,fastmsbpre,[(out_j3+16384)\16] pulsout load_dacs,1 ;pulse loaddacs for each channel shiftout ser_data,clock,fastmsbpre,[out_j4\16] pulsout load_dacs,1 ;pulse loaddacs for each channel return ; ;******************************************* ;interrupt service routines ;******************************************* ; #ifdef timer_int_en ;interrupt service routine for timer ;increments time_count value ;toggle run led at 512 mS intervals ;written by David J. Brown tm_isr: let time_count=time_count+1 ;increment real time count let run_out=time_count.bit9 ;toggle run led if time_count=trig_end then ;check trig off time let out_j1=0 let trig_out=1 endif if time_count=note_end then ;check note off time let note_out=1 endif resume #endif ; ;******************************************* ;end of program ;*******************************************