midicount.asm 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. list p=16f88
  2. errorlevel -306 ; no page boundary warnings
  3. errorlevel -305 ; no default destination messages
  4. errorlevel -302 ; no bank 0 warnings
  5. errorlevel -202 ; no 'argument out of range' warning
  6. ; Configuration Bits:
  7. ; 32 1098 7654 3210 <-- bit number
  8. ; 11 1111 0001 1000 <-- value
  9. ; Hex: 3f18
  10. ; For the 16F88, which has two configuration words, we must specify which word we are writing.
  11. __config 0x2007 , 0x3f18 ; Config word one. All defaults, but no BOR, Power On Timer enabled, MCLR tied to Vdd internally
  12. __config 0x2008 , 0x3ffc ; Special features disabled.
  13. include <p16f88.inc>
  14. org 0
  15. ; Compiler instructions
  16. ; Connect LEDs to PORTB 0,1,6,7
  17. led1 equ b'00000001'
  18. led2 equ b'00000010'
  19. led3 equ b'01000000'
  20. led4 equ b'10000000'
  21. RawCount equ 20h
  22. BeatCount equ 21h
  23. BarCount equ 22h
  24. MsgBuffer equ 23h
  25. CalcBuffer equ 24h
  26. start
  27. ; Set the internal oscillator frequency
  28. bsf STATUS, RP0
  29. movlw b'01110110' ; Internal frequency = 8MHz, OSTS uses INTOSC.
  30. movwf OSCCON
  31. ; Set PORTA as output, PORTB as output except for the RX pin
  32. clrf ANSEL ; Turn Off Analog Converter
  33. clrf TRISA ; Make PORTA output
  34. clrf TRISB ; Make PORTB output
  35. bsf TRISB, 2
  36. ; Set up UART to 31.250 kBaud for MIDI.
  37. ; We're using the internal 8MHz oscillator clock.
  38. ; Baud low speed, X=3 (gives exact match)
  39. movlw 3h
  40. movwf SPBRG
  41. bcf TXSTA, BRGH ; Low Speed
  42. bcf TXSTA, SYNC
  43. bcf STATUS, RP0
  44. bsf RCSTA, SPEN ; Enable Serial Port
  45. bsf RCSTA, CREN ; Enable Continuous Receive
  46. ; Initialization of the setup
  47. reset
  48. clrf PORTB ; Turn off all beat LEDs
  49. clrf PORTA ; Set 7 seg to zero.
  50. movlw d'25' ; Set the Raw Count to 24
  51. movwf RawCount
  52. clrf BarCount
  53. clrf BeatCount
  54. ; There are two options for counting the beats:
  55. ; 1. Using the MIDI Timing Clock
  56. ;
  57. ; MIDI Sends the Timing Clock message 24 times per quarter note.
  58. ; We must therefore count to 24 before changing the beat
  59. ; And 4 beats to increase the bar
  60. ;
  61. ; The Timing Clock message is b'11111000'
  62. ;
  63. ; At the start of the sequence, the message b'11111010' will be sent.
  64. ; We can use this to reset the counter? This seems like a mess.
  65. ;
  66. ;
  67. ; 2. Using the Song Position Pointer
  68. ; This increases 4 times per beat and is a 14-bit counter.
  69. ; Max 16384 MIDI beats = = 4096 music beats = 1024 bars > 30 minutes @ 120 bpm.
  70. ;
  71. ; We can then decide to use absolute timing or relative (counting).
  72. ; Let's see where we get with absolute timing.
  73. ;
  74. ; The Song Position Counter message is b'11110010' = 0F2h
  75. ; Followed by the LSB (7 bit value) and the MSB (7 bit value)
  76. ; So we get 128 16th notes = 32 beats = 4 bars per LSB cycle.
  77. ; We are only interested in every 4 of these messages.
  78. ; We could therefore do two RRFs on this register and compare to the previous one.
  79. ;
  80. ; For the bar counter, we are interested in every 16 of these messages.
  81. ; We could do 4 RRFs on this register and read the bar count.
  82. ; This means we have 8 bars on this LSB register, which is what we need.
  83. ;
  84. ; MIDI devices will send this thing when they scrub position.
  85. wait_for_start
  86. btfss PIR1, RCIF
  87. goto wait_for_start
  88. movf RCREG, W
  89. movwf MsgBuffer
  90. sublw 0FA ; Is this a Start message?
  91. btfsc STATUS, Z
  92. goto get_started
  93. movf MsgBuffer, W
  94. sublw 0FB ; Is it a Continue message?
  95. btfsc STATUS, Z
  96. goto loop
  97. movf MsgBuffer, W
  98. sublw 0F2 ; Is it a Song Position Pointer message?
  99. btfsc STATUS, Z
  100. goto wait_for_position
  101. goto wait_for_start ; If it is neither of these, ignore.
  102. wait_for_position
  103. ; If we have received a Song Position Counter,
  104. ; we will now receive two bytes that represent the song position.
  105. ; The resolution is 4 per quarter note, so 16 per bar.
  106. ; We use this to calculate where we are by shifting the bits to the right.
  107. ; Since we only need the LSB (which comes first), we can safely ignore the second one.
  108. ; This is a 7-bit value, so the maximum can be 127.
  109. ; This works out to exactly 8 bars in this 7-bit register,
  110. ; so it's easy to convert to beats and bars in this case.
  111. btfss PIR1, RCIF
  112. goto wait_for_position
  113. movf RCREG, W
  114. movwf MsgBuffer
  115. movwf CalcBuffer
  116. ; Derive the bar number by dividing by 16:
  117. rrf CalcBuffer, f
  118. rrf CalcBuffer, f
  119. rrf CalcBuffer, f
  120. rrf CalcBuffer, W
  121. andlw b'00001111' ; Mask whatever was carried in
  122. movwf BarCount ; Copy to BarCount
  123. sublw 8 ; Are we past bar 8?
  124. btfsc STATUS, Z
  125. clrf BarCount ; If so, reset bar count.
  126. incf BarCount, W
  127. movwf PORTA ; Immediately display on the indicator
  128. ; Get the beat position
  129. ; The Song Position Counter must be multiplied by 6 to get the Count as we know it in
  130. ; the clock signal. The clock uses 24 per quarter, the Song Position just 4 per quarter.
  131. ; We first have to get the beat we are in,
  132. ; then reconcile the remainder of the position to the RawCount.
  133. ; Get the current beat: divide by 4.
  134. movf MsgBuffer, W
  135. andlw b'00001111'
  136. movwf CalcBuffer
  137. rrf CalcBuffer, f
  138. rrf CalcBuffer, W
  139. andlw b'00111111'
  140. movwf BeatCount
  141. ; Get the position within the beat.
  142. movf MsgBuffer, W
  143. andlw b'00000011' ; Only the remainder part
  144. movwf CalcBuffer
  145. bcf STATUS, C
  146. rlf CalcBuffer, f ; Multiply by two and update CalcBuffer
  147. rlf CalcBuffer, W ; Multiply by two and keep in W
  148. addwf CalcBuffer, W ; Now we have the 24-step raw count in W. Subtract from 24.
  149. sublw d'24'
  150. btfsc STATUS, Z
  151. movlw d'24'
  152. addlw 1
  153. movwf RawCount
  154. call get_beat ; Display the new beat count on the LEDS
  155. movwf PORTB
  156. goto wait_for_start
  157. get_started
  158. movlw d'25' ; Set the Raw Count to 25
  159. movwf RawCount
  160. clrf BarCount
  161. clrf BeatCount
  162. movlw led1
  163. movwf PORTB ; Display the new Beat Count
  164. incf BarCount, W
  165. movwf PORTA ; Display the new Bar Count
  166. goto loop
  167. loop ; We will now receive 24 MIDI clocks per beat.
  168. btfss PIR1, RCIF ; Check if there is a new MIDI message
  169. goto loop
  170. movf RCREG, W
  171. movwf MsgBuffer ; Copy the MIDI message to the buffer
  172. sublw 0FC ; Is it a Stop message?
  173. btfsc STATUS, Z
  174. goto wait_for_start
  175. movf MsgBuffer, W ; Read MIDI message from the buffer
  176. sublw 0F8 ; Is this a MIDI clock beat?
  177. btfss STATUS, Z
  178. goto loop ; If it is neither a Stop nor a Clock message, go back and wait for more.
  179. decf RawCount, f ; Counting down from 24 every time.
  180. movf RawCount, W
  181. sublw d'18'
  182. btfsc STATUS, C
  183. clrf PORTB ; If we are at a quarter of the beat, clear the leds
  184. movf RawCount, W
  185. btfss STATUS, Z
  186. goto loop ; If we have not reached the next beat, go back to the loop
  187. movlw d'24' ; Reset the Raw Counter
  188. movwf RawCount
  189. incf BeatCount, f ; Increase the Beat Count
  190. movf BeatCount, W
  191. sublw 4 ; Are we past beat 4?
  192. btfsc STATUS, Z
  193. call next_bar
  194. call get_beat
  195. movwf PORTB
  196. goto loop
  197. next_bar
  198. clrf BeatCount
  199. incf BarCount, f
  200. movf BarCount, W
  201. sublw 8
  202. btfsc STATUS, Z
  203. clrf BarCount
  204. incf BarCount, W
  205. movwf PORTA
  206. return
  207. get_beat
  208. movf BeatCount, W
  209. addwf PCL, f
  210. retlw led1
  211. retlw led2
  212. retlw led3
  213. retlw led4
  214. end