Subversion Repositories svn.mios

Rev

Rev 1 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
44 tk 1
; $Id: sid_midi.inc 44 2008-01-30 21:39:30Z tk $
1 tk 2
;
3
; MIDIbox SID
4
; MIDI Interface part
5
;
6
; Activate this #define to measure the performance with a scope
7
; the used port has to be specified here - comment out for no measuring
8
;#define SID_ME_MEASURE_PERFORMANCE_PORT   LATC, 3
9
;
10
; ==========================================================================
11
;
12
;  Copyright 1998-2007 Thorsten Klose (tk@midibox.org)
13
;  Licensed for personal non-commercial use only.
14
;  All other rights reserved.
15
;
16
; ==========================================================================
17
 
18
 
19
;; experimental arp behaviour for improved playing:
20
;; even when HOLD mode not active, a note off doesn't remove notes in stack
21
;; the notes of released keys will be removed from stack once a *new* note is played
22
#define EXP_ARP_BEHAVIOUR 1
23
 
24
;; --------------------------------------------------------------------------
25
;;  This function is called by SID_MPROC when a complete MIDI event has been
26
;;  received
27
;;  Input:
28
;;     o first  MIDI event byte in MIOS_PARAMETER1
29
;;     o second MIDI event byte in MIOS_PARAMETER2
30
;;     o third  MIDI event byte in MIOS_PARAMETER3
31
;; --------------------------------------------------------------------------
32
SID_MIDI_NotifyReceivedEvent
33
	;; store channel in SID_CURRENT_CHANNEL
34
	movf	MIOS_PARAMETER1, W
35
	andlw	0x0f
36
	movff	WREG, SID_CURRENT_CHANNEL
37
 
38
	;; store MIDI event byte 2 and 3 in SID_MIDI_PARAMETER[12]
39
	movff	MIOS_PARAMETER2, SID_MIDI_PARAMETER1
40
	movff	MIOS_PARAMETER3, SID_MIDI_PARAMETER2
41
 
42
#ifdef SID_ME_MEASURE_PERFORMANCE_PORT
43
	bsf	SID_ME_MEASURE_PERFORMANCE_PORT
44
#endif
45
 
46
	;; disable IRQs to avoid inconsistencies and to allow the use of FSR2
47
	IRQ_DISABLE
48
 
49
	;; call MIDI event handler depending on event type
50
	rcall	SID_MIDI_NotifyReceivedEventJmp
51
 
52
	;; enable IRQs again
53
	IRQ_ENABLE
54
 
55
#ifdef SID_ME_MEASURE_PERFORMANCE_PORT
56
	bcf	SID_ME_MEASURE_PERFORMANCE_PORT
57
#endif
58
 
59
	return
60
 
61
SID_MIDI_NotifyReceivedEventJmp
62
	;; branch to appr. SID routine depending on received event
63
	swapf	MIOS_PARAMETER1, W
64
	andlw	0x07
65
	JUMPTABLE_2BYTES_UNSECURE
66
	rgoto	SID_MIDI_NoteOff
67
	rgoto	SID_MIDI_NoteOn
68
	rgoto	SID_MIDI_AfterTouch
69
	rgoto	SID_MIDI_CC
70
	rgoto	SID_MIDI_ProgramChange
71
	rgoto	SID_MIDI_PolyAfterTouch
72
	rgoto	SID_MIDI_PitchBender
73
	return
74
 
75
 
76
;; --------------------------------------------------------------------------
77
;;  This function is called to forward a Note On event to the synthesizer
78
;;  Input:
79
;;     o MIDI Voice in SID_CURRENT_MIDI_VOICE
80
;;     o MIDI channel in SID_CURRENT_CHANNEL
81
;;     o note number in SID_MIDI_PARAMETER1
82
;;     o velocity in SID_MIDI_PARAMETER2
83
;; --------------------------------------------------------------------------
84
SID_MIDI_NoteOn
85
	SET_BSR	SID_BASE
86
	movf	SID_MIDI_PARAMETER2, W, BANKED	; branch to NoteOff if velocity is zero
87
	skpnz
88
	rgoto	SID_MIDI_NoteOff
89
 
90
	;; branch depending on engine
91
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
92
	andlw	0x03
93
	JUMPTABLE_4BYTES_UNSECURE
94
	goto	SID_MIDI_L_NoteOn
95
	goto	SID_MIDI_B_NoteOn
96
	goto	SID_MIDI_D_NoteOn
97
	goto	SID_MIDI_M_NoteOn
98
 
99
 
100
;; --------------------------------------------------------------------------
101
;;  This function is called to forward a Note Off event to the synthesizer
102
;;  Input:
103
;;     o MIDI channel in SID_CURRENT_CHANNEL
104
;;     o note number in SID_MIDI_PARAMETER1
105
;;     o velocity in SID_MIDI_PARAMETER2
106
;; --------------------------------------------------------------------------
107
SID_MIDI_NoteOff
108
	;; branch depending on engine
109
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
110
	andlw	0x03
111
	JUMPTABLE_4BYTES_UNSECURE
112
	goto	SID_MIDI_L_NoteOff
113
	goto	SID_MIDI_B_NoteOff
114
	goto	SID_MIDI_D_NoteOff
115
	goto	SID_MIDI_M_NoteOff
116
 
117
 
118
;; --------------------------------------------------------------------------
119
;;  This function is called to forward a PitchBender event to the synthesizer
120
;;  Input:
121
;;     o MIDI channel in SID_CURRENT_CHANNEL
122
;;     o low byte in SID_MIDI_PARAMETER1
123
;;     o high byte in SID_MIDI_PARAMETER2
124
;; --------------------------------------------------------------------------
125
SID_MIDI_PitchBender
126
	;; derive 8bit value from PitchBender L/H byte
127
	SET_BSR	SID_BASE
128
	clrc
129
	btfsc	SID_MIDI_PARAMETER1, 6, BANKED
130
	setc
131
	rlf	SID_MIDI_PARAMETER2, W, BANKED
132
	movwf	SID_MIDI_PARAMETER1, BANKED
133
 
134
	;; forward to knob handler (only checks for MIDI channel of first MIDI voice)
135
	clrf	SID_CURRENT_MIDIVOICE, BANKED
136
	lfsr	FSR0, SID_MV1_BASE
137
 
138
	;; check for MIDI channel
139
	movlw	SID_MVx_MIDI_CHANNEL
140
	movf	PLUSW0, W
141
	cpfseq	SID_CURRENT_CHANNEL, BANKED
142
	rgoto SID_MIDI_PitchBender_NoKnob
143
SID_MIDI_PitchBender_Knob
144
	;; special measure for bassline: select first instrument
145
	movff	CS_MENU_SELECTED_SID_LR, TMP1		; taken from CS_MENU_SELECTED_SID_LR... dirty :-/
146
	movlw	0x01
147
	movwf	CS_MENU_SELECTED_SID_LR
148
 
149
	;; copy converted pitch bender into mod matrix source
150
	movff	SID_MIDI_PARAMETER1, MIOS_PARAMETER1
151
	movlw	SID_KNOB_PB
152
	call	SID_KNOB_SetValue
153
	movff	TMP1, CS_MENU_SELECTED_SID_LR
154
SID_MIDI_PitchBender_NoKnob
155
 
156
	;; branch depending on engine
157
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
158
	andlw	0x03
159
	JUMPTABLE_4BYTES_UNSECURE
160
	goto	SID_MIDI_L_PitchBender
161
	goto	SID_MIDI_B_PitchBender
162
	goto	SID_MIDI_D_PitchBender
163
	goto	SID_MIDI_M_PitchBender
164
 
165
 
166
;; --------------------------------------------------------------------------
167
;;  This function is called to forward a Controller event to the synthesizer
168
;;  Input:
169
;;     o MIDI channel in SID_CURRENT_CHANNEL
170
;;     o CC number in SID_MIDI_PARAMETER1
171
;;     o CC value in SID_MIDI_PARAMETER2
172
;; --------------------------------------------------------------------------
173
SID_MIDI_CC
174
 
175
	;; special treatment for CC#0 (bank change)
176
	SET_BSR	SID_BASE
177
	movf	SID_MIDI_PARAMETER1, W, BANKED
178
	bnz	SID_MIDI_CC_No00
179
SID_MIDI_CC_00
180
	;; exit if channel doesn't match
181
 
182
	;; exit if bank number >= 8
183
	movlw	0x08
184
	cpfslt	SID_MIDI_PARAMETER2, BANKED
185
	return
186
 
187
	movff	MIOS_PARAMETER3, SID_BANK
188
 
189
	movf	SID_MIDI_DEVICE, W	; skip if device ID != 0x00
190
	bnz	SID_MIDI_CC_00_NoCS
191
	;; notify the patch change to the control surface
192
	goto	CS_MENU_MS_NotifyBankChange
193
 
194
SID_MIDI_CC_00_NoCS
195
 
196
	;; exit routine if MIDI channel doesn't match
197
	movf	SID_CURRENT_CHANNEL, W, BANKED
198
	cpfseq	SID_MV1_BASE + SID_MVx_MIDI_CHANNEL, BANKED
199
	return
200
 
201
	;; transfer EEPROM/BankStick content into patch buffer and re-init patch
202
	call	SID_PATCH_LoadPatchBuffer
203
	goto	USER_DISPLAY_Init
204
 
205
SID_MIDI_CC_No00
206
 
207
	;; NRPN address/LSB?
208
	;; always stored independent from channel for more consistent usage
209
	;; (e.g. for Multi Engine, where up to 6 instruments could be assigned
210
	;; to different MIDI channels)
211
 
212
	;; check if NRPN address (LSB) has been received
213
	movlw	0x62
214
	cpfseq	SID_MIDI_PARAMETER1, BANKED
215
	rgoto SID_MIDI_CC_NoNRPNAddrL
216
SID_MIDI_CC_NRPNAddrL
217
	lfsr	FSR1, NRPN_ADDRESS_LSB
218
	movf	SID_CURRENT_CHANNEL, W, BANKED
219
	movff	SID_MIDI_PARAMETER2, PLUSW1
220
	return
221
SID_MIDI_CC_NoNRPNAddrL
222
 
223
	;; check if NRPN address (MSB) has been received
224
	movlw	0x63
225
	cpfseq	SID_MIDI_PARAMETER1, BANKED
226
	rgoto SID_MIDI_CC_NoNRPNAddrH
227
SID_MIDI_CC_NRPNAddrH
228
	lfsr	FSR1, NRPN_ADDRESS_MSB
229
	movf	SID_CURRENT_CHANNEL, W, BANKED
230
	movff	SID_MIDI_PARAMETER2, PLUSW1
231
	return
232
SID_MIDI_CC_NoNRPNAddrH
233
 
234
	;; check if NRPN value (LSB) has been received
235
	movlw	0x26
236
	cpfseq	SID_MIDI_PARAMETER1, BANKED
237
	rgoto SID_MIDI_CC_NoNRPNDataL
238
SID_MIDI_CC_NRPNDataL
239
	lfsr	FSR1, NRPN_DATA_LSB
240
	movf	SID_CURRENT_CHANNEL, W, BANKED
241
	movff	SID_MIDI_PARAMETER2, PLUSW1
242
	return
243
SID_MIDI_CC_NoNRPNDataL
244
 
245
	;; NRPN value MSB is engine specific and therefore handled in sid_midi_*.inc
246
 
247
 
248
	;; check for knob values
249
	;; (only receiving on channel of first MIDI voice)
250
	clrf	SID_CURRENT_MIDIVOICE, BANKED
251
	lfsr	FSR0, SID_MV1_BASE
252
 
253
	;; check for MIDI channel
254
	movlw	SID_MVx_MIDI_CHANNEL
255
	movf	PLUSW0, W
256
	cpfseq	SID_CURRENT_CHANNEL, BANKED
257
	rgoto SID_MIDI_CC_NoKnob
258
SID_MIDI_CC_Knob
259
	;; Modwheel = Knob #1
260
	decf	SID_MIDI_PARAMETER1, W
261
	bnz	SID_MIDI_CC_Knob_NotMdW
262
SID_MIDI_CC_Knob_MdW
263
	clrc
264
	rlf	SID_MIDI_PARAMETER2, W, BANKED
265
	movwf	MIOS_PARAMETER1
266
	movlw	SID_KNOB_1
267
	goto	SID_KNOB_SetValue
268
SID_MIDI_CC_Knob_NotMdW
269
 
270
	;; CC17..CC20 = Knob #2-5
271
	movlw	0x10-1
272
	cpfsgt	SID_MIDI_PARAMETER1, BANKED
273
	rgoto SID_MIDI_CC_Knob_NotK2345
274
	movlw	0x13+1
275
	cpfslt	SID_MIDI_PARAMETER1, BANKED
276
	rgoto SID_MIDI_CC_Knob_NotK2345
277
SID_MIDI_CC_Knob_2345
278
	clrc
279
	rlf	SID_MIDI_PARAMETER2, W, BANKED
280
	movwf	MIOS_PARAMETER1
281
	movf	SID_MIDI_PARAMETER1, W, BANKED
282
	addlw	-0x10 + SID_KNOB_2
283
	goto	SID_KNOB_SetValue
284
SID_MIDI_CC_Knob_NotK2345
285
 
286
SID_MIDI_CC_NoKnob
287
 
288
 
289
	;; branch depending on engine
290
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
291
	andlw	0x03
292
	JUMPTABLE_4BYTES_UNSECURE
293
	goto	SID_MIDI_L_CC
294
	goto	SID_MIDI_B_CC
295
	goto	SID_MIDI_D_CC
296
	goto	SID_MIDI_M_CC
297
 
298
 
299
;; --------------------------------------------------------------------------
300
;;  This function is called to forward a Program Change event to the synthesizer
301
;;  Input:
302
;;     o MIDI channel in SID_CURRENT_CHANNEL
303
;;     o program number in SID_MIDI_PARAMETER1
304
;; --------------------------------------------------------------------------
305
SID_MIDI_ProgramChange
306
	movf	SID_MIDI_DEVICE, W	; skip if device ID != 0x00
307
	bnz	SID_MIDI_L_ProgramChange_NoCS
308
	;; notify the patch change to the control surface
309
	goto	CS_MENU_MS_NotifyProgramChange
310
SID_MIDI_L_ProgramChange_NoCS
311
	return
312
 
313
 
314
;; --------------------------------------------------------------------------
315
;;  This function is called to forward a Poly Aftertouch event to the synthesizer
316
;;  Input:
317
;;     o MIDI channel in SID_CURRENT_CHANNEL
318
;;     o Note number in SID_MIDI_PARAMETER1
319
;;     o Aftertouch value in SID_MIDI_PARAMETER2
320
;; --------------------------------------------------------------------------
321
SID_MIDI_PolyAfterTouch
322
	movff	SID_MIDI_PARAMETER2, SID_MIDI_PARAMETER1
323
	rgoto	SID_MIDI_AfterTouch
324
 
325
;; --------------------------------------------------------------------------
326
;;  This function is called to forward a Aftertouch event to the synthesizer
327
;;  Input:
328
;;     o MIDI channel in SID_CURRENT_CHANNEL
329
;;     o Aftertouch value in SID_MIDI_PARAMETER1
330
;; --------------------------------------------------------------------------
331
SID_MIDI_AfterTouch
332
	;; only check channel of first MIDI voice
333
	SET_BSR	SID_BASE
334
	clrf	SID_CURRENT_MIDIVOICE, BANKED
335
	lfsr	FSR0, SID_MV1_BASE
336
 
337
	;; check for MIDI channel
338
	movlw	SID_MVx_MIDI_CHANNEL
339
	movf	PLUSW0, W
340
	cpfseq	SID_CURRENT_CHANNEL, BANKED
341
	rgoto SID_MIDI_AfterTouch_End
342
 
343
	;; forward to knob handler
344
	clrc
345
	rlf	SID_MIDI_PARAMETER1, W, BANKED
346
	movwf	MIOS_PARAMETER1
347
	movlw	SID_KNOB_ATH
348
	call	SID_KNOB_SetValue
349
 
350
SID_MIDI_AfterTouch_End
351
	return
352
 
353
 
354
;; --------------------------------------------------------------------------
355
;;  help routines used by all engines
356
;; --------------------------------------------------------------------------
357
 
358
;; --------------------------------------------------------------------------
359
;; Push a note into the note stack
360
;; expecting pointer to SID_MVx_NOTE_STACK_0 in FSR2 and note in SID_MIDI_PARAMETER1
361
;; OUT: ZERO flag cleared if note already stored
362
;; --------------------------------------------------------------------------
363
SID_MIDI_Hlp_PushNote
364
	clrf	PRODL
365
	;; do nothing if note is already stored in note stack
366
SID_MIDI_Hlp_PushNote_CheckLoop
367
	movf	PRODL, W
368
	movf	PLUSW2, W
369
	bz	SID_MIDI_Hlp_PushNote_StackNew	; new note
370
	xorwf	SID_MIDI_PARAMETER1, W, BANKED
371
	skpnz
372
	rgoto	SID_MIDI_Hlp_PushNote_Failed	; leave note routine if note already stored
373
	incf	PRODL, F
374
	movlw	SID_MVx_NOTE_STACK_LEN
375
	cpfseq	PRODL, ACCESS
376
	rgoto SID_MIDI_Hlp_PushNote_CheckLoop
377
	rgoto	SID_MIDI_Hlp_PushNote_StackFull
378
 
379
SID_MIDI_Hlp_PushNote_StackNew
380
	;; increment stack pointer
381
	movlw	SID_MVx_NOTE_STACK_PTR-SID_MVx_NOTE_STACK_0
382
	incf	PLUSW2, F
383
SID_MIDI_Hlp_PushNote_StackFull
384
 
385
	;; shift right note stack
386
	movlw	(SID_MVx_NOTE_STACK_LEN-2)
387
	movwf	PRODL
388
SID_MIDI_Hlp_PushNote_ShiftLoop
389
	movf	PRODL, W
390
	movff	PLUSW2, PRODH
391
	incf	PRODL, W
392
	movff	PRODH, PLUSW2
393
	decf	PRODL, F
394
	incf	PRODL, W
395
	bnz	SID_MIDI_Hlp_PushNote_ShiftLoop
396
 
397
	;; store new note at offset 0
398
	movff	SID_MIDI_PARAMETER1, INDF2
399
 
400
	andlw	0x00		; return 0x00 as error status (ZERO flag set)
401
	return
402
 
403
SID_MIDI_Hlp_PushNote_Failed
404
	iorlw	0xff		; return 0xff as error status (ZERO flag cleared)
405
	return
406
 
407
 
408
;; --------------------------------------------------------------------------
409
;; Pop a note from the note stack
410
;; Pop a note from the arp stack (same approach)
411
;; expecting pointer to SID_MVx_NOTE_STACK_0 in FSR2 and note in SID_MIDI_PARAMETER1
412
;; OUT: ZERO flag cleared if note already stored
413
;; --------------------------------------------------------------------------
414
SID_MIDI_Hlp_PopNote
415
SID_MIDI_Hlp_PopArp
416
SID_MIDI_Hlp_PopArpS
417
	; search for note entry with the same number, erase it and push the entries behind
418
	clrf	PRODL
419
SID_MIDI_Hlp_PopNote_SearchLoop
420
	movf	PRODL, W
421
	movf	PLUSW2, W
422
	xorwf	SID_MIDI_PARAMETER1, W, BANKED
423
	bz	SID_MIDI_Hlp_PopNote_Found
424
	incf	PRODL, F
425
	movlw	SID_MVx_NOTE_STACK_LEN
426
	cpfseq	PRODL, ACCESS
427
	rgoto SID_MIDI_Hlp_PopNote_SearchLoop
428
	rgoto	SID_MIDI_Hlp_PopNote_Failed
429
SID_MIDI_Hlp_PopNote_Found
430
 
431
	;; push the entries behind the found entry
432
SID_MIDI_Hlp_PopNote_ShiftLoop
433
	incf	PRODL, W
434
	movff	PLUSW2, PRODH
435
	movf	PRODL, W
436
	movff	PRODH, PLUSW2
437
	incf	PRODL, F
438
	movlw	SID_MVx_NOTE_STACK_LEN
439
	cpfseq	PRODL, ACCESS
440
	rgoto SID_MIDI_Hlp_PopNote_ShiftLoop
441
	;; clear the last entry
442
	movlw	SID_MVx_NOTE_STACK_LEN-1
443
	clrf	PLUSW2
444
	;; decrement stack pointer
445
	movlw	SID_MVx_NOTE_STACK_PTR-SID_MVx_NOTE_STACK_0
446
	decf	PLUSW2, F
447
	andlw	0x00		; return 0x00 as error status (ZERO flag set)
448
	return
449
 
450
SID_MIDI_Hlp_PopNote_Failed
451
	iorlw	0xff		; return 0xff as error status (ZERO flag cleared)
452
	return
453
 
454
 
455
;; --------------------------------------------------------------------------
456
;; Special Pop function for Arp Hold mode
457
;; the note won't be removed from stack, but it will be marked with bit 7
458
;; expecting pointer to SID_MVx_NOTE_STACK_0 in FSR2 and note in SID_MIDI_PARAMETER1
459
;; OUT: ZERO flag cleared if no matching note has been found
460
;; --------------------------------------------------------------------------
461
SID_MIDI_Hlp_PopArpHold
462
	; search for note entry with the same number, mark it with bit 7
463
	clrf	PRODL
464
SID_MIDI_Hlp_PopArpHold_Loop
465
	movf	PRODL, W
466
	movf	PLUSW2, W
467
	xorwf	SID_MIDI_PARAMETER1, W, BANKED
468
	bz	SID_MIDI_Hlp_PopArpHold_Found
469
	incf	PRODL, F
470
	movlw	SID_MVx_NOTE_STACK_LEN
471
	cpfseq	PRODL, ACCESS
472
	rgoto SID_MIDI_Hlp_PopArpHold_Loop
473
	iorlw	0xff		; return 0xff - nothing to mark
474
	return
475
 
476
SID_MIDI_Hlp_PopArpHold_Found
477
	;; mark note
478
	movf	PRODL, W
479
	bsf	PLUSW2, 7
480
	andlw	0x00		; return 0x00 - note found and marked
481
	return
482
 
483
 
484
;; --------------------------------------------------------------------------
485
;; Push a note into the arp stack
486
;; in difference to SID_MIDI_Hlp_PushNote, the note will be added at the end
487
;; expecting pointer to SID_MVx_NOTE_STACK_0 in FSR2 and note in SID_MIDI_PARAMETER1
488
;; OUT: ZERO flag cleared if note already stored
489
;; --------------------------------------------------------------------------
490
SID_MIDI_Hlp_PushArp
491
	clrf	PRODL
492
	;; do nothing if note is already stored in arp stack
493
	;; break at first entry which is zero
494
SID_MIDI_Hlp_PushArp_CheckLoop
495
	movf	PRODL, W
496
	movf	PLUSW2, W
497
	andlw	0x7f
498
	bz	SID_MIDI_Hlp_PushArp_Break
499
	xorwf	SID_MIDI_PARAMETER1, W, BANKED
500
	andlw	0x7f
501
	bz	SID_MIDI_Hlp_PushArp_Failed       ; leave note routine if note already stored
502
	incf	PRODL, F
503
	movlw	SID_MVx_NOTE_STACK_LEN
504
	cpfseq	PRODL, ACCESS
505
	rgoto SID_MIDI_Hlp_PushArp_CheckLoop
506
	rgoto	SID_MIDI_Hlp_PushArp_Failed	; stack full
507
SID_MIDI_Hlp_PushArp_Break
508
 
509
	;; add note to empty stack slot
510
	movf	PRODL, W
511
	movff	SID_MIDI_PARAMETER1, PLUSW2
512
 
513
	;; increment stack pointer
514
	movlw	SID_MVx_NOTE_STACK_PTR-SID_MVx_NOTE_STACK_0
515
	incf	PLUSW2, F
516
 
517
	andlw	0x00		; return 0x00 as error status (ZERO flag set)
518
	return
519
 
520
SID_MIDI_Hlp_PushArp_Failed
521
	iorlw	0xff		; return 0xff as error status (ZERO flag cleared)
522
	return
523
 
524
 
525
;; --------------------------------------------------------------------------
526
;; Push a note into the sorted arp stack
527
;; in difference to SID_MIDI_Hlp_PushArp, we have a sorted order (low -> high)
528
;; expecting pointer to SID_MVx_NOTE_STACK_0 in FSR2 and note in SID_MIDI_PARAMETER1
529
;; --------------------------------------------------------------------------
530
SID_MIDI_Hlp_PushArpS
531
	clrf	PRODL		; PRODL used as loop counter
532
SID_MIDI_Hlp_PushArpS_Loop
533
	movf	PRODL, W
534
	movf	PLUSW2, W
535
	andlw	0x7f
536
	bz	SID_MIDI_Hlp_PushArpS_Push	; check for last entry (=0)
537
	xorwf	SID_MIDI_PARAMETER1, W, BANKED	; ignore if note is already in stack (-> hold mode)
538
	andlw	0x7f
539
	bz	SID_MIDI_Hlp_PushArpS_End
540
	xorwf	SID_MIDI_PARAMETER1, W, BANKED	; check if new note is >= current note
541
	subwf	SID_MIDI_PARAMETER1, W, BANKED
542
	bnc	SID_MIDI_Hlp_PushArpS_Push
543
	incf	PRODL, F
544
	movlw	SID_MVx_NOTE_STACK_LEN
545
	cpfseq	PRODL, ACCESS
546
	rgoto SID_MIDI_Hlp_PushArpS_Loop
547
	return
548
 
549
SID_MIDI_Hlp_PushArpS_Push
550
	;; increment stack pointer
551
	movlw	SID_MVx_NOTE_STACK_PTR-SID_MVx_NOTE_STACK_0
552
	incf	PLUSW2, F
553
	;; ensure that pointer never > SID_MVx_NOTE_STACK_LEN
554
	movf	PLUSW2, W
555
	xorlw	SID_MVx_NOTE_STACK_LEN+1
556
	movlw	SID_MVx_NOTE_STACK_PTR-SID_MVx_NOTE_STACK_0
557
	skpnz
558
	decf	PLUSW2, F
559
 
560
	movf	PRODL, W		; max note: no shift required
561
	xorlw	SID_MVx_NOTE_STACK_LEN-1
562
	bz	SID_MIDI_Hlp_PushArpS_PushN
563
 
564
	movlw	SID_MVx_NOTE_STACK_LEN-2
565
	movwf	PRODH
566
SID_MIDI_Hlp_PushArpS_PushL
567
	movf	PRODH, W
568
	movff	PLUSW2, TABLAT
569
	addlw	1
570
	movff	TABLAT, PLUSW2
571
	movf	PRODL, W
572
	xorwf	PRODH, W
573
	bz	SID_MIDI_Hlp_PushArpS_PushN
574
	decf	PRODH, F
575
	rgoto	SID_MIDI_Hlp_PushArpS_PushL
576
 
577
SID_MIDI_Hlp_PushArpS_PushN
578
	movf	PRODL, W
579
	movff	SID_MIDI_PARAMETER1, PLUSW2
580
SID_MIDI_Hlp_PushArpS_End
581
	return
582
 
583
 
584
;; --------------------------------------------------------------------------
585
;; This function checks if the note stack is empty, or only contains
586
;; released notes
587
;; OUT: ZERO flag set if at least one note is played
588
;; --------------------------------------------------------------------------
589
SID_MIDI_Hlp_CheckActiveNote
590
	movlw	0
591
	movwf	PRODL
592
	movf	PLUSW2, W	; check if first value is 0 (no note in stack)
593
	bz	SID_MIDI_Hlp_CheckActiveNote_Clr
594
 
595
SID_MIDI_Hlp_CheckActiveNoteLoop
596
	movf	PRODL, W	; if hold flag is active: check if value either 0x80 or 0x00
597
	movf	PLUSW2, W
598
	bz	SID_MIDI_Hlp_CheckActiveNote_Clr
599
	andlw	0x80
600
	bz	SID_MIDI_Hlp_CheckActiveNote_Not
601
	incf	PRODL, F
602
	movlw	SID_MVx_NOTE_STACK_LEN - 1
603
	cpfsgt	PRODL, ACCESS
604
	rgoto SID_MIDI_Hlp_CheckActiveNoteLoop
605
SID_MIDI_Hlp_CheckActiveNote_Clr
606
	iorlw	0xff		; clear ZERO flag - no active note in stack
607
	return
608
 
609
SID_MIDI_Hlp_CheckActiveNote_Not
610
	andlw	0x00		; set ZERO flag - found active note
611
	return
612
 
613
 
614
;; --------------------------------------------------------------------------
615
;; This function removes all non-active notes from the stack
616
;; OUT: ZERO flag set if at least one note is played
617
;; --------------------------------------------------------------------------
618
SID_MIDI_Hlp_ClrNonActive
619
	movlw	0
620
	movwf	PRODL
621
	movf	PLUSW2, W	; check if first value is 0 (no note in stack)
622
	bz	SID_MIDI_Hlp_ClrNonActive_Clr
623
 
624
SID_MIDI_Hlp_ClrNonActiveLoop
625
	movf	PRODL, W	; if hold function is active: check if value either 0x80 or 0x00
626
	movf	PLUSW2, W
627
	bz	SID_MIDI_Hlp_ClrNonActive_Not
628
	andlw	0x80
629
	bz	SID_MIDI_Hlp_ClrNonActiveLoopNxt
630
 
631
	;; remove note from stack and loop again
632
SID_MIDI_Hlp_ClrNonActiveShLoop
633
	incf	PRODL, W
634
	movff	PLUSW2, PRODH
635
	movf	PRODL, W
636
	movff	PRODH, PLUSW2
637
	incf	PRODL, F
638
	movlw	SID_MVx_NOTE_STACK_LEN
639
	cpfseq	PRODL, ACCESS
640
	rgoto SID_MIDI_Hlp_ClrNonActiveShLoop
641
	;; clear the last entry
642
	movlw	SID_MVx_NOTE_STACK_LEN-1
643
	clrf	PLUSW2
644
	;; decrement stack pointer
645
	movlw	SID_MVx_NOTE_STACK_PTR-SID_MVx_NOTE_STACK_0
646
	decf	PLUSW2, F
647
	rgoto	SID_MIDI_Hlp_ClrNonActive
648
 
649
SID_MIDI_Hlp_ClrNonActiveLoopNxt
650
	incf	PRODL, F
651
	movlw	SID_MVx_NOTE_STACK_LEN - 1
652
	cpfsgt	PRODL, ACCESS
653
	rgoto SID_MIDI_Hlp_ClrNonActiveLoop
654
	;; when we reached this point, at least one note has been found
655
	rgoto	SID_MIDI_Hlp_ClrNonActive_Not
656
 
657
SID_MIDI_Hlp_ClrNonActive_Clr
658
	iorlw	0xff		; clear ZERO flag - no active note in stack
659
	return
660
 
661
SID_MIDI_Hlp_ClrNonActive_Not
662
	andlw	0x00		; set ZERO flag - found active note
663
	return
664
 
665
 
666
;; --------------------------------------------------------------------------
667
;; Clears the note stack
668
;; expecting pointer to SID_MVx_NOTE_STACK_0 in FSR2
669
;; --------------------------------------------------------------------------
670
SID_MIDI_Hlp_ClrStack
671
	movlw	SID_MVx_NOTE_STACK_PTR-SID_MVx_NOTE_STACK_0	; clear SID_MVx_NOTE_STACK_PTR
672
	clrf	PLUSW2
673
 
674
	clrf	PRODL		; clear all SID_MVx_NOTE_STACK_x entries
675
SID_MIDI_Hlp_ClrStack_Loop
676
	movf	PRODL, W
677
	clrf	PLUSW2
678
	incf	PRODL, F
679
	movlw	SID_MVx_NOTE_STACK_LEN-1
680
	cpfsgt	PRODL, ACCESS
681
	rgoto SID_MIDI_Hlp_ClrStack_Loop
682
	return
683
 
684
 
685
 
686
;; --------------------------------------------------------------------------
687
;; used by multi and lead engine: note on when arp is active
688
;; IN: pointer to MIDI voice in FSR0
689
;;     pointer to voice in FSR1
690
;;     pointer to note stack in FSR2
691
;;     new note in SID_MIDI_PARAMETER1
692
;;     ARP_MODE flags in TABLAT
693
;; --------------------------------------------------------------------------
694
SID_MIDI_Hlp_ArpNoteOn
695
#if EXP_ARP_BEHAVIOUR == 0
696
	;; if no note is played anymore, clear stack again (so that new notes can be filled in HOLD mode)
697
	rcall	SID_MIDI_Hlp_CheckActiveNote
698
#else
699
	BRA_IFSET TABLAT, SID_I_V_ARP_MODE_HOLD, ACCESS, SID_MIDI_Hlp_ArpNoteOn_Hold
700
	rcall	SID_MIDI_Hlp_ClrNonActive
701
	rgoto	SID_MIDI_Hlp_ArpNoteOn_Cont
702
SID_MIDI_Hlp_ArpNoteOn_Hold
703
	rcall	SID_MIDI_Hlp_CheckActiveNote
704
SID_MIDI_Hlp_ArpNoteOn_Cont
705
#endif
706
	bz	SID_MIDI_Hlp_ArpNoteOn_NoClr
707
SID_MIDI_Hlp_ArpNoteOn_Clr
708
	movlw	SID_MVx_ARP_STATE	; set SYNC_ARP flag
709
	bsf	PLUSW0, SID_MV_ARP_STATE_SYNC_ARP
710
	rcall	SID_MIDI_Hlp_ClrStack	; clear note stack (for the case that some notes are marked with bit #7 (key released)
711
SID_MIDI_Hlp_ArpNoteOn_NoClr
712
 
713
	;; push note into stack - select handler depending on sort mode
714
	BRA_IFSET TABLAT, SID_I_V_ARP_MODE_SORTED, ACCESS, SID_MIDI_Hlp_ArpNoteOn_Sort
715
	rcall	SID_MIDI_Hlp_PushArp
716
	rgoto	SID_MIDI_Hlp_ArpNoteOn_NoSort
717
SID_MIDI_Hlp_ArpNoteOn_Sort
718
	rcall	SID_MIDI_Hlp_PushArpS
719
SID_MIDI_Hlp_ArpNoteOn_NoSort
720
 
721
	;; activate voice
722
	movlw	SID_Vx_STATE
723
	bsf	PLUSW1, SID_V_STATE_VOICE_ACTIVE
724
 
725
	;; remember note
726
	movlw	SID_Vx_PLAYED_NOTE
727
	movff	SID_MIDI_PARAMETER1, PLUSW1
728
 
729
	return
730
 
731
;; --------------------------------------------------------------------------
732
;; used by multi and lead engine: note off when arp is active
733
;; IN: pointer to MIDI voice in FSR0
734
;;     pointer to voice in FSR1
735
;;     pointer to note stack in FSR2
736
;;     note to be released in SID_MIDI_PARAMETER1
737
;;     ARP_MODE flags in TMP1
738
;; --------------------------------------------------------------------------
739
SID_MIDI_Hlp_ArpNoteOff
740
#if EXP_ARP_BEHAVIOUR == 0
741
	BRA_IFSET TMP1, SID_I_V_ARP_MODE_HOLD, ACCESS, SID_MIDI_Hlp_ArpNoteOff_Hold
742
	;; pop note from stack (pointer to stack in FSR2)
743
	rcall	SID_MIDI_Hlp_PopNote
744
	rgoto	SID_MIDI_Hlp_ArpNoteOff_Cont
745
SID_MIDI_Hlp_ArpNoteOff_Hold
746
	rcall	SID_MIDI_Hlp_PopArpHold
747
SID_MIDI_Hlp_ArpNoteOff_Cont
748
 
749
#else
750
	rcall	SID_MIDI_Hlp_PopArpHold
751
#endif
752
 
753
	;; release voice if no note in queue anymore
754
	BRA_IFSET TMP1, SID_I_V_ARP_MODE_HOLD, ACCESS, SID_MIDI_Hlp_ArpNoteOff_NoClr
755
#if EXP_ARP_BEHAVIOUR == 0
756
	movlw	SID_MVx_NOTE_STACK_PTR
757
	movf	PLUSW0, W
758
	bnz	SID_MIDI_Hlp_ArpNoteOff_NoClr
759
#else
760
	rcall	SID_MIDI_Hlp_CheckActiveNote
761
	bz	SID_MIDI_Hlp_ArpNoteOff_NoClr
762
#endif
763
SID_MIDI_Hlp_ArpNoteOff_Clr
764
	movlw	SID_Vx_STATE
765
	bcf	PLUSW1, SID_V_STATE_VOICE_ACTIVE
766
SID_MIDI_Hlp_ArpNoteOff_NoClr
767
	return
768
 
769
 
770
;; --------------------------------------------------------------------------
771
;; Pop a note from the WT stack
772
;; expecting pointer to SID_MVx_WT_STACK_0 in FSR2 and note in SID_MIDI_PARAMETER1
773
;; OUT: ZERO flag cleared if note already stored
774
;; --------------------------------------------------------------------------
775
SID_MIDI_Hlp_PopWT
776
	; search for note entry with the same number, erase it and push the entries behind
777
	clrf	PRODL
778
SID_MIDI_Hlp_PopWT_SearchLoop
779
	movf	PRODL, W
780
	movf	PLUSW2, W
781
	xorwf	SID_MIDI_PARAMETER1, W, BANKED
782
	bz	SID_MIDI_Hlp_PopWT_Found
783
	incf	PRODL, F
784
	movlw	4
785
	cpfseq	PRODL, ACCESS
786
	rgoto SID_MIDI_Hlp_PopWT_SearchLoop
787
	rgoto	SID_MIDI_Hlp_PopWT_Failed
788
SID_MIDI_Hlp_PopWT_Found
789
 
790
	;; push the entries behind the found entry
791
SID_MIDI_Hlp_PopWT_ShiftLoop
792
	incf	PRODL, W
793
	movff	PLUSW2, PRODH
794
	movf	PRODL, W
795
	movff	PRODH, PLUSW2
796
	incf	PRODL, F
797
	movlw	4
798
	cpfseq	PRODL, ACCESS
799
	rgoto SID_MIDI_Hlp_PopWT_ShiftLoop
800
	;; clear the last entry
801
	movlw	4-1
802
	clrf	PLUSW2
803
	andlw	0x00		; return 0x00 as error status (ZERO flag set)
804
	return
805
 
806
SID_MIDI_Hlp_PopWT_Failed
807
	iorlw	0xff		; return 0xff as error status (ZERO flag cleared)
808
	return
809
 
810
;; --------------------------------------------------------------------------
811
;; Push a note into the sorted WT stack
812
;; expecting pointer to SID_MVx_WT_STACK_0 in FSR2 and note in SID_MIDI_PARAMETER1
813
;; --------------------------------------------------------------------------
814
SID_MIDI_Hlp_PushWT
815
	clrf	PRODL		; PRODL used as loop counter
816
SID_MIDI_Hlp_PushWT_Loop
817
	movf	PRODL, W
818
	movf	PLUSW2, W
819
	andlw	0x7f
820
	bz	SID_MIDI_Hlp_PushWT_Push	; check for last entry (=0)
821
	xorwf	SID_MIDI_PARAMETER1, W, BANKED	; ignore if note is already in stack (-> hold mode)
822
	andlw	0x7f
823
	bz	SID_MIDI_Hlp_PushWT_End
824
	xorwf	SID_MIDI_PARAMETER1, W, BANKED	; check if new note is >= current note
825
	subwf	SID_MIDI_PARAMETER1, W, BANKED
826
	bnc	SID_MIDI_Hlp_PushWT_Push
827
	incf	PRODL, F
828
	movlw	4
829
	cpfseq	PRODL, ACCESS
830
	rgoto SID_MIDI_Hlp_PushWT_Loop
831
	return
832
 
833
SID_MIDI_Hlp_PushWT_Push
834
	movf	PRODL, W		; max note: no shift required
835
	xorlw	4-1
836
	bz	SID_MIDI_Hlp_PushWT_PushN
837
 
838
	movlw	4-2
839
	movwf	PRODH
840
SID_MIDI_Hlp_PushWT_PushL
841
	movf	PRODH, W
842
	movff	PLUSW2, TABLAT
843
	addlw	1
844
	movff	TABLAT, PLUSW2
845
	movf	PRODL, W
846
	xorwf	PRODH, W
847
	bz	SID_MIDI_Hlp_PushWT_PushN
848
	decf	PRODH, F
849
	rgoto	SID_MIDI_Hlp_PushWT_PushL
850
 
851
SID_MIDI_Hlp_PushWT_PushN
852
	movf	PRODL, W
853
	movff	SID_MIDI_PARAMETER1, PLUSW2
854
SID_MIDI_Hlp_PushWT_End
855
	return
856
 
857
 
858
;; --------------------------------------------------------------------------
859
;; Check MIDI Channel and Split Points
860
;; IN: current channel IN SID_CURRENT_CHANNEL
861
;;     current note in SID_MIDI_PARAMETER1
862
;;     SID_MVx_BASE in FSR0
863
;; OUT: zero flag set if channel and split zone matches
864
;; --------------------------------------------------------------------------
865
SID_MIDI_Hlp_ChkChnAndSplit
866
	SET_BSR	SID_BASE
867
 
868
	;; check if MIDI channel matches
869
	movlw	SID_MVx_MIDI_CHANNEL
870
	movf	PLUSW0, W
871
	cpfseq	SID_CURRENT_CHANNEL, BANKED
872
	rgoto SID_MIDI_Hlp_ChkChnAndSplit_Fail
873
 
874
	;; get split points
875
	movlw	SID_MVx_SPLIT_LOWER
876
	movff	PLUSW0, PRODL
877
	movlw	SID_MVx_SPLIT_UPPER
878
	movff	PLUSW0, PRODH
879
 
880
	;; don't split if both are zero
881
	movf	PRODL, W
882
	iorwf	PRODH, W
883
	bz	SID_MIDI_Hlp_ChkChnAndSplit_Ok
884
 
885
	;; check lower split point
886
	movf	PRODL, W
887
	cpfslt	SID_MIDI_PARAMETER1, BANKED
888
	rgoto SID_MIDI_Hlp_ChkChnAndSplit_LOk
889
	rgoto	SID_MIDI_Hlp_ChkChnAndSplit_Fail
890
SID_MIDI_Hlp_ChkChnAndSplit_LOk
891
 
892
	;; check upper split point
893
	movf	PRODH, W
894
	cpfsgt	SID_MIDI_PARAMETER1, BANKED
895
	rgoto SID_MIDI_Hlp_ChkChnAndSplit_UOk
896
	rgoto	SID_MIDI_Hlp_ChkChnAndSplit_Fail
897
SID_MIDI_Hlp_ChkChnAndSplit_UOk
898
 
899
SID_MIDI_Hlp_ChkChnAndSplit_Ok
900
	andlw	0x00		; ZERO flag set: note within range
901
	return
902
 
903
SID_MIDI_Hlp_ChkChnAndSplit_Fail
904
	iorlw	0xff		; ZERO flag cleared: note not within range
905
	return