  ;  *** COMPILED WITH SX/B VERSION 1.42  8/28/2005 ***
                                 ;' =========================================================================
                                 ;'
                                 ;'   File...... MIDI_Control.SXB
                                 ;'   Purpose... MIDI Digital Controller
                                 ;'   Author.... Jon Williams, Parallax EFX
                                 ;'   E-mail.... jwilliams@parallax.com
                                 ;'   Started... 
                                 ;'   Updated... 18 APR 2005
                                 ;'
                                 ;' =========================================================================


                                 ;' -------------------------------------------------------------------------
                                 ;' Program Description
                                 ;' -------------------------------------------------------------------------'
                                 ;'
                                 ;' "Listens" to a MIDI stream and will control outputs if Note value 
                                 ;' received is within the range of the device settings.
                                 ;'
                                 ;' Commands:
                                 ;' -- Note Off ($80)
                                 ;' -- Note On  ($90)
                                 ;' -- Controller message ($B0 $7B $00) = All off
                                 ;' -- System reset ($FF) = All off


                                 ;' -------------------------------------------------------------------------
                                 ;' Device Settings
                                 ;' -------------------------------------------------------------------------

DEVICE          SX28, OSCHS3, TURBO, STACKX, OPTIONX ;DEVICE          SX28, OSCHS3, TURBO, STACKX, OPTIONX

  ; DEFINE SYSTEM VARIABLES
__RAM         EQU  $00
__PARAMCNT    EQU  FSR
__PARAM1      EQU  $08
__PARAM2      EQU  $09
__PARAM3      EQU  $0A
__PARAM4      EQU  $0B
__TRISA       EQU  $FA
TRIS_A        EQU  $FA
__TRISB       EQU  $FB
TRIS_B        EQU  $FB
__TRISC       EQU  $FC
TRIS_C        EQU  $FC
__INTPARAMFSR EQU  $F5
__REMAINDER   EQU  $08

; Define ShiftIn & ShiftOut Constants
LSBFIRST      EQU  0
MSBFIRST      EQU  1
MSBPRE        EQU  0
LSBPRE        EQU  1
MSBPOST       EQU  2
LSBPOST       EQU  3


FREQ 50_000_000                  ;FREQ            50_000_000	
__FREQMHZ     EQU  50



                                 ;' -------------------------------------------------------------------------
                                 ;' IO Pins
                                 ;' -------------------------------------------------------------------------

MidiIn        EQU  RA.0          ;MidiIn      	VAR     RA.0                    ' from 6N138 interface

                                 ;'Octave		VAR	RA			' RA.1 - RA.3
                                 ;'CtrlLo		VAR	RB			' outs 0 - 7
                                 ;'CtrlHi		VAR	RC			' outs 8 - 11, chan select


                                 ;' -------------------------------------------------------------------------
                                 ;' Constants
                                 ;' -------------------------------------------------------------------------

                                 ;'Transpose	CON	3			' octave offset 
                                 ;						' -- for user keyboard

                                 ;' -------------------------------------------------------------------------
                                 ;' Variables
                                 ;' -------------------------------------------------------------------------

rxCount       EQU  0x0C          ;rxCount		VAR	Byte			' bits to receive

rxTimer       EQU  0x0D          ;rxTimer		VAR	Byte			' bit timer for ISR

rxByte        EQU  0x0E          ;rxByte		VAR	Byte			' serial byte

rxHead        EQU  0x0F          ;rxHead		VAR	Byte			' available slot

rxTail        EQU  0x10          ;rxTail		VAR	Byte			' next byte to read

rxBuf         EQU  0x30          ;rxBuf			VAR	Byte(16)	' circular buffer


midiStatus    EQU  0x11          ;midiStatus	VAR	Byte		' midi packet

midiDB1       EQU  0x12          ;midiDB1		VAR	Byte

midiDB2       EQU  0x13          ;midiDB2		VAR	Byte

runStatus     EQU  0x14          ;runStatus	VAR	Byte			' running status byte

hasStatus     EQU  runStatus.7   ;hasStatus	VAR	runStatus.7		' we have a status byte

channel       EQU  0x15          ;channel		VAR	Byte			' channel assignment

baseNote      EQU  0x16          ;baseNote	VAR	Byte			' base note (C) of port 0


regAddr       EQU  0x17          ;regAddr   VAR Byte  ' register address

temp1         EQU  0x18          ;temp1     VAR Byte  ' parameters

temp2         EQU  0x19          ;temp2			VAR	Byte


SMPTEtype     EQU  0x1A          ;SMPTEtype       VAR  Byte

Time          EQU  0x50          ;Time          	VAR  Byte(4)

Frames        EQU  Time+0        ;Frames          VAR  Time(0)

Seconds       EQU  Time+1        ;Seconds					VAR	 Time(1)

Minutes       EQU  Time+2        ;Minutes         VAR  Time(2)

Hours         EQU  Time+3        ;Hours           VAR  Time(3)

MaxFrames     EQU  0x1B          ;MaxFrames       VAR  Byte

temp3         EQU  0x1C          ;temp3						VAR	 Byte


                                 ;' -------------------------------------------------------------------------
ORG $000                         ;  INTERRUPT NOPRESERVE
  BANK $00                      

                                 ;' -------------------------------------------------------------------------

                                 ;' ISR is setup to receive N81, true mode.
                                 ;'
                                 ;' Notes:
                                 ;' -- MIDI baud is 31.25 kB, 32 uS per bit
                                 ;' -- Interrupt setup for 4x bit period for adequate sampling

ISR_Start:                       ;ISR_Start:

  ; ASM                          ;  ASM

    MOVB C, MidiIn				; sample serial input
    TEST rxCount				; receiving now?
    JNZ RX_Bit					; yes if rxCount > 0
    MOV W, #9					; start + 8 bits
    SC						; skip if no start bit
    MOV rxCount, W				; got start, load bit count
    MOV rxTimer, #6				; delay 1.5 bits
                                
RX_Bit:                         
    DJNZ rxTimer, ISR_Exit			; update bit timer
    MOV rxTimer, #4				; reload bit timer
    DEC rxCount					; mark bit done
    SZ						; if last bit, we;re done
    RR rxByte					; move bit into rxByte
    SZ                                          ; if not 0, get more bits
    JMP ISR_Exit                
                                
RX_Buffer:                      
    MOV FSR, #rxBuf				; get buffer address
    ADD FSR, rxHead				; point to head
    MOV IND, rxByte				; move rxByte to head
    INC rxHead					; update head
    CLRB rxHead.4    				; keep 0 - 15
  ; ENDASM                       ;  ENDASM


ISR_Exit:                        ;ISR_Exit:

  BANK $00                       ;  BANK $00

  MOV W,#56                      ;  RETURNINT 200					' 8 uS @ 20 MHz
  RETIW                         



                                 ;' =========================================================================
RESET __PROGSTART                ;  PROGRAM Start
__PROGSTART:                    
  JNB TO,@__RESTORETRIS         
  JB PD,@__POWERUP              
  CLR W                         
  MODE $09                      
  MOV !RB,W                     
  MOV FSR,W                     
  MODE $0F                      
  CJE FSR,#0,@__POWERUP         
  CJNE FSR,#$FF,@__RESTORETRIS  
__POWERUP:                      
  CLR RA                        
  CLR RB                        
  CLR RC                        
  MOV FSR,#$08                  
  SB FSR.3                      
  SETB FSR.4                    
  CLR IND                       
  IJNZ FSR,@$-3                 
  MOV FSR,#TRIS_A               
  MOV IND,#255                  
  INC FSR                       
  MOV IND,W                     
  INC FSR                       
  MOV IND,W                     
__RESTORETRIS:                  
  MOV FSR,#TRIS_A               
  MOV !RA,IND                   
  INC FSR                       
  MOV !RB,IND                   
  INC FSR                       
  MOV !RC,IND                   
  CLR FSR                       
  JMP @Start                    
  ORG $+2  ; FOR DEBUGGER       

                                 ;' =========================================================================


                                 ;' -------------------------------------------------------------------------
                                 ;' Subroutine Declarations
                                 ;' -------------------------------------------------------------------------

__STFO:                          ;STFO		  SUB	2		' shifts 2 bytes to the MAX7219
  JMP @STFO                     

__GETBYTE:                       ;GETBYTE		SUB	1			' pass addr of byte
  JMP @GETBYTE                  

__UPDTMAX:                       ;UPDTMAX		SUB
  JMP @UPDTMAX                  



                                 ;' -------------------------------------------------------------------------
                                 ;' Program Code
                                 ;' -------------------------------------------------------------------------

Start:                           ;Start:

  MOV FSR,#TRIS_A                ;  TRIS_A = %1111				' Serial + Base select
  MOV IND,#%1111                
  MODE $0F                      
  MOV !RA,IND                   
  BANK $00                      

  MOV FSR,#TRIS_B                ;  TRIS_B = %00000000				' lo outs
  CLR IND                       
  MODE $0F                      
  MOV !RB,IND                   
  BANK $00                      

  MOV FSR,#TRIS_C                ;  TRIS_C = %11110000				' hi outs, channel in
  MOV IND,#%11110000            
  MODE $0F                      
  MOV !RC,IND                   
  BANK $00                      

  MOV !OPTION,#$80               ;  OPTION = $80					' Interrupt on, 1:2


                                 ;'Initialize This addition must overrun program memory
  MOV __PARAM1,#$0C              ;	STFO $0C, $01
  MOV __PARAM2,#$01             
  CALL @__STFO                  

  MOV __PARAM1,#$09              ;	STFO $09, $FF
  MOV __PARAM2,#$FF             
  CALL @__STFO                  

  MOV __PARAM1,#$0A              ;	STFO $0A, $0F
  MOV __PARAM2,#$0F             
  CALL @__STFO                  

  MOV __PARAM1,#$0B              ;	STFO $0B, $07
  MOV __PARAM2,#$07             
  CALL @__STFO                  

  MOV __PARAM1,#$0F              ;	STFO $0F, $01	
  MOV __PARAM2,#$01             
  CALL @__STFO                  

  MOV __PARAM1,#4                ;	PAUSE 1000
  MOV __PARAM2,#232             
  MOV __PARAM3,#39              
  MOV __PARAM4,#233             
  DJNZ __PARAM4,@$              
  DJNZ __PARAM3,@$-3            
  DJNZ __PARAM2,@$-10           
  DJNZ __PARAM1,@$-13           

  MOV __PARAM1,#$0F              ;	STFO $0F, $00
  MOV __PARAM2,#$00             
  CALL @__STFO                  


                                 ;	' Set internal clock to 00
  MOV FSR,#Frames                ;	Frames = 0
  CLR IND                       
  BANK $00                      

  MOV FSR,#Seconds               ;	Seconds = 0
  CLR IND                       
  BANK $00                      

  MOV FSR,#Minutes               ;	Minutes = 0
  CLR IND                       
  BANK $00                      

  MOV FSR,#Hours                 ;	Hours = 0
  CLR IND                       
  BANK $00                      


  MOV temp1,#1                   ;	FOR Temp1 = 1 TO 8
__FOR_temp1_1:                  

  MOV __PARAM1,temp1             ;		STFO Temp1, $00
  MOV __PARAM2,#$00             
  CALL @__STFO                  

  CJAE temp1,#8,@__NEXT_temp1_1  ;	NEXT
  INC temp1                     
  JNZ @__FOR_temp1_1            
__NEXT_temp1_1:                 


  MOV temp1,#3                   ;	FOR Temp1 = 3 TO 7
__FOR_temp1_2:                  

  MOV __PARAM1,temp1             ;		Temp2 = Temp1//2
  AND __PARAM1,#1               
  MOV temp2,__PARAM1            

  CJNE temp2,#0,@__ELSE_1        ;		IF Temp2 = 0 THEN 

  JMP @Pass                      ;			GOTO Pass

__ELSE_1:                        ;		ENDIF
__ENDIF_1:                      

  MOV __PARAM1,temp1             ;		STFO Temp1, 128 ' This enables the decimal point
  MOV __PARAM2,#128             
  CALL @__STFO                  

Pass:                            ;Pass:

  CJAE temp1,#7,@__NEXT_temp1_2  ;	NEXT
  INC temp1                     
  JNZ @__FOR_temp1_2            
__NEXT_temp1_2:                 



Main:                            ;Main:

  JB hasStatus,@__ELSE_2         ;  IF hasStatus = 0 THEN				' running status?

  MOV __PARAM1,#midiStatus       ;    GETBYTE @midiStatus				'   no, get status
  CALL @__GETBYTE               

  JNB midiStatus.7,@Main         ;    IF midiStatus.7 = 0 THEN Main		' skip orphan data


Check_Sys:                       ;Check_Sys:

  CJAE midiStatus,#$F0,@Sys_Cmd  ;    IF midiStatus >= $F0 THEN Sys_Cmd		' handle system command

  MOV runStatus,midiStatus       ;    runStatus = midiStatus			' save regular status

__ELSE_2:                        ;  ENDIF
__ENDIF_2:                      


Get_DB1:                         ;Get_DB1:

  MOV midiStatus,runStatus       ;  midiStatus = runStatus			' refresh status byte

  MOV __PARAM1,#midiDB1          ;  GETBYTE @midiDB1				' get first data byte
  CALL @__GETBYTE               

  JNB midiDB1.7,@__ELSE_3        ;  IF midiDB1.7 = 1 THEN				' new status byte?

  MOV midiStatus,midiDB1         ;    midiStatus = midiDB1			'   yes, reposition

  JMP @Check_Sys                 ;    GOTO Check_Sys

__ELSE_3:                        ;  ENDIF
__ENDIF_3:                      


  JMP @Main                      ;  GOTO Main



                                 ;' *** MIDI Command Processing ***

Sys_Cmd:                         ;Sys_Cmd:

  CJNE midiStatus,#$F0,@__ELSE_4 ;  IF midiStatus = $F0 THEN			' handle SysEx bytes

  MOV __PARAM1,#midiDB1          ;    GETBYTE @midiDB1
  CALL @__GETBYTE               

  CJE midiDB1,#$F7,@Main         ;    IF midiDB1 = $F7 THEN Main			' wait for end ($F7)

  CLR runStatus                  ;    runStatus = $00

  JMP @Sys_Cmd                   ;    GOTO Sys_Cmd

__ELSE_4:                        ;  ENDIF
__ENDIF_4:                      


  CJNE midiStatus,#$FF,@__ELSE_5 ;  IF midiStatus = $FF THEN			' reset?

                                 ;    'CtrlLo = %00000000
                                 ;    'CtrlHi = %0000
  CLR runStatus                  ;    runStatus = $00

__ELSE_5:                        ;  ENDIF
__ENDIF_5:                      

                                 ;																'THIS IS THE MTC ADDITION
  CJNE midiStatus,#$F1,@__ELSE_6 ;	IF midiStatus = $F1 THEN			' MTC Quarter Frame

  MOV __PARAM1,#midiDB1          ;		GETBYTE @midiDB1 
  CALL @__GETBYTE               

  MOV __PARAM1,midiDB1           ;		temp2 = midiDB1 >> 4
  AND __PARAM1,#$F0             
  SWAP __PARAM1                 
  MOV temp2,__PARAM1            

  CJNE temp2,#0,@__ELSE_7        ;		IF temp2 = 0 THEN

  MOV W,midiDB1                  ;			temp3 = midiDB1 & $0F
  AND W,#$0F                    
  MOV temp3,W                   

  CALL @__UPDTMAX                ;			UPDTMAX

__ELSE_7:                        ;		ENDIF
__ENDIF_7:                      

  CJNE temp2,#1,@__ELSE_8        ;		IF temp2 = 1 THEN

  MOV W,midiDB1                  ;			temp1 = midiDB1 & $0F
  AND W,#$0F                    
  MOV temp1,W                   

   MOV __PARAM1,temp1            ;			temp1 = temp1 << 4
  AND __PARAM1,#$0F             
  SWAP __PARAM1                 
  MOV temp1,__PARAM1            

  MOV __PARAM2,temp1             ;			Frames =  temp1 + temp3
  ADD __PARAM2,temp3            
  MOV FSR,#Frames               
  MOV IND,__PARAM2              
  BANK $00                      

__ELSE_8:                        ;		ENDIF
__ENDIF_8:                      

  CJNE temp2,#2,@__ELSE_9        ;		IF temp2 = 2 THEN

  MOV W,midiDB1                  ;			temp3 = midiDB1 & $0F
  AND W,#$0F                    
  MOV temp3,W                   

__ELSE_9:                        ;		ENDIF
__ENDIF_9:                      

  CJNE temp2,#3,@__ELSE_10       ;		IF temp2 = 3 THEN

  MOV W,midiDB1                  ;			temp1 = midiDB1 & $0F
  AND W,#$0F                    
  MOV temp1,W                   

   MOV __PARAM1,temp1            ;			temp1 = temp1 << 4
  AND __PARAM1,#$0F             
  SWAP __PARAM1                 
  MOV temp1,__PARAM1            

  MOV __PARAM2,temp1             ;			Seconds =  temp1 + temp3
  ADD __PARAM2,temp3            
  MOV FSR,#Seconds              
  MOV IND,__PARAM2              
  BANK $00                      

__ELSE_10:                       ;		ENDIF
__ENDIF_10:                     

  CJNE temp2,#4,@__ELSE_11       ;		IF temp2 = 4 THEN

  MOV W,midiDB1                  ;			temp3 = midiDB1 & $0F
  AND W,#$0F                    
  MOV temp3,W                   

  CALL @__UPDTMAX                ;			UPDTMAX

__ELSE_11:                       ;		ENDIF
__ENDIF_11:                     

  CJNE temp2,#5,@__ELSE_12       ;		IF temp2 = 5 THEN

  MOV W,midiDB1                  ;			temp1 = midiDB1 & $0F
  AND W,#$0F                    
  MOV temp1,W                   

   MOV __PARAM1,temp1            ;			temp1 = temp1 << 4
  AND __PARAM1,#$0F             
  SWAP __PARAM1                 
  MOV temp1,__PARAM1            

  MOV __PARAM2,temp1             ;			Minutes =  temp1 + temp3
  ADD __PARAM2,temp3            
  MOV FSR,#Minutes              
  MOV IND,__PARAM2              
  BANK $00                      

__ELSE_12:                       ;		ENDIF
__ENDIF_12:                     

  CJNE temp2,#6,@__ELSE_13       ;		IF temp2 = 6 THEN

  MOV W,midiDB1                  ;			temp3 = midiDB1 & $0F
  AND W,#$0F                    
  MOV temp3,W                   

__ELSE_13:                       ;		ENDIF
__ENDIF_13:                     

  CJNE temp2,#7,@__ELSE_14       ;		IF temp2 = 7 THEN

  MOV W,midiDB1                  ;			temp1 = midiDB1 & $01
  AND W,#$01                    
  MOV temp1,W                   

   MOV __PARAM1,temp1            ;			temp1 = temp1 << 4
  AND __PARAM1,#$0F             
  SWAP __PARAM1                 
  MOV temp1,__PARAM1            

  MOV __PARAM2,temp1             ;			Hours =  temp1 + temp3
  ADD __PARAM2,temp3            
  MOV FSR,#Hours                
  MOV IND,__PARAM2              
  BANK $00                      

  MOVB SMPTEtype.1,midiDB1.2     ;    	SMPTEtype.1 = midiDB1.2

  MOVB SMPTEtype.0,midiDB1.1     ;    	SMPTEtype.0 = midiDB1.1

  CJNE SMPTEtype,#0,@__ELSE_15   ;			IF SMPTEtype = 0 THEN

  MOV MaxFrames,#24              ;				MaxFrames = 24

__ELSE_15:                       ;			ENDIF
__ENDIF_15:                     

  CJNE SMPTEtype,#1,@__ELSE_16   ;			IF SMPTEtype = 1 THEN

  MOV MaxFrames,#25              ;				MaxFrames = 25

__ELSE_16:                       ;			ENDIF
__ENDIF_16:                     

  CJNE SMPTEtype,#2,@__ELSE_17   ;			IF SMPTEtype = 2 THEN

  MOV MaxFrames,#30              ;				MaxFrames = 30			'Drop Frame

__ELSE_17:                       ;			ENDIF
__ENDIF_17:                     

  CJNE SMPTEtype,#3,@__ELSE_18   ;			IF SMPTEtype = 3 THEN

  MOV MaxFrames,#30              ;				MaxFrames = 30

__ELSE_18:                       ;			ENDIF
__ENDIF_18:                     

__ELSE_14:                       ;		ENDIF
__ENDIF_14:                     

__ELSE_6:                        ;	ENDIF
__ENDIF_6:                      


  JMP @Main                      ;  GOTO Main




                                 ;' -------------------------------------------------------------------------
                                 ;' Subroutine Code
                                 ;' -------------------------------------------------------------------------

UPDTMAX:                         ;UPDTMAX:

  MOV FSR,#Frames                ;	temp1 = Frames / 10
  MOV __PARAM1,IND              
  BANK $00                      
  MOV __PARAM2,#10              
  CLR __PARAM3                  
  TEST __PARAM2                 
  JZ @$+28                      
  CLR __PARAM4                  
  CLC                           
  SKIP                          
  RL __PARAM2                   
  INC __PARAM4                  
  JNB __PARAM2.7,@$-2           
  JMP @$+5                      
  CLC                           
  RL __PARAM3                   
  RR __PARAM2                   
  INC __PARAM3                  
  SUB __PARAM1,__PARAM2         
  JC @$+6                       
  DEC __PARAM3                  
  ADD __PARAM1,__PARAM2         
  DJNZ __PARAM4,@$-12           
  MOV temp1,__PARAM3            

  MOV __PARAM1,#$02              ;	STFO $02, temp1
  MOV __PARAM2,temp1            
  CALL @__STFO                  

  MOV __PARAM1,temp1             ;	temp1 = temp1 * 10
  MOV __PARAM2,#10              
  CLR __PARAM3                  
  MOV __PARAM4,#8               
  CLC                           
  RL __PARAM3                   
  RL __PARAM1                   
  JNC @$+5                      
  ADD __PARAM3,__PARAM2         
  DJNZ __PARAM4,@$-8            
  MOV temp1,__PARAM3            

  MOV FSR,#Frames                ;	temp2 = Frames - temp1
  MOV __PARAM1,IND              
  BANK $00                      
  MOV W,temp1                   
  MOV W,__PARAM1-W              
  MOV temp2,W                   
  MOV FSR,#Frames               
  MOV __PARA