;
;       "QWER" box MIDI-to-analog switch trigger box on PIC16C84.
;
;       Written by the Old Crow (Scott Rider) on 08-JUN-97
;
;       Revision History:
;
;       V1.00   on 08-JUN-97    Basic no-frills version
;
;       This version uses Microchip assembler mnemonics and the
;       Microchip MPASM assembler.  Default config options are set
;       in the __FUSES line below: PWRTE on, CP off, WDT off, OSC=XT.
;
;       MIDI IN opto connects to 16C84 via RB7.
;       QWER triggers (active high) connect to 16C84 via RA0-RA3.
;
;                       _______  _______
;                      |       \/       |
;                RA2 --+ 1**       **18 +-- RA1           +----+
;                      |      MIDI4     |                 |   _|_
;                RA3 --+ 2**  CHIP **17 +-- RA0     22pf ===  \_/
;                      |                |                 |
;               RTCC --+ 3           16 +-- OSC1/CLKIN ---+
;                      |                |               [XXX] 8MHz xtal
;              !MCLR --+ 4 ##      >>15 +-- OSC2/CLKOUT --+
;                      |                |                 |
;                Vss --+ 5 <<      >>14 +-- Vdd          === 22pf
;                      |                |                _|_
;                RB0 --+ 6 ##      !!13 +-- RB7          \_/
;                      |                |
;                RB1 --+ 7           12 +-- RB6
;                      |                |
;                RB2 --+ 8           11 +-- RB5
;                      |                |
;                RB3 --+ 9           10 +-- RB4
;                      |   (PIC16C84)   |
;                      +----------------+
;
	list    p=16c84
	radix   dec
	include "p16c84.inc"

	__FUSES _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON

	cblock  0x0C    ;Store variables above control registers 

		i       ;MIDI byte storage
		j       ;/
		
		l       ;Loop variable in serial routines

		y       ;y,z for delay loops only
		z       ;/

		trigval ;trigger note value
		flags   ;Flag bits for whatnot
		recv    ;Receive data holding register
	endc

	org     0x00            ;Start of code space 
	goto    start
;
;  Support routines
;
;  dly200 -- entry for 100ms delay
;  dly_ms -- entry with number of ms in w (1 to 255)
;
dly200  movlw   200             ;Enter here for a 200ms delay
dly_ms  movwf   y               ;/

dym_0   movlw   249             ;Got to burn a thousand cycles
	movwf   z

dym_1   nop                     ;((N - 1) x 8) + 7
	nop
	nop
	nop
	nop                     ;(also could be clrwdt)
	decfsz  z,F
	goto    dym_1
	
	nop
	nop
	decfsz  y,F
	goto    dym_0           ;((1998) x 199) + 1997) = 199.8ms
	
	retlw   0
;
;   midi_in -- receive a midi byte at 31250 bps.  Detects framing errors 
; (i.e., if stop bit didn't happen.) with flags(4) cleared on error.
;
midi_in btfsc   PORTB,7         ;Detect start bit
	goto    midi_in
;
;   Found start...could be as much as 4 cycles late if start happened
; just after the btfsc.  Not a problem.  Wait 32 cycles (16us) to get
; to center of start bit.
;
	movlw   7               ;(1) Started -- get to center of bit-time
	movwf   z               ;(1)

m_in0   nop                     ;(6 x 4) + 3 = 27 cycles
	decfsz  z,F
	goto    m_in0           ;/

	movlw   8               ;(1) Eight bits of data to get
	movwf   l               ;(1)
	clrf    recv            ;(1)  [32 cycles to here]
;
;   Delay one bit-width (64 cycles) to get to center of LSB.
; Gather the bits into recv.
;
m_in1   movlw   14              ;(1)
	movwf   z               ;(1)

mdy_0   nop                     ;\
	decfsz  z,F             ;((13 x 4) + 3) = 55
	goto    mdy_0           ;/

	bcf     STATUS,C        ;(1) Default 0
	rrf     recv,F          ;(1) Put 0 in MSB of recv, then
	btfsc   PORTB,7         ;(1/2) sample the serial data and
	bsf     recv,7          ;(1) set the bit if sample was=1
	decfsz  l,F             ;((7 x 3) + 2) = 23, Do all bits
	goto    m_in1           ;/ [total cycles = 64, MSB = 63]

	bsf     flags,4         ;Say byte is good (test below). [MSB=64]
;
;   Time to first third of stop bit.  (55 cycles)
;
	movlw   18              ;(1)
	movwf   z               ;(1)

mdy_1   decfsz  z,F             ;((17 x 3) + 2) = 53
	goto    mdy_1           ;/

	btfss   PORTB,7         ;Stop bit showed up?
	bcf     flags,4         ;If not, indicate framing error

	retlw   0
;
;
; Main program loop.
;
;
	org     0x0100

start   movlw   b'0000'         ;Preset port A: triggers off (low)
	movwf   PORTA

	bsf     STATUS,RP0      ;Set up controls regs
	movlw   b'00000010'     ;Set TMR0 prescaler = 1:8 (f_osc=8MHz)     
	movwf   OPTION_REG      ;/
	movlw   b'0000'         ;Port A all outputs
	movwf   TRISA
	movlw   b'11111111'     ;Port B all inputs (only RB7 used)
	movwf   TRISB
	bcf     STATUS,RP0      ;Back to normal
;        
;  Step 1: Wait one second.
;
step1   movlw   5               ;5 x 200ms
	movwf   i

s1_0    call    dly200
	decfsz  i,F
	goto    s1_0
;
;  Step 2: Read MIDI data and find NOTE_ON commands. 
;        
step2   call    midi_in         ;Look for command byte
	btfss   flags,4         ;Continue if byte recvd OK
	goto    step2

	movlw   b'10010000'
	subwf   recv,W          ;Note on cmd?
	btfss   STATUS,Z
	goto    step2
;
;  Step 3: Read 2nd MIDI byte and see which key to trigger.
;
step3   call    midi_in
	btfss   flags,4         ;Got byte OK?
	goto    step2

	movf    recv,W          ;Get MIDI note no.
	movwf   trigval         ;/

	call    midi_in         ;Get note vel. and discard
	btfss   flags,4         ;Got it OK?
	goto    step2

	movlw   39              ;Note #1?
	subwf   trigval,W
	btfss   STATUS,Z
	goto    note2
;
;  Found note 1.  Trigger Q line for 200ms.
;
	movlw   b'0010'         ;Trigger "Q"
	movwf   PORTA           ;/
	goto    untrig

note2   movlw   51              ;Note #2?
	subwf   trigval,W
	btfss   STATUS,Z
	goto    note3
;
;  Found note 2.  Trigger W line for 200ms
;
	movlw   b'0001'         ;Trigger "W"
	movwf   PORTA           ;/
	goto    untrig

note3   movlw   75              ;Note #3?
	subwf   trigval,W
	btfss   STATUS,Z
	goto    note4
;
;  Found note 3.  Trigger E line for 200ms
;
	movlw   b'1000'         ;Trigger "E"
	movwf   PORTA           ;/
	goto    untrig

note4   movlw   87              ;Note #4?
	subwf   trigval,W
	btfss   STATUS,Z
	goto    step2
;
;  Found note 4.  Trigger R line for 200ms
;
	movlw   b'0100'         ;Trigger "R"
	movwf   PORTA           ;/

untrig  call    dly200          ;Wait 200ms
	movlw   0
	movwf   PORTA           ;Clear all triggers
	goto    step2
;
; That's all, folks!
;
	end

