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