TITLE "barrel.asm: midi barrel organ @4.0000MHz" LIST p=16C73A ERRORLEVEL 0, -302 ; Electronic barrel organ: play a song according to the rotation speed ; of a cranc. First version was realized with a pic 16C71 and a 2kByte ; eeprom in a small box with an AC motor to take the crank speed, midi ; in/out connectors, a dc supply connector and a momentary switch. ; ; This is an enhanced version with a 16C73 and the following features: ; - powered from the crank with a bike dynamo including the attached ; wavetable module and an audio amplifier (SANYO dynamo 6V, 2.5W) ; - up to 8 serial 8kByte eeproms to store songs ; - merging midi input with own midi data to midi output ; - continuous square wave from PWM for negative voltage supply ; of the wave module over a charge pump ; - can record all midi data or reduce data to save eeprom space ; ; Connections: (not all details included) ; Full wave rectifier, 22000uF Cap, LM2940-5 LDO regulator for VCC. ; Two 10k resistors to VCC and GND at CRANK, 10k+220nF to generator. ; Switch to GND at AUTO, Key to GND at PROG, song selector at SEL. ; SCL an SDA to I2C EEPROMs. PWM1, PWM2 each over 100R to a 4 stage ; diode/cap network for negative supply of wave module. PWR over 1k ; to a 2N327 PNP open collector at VCC for wave module logic power: ; this was necessary because my wave module did not recognize RST. ; RST to wave module. Resistor 330R to LED with grounded cathode. ; 220R resistor and reversed diode to 6N138 opto coupler for MIDI in ; out with 560R pullup to RX, 2x220R from VCC and TX for MIDI output. ; Current versions of Roland and Yamaha wave modules sound very ; good but consume a lot of power. I use a Terratec module with a ; pretty good sound. Another very small sound module with lowest ; supply current consumption turned out to be too noisy. ; ********** ********** ; * *** * ; pullup MCLR ** 1 28 ** B7 SCL ; * * ; CRANK A0 ** 2 27 ** B6 SDA ; * * ; NC A1 ** 3 26 ** B5 AUTO ; * * ; NC A2 ** 4 25 ** B4 PROG ; * * ; NC A3 ** 5 24 ** B3 NC ; * * ; NC A4 ** 6 23 ** B2 SEL2 ; * * ; NC A5 ** 7 22 ** B1 SEL1 ; * * ; GND GND ** 8 21 ** B0 SEL0 ; * * ; XTAL XI ** 9 20 ** VCC +5V ; * * ; XTAL XO ** 10 19 ** GND GND ; * * ; NC C0 ** 11 18 ** C7 RX ; * * ; PWM2 C1 ** 12 17 ** C6 TX ; * * ; PWM1 C2 ** 13 16 ** C5 LED ; * * ; PWR C3 ** 14 15 ** C4 RST ; * PIC16C73A * ; *********************** ; ********** ********** ; * *** * ; ADDR A0 ** 1 8 ** VCC +5V ; * * ; ADDR A1 ** 2 7 ** NC GND ; * * ; ADDR A2 ** 3 6 ** SCL input ; * * ; GND VSS ** 4 5 ** SDA open drain ; * * ; * 24LC65 * ; *********************** ; dataformat in externel eeprom (24C65 8KB): ; 0x00..0x57 = note 21..108 on: 90 xx 40 (piano range) ; 0x58..0xAF = note 21..108 off: 90 xx 00 ; 0xB0..0xEF = delta time 1..64 ticks ; 0xF5 xx = start full midi data, delta time 0..127 ticks ; 0xF8 = new song, F8..FF start packed midi data ; 0xFB xx xx = control change: B0 xx xx ; 0xFC xx = program change: C0 xx ; 0xFF = end of record ; PLAY MODE ; --------- ; SEL0..SEL2: select EEPROM address. ; AUTO: if tied low song is clocked automatically. ; PROG: (momentary switch) program change (see subroutine getprog) ; RECORD MODE ; ----------- ; If PROG is low at power on recording is started: ; AUTO=0 recful: record all midi messages except f5, f8..ff. ; AUTO=1 recpak: record piano range note on/off from all channels and ; map to channel 0, control and program changes only from channel 0. ; Slow EEPROM write (10ms) limits recording to 30..45 notes per second ; in packed mode and 10..20 in full mode. (note on, note off, timing) ; To get better performance in full record mode this recording is made ; at a lower speed: Set the midi player to 50% speed at recful mode. __CONFIG H'3FF9' ; PWRTE, WDT off, XT osc INDF EQU 0 ; indirect file access TMR0 EQU 1 ; real time clock counter PCL EQU 2 ; program counter lsb STATUS EQU 3 ; program status FSR EQU 4 ; file selection register PORTA EQU 5 ; i/o port PORTB EQU 6 ; i/o port PORTC EQU 7 ; i/o port PCLATH EQU H'0A' ; program counter msb (CALL, GOTO > H'800') INTCON EQU H'0B' ; interrupt control PIR1 EQU H'0C' ; peripheral interrupt flags PIR2 EQU H'0D' ; peripheral interrupt flags TMR1L EQU H'0E' ; timer 1 lsb TMR1H EQU H'0F' ; timer 1 msb T1CON EQU H'10' ; timer 1 control TMR2 EQU H'11' ; timer 2 T2CON EQU H'12' ; timer 2 control SSPBUF EQU H'13' ; synchronous serial port rx/tx data SSPCON EQU H'14' ; synchronous serial port control CCPR1L EQU H'15' ; capture/compare/pwm lsb CCPR1H EQU H'16' ; capture/compare/pwm msb CCP1CON EQU H'17' ; capture/compare control RCSTA EQU H'18' ; USART receiver control and status TXREG EQU H'19' ; USART transmit data RCREG EQU H'1A' ; USART receive data CCPR2L EQU H'1B' ; capture/compare/pwm lsb CCPR2H EQU H'1C' ; capture/compare/pwm msb CCP2CON EQU H'1D' ; capture/compare control ADRES EQU H'1E' ; A/D result ADCON0 EQU H'1F' ; A/D control _OPTION EQU H'81' ; OPTION register in bank 1 _TRISA EQU H'85' ; tristate register in bank 1 _TRISB EQU H'86' ; tristate register in bank 1 _TRISC EQU H'87' ; tristate register in bank 1 _PIE1 EQU H'8C' ; peripheral interrupt enable _PIE2 EQU H'8D' ; peripheral interrupt enable _PCON EQU H'8E' ; power control/status _PR2 EQU H'92' ; timer 2 period _SSPADD EQU H'93' ; synchronous serial port I2C address _SSPSTA EQU H'94' ; synchronous serial port status _TXSTA EQU H'98' ; USART transmitter control and status _SPBRG EQU H'99' ; USART baud rate _ADCON1 EQU H'9F' ; A/D control in bank 1 C EQU 0 ; carry DC EQU 1 ; digit carry Z EQU 2 ; zero PD EQU 3 ; power down TO EQU 4 ; watch dog time out RP0 EQU 5 ; register bank select RP1 EQU 6 ; register bank select IRP EQU 7 ; register bank select (indirect) PROG EQU 4 ; key input on port B AUTO EQU 5 ; switch input on port B SDA EQU 6 ; 24LC65 EEPROM data pin on port B SCL EQU 7 ; 24LC65 EEPROM clock pin on port B PWR EQU 3 ; wave module power control on port C RST EQU 4 ; wave module reset output on port C LED EQU 5 ; led output on port C ORG H'020' ; RAM savw RES 1 ; interrupt context save savs RES 1 ; interrupt context save savf RES 1 ; interrupt context save tmp0 RES 1 ; temporary data tmp1 RES 1 ; temporary data tmp2 RES 1 ; temporary data tmp3 RES 1 ; temporary data midi0 RES 1 ; midi running status 0 midi1 RES 1 ; midi running status 1 data0 RES 1 ; midi data bytes expected data1 RES 1 ; midi data bytes expected midix RES 1 ; midi running status (record) datax RES 1 ; midi data bytes expected (record) notex RES 1 ; midi note/control number (record) sxcnt RES 1 ; speed transition counter dkey0 RES 1 ; program select key counter prgrm RES 1 ; program (sound) selector tload RES 1 ; tmr1h loading for timer interrupt dtime RES 1 ; delta time between notes elaps RES 1 ; dtime value for playback accord RES 1 ; number of notes received in sequence eelo RES 1 ; eeprom byte address (0..255) eehi RES 1 ; eeprom byte address (24C65 0..32) eedev RES 1 ; eeprom device address (0..3) rptr0 RES 1 ; buffer 0 read pointer wptr0 RES 1 ; buffer 0 write pointer rptr1 RES 1 ; buffer 1 read pointer wptr1 RES 1 ; buffer 1 write pointer ORG H'040' ; RAM+32 buf0 RES D'64' ; midi receive buffer in bank 0 ORG H'0A0' ; RAM page 2 _savw RES 1 ; interrupt context save ORG H'0C0' ; RAM page 2 + 32 buf1 RES D'64' ; ee write / midi tx buf in bank 1 ORG H'000' ; reset GOTO main ORG H'004' ; interrupt GOTO intserv ; midi receive ORG H'008' ; main MOVLW B'00001111' ; code H'800' rom tables H'F00' MOVWF PCLATH ; GOTO init ; ORG H'810' ; init MOVLW B'11111111' ; to PORTA MOVWF PORTA ; MOVLW B'00111111' ; to PORTB (6,7 open drain) MOVWF PORTB ; MOVLW B'11001111' ; to PORTC (4 led, 5 reset wave module) MOVWF PORTC ; BSF STATUS,RP0 ; set bank 1 MOVLW B'01010111' ; pullup, int rising, rtc internal, rtc:256 MOVWF _OPTION ; MOVLW B'00000100' ; A5, A2 digital; A3, A1, A0 analog MOVWF _ADCON1 ; MOVLW B'00001111' ; A4..A5 output, A3..A0 input MOVWF _TRISA ; MOVLW B'11111111' ; B inputs MOVWF _TRISB ; MOVLW B'10000000' ; C7 input (RX), C6..C0 outputs MOVWF _TRISC ; BCF STATUS,RP0 ; set bank 0 MOVLW H'20' ; reset vars in bank 0 MOVWF FSR ; initvar CLRF INDF ; INCF FSR,F ; MOVLW H'80' ; XORWF FSR,W ; BTFSS STATUS,Z ; GOTO initvar ; MOVLW H'C0' ; about -1/61 sec to tload MOVWF tload ; BSF STATUS,RP0 ; set bank 1 MOVLW H'1F' ; PWM period 31.25kHz MOVWF _PR2 ; BCF STATUS,RP0 ; set bank 0 MOVLW H'0F' ; 50% duty cycle pwm1 MOVWF CCPR1L ; MOVLW H'0F' ; 50% duty cycle pwm2 MOVWF CCPR2L ; MOVLW B'01111100' ; postscaler 16, timer2 on, prescaler 1 MOVWF T2CON ; MOVLW B'00001111' ; ccp1 pwm mode MOVWF CCP1CON ; MOVLW B'00001111' ; ccp2 pwm mode MOVWF CCP2CON ; CLRF TMR1L ; CLRF TMR1H ; MOVLW B'00000001' ; presc 1, osz off, sync, intrnl clk, on MOVWF T1CON ; BSF STATUS,RP0 ; set bank 1 BSF _PIE1,0 ; enable timer 1 interrupt BCF STATUS,RP0 ; set bank 0 MOVLW B'11000001' ; a/d internal osc, channel 0, power on MOVWF ADCON0 ; BSF STATUS,RP0 ; set bank 1 MOVLW D'1' ; baudrate = 4MHz / (64*(1+1)) = 31250 MOVWF _SPBRG ; BCF STATUS,RP0 ; set bank 0 MOVLW B'10000000' ; async port enable MOVWF RCSTA ; BSF STATUS,RP0 ; set bank 1 BSF _TXSTA,5 ; enable transmitter BSF _PIE1,5 ; enable receiver interrupt BCF STATUS,RP0 ; set bank 0 BSF RCSTA,4 ; enable receiver MOVLW B'11000000' ; enable global and peripheral interrupts MOVWF INTCON ; MOVF PORTB,W ; get eeprom device address ANDLW B'00000111' ; MOVWF eedev ; MOVLW D'127' ; delay before starting wave module CALL delayw ; BCF PORTC,PWR ; power to wave module MOVLW D'63' ; wait for power to stabilize CALL delayw ; BSF PORTC,RST ; start wave module MOVLW D'127' ; wait for wave module ready CALL delayw ; BTFSS PORTB,PROG ; get mode GOTO record ; GOTO play ; ORG H'880' ; interrupt service intserv MOVWF savw ; save W SWAPF STATUS,W ; status to W BCF STATUS,RP0 ; set bank 0 MOVWF savs ; save STATUS MOVF FSR,W ; save FSR MOVWF savf ; intrcv BTFSS PIR1,5 ; receiver interrupt? GOTO inttmr ; no: go ahead BCF PIR1,5 ; yes: clear flag write0 MOVF wptr0,W ; write rx data to buf0 ADDLW buf0 ; MOVWF FSR ; MOVF RCREG,W ; MOVWF INDF ; INCF wptr0,W ; ANDLW H'3F' ; MOVWF wptr0 ; inttmr BTFSS PIR1,0 ; timer interrupt? GOTO intend ; no: go ahead BCF PIR1,0 ; yes: clear flag BCF T1CON,0 ; stop timer CLRF TMR1L ; load new timeout MOVF tload,W ; MOVWF TMR1H ; BSF T1CON,0 ; start timer INCFSZ dtime,W ; advance dtime (saturation at 255) INCF dtime,F ; intend MOVF savf,W ; restore FSR MOVWF FSR ; SWAPF savs,W ; restore STATUS MOVWF STATUS ; SWAPF savw,F ; restore W SWAPF savw,W ; RETFIE ; ORG H'8B0' ; read0 MOVF rptr0,W ; read data from buf0 XORWF wptr0,W ; BCF STATUS,C ; no data: clear carry BTFSC STATUS,Z ; RETLW 0 ; MOVF rptr0,W ; data available: ADDLW buf0 ; MOVWF FSR ; INCF rptr0,W ; ANDLW H'3F' ; MOVWF rptr0 ; MOVF INDF,W ; BSF STATUS,C ; data valid: set carry RETURN ; write1 MOVWF tmp0 ; write tx or eewrite data to buf1 INCF wptr1,W ; > if buffer is full ANDLW H'3F' ; > rather loose data XORWF rptr1,W ; > written last than BTFSC STATUS,Z ; > overwriting oldest RETLW H'FF' ; > data in ringbuffer. MOVF wptr1,W ; ADDLW buf1 ; MOVWF FSR ; MOVF tmp0,W ; MOVWF INDF ; INCF wptr1,W ; ANDLW H'3F' ; MOVWF wptr1 ; RETURN ; read1 MOVF rptr1,W ; read data from buf1 XORWF wptr1,W ; BCF STATUS,C ; no data: clear carry BTFSC STATUS,Z ; RETLW 0 ; MOVF rptr1,W ; data available: ADDLW buf1 ; MOVWF FSR ; INCF rptr1,W ; ANDLW H'3F' ; MOVWF rptr1 ; MOVF INDF,W ; BSF STATUS,C ; data valid: set carry RETURN ; eereset CLRF eehi ; set 24C65 eeprom address to 0 CLRF eelo ; RETLW 0 ; eeinc INCFSZ eelo,F ; select next byte in eeprom 24C65 RETLW 0 ; INCF eehi,F ; BTFSS eehi,5 ; RETLW 0 ; RETLW H'FF' ; eeread BTFSC eehi,5 ; RETLW H'FF' ; BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SDA ; SDA low (start condition) BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SCL ; BCF STATUS,RP0 ; set bank 0 RLF eedev,W ; control byte with block address ANDLW H'0E' ; IORLW H'A0' ; CALL eebyte ; send MOVF eehi,W ; word address 1 CALL eebyte ; send MOVF eelo,W ; word address 0 CALL eebyte ; send GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SDA ; SDA high BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SCL ; start condition BCF STATUS,RP0 ; set bank 0 GOTO $+3 ; eenext BTFSC eehi,5 ; RETLW H'FF' ; BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SDA ; SDA low (start condition) BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SCL ; BCF STATUS,RP0 ; set bank 0 RLF eedev,W ; control byte with block address ANDLW H'0E' ; IORLW H'A1' ; CALL eebyte ; send MOVLW H'08' ; bit counter MOVWF tmp1 ; eerd0 BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SCL ; clock high BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BCF STATUS,C ; BTFSC PORTB,SDA ; read bit BSF STATUS,C ; BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SCL ; clock low BCF STATUS,RP0 ; set bank 0 RLF tmp0,F ; DECFSZ tmp1,F ; GOTO eerd0 ; BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SDA ; SDA low BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SCL ; stop condition BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SDA ; SDA high (stop condition) BCF STATUS,RP0 ; set bank 0 CALL eeinc ; MOVF tmp0,W ; result to W RETURN ; ;attention: after eewrite wait 10ms before accessing eeprom again! eewrite BTFSC eehi,5 ; RETURN ; MOVWF tmp2 ; store data BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SDA ; SDA low (start condition) BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SCL ; BCF STATUS,RP0 ; set bank 0 RLF eedev,W ; control byte with block address ANDLW H'0E' ; IORLW H'A0' ; CALL eebyte ; send MOVF eehi,W ; word address CALL eebyte ; send MOVF eelo,W ; word address CALL eebyte ; send MOVF tmp2,W ; data CALL eebyte ; send BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SDA ; SDA low BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SCL ; stop condition BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SDA ; SDA high (stop condition) BCF STATUS,RP0 ; set bank 0 CALL eeinc ; MOVF tmp2,W ; restore W RETURN ; eebyte MOVWF tmp0 ; write W to I2C bus MOVLW H'08' ; bit counter MOVWF tmp1 ; eebt8 RLF tmp0,F ; BTFSS STATUS,C ; GOTO eebt0 ; GOTO eebt1 ; eebt0 BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SDA ; SDA low BCF STATUS,RP0 ; set bank 0 GOTO eebt2 ; eebt1 BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SDA ; SDA high BCF STATUS,RP0 ; set bank 0 GOTO eebt2 ; eebt2 BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SCL ; clock BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SCL ; BCF STATUS,RP0 ; set bank 0 DECFSZ tmp1,F ; GOTO eebt8 ; BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SDA ; SDA high BCF STATUS,RP0 ; set bank 0 BSF STATUS,RP0 ; set bank 1 BSF _TRISB,SCL ; ignore ack BCF STATUS,RP0 ; set bank 0 GOTO $+1 ; BSF STATUS,RP0 ; set bank 1 BCF _TRISB,SCL ; BCF STATUS,RP0 ; set bank 0 RETURN ; delayw MOVWF tmp0 ; delay w * 1000 cycles (w/o call and ret) dlyw0 MOVLW D'249' MOVWF tmp1 dlyw1 NOP DECFSZ tmp1,F GOTO dlyw1 DECFSZ tmp0,F GOTO dlyw0 RETLW 0 txmidi BSF STATUS,RP0 ; set bank 1 txmidw BTFSS _TXSTA,1 ; wait for transmitter empty GOTO txmidw ; BCF STATUS,RP0 ; set bank 0 MOVWF TXREG ; transmit RETURN ; clrtimr BCF T1CON,0 ; stop timer CLRF TMR1L ; load timeout BSF STATUS,C ; negative (arithmetic) shift RRF tload,W ; tload/2 for time rounding MOVWF TMR1H ; BSF T1CON,0 ; start timer CLRF dtime ; RETLW 0 ; clrtime BCF T1CON,0 ; stop timer CLRF TMR1L ; load timeout MOVF tload,W ; MOVWF TMR1H ; BSF T1CON,0 ; start timer CLRF dtime ; RETLW 0 ; eetimer MOVLW D'40' ; ee write time 10.24ms settmr SUBLW 0 ; 0 - timeout(*256us) MOVWF TMR0 ; reset prescaler, initialize rtcc BCF INTCON,2 ; reset overflow flag RETLW 0 ; step0 BTFSC PORTB,AUTO ; auto play clock or crank speed? GOTO step1 ; MOVF elaps,W ; dtime >= elaps SUBWF dtime,W ; BTFSS STATUS,C ; RETLW 0 ; CLRF elaps ; CALL clrtime ; RETLW 0 ; step1 BSF ADCON0,2 ; start ad conversion sampl BTFSC ADCON0,2 ; wait for conversion complete GOTO sampl ; MOVF ADRES,W ; software smith-trigger: SUBLW D'134' ; CY clr if > 2.65V BTFSS STATUS,C ; GOTO stephi ; SUBLW D'14' ; CY set if < 2.35V BTFSS STATUS,C ; GOTO steplo ; RETLW 0 ; stephi BTFSC sxcnt,1 ; stepx if it was at 2 GOTO stepx ; MOVLW 1 ; MOVWF sxcnt ; RETLW 0 ; steplo BTFSC sxcnt,0 ; stepx if it was at 1 GOTO stepx ; MOVLW 2 ; MOVWF sxcnt ; RETLW 0 ; stepx CLRF sxcnt ; MOVF elaps,W ; count down elaps until 0 BTFSS STATUS,Z ; DECF elaps,F ; RETLW 0 ; timepak INCFSZ accord,W ; add event to accord INCF accord,F ; saturation at 255 DECFSZ accord,W ; first event of accord? RETLW 0 ; no: done MOVF dtime,W ; copy dtime MOVWF tmp1 ; CALL clrtimr ; MOVLW H'EF' ; dtime 64 BTFSC tmp1,7 ; 128? CALL write1 ; BTFSC tmp1,7 ; 128? CALL write1 ; BTFSC tmp1,6 ; 64? CALL write1 ; MOVF tmp1,W ; ANDLW H'3F' ; BTFSC STATUS,Z ; > 0? RETLW 0 ; ADDLW H'AF' ; CALL write1 ; RETLW 0 ; timeful INCFSZ accord,W ; add event to accord INCF accord,F ; saturation at 255 DECFSZ accord,W ; first event of accord? RETLW 0 ; no: done MOVF dtime,W ; copy dtime MOVWF tmp1 ; CALL clrtimr ; BTFSS tmp1,7 ; >= 128? GOTO timefu0 ; MOVLW H'F5' ; CALL write1 ; MOVLW H'7F' ; write 127 (failure < 1%) CALL write1 ; timefu0 MOVLW H'7F' ; mask off first bit, check result ANDWF tmp1,F ; BTFSC STATUS,Z ; > 0? RETLW 0 ; MOVLW H'F5' ; CALL write1 ; MOVF tmp1,W ; CALL write1 ; RETLW 0 ; eeproc MOVF rptr1,W ; call periodically to clear buf1 XORWF wptr1,W ; BTFSC STATUS,Z ; RETLW 0 ; no data in buf1 BTFSS INTCON,2 ; RETLW 0 ; ee write timer not elapsed CALL read1 ; read buf1 CALL eewrite ; write to eeprom CALL eetimer ; set 10ms timer BTFSC eehi,5 ; if eeprom full BSF PORTC,LED ; turn led on RETLW 0 ; ;;; play midi from external eeprom play CALL eeread ; GOTO plchk ; plpak CALL step0 ; CALL merge0 ; merge input MOVF elaps,W ; BTFSC STATUS,Z ; GOTO plpak0 ; CALL merge1 ; merge our events in idle time CALL keyck ; GOTO plpak ; plpak0 CALL eenext ; plchk MOVWF tmp3 ; XORLW H'FF' ; end of eeprom? BTFSC STATUS,Z ; GOTO init ; BTFSS eehi,1 ; led off for bytes 0..511 BCF PORTC,LED ; BTFSC eehi,1 ; led on for bytes 512..1KB BSF PORTC,LED ; MOVF tmp3,W ; switch to full midi data? XORLW H'F5' ; BTFSC STATUS,Z ; GOTO plful1 ; MOVF tmp3,W ; new song? XORLW H'F8' ; BTFSC STATUS,Z ; GOTO plnew ; MOVF tmp3,W ; control change? XORLW H'FB' ; BTFSC STATUS,Z ; GOTO plpak3 ; MOVF tmp3,W ; program change? XORLW H'FC' ; BTFSC STATUS,Z ; GOTO plpak4 ; MOVF tmp3,W ; nop? ANDLW H'F0' ; XORLW H'F0' ; BTFSC STATUS,Z ; GOTO plpak ; MOVLW H'B0' ; delta time? SUBWF tmp3,W ; BTFSC STATUS,C ; GOTO plpak2 ; MOVLW H'58' ; note off? SUBWF tmp3,W ; BTFSC STATUS,C ; GOTO plpak1 ; MOVLW H'90' ; note on CALL write1 ; MOVF tmp3,W ; ADDLW D'21' ; CALL write1 ; MOVLW H'60' ; forte CALL write1 ; GOTO plpak ; plpak1 MOVLW H'90' ; note off CALL write1 ; MOVLW D'67' ; SUBWF tmp3,W ; CALL write1 ; MOVLW H'00' ; velocity 0 CALL write1 ; GOTO plpak ; plpak2 MOVLW H'AF' ; delta time SUBWF tmp3,W ; MOVWF elaps ; GOTO plpak ; plpak3 MOVLW H'B0' ; control change CALL write1 ; CALL eeread ; CALL write1 ; CALL eeread ; CALL write1 ; GOTO plpak ; plpak4 MOVLW H'C0' ; program change CALL write1 ; CALL eeread ; CALL write1 ; GOTO plpak ; plful CALL step0 ; CALL merge0 ; merge input MOVF elaps,W ; BTFSC STATUS,Z ; GOTO plful0 ; CALL merge1 ; merge our events in idle time CALL keyck ; GOTO plful ; plful0 CALL eenext ; MOVWF tmp3 ; XORLW H'FF' ; end of eeprom? BTFSC STATUS,Z ; GOTO init ; BTFSS eehi,1 ; led off for bytes 0..511 BCF PORTC,LED ; BTFSC eehi,1 ; led on for bytes 512..1KB BSF PORTC,LED ; MOVF tmp3,W ; delta time? XORLW H'F5' ; BTFSC STATUS,Z ; GOTO plful1 ; MOVF tmp3,W ; new song? XORLW H'F8' ; BTFSC STATUS,Z ; GOTO plnew ; MOVLW H'F8' ; switch to packed midi data? SUBWF tmp3,W ; BTFSC STATUS,C ; GOTO plpak ; MOVF tmp3,W ; midi data to buffer CALL write1 ; BTFSC tmp3,7 ; new status byte? CALL merge1 ; merge last command GOTO plful ; plful1 CALL eeread ; delta time MOVWF elaps ; GOTO plful ; plnew CLRF wptr1 ; new song: CLRF rptr1 ; some resets on all channels MOVLW H'B0' ; control change MOVWF tmp2 ; mreset MOVF tmp2,W ; reset all sounds CALL write1 ; MOVLW H'78' ; CALL write1 ; MOVLW H'00' ; CALL write1 ; CALL merge0 ; CALL merge1 ; MOVF tmp2,W ; reset all controllers CALL write1 ; MOVLW H'79' ; CALL write1 ; MOVLW H'00' ; CALL write1 ; CALL merge0 ; CALL merge1 ; MOVF tmp2,W ; channel volume CALL write1 ; MOVLW H'07' ; CALL write1 ; MOVLW H'60' ; mezzo forte CALL write1 ; CALL merge0 ; CALL merge1 ; INCF tmp2,F ; next channel BTFSS tmp2,6 ; BF -> C0: all channels done GOTO mreset ; MOVLW H'0F' ; insert a short delay MOVWF elaps ; GOTO plpak ; continue playing ;;; record midi to external eeprom record BSF PORTC,LED ; led on MOVF rptr0,W ; wait for midi data XORWF wptr0,W ; BTFSC STATUS,Z ; GOTO record ; BCF PORTC,LED ; led off MOVLW H'C0' ; about -1/61 sec to tload MOVWF tload ; MOVLW H'F8' ; new song CALL write1 ; CALL clrtime ; reset timer BTFSC PORTB,AUTO ; GOTO recpak0 ; record part of midi only MOVLW H'80' ; about -1/30 sec to tload MOVWF tload ; MOVLW H'F5' ; record full midi data CALL write1 ; MOVLW 0 ; first delta time 0 CALL write1 ; GOTO recful0 ; recpak0 MOVLW D'200' ; set timeout (*12us) MOVWF tmp0 ; recpak1 CALL read0 ; [7] read rx buffer BTFSC STATUS,C ; [8] data available? GOTO recpak2 ; yes: check data DECFSZ tmp0,F ; [10] if not timeout GOTO recpak1 ; [12] retry reading CLRF accord ; time gap between data CALL eeproc ; transfer buf1 to eeprom MOVLW 5 ; retry read with short timeout MOVWF tmp0 ; INCF dtime,W ; end of rec if pause >= 255 ticks BTFSS STATUS,Z ; GOTO recpak1 ; retry read GOTO rectail ; recpak2 MOVWF tmp3 ; store rx byte CALL txmidi ; midi through BTFSS tmp3,7 ; midi status? GOTO recpak3 ; no: midi data MOVF tmp3,W ; yes: store running status MOVWF midix ; SWAPF midix,W ; get number of data bytes ANDLW H'07' ; CALL getprt ; MOVWF datax ; GOTO recpak0 ; recpak3 MOVF datax,F ; data expected BTFSC STATUS,Z ; GOTO recpak0 ; no: do nothing DECF datax,F ; last data byte BTFSC STATUS,Z ; GOTO recpak4 ; yes: store in eeprom MOVF tmp3,W ; store note/control number MOVWF notex ; GOTO recpak0 ; recpak4 SWAPF midix,W ; get number of data bytes ANDLW H'07' ; CALL getprt ; MOVWF datax ; MOVF midix,W ; ANDLW H'F0' ; XORLW H'B0' ; control change? BTFSC STATUS,Z ; GOTO recpak5 ; MOVF midix,W ; ANDLW H'F0' ; XORLW H'C0' ; program change? BTFSC STATUS,Z ; GOTO recpak6 ; MOVF midix,W ; note off? ANDLW H'F0' ; XORLW H'80' ; BTFSC STATUS,Z ; CLRF tmp3 ; yes: velocity to 0 MOVLW D'21' ; SUBWF notex,F ; BTFSS STATUS,C ; GOTO recpak0 ; notenr < D'21' MOVLW D'88' ; SUBWF notex,W ; BTFSC STATUS,C ; GOTO recpak0 ; notenr > D'108' CALL timepak ; MOVF notex,W ; note on to eeprom MOVF tmp3,F ; BTFSC STATUS,Z ; note off? ADDLW D'88' ; CALL write1 ; GOTO recpak0 ; recpak5 MOVF midix,W ; control change on channel 0? ANDLW H'0F' ; BTFSS STATUS,Z ; GOTO recpak0 ; CALL timepak ; MOVLW H'FB' ; control change to eeprom CALL write1 ; MOVF notex,W ; control nr to eeprom CALL write1 ; MOVF tmp3,W ; control data to eeprom CALL write1 ; GOTO recpak0 ; recpak6 MOVF midix,W ; program change on channel 0? ANDLW H'0F' ; BTFSS STATUS,Z ; CALL timepak ; MOVLW H'FC' ; program change to eeprom CALL write1 ; MOVF tmp3,W ; program nr to eeprom CALL write1 ; GOTO recpak0 ; recful0 MOVLW D'200' ; set timeout (*12us) MOVWF tmp0 ; recful1 CALL read0 ; [7] read rx buffer BTFSC STATUS,C ; [8] data available? GOTO recful2 ; yes: check data DECFSZ tmp0,F ; [10] if not timeout GOTO recful1 ; [12] retry reading CLRF accord ; time gap between data CALL eeproc ; transfer buf1 to eeprom MOVLW 5 ; retry read with short timeout MOVWF tmp0 ; INCF dtime,W ; end of rec if pause >= 255 ticks BTFSS STATUS,Z ; GOTO recful1 ; retry read GOTO rectail ; recful2 MOVWF tmp3 ; store rx byte CALL txmidi ; midi through BTFSS tmp3,7 ; midi status? GOTO recful3 ; no: midi data SUBLW H'F7' ; system realtime? BTFSS STATUS,C ; GOTO recful0 ; yes: ignore CLRF datax ; ignore data if F5 MOVF tmp3,W ; XORLW H'F5' ; undefined F5 BTFSC STATUS,Z ; GOTO recful0 ; yes: ignore BSF datax,0 ; accept all data if event <> F5 CALL timeful ; MOVF tmp3,W ; status byte to eeprom CALL write1 ; GOTO recful0 ; recful3 BTFSS datax,0 ; data expected? GOTO recful0 ; no: do nothing CALL timeful ; MOVF tmp3,W ; yes: data byte to eeprom CALL write1 ; GOTO recful0 ; rectail BTFSS PORTB,PROG ; get mode ; GOTO record ; append another song MOVLW H'FF' ; done: end of record CALL eewrite ; direct to eeprom (buf1 now empty) MOVLW H'10' ; CALL delayw ; wait until write complete GOTO init ; restart device merge0 CALL read0 ; next byte from buf0 BTFSC STATUS,C ; data available? GOTO mergb0 ; yes: begin merging MOVF data0,W ; no: more data required? BTFSC STATUS,Z ; RETLW 0 ; no: done MOVLW D'255' ; yes: set timeout (*12us) MOVWF tmp0 ; merga0 CALL read0 ; retry reading BTFSC STATUS,C ; data available? GOTO mergb0 ; yes: begin merging DECFSZ tmp0,F ; no: if not timeout GOTO merga0 ; retry reading CLRF data0 ; else return error 1 RETLW 1 ; mergb0 MOVWF tmp0 ; save byte BTFSS tmp0,7 ; midi status? GOTO mergd0 ; no: midi data CALL txmidi ; yes: transmit status byte CLRF data0 ; SUBLW H'F7' ; system realtime? BTFSS STATUS,C ; GOTO merge0 ; yes: keep running status MOVF tmp0,W ; MOVWF midi0 ; set new running status SUBLW H'EF' ; system common? BTFSS STATUS,C ; GOTO mergc0 ; SWAPF midi0,W ; no: normal event ANDLW H'07' ; CALL getcmd ; MOVWF data0 ; data bytes expected GOTO merge0 ; read next mergc0 MOVF midi0,W ; system common ANDLW H'07' ; CALL getsys ; MOVWF data0 ; data bytes expected GOTO merge0 ; read next mergd0 MOVF data0,W ; midi data: BTFSS STATUS,Z ; more data required? GOTO mergs0 ; yes: send data MOVF midi0,W ; no: insert midi status CALL txmidi ; SWAPF midi0,W ; get number of data bytes ANDLW H'07' ; CALL getcmd ; MOVWF data0 ; mergs0 MOVF tmp0,W ; send data CALL txmidi ; INCF data0,W ; undefined data count 0xff ? BTFSC STATUS,Z ; GOTO merge0 ; yes: get more data DECF data0,F ; no: decrement data count BTFSS STATUS,Z ; command complete? GOTO merge0 ; no: get more data RETLW 0 ; yes: done merge1 CALL read1 ; next byte from buf1 BTFSC STATUS,C ; data available? GOTO mergb1 ; yes: begin merging MOVF data1,W ; no: more data required? BTFSC STATUS,Z ; RETLW 0 ; no: done MOVLW D'255' ; yes: set timeout (*12us) MOVWF tmp0 ; merga1 CALL read1 ; retry reading BTFSC STATUS,C ; data available? GOTO mergb1 ; yes: begin merging DECFSZ tmp0,F ; no: if not timeout GOTO merga1 ; retry reading CLRF data1 ; else return error 1 RETLW 1 ; mergb1 MOVWF tmp0 ; save byte BTFSS tmp0,7 ; midi status? GOTO mergd1 ; no: midi data CALL txmidi ; yes: transmit status byte CLRF data1 ; SUBLW H'F7' ; system realtime? BTFSS STATUS,C ; GOTO merge1 ; yes: keep running status MOVF tmp0,W ; MOVWF midi1 ; set new running status SUBLW H'EF' ; system common? BTFSS STATUS,C ; GOTO mergc1 ; SWAPF midi1,W ; no: normal event ANDLW H'07' ; CALL getcmd ; MOVWF data1 ; data bytes expected GOTO merge1 ; read next mergc1 MOVF midi1,W ; system common ANDLW H'07' ; CALL getsys ; MOVWF data1 ; data bytes expected GOTO merge1 ; read next mergd1 MOVF data1,W ; midi data: BTFSS STATUS,Z ; more data required? GOTO mergs1 ; yes: send data MOVF midi1,W ; no: insert midi status CALL txmidi ; SWAPF midi1,W ; get number of data bytes ANDLW H'07' ; CALL getcmd ; MOVWF data1 ; mergs1 MOVF tmp0,W ; send data CALL txmidi ; INCF data1,W ; undefined data count 0xff ? BTFSC STATUS,Z ; GOTO merge1 ; yes: get more data DECF data1,F ; no: decrement data count BTFSS STATUS,Z ; command complete? GOTO merge1 ; no: get more data RETLW 0 ; yes: done keyck BTFSS PORTB,PROG ; key pressed? GOTO key0b ; DECF dkey0,W ; no: dec dkey BTFSS dkey0,7 ; if accepted CLRW ; else reset MOVWF dkey0 ; RETLW 0 ; done key0b INCF dkey0,W ; yes: inc dkey BTFSC dkey0,7 ; if not accepted MOVLW H'E0' ; else set bit 7 + key release min MOVWF dkey0 ; XORLW H'70' ; if not key press min BTFSS STATUS,Z ; RETLW 0 ; done MOVLW H'E0' ; else accept key MOVWF dkey0 ; prgchg MOVLW H'B0' ; all sounds off CALL write1 ; MOVLW H'78' ; CALL write1 ; MOVLW D'00' ; CALL write1 ; MOVLW H'C0' ; program change CALL write1 ; MOVF prgrm,W ; sound number CALL getprog ; CALL write1 ; INCF prgrm,W ; to next sound ANDLW H'03' ; MOVWF prgrm ; RETLW 0 ; ORG H'F00' ; rom tables getcmd ADDWF PCL,F ; data bytes for midi status msg RETLW 2 ; 8x nn vv (note off) RETLW 2 ; 9x nn vv (note on) RETLW 2 ; Ax nn mm (after touch) RETLW 2 ; Bx cc mm (control change) RETLW 1 ; Cx pp (program change) RETLW 1 ; Dx mm (channel pressure) RETLW 2 ; Ex ii ii (pitch weel) RETLW H'FF' ; system messages getsys ADDWF PCL,F ; data bytes system common msg RETLW H'FF' ; F0 start sysex RETLW 1 ; F1 quarter RETLW 2 ; F2 song pos. RETLW 1 ; F3 song sel. RETLW H'FF' ; F4 undef RETLW H'FF' ; F5 undef RETLW 0 ; F6 tune req. RETLW 0 ; F7 end sysex getprt ADDWF PCL,F ; data bytes for midi status msg RETLW 2 ; 8x nn vv (note off) RETLW 2 ; 9x nn vv (note on) RETLW 0 ; Ax nn mm (after touch) RETLW 2 ; Bx cc mm (control change) RETLW 1 ; Cx pp (program change) RETLW 0 ; Dx mm (channel pressure) RETLW 0 ; Ex ii ii (pitch weel) RETLW 0 ; system messages getprog ADDWF PCL,F ; RETLW D'000' ; acoustic grand piano RETLW D'006' ; harpsichord RETLW D'019' ; church organ RETLW D'075' ; pan flute END;