TITLE "barorg.asm: midi barrel organ @4.0000MHz" LIST p=16C71 ERRORLEVEL 0, -302 ; ********** ********** ; * *** * ; NC A2 ** 1 18 ** A1 ; * * ; NC A3 ** 2 17 ** A0 SPEED ; * * ; NC A4 ** 3 16 ** Xin X1 ; * * ; pullup MCLR ** 4 15 ** Xout X0 ; * * ; GND ** 5 14 ** VCC ; * * ; RX B0 ** 6 13 ** B7 /TX ; * * ; KEY B1 ** 7 12 ** B6 TX ; * * ; SCL B2 ** 8 11 ** B5 LED1 ; * * ; SDA B3 ** 9 10 ** B4 LED0 ; * PIC16C71 * ; *********************** ; ********** ********** ; * *** * ; NC A0 ** 1 8 ** VCC +5V ; * * ; NC A1 ** 2 7 ** WP GND ; * * ; NC A2 ** 3 6 ** SCL input ; * * ; GND VSS ** 4 5 ** SDA open drain ; * * ; * LM24C16 * ; *********************** ; dataformat in externel eeprom (24C16 2KB): ; values 0..87 = note 21..108 on (piano range) ; values 88..175 = note 21..108 off ; values 176..254 = delta time 1..79 ; value 255 = end of piece ; midi note on: H'9x', H'nn', H'vv' ; channel x=0..15, note nn=0..127, velocity 0..127 ; CONFIG: CP off (1), PWRTE enabled (1), WDT off (0), FOSC xt (01) RTCC EQU 1 PC EQU 2 STATUS EQU 3 FSR EQU 4 PORTA EQU 5 PORTB EQU 6 ADCON0 EQU 0x08 ADRES EQU 0x09 PCLATH EQU H'0A' INTCON EQU H'0B' _OPTION EQU H'81' ; OPTION register in page 1 _TRISA EQU H'85' ; TRISA in page 1 _TRISB EQU H'86' ; TRISB in page 1 C EQU 0 DC EQU 1 Z EQU 2 P EQU 5 SCL EQU 2 ; 24C16 EEPROM clock pin on port B SDA EQU 3 ; 24C16 EEPROM data pin on port B tmp0 EQU H'0C' ; temporary data tmp1 EQU H'0D' tmp2 EQU H'0E' tmp3 EQU H'0F' sav0 EQU H'10' ; interrupt temporary data sav1 EQU H'11' ; bitdly EQU H'12' ; receiver bit delay rxshft EQU H'13' ; receiver shift register state EQU H'14' ; rx state (0=idle, 1=note on, 2=2nd byte in) notenr EQU H'15' ; note number (1st data byte of 'note on') sxcnt EQU H'16' ; speed transition counter s0cnt EQU H'17' ; program change switch counter prgrm EQU H'18' ; program (sound) selector time EQU H'19' ; delta time between notes accord EQU H'1A' ; number of notes received in sequence eeblk EQU H'1C' ; eeprom block address (24C16=0..7) eewrd EQU H'1D' ; eeprom word address (24C16=0..255) rdptr EQU H'1E' ; buffer read pointer wrptr EQU H'1F' ; buffer write pointer buf EQU H'20' ; midi receive buffer ORG H'000' ; reset GOTO main ORG H'004' ; interrupt GOTO intserv ; midi receive ORG H'008' ; eeread GOTO xeeread ; ORG H'00C' ; eewrite GOTO xeewrit ; ORG H'010' ; step GOTO step1 ; ORG H'014' ; record GOTO xrecord ; ORG H'018' ; play GOTO xplay ; ORG H'01C' ; disbuf GOTO xdisbuf ; ORG H'020' ; prgchg GOTO xprgchg ; ORG H'030' ; main MOVLW B'11111111' ; to PORTA MOVWF PORTA ; MOVLW B'00000000' ; to PORTB MOVWF PORTB ; MOVLW B'11100001' ; internal osc, channel 0, power on MOVWF ADCON0 BSF STATUS,P ; set page 1 MOVLW B'00000001' ; A0 input MOVWF _TRISA ; MOVLW B'10111111' ; B 0,1 inputs; 2...7 open drain MOVWF _TRISB ; MOVLW B'01010111' ; B pullup, int rising edge, rtc:256 MOVWF _OPTION ; BCF STATUS,P ; set page 0 MOVLW H'0C' ; reset vars MOVWF FSR ; initvar CLRF 0 ; INCF FSR,F ; MOVLW H'30' ; XORWF FSR,W ; BTFSS STATUS,Z ; GOTO initvar ; BTFSC PORTB,1 ; key pressed at power on? GOTO play ; GOTO record ; ORG H'050' ; interrupt service: midi receive intserv MOVWF sav0 ; [8] save working register MOVF STATUS,W ; [9] save status register MOVWF sav1 ; [10] BCF STATUS,P ; [11] set page 0 MOVLW H'80' ; [12] startbit MOVWF rxshft ; [13] MOVLW D'2' ; [14] adjust to poll middle of bits [48] MOVWF bitdly ; [W*3] ints0 DECFSZ bitdly,F ; [.] GOTO ints0 ; [.] ints1 MOVLW D'8' ; [1] bit time delay [32] MOVWF bitdly ; [W*3] ints2 DECFSZ bitdly,F ; [.] GOTO ints2 ; [.] RRF rxshft,F ; [26] shift in new bit BCF rxshft,7 ; [27] BTFSS PORTB,0 ; [28] poll midi in BSF rxshft,7 ; [29] BTFSS STATUS,C ; [30] startbit rotated to carry? GOTO ints1 ; [32] BTFSC rxshft,7 ; [1] test if this is a command byte GOTO tstcmd ; [3] BTFSS state,1 ; [3] 2nd data byte of 'note on' command? GOTO tstdat ; [5] BTFSC state,4 ; [5] note off command if bit 4 set GOTO noff ; [7] MOVF rxshft,F ; [7] note on command if data>0 BTFSC STATUS,Z ; [8] noff BSF notenr,7 ; [9] note off MOVF wrptr,W ; [10] write note on data to buf ADDLW buf ; [11] MOVWF FSR ; [12] INCF wrptr,W ; [13] ANDLW H'0F' ; [14] MOVWF wrptr ; [15] MOVF notenr,W ; [16] MOVWF 0 ; [17] DECF state,F ; [18] switch back to note on/off state intend BCF INTCON,1 ; [19] clr int flag for next startbit MOVF sav1,W ; [20] restore status register MOVWF STATUS ; [21] MOVF sav0,W ; [22] restore working register BCF STATUS,Z ; [23] BTFSC sav1,Z ; [24] restore zero bit BSF STATUS,Z ; [25] RETFIE ; [27] exit (short path for longest code) tstcmd MOVLW H'F0' ; [4] is it a 'note on' command? ANDWF rxshft,W ; [5] XORLW H'90' ; [6] BTFSC STATUS,Z ; [7] GOTO noteon ; [9] XORLW H'10' ; [9] or is it a 'note off' command? BTFSC STATUS,Z ; [10] GOTO noteof ; [12] CLRF state ; [12] switch to idle state GOTO intend ; [14] tstdat BTFSS state,0 ; [6] 1st data byte of 'note on/off' command? GOTO intend ; [8] MOVF rxshft,W ; [8] copy rxshft to notenr MOVWF notenr ; [9] INCF state,F ; [10] GOTO intend ; [12] noteon MOVLW H'01' ; [10] MOVWF state ; [11] GOTO intend ; [13] noteof MOVLW H'11' ; [13] MOVWF state ; [14] GOTO intend ; [16] waitrx MOVF rdptr,W ; [1] wait here until char received XORWF wrptr,W ; [2] BTFSC STATUS,Z ; [3] GOTO waitrx ; [5] bufrd MOVF rdptr,W ; [1] read data from buf ADDLW buf ; [2] BCF INTCON,4 ; [3] disable interrupt MOVWF FSR ; [4] INCF rdptr,W ; [5] ANDLW H'0F' ; [6] MOVWF rdptr ; [7] MOVF 0,W ; [8] BSF INTCON,4 ; [9] enable interrupt RETURN ; [11] eereset CLRF eeblk ; set 24C16 eeprom address to 0 CLRF eewrd ; RETLW 0 ; eeinc INCFSZ eewrd,F ; select next byte in eeprom 24C16 RETLW 0 ; INCF eeblk,F ; BTFSS eeblk,3 ; RETLW 0 ; RETLW H'FF' ; xeeread BTFSC eeblk,3 ; return FF if at end of eeprom RETLW H'FF' ; BSF STATUS,P ; set page 1 BCF _TRISB,SDA ; SDA low (start condition) BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BCF _TRISB,SCL ; BCF STATUS,P ; set page 0 RLF eeblk,W ; control byte with block address ANDLW H'0E' ; IORLW H'A0' ; CALL eebyte ; send MOVF eewrd,W ; word address CALL eebyte ; send GOTO $+1 ; BSF STATUS,P ; set page 1 BSF _TRISB,SDA ; SDA high BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BSF _TRISB,SCL ; start condition BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BCF _TRISB,SDA ; SDA low (start condition) BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BCF _TRISB,SCL ; BCF STATUS,P ; set page 0 RLF eeblk,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,P ; set page 1 BSF _TRISB,SCL ; clock high BCF STATUS,P ; set page 0 GOTO $+1 ; BCF STATUS,C ; BTFSC PORTB,SDA ; read bit BSF STATUS,C ; BSF STATUS,P ; set page 1 BCF _TRISB,SCL ; clock low BCF STATUS,P ; set page 0 RLF tmp0,F ; DECFSZ tmp1,F ; GOTO eerd0 ; BSF STATUS,P ; set page 1 BCF _TRISB,SDA ; SDA low BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BSF _TRISB,SCL ; stop condition BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BSF _TRISB,SDA ; SDA high (stop condition) BCF STATUS,P ; set page 0 CALL eeinc ; MOVF tmp0,W ; result to W RETURN ; xeewrit BTFSC eeblk,3 ; return if at end of eeprom RETURN ; MOVWF tmp2 ; store data BSF STATUS,P ; set page 1 BCF _TRISB,SDA ; SDA low (start condition) BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BCF _TRISB,SCL ; BCF STATUS,P ; set page 0 RLF eeblk,W ; control byte with block address ANDLW H'0E' ; IORLW H'A0' ; CALL eebyte ; send MOVF eewrd,W ; word address CALL eebyte ; send MOVF tmp2,W ; data CALL eebyte ; send BSF STATUS,P ; set page 1 BCF _TRISB,SDA ; SDA low BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BSF _TRISB,SCL ; stop condition BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BSF _TRISB,SDA ; SDA high (stop condition) BCF STATUS,P ; set page 0 MOVLW D'10' ; CALL delayw ; 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,P ; set page 1 BCF _TRISB,SDA ; SDA low BCF STATUS,P ; set page 0 GOTO eebt2 ; eebt1 BSF STATUS,P ; set page 1 BSF _TRISB,SDA ; SDA high BCF STATUS,P ; set page 0 GOTO eebt2 ; eebt2 BSF STATUS,P ; set page 1 BSF _TRISB,SCL ; clock BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BCF _TRISB,SCL ; BCF STATUS,P ; set page 0 DECFSZ tmp1,F ; GOTO eebt8 ; BSF STATUS,P ; set page 1 BSF _TRISB,SDA ; SDA high BCF STATUS,P ; set page 0 BSF STATUS,P ; set page 1 BSF _TRISB,SCL ; ignore ack BCF STATUS,P ; set page 0 GOTO $+1 ; BSF STATUS,P ; set page 1 BCF _TRISB,SCL ; BCF STATUS,P ; set page 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,P ; [1] set page 1 BCF _TRISB,7 ; [2] startbit BSF _TRISB,6 ; [3] startbit BCF STATUS,P ; [4] set page 0 MOVWF tmp2 ; [5] save char MOVLW H'08' ; [6] bitcounter MOVWF tmp1 ; [7] MOVLW D'6' ; [8] startbit delay MOVWF tmp0 ; [9] txmidia DECFSZ tmp0,F ; [6 * 3 - 1] GOTO txmidia ; [27] GOTO $+1 ; [28] txmidib RRF tmp2,F ; [29] data bit BTFSS STATUS,C ; [30] GOTO txmidi0 ; [32] NOP ; [32] txmidi1 BSF STATUS,P ; [1] set page 1 BSF _TRISB,7 ; [2] high bit BCF _TRISB,6 ; [3] high bit BCF STATUS,P ; [4] set page 0 GOTO txmidic ; [6] txmidi0 BSF STATUS,P ; [1] set page 1 BCF _TRISB,7 ; [2] low bit BSF _TRISB,6 ; [3] low bit BCF STATUS,P ; [4] set page 0 GOTO txmidic ; [6] txmidic MOVLW D'6' ; [7] databit delay MOVWF tmp0 ; [8] txmidid DECFSZ tmp0,F ; [6 * 3 - 1] GOTO txmidid ; [26] DECFSZ tmp1,F ; [26] done? GOTO txmidib ; [28] NOP ; [28] GOTO $+1 ; [30] GOTO $+1 ; [32] BSF STATUS,P ; [1] set page 1 BSF _TRISB,7 ; [2] stopbit BCF _TRISB,6 ; [3] stopbit BCF STATUS,P ; [4] set page 0 MOVLW D'10' ; [5] stopbit delay MOVWF tmp0 ; [6] txmidig DECFSZ tmp0,F ; [10 * 3 - 1] GOTO txmidig ; [36] RETLW 0 ; [38] xdisbuf BSF STATUS,P ; set page 1 BSF _TRISB,4 ; BSF _TRISB,5 ; BCF STATUS,P ; set page 0 disbuf0 MOVF eeblk,W ; XORLW H'00' ; BTFSS STATUS,Z ; GOTO disbuf1 ; BSF STATUS,P ; set page 1 BCF _TRISB,5 ; BCF STATUS,P ; set page 0 RETLW 0 ; disbuf1 MOVF eeblk,W ; XORLW H'07' ; BTFSS STATUS,Z ; RETLW 0 ; BSF STATUS,P ; set page 1 BCF _TRISB,4 ; BCF STATUS,P ; set page 0 RETLW 0 ; ;;;quick and dirty record routine xrecord MOVLW B'10010000' ; int enable, global interrupt enable MOVWF INTCON ; waitrec MOVF rdptr,W ; wait here until char received XORWF wrptr,W ; BTFSC STATUS,Z ; GOTO waitrec ; CLRF RTCC ; reset rtcc and flag BCF INTCON,2 ; bufempt CLRF accord ; recloop BTFSS INTCON,2 ; GOTO ckmore ; BCF INTCON,2 ; INCF time,F ; MOVLW D'80' ; XORWF time,W ; BTFSS STATUS,Z ; GOTO ckmore ; MOVLW D'255' ; CALL eewrite ; CLRF time ; ckmore MOVF rdptr,W ; char received? XORWF wrptr,W ; BTFSC STATUS,Z ; GOTO bufempt ; CALL bufrd ; MOVWF tmp0 ; MOVLW D'88' ; MOVWF tmp1 ; BTFSS tmp0,7 ; note off? CLRF tmp1 ; BCF tmp0,7 ; MOVLW D'21' ; SUBWF tmp0,F ; BTFSS STATUS,C ; GOTO recloop ; notenr < D'21' MOVLW D'88' ; SUBWF tmp0,W ; BTFSC STATUS,C ; GOTO recloop ; notenr > D'108' MOVF tmp0,W ; note on/off valid ADDWF tmp1,W ; MOVWF tmp3 ; MOVF accord,F ; if new accord BTFSS STATUS,Z ; GOTO wrnote ; MOVF time,W ; and if time > 0 BTFSC STATUS,Z ; GOTO wrnote ; BCF INTCON,2 ; MOVF time,W ; write time first ADDLW D'175' ; CALL eewrite ; MOVLW D'128' ; reset rtcc and time MOVWF RTCC ; CLRF time ; wrnote MOVF tmp3,W ; write note on/off CALL eewrite ; CALL disbuf ; INCF accord,F ; GOTO recloop ; ;;; play midi from external eeprom xplay MOVLW H'B0' ; all notes off CALL txmidi ; MOVLW D'123' ; CALL txmidi ; MOVLW D'00' ; CALL txmidi ; CALL step ; playnex CALL eeread ; MOVWF tmp3 ; XORLW D'255' ; BTFSC STATUS,Z ; GOTO main ; CALL disbuf ; MOVLW D'176' ; SUBWF tmp3,W ; BTFSS STATUS,C ; GOTO playoff ; MOVWF tmp3 ; INCF tmp3,F ; playbrk CALL step ; DECFSZ tmp3,F ; GOTO playbrk ; GOTO playnex ; playoff MOVLW D'88' ; SUBWF tmp3,W ; BTFSS STATUS,C ; GOTO playon ; ADDLW D'21' ; MOVWF tmp3 ; MOVLW H'90' ; CALL txmidi ; MOVF tmp3,W ; CALL txmidi ; MOVLW D'0' ; CALL txmidi ; GOTO playnex ; playon MOVLW H'90' ; CALL txmidi ; MOVF tmp3,W ; ADDLW D'21' ; CALL txmidi ; MOVLW D'96' ; CALL txmidi ; GOTO playnex ; step0 MOVLW D'66' ; MOVWF tmp0 ; step0b MOVLW D'120' ; MOVWF tmp1 ; step0a BTFSS PORTB,1 ; GOTO step0lo ; CLRF s0cnt ; GOTO step0x ; step0lo INCF s0cnt,F ; saturation at 255 BTFSC STATUS,Z ; DECF s0cnt,F ; MOVLW D'200' ; key pressed if count reached XORWF s0cnt,W ; BTFSC STATUS,Z ; CALL prgchg ; step0x DECFSZ tmp1,F ; GOTO step0a ; DECFSZ tmp0,F ; GOTO step0b ; RETLW 0 ; step1 BSF ADCON0,2 ; start ad conversion sampl BTFSC ADCON0,2 ; wait for conversion complete GOTO sampl ; MOVF ADRES,W ; get A/D result 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 ; GOTO switch0 ; stephi BTFSC sxcnt,1 ; stepx if it was at 2 GOTO stepx ; MOVLW 1 ; MOVWF sxcnt ; GOTO switch0 ; steplo BTFSC sxcnt,0 ; stepx if it was at 1 GOTO stepx ; MOVLW 2 ; MOVWF sxcnt ; switch0 BTFSS PORTB,1 ; GOTO sw0low ; CLRF s0cnt ; GOTO step ; sw0low INCF s0cnt,F ; saturation at 255 BTFSC STATUS,Z ; DECF s0cnt,F ; MOVLW D'200' ; key pressed if count reached XORWF s0cnt,W ; BTFSC STATUS,Z ; CALL prgchg ; GOTO step ; stepx CLRF sxcnt ; RETLW 0 ; xprgchg MOVLW H'B0' ; all notes off CALL txmidi ; MOVLW D'123' ; CALL txmidi ; MOVLW D'00' ; CALL txmidi ; MOVLW H'C0' ; program change CALL txmidi ; INCF prgrm,W ; to next sound ANDLW H'07' ; MOVWF prgrm ; MOVLW B'00011' ; PCLATH to pattern MOVWF PCLATH ; MOVF prgrm,W ; CALL getprog ; CALL txmidi ; RETLW 0 ; ORG H'300' ; rom tables getprog ADDWF PC,F ; RETLW D'000' ; acoustic grand piano RETLW D'003' ; honky tonk piano RETLW D'006' ; harpsichord RETLW D'009' ; glockenspiel RETLW D'016' ; drawbar organ RETLW D'019' ; church organ RETLW D'075' ; pan flute RETLW D'105' ; banjo END;