Browse Source

Initial Commit of working program.

Stan Janssen 7 years ago
commit
1b97fe3ff8
1 changed files with 276 additions and 0 deletions
  1. 276 0
      midicount.asm

+ 276 - 0
midicount.asm

@@ -0,0 +1,276 @@
+    list p=16f88
+    errorlevel -306 ; no page boundary warnings
+    errorlevel -305 ; no default destination messages
+    errorlevel -302 ; no bank 0 warnings
+    errorlevel -202 ; no 'argument out of range' warning
+    
+    ;   Configuration Bits:
+    ;   32 1098 7654 3210 <-- bit number
+    ;   11 1111 0001 1000 <-- value
+    ;   Hex: 3f18
+    ;   For the 16F88, which has two configuration words, we must specify which word we are writing.
+
+    __config 0x2007 , 0x3f18        ; Config word one. All defaults, but no BOR, Power On Timer enabled, MCLR tied to Vdd internally
+    __config 0x2008 , 0x3ffc        ; Special features disabled.
+
+    include <p16f88.inc>
+    org     0
+    
+    ; Compiler instructions
+
+
+; Connect LEDs to PORTB 0,1,6,7
+
+led1    equ b'00000001'
+led2    equ b'00000010'
+led3    equ b'01000000'
+led4    equ b'10000000'
+
+RawCount    equ 20h
+BeatCount   equ 21h
+BarCount    equ 22h
+MsgBuffer   equ 23h
+CalcBuffer  equ 24h
+
+start
+    ; Set the internal oscillator frequency
+    bsf     STATUS, RP0
+    movlw   b'01110110'     ; Internal frequency = 8MHz, OSTS uses INTOSC.
+    movwf   OSCCON
+    
+
+    ; Set PORTA as output, PORTB as output except for the RX pin    
+    clrf    ANSEL   ; Turn Off Analog Converter
+    clrf    TRISA   ; Make PORTA output
+    clrf    TRISB   ; Make PORTB output
+    bsf     TRISB, 2
+
+    ; Set up UART to 31.250 kBaud for MIDI.
+    ; We're using the internal 8MHz oscillator clock.
+    ; Baud low speed, X=3 (gives exact match)
+    
+    movlw   3h
+    movwf   SPBRG
+    
+    bcf     TXSTA, BRGH     ; Low Speed
+    bcf     TXSTA, SYNC
+    
+    bcf     STATUS, RP0
+    
+    bsf     RCSTA, SPEN     ; Enable Serial Port
+    bsf     RCSTA, CREN     ; Enable Continuous Receive
+    
+    ; Initialization of the setup
+
+reset   
+    clrf    PORTB       ; Turn off all beat LEDs
+    clrf    PORTA       ; Set 7 seg to zero.
+
+    movlw   d'25'       ; Set the Raw Count to 24
+    movwf   RawCount
+
+    clrf    BarCount
+    clrf    BeatCount   
+            
+    ;   There are two options for counting the beats: 
+    ;   1. Using the MIDI Timing Clock
+    ;
+    ;   MIDI Sends the Timing Clock message 24 times per quarter note.
+    ;   We must therefore count to 24 before changing the beat
+    ;   And 4 beats to increase the bar
+    ;
+    ;   The Timing Clock message is b'11111000'
+    ;
+    ;   At the start of the sequence, the message b'11111010' will be sent.
+    ;   We can use this to reset the counter? This seems like a mess.
+    ;
+    ;
+    ;   2. Using the Song Position Pointer
+    ;   This increases 4 times per beat and is a 14-bit counter.
+    ;   Max 16384 MIDI beats =  = 4096 music beats = 1024 bars > 30 minutes @ 120 bpm.
+    ;   
+    ;   We can then decide to use absolute timing or relative (counting).
+    ;   Let's see where we get with absolute timing.
+    ;   
+    ;   The Song Position Counter message is b'11110010' = 0F2h
+    ;   Followed by the LSB (7 bit value) and the MSB (7 bit value)
+    ;   So we get 128 16th notes = 32 beats = 4 bars per LSB cycle.
+    ;   We are only interested in every 4 of these messages.
+    ;   We could therefore do two RRFs on this register and compare to the previous one.
+    ;
+    ;   For the bar counter, we are interested in every 16 of these messages.
+    ;   We could do 4 RRFs on this register and read the bar count. 
+    ;   This means we have 8 bars on this LSB register, which is what we need.
+    ;
+    ;   MIDI devices will send this thing when they scrub position.
+
+wait_for_start
+    btfss   PIR1, RCIF
+    goto    wait_for_start
+    movf    RCREG, W
+    movwf   MsgBuffer
+    sublw   0FA             ; Is this a Start message?
+    btfsc   STATUS, Z
+    goto    get_started
+    
+    movf    MsgBuffer, W
+    sublw   0FB             ; Is it a Continue message?
+    btfsc   STATUS, Z
+    goto    loop
+    
+    movf    MsgBuffer, W
+    sublw   0F2             ; Is it a Song Position Pointer message?
+    btfsc   STATUS, Z
+    goto    wait_for_position
+
+    goto    wait_for_start  ; If it is neither of these, ignore.
+
+wait_for_position
+    ; If we have received a Song Position Counter, 
+    ; we will now receive two bytes that represent the song position. 
+    ; The resolution is 4 per quarter note, so 16 per bar.
+    ; We use this to calculate where we are by shifting the bits to the right.
+    ; Since we only need the LSB (which comes first), we can safely ignore the second one.
+    ; This is a 7-bit value, so the maximum can be 127.
+    ; This works out to exactly 8 bars in this 7-bit register, 
+    ; so it's easy to convert to beats and bars in this case.
+    
+    btfss   PIR1, RCIF
+    goto    wait_for_position
+    
+    movf    RCREG, W
+    movwf   MsgBuffer
+    movwf   CalcBuffer
+    
+    
+    ; Derive the bar number by dividing by 16:
+    rrf     CalcBuffer, f
+    rrf     CalcBuffer, f
+    rrf     CalcBuffer, f
+    rrf     CalcBuffer, W
+    andlw   b'00001111'     ; Mask whatever was carried in
+    movwf   BarCount        ; Copy to BarCount
+    
+    sublw   8               ; Are we past bar 8?
+    btfsc   STATUS, Z
+    clrf    BarCount        ; If so, reset bar count.
+    
+    incf    BarCount, W
+    movwf   PORTA           ; Immediately display on the indicator
+    
+    ; Get the beat position
+    ; The Song Position Counter must be multiplied by 6 to get the Count as we know it in 
+    ; the clock signal. The clock uses 24 per quarter, the Song Position just 4 per quarter.
+    ; We first have to get the beat we are in, 
+    ; then reconcile the remainder of the position to the RawCount.
+    
+    
+    ; Get the current beat: divide by 4.
+    movf    MsgBuffer, W
+    andlw   b'00001111'
+    movwf   CalcBuffer
+    rrf     CalcBuffer, f
+    rrf     CalcBuffer, W
+    andlw   b'00111111'
+    movwf   BeatCount
+
+    ; Get the position within the beat. 
+    movf    MsgBuffer, W
+    andlw   b'00000011'     ; Only the remainder part
+    movwf   CalcBuffer
+    bcf     STATUS, C
+    rlf     CalcBuffer, f   ; Multiply by two and update CalcBuffer
+    rlf     CalcBuffer, W   ; Multiply by two and keep in W
+    addwf   CalcBuffer, W   ; Now we have the 24-step raw count in W. Subtract from 24.
+    
+    sublw   d'24'
+    btfsc   STATUS, Z
+    movlw   d'24'
+    
+    addlw   1
+    movwf   RawCount
+
+    call    get_beat        ; Display the new beat count on the LEDS
+    movwf   PORTB
+
+    goto    wait_for_start
+
+get_started
+    movlw   d'25'       ; Set the Raw Count to 25
+    movwf   RawCount
+
+    clrf    BarCount
+    clrf    BeatCount
+    
+    movlw   led1
+    movwf   PORTB           ; Display the new Beat Count 
+
+    incf    BarCount, W
+    movwf   PORTA           ; Display the new Bar Count
+    
+    goto    loop
+
+
+loop                        ; We will now receive 24 MIDI clocks per beat.
+    btfss   PIR1, RCIF      ; Check if there is a new MIDI message
+    goto    loop
+
+    movf    RCREG, W
+    movwf   MsgBuffer       ; Copy the MIDI message to the buffer
+    sublw   0FC             ; Is it a Stop message?
+    btfsc   STATUS, Z
+    goto    wait_for_start
+
+    movf    MsgBuffer, W    ; Read MIDI message from the buffer
+    sublw   0F8             ; Is this a MIDI clock beat?
+    btfss   STATUS, Z       
+    goto    loop            ; If it is neither a Stop nor a Clock message, go back and wait for more.
+
+    decf    RawCount, f     ; Counting down from 24 every time.
+    
+    movf    RawCount, W
+    sublw   d'18'
+    btfsc   STATUS, C
+    clrf    PORTB           ; If we are at a quarter of the beat, clear the leds
+
+    movf    RawCount, W
+    btfss   STATUS, Z
+    goto    loop            ; If we have not reached the next beat, go back to the loop
+
+    movlw   d'24'           ; Reset the Raw Counter
+    movwf   RawCount
+    
+    incf    BeatCount, f    ; Increase the Beat Count
+    movf    BeatCount, W
+    sublw   4               ; Are we past beat 4?
+    btfsc   STATUS, Z
+    call    next_bar
+
+    call    get_beat
+    movwf   PORTB
+    
+    goto    loop
+    
+
+next_bar
+    clrf    BeatCount
+    incf    BarCount, f
+    
+    movf    BarCount, W
+    sublw   8
+    btfsc   STATUS, Z
+    clrf    BarCount
+    
+    incf    BarCount, W
+    movwf   PORTA
+    return
+
+get_beat
+    movf    BeatCount, W
+    addwf   PCL, f
+    retlw   led1
+    retlw   led2
+    retlw   led3
+    retlw   led4
+    
+    end