diff options
Diffstat (limited to 'lib/rbcodec/codecs/libgme/emu8950.c')
-rw-r--r-- | lib/rbcodec/codecs/libgme/emu8950.c | 1206 |
1 files changed, 1206 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/emu8950.c b/lib/rbcodec/codecs/libgme/emu8950.c new file mode 100644 index 0000000000..2f8a32044f --- /dev/null +++ b/lib/rbcodec/codecs/libgme/emu8950.c | |||
@@ -0,0 +1,1206 @@ | |||
1 | /* | ||
2 | * This file is based on: | ||
3 | * Y8950.cc -- Y8950 emulator from the openMSX team | ||
4 | * ported to c by gama | ||
5 | * | ||
6 | * The openMSX version is based on: | ||
7 | * emu8950.c -- Y8950 emulator written by Mitsutaka Okazaki 2001 | ||
8 | * heavily rewritten to fit openMSX structure | ||
9 | */ | ||
10 | |||
11 | #include <math.h> | ||
12 | #include "emu8950.h" | ||
13 | |||
14 | #ifdef _MSC_VER | ||
15 | #pragma warning( disable : 4355 ) | ||
16 | #endif | ||
17 | |||
18 | #if !defined(ROCKBOX) | ||
19 | #define EMU8950_CALCUL_TABLES | ||
20 | #else | ||
21 | #include "opltables.h" | ||
22 | #endif | ||
23 | |||
24 | // dB to Liner table | ||
25 | static short dB2LinTab[(2*DB_MUTE)*2]; | ||
26 | // Dynamic range | ||
27 | static unsigned int dphaseNoiseTable[1024][8]; | ||
28 | // LFO Table | ||
29 | int pmtable[2][PM_PG_WIDTH]; | ||
30 | int amtable[2][AM_PG_WIDTH]; | ||
31 | |||
32 | /** WaveTable for each envelope amp. */ | ||
33 | static int sintable[PG_WIDTH]; | ||
34 | /** Phase incr table for Attack. */ | ||
35 | static unsigned int dphaseARTable[16][16]; | ||
36 | /** Phase incr table for Decay and Release. */ | ||
37 | static unsigned int dphaseDRTable[16][16]; | ||
38 | /** KSL + TL Table. */ | ||
39 | #if !defined(ROCKBOX) | ||
40 | static unsigned char tllTable[16][8][1<<TL_BITS][4]; | ||
41 | #else | ||
42 | /* Use the table calculated in emu2413 which is identical. */ | ||
43 | extern unsigned char tllTable[16][8][1<<TL_BITS][4]; | ||
44 | #endif | ||
45 | static int rksTable[2][8][2]; | ||
46 | /** Since we wont change clock rate in rockbox we can | ||
47 | skip this table */ | ||
48 | #if !defined(ROCKBOX) | ||
49 | /** Phase incr table for PG. */ | ||
50 | static unsigned int dphaseTable[1024][8][16]; | ||
51 | #endif | ||
52 | |||
53 | /** Liner to Log curve conversion table (for Attack rate). */ | ||
54 | static int AR_ADJUST_TABLE[1<<EG_BITS]; | ||
55 | |||
56 | //**************************************************// | ||
57 | // // | ||
58 | // Helper functions // | ||
59 | // // | ||
60 | //**************************************************// | ||
61 | |||
62 | #define ALIGN(d, SS, SD) (d*(int)(SS/SD)) | ||
63 | |||
64 | inline static int DB_POS(int x) | ||
65 | { | ||
66 | return (int)(x/DB_STEP); | ||
67 | } | ||
68 | |||
69 | inline static int DB_NEG(int x) | ||
70 | { | ||
71 | return (int)(2*DB_MUTE+x/DB_STEP); | ||
72 | } | ||
73 | |||
74 | // Cut the lower b bits off | ||
75 | inline static int HIGHBITS(int c, int b) | ||
76 | { | ||
77 | return c >> b; | ||
78 | } | ||
79 | // Leave the lower b bits | ||
80 | inline static int LOWBITS(int c, int b) | ||
81 | { | ||
82 | return c & ((1<<b)-1); | ||
83 | } | ||
84 | // Expand x which is s bits to d bits | ||
85 | inline static int EXPAND_BITS(int x, int s, int d) | ||
86 | { | ||
87 | return x << (d-s); | ||
88 | } | ||
89 | |||
90 | //**************************************************// | ||
91 | // // | ||
92 | // Create tables // | ||
93 | // // | ||
94 | //**************************************************// | ||
95 | |||
96 | // Table for AR to LogCurve. | ||
97 | static void makeAdjustTable(void) | ||
98 | { | ||
99 | AR_ADJUST_TABLE[0] = 1 << EG_BITS; | ||
100 | for (int i = 1; i < (1 << EG_BITS); i++) | ||
101 | #ifdef EMU8950_CALCUL_TABLES | ||
102 | AR_ADJUST_TABLE[i] = (int)((double)(1 << EG_BITS) - 1 - | ||
103 | (1 << EG_BITS) * log((double)i) / log((double)(1 << EG_BITS))) >> 1; | ||
104 | #else | ||
105 | AR_ADJUST_TABLE[i] = ar_adjust_coeff[i-1]; | ||
106 | #endif | ||
107 | } | ||
108 | |||
109 | // Table for dB(0 -- (1<<DB_BITS)) to Liner(0 -- DB2LIN_AMP_WIDTH) | ||
110 | static void makeDB2LinTable(void) | ||
111 | { | ||
112 | int i; | ||
113 | for (i=0; i < 2*DB_MUTE; i++) { | ||
114 | dB2LinTab[i] = (i<DB_MUTE) ? | ||
115 | #ifdef EMU8950_CALCUL_TABLES | ||
116 | (int)((double)((1<<DB2LIN_AMP_BITS)-1)*pow((double)10,-(double)i*DB_STEP/20)) : | ||
117 | #else | ||
118 | db2lin_coeff[i] : | ||
119 | #endif | ||
120 | 0; | ||
121 | dB2LinTab[i + 2*DB_MUTE] = -dB2LinTab[i]; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | // Sin Table | ||
126 | static void makeSinTable(void) | ||
127 | { | ||
128 | int i; | ||
129 | for (i=0; i < PG_WIDTH/4; i++) | ||
130 | #ifdef EMU8950_CALCUL_TABLES | ||
131 | sintable[i] = lin2db(sin(2.0*MPI*i/PG_WIDTH)); | ||
132 | #else | ||
133 | sintable[i] = sin_coeff[i]; | ||
134 | #endif | ||
135 | for (int i=0; i < PG_WIDTH/4; i++) | ||
136 | sintable[PG_WIDTH/2 - 1 - i] = sintable[i]; | ||
137 | for (int i=0; i < PG_WIDTH/2; i++) | ||
138 | sintable[PG_WIDTH/2 + i] = 2*DB_MUTE + sintable[i]; | ||
139 | } | ||
140 | |||
141 | static void makeDphaseNoiseTable(int sampleRate, int clockRate) | ||
142 | { | ||
143 | for (int i=0; i<1024; i++) | ||
144 | for (int j=0; j<8; j++) | ||
145 | dphaseNoiseTable[i][j] = rate_adjust(i<<j, sampleRate, clockRate); | ||
146 | } | ||
147 | |||
148 | // Table for Pitch Modulator | ||
149 | static void makePmTable(void) | ||
150 | { | ||
151 | int i; | ||
152 | for (i=0; i < PM_PG_WIDTH; i++) | ||
153 | #ifdef EMU8950_CALCUL_TABLES | ||
154 | pmtable[0][i] = (int)((double)PM_AMP * pow(2.,(double)PM_DEPTH*sin(2.0*MPI*i/PM_PG_WIDTH)/1200)); | ||
155 | #else | ||
156 | pmtable[0][i] = pm0_coeff[i]; | ||
157 | #endif | ||
158 | for (i=0; i < PM_PG_WIDTH; i++) | ||
159 | #ifdef EMU8950_CALCUL_TABLES | ||
160 | pmtable[1][i] = (int)((double)PM_AMP * pow(2.,(double)PM_DEPTH2*sin(2.0*MPI*i/PM_PG_WIDTH)/1200)); | ||
161 | #else | ||
162 | pmtable[1][i] = pm1_coeff[i]; | ||
163 | #endif | ||
164 | } | ||
165 | |||
166 | // Table for Amp Modulator | ||
167 | static void makeAmTable(void) | ||
168 | { | ||
169 | int i; | ||
170 | for (i=0; i<AM_PG_WIDTH; i++) | ||
171 | #ifdef EMU8950_CALCUL_TABLES | ||
172 | amtable[0][i] = (int)((double)AM_DEPTH/2/DB_STEP * (1.0 + sin(2.0*MPI*i/PM_PG_WIDTH))); | ||
173 | #else | ||
174 | amtable[0][i] = am0_coeff[i]; | ||
175 | #endif | ||
176 | for (i=0; i<AM_PG_WIDTH; i++) | ||
177 | #ifdef EMU8950_CALCUL_TABLES | ||
178 | amtable[1][i] = (int)((double)AM_DEPTH2/2/DB_STEP * (1.0 + sin(2.0*MPI*i/PM_PG_WIDTH))); | ||
179 | #else | ||
180 | amtable[1][i] = am1_coeff[i]; | ||
181 | #endif | ||
182 | } | ||
183 | |||
184 | #if !defined(ROCKBOX) | ||
185 | // Phase increment counter table | ||
186 | static void makeDphaseTable(int sampleRate, int clockRate) | ||
187 | { | ||
188 | int mltable[16] = { | ||
189 | 1,1*2,2*2,3*2,4*2,5*2,6*2,7*2,8*2,9*2,10*2,10*2,12*2,12*2,15*2,15*2 | ||
190 | }; | ||
191 | |||
192 | int fnum, block, ML; | ||
193 | for (fnum=0; fnum<1024; fnum++) | ||
194 | for (block=0; block<8; block++) | ||
195 | for (ML=0; ML<16; ML++) | ||
196 | dphaseTable[fnum][block][ML] = | ||
197 | rate_adjust((((fnum * mltable[ML]) << block) >> (21 - DP_BITS)), sampleRate, clockRate); | ||
198 | } | ||
199 | #endif | ||
200 | |||
201 | #if !defined(ROCKBOX) | ||
202 | static void makeTllTable(void) | ||
203 | { | ||
204 | #define dB2(x) (int)((x)*2) | ||
205 | static int kltable[16] = { | ||
206 | dB2( 0.000),dB2( 9.000),dB2(12.000),dB2(13.875), | ||
207 | dB2(15.000),dB2(16.125),dB2(16.875),dB2(17.625), | ||
208 | dB2(18.000),dB2(18.750),dB2(19.125),dB2(19.500), | ||
209 | dB2(19.875),dB2(20.250),dB2(20.625),dB2(21.000) | ||
210 | }; | ||
211 | |||
212 | int fnum, block, TL, KL; | ||
213 | for (fnum=0; fnum<16; fnum++) | ||
214 | for (block=0; block<8; block++) | ||
215 | for (TL=0; TL<64; TL++) | ||
216 | for (KL=0; KL<4; KL++) { | ||
217 | if (KL==0) { | ||
218 | tllTable[fnum][block][TL][KL] = (ALIGN(TL, TL_STEP, EG_STEP) ) >> 1; | ||
219 | } else { | ||
220 | int tmp = kltable[fnum] - dB2(3.000) * (7 - block); | ||
221 | if (tmp <= 0) | ||
222 | tllTable[fnum][block][TL][KL] = (ALIGN(TL, TL_STEP, EG_STEP) ) >> 1; | ||
223 | else | ||
224 | tllTable[fnum][block][TL][KL] = ((int)((tmp>>(3-KL))/EG_STEP) + ALIGN(TL, TL_STEP, EG_STEP) ) >> 1; | ||
225 | } | ||
226 | } | ||
227 | } | ||
228 | #endif | ||
229 | |||
230 | // Rate Table for Attack | ||
231 | static void makeDphaseARTable(int sampleRate, int clockRate) | ||
232 | { | ||
233 | int AR, Rks; | ||
234 | for (AR=0; AR<16; AR++) | ||
235 | for (Rks=0; Rks<16; Rks++) { | ||
236 | int RM = AR + (Rks>>2); | ||
237 | int RL = Rks&3; | ||
238 | if (RM>15) RM=15; | ||
239 | switch (AR) { | ||
240 | case 0: | ||
241 | dphaseARTable[AR][Rks] = 0; | ||
242 | break; | ||
243 | case 15: | ||
244 | dphaseARTable[AR][Rks] = EG_DP_WIDTH; | ||
245 | break; | ||
246 | default: | ||
247 | dphaseARTable[AR][Rks] = rate_adjust((3*(RL+4) << (RM+1)), sampleRate, clockRate); | ||
248 | break; | ||
249 | } | ||
250 | } | ||
251 | } | ||
252 | |||
253 | // Rate Table for Decay | ||
254 | static void makeDphaseDRTable(int sampleRate, int clockRate) | ||
255 | { | ||
256 | int DR, Rks; | ||
257 | for (DR=0; DR<16; DR++) | ||
258 | for (Rks=0; Rks<16; Rks++) { | ||
259 | int RM = DR + (Rks>>2); | ||
260 | int RL = Rks&3; | ||
261 | if (RM>15) RM=15; | ||
262 | switch (DR) { | ||
263 | case 0: | ||
264 | dphaseDRTable[DR][Rks] = 0; | ||
265 | break; | ||
266 | default: | ||
267 | dphaseDRTable[DR][Rks] = rate_adjust((RL+4) << (RM-1), sampleRate, clockRate); | ||
268 | break; | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | |||
273 | static void makeRksTable(void) | ||
274 | { | ||
275 | int fnum9, block, KR; | ||
276 | for (fnum9=0; fnum9<2; fnum9++) | ||
277 | for (block=0; block<8; block++) | ||
278 | for (KR=0; KR<2; KR++) { | ||
279 | rksTable[fnum9][block][KR] = (KR != 0) ? | ||
280 | (block<<1) + fnum9: | ||
281 | block>>1; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | //**********************************************************// | ||
286 | // // | ||
287 | // Patch // | ||
288 | // // | ||
289 | //**********************************************************// | ||
290 | |||
291 | |||
292 | void patchReset(struct Patch* patch) | ||
293 | { | ||
294 | patch->AM = patch->PM = patch->EG = false; | ||
295 | patch->KR = patch->ML = patch->KL = patch->TL = | ||
296 | patch->FB = patch->AR = patch->DR = patch->SL = patch->RR = 0; | ||
297 | } | ||
298 | |||
299 | |||
300 | //**********************************************************// | ||
301 | // // | ||
302 | // Slot // | ||
303 | // // | ||
304 | //**********************************************************// | ||
305 | |||
306 | |||
307 | static inline void slotUpdatePG(struct Slot* slot) | ||
308 | { | ||
309 | #if defined(ROCKBOX) | ||
310 | static const int mltable[16] = { | ||
311 | 1,1*2,2*2,3*2,4*2,5*2,6*2,7*2,8*2,9*2,10*2,10*2,12*2,12*2,15*2,15*2 | ||
312 | }; | ||
313 | |||
314 | slot->dphase = ((slot->fnum * mltable[slot->patch.ML]) << slot->block) >> (21 - DP_BITS); | ||
315 | #else | ||
316 | slot->dphase = dphaseTable[slot->fnum][slot->block][slot->patch.ML]; | ||
317 | #endif | ||
318 | } | ||
319 | |||
320 | static inline void slotUpdateTLL(struct Slot* slot) | ||
321 | { | ||
322 | slot->tll = (int)(tllTable[slot->fnum>>6][slot->block][slot->patch.TL][slot->patch.KL]) << 1; | ||
323 | } | ||
324 | |||
325 | static inline void slotUpdateRKS(struct Slot* slot) | ||
326 | { | ||
327 | slot->rks = rksTable[slot->fnum>>9][slot->block][slot->patch.KR]; | ||
328 | } | ||
329 | |||
330 | static inline void slotUpdateEG(struct Slot* slot) | ||
331 | { | ||
332 | switch (slot->eg_mode) { | ||
333 | case ATTACK: | ||
334 | slot->eg_dphase = dphaseARTable[slot->patch.AR][slot->rks]; | ||
335 | break; | ||
336 | case DECAY: | ||
337 | slot->eg_dphase = dphaseDRTable[slot->patch.DR][slot->rks]; | ||
338 | break; | ||
339 | case SUSTINE: | ||
340 | slot->eg_dphase = dphaseDRTable[slot->patch.RR][slot->rks]; | ||
341 | break; | ||
342 | case RELEASE: | ||
343 | slot->eg_dphase = slot->patch.EG ? | ||
344 | dphaseDRTable[slot->patch.RR][slot->rks]: | ||
345 | dphaseDRTable[7] [slot->rks]; | ||
346 | break; | ||
347 | case SUSHOLD: | ||
348 | case FINISH: | ||
349 | slot->eg_dphase = 0; | ||
350 | break; | ||
351 | } | ||
352 | } | ||
353 | |||
354 | static inline void slotUpdateAll(struct Slot* slot) | ||
355 | { | ||
356 | slotUpdatePG(slot); | ||
357 | slotUpdateTLL(slot); | ||
358 | slotUpdateRKS(slot); | ||
359 | slotUpdateEG(slot); // EG should be last | ||
360 | } | ||
361 | |||
362 | void slotReset(struct Slot* slot) | ||
363 | { | ||
364 | slot->phase = 0; | ||
365 | slot->dphase = 0; | ||
366 | slot->output[0] = 0; | ||
367 | slot->output[1] = 0; | ||
368 | slot->feedback = 0; | ||
369 | slot->eg_mode = FINISH; | ||
370 | slot->eg_phase = EG_DP_WIDTH; | ||
371 | slot->eg_dphase = 0; | ||
372 | slot->rks = 0; | ||
373 | slot->tll = 0; | ||
374 | slot->fnum = 0; | ||
375 | slot->block = 0; | ||
376 | slot->pgout = 0; | ||
377 | slot->egout = 0; | ||
378 | slot->slotStatus = false; | ||
379 | patchReset(&slot->patch); | ||
380 | slotUpdateAll(slot); | ||
381 | } | ||
382 | |||
383 | // Slot key on | ||
384 | static inline void slotOn(struct Slot* slot) | ||
385 | { | ||
386 | if (!slot->slotStatus) { | ||
387 | slot->slotStatus = true; | ||
388 | slot->eg_mode = ATTACK; | ||
389 | slot->phase = 0; | ||
390 | slot->eg_phase = 0; | ||
391 | } | ||
392 | } | ||
393 | |||
394 | // Slot key off | ||
395 | static inline void slotOff(struct Slot* slot) | ||
396 | { | ||
397 | if (slot->slotStatus) { | ||
398 | slot->slotStatus = false; | ||
399 | if (slot->eg_mode == ATTACK) | ||
400 | slot->eg_phase = EXPAND_BITS(AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase, EG_DP_BITS-EG_BITS)], EG_BITS, EG_DP_BITS); | ||
401 | slot->eg_mode = RELEASE; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | |||
406 | //**********************************************************// | ||
407 | // // | ||
408 | // OPLChannel // | ||
409 | // // | ||
410 | //**********************************************************// | ||
411 | |||
412 | |||
413 | void channelReset(struct OPLChannel* ch) | ||
414 | { | ||
415 | slotReset(&ch->mod); | ||
416 | slotReset(&ch->car); | ||
417 | ch->alg = false; | ||
418 | } | ||
419 | |||
420 | // Set F-Number ( fnum : 10bit ) | ||
421 | static void channelSetFnumber(struct OPLChannel* ch, int fnum) | ||
422 | { | ||
423 | ch->car.fnum = fnum; | ||
424 | ch->mod.fnum = fnum; | ||
425 | } | ||
426 | |||
427 | // Set Block data (block : 3bit ) | ||
428 | static void channelSetBlock(struct OPLChannel* ch, int block) | ||
429 | { | ||
430 | ch->car.block = block; | ||
431 | ch->mod.block = block; | ||
432 | } | ||
433 | |||
434 | // OPLChannel key on | ||
435 | static void keyOn(struct OPLChannel* ch) | ||
436 | { | ||
437 | slotOn(&ch->mod); | ||
438 | slotOn(&ch->car); | ||
439 | } | ||
440 | |||
441 | // OPLChannel key off | ||
442 | static void keyOff(struct OPLChannel* ch) | ||
443 | { | ||
444 | slotOff(&ch->mod); | ||
445 | slotOff(&ch->car); | ||
446 | } | ||
447 | |||
448 | |||
449 | //**********************************************************// | ||
450 | // // | ||
451 | // Y8950 // | ||
452 | // // | ||
453 | //**********************************************************// | ||
454 | |||
455 | void OPL_init(struct Y8950* this, byte* ramBank, int sampleRam) | ||
456 | { | ||
457 | this->clockRate = CLK_FREQ; | ||
458 | |||
459 | ADPCM_init(&this->adpcm, this, ramBank, sampleRam); | ||
460 | |||
461 | makePmTable(); | ||
462 | makeAmTable(); | ||
463 | |||
464 | makeAdjustTable(); | ||
465 | makeDB2LinTable(); | ||
466 | #if !defined(ROCKBOX) | ||
467 | makeTllTable(); | ||
468 | #endif | ||
469 | makeRksTable(); | ||
470 | makeSinTable(); | ||
471 | |||
472 | int i; | ||
473 | for (i=0; i<9; i++) { | ||
474 | // TODO cleanup | ||
475 | this->slot[i*2+0] = &(this->ch[i].mod); | ||
476 | this->slot[i*2+1] = &(this->ch[i].car); | ||
477 | this->ch[i].mod.plfo_am = &this->lfo_am; | ||
478 | this->ch[i].mod.plfo_pm = &this->lfo_pm; | ||
479 | this->ch[i].car.plfo_am = &this->lfo_am; | ||
480 | this->ch[i].car.plfo_pm = &this->lfo_pm; | ||
481 | } | ||
482 | |||
483 | OPL_reset(this); | ||
484 | } | ||
485 | |||
486 | void OPL_setSampleRate(struct Y8950* this, int sampleRate, int clockRate) | ||
487 | { | ||
488 | this->clockRate = clockRate; | ||
489 | ADPCM_setSampleRate(&this->adpcm, sampleRate, clockRate); | ||
490 | |||
491 | #if !defined(ROCKBOX) | ||
492 | makeDphaseTable(sampleRate, clockRate); | ||
493 | #endif | ||
494 | makeDphaseARTable(sampleRate, clockRate); | ||
495 | makeDphaseDRTable(sampleRate, clockRate); | ||
496 | makeDphaseNoiseTable(sampleRate, clockRate); | ||
497 | this->pm_dphase = rate_adjust( (int)(PM_SPEED * PM_DP_WIDTH) / (clockRate/72), sampleRate, clockRate); | ||
498 | this->am_dphase = rate_adjust( (int)(AM_SPEED * AM_DP_WIDTH) / (clockRate/72), sampleRate, clockRate); | ||
499 | } | ||
500 | |||
501 | // Reset whole of opl except patch datas. | ||
502 | void OPL_reset(struct Y8950* this) | ||
503 | { | ||
504 | int i; | ||
505 | for (i=0; i<9; i++) | ||
506 | channelReset(&this->ch[i]); | ||
507 | this->output[0] = 0; | ||
508 | this->output[1] = 0; | ||
509 | |||
510 | |||
511 | this->dacSampleVolume = 0; | ||
512 | this->dacOldSampleVolume = 0; | ||
513 | this->dacSampleVolumeSum = 0; | ||
514 | this->dacCtrlVolume = 0; | ||
515 | this->dacDaVolume = 0; | ||
516 | this->dacEnabled = 0; | ||
517 | |||
518 | this->rythm_mode = false; | ||
519 | this->am_mode = 0; | ||
520 | this->pm_mode = 0; | ||
521 | this->pm_phase = 0; | ||
522 | this->am_phase = 0; | ||
523 | this->noise_seed = 0xffff; | ||
524 | this->noiseA = 0; | ||
525 | this->noiseB = 0; | ||
526 | this->noiseA_phase = 0; | ||
527 | this->noiseB_phase = 0; | ||
528 | this->noiseA_dphase = 0; | ||
529 | this->noiseB_dphase = 0; | ||
530 | |||
531 | for (i = 0; i < 0x100; ++i) | ||
532 | this->reg[i] = 0x00; | ||
533 | |||
534 | this->reg[0x04] = 0x18; | ||
535 | this->reg[0x19] = 0x0F; // fixes 'Thunderbirds are Go' | ||
536 | this->status = 0x00; | ||
537 | this->statusMask = 0; | ||
538 | /* irq.reset(); */ | ||
539 | |||
540 | ADPCM_reset(&this->adpcm); | ||
541 | OPL_setInternalMute(this, true); // muted | ||
542 | } | ||
543 | |||
544 | |||
545 | // Drum key on | ||
546 | static inline void keyOn_BD(struct Y8950* this) { keyOn(&this->ch[6]); } | ||
547 | static inline void keyOn_HH(struct Y8950* this) { slotOn(&this->ch[7].mod); } | ||
548 | static inline void keyOn_SD(struct Y8950* this) { slotOn(&this->ch[7].car); } | ||
549 | static inline void keyOn_TOM(struct Y8950* this) { slotOn(&this->ch[8].mod); } | ||
550 | static inline void keyOn_CYM(struct Y8950* this) { slotOn(&this->ch[8].car); } | ||
551 | |||
552 | // Drum key off | ||
553 | static inline void keyOff_BD(struct Y8950* this) { keyOff(&this->ch[6]); } | ||
554 | static inline void keyOff_HH(struct Y8950* this) { slotOff(&this->ch[7].mod); } | ||
555 | static inline void keyOff_SD(struct Y8950* this) { slotOff(&this->ch[7].car); } | ||
556 | static inline void keyOff_TOM(struct Y8950* this){ slotOff(&this->ch[8].mod); } | ||
557 | static inline void keyOff_CYM(struct Y8950* this){ slotOff(&this->ch[8].car); } | ||
558 | |||
559 | // Change Rhythm Mode | ||
560 | static inline void setRythmMode(struct Y8950* this, int data) | ||
561 | { | ||
562 | bool newMode = (data & 32) != 0; | ||
563 | if (this->rythm_mode != newMode) { | ||
564 | this->rythm_mode = newMode; | ||
565 | if (!this->rythm_mode) { | ||
566 | // ON->OFF | ||
567 | this->ch[6].mod.eg_mode = FINISH; // BD1 | ||
568 | this->ch[6].mod.slotStatus = false; | ||
569 | this->ch[6].car.eg_mode = FINISH; // BD2 | ||
570 | this->ch[6].car.slotStatus = false; | ||
571 | this->ch[7].mod.eg_mode = FINISH; // HH | ||
572 | this->ch[7].mod.slotStatus = false; | ||
573 | this->ch[7].car.eg_mode = FINISH; // SD | ||
574 | this->ch[7].car.slotStatus = false; | ||
575 | this->ch[8].mod.eg_mode = FINISH; // TOM | ||
576 | this->ch[8].mod.slotStatus = false; | ||
577 | this->ch[8].car.eg_mode = FINISH; // CYM | ||
578 | this->ch[8].car.slotStatus = false; | ||
579 | } | ||
580 | } | ||
581 | } | ||
582 | |||
583 | //********************************************************// | ||
584 | // // | ||
585 | // Generate wave data // | ||
586 | // // | ||
587 | //********************************************************// | ||
588 | |||
589 | // Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). | ||
590 | inline static int wave2_4pi(int e) | ||
591 | { | ||
592 | int shift = SLOT_AMP_BITS - PG_BITS - 1; | ||
593 | if (shift > 0) | ||
594 | return e >> shift; | ||
595 | else | ||
596 | return e << -shift; | ||
597 | } | ||
598 | |||
599 | // Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). | ||
600 | inline static int wave2_8pi(int e) | ||
601 | { | ||
602 | int shift = SLOT_AMP_BITS - PG_BITS - 2; | ||
603 | if (shift > 0) | ||
604 | return e >> shift; | ||
605 | else | ||
606 | return e << -shift; | ||
607 | } | ||
608 | |||
609 | static inline void update_noise(struct Y8950* this) | ||
610 | { | ||
611 | if (this->noise_seed & 1) | ||
612 | this->noise_seed ^= 0x24000; | ||
613 | this->noise_seed >>= 1; | ||
614 | this->whitenoise = this->noise_seed&1 ? DB_POS(6) : DB_NEG(6); | ||
615 | |||
616 | this->noiseA_phase += this->noiseA_dphase; | ||
617 | this->noiseB_phase += this->noiseB_dphase; | ||
618 | |||
619 | this->noiseA_phase &= (0x40<<11) - 1; | ||
620 | if ((this->noiseA_phase>>11)==0x3f) | ||
621 | this->noiseA_phase = 0; | ||
622 | this->noiseA = this->noiseA_phase&(0x03<<11)?DB_POS(6):DB_NEG(6); | ||
623 | |||
624 | this->noiseB_phase &= (0x10<<11) - 1; | ||
625 | this->noiseB = this->noiseB_phase&(0x0A<<11)?DB_POS(6):DB_NEG(6); | ||
626 | } | ||
627 | |||
628 | static inline void update_ampm(struct Y8950* this) | ||
629 | { | ||
630 | this->pm_phase = (this->pm_phase + this->pm_dphase)&(PM_DP_WIDTH - 1); | ||
631 | this->am_phase = (this->am_phase + this->am_dphase)&(AM_DP_WIDTH - 1); | ||
632 | this->lfo_am = amtable[this->am_mode][HIGHBITS(this->am_phase, AM_DP_BITS - AM_PG_BITS)]; | ||
633 | this->lfo_pm = pmtable[this->pm_mode][HIGHBITS(this->pm_phase, PM_DP_BITS - PM_PG_BITS)]; | ||
634 | } | ||
635 | |||
636 | static inline void calc_phase(struct Slot* slot) | ||
637 | { | ||
638 | if (slot->patch.PM) | ||
639 | slot->phase += (slot->dphase * (*slot->plfo_pm)) >> PM_AMP_BITS; | ||
640 | else | ||
641 | slot->phase += slot->dphase; | ||
642 | slot->phase &= (DP_WIDTH - 1); | ||
643 | slot->pgout = HIGHBITS(slot->phase, DP_BASE_BITS); | ||
644 | } | ||
645 | |||
646 | static inline void calc_envelope(struct Slot* slot) | ||
647 | { | ||
648 | #define S2E(x) (ALIGN((unsigned int)(x/SL_STEP),SL_STEP,EG_STEP)<<(EG_DP_BITS-EG_BITS)) | ||
649 | static unsigned int SL[16] = { | ||
650 | S2E( 0), S2E( 3), S2E( 6), S2E( 9), S2E(12), S2E(15), S2E(18), S2E(21), | ||
651 | S2E(24), S2E(27), S2E(30), S2E(33), S2E(36), S2E(39), S2E(42), S2E(93) | ||
652 | }; | ||
653 | |||
654 | switch (slot->eg_mode) { | ||
655 | case ATTACK: | ||
656 | slot->eg_phase += slot->eg_dphase; | ||
657 | if (EG_DP_WIDTH & slot->eg_phase) { | ||
658 | slot->egout = 0; | ||
659 | slot->eg_phase= 0; | ||
660 | slot->eg_mode = DECAY; | ||
661 | slotUpdateEG(slot); | ||
662 | } else { | ||
663 | slot->egout = AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS)]; | ||
664 | } | ||
665 | break; | ||
666 | |||
667 | case DECAY: | ||
668 | slot->eg_phase += slot->eg_dphase; | ||
669 | slot->egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); | ||
670 | if (slot->eg_phase >= SL[slot->patch.SL]) { | ||
671 | if (slot->patch.EG) { | ||
672 | slot->eg_phase = SL[slot->patch.SL]; | ||
673 | slot->eg_mode = SUSHOLD; | ||
674 | slotUpdateEG(slot); | ||
675 | } else { | ||
676 | slot->eg_phase = SL[slot->patch.SL]; | ||
677 | slot->eg_mode = SUSTINE; | ||
678 | slotUpdateEG(slot); | ||
679 | } | ||
680 | slot->egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); | ||
681 | } | ||
682 | break; | ||
683 | |||
684 | case SUSHOLD: | ||
685 | slot->egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); | ||
686 | if (!slot->patch.EG) { | ||
687 | slot->eg_mode = SUSTINE; | ||
688 | slotUpdateEG(slot); | ||
689 | } | ||
690 | break; | ||
691 | |||
692 | case SUSTINE: | ||
693 | case RELEASE: | ||
694 | slot->eg_phase += slot->eg_dphase; | ||
695 | slot->egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS); | ||
696 | if (slot->egout >= (1<<EG_BITS)) { | ||
697 | slot->eg_mode = FINISH; | ||
698 | slot->egout = (1<<EG_BITS) - 1; | ||
699 | } | ||
700 | break; | ||
701 | |||
702 | case FINISH: | ||
703 | slot->egout = (1<<EG_BITS) - 1; | ||
704 | break; | ||
705 | } | ||
706 | |||
707 | if (slot->patch.AM) | ||
708 | slot->egout = ALIGN(slot->egout+slot->tll,EG_STEP,DB_STEP) + (*slot->plfo_am); | ||
709 | else | ||
710 | slot->egout = ALIGN(slot->egout+slot->tll,EG_STEP,DB_STEP); | ||
711 | if (slot->egout >= DB_MUTE) | ||
712 | slot->egout = DB_MUTE-1; | ||
713 | } | ||
714 | |||
715 | inline static int calc_slot_car(struct Slot* slot, int fm) | ||
716 | { | ||
717 | calc_envelope(slot); | ||
718 | calc_phase(slot); | ||
719 | if (slot->egout>=(DB_MUTE-1)) | ||
720 | return 0; | ||
721 | return dB2LinTab[sintable[(slot->pgout+wave2_8pi(fm))&(PG_WIDTH-1)] + slot->egout]; | ||
722 | } | ||
723 | |||
724 | inline static int calc_slot_mod(struct Slot* slot) | ||
725 | { | ||
726 | slot->output[1] = slot->output[0]; | ||
727 | calc_envelope(slot); | ||
728 | calc_phase(slot); | ||
729 | |||
730 | if (slot->egout>=(DB_MUTE-1)) { | ||
731 | slot->output[0] = 0; | ||
732 | } else if (slot->patch.FB!=0) { | ||
733 | int fm = wave2_4pi(slot->feedback) >> (7-slot->patch.FB); | ||
734 | slot->output[0] = dB2LinTab[sintable[(slot->pgout+fm)&(PG_WIDTH-1)] + slot->egout]; | ||
735 | } else | ||
736 | slot->output[0] = dB2LinTab[sintable[slot->pgout] + slot->egout]; | ||
737 | |||
738 | slot->feedback = (slot->output[1] + slot->output[0])>>1; | ||
739 | return slot->feedback; | ||
740 | } | ||
741 | |||
742 | // TOM | ||
743 | inline static int calc_slot_tom(struct Slot* slot) | ||
744 | { | ||
745 | calc_envelope(slot); | ||
746 | calc_phase(slot); | ||
747 | if (slot->egout>=(DB_MUTE-1)) | ||
748 | return 0; | ||
749 | return dB2LinTab[sintable[slot->pgout] + slot->egout]; | ||
750 | } | ||
751 | |||
752 | // SNARE | ||
753 | inline static int calc_slot_snare(struct Slot* slot, int whitenoise) | ||
754 | { | ||
755 | calc_envelope(slot); | ||
756 | calc_phase(slot); | ||
757 | if (slot->egout>=(DB_MUTE-1)) | ||
758 | return 0; | ||
759 | if (slot->pgout & (1<<(PG_BITS-1))) { | ||
760 | return (dB2LinTab[slot->egout] + dB2LinTab[slot->egout+whitenoise]) >> 1; | ||
761 | } else { | ||
762 | return (dB2LinTab[2*DB_MUTE + slot->egout] + dB2LinTab[slot->egout+whitenoise]) >> 1; | ||
763 | } | ||
764 | } | ||
765 | |||
766 | // TOP-CYM | ||
767 | inline static int calc_slot_cym(struct Slot* slot, int a, int b) | ||
768 | { | ||
769 | calc_envelope(slot); | ||
770 | if (slot->egout>=(DB_MUTE-1)) { | ||
771 | return 0; | ||
772 | } else { | ||
773 | return (dB2LinTab[slot->egout+a] + dB2LinTab[slot->egout+b]) >> 1; | ||
774 | } | ||
775 | } | ||
776 | |||
777 | // HI-HAT | ||
778 | inline static int calc_slot_hat(struct Slot* slot, int a, int b, int whitenoise) | ||
779 | { | ||
780 | calc_envelope(slot); | ||
781 | if (slot->egout>=(DB_MUTE-1)) { | ||
782 | return 0; | ||
783 | } else { | ||
784 | return (dB2LinTab[slot->egout+whitenoise] + dB2LinTab[slot->egout+a] + dB2LinTab[slot->egout+b]) >>2; | ||
785 | } | ||
786 | } | ||
787 | |||
788 | |||
789 | static inline int calcSample(struct Y8950* this, int channelMask) | ||
790 | { | ||
791 | // while muted update_ampm() and update_noise() aren't called, probably ok | ||
792 | update_ampm(this); | ||
793 | update_noise(this); | ||
794 | |||
795 | int mix = 0; | ||
796 | |||
797 | if (this->rythm_mode) { | ||
798 | // TODO wasn't in original source either | ||
799 | calc_phase(&this->ch[7].mod); | ||
800 | calc_phase(&this->ch[8].car); | ||
801 | |||
802 | if (channelMask & (1 << 6)) | ||
803 | mix += calc_slot_car(&this->ch[6].car, calc_slot_mod(&this->ch[6].mod)); | ||
804 | if (this->ch[7].mod.eg_mode != FINISH) | ||
805 | mix += calc_slot_hat(&this->ch[7].mod, this->noiseA, this->noiseB, this->whitenoise); | ||
806 | if (channelMask & (1 << 7)) | ||
807 | mix += calc_slot_snare(&this->ch[7].car, this->whitenoise); | ||
808 | if (this->ch[8].mod.eg_mode != FINISH) | ||
809 | mix += calc_slot_tom(&this->ch[8].mod); | ||
810 | if (channelMask & (1 << 8)) | ||
811 | mix += calc_slot_cym(&this->ch[8].car, this->noiseA, this->noiseB); | ||
812 | |||
813 | channelMask &= (1<< 6) - 1; | ||
814 | mix *= 2; | ||
815 | } | ||
816 | struct OPLChannel *cp; | ||
817 | for (cp = this->ch; channelMask; channelMask >>=1, cp++) { | ||
818 | if (channelMask & 1) { | ||
819 | if (cp->alg) | ||
820 | mix += calc_slot_car(&cp->car, 0) + | ||
821 | calc_slot_mod(&cp->mod); | ||
822 | else | ||
823 | mix += calc_slot_car(&cp->car, | ||
824 | calc_slot_mod(&cp->mod)); | ||
825 | } | ||
826 | } | ||
827 | |||
828 | mix += ADPCM_calcSample(&this->adpcm); | ||
829 | |||
830 | return (mix*this->maxVolume) >> (DB2LIN_AMP_BITS - 1); | ||
831 | } | ||
832 | |||
833 | static bool checkMuteHelper(struct Y8950* this) | ||
834 | { | ||
835 | int i; | ||
836 | struct OPLChannel *ch = this->ch; | ||
837 | for (i = 0; i < 6; i++) { | ||
838 | if (ch[i].car.eg_mode != FINISH) return false; | ||
839 | } | ||
840 | if (!this->rythm_mode) { | ||
841 | for(i = 6; i < 9; i++) { | ||
842 | if (ch[i].car.eg_mode != FINISH) return false; | ||
843 | } | ||
844 | } else { | ||
845 | if (ch[6].car.eg_mode != FINISH) return false; | ||
846 | if (ch[7].mod.eg_mode != FINISH) return false; | ||
847 | if (ch[7].car.eg_mode != FINISH) return false; | ||
848 | if (ch[8].mod.eg_mode != FINISH) return false; | ||
849 | if (ch[8].car.eg_mode != FINISH) return false; | ||
850 | } | ||
851 | |||
852 | return ADPCM_muted(&this->adpcm); | ||
853 | } | ||
854 | |||
855 | static void checkMute(struct Y8950* this) | ||
856 | { | ||
857 | bool mute = checkMuteHelper(this); | ||
858 | //PRT_DEBUG("Y8950: muted " << mute); | ||
859 | OPL_setInternalMute(this, mute); | ||
860 | } | ||
861 | |||
862 | int* OPL_updateBuffer(struct Y8950* this, int length) | ||
863 | { | ||
864 | //PRT_DEBUG("Y8950: update buffer"); | ||
865 | |||
866 | if (OPL_isInternalMuted(this) && !this->dacEnabled) { | ||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | this->dacCtrlVolume = this->dacSampleVolume - this->dacOldSampleVolume + 0x3fe7 * this->dacCtrlVolume / 0x4000; | ||
871 | this->dacOldSampleVolume = this->dacSampleVolume; | ||
872 | |||
873 | int channelMask = 0, i; | ||
874 | struct OPLChannel *ch = this->ch; | ||
875 | for (i = 9; i--; ) { | ||
876 | channelMask <<= 1; | ||
877 | if (ch[i].car.eg_mode != FINISH) channelMask |= 1; | ||
878 | } | ||
879 | |||
880 | int* buf = this->buffer; | ||
881 | while (length--) { | ||
882 | int sample = calcSample(this, channelMask); | ||
883 | |||
884 | this->dacCtrlVolume = 0x3fe7 * this->dacCtrlVolume / 0x4000; | ||
885 | this->dacDaVolume += 2 * (this->dacCtrlVolume - this->dacDaVolume) / 3; | ||
886 | sample += 48 * this->dacDaVolume; | ||
887 | *(buf++) = sample; | ||
888 | } | ||
889 | |||
890 | this->dacEnabled = this->dacDaVolume; | ||
891 | |||
892 | checkMute(this); | ||
893 | return this->buffer; | ||
894 | } | ||
895 | |||
896 | void OPL_setInternalVolume(struct Y8950* this, short newVolume) | ||
897 | { | ||
898 | this->maxVolume = newVolume; | ||
899 | } | ||
900 | |||
901 | //**************************************************// | ||
902 | // // | ||
903 | // I/O Ctrl // | ||
904 | // // | ||
905 | //**************************************************// | ||
906 | |||
907 | void OPL_writeReg(struct Y8950* this, byte rg, byte data) | ||
908 | { | ||
909 | //PRT_DEBUG("Y8950 write " << (int)rg << " " << (int)data); | ||
910 | int stbl[32] = { | ||
911 | 0, 2, 4, 1, 3, 5,-1,-1, | ||
912 | 6, 8,10, 7, 9,11,-1,-1, | ||
913 | 12,14,16,13,15,17,-1,-1, | ||
914 | -1,-1,-1,-1,-1,-1,-1,-1 | ||
915 | }; | ||
916 | |||
917 | //TODO only for registers that influence sound | ||
918 | //TODO also ADPCM | ||
919 | |||
920 | switch (rg & 0xe0) { | ||
921 | case 0x00: { | ||
922 | switch (rg) { | ||
923 | case 0x01: // TEST | ||
924 | // TODO | ||
925 | // Y8950 MSX-AUDIO Test register $01 (write only) | ||
926 | // | ||
927 | // Bit Description | ||
928 | // | ||
929 | // 7 Reset LFOs - seems to force the LFOs to their initial values (eg. | ||
930 | // maximum amplitude, zero phase deviation) | ||
931 | // | ||
932 | // 6 something to do with ADPCM - bit 0 of the status register is | ||
933 | // affected by setting this bit (PCM BSY) | ||
934 | // | ||
935 | // 5 No effect? - Waveform select enable in YM3812 OPL2 so seems | ||
936 | // reasonable that this bit wouldn't have been used in OPL | ||
937 | // | ||
938 | // 4 No effect? | ||
939 | // | ||
940 | // 3 Faster LFOs - increases the frequencies of the LFOs and (maybe) | ||
941 | // the timers (cf. YM2151 test register) | ||
942 | // | ||
943 | // 2 Reset phase generators - No phase generator output, but envelope | ||
944 | // generators still work (can hear a transient when they are gated) | ||
945 | // | ||
946 | // 1 No effect? | ||
947 | // | ||
948 | // 0 Reset envelopes - Envelope generator outputs forced to maximum, | ||
949 | // so all enabled voices sound at maximum | ||
950 | this->reg[rg] = data; | ||
951 | break; | ||
952 | |||
953 | case 0x02: // TIMER1 (reso. 80us) | ||
954 | this->reg[rg] = data; | ||
955 | break; | ||
956 | |||
957 | case 0x03: // TIMER2 (reso. 320us) | ||
958 | this->reg[rg] = data; | ||
959 | break; | ||
960 | |||
961 | case 0x04: // FLAG CONTROL | ||
962 | if (data & R04_IRQ_RESET) { | ||
963 | OPL_resetStatus(this, 0x78); // reset all flags | ||
964 | } else { | ||
965 | OPL_changeStatusMask(this, (~data) & 0x78); | ||
966 | this->reg[rg] = data; | ||
967 | } | ||
968 | break; | ||
969 | |||
970 | case 0x06: // (KEYBOARD OUT) | ||
971 | this->reg[rg] = data; | ||
972 | break; | ||
973 | |||
974 | case 0x07: // START/REC/MEM DATA/REPEAT/SP-OFF/-/-/RESET | ||
975 | case 0x08: // CSM/KEY BOARD SPLIT/-/-/SAMPLE/DA AD/64K/ROM | ||
976 | case 0x09: // START ADDRESS (L) | ||
977 | case 0x0A: // START ADDRESS (H) | ||
978 | case 0x0B: // STOP ADDRESS (L) | ||
979 | case 0x0C: // STOP ADDRESS (H) | ||
980 | case 0x0D: // PRESCALE (L) | ||
981 | case 0x0E: // PRESCALE (H) | ||
982 | case 0x0F: // ADPCM-DATA | ||
983 | case 0x10: // DELTA-N (L) | ||
984 | case 0x11: // DELTA-N (H) | ||
985 | case 0x12: // ENVELOP CONTROL | ||
986 | case 0x1A: // PCM-DATA | ||
987 | this->reg[rg] = data; | ||
988 | ADPCM_writeReg(&this->adpcm, rg, data); | ||
989 | break; | ||
990 | |||
991 | case 0x15: // DAC-DATA (bit9-2) | ||
992 | this->reg[rg] = data; | ||
993 | if (this->reg[0x08] & 0x04) { | ||
994 | static int damp[] = { 256, 279, 304, 332, 362, 395, 431, 470 }; | ||
995 | int sample = (short)(256 * this->reg[0x15] + this->reg[0x16]) * 128 / damp[this->reg[0x17]]; | ||
996 | this->dacSampleVolume = sample; | ||
997 | this->dacEnabled = 1; | ||
998 | } | ||
999 | break; | ||
1000 | case 0x16: // (bit1-0) | ||
1001 | this->reg[rg] = data & 0xC0; | ||
1002 | break; | ||
1003 | case 0x17: // (exponent) | ||
1004 | this->reg[rg] = data & 0x07; | ||
1005 | break; | ||
1006 | |||
1007 | case 0x18: // I/O-CONTROL (bit3-0) | ||
1008 | // TODO | ||
1009 | // 0 -> input | ||
1010 | // 1 -> output | ||
1011 | this->reg[rg] = data; | ||
1012 | break; | ||
1013 | |||
1014 | case 0x19: // I/O-DATA (bit3-0) | ||
1015 | // TODO | ||
1016 | this->reg[rg] = data; | ||
1017 | break; | ||
1018 | } | ||
1019 | |||
1020 | break; | ||
1021 | } | ||
1022 | case 0x20: { | ||
1023 | int s = stbl[rg&0x1f]; | ||
1024 | if (s >= 0) { | ||
1025 | this->slot[s]->patch.AM = (data>>7)&1; | ||
1026 | this->slot[s]->patch.PM = (data>>6)&1; | ||
1027 | this->slot[s]->patch.EG = (data>>5)&1; | ||
1028 | this->slot[s]->patch.KR = (data>>4)&1; | ||
1029 | this->slot[s]->patch.ML = (data)&15; | ||
1030 | slotUpdateAll(this->slot[s]); | ||
1031 | } | ||
1032 | this->reg[rg] = data; | ||
1033 | break; | ||
1034 | } | ||
1035 | case 0x40: { | ||
1036 | int s = stbl[rg&0x1f]; | ||
1037 | if (s >= 0) { | ||
1038 | this->slot[s]->patch.KL = (data>>6)&3; | ||
1039 | this->slot[s]->patch.TL = (data)&63; | ||
1040 | slotUpdateAll(this->slot[s]); | ||
1041 | } | ||
1042 | this->reg[rg] = data; | ||
1043 | break; | ||
1044 | } | ||
1045 | case 0x60: { | ||
1046 | int s = stbl[rg&0x1f]; | ||
1047 | if (s >= 0) { | ||
1048 | this->slot[s]->patch.AR = (data>>4)&15; | ||
1049 | this->slot[s]->patch.DR = (data)&15; | ||
1050 | slotUpdateEG(this->slot[s]); | ||
1051 | } | ||
1052 | this->reg[rg] = data; | ||
1053 | break; | ||
1054 | } | ||
1055 | case 0x80: { | ||
1056 | int s = stbl[rg&0x1f]; | ||
1057 | if (s >= 0) { | ||
1058 | this->slot[s]->patch.SL = (data>>4)&15; | ||
1059 | this->slot[s]->patch.RR = (data)&15; | ||
1060 | slotUpdateEG(this->slot[s]); | ||
1061 | } | ||
1062 | this->reg[rg] = data; | ||
1063 | break; | ||
1064 | } | ||
1065 | case 0xa0: { | ||
1066 | if (rg==0xbd) { | ||
1067 | this->am_mode = (data>>7)&1; | ||
1068 | this->pm_mode = (data>>6)&1; | ||
1069 | |||
1070 | setRythmMode(this, data); | ||
1071 | if (this->rythm_mode) { | ||
1072 | if (data&0x10) keyOn_BD(this); else keyOff_BD(this); | ||
1073 | if (data&0x08) keyOn_SD(this); else keyOff_SD(this); | ||
1074 | if (data&0x04) keyOn_TOM(this); else keyOff_TOM(this); | ||
1075 | if (data&0x02) keyOn_CYM(this); else keyOff_CYM(this); | ||
1076 | if (data&0x01) keyOn_HH(this); else keyOff_HH(this); | ||
1077 | } | ||
1078 | slotUpdateAll(&this->ch[6].mod); | ||
1079 | slotUpdateAll(&this->ch[6].car); | ||
1080 | slotUpdateAll(&this->ch[7].mod); | ||
1081 | slotUpdateAll(&this->ch[7].car); | ||
1082 | slotUpdateAll(&this->ch[8].mod); | ||
1083 | slotUpdateAll(&this->ch[8].car); | ||
1084 | |||
1085 | this->reg[rg] = data; | ||
1086 | break; | ||
1087 | } | ||
1088 | if ((rg&0xf) > 8) { | ||
1089 | // 0xa9-0xaf 0xb9-0xbf | ||
1090 | break; | ||
1091 | } | ||
1092 | if (!(rg&0x10)) { | ||
1093 | // 0xa0-0xa8 | ||
1094 | int c = rg-0xa0; | ||
1095 | int fNum = data + ((this->reg[rg+0x10]&3)<<8); | ||
1096 | int block = (this->reg[rg+0x10]>>2)&7; | ||
1097 | channelSetFnumber(&this->ch[c], fNum); | ||
1098 | switch (c) { | ||
1099 | case 7: this->noiseA_dphase = dphaseNoiseTable[fNum][block]; | ||
1100 | break; | ||
1101 | case 8: this->noiseB_dphase = dphaseNoiseTable[fNum][block]; | ||
1102 | break; | ||
1103 | } | ||
1104 | slotUpdateAll(&this->ch[c].car); | ||
1105 | slotUpdateAll(&this->ch[c].mod); | ||
1106 | this->reg[rg] = data; | ||
1107 | } else { | ||
1108 | // 0xb0-0xb8 | ||
1109 | int c = rg-0xb0; | ||
1110 | int fNum = ((data&3)<<8) + this->reg[rg-0x10]; | ||
1111 | int block = (data>>2)&7; | ||
1112 | channelSetFnumber(&this->ch[c], fNum); | ||
1113 | channelSetBlock(&this->ch[c], block); | ||
1114 | switch (c) { | ||
1115 | case 7: this->noiseA_dphase = dphaseNoiseTable[fNum][block]; | ||
1116 | break; | ||
1117 | case 8: this->noiseB_dphase = dphaseNoiseTable[fNum][block]; | ||
1118 | break; | ||
1119 | } | ||
1120 | if (data&0x20) | ||
1121 | keyOn(&this->ch[c]); | ||
1122 | else | ||
1123 | keyOff(&this->ch[c]); | ||
1124 | slotUpdateAll(&this->ch[c].mod); | ||
1125 | slotUpdateAll(&this->ch[c].car); | ||
1126 | this->reg[rg] = data; | ||
1127 | } | ||
1128 | break; | ||
1129 | } | ||
1130 | case 0xc0: { | ||
1131 | if (rg > 0xc8) | ||
1132 | break; | ||
1133 | int c = rg-0xC0; | ||
1134 | this->slot[c*2]->patch.FB = (data>>1)&7; | ||
1135 | this->ch[c].alg = data&1; | ||
1136 | this->reg[rg] = data; | ||
1137 | } | ||
1138 | } | ||
1139 | |||
1140 | //TODO only for registers that influence sound | ||
1141 | checkMute(this); | ||
1142 | } | ||
1143 | |||
1144 | byte OPL_readReg(struct Y8950* this, byte rg) | ||
1145 | { | ||
1146 | byte result; | ||
1147 | switch (rg) { | ||
1148 | case 0x05: // (KEYBOARD IN) | ||
1149 | result = 0xff; | ||
1150 | break; | ||
1151 | |||
1152 | case 0x0f: // ADPCM-DATA | ||
1153 | case 0x13: // ??? | ||
1154 | case 0x14: // ??? | ||
1155 | case 0x1a: // PCM-DATA | ||
1156 | result = ADPCM_readReg(&this->adpcm, rg); | ||
1157 | break; | ||
1158 | |||
1159 | case 0x19: // I/O DATA TODO | ||
1160 | /* result = ~(switchGetAudio() ? 0 : 0x04); */ | ||
1161 | result = 0; | ||
1162 | break; | ||
1163 | default: | ||
1164 | result = 255; | ||
1165 | } | ||
1166 | //PRT_DEBUG("Y8950 read " << (int)rg<<" "<<(int)result); | ||
1167 | return result; | ||
1168 | } | ||
1169 | |||
1170 | byte OPL_readStatus(struct Y8950* this) | ||
1171 | { | ||
1172 | OPL_setStatus(this, STATUS_BUF_RDY); // temp hack | ||
1173 | byte tmp = this->status & (0x80 | this->statusMask); | ||
1174 | //PRT_DEBUG("Y8950 read status " << (int)tmp); | ||
1175 | return tmp | 0x06; // bit 1 and 2 are always 1 | ||
1176 | } | ||
1177 | |||
1178 | |||
1179 | void OPL_setStatus(struct Y8950* this, byte flags) | ||
1180 | { | ||
1181 | this->status |= flags; | ||
1182 | if (this->status & this->statusMask) { | ||
1183 | this->status |= 0x80; | ||
1184 | /* irq.set(); */ | ||
1185 | } | ||
1186 | } | ||
1187 | void OPL_resetStatus(struct Y8950* this, byte flags) | ||
1188 | { | ||
1189 | this->status &= ~flags; | ||
1190 | if (!(this->status & this->statusMask)) { | ||
1191 | this->status &= 0x7f; | ||
1192 | /* irq.reset(); */ | ||
1193 | } | ||
1194 | } | ||
1195 | void OPL_changeStatusMask(struct Y8950* this, byte newMask) | ||
1196 | { | ||
1197 | this->statusMask = newMask; | ||
1198 | this->status &= this->statusMask; | ||
1199 | if (this->status) { | ||
1200 | this->status |= 0x80; | ||
1201 | /* irq.set(); */ | ||
1202 | } else { | ||
1203 | this->status &= 0x7f; | ||
1204 | /* irq.reset(); */ | ||
1205 | } | ||
1206 | } | ||