;-----------------------------------------------------------------------------------------------; ; LICENSE AGREEMENT: ; ; This program is free software. You can redistribute it and/or modify it under the terms of ; ; the GNU General Public License as published by the Free Software Foundation, either version ; ; 2 of the License, or (at your option) any later version. No part of this code may be used ; ; in any commercial application without prior written permission. ; ; ; ; 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. ; ; See the GNU General Public License for more details. ; ; ; ; Copyright (c) July 11, 2005 David J. Brown ; ; ; ;-----------------------------------------------------------------------------------------------; ; ; MIDI processor.asm ; developed for the ATTINY2313 ; ; written by David J. Brown ; email: davebr@earthlink.net ; web: http://modularsynthesis.com ; ; created: July 11, 2005 ; last modified: September 17, 2005 ; ; revision 0.6 ; rev 0.6 added conditional directives for timing clock, ; active status, and expression ; rev 0.5 removed rc calibration, updated watchdog timer, ; added map expression cc to volume cc ; rev 0.4 added realtime message processing in wait_rx, ; process and send undefined system messages ; rev 0.3 added .def statements for registers ; rev 0.2 changed transpose switch codes 13 - 15 to octaves, ; added specifications, changed subi to cpi command ; rev 0.1 changed multi from polyphony to keyboard split ; rev 0.0 initial code release ; ;------------------------ ; conditional directives ;------------------------ ; ;.equ map_expr_to_vol =1 ;uncomment to map expression cc to volume cc ; ;.equ filter_timing_clock =1 ;uncomment to filter timing clock (0xf8) ; ;.equ filter_active_status =1 ;uncomment to filter active status (0xfe) ; ;------------------------ ; specifications ;------------------------ ; ; Switch settings: ; ; SD5 note transpose on/off ; SD4 channel transpose on/off ; SD3 note reverse on/off ; SD2 multi keyboard on/off ; ; SB7 note transpose bit3 0/1 0-11 transpose up 0 to 11 semitones ; SB6 note transpose bit2 0/1 12 transpose up 1 octave ; SB5 note transpose bit1 0/1 13 transpose up 2 octaves ; SB4 note transpose bit0 0/1 14 transpose down 1 octave ; 15 transpose down 2 octaves ; ; SB3 channel transpose bit3 0/1 0-15 selects MIDI channel ; SB2 channel transpose bit2 0/1 ; SB1 channel transpose bit1 0/1 ; SB0 channel transpose bit0 0/1 ; ; Note transpose adjusts all MIDI note on and off commands by 0 to 11 semitones ; or +1, +2, -1 or -2 octaves as selected by SB7 - SB4. ; ; Channel transpose replaces the channel in all MIDI commands with the channel ; selected by SB3 - SB0. ; ; Note reverse inverts the keyboard around key C4. D4 is transposed to A3#, etc. Note ; transpose operates after the reverse function. ; ; Multi keyboard splits and replicates the keyboard at key C4. The notes below key C4 ; are scaled up two octaves and the notes at key C4 and above are scaled down 2 octaves ; resulting in two keyboards with key C2 and key C6 playing C4 (middle C). Positioning ; your hands appropriately can 'reverse' the registers with your right hand playing the low ; notes and your left hand playing the high notes. Add reverse for an even crazier mode ; as each multi keyboard reverses. Note transpose operates after the multi and reverse ; functions. ; ; An external 8 MHz crystal is required since power for the AVR is unregulated. ; ;------------------------ ; pins ;------------------------ ; ; pa0 xtal ; pa1 xtal ; pa2 reset ; ; pb0 channel transpose bit0 ; pb1 channel transpose bit1 ; pb2 channel transpose bit2 ; pb3 channel transpose bit3 ; pb4 note transpose bit0 ; pb5 note transpose bit1 / mosi ; pb6 note transpose bit2 / miso ; pb7 note transpose bit3 / sck ; ; pd0 MIDI in ; pd1 MIDI out ; pd2 multi (on=1 / off) ; pd3 reverse (on=1 / off) ; pd4 channel transpose (on=1 / off) ; pd5 note transpose (on=1 / off) ; pd6 led (on=1 / off) ; ; timer0 25 mS interrupts ; timer1 not used ; ;------------------------ ; registers ;------------------------ ; ; r0 ; r1 ; r2 ; r3 ; r4 ; r5 ; r6 ; r7 ; r8 ; r9 ; r10 ; r11 ; r12 ; r13 ; r14 .def led_blnk =r15 ;r15 led blink count .def tempr =r16 ;r16 temp .def tx_cnt =r17 ;r17 tx character count (0=empty) .def tx_putptr =r18 ;r18 tx put pointer (first empty) .def tx_getptr =r19 ;r19 tx get buffer pointer (first valid) .def rx_cnt =r20 ;r20 rx character count (0=empty) .def rx_putptr =r21 ;r21 rx put pointer (first empty) .def rx_getptr =r22 ;r22 rx get buffer pointer (first valid) .def run_cmd =r23 ;r23 running status command .def xlat_note =r24 ;r24 translate note temp .def xlat_chan =r25 ;r25 translate channel temp ; r26/xl ;r26/xl rx temp address pointer ; r27/xh ;r27/xh rx temp address pointer ; r28/yl ;r28/yl tx temp address pointer ; r29/yh ;r29/yh tx temp address pointer ; r30/zl ; r31/zh ; ; ;------------------------ ; assembler directives ;------------------------ ; .nolist .include "tn2313def.inc" .list .listmac ; ;------------------------ ; ram ;------------------------ ; .dseg .org sram_start ; .equ rx_len =32 ;MIDI in buffer length (must be <256) .equ tx_len =32 ;MIDI out buffer length (must be <256) ; rx_bfr: .byte rx_len ;MIDI in buffer tx_bfr: .byte tx_len ;MIDI out buffer ; ;------------------------ ; program ;------------------------ ; .cseg .org 0x0000 ; rjmp start ;0x000 reset rjmp irq_none ;0x001 int0 rjmp irq_none ;0x002 int1 rjmp irq_none ;0x003 timer1 capt rjmp irq_none ;0x004 timer1 comp-a rjmp irq_none ;0x005 timer1 ovf rjmp tmr0_isr ;0x006 timer0 ovf rjmp rx_isr ;0x007 uart rx rjmp tx_isr ;0x008 uart udre rjmp irq_none ;0x009 uart tx rjmp irq_none ;0x00A analog comp rjmp irq_none ;0x00B pcint rjmp irq_none ;0x00C timer1 comp-b rjmp irq_none ;0x00D timer0 comp-a rjmp irq_none ;0x00E timer0 comp-b rjmp irq_none ;0x00F usi start rjmp irq_none ;0x010 usi ovf rjmp irq_none ;0x011 ee rdy rjmp irq_none ;0x012 wdt ovf ; ;------------------------ ; unused interrupt isr ;------------------------ ; irq_none: reti ; ;------------------------ ; serial in isr ;------------------------ ; ;serial in isr ;puts data into buffer ;decrements character count if not empty ;registers used ; tempr=temp ; rx_cnt=character count ; rx_putptr=put pointer ; x=buffer address rx_isr: push xh push xl push tempr in tempr,sreg push tempr in tempr,udr ;read received byte ; .ifdef filter_timing_clock ;conditional filter timing clock cpi tempr,0xf8 ;check for timing clock breq rx_3 ;filter out active status .endif ; .ifdef filter_active_status ;conditional filter active status cpi tempr,0xfe ;check for active status breq rx_3 ;filter out active status .endif ; ldi xh,high(rx_bfr) ;get input buffer address ldi xl,low(rx_bfr) add xl,rx_putptr ;add put pointer brcc rx_1 ;check for carry inc xh ;correct high byte rx_1: st x,tempr ;store character inc rx_putptr ;increment put pointer cpi rx_putptr,rx_len ;check if at end of buffer brne rx_2 ldi rx_putptr,0 ;wrap pointer rx_2: cpi rx_cnt,rx_len ;check if buffer full breq rx_3 ;overwrites first data if full inc rx_cnt ;increment character count if not full rx_3: pop tempr out sreg,tempr pop tempr pop xl pop xh reti ; ;------------------------ ; serial out isr ;------------------------ ; ;serial out isr ;gets data from buffer ;decrements character count ;registers used ; tempr=temp ; tx_cnt=character count ; tx_getptr=get pointer ; y=buffer address tx_isr: push yh push yl push tempr in tempr,sreg push tempr ldi yh,high(tx_bfr) ;get input buffer address ldi yl,low(tx_bfr) add yl,tx_getptr ;add put pointer brcc tx_1 ;check for carry inc yh ;correct high byte tx_1: ld tempr,y ;get character out udr,tempr ;send to uart inc tx_getptr ;increment put pointer cpi tx_getptr,tx_len ;check if at end of buffer brne tx_2 ldi tx_getptr,0 ;wrap pointer tx_2: dec tx_cnt ;decrement character count brne tx_3 ;check for empty push tempr ;save data in tempr,ucsrb ;get control register andi tempr,~(1<