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_se.inc 44 2008-01-30 21:39:30Z tk $
1 tk 2
;
3
; MIDIbox SID
4
; Software Synthesizer Engine
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_SE_MEASURE_PERFORMANCE_PORT   LATC, 3
9
;
10
; ==========================================================================
11
;
12
;  Copyright 1998-2007 Thorsten Klose (tk@midibox.org)
13
;  Idea for ENV Curve Parameter and OSC synchronization by Jess D. Skov-Nielsen
14
;  Licensed for personal non-commercial use only.
15
;  All other rights reserved.
16
;
17
; ==========================================================================
18
 
19
;; ==========================================================================
20
;;  SID Flags
21
;; ==========================================================================
22
 
23
;; MIDI Voice Control Flags
24
SID_MV_ARP_STATE_ARP_ACTIVE	EQU	0 ; set if at least one arp is active
25
SID_MV_ARP_STATE_ARP_UP		EQU	1 ; arp order is incremented
26
SID_MV_ARP_STATE_SYNC_ARP	EQU	2 ; syncs the arpeggiator when a new note is played
27
SID_MV_ARP_STATE_HOLD_SAVED	EQU	3 ; notifies if hold mode was active (notes should be disabled on 1->0 transition)
28
 
29
SID_V_STATE_VOICE_ACTIVE	EQU	0
30
SID_V_STATE_VOICE_DISABLED	EQU	1
31
SID_V_STATE_GATE_ACTIVE		EQU	2
32
SID_V_STATE_GATE_SET_REQ	EQU	3
33
SID_V_STATE_GATE_CLR_REQ	EQU	4
34
SID_V_STATE_PORTA_ACTIVE	EQU	5
35
SID_V_STATE_ACCENT		EQU	6
36
SID_V_STATE_SLIDE		EQU	7
37
 
38
SID_I_OPT1_FLAGS_ABW		EQU	0	; ADSR bug workaround
39
 
40
SID_I_L_FLAGS1_LEGATO		EQU	0	; switches between mono/legato mode
41
SID_I_L_FLAGS1_WT_ONLY		EQU	1	; notes played by WT only
42
SID_I_L_FLAGS1_SUS_KEY		EQU	2	; enables SusKey
43
 
44
SID_I_M_V_FLAGS2_LEGATO		EQU	0	; multi engine only: switches between mono/legato mode when poly not enabled
45
SID_I_M_V_FLAGS2_WT_ONLY	EQU	1	; multi engine only: notes played by WT only
46
SID_I_M_V_FLAGS2_SUS_KEY	EQU	2	; multi engine only: enables SusKey
47
SID_I_M_V_FLAGS2_POLY		EQU	3	; multi engine only: enables Poly mode
48
SID_I_M_V_FLAGS2_PHASE		EQU	4	; multi engine only: oscillator phase synchronisation
49
 
50
SID_I_B_V_FLAGS2_LEGATO		EQU	0	; bassline engine only: switches between mono/legato mode when poly not enabled
51
SID_I_B_V_FLAGS2_WT_ONLY	EQU	1	; bassline engine only: notes played by WT only
52
SID_I_B_V_FLAGS2_SUS_KEY	EQU	2	; bassline engine only: enables SusKey
53
SID_I_B_V_FLAGS2_PHASE		EQU	4	; bassline engine only: oscillator phase synchronisation
54
 
55
SID_I_V_FLAGS1_PORTA_CTG	EQU	0	; constant time glide
56
SID_I_V_FLAGS1_PORTA_GLISSANDO	EQU	1	; glissando
57
SID_I_V_FLAGS1_GSA		EQU	2	; gate stays active
58
SID_I_V_FLAGS1_VASG_0		EQU	4	; only relevant for drum engine: voice assignment, bit 0
59
SID_I_V_FLAGS1_VASG_1		EQU	5	; only relevant for drum engine: voice assignment, bit 1
60
SID_I_V_FLAGS1_VASG_2		EQU	6	; only relevant for drum engine: voice assignment, bit 2
61
SID_I_V_FLAGS1_VASG_3		EQU	7	; only relevant for drum engine: voice assignment, bit 3
62
 
63
SID_I_V_ARP_MODE_ENABLE		EQU	0	; enables arp
64
SID_I_V_ARP_MODE_DIR_DOWN	EQU	1	; arp dir Up/Down
65
SID_I_V_ARP_MODE_DIR_ALT	EQU	2	; arp dir U&D/D&U
66
SID_I_V_ARP_MODE_DIR_ALT2	EQU	3	; arp dir U&D/D&U second option (border notes played twice) - dir mode 6 and 7 will select random
67
SID_I_V_ARP_MODE_SORTED		EQU	4	; select sorted stack
68
SID_I_V_ARP_MODE_HOLD		EQU	5	; select hold stack
69
SID_I_V_ARP_MODE_DIV_SYNC	EQU	6	; disables divider reset on new notes
70
SID_I_V_ARP_MODE_CAC		EQU	7	; constant arp cycle
71
 
72
SID_I_V_ARP_SPEED_DIV_ONESHOT	EQU	7	; oneshot mode
73
 
74
SID_I_V_SEQ_ON			EQU	6	; drum engine only: sequencer on/off flag
75
SID_I_V_SEQ_SYNC16		EQU	7	; bassline/drum engine only: 16step sync function
76
 
77
SID_SEQ_MISC_SUBCTR		EQU	0	; [2:0] counts from 0 to 6
78
SID_SEQ_MISC_SEQ_ON		EQU	3	; stores sequencer on flag
79
SID_SEQ_MISC_SEQ_RUNNING	EQU	4	; stores sequencer running flag (only used in drum mode)
80
 
81
SID_I_F_FIP_ON			EQU	7	; located in high byte of cutoff frequency: filter interpolation flag
82
 
83
SID_I_LFO_MODE_ENABLE		EQU	0
84
SID_I_LFO_MODE_SYNC_M		EQU	1	; sync LFO (only for multi and bassline engine - lead engine uses trigger matrix!)
85
SID_I_LFO_MODE_CLKSYNC		EQU	2
86
SID_I_LFO_MODE_ONESHOT		EQU	3
87
SID_I_LFO_MODE_WAVEFORM0	EQU	4
88
SID_I_LFO_MODE_WAVEFORM1	EQU	5
89
SID_I_LFO_MODE_WAVEFORM2	EQU	6
90
SID_I_LFO_MODE_WAVEFORM3	EQU	7
91
 
92
SID_I_ENV_MODE_LOOP_B0		EQU	0	; loop begin (3 bit)
93
SID_I_ENV_MODE_LOOP_B1		EQU	1
94
SID_I_ENV_MODE_LOOP_B2		EQU	2
95
SID_I_ENV_MODE_LOOP_E0		EQU	4	; loop end (3 bit)
96
SID_I_ENV_MODE_LOOP_E1		EQU	5
97
SID_I_ENV_MODE_LOOP_E2		EQU	6
98
SID_I_ENV_MODE_CLKSYNC		EQU	7
99
 
100
SID_SE_STATE_LFO_OVERRUN	EQU	0	; used by LFO handler to notify an overrun
101
SID_SE_STATE_WT_NEW_STEP_REQ	EQU	0	; used by WT handler to request new step (shared flag with LFO)
102
SID_SE_STATE_ARP_NEW_NOTE_REQ	EQU	0	; used by ARP handler to request new note (shared flag with LFO)
103
SID_SE_STATE_ARP_FIRST_NOTE_REQ	EQU	1	; used by ARP handler to request the first note (oneshot function)
104
SID_SE_STATE_ARP_GATE_CLR_REQ	EQU	2	; used by ARP handler to request a gate clear
105
SID_SE_STATE_GLOBAL_CLK_EVENT	EQU	3	; temporary "global clock event" flag
106
SID_SE_STATE_MIDI_CLK_FA_REQ	EQU	4	; "MIDI clock start" request flag
107
SID_SE_STATE_MIDI_CLK_FB_REQ	EQU	5	; "MIDI clock continue" request flag
108
SID_SE_STATE_MIDI_CLK_FC_REQ	EQU	6	; "MIDI clock stop" request flag
109
SID_SE_STATE_ACCENT		EQU	7	; used by multi/bassline ENV handler to increase depth on accent
110
 
111
SID_ENV_STATE_ATTACK1		EQU	0
112
SID_ENV_STATE_ATTACK2		EQU	1
113
SID_ENV_STATE_DECAY1		EQU	2
114
SID_ENV_STATE_DECAY2		EQU	3
115
SID_ENV_STATE_SUSTAIN		EQU	4
116
SID_ENV_STATE_RELEASE1		EQU	5
117
SID_ENV_STATE_RELEASE2		EQU	6
118
 
119
SID_ENS_CTRL1_CLK_SLAVE		EQU	0	; switch between MIDI clock master/slave
120
SID_ENS_CTRL1_CLK_AUTO		EQU	1	; automatic switching between master/slave
121
SID_ENS_CTRL1_MONO		EQU	6	; SIDs are played mono
122
 
123
SID_ENS_CTRL2_F2A		EQU	0	; forward Filter CutOff/Resonance to AOUTs
124
SID_ENS_CTRL2_V2A		EQU	1	; forward Volume to AOUTs
125
SID_ENS_CTRL2_P2A		EQU	2	; forward Pulsewidth to AOUTs
126
SID_ENS_CTRL2_K2A		EQU	3	; forward Key Values to AOUTs and Gates to Digital Out
127
SID_ENS_CTRL2_O2A		EQU	4	; forward Oscillator Values to AOUTs and Gates to Digital Out
128
 
129
SID_TRG_TARGET_L_O1L		EQU	0	; OSC1 Left
130
SID_TRG_TARGET_L_O2L		EQU	1	; OSC2 Left
131
SID_TRG_TARGET_L_O3L		EQU	2	; OSC3 Left
132
SID_TRG_TARGET_L_O1R		EQU	3	; OSC1 Right
133
SID_TRG_TARGET_L_O2R		EQU	4	; OSC2 Right
134
SID_TRG_TARGET_L_O3R		EQU	5	; OSC3 Right
135
SID_TRG_TARGET_L_E1A		EQU	6	; ENV1 Attack
136
SID_TRG_TARGET_L_E2A		EQU	7	; ENV2 Attack
137
SID_TRG_TARGET_H_E1R		EQU	0	; ENV1 Release
138
SID_TRG_TARGET_H_E2R		EQU	1	; ENV2 Release
139
SID_TRG_TARGET_H_L1		EQU	2	; LFO1 sync
140
SID_TRG_TARGET_H_L2		EQU	3	; LFO2 sync
141
SID_TRG_TARGET_H_L3		EQU	4	; LFO3 sync
142
SID_TRG_TARGET_H_L4		EQU	5	; LFO4 sync
143
SID_TRG_TARGET_H_L5		EQU	6	; LFO5 sync
144
SID_TRG_TARGET_H_L6		EQU	7	; LFO6 sync
145
SID_TRG_TARGET_U_W1R		EQU	0	; WT1 reset
146
SID_TRG_TARGET_U_W2R		EQU	1	; WT2 reset
147
SID_TRG_TARGET_U_W3R		EQU	2	; WT3 reset
148
SID_TRG_TARGET_U_W4R		EQU	3	; WT4 reset
149
SID_TRG_TARGET_U_W1S		EQU	4	; WT1 step
150
SID_TRG_TARGET_U_W2S		EQU	5	; WT2 step
151
SID_TRG_TARGET_U_W3S		EQU	6	; WT3 step
152
SID_TRG_TARGET_U_W4S		EQU	7	; WT4 step
153
 
154
;; ==========================================================================
155
 
156
 
157
;; --------------------------------------------------------------------------
158
;;  SID Sound Engine Handler: Software Synthesizer part for the SID
159
;;  called by User Timer every 1 mS
160
;; --------------------------------------------------------------------------
161
SIDSE_Handler
162
	SET_BSR	SID_BASE		; prepare BSR for SID register access
163
 
164
	;; return immediately if engine has been disabled
165
	btfsc	SID_STAT, SID_STAT_ENGINE_DISABLE
166
	return
167
 
168
	;; second possibility (see comment in main.inc)
169
	btfsc	SID_STAT, SID_STAT_ENGINE_DISABLE_LEVEL0
170
	return
171
 
172
#ifdef SID_SE_MEASURE_PERFORMANCE_PORT
173
	bsf	SID_SE_MEASURE_PERFORMANCE_PORT
174
#endif
175
 
176
	;; save FSR0[LH] and PROD[LH] - we are in an interrupt routine
177
	movff	FSR0L, SAVED_FSR0L
178
	movff	FSR0H, SAVED_FSR0H
179
	movff	PRODL, SAVED_PRODL
180
	movff	PRODH, SAVED_PRODH
181
 
182
	;; branch depending on engine
183
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
184
	BRA_IFSET WREG, 1, ACCESS, SIDSE_Handler_23
185
SIDSE_Handler_01
186
	BRA_IFSET WREG, 0, ACCESS, SIDSE_Handler_1
187
SIDSE_Handler_0
188
	call	SIDSE_L_Handler
189
	rgoto	SIDSE_Handler_Cont
190
SIDSE_Handler_1
191
	call	SIDSE_B_Handler
192
	rgoto	SIDSE_Handler_Cont
193
 
194
SIDSE_Handler_23
195
	BRA_IFSET WREG, 0, ACCESS, SIDSE_Handler_3
196
SIDSE_Handler_2
197
	call	SIDSE_D_Handler
198
	rgoto	SIDSE_Handler_Cont
199
SIDSE_Handler_3
200
	call	SIDSE_M_Handler
201
	rgoto	SIDSE_Handler_Cont
202
 
203
SIDSE_Handler_Cont
204
 
205
	;; thats all
206
	movff	SAVED_FSR0L, FSR0L		; copy back saved FSR0[LH]
207
	movff	SAVED_FSR0H, FSR0H
208
	movff	SAVED_PRODL, PRODL		; copy back saved PROD[LH]
209
	movff	SAVED_PRODH, PRODH
210
 
211
#ifdef SID_SE_MEASURE_PERFORMANCE_PORT
212
	bcf	SID_SE_MEASURE_PERFORMANCE_PORT
213
#endif
214
 
215
	return
216
 
217
 
218
 
219
 
220
;; --------------------------------------------------------------------------
221
;; This function handles the global clock
222
;; --------------------------------------------------------------------------
223
SIDSE_Clk
224
	;; by default no clock event
225
	bcf	SID_SE_STATE, SID_SE_STATE_GLOBAL_CLK_EVENT, BANKED
226
 
227
	;; increment the clock counter, used to measure the delay between two F8 events
228
	;; see also USER_MIDI_NotifyRx
229
	;; ensure that it doesn't overrun
230
	incf	SID_INCOMING_CLK_CTR, W, BANKED
231
	skpz
232
	incf	SID_INCOMING_CLK_CTR, F, BANKED
233
 
234
#if 0
235
	;; try: if no slave clock is received, increment sent clock counter slowly
236
	;; (disabled by default - works fine, but is inconsistent)
237
	movf	SID_INCOMING_CLK_CTR, W, BANKED
238
	andlw	0x3f
239
	skpnz
240
	incf	SID_CLK_REQ_CTR, F, BANKED
241
#endif
242
 
243
	;; now determine master/slave flag depending on environment setup
244
	movff	SID_LOCAL_ENS + SID_ENSx_CTRL1, WREG
245
	BRA_IFSET WREG, SID_ENS_CTRL1_CLK_AUTO, ACCESS, SIDSE_Clk_ModeAuto
246
	BRA_IFSET WREG, SID_ENS_CTRL1_CLK_SLAVE, ACCESS, SIDSE_Clk_ModeSlave
247
SIDSE_Clk_ModeMaster
248
	bcf	SID_STAT, SID_STAT_CLK_SLAVE
249
	rgoto	SIDSE_Clk_Mode_Cont
250
SIDSE_Clk_ModeSlave
251
	bsf	SID_STAT, SID_STAT_CLK_SLAVE
252
	rgoto	SIDSE_Clk_Mode_Cont
253
SIDSE_Clk_ModeAuto
254
	;; slave mode so long INCOMING_CLK_CTR != 0xff
255
	bcf	SID_STAT, SID_STAT_CLK_SLAVE
256
	incf	SID_INCOMING_CLK_CTR, W, BANKED
257
	skpz
258
	bsf	SID_STAT, SID_STAT_CLK_SLAVE
259
	;; 	rgoto	SIDSE_Clk_Mode_Cont
260
SIDSE_Clk_Mode_Cont
261
 
262
	;; decrement sent clock delay, send interpolated clock events 3 times
263
	decf	SID_SENT_CLK_DELAY, F, BANKED
264
	bnz	SIDSE_Clk_NoSlaveTrigger
265
SIDSE_Clk_SlaveTrigger
266
	movf	SID_SENT_CLK_CTR, W, BANKED
267
	xorlw	0x03
268
	bz	SIDSE_Clk_NoSlaveTrigger
269
	incf	SID_SENT_CLK_CTR, F, BANKED
270
	incf	SID_CLK_REQ_CTR, F, BANKED
271
	rrf	SID_INCOMING_CLK_DELAY, W, BANKED
272
	rrf	WREG, W
273
	andlw	0x3f
274
	movwf	SID_SENT_CLK_DELAY, BANKED
275
SIDSE_Clk_NoSlaveTrigger
276
 
277
	;; handle FA event (MIDI clock start)
278
	BRA_IFCLR SID_SE_STATE, SID_SE_STATE_MIDI_CLK_FA_REQ, BANKED, SIDSE_Clk_NoFA
279
SIDSE_Clk_FA
280
	;; request not cleared here, because it's also used by Arp and sequencer
281
	;; must be cleared at end of update cycle
282
	;; 	bcf	SID_SE_STATE, SID_SE_STATE_MIDI_CLK_FA_REQ, BANKED
283
	;; reset counters
284
	clrf	SID_SE_GLOBAL_CLK_CTR, BANKED
285
	;; propagate LFO/Env/WT sync via trigger matrix
286
	;; (static for Multi Engine)
287
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
288
	andlw	0x03
289
	bz	SIDSE_Clk_FA_Lead
290
SIDSE_Clk_FA_Multi
291
	;; no reset - this disturbs sequence recordings
292
#if 0
293
	movlw	0xfc		; LFO reset
294
	iorwf	SID_SE_TRG_EVNT_H, F, BANKED
295
	movlw	0x3f		; WT reset for all 6 instruments
296
	iorwf	SID_SE_TRG_EVNT_U, F, BANKED
297
#endif
298
	rgoto	SIDSE_Clk_FA_Multi_Cont
299
 
300
SIDSE_Clk_FA_Lead
301
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_MSt_BASE + 0, WREG
302
	iorwf	SID_SE_TRG_EVNT_L, F, BANKED
303
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_MSt_BASE + 1, WREG
304
	iorwf	SID_SE_TRG_EVNT_H, F, BANKED
305
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_MSt_BASE + 2, WREG
306
	iorwf	SID_SE_TRG_EVNT_U, F, BANKED
307
SIDSE_Clk_FA_Multi_Cont
308
SIDSE_Clk_NoFA
309
 
310
	;; handle F8 event (MIDI clock)
311
	movf	SID_CLK_REQ_CTR, W, BANKED
312
	bz	SIDSE_Clk_NoF8
313
SIDSE_Clk_F8
314
	;; decrement counter by one (if there are more requests, they will be handled on next handler invocation
315
	decf	SID_CLK_REQ_CTR, F, BANKED
316
	;; if slave: continue at inc routine
317
	BRA_IFSET SID_STAT, SID_STAT_CLK_SLAVE, ACCESS, SIDSE_Clk_Inc
318
SIDSE_Clk_NoF8
319
 
320
	;; if slave: no Inc (only on F8 event)
321
	BRA_IFSET SID_STAT, SID_STAT_CLK_SLAVE, ACCESS, SIDSE_Clk_NoInc
322
 
323
	;; check timer0 overrun flag
324
	BRA_IFCLR INTCON, TMR0IF, ACCESS, SIDSE_Clk_NoInc
325
	bcf	INTCON, TMR0IF			; clear overrun flag
326
	SET_BSR	TIMER0_RELOAD_L
327
	bcf	T0CON, TMR0ON
328
	movf	TIMER0_RELOAD_L, W, BANKED	; (dummy add to update TMR0H, and to get carry flag of addition)
329
	addwf	TMR0L, W			; (update TMR0H)
330
	movf	TIMER0_RELOAD_H, W, BANKED
331
	addwfc	TMR0H, F
332
	movf	TIMER0_RELOAD_L, W, BANKED
333
	addwf	TMR0L, F			; 16bit register update takes place with this write
334
	bsf	T0CON, TMR0ON
335
	SET_BSR	SID_BASE
336
SIDSE_Clk_Inc
337
 
338
	;; increment global clock counter (for Clk/4 and Clk/16 divider)
339
	incf	SID_SE_GLOBAL_CLK_CTR, F, BANKED
340
 
341
	;; notify clock event
342
	bsf	SID_SE_STATE, SID_SE_STATE_GLOBAL_CLK_EVENT, BANKED
343
 
344
	;; clock propagation
345
	;; (static for Multi Engine)
346
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
347
	andlw	0x03
348
	bz	SIDSE_Clk_Reset_Lead
349
SIDSE_Clk_Reset_Multi
350
	movlw	0x80		; only step WTs (global flag bit #7 in multi mode)
351
	iorwf	SID_SE_TRG_EVNT_U, F, BANKED
352
	rgoto	SIDSE_Clk_Reset_Multi_Cont
353
 
354
SIDSE_Clk_Reset_Lead
355
 
356
	;; propagate clock event to trigger matrix
357
	lfsr	FSR1, SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_Clk_BASE
358
	rcall	SIDSE_Clk_Hlp_PropClkEvent
359
 
360
	;; propagate clock/4 event to trigger matrix on each 4th clock
361
	lfsr	FSR1, SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_Cl4_BASE
362
	movf	SID_SE_GLOBAL_CLK_CTR, W, BANKED
363
	andlw	0x03
364
	skpnz
365
	rcall	SIDSE_Clk_Hlp_PropClkEvent
366
 
367
	;; propagate clock/16 event to trigger matrix on each 16th clock
368
	lfsr	FSR1, SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_C16_BASE
369
	movf	SID_SE_GLOBAL_CLK_CTR, W, BANKED
370
	andlw	0x0f
371
	skpnz
372
	rcall	SIDSE_Clk_Hlp_PropClkEvent
373
 
374
SIDSE_Clk_Reset_Multi_Cont
375
SIDSE_Clk_NoReset
376
SIDSE_Clk_NoInc
377
 
378
SIDSE_Clk_End
379
	return
380
 
381
 
382
;; expecting pointer to SID_Ix_L_TRG_Clk*_BASE in FSR1
383
SIDSE_Clk_Hlp_PropClkEvent
384
	movf	POSTINC1, W
385
	iorwf	SID_SE_TRG_EVNT_L, F, BANKED
386
	movf	POSTINC1, W
387
	iorwf	SID_SE_TRG_EVNT_H, F, BANKED
388
	movf	POSTINC1, W
389
	iorwf	SID_SE_TRG_EVNT_U, F, BANKED
390
	return
391
 
392
 
393
 
394
;; --------------------------------------------------------------------------
395
;; This function handles the arpeggiators
396
;; IN: pointer to SID_Ix_e_SxVy_BASE in FSR0 (patch record)
397
;;     pointer to SIDx_Vx_BASE in FSR1 (voice record)
398
;;     pointer to SIDx_MVx_BASE in FSR2 (MIDI voice record)
399
;;     Voice number in SID_SE_ELEMENT_NUM
400
;; --------------------------------------------------------------------------
401
SIDSE_Arp
402
	;; TODO: portamento handling on note overlaps
403
 
404
	;; clear temporary arp flags
405
	bcf	SID_SE_STATE, SID_SE_STATE_ARP_NEW_NOTE_REQ, BANKED
406
	bcf	SID_SE_STATE, SID_SE_STATE_ARP_FIRST_NOTE_REQ, BANKED
407
	bcf	SID_SE_STATE, SID_SE_STATE_ARP_GATE_CLR_REQ, BANKED
408
 
409
	;; check if arp sync requested
410
	BRA_IFSET SID_SE_STATE, SID_SE_STATE_MIDI_CLK_FA_REQ, BANKED, SIDSE_Arp_Sync
411
	movlw	SID_MVx_ARP_STATE
412
	BRA_IFCLR PLUSW2, SID_MV_ARP_STATE_SYNC_ARP, ACCESS, SIDSE_Arp_NoSync
413
SIDSE_Arp_Sync
414
	;; set arp counters to max value (forces proper reset)
415
	movlw	SID_MVx_ARP_NOTE_CTR
416
	setf	PLUSW2
417
	movlw	SID_MVx_ARP_OCT_CTR
418
	setf	PLUSW2
419
 
420
	;; reset ARP up flag (will be set to 1 with first note)
421
	movlw	SID_MVx_ARP_STATE
422
	bcf	PLUSW2, SID_MV_ARP_STATE_ARP_UP
423
 
424
	;; request first note (for oneshot function)
425
	bsf	SID_SE_STATE, SID_SE_STATE_ARP_FIRST_NOTE_REQ, BANKED
426
 
427
	;; reset divider if not disabled or if arp synch on MIDI clock start event
428
	BRA_IFSET SID_SE_STATE, SID_SE_STATE_MIDI_CLK_FA_REQ, BANKED, SIDSE_Arp_DivReset
429
	movlw	SID_Ix_Vx_ARP_MODE
430
	BRA_IFSET PLUSW0, SID_I_V_ARP_MODE_DIV_SYNC, ACCESS, SIDSE_Arp_NoDivReset
431
SIDSE_Arp_DivReset
432
	movlw	SID_MVx_ARP_DIV_CTR
433
	setf	PLUSW2
434
	movlw	SID_MVx_ARP_GL_CTR
435
	setf	PLUSW2
436
	;; request new note
437
	bsf	SID_SE_STATE, SID_SE_STATE_ARP_NEW_NOTE_REQ, BANKED
438
SIDSE_Arp_NoDivReset
439
SIDSE_Arp_NoSync
440
 
441
	;; if clock sync event:
442
	BRA_IFCLR SID_SE_STATE, SID_SE_STATE_GLOBAL_CLK_EVENT, BANKED, SIDSE_Arp_NoClk
443
SIDSE_Arp_Clk
444
	;; increment clock divider
445
	;; reset divider if it already has reached the target value
446
 
447
	movlw	SID_Ix_Vx_ARP_MODE
448
	BRA_IFCLR PLUSW0, SID_I_V_ARP_MODE_CAC, ACCESS, SIDSE_Arp_Clk_NoCAC
449
SIDSE_Arp_Clk_CAC
450
	;; in CAC mode: increment depending on number of pressed keys
451
	movlw	SID_MVx_NOTE_STACK_PTR	; using note of MV1 note stack
452
	movf	PLUSW2, W
453
	skpnz
454
	movlw	1
455
	movwf	PRODL
456
	rgoto	SIDSE_Arp_Clk_CAC_Cont
457
 
458
SIDSE_Arp_Clk_NoCAC
459
	;; if CAC disabled: increment only by one
460
	movlw	1
461
	movwf	PRODL
462
 
463
SIDSE_Arp_Clk_CAC_Cont
464
	;; add to div counter, temp. store result in PRODL
465
	movlw	SID_MVx_ARP_DIV_CTR
466
	movf	PLUSW2, W
467
	addwf	PRODL, F
468
 
469
	;; max value -> PRODH
470
	movlw	SID_Ix_Vx_ARP_SPEED_DIV
471
	movf	PLUSW0, W
472
	andlw	0x3f
473
	addlw	1
474
	movwf	PRODH
475
 
476
	;; now subtract PRODH from PRODL until PRODL is < PRODH
477
SIDSE_Arp_Clk_FixLoop
478
	decf	PRODH, W
479
	cpfsgt	PRODL, ACCESS
480
	rgoto SIDSE_Arp_Clk_FixLoop_Break
481
	movf	PRODH, W
482
	subwf	PRODL, F	; F-WREG
483
 
484
	;; request new note
485
	bsf	SID_SE_STATE, SID_SE_STATE_ARP_NEW_NOTE_REQ, BANKED
486
 
487
	;; request gate clear if voice not active anymore (required if Gln>=Speed)
488
	movlw	SID_Vx_STATE
489
	btfss	PLUSW1, SID_V_STATE_VOICE_ACTIVE
490
	bsf	SID_SE_STATE, SID_SE_STATE_ARP_GATE_CLR_REQ, BANKED
491
 
492
	rgoto	SIDSE_Arp_Clk_FixLoop
493
 
494
SIDSE_Arp_Clk_FixLoop_Break
495
 
496
	;; transfer new divider value into Vx register
497
	movlw	SID_MVx_ARP_DIV_CTR
498
	movff	PRODL, PLUSW2
499
 
500
 
501
	;; increment gatelength counter
502
	;; reset counter if it already has reached the target value
503
	movlw	SID_MVx_ARP_GL_CTR
504
	incf	PLUSW2, F
505
	decf	PLUSW2, W
506
	movwf	PRODL
507
	movlw	SID_Ix_Vx_ARP_GL_RNG
508
	movf	PLUSW0, W
509
	andlw	0x1f
510
	addlw	1
511
	cpfslt	PRODL, ACCESS
512
	rgoto SIDSE_Arp_Clk_GlReset
513
	rgoto	SIDSE_Arp_Clk_NoGlReset
514
SIDSE_Arp_Clk_GlReset
515
	;; reset counter
516
	movlw	SID_MVx_ARP_GL_CTR
517
	clrf	PLUSW2
518
 
519
	;; request gate clear
520
	bsf	SID_SE_STATE, SID_SE_STATE_ARP_GATE_CLR_REQ, BANKED
521
SIDSE_Arp_Clk_NoGlReset
522
 
523
SIDSE_Arp_NoClk
524
 
525
 
526
	;; check if HOLD mode has been deactivated - disable notes in this case
527
	movlw	SID_Ix_Vx_ARP_MODE
528
	BRA_IFSET PLUSW0, SID_I_V_ARP_MODE_HOLD, ACCESS, SIDSE_Arp_Hold_Active
529
SIDSE_Arp_Hold_NotActive
530
	;; check if HOLD flag was active before
531
	movlw	SID_MVx_ARP_STATE
532
	BRA_IFCLR PLUSW2, SID_MV_ARP_STATE_HOLD_SAVED, ACCESS, SIDSE_Arp_Hold_Cont
533
	;; clear HOLD flag and disable all notes
534
	bcf	PLUSW2, SID_MV_ARP_STATE_HOLD_SAVED
535
	rgoto	SIDSE_Arp_NotActive
536
 
537
SIDSE_Arp_Hold_Active
538
	;; store HOLD flag in MIDI voice record
539
	movlw	SID_MVx_ARP_STATE
540
	bsf	PLUSW2, SID_MV_ARP_STATE_HOLD_SAVED
541
SIDSE_Arp_Hold_Cont
542
 
543
 
544
	;; skip the rest if arp is disabled
545
	movlw	SID_Ix_Vx_ARP_MODE
546
	BRA_IFSET PLUSW0, SID_I_V_ARP_MODE_ENABLE, ACCESS, SIDSE_Arp_Active
547
SIDSE_Arp_NotActive
548
	;; check if arp was active before (for proper 1->0 transition when ARP is disabled)
549
	movlw	SID_MVx_ARP_STATE
550
	BRA_IFCLR PLUSW2, SID_MV_ARP_STATE_ARP_ACTIVE, ACCESS, SIDSE_Arp_End
551
 
552
	;; notify that arp is not active anymore
553
	bcf	PLUSW2, SID_MV_ARP_STATE_ARP_ACTIVE
554
 
555
	;; clear note stack (especially important in HOLD mode!)
556
	movlw	SID_MVx_NOTE_STACK_PTR
557
	movwf	PRODL
558
SIDSE_Arp_NotActive_ClrLoop
559
	movf	PRODL, W
560
	clrf	PLUSW2
561
	incf	PRODL, F
562
	movlw	SID_MVx_NOTE_STACK_0 + SID_MVx_NOTE_STACK_LEN - 1
563
	cpfsgt	PRODL, ACCESS
564
	rgoto SIDSE_Arp_NotActive_ClrLoop
565
 
566
	;; propagate Note Off through trigger matrix
567
	;; (static for Multi Engine)
568
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
569
	andlw	0x03
570
	bz	SIDSE_Arp_NotActive_ClrLoop_L
571
SIDSE_Arp_NotActive_ClrLoop_M
572
	;; only ENV release phase
573
	movf	SID_SE_ELEMENT_NUM, W, BANKED
574
	call	MIOS_HLP_GetBitORMask
575
	iorwf	SID_SE_TRG_EVNT_ENVR, F, BANKED
576
	rgoto	SIDSE_Arp_NotActive_ClrLoop_C
577
SIDSE_Arp_NotActive_ClrLoop_L
578
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_NOf_BASE + 0, WREG
579
	andlw	0xc0		; (Gates handled seperately by Arp)
580
	iorwf	SID_SE_TRG_EVNT_L, F, BANKED
581
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_NOf_BASE + 1, WREG
582
	iorwf	SID_SE_TRG_EVNT_H, F, BANKED
583
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_NOf_BASE + 2, WREG
584
	iorwf	SID_SE_TRG_EVNT_U, F, BANKED
585
SIDSE_Arp_NotActive_ClrLoop_C
586
 
587
	;; request gate clear
588
	movlw	SID_Vx_STATE
589
	bcf	PLUSW1, SID_V_STATE_GATE_SET_REQ
590
	bsf	PLUSW1, SID_V_STATE_GATE_CLR_REQ
591
 
592
	rgoto	SIDSE_Arp_End
593
 
594
 
595
SIDSE_Arp_Active
596
	;; notify that arp is active (for proper 1->0 transition when ARP is disabled)
597
	movlw	SID_MVx_ARP_STATE
598
	bsf	PLUSW2, SID_MV_ARP_STATE_ARP_ACTIVE
599
 
600
	;; check if voice not active anymore (not valid in HOLD mode) or gate clear has been requested
601
	;; skip voice active check in hold mode
602
	movlw	SID_Ix_Vx_ARP_MODE
603
	BRA_IFSET PLUSW0, SID_I_V_ARP_MODE_HOLD, ACCESS, SIDSE_Arp_GateClr_Hold
604
	;; skip voice active check in voice sync mode
605
	movlw	SID_Ix_Vx_ARP_MODE
606
	BRA_IFSET PLUSW0, SID_I_V_ARP_MODE_DIV_SYNC, ACCESS, SIDSE_Arp_GateClr_Sync
607
	movlw	SID_Vx_STATE
608
	BRA_IFCLR PLUSW1, SID_V_STATE_VOICE_ACTIVE, ACCESS, SIDSE_Arp_GateClr
609
SIDSE_Arp_GateClr_Sync
610
SIDSE_Arp_GateClr_Hold
611
	BRA_IFCLR SID_SE_STATE, SID_SE_STATE_ARP_GATE_CLR_REQ, BANKED, SIDSE_Arp_NoGateClr
612
SIDSE_Arp_GateClr
613
	;; forward this to note handler if gate is not already deactivated
614
	movlw	SID_Vx_STATE
615
	BRA_IFCLR PLUSW1, SID_V_STATE_GATE_ACTIVE, ACCESS, SIDSE_Arp_NoGateClr
616
	bcf	PLUSW1, SID_V_STATE_GATE_SET_REQ
617
	bsf	PLUSW1, SID_V_STATE_GATE_CLR_REQ
618
	;; Note Off ENV/LFO/... synchronisation via trigger matrix
619
	;; (static for Multi Engine)
620
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
621
	andlw	0x03
622
	bz	SIDSE_Arp_GateClr_Lead
623
SIDSE_Arp_GateClr_Multi
624
	;; only ENV release phase
625
	movf	SID_SE_ELEMENT_NUM, W, BANKED
626
	call	MIOS_HLP_GetBitORMask
627
	iorwf	SID_SE_TRG_EVNT_ENVR, F, BANKED
628
	rgoto	SIDSE_Arp_GateClr_Multi_Cont
629
SIDSE_Arp_GateClr_Lead
630
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_NOf_BASE + 0, WREG
631
	andlw	0xc0		; (Gates handled seperately by Arp)
632
	iorwf	SID_SE_TRG_EVNT_L, F, BANKED
633
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_NOf_BASE + 1, WREG
634
	iorwf	SID_SE_TRG_EVNT_H, F, BANKED
635
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_NOf_BASE + 2, WREG
636
	iorwf	SID_SE_TRG_EVNT_U, F, BANKED
637
SIDSE_Arp_GateClr_Multi_Cont
638
SIDSE_Arp_NoGateClr
639
 
640
	;; check if a new arp note has been requested
641
	BRA_IFCLR SID_SE_STATE, SID_SE_STATE_ARP_NEW_NOTE_REQ, BANKED, SIDSE_Arp_NoNewNote
642
SIDSE_Arp_NewNote
643
	;; skip if note counter is 0xaa (oneshot mode)
644
	movlw	SID_MVx_ARP_NOTE_CTR
645
	movf	PLUSW2, W
646
	xorlw	0xaa
647
	skpnz
648
	rgoto	SIDSE_Arp_NoNewNote
649
 
650
	;; increment arp counter
651
	movlw	SID_MVx_ARP_NOTE_CTR
652
	incf	PLUSW2, F
653
 
654
	;; reset gatelength counter
655
	movlw	SID_MVx_ARP_GL_CTR
656
	clrf	PLUSW2
657
 
658
	;; if max value of arp note counter reached, reset it
659
	movlw	SID_MVx_ARP_NOTE_CTR
660
	movff	PLUSW2, PRODL
661
	movlw	SID_MVx_NOTE_STACK_LEN
662
	cpfslt	PRODL, ACCESS
663
	clrf PRODL
664
 
665
	;; if note is zero, reset arp note counter
666
	movf	PRODL, W
667
	addlw	SID_MVx_NOTE_STACK_0
668
	movf	PLUSW2, W
669
	skpnz
670
	clrf	PRODL
671
 
672
	;; don't play if note still zero
673
	movf	PRODL, W
674
	addlw	SID_MVx_NOTE_STACK_0
675
	movf	PLUSW2, W
676
	skpnz
677
	rgoto	SIDSE_Arp_NoNewNote
678
 
679
	;; store new arp note counter
680
	movlw	SID_MVx_ARP_NOTE_CTR
681
	movff	PRODL, PLUSW2
682
 
683
	;; dir mode 6 (and 7) selects random direction
684
	movlw	SID_Ix_Vx_ARP_MODE
685
	rrf	PLUSW0, W
686
	andlw	0x06
687
	xorlw	0x06
688
	bz	SIDSE_Arp_NewNote_Rnd
689
 
690
	;; in DIR_ALT mode: branch depending on Up/Down flag
691
	movlw	SID_Ix_Vx_ARP_MODE
692
	BRA_IFSET PLUSW0, SID_I_V_ARP_MODE_DIR_ALT2, ACCESS, SIDSE_Arp_NewNote_AltDir
693
	BRA_IFCLR PLUSW0, SID_I_V_ARP_MODE_DIR_ALT, ACCESS, SIDSE_Arp_NewNote_NoAltDir
694
SIDSE_Arp_NewNote_AltDir
695
	;; toggle ARP_UP flag each time the arp note counter is zero
696
	movf	PRODL, W
697
	bnz	SIDSE_Arp_NewNote_AltDir_NoTog
698
SIDSE_Arp_NewNote_AltDir_Tog
699
	movlw	SID_MVx_ARP_STATE
700
	btg	PLUSW2, SID_MV_ARP_STATE_ARP_UP
701
 
702
	;; if not ALT2 mode: increment PRODL to prevent double played notes
703
	movlw	SID_Ix_Vx_ARP_MODE
704
	BRA_IFSET PLUSW0, SID_I_V_ARP_MODE_DIR_ALT2, ACCESS, SIDSE_Arp_NewNote_AltDir_Alt2
705
SIDSE_Arp_NewNote_AltDirNoAlt2
706
	incf	PRODL, F
707
SIDSE_Arp_NewNote_AltDir_Alt2
708
 
709
	;; reset PRODL again if note is zero (only one note played)
710
	movf	PRODL, W
711
	addlw	SID_MVx_NOTE_STACK_0
712
	movf	PLUSW2, W
713
	skpnz
714
	clrf	PRODL
715
 
716
	;; store updated arp note counter
717
	movlw	SID_MVx_ARP_NOTE_CTR
718
	movff	PRODL, PLUSW2
719
SIDSE_Arp_NewNote_AltDir_NoTog
720
 
721
	;; branch depending on Arp Up/Down and Alt Up/Down flag
722
	movlw	SID_Ix_Vx_ARP_MODE
723
	BRA_IFSET PLUSW0, SID_I_V_ARP_MODE_DIR_DOWN, ACCESS, SIDSE_Arp_NewNote_AltDir_D
724
SIDSE_Arp_NewNote_AltDir_U
725
	movlw	SID_MVx_ARP_STATE
726
	BRA_IFSET PLUSW2, SID_MV_ARP_STATE_ARP_UP, ACCESS, SIDSE_Arp_NewNote_Up
727
	rgoto	SIDSE_Arp_NewNote_Down
728
SIDSE_Arp_NewNote_AltDir_D
729
	movlw	SID_MVx_ARP_STATE
730
	BRA_IFSET PLUSW2, SID_MV_ARP_STATE_ARP_UP, ACCESS, SIDSE_Arp_NewNote_Down
731
	rgoto	SIDSE_Arp_NewNote_Up
732
 
733
SIDSE_Arp_NewNote_NoAltDir
734
 
735
	;; branch depending on direction
736
	movlw	SID_Ix_Vx_ARP_MODE
737
	BRA_IFCLR PLUSW0, SID_I_V_ARP_MODE_DIR_DOWN, ACCESS, SIDSE_Arp_NewNote_Up
738
SIDSE_Arp_NewNote_Down
739
	;; offset to last note stack entry: PTR-1-PRODL
740
	movlw	SID_MVx_NOTE_STACK_PTR
741
	movf	PLUSW2, W
742
	bz	SIDSE_Arp_NewNote_Down_NoNote
743
	addlw	-1
744
	movwf	PRODH
745
	movf	PRODL, W
746
	subwf	PRODH, W
747
	movwf	PRODL
748
SIDSE_Arp_NewNote_Down_NoNote
749
	rgoto	SIDSE_Arp_NewNote_Down_Cont
750
 
751
SIDSE_Arp_NewNote_Rnd
752
	;; generate new random number
753
	call	SID_RND_GenRandomNumber
754
	;; scale between 0 and SID_MVx_NOTE_STACK_PTR-1
755
	movlw	SID_MVx_NOTE_STACK_PTR
756
	movf	PLUSW2, W	; (ok, no dec!)
757
	mulwf	SID_RANDOM_SEED_L, BANKED
758
	movff	PRODH, PRODL	; new pointer in PRODL
759
 
760
SIDSE_Arp_NewNote_Down_Cont
761
SIDSE_Arp_NewNote_Up
762
 
763
	;; now check for oneshot mode: if note is 0, or if note and oct counter is 0, stop here
764
	movlw	SID_Ix_Vx_ARP_SPEED_DIV
765
	BRA_IFCLR PLUSW0, SID_I_V_ARP_SPEED_DIV_ONESHOT, ACCESS, SIDSE_Arp_NewNote_NoOneShot
766
SIDSE_Arp_NewNote_OneShot
767
	BRA_IFSET SID_SE_STATE, SID_SE_STATE_ARP_FIRST_NOTE_REQ, BANKED, SIDSE_Arp_NewNote_NoOneShot
768
	movf	PRODL, W
769
	addlw	SID_MVx_NOTE_STACK_0
770
	movf	PLUSW2, W
771
	bz	SIDSE_Arp_NewNote_OneShot_Ok
772
	movf	PRODL, W
773
	movlw	SID_MVx_ARP_NOTE_CTR
774
	movf	PLUSW2, W
775
	bnz	SIDSE_Arp_NewNote_NoOneShot
776
	movlw	SID_MVx_ARP_OCT_CTR
777
	movf	PLUSW2, W
778
	bnz	SIDSE_Arp_NewNote_NoOneShot
779
 
780
SIDSE_Arp_NewNote_OneShot_Ok
781
	;; set note counter to 0xaa to stop ARP until next reset
782
	movlw	0xaa
783
	movwf	PRODL
784
	movlw	SID_MVx_ARP_NOTE_CTR
785
	movff	PRODL, PLUSW2
786
	rgoto	SIDSE_Arp_NoNewNote
787
SIDSE_Arp_NewNote_NoOneShot
788
 
789
	;; store new arp note if != 0
790
	movf	PRODL, W
791
	addlw	SID_MVx_NOTE_STACK_0
792
	movf	PLUSW2, W
793
	andlw	0x7f
794
	movwf	PRODH
795
	movlw	SID_Vx_ARP_NOTE
796
	skpz
797
	movff	PRODH, PLUSW1
798
 
799
	;; if counter is 0, increase octave until max value is reached
800
	movf	PRODL, W
801
	bnz	SIDSE_Arp_NewNote_NoOctInc
802
SIDSE_Arp_NewNote_OctInc
803
	movlw	SID_MVx_ARP_OCT_CTR
804
	incf	PLUSW2, F
805
	decf	PLUSW2, W
806
	movwf	PRODL
807
	movlw	SID_Ix_Vx_ARP_GL_RNG
808
	swapf	PLUSW0, W
809
	rrf	WREG, W
810
	andlw	0x07
811
	cpfslt	PRODL, ACCESS
812
	rgoto SIDSE_Arp_NewNote_OctReset
813
	rgoto	SIDSE_Arp_NewNote_NoOctReset
814
SIDSE_Arp_NewNote_OctReset
815
	;; reset octave counter
816
	movlw	SID_MVx_ARP_OCT_CTR
817
	clrf	PLUSW2
818
SIDSE_Arp_NewNote_NoOctReset
819
 
820
SIDSE_Arp_NewNote_NoOctInc
821
	;; transpose note by 12*octave counter
822
	movlw	SID_MVx_ARP_OCT_CTR
823
	movf	PLUSW2, W
824
	bz	SIDSE_Arp_NewNote_OctNoTrans
825
	mullw	12
826
SIDSE_Arp_NewNote_OctTrans
827
	movlw	SID_Vx_ARP_NOTE
828
	movf	PLUSW1, W
829
	addwf	PRODL, W
830
	movwf	PRODH
831
 
832
	;; if >0x6b, decrement by 12 until we are in 0x00..0x6b range again
833
	;; (range 0x6c..0x7f sets frequency to 0xffff...)
834
SIDSE_Arp_NewNote_OctTransSat
835
	movlw	0x6b
836
	cpfsgt	PRODH, ACCESS
837
	rgoto SIDSE_Arp_NewNote_OctTransOk
838
	movlw	-12
839
	addwf	PRODH, F
840
	rgoto	SIDSE_Arp_NewNote_OctTransSat
841
SIDSE_Arp_NewNote_OctTransOk
842
	movlw	SID_Vx_ARP_NOTE
843
	movff	PRODH, PLUSW1
844
SIDSE_Arp_NewNote_OctNoTrans
845
 
846
	;; forward gate set request if voice is active and gate not active
847
	movlw	SID_Vx_STATE
848
	BRA_IFCLR PLUSW1, SID_V_STATE_VOICE_ACTIVE, ACCESS, SIDSE_Arp_NewNote_NoOn
849
SIDSE_Arp_NewNote_NoVAChk
850
	movlw	SID_Vx_STATE
851
	bcf	PLUSW1, SID_V_STATE_GATE_CLR_REQ	; ensure that gate won't be cleared by previous CLR_REQ
852
	BRA_IFSET PLUSW1, SID_V_STATE_GATE_ACTIVE, ACCESS, SIDSE_Arp_NewNote_NoOn
853
SIDSE_Arp_NewNote_On
854
	;; set gate
855
	bsf	PLUSW1, SID_V_STATE_GATE_SET_REQ
856
 
857
	;; Note On ENV/LFO/... synchronisation via trigger matrix
858
	;; (static for Multi Engine)
859
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
860
	andlw	0x03
861
	bz	SIDSE_Arp_NewNote_Lead
862
SIDSE_Arp_NewNote_Multi
863
	;; only ENV attack, LFO sync and WT reset
864
	movf	SID_SE_ELEMENT_NUM, W, BANKED
865
	call	MIOS_HLP_GetBitORMask
866
	iorwf	SID_SE_TRG_EVNT_ENVA, F, BANKED
867
 
868
	movf	SID_SE_ELEMENT_NUM, W, BANKED
869
	addlw	2
870
	call	MIOS_HLP_GetBitORMask
871
	iorwf	SID_SE_TRG_EVNT_H, F, BANKED
872
 
873
	movlw	0x3f
874
	iorwf	SID_SE_TRG_EVNT_U, F, BANKED
875
	;; TODO: optional LFO resync!
876
	rgoto	SIDSE_Arp_NewNote_Cont
877
 
878
SIDSE_Arp_NewNote_Lead
879
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_NOn_BASE + 0, WREG
880
	andlw	0xc0		; (gates handled seperately)
881
	iorwf	SID_SE_TRG_EVNT_L, F, BANKED
882
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_NOn_BASE + 1, WREG
883
	iorwf	SID_SE_TRG_EVNT_H, F, BANKED
884
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_TRG_NOn_BASE + 2, WREG
885
	iorwf	SID_SE_TRG_EVNT_U, F, BANKED
886
SIDSE_Arp_NewNote_Cont
887
SIDSE_Arp_NewNote_NoOn
888
 
889
SIDSE_Arp_NoNewNote
890
 
891
SIDSE_Arp_End
892
	;; clear arp sync flag
893
	movlw	SID_MVx_ARP_STATE
894
	bcf	PLUSW2, SID_MV_ARP_STATE_SYNC_ARP
895
 
896
	return
897
 
898
 
899
 
900
;; --------------------------------------------------------------------------
901
;; This function handles the Voice Gate
902
;; IN: pointer to SID_Ix_e_SxVy_BASE in FSR0 (patch record)
903
;;     pointer to SIDx_Vx_BASE in FSR1 (voice record)
904
;;     Voice number in SID_SE_ELEMENT_NUM
905
;;     If SIDSE_Gate_Drums is called: bit #4 of Waveform (disable bit) in IRQ_TMP1
906
;; OUT: ZERO flag set if pitch should be changed
907
;; --------------------------------------------------------------------------
908
SIDSE_Gate
909
	;; small variation between Lead/Bassline/Multi Engine, and Drum Engine:
910
	;; for Drum Engine the waveform is predefined in model table, and not
911
	;; part of the patch (drum engine has to call SIDSE_Gate_Drums with
912
	;; waveform[4] in IRQ_TMP1)
913
	movlw	SID_Ix_Vx_WAVEFORM
914
	movff	PLUSW0, IRQ_TMP1
915
 
916
SIDSE_Gate_Drums		; expecting waveform[4] in IRQ_TMP1
917
 
918
	;; transfer pointer to SIDx_Vx_FRQ_L register -> FSR2
919
	call	SIDSE_Hlp_GetSIDFrqPtr
920
 
921
	;; voice disable handling (allows to turn on/off voice via waveform parameter)
922
	movlw	SID_Vx_STATE
923
	BRA_IFCLR PLUSW1, SID_V_STATE_VOICE_DISABLED, ACCESS, SIDSE_Gate_VoiceNotDisabled
924
SIDSE_Gate_VoiceDisabled
925
	BRA_IFSET IRQ_TMP1, 4, ACCESS, SIDSE_Gate_Voice_Cont	; IRQ_TMP1 == SID_Ix_Vx_WAVEFORM
926
	movlw	SID_Vx_STATE
927
	bcf	PLUSW1, SID_V_STATE_VOICE_DISABLED
928
	btfsc	PLUSW1, SID_V_STATE_VOICE_ACTIVE
929
	bsf	PLUSW1, SID_V_STATE_GATE_SET_REQ
930
	rgoto	SIDSE_Gate_Voice_Cont
931
SIDSE_Gate_VoiceNotDisabled
932
	BRA_IFCLR IRQ_TMP1, 4, ACCESS, SIDSE_Gate_Voice_Cont	; IRQ_TMP1 == SID_Ix_Vx_WAVEFORM
933
	movlw	SID_Vx_STATE
934
	bsf	PLUSW1, SID_V_STATE_VOICE_DISABLED
935
	bsf	PLUSW1, SID_V_STATE_GATE_CLR_REQ
936
	;; 	rgoto	SIDSE_Gate_Voice_Cont
937
SIDSE_Gate_Voice_Cont
938
 
939
	;; if gate not active: ignore clear request
940
	movlw	SID_Vx_STATE
941
	btfss	PLUSW1, SID_V_STATE_GATE_ACTIVE
942
	bcf	PLUSW1, SID_V_STATE_GATE_CLR_REQ
943
 
944
	;; gate set/clear request?
945
	BRA_IFSET PLUSW1, SID_V_STATE_GATE_CLR_REQ, ACCESS, SIDSE_Gate_ClrReq
946
	BRA_IFSET PLUSW1, SID_V_STATE_GATE_SET_REQ, ACCESS, SIDSE_Gate_SetReq
947
	rgoto	SIDSE_Gate_Skip
948
SIDSE_Gate_ClrReq
949
	bcf	PLUSW1, SID_V_STATE_GATE_CLR_REQ
950
 
951
	movlw	SIDx_V1_CTRL
952
	bcf	PLUSW2, 3	; SIDx_Vx_CTRL.3 is the test bit which allows to sync an oscillator
953
 
954
	;; clear SID gate flag (SIDx_Vx_CTRL.0) if GSA (gate stays active) function not enabled
955
	movlw	SID_Ix_Vx_FLAGS1
956
	BRA_IFSET PLUSW0, SID_I_V_FLAGS1_GSA, ACCESS, SIDSE_Gate_ClrReq_GSA
957
SIDSE_Gate_ClrReq_NoGSA
958
	movlw	SIDx_V1_CTRL
959
	bcf	PLUSW2, 0
960
SIDSE_Gate_ClrReq_GSA
961
 
962
	;; gate not active anymore
963
	movlw	SID_Vx_STATE
964
	bcf	PLUSW1, SID_V_STATE_GATE_ACTIVE
965
 
966
	;; sync with SID_SR handler
967
	movf	SID_SE_ELEMENT_NUM, W, BANKED
968
	call	MIOS_HLP_GetBitORMask
969
	iorwf	SID_SE_SR_UPDATE_SYNC, F, BANKED	; (cleared by SID_SR_Handler)
970
 
971
	rgoto	SIDSE_Gate_End
972
 
973
SIDSE_Gate_SetReq
974
	;; skip so long SRs haven't been updated
975
	movf	SID_SE_ELEMENT_NUM, W, BANKED
976
	call	MIOS_HLP_GetBitORMask
977
	andwf	SID_SE_SR_UPDATE_SYNC, W, BANKED
978
	bnz	SIDSE_Gate_Skip
979
 
980
	;; don't set gate if oscillator disabled
981
	BRA_IFSET IRQ_TMP1, 4, ACCESS, SIDSE_Gate_SetReqSkp	; IRQ_TMP1 == SID_Ix_Vx_WAVEFORM
982
 
983
	;; temporary shift FSR1 to SID_Vx_SET_DELAY_CTR_L for easier handling
984
	movlw	SID_Vx_SET_DELAY_CTR_L
985
	addwf	FSR1L, F
986
 
987
	;; delay note so long 16bit delay counter != 0
988
	movf	POSTINC1, W
989
	iorwf	POSTDEC1, W
990
	bz	SIDSE_Gate_SetReq_NoDelay
991
SIDSE_Gate_SetReq_Delay
992
	;; increment counter, set it to zero on overrun (no delay anymore)
993
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
994
	andlw	0x03
995
	xorlw	0x02
996
	bnz	SIDSE_Gate_SetReq_Delay_LBM
997
SIDSE_Gate_SetReq_Delay_D
998
	clrf	IRQ_TMP3	; not available for drum engine
999
	rgoto	SIDSE_Gate_SetReq_Delay_Cont
1000
SIDSE_Gate_SetReq_Delay_LBM
1001
	movlw	SID_Ix_Vx_DELAY
1002
	movff	PLUSW0, IRQ_TMP3; incrementer is the same like used for envelopes
1003
SIDSE_Gate_SetReq_Delay_Cont
1004
 
1005
	;; if ABW (ADSR bug workaround) active: use at least 30 ms delay
1006
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_OPT1_FLAGS, WREG
1007
	BRA_IFCLR WREG, SID_I_OPT1_FLAGS_ABW, ACCESS, SIDSE_Gate_SetReq_DelayNoA
1008
SIDSE_Gate_SetReq_DelayABW
1009
	movlw	25
1010
	addwf	IRQ_TMP3, F
1011
	skpnc
1012
	setf	IRQ_TMP3
1013
SIDSE_Gate_SetReq_DelayNoA
1014
 
1015
	movlw	0x80		; curve *must* be disabled!
1016
	call	SIDSE_Hlp_ENV_GetBendedValue	; incrementer in MIOS_PARAMETER[12]
1017
	movf	MIOS_PARAMETER1, W
1018
	addwf	POSTINC1, F
1019
	movf	MIOS_PARAMETER2, W
1020
	addwfc	POSTDEC1, F
1021
	bc	SIDSE_Gate_SetReq_DelayOv
1022
	movlw	-SID_Vx_SET_DELAY_CTR_L	; switch back to SID_Vx_CTR_L
1023
	addwf	FSR1L, F
1024
#if 0
1025
	rgoto	SIDSE_Gate_End
1026
#else
1027
	;; experimental: don't modify frequency so long delay is active!
1028
	;; this is especially to avoid, that a new note frequency will already be set before the envelope is released
1029
	rgoto	SIDSE_Gate_End_NoPitch
1030
#endif
1031
SIDSE_Gate_SetReq_DelayOv
1032
	;; overrun: clear counter to disable delay
1033
	clrf	POSTINC1
1034
	clrf	POSTDEC1
1035
SIDSE_Gate_SetReq_NoDelay
1036
	movlw	-SID_Vx_SET_DELAY_CTR_L	; switch back to SID_Vx_CTR_L
1037
	addwf	FSR1L, F
1038
 
1039
	;; now acknowledge the set request
1040
	movlw	SID_Vx_STATE
1041
	bcf	PLUSW1, SID_V_STATE_GATE_SET_REQ
1042
 
1043
	;; if ABW (ADSR bug workaround) function active: update ADSR registers now!
1044
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_OPT1_FLAGS, WREG
1045
	BRA_IFCLR WREG, SID_I_OPT1_FLAGS_ABW, ACCESS, SIDSE_Gate_SetReq_NoABW
1046
SIDSE_Gate_SetReq_ABW
1047
	movlw	SID_Ix_Vx_AD
1048
	movff	PLUSW0, PRODL
1049
	movlw	SIDx_V1_ENV_AD
1050
	movff	PRODL, PLUSW2
1051
 
1052
	;; accent only used in bassline and drum mode
1053
	;; force sustain to maximum if accent active
1054
	movlw	SID_Ix_Vx_SR
1055
	movff	PLUSW0, PRODL
1056
	movlw	SID_Vx_STATE
1057
	BRA_IFCLR PLUSW1, SID_V_STATE_ACCENT, ACCESS, SIDSE_Gate_SetReq_ABW_NoAcc
1058
SIDSE_Gate_SetReq_ABW_Acc
1059
	movlw	0xf0
1060
	iorwf	PRODL, F
1061
SIDSE_Gate_SetReq_ABW_NoAcc
1062
	movlw	SIDx_V1_ENV_SR
1063
	movff	PRODL, PLUSW2
1064
SIDSE_Gate_SetReq_NoABW
1065
 
1066
	;; branch for phase synchronisation: set test flag instead of gate
1067
	;; a special handler in sid_sr.inc will take care about the rest
1068
 
1069
	;; different handling depending on selected engine:
1070
	;; lead engine: global sync for all voices with phase shift
1071
	;; multi engine has individual flag for each instrument
1072
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
1073
	BRA_IFSET WREG, 1, ACCESS, SIDSE_Gate_SetReq_Sync_DM
1074
SIDSE_Gate_SetReq_Sync_LB
1075
	BRA_IFSET WREG, 0, ACCESS, SIDSE_Gate_SetReq_Sync_B
1076
SIDSE_Gate_SetReq_Sync_L
1077
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_OSC_PHASE, WREG
1078
	andlw	0xff
1079
	bz	SIDSE_Gate_SetReq_NoSync
1080
	rgoto	SIDSE_Gate_SetReq_Sync_Cont
1081
 
1082
SIDSE_Gate_SetReq_Sync_B
1083
	movlw	SID_Ix_B_Vx_FLAGS2
1084
	BRA_IFCLR PLUSW0, SID_I_M_V_FLAGS2_PHASE, ACCESS, SIDSE_Gate_SetReq_NoSync
1085
	rgoto	SIDSE_Gate_SetReq_NoSync
1086
 
1087
SIDSE_Gate_SetReq_Sync_DM
1088
	BRA_IFSET WREG, 0, ACCESS, SIDSE_Gate_SetReq_Sync_M
1089
SIDSE_Gate_SetReq_Sync_D
1090
	;; synchronisation requested in SIDSE_D_NOTE_Restart
1091
	rgoto	SIDSE_Gate_SetReq_NoSync
1092
 
1093
SIDSE_Gate_SetReq_Sync_M
1094
	movlw	SID_Ix_M_Vx_FLAGS2
1095
	BRA_IFCLR PLUSW0, SID_I_M_V_FLAGS2_PHASE, ACCESS, SIDSE_Gate_SetReq_NoSync
1096
	;; 	rgoto	SIDSE_Gate_SetReq_Sync_Cont
1097
 
1098
SIDSE_Gate_SetReq_Sync_Cont
1099
	;; set SID test flag (SIDx_Vx_CTRL.3), ensure that gate flag not set
1100
	movlw	SIDx_V1_CTRL
1101
	bsf	PLUSW2, 3
1102
	bcf	PLUSW2, 0
1103
 
1104
	;; notify sync request
1105
	movf	SID_SE_ELEMENT_NUM, W, BANKED
1106
	call	MIOS_HLP_GetBitORMask
1107
	iorwf	SID_SE_PHASE_SYNC_REQ, F, BANKED
1108
 
1109
	rgoto	SIDSE_Gate_SetReq_NoGate
1110
 
1111
SIDSE_Gate_SetReq_NoSync
1112
	;; set SID gate flag (SIDx_Vx_CTRL.0)
1113
	movlw	SIDx_V1_CTRL
1114
	bsf	PLUSW2, 0
1115
 
1116
SIDSE_Gate_SetReq_NoGate
1117
SIDSE_Gate_SetReqSkp
1118
	movlw	SID_Vx_STATE
1119
	bsf	PLUSW1, SID_V_STATE_GATE_ACTIVE
1120
 
1121
SIDSE_Gate_End
1122
SIDSE_Gate_Skip
1123
	iorlw	0xff		; clear ZERO flag (-> pitch should be changed)
1124
	return
1125
 
1126
 
1127
SIDSE_Gate_End_NoPitch
1128
	andlw	0x00		; set ZERO flag (-> pitch should not be changed)
1129
	return
1130
 
1131
 
1132
;; --------------------------------------------------------------------------
1133
;; This function handles the Voice Pitch
1134
;; IN: pointer to SID_Ix_e_SxVy_BASE in FSR0 (patch record)
1135
;;     pointer to SIDx_Vx_BASE in FSR1 (voice record)
1136
;;     Voice number in SID_SE_ELEMENT_NUM
1137
;; --------------------------------------------------------------------------
1138
SIDSE_Pitch
1139
 
1140
	;; ------------------------------------------------------------------
1141
	;; Transpose MIDI Note
1142
	;; ------------------------------------------------------------------
1143
SIDSE_Pitch_Transp
1144
	;; if arp mode is active: use SID_Vx_ARP_NOTE, otherwise SID_Vx_NOTE
1145
	movlw	SID_Ix_Vx_ARP_MODE
1146
	BRA_IFCLR PLUSW0, SID_I_V_ARP_MODE_ENABLE, ACCESS, SIDSE_Pitch_Transp_NoArp
1147
SIDSE_Pitch_Transp_Arp
1148
	movlw	SID_Vx_ARP_NOTE
1149
	rgoto	SIDSE_Pitch_Transp_Cont
1150
SIDSE_Pitch_Transp_NoArp
1151
	movlw	SID_Vx_NOTE
1152
	;; 	rgoto	SIDSE_Pitch_Transp_Cont
1153
SIDSE_Pitch_Transp_Cont
1154
	movff	PLUSW1, SID_SE_TRANSPOSED_NOTE
1155
 
1156
	;; pointer to MIDI note -> FSR2
1157
	lfsr	FSR2, SID_MV1_BASE
1158
	movlw	SID_Vx_ASSIGNED_MV
1159
	movf	PLUSW1, W
1160
	mullw	SID_MVx_RECORD_LEN
1161
	movf	PRODL, W
1162
	addwf	FSR2L, F
1163
 
1164
	;; add voice based transpose to MIDI voice based transpose and saturate
1165
	;; if lead engine: transpose value taken from first MIDI voice!
1166
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
1167
	andlw	0x03
1168
	xorlw	0x01
1169
	bnz	SIDSE_Pitch_Transp_NoBassline
1170
SIDSE_Pitch_Transp_Bassline
1171
	;; if WTO enabled, note is not transposed via MIDI voice (instead the sequence number will be transposed)
1172
	movlw	SID_Ix_B_Vx_FLAGS2
1173
	BRA_IFCLR PLUSW0, SID_I_B_V_FLAGS2_WT_ONLY, ACCESS, SIDSE_Pitch_Transp_NoLead
1174
	movlw	0x40
1175
	movwf	IRQ_TMP1
1176
	rgoto	SIDSE_Pitch_Transp_Bassline_Seq
1177
SIDSE_Pitch_Transp_NoBassline
1178
	xorlw	0x01
1179
	bnz	SIDSE_Pitch_Transp_NoLead
1180
SIDSE_Pitch_Transp_Lead
1181
	lfsr	FSR2, SID_MV1_BASE
1182
SIDSE_Pitch_Transp_NoLead
1183
	movlw	SID_MVx_TRANSPOSE
1184
	movff	PLUSW2, IRQ_TMP1
1185
SIDSE_Pitch_Transp_Bassline_Seq
1186
	movlw	SID_Ix_Vx_TRANSPOSE
1187
	movf	PLUSW0, W
1188
	addwf	IRQ_TMP1, W
1189
	sublw	0x80
1190
	xorlw	0xff
1191
	movwf	IRQ_TMP1
1192
 
1193
	;; transpose note
1194
	addwf	SID_SE_TRANSPOSED_NOTE, W, BANKED
1195
	addlw	1
1196
	BRA_IFCLR WREG, 7, ACCESS, SIDSE_Pitch_Transp_NoOverflow
1197
SIDSE_Pitch_Transp_Overflow
1198
	btfss	IRQ_TMP1, 7; pos saturation
1199
	movlw 0x7f
1200
	btfsc	IRQ_TMP1, 7; neg saturation
1201
	movlw 0x00
1202
SIDSE_Pitch_Transp_NoOverflow
1203
	movwf	SID_SE_TRANSPOSED_NOTE, BANKED
1204
 
1205
 
1206
	;; ------------------------------------------------------------------
1207
	;; Glissando Handling
1208
	;; ------------------------------------------------------------------
1209
	movlw	SID_Vx_OLD_TRANSP_NOTE		; store new transposed note?
1210
	movf	PLUSW1, W
1211
	cpfseq	SID_SE_TRANSPOSED_NOTE, BANKED
1212
	rgoto SIDSE_Pitch_GlissNewNote
1213
	rgoto	SIDSE_Pitch_GlissNoNewNote
1214
SIDSE_Pitch_GlissNewNote
1215
	movlw	SID_Vx_OLD_TRANSP_NOTE		; store new transposed note!
1216
	movff	SID_SE_TRANSPOSED_NOTE, PLUSW1
1217
 
1218
	;; init portamento/glissando counter if glissando active
1219
	movlw	SID_Ix_Vx_FLAGS1
1220
	BRA_IFCLR PLUSW0, SID_I_V_FLAGS1_PORTA_GLISSANDO, ACCESS, SIDSE_Pitch_GlissNoNewNote
1221
	movlw	SID_Vx_PORTA_CTR_L
1222
	setf	PLUSW1		; force overrun
1223
	movlw	SID_Vx_PORTA_CTR_H
1224
	setf	PLUSW1
1225
SIDSE_Pitch_GlissNoNewNote
1226
 
1227
	;; portamento active?
1228
	movlw	SID_Vx_STATE
1229
	BRA_IFCLR PLUSW1, SID_V_STATE_PORTA_ACTIVE, ACCESS, SIDSE_Pitch_Gliss_Final
1230
 
1231
	;; glissando active?
1232
	movlw	SID_Ix_Vx_FLAGS1
1233
	BRA_IFCLR PLUSW0, SID_I_V_FLAGS1_PORTA_GLISSANDO, ACCESS, SIDSE_Pitch_Gliss_Final
1234
 
1235
	;; get portamento multiplier from envelope table -> MUL_A
1236
	;; this one is used for "constant time glide" and "normal portamento"
1237
	movlw	SID_Ix_Vx_PORTAMENTO
1238
	movf	PLUSW0, W
1239
	skpnz
1240
	rgoto	SIDSE_Pitch_Gliss_Final	; if rate has been changed to zero, set target frequency and finish portamento
1241
	clrc
1242
	rrf	WREG, W		; make it faster
1243
	TABLE_ADDR_MUL_W SID_ENV_TABLE, 2	; determine table address
1244
	tblrd*+				; transfer table entry to MUL_A_[LH]
1245
	movff	TABLAT, MUL_A_L
1246
	tblrd*+
1247
	movff	TABLAT, MUL_A_H
1248
 
1249
	;; increment portamento counter by MUL_A_[LH], result in MUL_A_[LH]
1250
	movlw	SID_Vx_PORTA_CTR_L
1251
	movf	PLUSW1, W
1252
	addwf	MUL_A_L, F, BANKED
1253
	movlw	SID_Vx_PORTA_CTR_H
1254
	movf	PLUSW1, W
1255
	addwfc	MUL_A_H, F, BANKED
1256
	bc	SIDSE_Pitch_Gliss_Ov
1257
SIDSE_Pitch_Gliss_NoOv
1258
	;; store new counter and continue with current note
1259
	movlw	SID_Vx_PORTA_CTR_L
1260
	movff	MUL_A_L, PLUSW1
1261
	movlw	SID_Vx_PORTA_CTR_H
1262
	movff	MUL_A_H, PLUSW1
1263
	movlw	SID_Vx_TRANSP_NOTE
1264
	movff	PLUSW1, SID_SE_TRANSPOSED_NOTE
1265
	rgoto	SIDSE_Pitch_Gliss_End
1266
 
1267
SIDSE_Pitch_Gliss_Ov
1268
	;; reset portamento/glissando counter
1269
	movlw	SID_Vx_PORTA_CTR_L
1270
	clrf	PLUSW1
1271
	movlw	SID_Vx_PORTA_CTR_H
1272
	clrf	PLUSW1
1273
 
1274
	;; increment/decrement note
1275
	movlw	SID_Vx_TRANSP_NOTE
1276
	movf	PLUSW1, W
1277
	movwf	IRQ_TMP1
1278
	cpfsgt	SID_SE_TRANSPOSED_NOTE, BANKED
1279
	rgoto SIDSE_Pitch_Gliss_Dec
1280
SIDSE_Pitch_Gliss_Inc
1281
	incf	IRQ_TMP1, F	; increment note and check if final value reached
1282
	rgoto	SIDSE_Pitch_Gliss_Inc_Cont
1283
SIDSE_Pitch_Gliss_Dec
1284
	decf	IRQ_TMP1, F	; decrement note and check if final value reached
1285
	;; 	rgoto	SIDSE_Pitch_Gliss_Inc_Cont
1286
 
1287
SIDSE_Pitch_Gliss_Inc_Cont
1288
	BRA_IFSET IRQ_TMP1, 7, ACCESS, SIDSE_Pitch_Gliss_Final
1289
	movf	IRQ_TMP1, W
1290
	cpfseq	SID_SE_TRANSPOSED_NOTE, BANKED
1291
	rgoto SIDSE_Pitch_Gliss_NewNote
1292
	rgoto	SIDSE_Pitch_Gliss_Final
1293
SIDSE_Pitch_Gliss_NewNote
1294
	movff	IRQ_TMP1, SID_SE_TRANSPOSED_NOTE
1295
	movlw	SID_Vx_TRANSP_NOTE
1296
	movff	SID_SE_TRANSPOSED_NOTE, PLUSW1
1297
	rgoto	SIDSE_Pitch_Gliss_End
1298
 
1299
SIDSE_Pitch_Gliss_Final
1300
	movlw	SID_Vx_OLD_TRANSP_NOTE	; target note reached
1301
	movff	PLUSW1, SID_SE_TRANSPOSED_NOTE
1302
	movlw	SID_Vx_TRANSP_NOTE
1303
	movff	SID_SE_TRANSPOSED_NOTE, PLUSW1
1304
 
1305
	;; deactivate porta active gate if glissando active
1306
	movlw	SID_Ix_Vx_FLAGS1
1307
	BRA_IFCLR PLUSW0, SID_I_V_FLAGS1_PORTA_GLISSANDO, ACCESS, SIDSE_Pitch_Gliss_End
1308
	movlw	SID_Vx_STATE
1309
	bcf	PLUSW1, SID_V_STATE_PORTA_ACTIVE
1310
SIDSE_Pitch_Gliss_End
1311
 
1312
	;; ------------------------------------------------------------------
1313
	;; Determine Target frequency depending on transposed note
1314
	;; ------------------------------------------------------------------
1315
SIDSE_Pitch_InitTargetFrq
1316
	;; use linear value if O2A activated
1317
	movff	SID_LOCAL_ENS + SID_ENSx_CTRL2, WREG
1318
	BRA_IFCLR WREG, SID_ENS_CTRL2_O2A, ACCESS, SIDSE_Pitch_InitTargetFrq_NoO2A
1319
SIDSE_Pitch_InitTargetFrq_O2A
1320
	clrf	SID_SE_TARGET_FRQ_L, BANKED
1321
	clrc
1322
	rlf	SID_SE_TRANSPOSED_NOTE, W, BANKED
1323
	movwf	SID_SE_TARGET_FRQ_H, BANKED
1324
	rgoto	SIDSE_Pitch_InitTargetFrq_O2A_C
1325
 
1326
SIDSE_Pitch_InitTargetFrq_NoO2A
1327
	movf	SID_SE_TRANSPOSED_NOTE, W, BANKED
1328
	addlw	21			; due to the new frequency table we have to transpose
1329
	btfsc	WREG, 7; the note value
1330
	movlw 0x7f
1331
	TABLE_ADDR_MUL_W SID_FRQ_TABLE, 2	; determine table address
1332
	tblrd*+				; transfer table entry to SID_SE_TARGET_FRQ_[LH]
1333
	movff	TABLAT, SID_SE_TARGET_FRQ_L
1334
	tblrd*+
1335
	movff	TABLAT, SID_SE_TARGET_FRQ_H
1336
SIDSE_Pitch_InitTargetFrq_O2A_C
1337
 
1338
	;; ------------------------------------------------------------------
1339
	;; Increase/Decrease target frequency by pitchrange depending on
1340
	;; Pitchbender and Finetune value
1341
	;; ------------------------------------------------------------------
1342
 
1343
	;; skip Pitchbender+Finetune processing if PITCHRANGE == zero
1344
	movlw	SID_Ix_Vx_PITCHRANGE
1345
	movf	PLUSW0, W
1346
	skpnz
1347
	rgoto	SIDSE_Pitch_NoOffset
1348
SIDSE_Pitch_Offset
1349
	;; determine scale multipler (9 bit signed value) -> MUL_B_L
1350
	clrf	MUL_B_L, BANKED
1351
	clrf	MUL_B_H, BANKED
1352
 
1353
	;; multi/bassline/drum engine: take pitchbender value from MIDI voice (pointer still in FSR2)
1354
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
1355
	BRA_IFSET WREG, 1, ACCESS, SIDSE_Pitch_PB_DM
1356
SIDSE_Pitch_PB_LB
1357
	BRA_IFSET WREG, 0, ACCESS, SIDSE_Pitch_PB_B
1358
SIDSE_Pitch_PB_L
1359
	movlw	SID_Vx_PITCHBENDER
1360
	movf	PLUSW1, W
1361
	rgoto	SIDSE_Pitch_PB_Cont
1362
 
1363
SIDSE_Pitch_PB_B
1364
SIDSE_Pitch_PB_DM
1365
	movlw	SID_MVx_PITCHBENDER
1366
	movf	PLUSW2, W
1367
	;; 	rgoto	SIDSE_Pitch_PB_Cont
1368
 
1369
SIDSE_Pitch_PB_Cont
1370
	movwf	IRQ_TMP1
1371
	bz	SIDSE_Pitch_NoPitchBender
1372
SIDSE_Pitch_PitchBender
1373
	clrc
1374
	rlf	IRQ_TMP1, W
1375
	addwf	MUL_B_L, F, BANKED
1376
	movlw	0x00
1377
	btfsc	IRQ_TMP1, 7
1378
	movlw 0xff
1379
	addwfc	MUL_B_H, F, BANKED
1380
SIDSE_Pitch_NoPitchBender
1381
 
1382
	movlw	SID_Ix_Vx_FINETUNE
1383
	movf	PLUSW0, W
1384
	movwf	IRQ_TMP1
1385
	xorlw	0x80
1386
	bz	SIDSE_Pitch_NoFinetune
1387
SIDSE_Pitch_Finetune
1388
	clrc
1389
	rlf	IRQ_TMP1, W
1390
	addwf	MUL_B_L, F, BANKED
1391
	movlw	0x00
1392
	btfss	IRQ_TMP1, 7
1393
	movlw 0xff
1394
	addwfc	MUL_B_H, F, BANKED
1395
SIDSE_Pitch_NoFinetune
1396
 
1397
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
1398
	andlw	0x03
1399
	bnz	SIDSE_Pitch_NoDetune
1400
	SET_BSR	SID_PATCH_BUFFER_SHADOW
1401
	movf	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_OSC_DETUNE, W, BANKED
1402
	SET_BSR	SID_BASE
1403
	bz	SIDSE_Pitch_NoDetune
1404
SIDSE_Pitch_Detune
1405
	movwf	IRQ_TMP1
1406
	;; additional detuning depending on SID channel and oscillator
1407
	;; Left OSC1: +detune/4
1408
	;; Right OSC1: -detune/4
1409
	;; Left OSC2: +detune
1410
	;; Right OSC2: -detune
1411
	;; Left OSC3: -detune
1412
	;; Right OSC3: +detune
1413
 
1414
	BRA_IFSET SID_SE_ELEMENT_NUM, 2, BANKED, SIDSE_Pitch_Detune_45
1415
SIDSE_Pitch_Detune_0123
1416
	BRA_IFSET SID_SE_ELEMENT_NUM, 1, BANKED, SIDSE_Pitch_Detune_23
1417
SIDSE_Pitch_Detune_01
1418
	BRA_IFSET SID_SE_ELEMENT_NUM, 0, BANKED, SIDSE_Pitch_Detune_1
1419
SIDSE_Pitch_Detune_0	; SIDL, OSC1
1420
	;; in mono mode: don't detune OSC1, so that at least one oscillator runs at the target frequency!
1421
	movff	SID_LOCAL_ENS + SID_ENSx_CTRL1, WREG
1422
	BRA_IFSET WREG, SID_ENS_CTRL1_MONO, ACCESS, SIDSE_Pitch_NoDetune
1423
	clrc
1424
	rrf	IRQ_TMP1, F
1425
	clrc
1426
	rrf	IRQ_TMP1, F
1427
	rgoto	SIDSE_Pitch_Detune_Add
1428
SIDSE_Pitch_Detune_1	; SIDL, OSC2
1429
	rgoto	SIDSE_Pitch_Detune_Add
1430
 
1431
SIDSE_Pitch_Detune_23
1432
	BRA_IFSET SID_SE_ELEMENT_NUM, 0, BANKED, SIDSE_Pitch_Detune_3
1433
SIDSE_Pitch_Detune_2	; SIDL, OSC3
1434
	rgoto	SIDSE_Pitch_Detune_Sub
1435
SIDSE_Pitch_Detune_3	; SIDR, OSC1
1436
	clrc
1437
	rrf	IRQ_TMP1, F
1438
	clrc
1439
	rrf	IRQ_TMP1, F
1440
	rgoto	SIDSE_Pitch_Detune_Sub
1441
 
1442
SIDSE_Pitch_Detune_45
1443
	BRA_IFSET SID_SE_ELEMENT_NUM, 0, BANKED, SIDSE_Pitch_Detune_5
1444
SIDSE_Pitch_Detune_4	; SIDR, OSC2
1445
	rgoto	SIDSE_Pitch_Detune_Sub
1446
SIDSE_Pitch_Detune_5	; SIDR, OSC3
1447
	rgoto	SIDSE_Pitch_Detune_Add
1448
 
1449
SIDSE_Pitch_Detune_Sub
1450
	comf	IRQ_TMP1, W
1451
	addlw	1
1452
	addwf	MUL_B_L, F, BANKED
1453
	movf	IRQ_TMP1, W
1454
	skpz
1455
	movlw	0xff
1456
	addwfc	MUL_B_H, F, BANKED
1457
	rgoto	SIDSE_Pitch_Detune_Cont
1458
 
1459
SIDSE_Pitch_Detune_Add
1460
	movf	IRQ_TMP1, W
1461
	addwf	MUL_B_L, F, BANKED
1462
	movlw	0x00
1463
	addwfc	MUL_B_H, F, BANKED
1464
	;; 	rgoto	SIDSE_Pitch_Detune_Cont
1465
SIDSE_Pitch_Detune_Cont
1466
SIDSE_Pitch_NoDetune
1467
 
1468
 
1469
	;; skip tuning if scale multiplier is zero
1470
	movf	MUL_B_L, W, BANKED
1471
	iorwf	MUL_B_H, W, BANKED
1472
	bz	SIDSE_Pitch_NoOffset
1473
 
1474
	;; get f_in[target], save it in MIOS_PARAMETER[12]
1475
	;; add pitchrange depending on direction with saturation
1476
	BRA_IFSET MUL_B_H, 7, BANKED, SIDSE_Pitch_Frq_Dec
1477
SIDSE_Pitch_Frq_Inc
1478
	movlw	SID_Ix_Vx_PITCHRANGE
1479
	movf	PLUSW0, W
1480
	addwf	SID_SE_TRANSPOSED_NOTE, W, BANKED
1481
	btfsc	WREG, 7
1482
	movlw 0x7f
1483
	rgoto	SIDSE_Pitch_Frq_Inc_Cont
1484
SIDSE_Pitch_Frq_Dec
1485
	movlw	SID_Ix_Vx_PITCHRANGE
1486
	movf	PLUSW0, W
1487
	subwf	SID_SE_TRANSPOSED_NOTE, W, BANKED
1488
	btfsc	WREG, 7
1489
	movlw 0x00
1490
SIDSE_Pitch_Frq_Inc_Cont
1491
 
1492
	;; use linear value if O2A activated
1493
	movff	SID_LOCAL_ENS + SID_ENSx_CTRL2, PRODL
1494
	BRA_IFCLR PRODL, SID_ENS_CTRL2_O2A, ACCESS, SIDSE_Pitch_Frq_TuneFrq_NoO2A
1495
SIDSE_Pitch_Frq_TuneFrq_O2A
1496
	clrf	MIOS_PARAMETER1
1497
	clrc
1498
	rlf	WREG, W
1499
	movwf	MIOS_PARAMETER2
1500
	rgoto	SIDSE_Pitch_Frq_TuneFrq_O2A_C
1501
 
1502
SIDSE_Pitch_Frq_TuneFrq_NoO2A
1503
	addlw	21			; due to the new frequency table we have to transpose
1504
	btfsc	WREG, 7; the note value
1505
	movlw 0x7f
1506
	TABLE_ADDR_MUL_W SID_FRQ_TABLE, 2	; determine table address
1507
	tblrd*+				; transfer table entry to MIOS_PARAMETER[12]
1508
	movff	TABLAT, MIOS_PARAMETER1
1509
	tblrd*+
1510
	movff	TABLAT, MIOS_PARAMETER2
1511
SIDSE_Pitch_Frq_TuneFrq_O2A_C
1512
 
1513
 
1514
	;; add and multiply to target frequency
1515
SIDSE_Pitch_Frq_AddMul
1516
	BRA_IFSET MUL_B_H, 7, BANKED, SIDSE_Pitch_Frq_AddMul_Neg
1517
SIDSE_Pitch_Frq_AddMul_Pos
1518
	;; calc MUL_A_[LH] = MIOS_PARAMETER[12] - SID_Vx_FRQ_[LH]
1519
	movf	SID_SE_TARGET_FRQ_L, W, BANKED
1520
	subwf	MIOS_PARAMETER1, W
1521
	movwf	MUL_A_L, BANKED
1522
	movf	SID_SE_TARGET_FRQ_H, W, BANKED
1523
	subwfb	MIOS_PARAMETER2, W
1524
	movwf	MUL_A_H, BANKED
1525
 
1526
	;; ensure that bit #15 (sign) not set
1527
	BRA_IFCLR MUL_A_H, 7, BANKED, SIDSE_Pitch_Frq_AddMul_Cont
1528
	setf	MUL_A_L, BANKED
1529
	movlw	0x7f
1530
	movwf	MUL_A_H, BANKED
1531
	rgoto	SIDSE_Pitch_Frq_AddMul_Cont
1532
 
1533
SIDSE_Pitch_Frq_AddMul_Neg
1534
	;; calc MUL_A_[LH] = SID_Vx_FRQ_[LH] - MIOS_PARAMETER[12]
1535
	movf	MIOS_PARAMETER1, W
1536
	subwf	SID_SE_TARGET_FRQ_L, W, BANKED
1537
	movwf	MUL_A_L, BANKED
1538
	movf	MIOS_PARAMETER2, W
1539
	subwfb	SID_SE_TARGET_FRQ_H, W, BANKED
1540
	movwf	MUL_A_H, BANKED
1541
	;; 	rgoto	SIDSE_Pitch_Frq_AddMul_Cont
1542
 
1543
SIDSE_Pitch_Frq_AddMul_Cont
1544
	;; calc MUL_R_[12] = MUL_A_[LH] * MUL_B_[LH]
1545
	call	MATH_MUL16_16_SIGNED
1546
 
1547
	;; SID_Vx_FRQ += signed result[23:8]
1548
	movf	MUL_R_1, W, BANKED
1549
	addwf	SID_SE_TARGET_FRQ_L, F, BANKED
1550
	movf	MUL_R_2, W, BANKED
1551
	addwfc	SID_SE_TARGET_FRQ_H, F, BANKED
1552
 
1553
SIDSE_Pitch_NoOffset
1554
	;; store target frequency in Vx
1555
	movlw	SID_Vx_TARGET_FRQ_L
1556
	movff	SID_SE_TARGET_FRQ_L, PLUSW1
1557
	movlw	SID_Vx_TARGET_FRQ_H
1558
	movff	SID_SE_TARGET_FRQ_H, PLUSW1
1559
 
1560
 
1561
	;; ------------------------------------------------------------------
1562
	;; pitch modulation
1563
	;; ------------------------------------------------------------------
1564
	rcall	SIDSE_Hlp_Pitch_Mod
1565
 
1566
	;; ------------------------------------------------------------------
1567
	;; Portamento Handling
1568
	;; ------------------------------------------------------------------
1569
 
1570
	;; transfer pointer to SIDx_Vx_FRQ_L register -> FSR2
1571
	call	SIDSE_Hlp_GetSIDFrqPtr
1572
 
1573
SIDSE_Pitch_Porta
1574
	;; whenever target frequency has been changed, update portamento frequency
1575
	movlw	SID_Vx_OLD_TARGET_FRQ_L
1576
	movf	PLUSW1, W
1577
	cpfseq	SID_SE_TARGET_FRQ_L, BANKED
1578
	rgoto SIDSE_Pitch_Porta_Update
1579
	movlw	SID_Vx_OLD_TARGET_FRQ_H
1580
	movf	PLUSW1, W
1581
	cpfseq	SID_SE_TARGET_FRQ_H, BANKED
1582
	rgoto SIDSE_Pitch_Porta_Update
1583
	rgoto	SIDSE_Pitch_Porta_NoUpdate
1584
SIDSE_Pitch_Porta_Update
1585
	;; memorize new target frequency
1586
	movlw	SID_Vx_OLD_TARGET_FRQ_L
1587
	movff	SID_SE_TARGET_FRQ_L, PLUSW1
1588
	movlw	SID_Vx_OLD_TARGET_FRQ_H
1589
	movff	SID_SE_TARGET_FRQ_H, PLUSW1
1590
 
1591
	;; skip portamento effect if current frequency is 0 (after patch change)
1592
	movf	POSTINC2, W
1593
	iorwf	POSTDEC2, W
1594
	skpnz
1595
	rgoto	SIDSE_Pitch_Porta_Final
1596
 
1597
	;; store current frequency (SIDx_FRQ_[LH]) in SID_Vx_PORTA_FRQ_[LH]
1598
	movlw	SID_Vx_PORTA_FRQ_L
1599
	movff	POSTINC2, PLUSW1
1600
	movlw	SID_Vx_PORTA_FRQ_H
1601
	movff	POSTDEC2, PLUSW1
1602
 
1603
	;; skip if glissando active
1604
	movlw	SID_Ix_Vx_FLAGS1
1605
	BRA_IFSET PLUSW0, SID_I_V_FLAGS1_PORTA_GLISSANDO, ACCESS, SIDSE_Pitch_Porta_NoUpdate
1606
 
1607
	;; reset portamento counter
1608
	movlw	SID_Vx_PORTA_CTR_L
1609
	clrf	PLUSW1
1610
	movlw	SID_Vx_PORTA_CTR_H
1611
	clrf	PLUSW1
1612
SIDSE_Pitch_Porta_NoUpdate
1613
 
1614
	;; skip the rest if portamento not active
1615
	movlw	SID_Vx_STATE
1616
	BRA_IFCLR PLUSW1, SID_V_STATE_PORTA_ACTIVE, ACCESS, SIDSE_Pitch_Porta_Final
1617
 
1618
	;; skip if glissando active
1619
	movlw	SID_Ix_Vx_FLAGS1
1620
	BRA_IFSET PLUSW0, SID_I_V_FLAGS1_PORTA_GLISSANDO, ACCESS, SIDSE_Pitch_Porta_Final
1621
 
1622
	;; get portamento multiplier from envelope table -> MUL_A
1623
	;; this one is used for "constant time glide" and "normal portamento"
1624
	movlw	SID_Ix_Vx_PORTAMENTO
1625
	movf	PLUSW0, W
1626
	skpnz
1627
	rgoto	SIDSE_Pitch_Porta_Final	; if rate has been changed to zero, set target frequency and finish portamento
1628
	TABLE_ADDR_MUL_W SID_ENV_TABLE, 2	; determine table address
1629
	tblrd*+				; transfer table entry to MUL_A_[LH]
1630
	movff	TABLAT, MUL_A_L
1631
	tblrd*+
1632
	movff	TABLAT, MUL_A_H
1633
 
1634
	;; branch depending on portamento mode
1635
	movlw	SID_Ix_Vx_FLAGS1
1636
	BRA_IFCLR PLUSW0, SID_I_V_FLAGS1_PORTA_CTG, ACCESS, SIDSE_Pitch_Porta_Norm
1637
 
1638
	;; ------------------------------------------------------------------
1639
	;; constant glide time
1640
SIDSE_Pitch_Porta_Const
1641
 
1642
	;; increment portamento counter by MUL_A_[LH], result in MUL_A_[LH]
1643
	movlw	SID_Vx_PORTA_CTR_L
1644
	movf	PLUSW1, W
1645
	addwf	MUL_A_L, F, BANKED
1646
	movlw	SID_Vx_PORTA_CTR_H
1647
	movf	PLUSW1, W
1648
	addwfc	MUL_A_H, F, BANKED
1649
	bnc	SIDSE_Pitch_Porta_Const_NoOv
1650
SIDSE_Pitch_Porta_Const_Ov
1651
	;; if value >= 0xffff target reached, saturate
1652
	setf	MUL_A_L, BANKED
1653
	setf	MUL_A_H, BANKED
1654
SIDSE_Pitch_Porta_Const_NoOv
1655
 
1656
	;; transfer back new counter value to original registers
1657
	;; portamento counter value -> MUL_A_[LH]
1658
	movlw	SID_Vx_PORTA_CTR_L
1659
	movff	MUL_A_L, PLUSW1
1660
	movlw	SID_Vx_PORTA_CTR_H
1661
	movff	MUL_A_H, PLUSW1
1662
 
1663
	;; get difference between target and previous frequency -> IRQ_TMP[12]
1664
	movlw	SID_Vx_PORTA_FRQ_L
1665
	movf	PLUSW1, W
1666
	subwf	SID_SE_TARGET_FRQ_L, W, BANKED
1667
	movwf	IRQ_TMP1
1668
	movlw	SID_Vx_PORTA_FRQ_H
1669
	movf	PLUSW1, W
1670
	subwfb	SID_SE_TARGET_FRQ_H, W, BANKED
1671
	movwf	IRQ_TMP2
1672
 
1673
	;; convert IRQ_TMP[12] to absolute value
1674
	call	SIDSE_Hlp_GetAbs16
1675
	;; result in IRQ_TMP[12], sign in IRQ_TMP3[0]
1676
	;; increment four to ensure that target will be reached
1677
	movlw	4
1678
	addwf	IRQ_TMP1, F
1679
	movlw	0
1680
	addwfc	IRQ_TMP2, F
1681
	movff	IRQ_TMP1, MUL_B_L
1682
	movff	IRQ_TMP2, MUL_B_H
1683
 
1684
	;; calc MUL_A_[LH] * MUL_B_[LH]
1685
	call	MATH_MUL16_16
1686
	;; result in MUL_R_2 (low-byte) and MUL_R_3 (high-byte)
1687
 
1688
	;; branch depending on direction
1689
	BRA_IFSET IRQ_TMP3, 0, ACCESS, SIDSE_Pitch_Porta_Const_Down
1690
SIDSE_Pitch_Porta_Const_Up
1691
	;; add scaled value to starting frequency
1692
	movlw	SID_Vx_PORTA_FRQ_L
1693
	movf	PLUSW1, W
1694
	addwf	MUL_R_2, W, BANKED
1695
	movwf	POSTINC2
1696
 
1697
	movlw	SID_Vx_PORTA_FRQ_H
1698
	movf	PLUSW1, W
1699
	addwfc	MUL_R_3, W, BANKED
1700
	movwf	POSTDEC2
1701
 
1702
	;; continue at normal portamento routine
1703
	rgoto	SIDSE_Pitch_Porta_Const_Up_Cont
1704
 
1705
SIDSE_Pitch_Porta_Const_Down
1706
	;; subtract scaled value from starting frequency
1707
	movlw	SID_Vx_PORTA_FRQ_L
1708
	movff	PLUSW1, POSTINC2
1709
	movlw	SID_Vx_PORTA_FRQ_H
1710
	movff	PLUSW1, POSTDEC2
1711
 
1712
	movf	MUL_R_2, W, BANKED
1713
	subwf	POSTINC2, F
1714
	movf	MUL_R_3, W, BANKED
1715
	subwfb	POSTDEC2, F
1716
 
1717
	;; continue at normal portamento routine
1718
	rgoto	SIDSE_Pitch_Porta_Const_Down_C
1719
 
1720
 
1721
	;; ------------------------------------------------------------------
1722
	;; "normal" portamento mode (non-constant glide time)
1723
SIDSE_Pitch_Porta_Norm
1724
	;; get current frequency -> MUL_B
1725
	movff	POSTINC2, MUL_B_L
1726
	movff	POSTDEC2, MUL_B_H
1727
 
1728
	;; multiply MUL_A * MUL_B
1729
	call	MATH_MUL16_16
1730
	;; result in MUL_R_2 (low-byte) and MUL_R_3 (high-byte)
1731
	;; ensure that result is != 0
1732
	movf	MUL_R_2, W, BANKED
1733
	iorwf	MUL_R_3, W, BANKED
1734
	skpnz
1735
	incf	MUL_R_2, F, BANKED
1736
 
1737
	;; SID_Vx_FRQ += result (depending on Portamento Direction)
1738
 
1739
	;; branch depending on portamento direction
1740
	;; check if value > current value
1741
	movf	SID_SE_TARGET_FRQ_L, W, BANKED
1742
	subwf	POSTINC2, W
1743
	movf	SID_SE_TARGET_FRQ_H, W, BANKED
1744
	subwfb	POSTDEC2, W
1745
	bc	SIDSE_Pitch_Porta_Norm_Down
1746
 
1747
SIDSE_Pitch_Porta_Norm_Up
1748
	movf	MUL_R_2, W
1749
	addwf	POSTINC2, F
1750
	movf	MUL_R_3, W, BANKED
1751
	addwfc	POSTDEC2, F
1752
 
1753
SIDSE_Pitch_Porta_Const_Up_Cont		; re-used by "constant glide" option
1754
	;; check if value > MAX_VALUE
1755
	movf	POSTINC2, W
1756
	subwf	SID_SE_TARGET_FRQ_L, W, BANKED
1757
	movf	POSTDEC2, W
1758
	subwfb	SID_SE_TARGET_FRQ_H, W, BANKED
1759
	bc	SIDSE_Pitch_Porta_End	; branch to end if MAX_VALUE not reached
1760
	rgoto	SIDSE_Pitch_Porta_Final	; final value reached
1761
 
1762
SIDSE_Pitch_Porta_Norm_Down
1763
	movf	MUL_R_2, W
1764
	subwf	POSTINC2, F
1765
	movf	MUL_R_3, W, BANKED
1766
	subwfb	POSTDEC2, F
1767
 
1768
SIDSE_Pitch_Porta_Const_Down_C	; re-used by "constant glide" option
1769
	;; check if value < MIN_VALUE
1770
	movf	SID_SE_TARGET_FRQ_L, W, BANKED
1771
	subwf	POSTINC2, W
1772
	movf	SID_SE_TARGET_FRQ_H, W, BANKED
1773
	subwfb	POSTDEC2, W
1774
	bc	SIDSE_Pitch_Porta_End	; branch to end if MIN_VALUE not reached
1775
	;; final value reached
1776
 
1777
SIDSE_Pitch_Porta_Final
1778
	;; copy target frequency to SIDx_FRQ_[LH] and finish portamento
1779
	movff	SID_SE_TARGET_FRQ_L, POSTINC2
1780
	movff	SID_SE_TARGET_FRQ_H, POSTDEC2
1781
 
1782
	;; deactivate porta active gate if glissando not active
1783
	movlw	SID_Ix_Vx_FLAGS1
1784
	BRA_IFSET PLUSW0, SID_I_V_FLAGS1_PORTA_GLISSANDO, ACCESS, SIDSE_Pitch_Porta_End
1785
	movlw	SID_Vx_STATE
1786
 	bcf	PLUSW1, SID_V_STATE_PORTA_ACTIVE
1787
SIDSE_Pitch_Porta_End
1788
 
1789
 
1790
	;; ------------------------------------------------------------------
1791
	;; K2A Function
1792
	;; ------------------------------------------------------------------
1793
 
1794
	;; forward key value to AOUT if enabled
1795
	movff	SID_LOCAL_ENS + SID_ENSx_CTRL2, WREG
1796
	BRA_IFCLR WREG, SID_ENS_CTRL2_K2A, ACCESS, SIDSE_Pitch_Key_NoAOUT
1797
SIDSE_Pitch_Key_AOUT
1798
	;; get channel assignment (0: channel disabled)
1799
	movlw	LOW(SIDSE_KEY_EXT_ASSG_TABLE)
1800
	addwf	SID_SE_ELEMENT_NUM, W, BANKED
1801
	movwf	TBLPTRL
1802
	clrf	TBLPTRH
1803
	movlw	HIGH(SIDSE_KEY_EXT_ASSG_TABLE)
1804
	addwfc	TBLPTRH, F
1805
	clrf    TBLPTRU
1806
        movlw   UPPER(SIDSE_KEY_EXT_ASSG_TABLE)
1807
	addwfc	TBLPTRU, F
1808
	tblrd*+
1809
	movf	TABLAT, W
1810
	bz	SIDSE_Pitch_Key_NoAOUT	; channel assignment disabled
1811
 
1812
	;; copy key value to IRQ_TMP[12]
1813
	clrf	IRQ_TMP1
1814
	clrc
1815
	rlf	SID_SE_TRANSPOSED_NOTE, W, BANKED
1816
	movwf	IRQ_TMP2
1817
 
1818
	;; forward IRQ_TMP[12] to AOUTx_LH[TABLAT-1]
1819
	lfsr	FSR1, AOUT0_L
1820
	decf	TABLAT, W
1821
	rlf	WREG, W
1822
	andlw	0xfe
1823
	addwf	FSR1L, F
1824
	rcall	SIDSE_Hlp_EXT_AOUT_Transfer
1825
 
1826
	;; notify that channel has already been updated by K2A function
1827
	decf	TABLAT, W
1828
	call	MIOS_HLP_GetBitORMask
1829
	iorwf	SID_SE_EXT_ALLOCATED, F, BANKED
1830
SIDSE_Pitch_Key_NoAOUT
1831
 
1832
	;; ------------------------------------------------------------------
1833
	;; O2A Function
1834
	;; ------------------------------------------------------------------
1835
 
1836
	;; forward key value to AOUT if enabled
1837
	movff	SID_LOCAL_ENS + SID_ENSx_CTRL2, WREG
1838
	BRA_IFCLR WREG, SID_ENS_CTRL2_O2A, ACCESS, SIDSE_Pitch_Osc_NoAOUT
1839
SIDSE_Pitch_Osc_AOUT
1840
	;; get channel assignment (0: channel disabled)
1841
	movlw	LOW(SIDSE_OSC_EXT_ASSG_TABLE)
1842
	addwf	SID_SE_ELEMENT_NUM, W, BANKED
1843
	movwf	TBLPTRL
1844
	clrf	TBLPTRH
1845
	movlw	HIGH(SIDSE_OSC_EXT_ASSG_TABLE)
1846
	addwfc	TBLPTRH, F
1847
	clrf    TBLPTRU
1848
        movlw   UPPER(SIDSE_OSC_EXT_ASSG_TABLE)
1849
	addwfc	TBLPTRU, F
1850
	tblrd*+
1851
	movf	TABLAT, W
1852
	bz	SIDSE_Pitch_Osc_NoAOUT	; channel assignment disabled
1853
 
1854
	;; copy oscillator frequency to IRQ_TMP[12]
1855
	movff	POSTINC2, IRQ_TMP1
1856
	movff	POSTDEC2, IRQ_TMP2
1857
 
1858
	;; forward IRQ_TMP[12] to AOUTx_LH[TABLAT-1]
1859
	lfsr	FSR1, AOUT0_L
1860
	decf	TABLAT, W
1861
	rlf	WREG, W
1862
	andlw	0xfe
1863
	addwf	FSR1L, F
1864
	rcall	SIDSE_Hlp_EXT_AOUT_Transfer
1865
 
1866
	;; notify that channel has already been updated by O2A function
1867
	decf	TABLAT, W
1868
	call	MIOS_HLP_GetBitORMask
1869
	iorwf	SID_SE_EXT_ALLOCATED, F, BANKED
1870
SIDSE_Pitch_Osc_NoAOUT
1871
 
1872
	return
1873
 
1874
 
1875
	;; AOUT assignments for K2A
1876
SIDSE_KEY_EXT_ASSG_TABLE
1877
	db	DEFAULT_K2A_OSC1_L_AOUT, DEFAULT_K2A_OSC2_L_AOUT
1878
	db	DEFAULT_K2A_OSC3_L_AOUT, DEFAULT_K2A_OSC1_R_AOUT
1879
	db	DEFAULT_K2A_OSC2_L_AOUT, DEFAULT_K2A_OSC3_R_AOUT
1880
 
1881
	;; AOUT assignments for O2A
1882
SIDSE_OSC_EXT_ASSG_TABLE
1883
	db	DEFAULT_O2A_OSC1_L_AOUT, DEFAULT_O2A_OSC2_L_AOUT
1884
	db	DEFAULT_O2A_OSC3_L_AOUT, DEFAULT_O2A_OSC1_R_AOUT
1885
	db	DEFAULT_O2A_OSC2_L_AOUT, DEFAULT_O2A_OSC3_R_AOUT
1886
 
1887
 
1888
;; --------------------------------------------------------------------------
1889
;; This function handles the Pulsewidth of a voice
1890
;; IN: pointer to SID_Ix_L_SxVy_BASE in FSR0 (patch record)
1891
;;     pointer to SIDx_Vx_BASE in FSR1 (voice record)
1892
;;     Voice number in SID_SE_ELEMENT_NUM
1893
;;     If SIDSE_PW_Drums is called: 16bit pulsewidth in IRQ_TMP[12]
1894
;; --------------------------------------------------------------------------
1895
SIDSE_PW
1896
	;; small variation between Lead/Bassline/Multi Engine, and Drum Engine:
1897
	;; for Drum Engine the pulsewidth is predefined in model table, and not
1898
	;; part of the patch (drum engine has to call SIDSE_PW_Drums with
1899
	;; 16bit pulsewidth in IRQ_TMP[12]
1900
 
1901
	;; store 12bit pulsewidth in IRQ_TMP[12]
1902
	movlw	SID_Ix_Vx_PULSEWIDTH_L
1903
	movff	PLUSW0, IRQ_TMP1
1904
	movlw	SID_Ix_Vx_PULSEWIDTH_H
1905
	movf	PLUSW0, W
1906
	andlw	0x0f
1907
	movwf	IRQ_TMP2
1908
 
1909
	;; extend to 16bit
1910
	swapf	IRQ_TMP2, F
1911
	swapf	IRQ_TMP1, W
1912
	andlw	0x0f
1913
	iorwf	IRQ_TMP2, F
1914
	swapf	IRQ_TMP1, W
1915
	andlw	0xf0
1916
	movwf	IRQ_TMP1
1917
 
1918
SIDSE_PW_Drums
1919
	;; transfer pointer to SIDx_Vx_PW_L register -> FSR2
1920
	call	SIDSE_Hlp_GetSIDPWPtr
1921
 
1922
	;; calculate pointer to MOD target array -> FSR2
1923
	movf	SID_SE_ELEMENT_NUM, W, BANKED
1924
	addlw	(SID_MOD_TARG_PW1_L-SID_MOD_TARG_BASE)/3
1925
	mullw	3
1926
	lfsr	FSR1, SID_MOD_TARG_BASE
1927
	movf	PRODL, W
1928
	addwf	FSR1L, F
1929
 
1930
	;; add modulation value to pulsewidth
1931
	movf	POSTINC1, W
1932
	addwf	IRQ_TMP1, F
1933
	movf	POSTINC1, W
1934
	addwfc	IRQ_TMP2, F
1935
 
1936
	;; saturate
1937
	BRA_IFSET INDF1, 7, ACCESS, SIDSE_PW_Mod_SatNeg
1938
SIDSE_PW_Mod_SatPos
1939
	movf	INDF1, W
1940
	bnz	SIDSE_PW_Mod_SatPos_Sat
1941
	bnc	SIDSE_PW_Mod_Sat_NoSat
1942
SIDSE_PW_Mod_SatPos_Sat
1943
	setf	IRQ_TMP1
1944
	setf	IRQ_TMP2
1945
	rgoto	SIDSE_PW_Mod_Sat_Cont
1946
SIDSE_PW_Mod_SatNeg
1947
	comf	INDF1, W
1948
	bnz	SIDSE_PW_Mod_SatNeg_Sat
1949
	bc	SIDSE_PW_Mod_Sat_NoSat
1950
SIDSE_PW_Mod_SatNeg_Sat
1951
	clrf	IRQ_TMP1
1952
	clrf	IRQ_TMP2
1953
	;; 	rgoto	SIDSE_PW_Mod_Sat_Cont
1954
SIDSE_PW_Mod_Sat_Cont
1955
SIDSE_PW_Mod_Sat_NoSat
1956
 
1957
	;; convert back to 12bit value -> IRQ_TMP[34]
1958
	swapf	IRQ_TMP1, W
1959
	andlw	0x0f
1960
	movwf	IRQ_TMP3
1961
	swapf	IRQ_TMP2, W
1962
	andlw	0xf0
1963
	iorwf	IRQ_TMP3, F
1964
	swapf	IRQ_TMP2, W
1965
	andlw	0x0f
1966
	movwf	IRQ_TMP4
1967
 
1968
	;; transfer to SID registers
1969
	movff	IRQ_TMP3, POSTINC2
1970
	movff	IRQ_TMP4, POSTDEC2
1971
 
1972
	;; forward pulsewidth to AOUT if enabled
1973
	movff	SID_LOCAL_ENS + SID_ENSx_CTRL2, WREG
1974
	BRA_IFCLR WREG, SID_ENS_CTRL2_P2A, ACCESS, SIDSE_PW_NoAOUT
1975
SIDSE_PW_AOUT
1976
	;; get channel assignment (0: channel disabled)
1977
	movlw	LOW(SIDSE_PW_EXT_ASSG_TABLE)
1978
	addwf	SID_SE_ELEMENT_NUM, W, BANKED
1979
	movwf	TBLPTRL
1980
	clrf	TBLPTRH
1981
	movlw	HIGH(SIDSE_PW_EXT_ASSG_TABLE)
1982
	addwfc	TBLPTRH, F
1983
	clrf    TBLPTRU
1984
        movlw   UPPER(SIDSE_PW_EXT_ASSG_TABLE)
1985
	addwfc	TBLPTRU, F
1986
	tblrd*+
1987
	movf	TABLAT, W
1988
	bz	SIDSE_PW_NoAOUT		; channel assignment disabled
1989
 
1990
	;; forward IRQ_TMP[12] to AOUTx_LH[TABLAT-1]
1991
	lfsr	FSR1, AOUT0_L
1992
	decf	TABLAT, W
1993
	rlf	WREG, W
1994
	andlw	0xfe
1995
	addwf	FSR1L, F
1996
	rcall	SIDSE_Hlp_EXT_AOUT_Transfer
1997
 
1998
	;; notify that channel has already been updated by P2A function
1999
	decf	TABLAT, W
2000
	call	MIOS_HLP_GetBitORMask
2001
	iorwf	SID_SE_EXT_ALLOCATED, F, BANKED
2002
SIDSE_PW_NoAOUT
2003
	return
2004
 
2005
 
2006
	;; AOUT assignments for P2A
2007
SIDSE_PW_EXT_ASSG_TABLE
2008
	db	DEFAULT_P2A_OSC1_L_AOUT, DEFAULT_P2A_OSC2_L_AOUT
2009
	db	DEFAULT_P2A_OSC3_L_AOUT, DEFAULT_P2A_OSC1_R_AOUT
2010
	db	DEFAULT_P2A_OSC2_L_AOUT, DEFAULT_P2A_OSC3_R_AOUT
2011
 
2012
;; --------------------------------------------------------------------------
2013
;; This function handles the Filter
2014
;; IN: pointer to SID_Ix_L_SxF_BASE in FSR0 (patch record)
2015
;;     pointer to SID_MOD_TARG_FILx_L in FSR1 (modulation target)
2016
;;     pointer to SIDx_BASE in FSR2 (SID registers)
2017
;;     Filter number in SID_SE_ELEMENT_NUM
2018
;; --------------------------------------------------------------------------
2019
SIDSE_Filter
2020
	;; store 12bit cutoff in IRQ_TMP[12]
2021
	movlw	SID_Ix_L_Fx_CUTOFF_L
2022
	movff	PLUSW0, IRQ_TMP1
2023
	movlw	SID_Ix_L_Fx_CUTOFF_H
2024
	movf	PLUSW0, W
2025
	andlw	0x0f
2026
	movwf	IRQ_TMP2
2027
 
2028
	;; extend to 16bit
2029
	swapf	IRQ_TMP2, F
2030
	swapf	IRQ_TMP1, W
2031
	andlw	0x0f
2032
	iorwf	IRQ_TMP2, F
2033
	swapf	IRQ_TMP1, W
2034
	andlw	0xf0
2035
	movwf	IRQ_TMP1
2036
 
2037
	;; pointer to SID_MOD_TARG_FILx_L already in FSR1
2038
	;; multiplty target value by 2 (for easy to realize extreme modulation results)
2039
	;; -> IRQ_TMP[345]
2040
	movf	INDF1, W
2041
	addwf	POSTINC1, W
2042
	movwf	IRQ_TMP3
2043
	movf	INDF1, W
2044
	addwfc	POSTINC1, W
2045
	movwf	IRQ_TMP4
2046
	movf	INDF1, W
2047
	addwfc	POSTINC1, W
2048
	movwf	IRQ_TMP5
2049
 
2050
	;; add modulation value to cutoff
2051
	movf	IRQ_TMP3, W
2052
	addwf	IRQ_TMP1, F
2053
	movf	IRQ_TMP4, W
2054
	addwfc	IRQ_TMP2, F
2055
 
2056
	;; saturate
2057
	BRA_IFSET IRQ_TMP5, 7, ACCESS, SIDSE_Filter_Mod_SatNeg
2058
SIDSE_Filter_Mod_SatPos
2059
	movf	IRQ_TMP5, W
2060
	bnz	SIDSE_Filter_Mod_SatPos_Sat
2061
	bnc	SIDSE_Filter_Mod_Sat_NoSat
2062
SIDSE_Filter_Mod_SatPos_Sat
2063
	setf	IRQ_TMP1
2064
	setf	IRQ_TMP2
2065
	rgoto	SIDSE_Filter_Mod_Sat_Cont
2066
SIDSE_Filter_Mod_SatNeg
2067
	comf	IRQ_TMP5, W
2068
	bnz	SIDSE_Filter_Mod_SatNeg_Sat
2069
	bc	SIDSE_Filter_Mod_Sat_NoSat
2070
SIDSE_Filter_Mod_SatNeg_Sat
2071
	clrf	IRQ_TMP1
2072
	clrf	IRQ_TMP2
2073
	;; 	rgoto	SIDSE_Filter_Mod_Sat_Cont
2074
SIDSE_Filter_Mod_Sat_Cont
2075
SIDSE_Filter_Mod_Sat_NoSat
2076
 
2077
	;; in bassline mode: add keytracking * NOTE value
2078
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_ENGINE, WREG
2079
	andlw	0x03
2080
	xorlw	0x01
2081
	bnz	SIDSE_Filter_KTr_Skip
2082
SIDSE_Filter_KTr
2083
	movlw	SID_Ix_L_Fx_KEYTRACK
2084
	movf	PLUSW0, W
2085
	bz	SIDSE_Filter_KTr_Skip
2086
	movwf	PRODL
2087
 
2088
	movff	SIDL_V1_BASE + SID_Vx_OLD_TRANSP_NOTE, WREG
2089
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2090
	movff	SIDR_V1_BASE + SID_Vx_OLD_TRANSP_NOTE, WREG
2091
	andlw	0x7f
2092
	mulwf	PRODL, ACCESS
2093
 
2094
	;; add to filter and saturate if required (value always positive)
2095
	movf	PRODL, W
2096
	addwf	IRQ_TMP1, F
2097
	movf	PRODH, W
2098
	addwfc	IRQ_TMP2, F
2099
	bnc	SIDSE_Filter_KTr_NoSat
2100
SIDSE_Filter_KTr_Sat
2101
	setf	IRQ_TMP1
2102
	setf	IRQ_TMP2
2103
SIDSE_Filter_KTr_NoSat
2104
SIDSE_Filter_KTr_Skip
2105
 
2106
 
2107
	;; copy SID_Gx_CALI_FIL[12]_[MIN|MAX]_[LH] into IRQ_TMP[34]/PROD[LH] depending on selected filter
2108
	BRA_IFSET SID_SE_ELEMENT_NUM, 0, BANKED, SIDSE_Filter_MinMax2
2109
SIDSE_Filter_MinMax1
2110
	movff	SID_LOCAL_ENS + SID_ENSx_CALI_FIL1_MIN_L, IRQ_TMP3
2111
	movff	SID_LOCAL_ENS + SID_ENSx_CALI_FIL1_MIN_H, IRQ_TMP4
2112
	movff	SID_LOCAL_ENS + SID_ENSx_CALI_FIL1_MAX_L, PRODL
2113
	movff	SID_LOCAL_ENS + SID_ENSx_CALI_FIL1_MAX_H, PRODH
2114
	rgoto	SIDSE_Filter_MinMax_Cont
2115
SIDSE_Filter_MinMax2
2116
	movff	SID_LOCAL_ENS + SID_ENSx_CALI_FIL2_MIN_L, IRQ_TMP3
2117
	movff	SID_LOCAL_ENS + SID_ENSx_CALI_FIL2_MIN_H, IRQ_TMP4
2118
	movff	SID_LOCAL_ENS + SID_ENSx_CALI_FIL2_MAX_L, PRODL
2119
	movff	SID_LOCAL_ENS + SID_ENSx_CALI_FIL2_MAX_H, PRODH
2120
	;; 	rgoto	SIDSE_Filter_MinMax_Cont
2121
SIDSE_Filter_MinMax_Cont
2122
 
2123
	;; scale between min/max value
2124
	movf	IRQ_TMP3, W	; SID_Gx_CALI_FIL1_MIN_L
2125
	subwf	PRODL, W	; SID_Gx_CALI_FIL1_MAX_L
2126
	movff	WREG, MUL_A_L
2127
	movf	IRQ_TMP4, W	; SID_Gx_CALI_FIL1_MIN_H
2128
	subwfb	PRODH, W	; SID_Gx_CALI_FIL1_MAX_H
2129
	movff	WREG, MUL_A_H
2130
	movff	IRQ_TMP1, MUL_B_L
2131
	movff	IRQ_TMP2, MUL_B_H
2132
	;; calc MUL_A_[LH] * MUL_B_[LH]
2133
	call	MATH_MUL16_16
2134
	;; result in MUL_R_2 (low-byte) and MUL_R_3 (high-byte)
2135
	movf	IRQ_TMP3, W	; SID_Gx_CALI_FIL1_MIN_L
2136
	addwf	MUL_R_2, W, BANKED
2137
	movwf	IRQ_TMP1
2138
	movf	IRQ_TMP4, W	; SID_Gx_CALI_FIL1_MIN_H
2139
	addwfc	MUL_R_3, W, BANKED
2140
	movwf	IRQ_TMP2
2141
 
2142
	;; optional interpolation
2143
	movlw	SID_Ix_L_Fx_CUTOFF_H
2144
	BRA_IFCLR PLUSW0, SID_I_F_FIP_ON, ACCESS, SIDSE_Filter_NoIP
2145
SIDSE_Filter_IP
2146
	lfsr	FSR1, FIP1_BASE
2147
	movf	SID_SE_ELEMENT_NUM, W, BANKED
2148
	skpz
2149
	lfsr	FSR1, FIP2_BASE
2150
	rcall	SIDSE_Hlp_IP
2151
SIDSE_Filter_NoIP
2152
 
2153
	;; convert to 11bit value (SID format: 3 lowes bits in bit [2:0] of low-register)
2154
	swapf	IRQ_TMP1, W
2155
	rrf	WREG, W
2156
	andlw	0x07
2157
	movwf	IRQ_TMP3
2158
 
2159
	;; transfer to SID registers
2160
	movlw	SIDx_FC_L
2161
	movff	IRQ_TMP3, PLUSW2
2162
	movlw	SIDx_FC_H
2163
	movff	IRQ_TMP2, PLUSW2
2164
 
2165
	;; Forward CutOff and Resonance to AOUT if enabled
2166
	movff	SID_LOCAL_ENS + SID_ENSx_CTRL2, WREG
2167
	BRA_IFCLR WREG, SID_ENS_CTRL2_F2A, ACCESS, SIDSE_Filter_NoAOUT
2168
SIDSE_Filter_AOUT
2169
	;; expecting 16bit value in IRQ_TMP[12] and pointer to AOUT register in FSR1
2170
#if DEFAULT_F2A_CUTOFF_L_AOUT == 0 || DEFAULT_F2A_CUTOFF_L_AOUT > 8
2171
	movf	SID_SE_ELEMENT_NUM, W, BANKED
2172
	bz	SIDSE_Filter_AOUT_NoCutOff
2173
#endif
2174
#if DEFAULT_F2A_CUTOFF_R_AOUT == 0 || DEFAULT_F2A_CUTOFF_R_AOUT > 8
2175
	decf	SID_SE_ELEMENT_NUM, W, BANKED
2176
	bz	SIDSE_Filter_AOUT_NoCutOff
2177
#endif
2178
 
2179
	lfsr	FSR1, AOUT0_L + 2*(DEFAULT_F2A_CUTOFF_L_AOUT-1)
2180
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2181
	lfsr	FSR1, AOUT0_L + 2*(DEFAULT_F2A_CUTOFF_R_AOUT-1)
2182
	rcall	SIDSE_Hlp_EXT_AOUT_Transfer
2183
 
2184
	;; notify that channel has already been updated by F2A function
2185
	;; (for faster handling, do this for all enabled channels each time this function is called)
2186
	movlw	0x00
2187
#if DEFAULT_F2A_CUTOFF_L_AOUT
2188
	iorlw	(1 << (DEFAULT_F2A_CUTOFF_L_AOUT-1))
2189
#endif
2190
#if DEFAULT_F2A_CUTOFF_R_AOUT
2191
	iorlw	(1 << (DEFAULT_F2A_CUTOFF_R_AOUT-1))
2192
#endif
2193
	iorwf	SID_SE_EXT_ALLOCATED, F, BANKED
2194
SIDSE_Filter_AOUT_NoCutOff
2195
 
2196
#if DEFAULT_F2A_RESONANCE_L_AOUT == 0 || DEFAULT_F2A_RESONANCE_L_AOUT > 8
2197
	movf	SID_SE_ELEMENT_NUM, W, BANKED
2198
	bz	SIDSE_Filter_AOUT_NoResonance
2199
#endif
2200
#if DEFAULT_F2A_RESONANCE_R_AOUT == 0 || DEFAULT_F2A_RESONANCE_R_AOUT > 8
2201
	decf	SID_SE_ELEMENT_NUM, W, BANKED
2202
	bz	SIDSE_Filter_AOUT_NoResonance
2203
#endif
2204
 
2205
	;; resonance (8bit)
2206
	movlw	SID_Ix_L_Fx_RESONANCE
2207
	movff	PLUSW0, IRQ_TMP2
2208
	clrf	IRQ_TMP1
2209
	lfsr	FSR1, AOUT0_L + 2*(DEFAULT_F2A_RESONANCE_L_AOUT-1)
2210
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2211
	lfsr	FSR1, AOUT0_L + 2*(DEFAULT_F2A_RESONANCE_R_AOUT-1)
2212
	rcall	SIDSE_Hlp_EXT_AOUT_Transfer
2213
 
2214
	;; notify that channel has already been updated by F2A function
2215
	;; (for faster handling, do this for all enabled channels each time this function is called)
2216
	movlw	0x00
2217
#if DEFAULT_F2A_RESONANCE_L_AOUT
2218
	iorlw	(1 << (DEFAULT_F2A_RESONANCE_L_AOUT-1))
2219
#endif
2220
#if DEFAULT_F2A_RESONANCE_R_AOUT
2221
	iorlw	(1 << (DEFAULT_F2A_RESONANCE_R_AOUT-1))
2222
#endif
2223
	iorwf	SID_SE_EXT_ALLOCATED, F, BANKED
2224
SIDSE_Filter_AOUT_NoResonance
2225
SIDSE_Filter_NoAOUT
2226
 
2227
	;; Channels and Resonance
2228
	movlw	SID_Ix_L_Fx_CHN_MODE
2229
	movf	PLUSW0, W
2230
	andlw	0x0f
2231
	movwf	PRODL
2232
	movlw	SID_Ix_L_Fx_RESONANCE
2233
	movf	PLUSW0, W
2234
	andlw	0xf0
2235
	iorwf	PRODL, F
2236
	movlw	SIDx_RES_FCHN
2237
	movff	PRODL, PLUSW2
2238
 
2239
	;; volume and filter mode
2240
 
2241
	;; volume can be modulated (result in IRQ_TMP[12])
2242
	clrf	IRQ_TMP1
2243
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_L_VOLUME, IRQ_TMP2
2244
	clrc
2245
	rlf	IRQ_TMP2, F
2246
 
2247
	;; calculate pointer to MOD target array -> FSR2
2248
	movf	SID_SE_ELEMENT_NUM, W, BANKED
2249
	addlw	(SID_MOD_TARG_VOL1_L-SID_MOD_TARG_BASE)/3
2250
	mullw	3
2251
	lfsr	FSR1, SID_MOD_TARG_BASE
2252
	movf	PRODL, W
2253
	addwf	FSR1L, F
2254
	movf	POSTINC1, W
2255
	addwf	IRQ_TMP1, F
2256
	movf	POSTINC1, W
2257
	addwfc	IRQ_TMP2, F
2258
 
2259
	;; saturate
2260
	BRA_IFSET INDF1, 7, ACCESS, SIDSE_Filter_Mod_Vol_SatNeg
2261
SIDSE_Filter_Mod_Vol_SatPos
2262
	movf	INDF1, W
2263
	bnz	SIDSE_Filter_Mod_Vol_SatPos_Sat
2264
	bnc	SIDSE_Filter_Mod_Vol_NoSat
2265
SIDSE_Filter_Mod_Vol_SatPos_Sat
2266
	setf	IRQ_TMP1
2267
	setf	IRQ_TMP2
2268
	rgoto	SIDSE_Filter_Mod_Vol_Sat_Cont
2269
SIDSE_Filter_Mod_Vol_SatNeg
2270
	comf	INDF1, W
2271
	bnz	SIDSE_Filter_Mod_Vol_SatNeg_Sat
2272
	bc	SIDSE_Filter_Mod_Vol_NoSat
2273
SIDSE_Filter_Mod_Vol_SatNeg_Sat
2274
	clrf	IRQ_TMP1
2275
	clrf	IRQ_TMP2
2276
	;; 	rgoto	SIDSE_Filter_Mod_Vol_Sat_Cont
2277
SIDSE_Filter_Mod_Vol_Sat_Cont
2278
SIDSE_Filter_Mod_Vol_NoSat
2279
 
2280
	;; copy SID_Gx_CTRL1 to IRQ_TMP3 - we need this to check if V2A is enabled
2281
	movff	SID_LOCAL_ENS + SID_ENSx_CTRL2, IRQ_TMP3
2282
 
2283
	;; patch volume -> PRODL
2284
	;; volume always maximum when V2A enabled
2285
	swapf	IRQ_TMP2, W
2286
	andlw	0x0f
2287
	btfsc	IRQ_TMP3, SID_ENS_CTRL2_V2A
2288
	movlw 0x0f
2289
	movwf	PRODL
2290
 
2291
	movlw	SID_Ix_L_Fx_CHN_MODE
2292
	movf	PLUSW0, W
2293
	andlw	0xf0
2294
	iorwf	PRODL, F
2295
	movlw	SIDx_MODE_VOL
2296
	movff	PRODL, PLUSW2
2297
 
2298
	;; Forward Volume to AOUT if enabled
2299
	BRA_IFCLR IRQ_TMP3, SID_ENS_CTRL2_V2A, ACCESS, SIDSE_Filter_Vol_NoAOUT
2300
SIDSE_Filter_Vol_AOUT
2301
	;; expecting 16bit value in IRQ_TMP[12] and pointer to AOUT register in FSR1
2302
#if DEFAULT_V2A_VOLUME_L_AOUT == 0 || DEFAULT_V2A_VOLUME_L_AOUT > 8
2303
	movf	SID_SE_ELEMENT_NUM, W, BANKED
2304
	bz	SIDSE_Filter_Vol_NoAOUT
2305
#endif
2306
#if DEFAULT_V2A_VOLUME_R_AOUT == 0 || DEFAULT_V2A_VOLUME_R_AOUT > 8
2307
	decf	SID_SE_ELEMENT_NUM, W, BANKED
2308
	bz	SIDSE_Filter_Vol_NoAOUT
2309
#endif
2310
 
2311
	lfsr	FSR1, AOUT0_L + 2*(DEFAULT_V2A_VOLUME_L_AOUT-1)
2312
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2313
	lfsr	FSR1, AOUT0_L + 2*(DEFAULT_V2A_VOLUME_R_AOUT-1)
2314
	rcall	SIDSE_Hlp_EXT_AOUT_Transfer
2315
 
2316
	;; notify that channel has already been updated by V2A function
2317
	;; (for faster handling, do this for all enabled channels each time this function is called)
2318
	movlw	0x00
2319
#if DEFAULT_V2A_VOLUME_L_AOUT
2320
	iorlw	(1 << (DEFAULT_V2A_VOLUME_L_AOUT-1))
2321
#endif
2322
#if DEFAULT_V2A_VOLUME_R_AOUT
2323
	iorlw	(1 << (DEFAULT_V2A_VOLUME_R_AOUT-1))
2324
#endif
2325
	iorwf	SID_SE_EXT_ALLOCATED, F, BANKED
2326
SIDSE_Filter_Vol_NoAOUT
2327
 
2328
	return
2329
 
2330
 
2331
;; --------------------------------------------------------------------------
2332
;; This function handles the AOUTs
2333
;; Used by Bassline/Drum and Multi Engine
2334
;; IN: pointer to SID_Ix_EXT_PARx_L in FSR0 (patch record)
2335
;;     pointer to AOUTx_L in FSR1 (aout target)
2336
;;     EXT number in SID_SE_ELEMENT_NUM
2337
;; --------------------------------------------------------------------------
2338
SIDSE_BDM_EXT
2339
	;; skip if any F2X function has already updated the AOUTx register
2340
	movf	SID_SE_ELEMENT_NUM, W, BANKED
2341
	call	MIOS_HLP_GetBitORMask
2342
	andwf	SID_SE_EXT_ALLOCATED, W, BANKED
2343
	bnz	SIDSE_BDM_EXT_End
2344
 
2345
	;; store 16bit offset in IRQ_TMP[12]
2346
	movff	POSTINC0, IRQ_TMP1
2347
	movff	POSTDEC0, IRQ_TMP2
2348
 
2349
	call	SIDSE_Hlp_EXT_AOUT_Transfer
2350
 
2351
SIDSE_BDM_EXT_End
2352
	return
2353
 
2354
 
2355
;; --------------------------------------------------------------------------
2356
;; This function updates the 8 external switches (called once per update cycle)
2357
;; IN: -
2358
;; --------------------------------------------------------------------------
2359
SIDSE_EXT_Switches
2360
	lfsr	FSR1, AOUT_GATE			; for easier addressing
2361
 
2362
	;; copy switch state from patch to AOUT_GATE
2363
	movff	SID_PATCH_BUFFER_SHADOW + SID_Ix_CUSTOM_SW, INDF1
2364
 
2365
	;; help macro to improve oversight
2366
SIDSE_EXT_SWITCHES_FORWARD MACRO addr, flag, out
2367
	;; overlay with various gate functions if enabled
2368
  IF out && (out <= 8)
2369
	bcf	INDF1, 	out-1
2370
	btfsc	addr, flag, BANKED
2371
	bsf	INDF1, 	out-1
2372
  ENDIF
2373
	ENDM
2374
 
2375
	SET_BSR	SIDL_V1_BASE			; BSR set to SIDx_Vx_BASE for direct access
2376
 
2377
	SIDSE_EXT_SWITCHES_FORWARD SIDL_V1_BASE + SID_Vx_STATE, SID_V_STATE_GATE_ACTIVE, DEFAULT_GATE_OSC1_L_OUT
2378
	SIDSE_EXT_SWITCHES_FORWARD SIDL_V2_BASE + SID_Vx_STATE, SID_V_STATE_GATE_ACTIVE, DEFAULT_GATE_OSC2_L_OUT
2379
	SIDSE_EXT_SWITCHES_FORWARD SIDL_V3_BASE + SID_Vx_STATE, SID_V_STATE_GATE_ACTIVE, DEFAULT_GATE_OSC3_L_OUT
2380
	SIDSE_EXT_SWITCHES_FORWARD SIDR_V1_BASE + SID_Vx_STATE, SID_V_STATE_GATE_ACTIVE, DEFAULT_GATE_OSC1_R_OUT
2381
	SIDSE_EXT_SWITCHES_FORWARD SIDR_V2_BASE + SID_Vx_STATE, SID_V_STATE_GATE_ACTIVE, DEFAULT_GATE_OSC2_R_OUT
2382
	SIDSE_EXT_SWITCHES_FORWARD SIDR_V3_BASE + SID_Vx_STATE, SID_V_STATE_GATE_ACTIVE, DEFAULT_GATE_OSC3_R_OUT
2383
 
2384
	SIDSE_EXT_SWITCHES_FORWARD SIDL_V1_BASE + SID_Vx_STATE, SID_V_STATE_SLIDE, DEFAULT_SLIDE_OSC1_L_OUT
2385
	SIDSE_EXT_SWITCHES_FORWARD SIDL_V2_BASE + SID_Vx_STATE, SID_V_STATE_SLIDE, DEFAULT_SLIDE_OSC2_L_OUT
2386
	SIDSE_EXT_SWITCHES_FORWARD SIDL_V3_BASE + SID_Vx_STATE, SID_V_STATE_SLIDE, DEFAULT_SLIDE_OSC3_L_OUT
2387
	SIDSE_EXT_SWITCHES_FORWARD SIDR_V1_BASE + SID_Vx_STATE, SID_V_STATE_SLIDE, DEFAULT_SLIDE_OSC1_R_OUT
2388
	SIDSE_EXT_SWITCHES_FORWARD SIDR_V2_BASE + SID_Vx_STATE, SID_V_STATE_SLIDE, DEFAULT_SLIDE_OSC2_R_OUT
2389
	SIDSE_EXT_SWITCHES_FORWARD SIDR_V3_BASE + SID_Vx_STATE, SID_V_STATE_SLIDE, DEFAULT_SLIDE_OSC3_R_OUT
2390
 
2391
	SIDSE_EXT_SWITCHES_FORWARD SIDL_V1_BASE + SID_Vx_STATE, SID_V_STATE_ACCENT, DEFAULT_ACCENT_OSC1_L_OUT
2392
	SIDSE_EXT_SWITCHES_FORWARD SIDL_V2_BASE + SID_Vx_STATE, SID_V_STATE_ACCENT, DEFAULT_ACCENT_OSC2_L_OUT
2393
	SIDSE_EXT_SWITCHES_FORWARD SIDL_V3_BASE + SID_Vx_STATE, SID_V_STATE_ACCENT, DEFAULT_ACCENT_OSC3_L_OUT
2394
	SIDSE_EXT_SWITCHES_FORWARD SIDR_V1_BASE + SID_Vx_STATE, SID_V_STATE_ACCENT, DEFAULT_ACCENT_OSC1_R_OUT
2395
	SIDSE_EXT_SWITCHES_FORWARD SIDR_V2_BASE + SID_Vx_STATE, SID_V_STATE_ACCENT, DEFAULT_ACCENT_OSC2_R_OUT
2396
	SIDSE_EXT_SWITCHES_FORWARD SIDR_V3_BASE + SID_Vx_STATE, SID_V_STATE_ACCENT, DEFAULT_ACCENT_OSC3_R_OUT
2397
 
2398
	SET_BSR	SID_BASE			; switch BSR back to SID base
2399
	return
2400
 
2401
 
2402
;; --------------------------------------------------------------------------
2403
;; --------------------------------------------------------------------------
2404
;;  Help Functions which are used by all engines
2405
;; --------------------------------------------------------------------------
2406
;; --------------------------------------------------------------------------
2407
 
2408
;; --------------------------------------------------------------------------
2409
;; Help Function: Scale Depth (7bit signed value scales 15bit signed value)
2410
;; IN:  8bit signed depth value (+0x80) in WREG
2411
;;      15bit signed value in MUL_A_[LH]
2412
;; OUT: result in MUL_R_[123]
2413
;; USES: TABLAT
2414
;; --------------------------------------------------------------------------
2415
SIDSE_Hlp_ScaleDepth
2416
	movwf	TABLAT		; temporary store depth value in TABLAT
2417
 
2418
	rcall	SIDSE_Hlp_GetAbs8
2419
	clrc
2420
	rlf	WREG, W
2421
	movwf	MUL_B_L, BANKED
2422
	clrf	MUL_B_H, BANKED
2423
 
2424
	;; 16*16 multiplication, result in MUL_R_[0123]
2425
	call	MATH_MUL16_16_SIGNED
2426
 
2427
	;; invert result if required
2428
	BRA_IFSET TABLAT, 7, ACCESS, SIDSE_Hlp_ScaleDepth_Cont
2429
SIDSE_Hlp_ScaleDepth_Neg
2430
	comf	MUL_R_1, BANKED
2431
	comf	MUL_R_2, BANKED
2432
	comf	MUL_R_3, BANKED
2433
	incf	MUL_R_1, F, BANKED
2434
	skpnz
2435
	incf	MUL_R_2, F, BANKED
2436
	skpnz
2437
	incf	MUL_R_3, F, BANKED
2438
SIDSE_Hlp_ScaleDepth_Cont
2439
	return
2440
 
2441
 
2442
;; --------------------------------------------------------------------------
2443
;; Like SIDSE_Hlp_ScaleDepth, but depth is increased/decreased by SID_Ix_Vx_ACCENT
2444
;; if the SID_SE_STATE, SID_SE_STATE_ACCENT flag is set
2445
;; IN:  8bit signed depth value (+0x80) in WREG
2446
;;      15bit signed value in MUL_A_[LH]
2447
;;      pointer to patch structure in FSR0
2448
;; OUT: result in MUL_R_[123]
2449
;; USES: TABLAT
2450
;; --------------------------------------------------------------------------
2451
SIDSE_Hlp_ScaleDepthAcc
2452
	;; check accent flag
2453
	BRA_IFCLR SID_SE_STATE, SID_SE_STATE_ACCENT, BANKED, SIDSE_Hlp_ScaleDepth
2454
 
2455
	movwf	MUL_R_1, BANKED	; (used as temporary register)
2456
 
2457
	;; increase/decrease value by 0x40 and saturate
2458
	BRA_IFCLR WREG, 7, ACCESS, SIDSE_Hlp_ScaleDepthAcc_Neg
2459
SIDSE_Hlp_ScaleDepthAcc_Pos
2460
	movlw	SID_Ix_Vx_ACCENT
2461
	movf	PLUSW0, W
2462
	addwf	MUL_R_1, W, BANKED
2463
	btfss	WREG, 7
2464
	movlw 0xff
2465
	rgoto	SIDSE_Hlp_ScaleDepth
2466
SIDSE_Hlp_ScaleDepthAcc_Neg
2467
	movlw	SID_Ix_Vx_ACCENT
2468
	comf	PLUSW0, W
2469
	addlw	1
2470
	btfsc	WREG, 7
2471
	movlw 0x00
2472
	rgoto	SIDSE_Hlp_ScaleDepth
2473
 
2474
;; --------------------------------------------------------------------------
2475
;; Help Function: Get absolute value
2476
;; IN:  signed 16-bit value in IRQ_TMP[12]
2477
;; Out: unsigned absolute value in IRQ_TMP[12]
2478
;;      sign in IRQ_TMP3[0]
2479
;; --------------------------------------------------------------------------
2480
SIDSE_Hlp_GetAbs16
2481
	;; convert IRQ_TMP[12] to unsigned integer, keep sign in IRQ_TMP3[0]
2482
	clrf	IRQ_TMP3
2483
	BRA_IFCLR IRQ_TMP2, 7, ACCESS, SIDSE_Hlp_GetABS16_Pos
2484
SIDSE_Hlp_GetABS16_Neg
2485
	bsf	IRQ_TMP3, 0	; memorize sign in IRQ_TMP3[0]
2486
	comf	IRQ_TMP1, F
2487
	comf	IRQ_TMP2, F
2488
	incf	IRQ_TMP1, F
2489
	skpnz
2490
	incf	IRQ_TMP2, F
2491
SIDSE_Hlp_GetABS16_Pos
2492
	return
2493
 
2494
 
2495
;; --------------------------------------------------------------------------
2496
;; Help Function which returns the absolute value
2497
;; IN:  8-bit signed value in WREG
2498
;; OUT:	absolute value (0x00-0x7f) in WREG
2499
;; --------------------------------------------------------------------------
2500
SIDSE_Hlp_GetAbs8
2501
	movf	WREG, W
2502
	skpnz
2503
	addlw	1
2504
	btfss	WREG, 7
2505
	sublw 0x80
2506
	andlw	0x7f
2507
	return
2508
 
2509
;; --------------------------------------------------------------------------
2510
;; Help Function for SIDSE_ENV to increase or decrease the ENV counter
2511
;; IN:  incr value in MIOS_PARAMETER[12]
2512
;;	target level in IRQ_TMP1
2513
;;      pointer to SID_ENVx_BASE in FSR1
2514
;; OUT:	zero flag set if max value reached
2515
;; --------------------------------------------------------------------------
2516
SIDSE_Hlp_ENV_IncrDecrCtr
2517
	;; if current level > target level, branch to decr function
2518
	movlw	SID_ENVx_CTR_H
2519
	movf	PLUSW1, W
2520
	cpfslt	IRQ_TMP1, ACCESS
2521
	rgoto SIDSE_Hlp_ENV_IncrCtr
2522
	rgoto	SIDSE_Hlp_ENV_DecrCtr
2523
 
2524
 
2525
;; --------------------------------------------------------------------------
2526
;; Help Function for SIDSE_ENV to increase the ENV counter
2527
;; IN:  incr value in MIOS_PARAMETER[12]
2528
;;	max level in IRQ_TMP1
2529
;;      pointer to SID_ENVx_BASE in FSR1
2530
;; OUT:	zero flag set if max value reached
2531
;; --------------------------------------------------------------------------
2532
SIDSE_Hlp_ENV_IncrCtr
2533
	;; add to ENV counter
2534
	movf	POSTINC1, W	; SID_ENVx_CTR_L
2535
	addwf	MIOS_PARAMETER1, F
2536
	movf	POSTDEC1, W	; SID_ENVx_CTR_H
2537
	addwfc	MIOS_PARAMETER2, F
2538
	bc	SIDSE_Hlp_ENV_IncrCtr_Max
2539
 
2540
	;; if value >= level: switch to next phase
2541
	movf	MIOS_PARAMETER1, W
2542
	sublw	0x00
2543
	movf	MIOS_PARAMETER2, W
2544
	subwfb	IRQ_TMP1, W
2545
	bc	SIDSE_Hlp_ENV_IncrCtr_NoMax
2546
 
2547
SIDSE_Hlp_ENV_IncrCtr_Max
2548
	;; write level to MIOS_PARAMETER[12]
2549
	movff	IRQ_TMP1, MIOS_PARAMETER2
2550
	clrf	MIOS_PARAMETER1
2551
 
2552
	andlw	0x00		; set zero flag and exit
2553
	return
2554
 
2555
SIDSE_Hlp_ENV_IncrCtr_NoMax
2556
	iorlw	0xff		; clear zero flag and exit
2557
	return
2558
 
2559
 
2560
;; --------------------------------------------------------------------------
2561
;; Help Function for SIDSE_ENV to decrease the ENV counter
2562
;; IN:  decr value in MIOS_PARAMETER[12]
2563
;;	min level in IRQ_TMP1
2564
;;      pointer to SID_ENVx_BASE in FSR1
2565
;; OUT:	zero flag set if min value reached
2566
;; --------------------------------------------------------------------------
2567
SIDSE_Hlp_ENV_DecrCtr
2568
	;; subtraction from ENV counter
2569
	movf	MIOS_PARAMETER1, W
2570
	subwf	POSTINC1, W	; SID_ENVx_CTR_L
2571
	movwf	MIOS_PARAMETER1
2572
	movf	MIOS_PARAMETER2, W
2573
	subwfb	POSTDEC1, W	; SID_ENVx_CTR_H
2574
	movwf	MIOS_PARAMETER2
2575
	bnc	SIDSE_Hlp_ENV_DecrCtr_Min
2576
 
2577
	;; if value < level: switch to next phase
2578
	movlw	0x00
2579
	subwf	MIOS_PARAMETER1, W
2580
	movf	IRQ_TMP1, W
2581
	subwfb	MIOS_PARAMETER2, W
2582
	bc	SIDSE_Hlp_ENV_DecrCtr_NoMin
2583
 
2584
SIDSE_Hlp_ENV_DecrCtr_Min
2585
	;; write level to MIOS_PARAMETER[12]
2586
	movff	IRQ_TMP1, MIOS_PARAMETER2
2587
	clrf	MIOS_PARAMETER1
2588
 
2589
	andlw	0x00		; set zero flag and exit
2590
	return
2591
 
2592
SIDSE_Hlp_ENV_DecrCtr_NoMin
2593
	iorlw	0xff		; clear zero flag and exit
2594
	return
2595
 
2596
;; --------------------------------------------------------------------------
2597
;; Help Function for SIDSE_ENV
2598
;; IN:  pointer to SID_Ix_L_ENVx_BASE in FSR0 (patch record)
2599
;;      pointer to SID_ENVx_BASE in FSR1
2600
;;      curve value in WREG (0x80: no curve)
2601
;;	MBFM_Ix_ENVx_ATTACKx/MBFM_Ix_ENVx_DECAYx or MBFM_Ix_ENVx_SUSTAINx in IRQ_TMP3
2602
;;
2603
;;      Can also be used to determine the delay increment value
2604
;;      In this case, only IRQ_TMP3 (Ix_xxx_Delay) is expected in IRQ_TMP3, and 0x80 in WREG (no curve!)
2605
;;
2606
;; OUT:	value which should be added to - or subtracted from - SID_ENVx_CTR_[LH]
2607
;;      low-byte in MIOS_PARAMETER1; high-byte in MIOS_PARAMETER2
2608
;; --------------------------------------------------------------------------
2609
SIDSE_Hlp_ENV_GetBendedValue
2610
	;; get curve parameter and store it in IRQ_TMP1
2611
	movwf	IRQ_TMP1
2612
	xorlw	0x80		; if != 0x80, we apply the curve, otherwise we have a linear waveform
2613
 	bnz	SIDSE_Hlp_ENV_GetBendedValue_Crv
2614
 
2615
	;; curve not selected, get value from ENV_TABLE
2616
	movf	IRQ_TMP3, W
2617
	TABLE_ADDR_MUL_W SID_ENV_TABLE, 2	; determine table address
2618
	tblrd*+				; transfer table entry to MUL_A_[LH]
2619
	movff	TABLAT, MIOS_PARAMETER1
2620
	tblrd*+
2621
	movff	TABLAT, MIOS_PARAMETER2
2622
	return
2623
 
2624
SIDSE_Hlp_ENV_GetBendedValue_Crv
2625
	;; copy current counter value to IRQ_TMP2
2626
	movlw	SID_ENVx_CTR_H
2627
	movff	PLUSW1, IRQ_TMP2
2628
 
2629
	;; rightshift IRQ_TMP3 (7bit value range)
2630
	clrc
2631
	rrf	IRQ_TMP3, F
2632
 
2633
	;; feedback: calculate ABS8(CURVE) * ENV_x_CTR_H
2634
	movf	IRQ_TMP1, W		; get absolute value of curve parameter
2635
	rcall	SIDSE_Hlp_GetAbs8
2636
	btfsc	IRQ_TMP1, 7; invert if positive range (for more logical behaviour of positive/negative curve)
2637
	xorlw 0x7f
2638
	mulwf	IRQ_TMP2, ACCESS	; multiply with current counter value
2639
 
2640
	;; when CURVE parameter < 0x80: bend down, else up
2641
	BRA_IFCLR IRQ_TMP1, 7, ACCESS, SIDSE_Hlp_ENV_GetBendedValueDown
2642
SIDSE_Hlp_ENV_GetBendedValueUp
2643
	comf	IRQ_TMP3, F
2644
	bcf	IRQ_TMP3, 7
2645
	movf	PRODH, W
2646
	subwf	IRQ_TMP3, W
2647
	btfsc	WREG, 7
2648
	movlw 0x00
2649
	rgoto	SIDSE_Hlp_ENV_GetBendedValueCont
2650
 
2651
SIDSE_Hlp_ENV_GetBendedValueDown
2652
	comf	IRQ_TMP3, W
2653
	andlw	0x7f
2654
	addwf	PRODH, W
2655
	btfsc	WREG, 7
2656
	movlw 0x7f
2657
	;; 	rgoto	SIDSE_Hlp_ENV_GetBendedValue_Cont
2658
 
2659
SIDSE_Hlp_ENV_GetBendedValueCont
2660
	andlw	0x7f
2661
	TABLE_ADDR_MUL_W SID_FRQ_TABLE, 2	; determine table address
2662
	tblrd*+				; transfer table entry to MIOS_PARAMETER[12]
2663
	movff	TABLAT, MIOS_PARAMETER1
2664
	tblrd*+
2665
	movff	TABLAT, MIOS_PARAMETER2
2666
	return
2667
 
2668
 
2669
 
2670
;; --------------------------------------------------------------------------
2671
;;  FUNCTION: SIDSE_Hlp_EXT_AOUT_Transfer
2672
;;  DESCRIPTION: transfers 16bit value in IRQ_TMP[12] to EXT registers
2673
;;  IN: 16bit value in IRQ_TMP[12]
2674
;;      pointer to AOUTx_L in FSR1 (aout target)
2675
;; --------------------------------------------------------------------------
2676
SIDSE_Hlp_EXT_AOUT_Transfer
2677
	;; convert to 12bit -> PROD[LH]
2678
	swapf	IRQ_TMP2, W
2679
	andlw	0x0f
2680
	movwf	PRODH
2681
	swapf	IRQ_TMP2, W
2682
	andlw	0xf0
2683
	movwf	PRODL
2684
	swapf	IRQ_TMP1, W
2685
	andlw	0x0f
2686
	iorwf	PRODL, F
2687
 
2688
	;; check if value has been changed
2689
	movf	PRODL, W
2690
	xorwf	INDF1, W
2691
	bnz	SIDSE_Hlp_EXT_AOUT_Transfer_Chng
2692
	movf	POSTINC1, W	; (increment)
2693
	movf	PRODH, W
2694
	xorwf	POSTDEC1, W
2695
	andlw	0x7f
2696
	bz	SIDSE_Hlp_EXT_AOUT_Transfer_End
2697
SIDSE_Hlp_EXT_AOUT_Transfer_Chng
2698
 
2699
	;; transfer to AOUT
2700
	movff	PRODL, POSTINC1
2701
	movf	PRODH, W
2702
	andlw	0x7f
2703
	movwf	POSTDEC1
2704
 
2705
SIDSE_Hlp_EXT_AOUT_Transfer_End
2706
	return
2707
 
2708
 
2709
;; --------------------------------------------------------------------------
2710
;;  FUNCTION: SIDSE_Hlp_GetSIDFrqPtr
2711
;;  DESCRIPTION: returns pointer to SIDx_V1_FRQ_L register
2712
;;  IN: SID_SE_ELEMENT_NUM
2713
;;  OUT: pointer in FSR2
2714
;; --------------------------------------------------------------------------
2715
SIDSE_Hlp_GetSIDFrqPtr
2716
	BRA_IFSET SID_SE_ELEMENT_NUM, 2, BANKED, SIDSE_Hlp_GetSIDFrqPtr_4567
2717
SIDSE_Hlp_GetSIDFrqPtr_0123
2718
	BRA_IFSET SID_SE_ELEMENT_NUM, 1, BANKED, SIDSE_Hlp_GetSIDFrqPtr_23
2719
SIDSE_Hlp_GetSIDFrqPtr_01
2720
	lfsr	FSR2, SIDL_BASE + SIDx_V1_FRQ_L
2721
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2722
	lfsr	FSR2, SIDL_BASE + SIDx_V2_FRQ_L
2723
	return
2724
 
2725
SIDSE_Hlp_GetSIDFrqPtr_23
2726
	lfsr	FSR2, SIDL_BASE + SIDx_V3_FRQ_L
2727
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2728
	lfsr	FSR2, SIDR_BASE + SIDx_V1_FRQ_L
2729
	return
2730
 
2731
SIDSE_Hlp_GetSIDFrqPtr_4567
2732
	lfsr	FSR2, SIDR_BASE + SIDx_V2_FRQ_L
2733
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2734
	lfsr	FSR2, SIDR_BASE + SIDx_V3_FRQ_L
2735
	return
2736
 
2737
;; --------------------------------------------------------------------------
2738
;;  FUNCTION: SIDSE_Hlp_GetSIDPWPtr
2739
;;  DESCRIPTION: returns pointer to SIDx_V1_PW_L register
2740
;;  IN: SID_SE_ELEMENT_NUM
2741
;;  OUT: pointer in FSR2
2742
;; --------------------------------------------------------------------------
2743
SIDSE_Hlp_GetSIDPWPtr
2744
	BRA_IFSET SID_SE_ELEMENT_NUM, 2, BANKED, SIDSE_Hlp_GetSIDPWPtr_4567
2745
SIDSE_Hlp_GetSIDPWPtr_0123
2746
	BRA_IFSET SID_SE_ELEMENT_NUM, 1, BANKED, SIDSE_Hlp_GetSIDPWPtr_23
2747
SIDSE_Hlp_GetSIDPWPtr_01
2748
	lfsr	FSR2, SIDL_BASE + SIDx_V1_PW_L
2749
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2750
	lfsr	FSR2, SIDL_BASE + SIDx_V2_PW_L
2751
	return
2752
 
2753
SIDSE_Hlp_GetSIDPWPtr_23
2754
	lfsr	FSR2, SIDL_BASE + SIDx_V3_PW_L
2755
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2756
	lfsr	FSR2, SIDR_BASE + SIDx_V1_PW_L
2757
	return
2758
 
2759
SIDSE_Hlp_GetSIDPWPtr_4567
2760
	lfsr	FSR2, SIDR_BASE + SIDx_V2_PW_L
2761
	btfsc	SID_SE_ELEMENT_NUM, 0, BANKED
2762
	lfsr	FSR2, SIDR_BASE + SIDx_V3_PW_L
2763
	return
2764
 
2765
;; --------------------------------------------------------------------------
2766
;;  FUNCTION: SIDSE_Hlp_Pitch_Mod
2767
;; IN: frequency in SID_SE_TARGET_FRQ_[LH]
2768
;;     Signed Modulation offset in SID_MOD_TARG_PITCHx_[LHU]
2769
;;     Voice number in SID_SE_ELEMENT_NUM
2770
;; OUT: modulated frequency in SID_SE_TARGET_FRQ_[LH]
2771
;; --------------------------------------------------------------------------
2772
SIDSE_Hlp_Pitch_Mod
2773
	;; calculate pointer to MOD target array -> FSR2
2774
	movf	SID_SE_ELEMENT_NUM, W, BANKED
2775
	addlw	(SID_MOD_TARG_PITCH1_L-SID_MOD_TARG_BASE)/3
2776
	mullw	3
2777
	lfsr	FSR2, SID_MOD_TARG_BASE
2778
	movf	PRODL, W
2779
	addwf	FSR2L, F
2780
 
2781
	;; add modulation value to frequency
2782
	;; value is divided by four for better usage of the depth range
2783
	movff	POSTINC2, PRODL
2784
	movff	POSTINC2, PRODH
2785
	movff	POSTINC2, TABLAT
2786
 
2787
	clrc
2788
	btfsc	TABLAT, 7
2789
	setc
2790
	rrf	TABLAT, F
2791
	rrf	PRODH, F
2792
	rrf	PRODL, F
2793
 
2794
	clrc
2795
	btfsc	TABLAT, 7
2796
	setc
2797
	rrf	TABLAT, F
2798
	rrf	PRODH, F
2799
	rrf	PRODL, F
2800
 
2801
	movf	PRODL, W
2802
	addwf	SID_SE_TARGET_FRQ_L, F, BANKED
2803
	movf	PRODH, W
2804
	addwfc	SID_SE_TARGET_FRQ_H, F, BANKED
2805
 
2806
	;; saturate
2807
	BRA_IFSET TABLAT, 7, ACCESS, SIDSE_Hlp_Pitch_Mod_SatNeg
2808
SIDSE_Hlp_Pitch_Mod_SatPos
2809
	movf	TABLAT, W
2810
	bnz	SIDSE_Hlp_Pitch_Mod_SatPos_Sat
2811
	bnc	SIDSE_Hlp_Pitch_Mod_NoSat
2812
SIDSE_Hlp_Pitch_Mod_SatPos_Sat
2813
	setf	SID_SE_TARGET_FRQ_L, BANKED
2814
	setf	SID_SE_TARGET_FRQ_H, BANKED
2815
	rgoto	SIDSE_Hlp_Pitch_Mod_Sat_Cont
2816
SIDSE_Hlp_Pitch_Mod_SatNeg
2817
	comf	TABLAT, W
2818
	bnz	SIDSE_Hlp_Pitch_Mod_SatNeg_Sat
2819
	bc	SIDSE_Hlp_Pitch_Mod_NoSat
2820
SIDSE_Hlp_Pitch_Mod_SatNeg_Sat
2821
	clrf	SID_SE_TARGET_FRQ_L, BANKED
2822
	clrf	SID_SE_TARGET_FRQ_H, BANKED
2823
	;; 	rgoto	SIDSE_Hlp_Pitch_Mod_Sat_Cont
2824
SIDSE_Hlp_Pitch_Mod_Sat_Cont
2825
SIDSE_Hlp_Pitch_Mod_NoSat
2826
	return
2827
 
2828
;; --------------------------------------------------------------------------
2829
;; Help Function which interpolates a 16bit value
2830
;; IN:  base address of parameter set (FIPx_BASE) in FSR1
2831
;;      target value in IRQ_TMP[12]
2832
;; OUT:	interpolated value in IRQ_TMP[12]
2833
;; USES: PROD[LH]
2834
;; --------------------------------------------------------------------------
2835
SIDSE_Hlp_IP
2836
	;; shift-right target value (internally we have to calculate with a signed 16-bit value)
2837
	clrc
2838
	rrf	IRQ_TMP2, F
2839
	rrf	IRQ_TMP1, F
2840
 
2841
	;; check for new target value
2842
	movlw	FIPx_TARGET_VALUE_L
2843
	movf	PLUSW1, W
2844
	cpfseq	IRQ_TMP1, ACCESS
2845
	rgoto SIDSE_Hlp_IP_Change
2846
	movlw	FIPx_TARGET_VALUE_H
2847
	movf	PLUSW1, W
2848
	cpfseq	IRQ_TMP2, ACCESS
2849
	rgoto SIDSE_Hlp_IP_Change
2850
	rgoto	SIDSE_Hlp_IP_Cont
2851
SIDSE_Hlp_IP_Change
2852
	;; copy new target value
2853
	movlw	FIPx_TARGET_VALUE_L
2854
	movff	IRQ_TMP1, PLUSW1
2855
	movlw	FIPx_TARGET_VALUE_H
2856
	movff	IRQ_TMP2, PLUSW1
2857
 
2858
	;; calculate difference between current value and new target value
2859
	movlw	FIPx_VALUE_L
2860
	movf	PLUSW1, W
2861
	subwf	IRQ_TMP1, W
2862
	movwf	IRQ_TMP1
2863
	movlw	FIPx_VALUE_H
2864
	movf	PLUSW1, W
2865
	subwfb	IRQ_TMP2, W
2866
	movwf	IRQ_TMP2
2867
 
2868
	;; value which will be added on each step: diverence / 8
2869
	rrf	IRQ_TMP2, F
2870
	rrf	IRQ_TMP1, F
2871
	rrf	IRQ_TMP2, F
2872
	rrf	IRQ_TMP1, F
2873
	rrf	IRQ_TMP2, F
2874
	rrf	IRQ_TMP1, F
2875
 
2876
	movf	IRQ_TMP2, W
2877
	andlw	0x1f
2878
	btfsc	WREG, 4
2879
	iorlw 0xe0
2880
	movwf	IRQ_TMP2
2881
 
2882
	;; should be at least 1 (!)
2883
	movf	IRQ_TMP1, W
2884
	iorwf	IRQ_TMP2, W
2885
	skpnz
2886
	incf	IRQ_TMP1, F
2887
 
2888
	;; store divided value
2889
	movlw	FIPx_DIV_VALUE_L
2890
	movff	IRQ_TMP1, PLUSW1
2891
	movlw	FIPx_DIV_VALUE_H
2892
	movff	IRQ_TMP2, PLUSW1
2893
 
2894
SIDSE_Hlp_IP_Cont
2895
 
2896
	;; step handler
2897
 
2898
	;; copy current filter value into IRQ_TMP[12]
2899
	movlw	FIPx_VALUE_L
2900
	movff	PLUSW1, IRQ_TMP1
2901
	movlw	FIPx_VALUE_H
2902
	movff	PLUSW1, IRQ_TMP2
2903
 
2904
	;; do nothing if target value already reached
2905
	movlw	FIPx_TARGET_VALUE_L
2906
	movf	PLUSW1, W
2907
	cpfseq	IRQ_TMP1, ACCESS
2908
	rgoto SIDSE_Hlp_IP_Cont_Do
2909
	movlw	FIPx_TARGET_VALUE_H
2910
	movf	PLUSW1, W
2911
	cpfseq	IRQ_TMP2, ACCESS
2912
	rgoto SIDSE_Hlp_IP_Cont_Do
2913
	rgoto	SIDSE_Hlp_IP_End
2914
SIDSE_Hlp_IP_Cont_Do
2915
 
2916
	;; add div value to current value
2917
	movlw	FIPx_DIV_VALUE_L
2918
	movf	PLUSW1, W
2919
	addwf	IRQ_TMP1, F
2920
	movlw	FIPx_DIV_VALUE_H
2921
	movf	PLUSW1, W
2922
	addwfc	IRQ_TMP2, F
2923
 
2924
	;; end reached on overflow
2925
	BRA_IFSET IRQ_TMP2, 7, ACCESS, SIDSE_Hlp_IP_TargetReached
2926
 
2927
	;; end reached if value >= target value
2928
	movlw	FIPx_TARGET_VALUE_L
2929
	movf	PLUSW1, W
2930
	subwf	IRQ_TMP1, W
2931
	movlw	FIPx_TARGET_VALUE_H
2932
	movf	PLUSW1, W
2933
	subwfb	IRQ_TMP2, W
2934
 
2935
	;; branch depending on increment/decrement
2936
	movlw	FIPx_DIV_VALUE_H
2937
	BRA_IFSET PLUSW1, 7, ACCESS, SIDSE_Hlp_IP_Dec
2938
SIDSE_Hlp_IP_Inc
2939
	bnc	SIDSE_Hlp_IP_End
2940
	;; 	rgoto	SIDSE_Hlp_IP_TargetReached
2941
 
2942
SIDSE_Hlp_IP_TargetReached
2943
	movlw	FIPx_TARGET_VALUE_L
2944
	movff	PLUSW1, IRQ_TMP1
2945
	movlw	FIPx_TARGET_VALUE_H
2946
	movff	PLUSW1, IRQ_TMP2
2947
	rgoto	SIDSE_Hlp_IP_End
2948
 
2949
SIDSE_Hlp_IP_Dec
2950
	bc	SIDSE_Hlp_IP_End
2951
	rgoto	SIDSE_Hlp_IP_TargetReached
2952
 
2953
SIDSE_Hlp_IP_End
2954
	;; copy back IRQ_TMP[12] to FIPx_VALUE_[LH]
2955
	movlw	FIPx_VALUE_L
2956
	movff	IRQ_TMP1, PLUSW1
2957
	movlw	FIPx_VALUE_H
2958
	movff	IRQ_TMP2, PLUSW1
2959
 
2960
	;; left-shift result (it's 16bit unsigned again)
2961
	clrc
2962
	rlf	IRQ_TMP1, F
2963
	rlf	IRQ_TMP2, F
2964
 
2965
	return
2966