1 // vgmSndDrv.c - VGM Sound Driver for OPL2
\r
2 // Valley Bell, 2015-07-27
\r
4 // Note: This uses quite a few optimizations that assume that your
\r
5 // machine is Little Endian.
\r
11 #include "src/lib/vgmsnd/common.h"
\r
12 #include "src/lib/vgmsnd/vgmSnd.h"
\r
19 #define FCC_VGM 0x206D6756 // 'Vgm '
\r
20 #define FCC_GD3 0x20336447 // 'Gd3 '
\r
22 typedef struct _vgm_file_header_base
\r
25 dword/*32*/ lngEOFOffset; // 04
\r
26 dword/*32*/ lngVersion; // 08
\r
27 dword/*32*/ lngSkip1[2]; // 0C
\r
28 dword/*32*/ lngGD3Offset; // 14
\r
29 dword/*32*/ lngTotalSamples; // 18
\r
30 dword/*32*/ lngLoopOffset; // 1C
\r
31 dword/*32*/ lngLoopSamples; // 20
\r
32 dword/*32*/ lngRate; // 24
\r
33 dword/*32*/ lngSkip2[3]; // 28
\r
34 dword/*32*/ lngDataOffset; // 34
\r
35 dword/*32*/ lngSkip3[2]; // 38
\r
38 #define PBMODE_MUSIC 0x00
\r
39 #define PBMODE_SFX 0x01
\r
40 typedef struct _vgm_playback
\r
43 UINT8 vgmEnd; // 00 - running, 01 - finished, FF - not loaded
\r
46 dword/*32*/ vgmSmplPos;
\r
47 dword/*32*/ pbSmplPos;
\r
51 // Music: mask of channels used/overridden by SFX
\r
52 // SFX: ID of channel used by SFX (all commands are forces to it)
\r
55 UINT8 workRAM[0x04];
\r
60 INLINE UINT16 ReadLE16(const UINT8* buffer)
\r
63 return *(UINT16*)buffer;
\r
65 return (buffer[0x00] << 0) | (buffer[0x01] << 8);
\r
69 INLINE dword/*32*/ ReadLE32(const UINT8* buffer)
\r
72 return *(dword/*32*/*)buffer;
\r
74 return (buffer[0x00] << 0) | (buffer[0x01] << 8) |
\r
75 (buffer[0x02] << 16) | (buffer[0x03] << 24);
\r
80 // Function Prototypes
\r
81 //UINT8 OpenVGMFile(const char* FileName, VGM_FILE* vgmFile);
\r
82 //void FreeVGMFile(VGM_FILE* vgmFile);
\r
84 static boolean DoVgmLoop(VGM_PBK* vgmPlay);
\r
85 static void UpdateVGM(VGM_PBK* vgmPlay, UINT16 Samples);
\r
87 //void InitEngine(void);
\r
88 //void DeinitEngine(void);
\r
90 //UINT8 PlayMusic(VGM_FILE* vgmFile);
\r
91 //UINT8 PlaySFX(VGM_FILE* vgmFile, UINT8 sfxChnID);
\r
92 //UINT8 StopMusic(void);
\r
93 //UINT8 StopSFX(UINT8 sfxChnID); // Note: sfxChnID == 0xFF -> stop all SFX
\r
94 //UINT8 PauseMusic(void);
\r
95 //UINT8 ResumeMusic(void);
\r
96 static void StartPlayback(VGM_PBK* vgmPb);
\r
97 static void StopPlayback(VGM_PBK* vgmPb);
\r
99 static void ym2413_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data);
\r
100 static void ym3812_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data);
\r
101 static void ym3512_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data);
\r
102 static void ymf262_write(VGM_PBK* vgmPb, UINT8 port, UINT8 reg, UINT8 data);
\r
104 //void UpdateSoundEngine(void);
\r
107 // Functions that must be supplied by external library
\r
108 extern void OPL2_Write(UINT8 reg, UINT8 data);
\r
109 extern UINT8 OPL2_ReadStatus(void);
\r
114 #define SFX_CHN_COUNT 6
\r
116 #define TIMER1_RATE 7 // 256-7 = 248; (3579545/72/4) / 248 = 50.12 Hz ~ 50 Hz
\r
117 #define VGM_UPD_RATE 880 // 880 samples (44.1 KHz) = 50.11 Hz
\r
119 static VGM_PBK vgmPbMusic;
\r
120 static VGM_PBK vgmPbSFX[SFX_CHN_COUNT];
\r
122 static UINT8 oplRegs_Music[0x100];
\r
123 static UINT8 oplRegs_SFX[SFX_CHN_COUNT][0x0D]; // 20 23 40 43 60 63 80 83 E0 E3 C0 A0 B0
\r
125 static const UINT8 SFX_REGS[0x0D] =
\r
126 { 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83,
\r
127 0xE0, 0xE3, 0xC0, 0xA0, 0xB0};
\r
128 static const UINT8 SFX_REGS_REV[0x10] = // 20/30 -> 0, 40/50 -> 2, ...
\r
129 { 0xFF, 0xFF, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04,
\r
130 0x06, 0x06, 0x0B, 0x0C, 0x0A, 0xFF, 0x08, 0x08};
\r
131 static const UINT8 CHN_OPMASK[0x09] =
\r
132 { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12};
\r
133 static const UINT8 CHN_OPMASK_REV[0x20] =
\r
134 { 0x00, 0x01, 0x02, 0x80, 0x81, 0x82, 0xFF, 0xFF,
\r
135 0x03, 0x04, 0x05, 0x83, 0x84, 0x85, 0xFF, 0xFF,
\r
136 0x06, 0x07, 0x08, 0x86, 0x87, 0x88, 0xFF, 0xFF,
\r
137 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
\r
140 UINT8 OpenVGMFile(const char* FileName, VGM_FILE* vgmFile, global_game_variables_t *gvar)
\r
143 size_t readEl; // 'elements' read from file
\r
144 size_t bytesToRead;
\r
145 VGM_BASE_HDR vgmBaseHdr;
\r
147 dword/*32*/ CurPos;
\r
149 memset(vgmFile, 0x00, sizeof(VGM_FILE));
\r
151 hFile = fopen(FileName, "rb");
\r
155 hdrSize = sizeof(VGM_BASE_HDR);
\r
156 readEl = fread(&vgmBaseHdr, hdrSize, 0x01, hFile);
\r
160 return 0xFE; // read error
\r
162 if (vgmBaseHdr.fccVGM != FCC_VGM)
\r
165 return 0x80; // bad signature
\r
167 if (vgmBaseHdr.lngVersion < 0x0150)
\r
170 return 0x81; // We don't support VGM v1.10 and earlier
\r
173 vgmFile->dataLen = vgmBaseHdr.lngEOFOffset + 0x04;
\r
174 #ifndef VGM_USESCAMMPM
\r
175 vgmFile->data = (UINT8*)malloc(vgmFile->dataLen);
\r
177 MM_GetPtr(MEMPTRCONV gvar->ca.audiosegs[0], vgmFile->dataLen, gvar);
\r
178 vgmFile->data = (UINT8*)gvar->ca.audiosegs[0];
\r
180 if (vgmFile->data == NULL)
\r
183 return 0xF0; // malloc error
\r
185 memcpy(vgmFile->data, &vgmBaseHdr, hdrSize);
\r
186 bytesToRead = vgmFile->dataLen - hdrSize;
\r
187 readEl = fread(vgmFile->data + hdrSize, 0x01, bytesToRead, hFile);
\r
188 if (readEl < bytesToRead)
\r
191 //return 0xFE; // read error
\r
192 vgmFile->dataLen = hdrSize + readEl;
\r
197 memcpy(&vgmFile->header, vgmFile->data, sizeof(VGM_HEADER));
\r
199 // relative -> absolute addresses
\r
200 vgmFile->header.lngEOFOffset += 0x04;
\r
201 if (vgmFile->header.lngGD3Offset)
\r
202 vgmFile->header.lngGD3Offset += 0x14;
\r
203 if (vgmFile->header.lngLoopOffset)
\r
204 vgmFile->header.lngLoopOffset += 0x1C;
\r
205 if (! vgmFile->header.lngDataOffset)
\r
206 vgmFile->header.lngDataOffset = 0x0C;
\r
207 vgmFile->header.lngDataOffset += 0x34;
\r
209 CurPos = vgmFile->header.lngDataOffset;
\r
210 if (vgmFile->header.lngVersion < 0x0150)
\r
212 hdrSize = sizeof(VGM_HEADER);
\r
213 if (hdrSize > CurPos)
\r
214 memset((UINT8*)&vgmFile->header + CurPos, 0x00, hdrSize - CurPos);
\r
220 void FreeVGMFile(VGM_FILE* vgmFile, global_game_variables_t *gvar)
\r
222 #ifndef VGM_USESCAMMPM
\r
223 if(vgmFile->data){ free(vgmFile->data); vgmFile->data = NULL; }
\r
225 MM_FreePtr(MEMPTRCONV gvar->ca.audiosegs[0], gvar);
\r
227 // if(vgmFile->data) free(vgmFile->data);
\r
228 vgmFile->dataLen = 0;
\r
234 static boolean DoVgmLoop(VGM_PBK* vgmPlay)
\r
236 const VGM_HEADER* vgmHdr = &vgmPlay->file->header;
\r
238 if (! vgmHdr->lngLoopOffset)
\r
241 vgmPlay->curLoopCnt ++;
\r
243 vgmPlay->vgmPos = vgmHdr->lngLoopOffset;
\r
244 vgmPlay->vgmSmplPos -= vgmHdr->lngLoopSamples;
\r
245 vgmPlay->pbSmplPos -= vgmHdr->lngLoopSamples;
\r
250 static void UpdateVGM(VGM_PBK* vgmPlay, UINT16 Samples)
\r
252 const dword/*32*/ vgmLen = vgmPlay->file->dataLen;
\r
253 const UINT8* vgmData = vgmPlay->file->data;
\r
254 const UINT8* VGMPnt;
\r
255 dword/*32*/ VGMPos;
\r
256 dword/*32*/ VGMSmplPos;
\r
259 dword/*32*/ blockLen;
\r
261 vgmPlay->pbSmplPos += Samples;
\r
262 VGMPos = vgmPlay->vgmPos;
\r
263 VGMSmplPos = vgmPlay->vgmSmplPos;
\r
264 while(VGMSmplPos < vgmPlay->pbSmplPos && ! vgmPlay->vgmEnd)
\r
266 VGMPnt = &vgmData[VGMPos];
\r
267 Command = VGMPnt[0x00];
\r
268 switch(Command & 0xF0)
\r
270 case 0x70: // small delay (1-16 samples)
\r
271 VGMSmplPos += (Command & 0x0F) + 0x01;
\r
274 case 0x80: // DAC write + small delay (0-15 samples)
\r
275 VGMSmplPos += (Command & 0x0F);
\r
281 case 0x66: // End Of File
\r
282 vgmPlay->vgmPos = VGMPos;
\r
283 vgmPlay->vgmSmplPos = VGMSmplPos;
\r
284 if (! DoVgmLoop(vgmPlay))
\r
285 vgmPlay->vgmEnd = 0x01;
\r
286 VGMPos = vgmPlay->vgmPos;
\r
287 VGMSmplPos = vgmPlay->vgmSmplPos;
\r
289 case 0x62: // 1/60s delay
\r
293 case 0x63: // 1/50s delay
\r
297 case 0x61: // xx Sample Delay
\r
298 VGMSmplPos += ReadLE16(&VGMPnt[0x01]);
\r
301 case 0x67: // Data Block (PCM Data Stream)
\r
302 blockType = VGMPnt[0x02];
\r
303 blockLen = ReadLE32(&VGMPnt[0x03]);
\r
304 blockLen &= 0x7FFFFFFF;
\r
305 VGMPos += 0x07 + blockLen;
\r
307 case 0x68: // PCM RAM write
\r
311 vgmPlay->vgmEnd = 0x01;
\r
316 if (Command == 0x50)
\r
318 VGMPos += 0x02; // SN76496 write
\r
323 case 0x51: // YM2413 write
\r
324 ym2413_write(vgmPlay, VGMPnt[0x01], VGMPnt[0x02]);
\r
326 case 0x5A: // YM3812 write
\r
327 ym3812_write(vgmPlay, VGMPnt[0x01], VGMPnt[0x02]);
\r
329 case 0x5B: // YM3526 write
\r
330 case 0x5C: // Y8950 write
\r
331 ym3512_write(vgmPlay, VGMPnt[0x01], VGMPnt[0x02]);
\r
333 case 0x5E: // YMF262 write, port 0
\r
334 case 0x5F: // YMF262 write, port 1
\r
335 ymf262_write(vgmPlay, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]);
\r
359 case 0x90: // DAC Ctrl: Setup Chip
\r
362 case 0x91: // DAC Ctrl: Set Data
\r
365 case 0x92: // DAC Ctrl: Set Freq
\r
368 case 0x93: // DAC Ctrl: Play from Start Pos
\r
371 case 0x94: // DAC Ctrl: Stop immediately
\r
374 case 0x95: // DAC Ctrl: Play Block (small)
\r
378 vgmPlay->vgmEnd = 0x01;
\r
383 vgmPlay->vgmEnd = 0x01;
\r
387 if (VGMPos >= vgmLen)
\r
388 vgmPlay->vgmEnd = 0x01;
\r
390 vgmPlay->vgmPos = VGMPos;
\r
391 vgmPlay->vgmSmplPos = VGMSmplPos;
\r
392 if (vgmPlay->vgmEnd)
\r
393 StopPlayback(vgmPlay);
\r
401 void InitEngine(void)
\r
406 memset(oplRegs_Music, 0x00, 0x100);
\r
407 memset(&vgmPbMusic, 0x00, sizeof(VGM_PBK));
\r
408 vgmPbMusic.pbMode = PBMODE_MUSIC;
\r
409 vgmPbMusic.vgmEnd = 0xFF;
\r
410 vgmPbMusic.oplChnMask = 0x0000;
\r
411 vgmPbMusic.oplRegCache = oplRegs_Music;
\r
413 for (curSFX = 0; curSFX < SFX_CHN_COUNT; curSFX ++)
\r
415 memset(&oplRegs_SFX[curSFX], 0x00, sizeof(VGM_PBK));
\r
416 memset(&vgmPbSFX[curSFX], 0x00, sizeof(VGM_PBK));
\r
417 vgmPbSFX[curSFX].pbMode = PBMODE_SFX;
\r
418 vgmPbSFX[curSFX].vgmEnd = 0xFF;
\r
419 vgmPbSFX[curSFX].oplChnMask = curSFX;
\r
420 vgmPbSFX[curSFX].oplRegCache = oplRegs_SFX[curSFX];
\r
428 OPL2_Write(curReg, 0x00);
\r
429 } while(curReg > 0x20);
\r
431 OPL2_Write(0x02, TIMER1_RATE); // set Timer 1 Period
\r
432 OPL2_Write(0x04, 0x01); // Timer 1 on/unmasked, Timer 2 off
\r
433 OPL2_Write(0x04, 0x80); // Reset Timer/IRQ Status Flags
\r
435 OPL2_Write(0x01, 0x20); // Waveform Select: Enable
\r
440 void DeinitEngine(void)
\r
444 StopPlayback(&vgmPbMusic);
\r
445 for (curSFX = 0; curSFX < SFX_CHN_COUNT; curSFX ++)
\r
446 StopPlayback(&vgmPbSFX[curSFX]);
\r
448 OPL2_Write(0x04, 0x00); // disable all timers
\r
454 UINT8 PlayMusic(VGM_FILE* vgmFile)
\r
456 VGM_PBK* vgmPb = &vgmPbMusic;
\r
458 if (! vgmPb->vgmEnd)
\r
459 StopPlayback(vgmPb);
\r
461 vgmPb->file = vgmFile;
\r
463 StartPlayback(vgmPb);
\r
468 UINT8 PlaySFX(VGM_FILE* vgmFile, UINT8 sfxChnID)
\r
472 if (sfxChnID >= SFX_CHN_COUNT)
\r
475 vgmPb = &vgmPbSFX[sfxChnID];
\r
477 if (! vgmPb->vgmEnd)
\r
478 StopPlayback(vgmPb);
\r
480 vgmPb->file = vgmFile;
\r
482 StartPlayback(vgmPb);
\r
487 UINT8 StopMusic(void)
\r
489 StopPlayback(&vgmPbMusic);
\r
493 UINT8 StopSFX(UINT8 sfxChnID)
\r
495 if (sfxChnID == 0xFF)
\r
497 for (sfxChnID = 0; sfxChnID < SFX_CHN_COUNT; sfxChnID ++)
\r
498 StopPlayback(&vgmPbSFX[sfxChnID]);
\r
502 if (sfxChnID >= SFX_CHN_COUNT)
\r
505 StopPlayback(&vgmPbSFX[sfxChnID]);
\r
509 UINT8 PauseMusic(void)
\r
511 if (vgmPbMusic.vgmEnd == 0xFF)
\r
512 return 0xFF; // not playing
\r
513 if (vgmPbMusic.vgmEnd == 0x01)
\r
514 return 0x80; // finished playing already
\r
515 if (vgmPbMusic.vgmEnd == 0x02)
\r
516 return 0x01; // is already paused
\r
518 StopPlayback(&vgmPbMusic);
\r
519 vgmPbMusic.vgmEnd = 0x02;
\r
524 UINT8 ResumeMusic(void)
\r
526 if (vgmPbMusic.vgmEnd == 0xFF)
\r
527 return 0xFF; // not playing
\r
528 if (vgmPbMusic.vgmEnd == 0x01)
\r
529 return 0x80; // finished playing already
\r
530 if (! (vgmPbMusic.vgmEnd & 0x02))
\r
531 return 0x01; // is not paused
\r
533 vgmPbMusic.vgmEnd &= ~0x02;
\r
538 static void StartPlayback(VGM_PBK* vgmPb)
\r
540 if (vgmPb->file == NULL || vgmPb->file->data == NULL ||
\r
541 vgmPb->file->header.fccVGM != FCC_VGM
\r
544 vgmPb->vgmEnd = 0xFF;
\r
548 vgmPb->vgmEnd = 0x00; // set to 'running'
\r
549 vgmPb->vgmPos = vgmPb->file->header.lngDataOffset;
\r
550 vgmPb->vgmSmplPos = 0;
\r
551 vgmPb->pbSmplPos = 0;
\r
552 vgmPb->curLoopCnt = 0;
\r
553 memset(vgmPb->workRAM, 0x00, 0x04);
\r
555 if (vgmPb->pbMode == PBMODE_SFX)
\r
559 curReg = 0xB0 | vgmPb->oplChnMask;
\r
560 if (oplRegs_Music[curReg] & 0x20)
\r
561 OPL2_Write(curReg, oplRegs_Music[curReg] & ~0x20); // send Key Off
\r
563 vgmPbMusic.oplChnMask |= (1 << vgmPb->oplChnMask); // mask out music channel
\r
569 static void StopPlayback(VGM_PBK* vgmPb)
\r
571 if (vgmPb->vgmEnd & 0x80)
\r
574 if (vgmPb->pbMode == PBMODE_MUSIC)
\r
580 for (curReg = 0xB0; curReg < 0xB9; curReg ++, chnMask <<= 1)
\r
582 if (vgmPb->oplChnMask & chnMask)
\r
583 continue; // keep channels used by SFX untouched
\r
584 if (vgmPb->oplRegCache[curReg] & 0x20)
\r
586 vgmPb->oplRegCache[curReg] &= ~0x20;
\r
587 OPL2_Write(curReg, vgmPb->oplRegCache[curReg]); // send Key Off
\r
590 curReg = 0xBD; // rhythm register
\r
591 if (vgmPb->oplRegCache[curReg] & 0x1F)
\r
593 vgmPb->oplRegCache[curReg] &= ~0x1F;
\r
594 OPL2_Write(curReg, vgmPb->oplRegCache[curReg]); // send Key Off
\r
597 vgmPb->vgmEnd = 0x01;
\r
599 else //if (vgmPb->pbMode == PBMODE_SFX)
\r
605 curReg = 0xB0 | vgmPb->oplChnMask;
\r
606 if (vgmPb->oplRegCache[0x0C] & 0x20)
\r
608 vgmPb->oplRegCache[0x0C] &= ~0x20;
\r
609 OPL2_Write(curReg, vgmPb->oplRegCache[0x0C]); // send Key Off
\r
612 vgmPb->vgmEnd = 0x01;
\r
614 if (! vgmPbMusic.vgmEnd) // if (music is playing)
\r
616 opMask = CHN_OPMASK[vgmPb->oplChnMask];
\r
617 for (regID = 0x00; regID < 0x0A; regID ++)
\r
619 curReg = SFX_REGS[regID] + opMask;
\r
620 OPL2_Write(curReg, oplRegs_Music[curReg]); // restore Music register
\r
622 for (; regID < 0x0D; regID ++)
\r
624 curReg = SFX_REGS[regID] | vgmPb->oplChnMask;
\r
625 OPL2_Write(curReg, oplRegs_Music[curReg]); // restore Music register
\r
628 vgmPbMusic.oplChnMask &= ~(1 << vgmPb->oplChnMask);
\r
637 static void OPL_CachedWrite(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)
\r
642 if (vgmPb->pbMode == PBMODE_MUSIC)
\r
645 data |= 0x20; // enforce "Waveform Select Enable" bit
\r
646 vgmPb->oplRegCache[reg] = data;
\r
648 ramOfs = SFX_REGS_REV[reg >> 4];
\r
649 if (ramOfs < 0x0A) // Operator 20/40/60/80/E0
\r
651 regChn = CHN_OPMASK_REV[reg & 0x1F] & 0x7F;
\r
652 if (vgmPb->oplChnMask & (1 << regChn))
\r
653 return; // channel overridden by SFX - return
\r
655 else if (ramOfs < 0x0D) // Operator C0/A0/B0
\r
657 regChn = reg & 0x0F;
\r
658 if (vgmPb->oplChnMask & (1 << regChn))
\r
659 return; // channel overridden by SFX - return
\r
662 else //if (vgmPb->pbMode == PBMODE_SFX)
\r
665 return; // no rhythm register for SFX
\r
667 ramOfs = SFX_REGS_REV[reg >> 4];
\r
668 if (ramOfs == 0xFF)
\r
671 if (ramOfs < 0x0A) // Operator 20/40/60/80/E0
\r
673 regChn = CHN_OPMASK_REV[reg & 0x1F];
\r
674 if (regChn == 0xFF)
\r
675 return; // ignore writes to invalid channels/operators
\r
676 ramOfs += (regChn & 0x80) >> 7;
\r
678 vgmPb->oplRegCache[ramOfs] = data;
\r
680 if (regChn != vgmPb->oplChnMask)
\r
682 // force command to current channel
\r
683 reg = SFX_REGS[ramOfs] + CHN_OPMASK[vgmPb->oplChnMask];
\r
686 else // Operator C0/A0/B0
\r
688 regChn = CHN_OPMASK_REV[reg & 0x0F];
\r
689 if (regChn >= 0x09)
\r
690 return; // ignore writes to invalid channels
\r
691 vgmPb->oplRegCache[ramOfs] = data;
\r
693 reg = (reg & 0xF0) | vgmPb->oplChnMask;
\r
697 OPL2_Write(reg, data);
\r
702 static void ym2413_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)
\r
704 return; // unsupported for now
\r
707 static void ym3812_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)
\r
711 vgmPb->workRAM[0x00] = data & 0x20; // store "Wave Select Enable" bit
\r
713 else if ((reg & 0xE0) == 0xE0)
\r
715 if (! vgmPb->workRAM[0x00]) // "Wave Select Enable" off?
\r
716 data = 0x00; // disable waveforms
\r
719 OPL_CachedWrite(vgmPb, reg, data);
\r
723 static void ym3512_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)
\r
725 if ((reg & 0xE0) == 0xE0)
\r
727 data = 0x00; // OPL1 has no waveforms
\r
729 if (reg >= 0x07 && reg < 0x20)
\r
732 data &= ~0x0F; // mask out Y8950 DeltaT data
\r
734 return; // ignore Y8950 DeltaT writes
\r
737 OPL_CachedWrite(vgmPb, reg, data);
\r
741 static void ymf262_write(VGM_PBK* vgmPb, UINT8 port, UINT8 reg, UINT8 data)
\r
743 return; // unsupported for now
\r
748 void UpdateSoundEngine(void)
\r
753 tmrMask = OPL2_ReadStatus();
\r
754 if (! (tmrMask & 0x40))
\r
755 return; // wait for overflow
\r
756 OPL2_Write(0x04, 0x80); // Reset Timer/IRQ Status Flags
\r
758 if (! vgmPbMusic.vgmEnd)
\r
759 UpdateVGM(&vgmPbMusic, VGM_UPD_RATE);
\r
760 for (curSFX = 0; curSFX < SFX_CHN_COUNT; curSFX ++)
\r
762 if (! vgmPbSFX[curSFX].vgmEnd)
\r
763 UpdateVGM(&vgmPbSFX[curSFX], VGM_UPD_RATE);
\r