--- /dev/null
+/*\r
+**\r
+** File: fmopl.c - software implementation of FM sound generator\r
+** types OPL and OPL2\r
+**\r
+** Copyright Jarek Burczynski (bujar at mame dot net)\r
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development\r
+**\r
+** Version 0.72\r
+**\r
+\r
+Revision History:\r
+\r
+04-08-2003 Jarek Burczynski:\r
+ - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip\r
+ handles memory read/write or during the adpcm synthesis when the chip\r
+ requests another byte of ADPCM data.\r
+\r
+24-07-2003 Jarek Burczynski:\r
+ - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after\r
+ some (unknown) delay). Right now it's always set.\r
+\r
+14-06-2003 Jarek Burczynski:\r
+ - implemented all of the status register flags in Y8950 emulation\r
+ - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since\r
+ they can be either RAM or ROM\r
+\r
+08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip)\r
+ - corrected ym3526_read() to always set bit 2 and bit 1\r
+ to HIGH state - identical to ym3812_read (verified on real YM3526)\r
+\r
+04-28-2002 Jarek Burczynski:\r
+ - binary exact Envelope Generator (verified on real YM3812);\r
+ compared to YM2151: the EG clock is equal to internal_clock,\r
+ rates are 2 times slower and volume resolution is one bit less\r
+ - modified interface functions (they no longer return pointer -\r
+ that's internal to the emulator now):\r
+ - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init()\r
+ - corrected 'off by one' error in feedback calculations (when feedback is off)\r
+ - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22)\r
+ - speeded up noise generator calculations (Nicola Salmoria)\r
+\r
+03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip)\r
+ Complete rewrite (all verified on real YM3812):\r
+ - corrected sin_tab and tl_tab data\r
+ - corrected operator output calculations\r
+ - corrected waveform_select_enable register;\r
+ simply: ignore all writes to waveform_select register when\r
+ waveform_select_enable == 0 and do not change the waveform previously selected.\r
+ - corrected KSR handling\r
+ - corrected Envelope Generator: attack shape, Sustain mode and\r
+ Percussive/Non-percussive modes handling\r
+ - Envelope Generator rates are two times slower now\r
+ - LFO amplitude (tremolo) and phase modulation (vibrato)\r
+ - rhythm sounds phase generation\r
+ - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm)\r
+ - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM)\r
+ - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1)\r
+\r
+12-28-2001 Acho A. Tang\r
+ - reflected Delta-T EOS status on Y8950 status port.\r
+ - fixed subscription range of attack/decay tables\r
+\r
+\r
+ To do:\r
+ add delay before key off in CSM mode (see CSMKeyControll)\r
+ verify volume of the FM part on the Y8950\r
+*/\r
+\r
+#include <math.h>\r
+#include "mamedef.h"\r
+#ifdef _DEBUG\r
+#include <stdio.h>\r
+#endif\r
+#include <malloc.h>\r
+#include <memory.h>\r
+//#include "sndintrf.h"\r
+#include "fmopl.h"\r
+#if BUILD_Y8950\r
+#include "ymdeltat.h"\r
+#endif\r
+\r
+\r
+#define NULL ((void *)0)\r
+\r
+\r
+/* output final shift */\r
+#if (OPL_SAMPLE_BITS==16)\r
+ #define FINAL_SH (0)\r
+ #define MAXOUT (+32767)\r
+ #define MINOUT (-32768)\r
+#else\r
+ #define FINAL_SH (8)\r
+ #define MAXOUT (+127)\r
+ #define MINOUT (-128)\r
+#endif\r
+\r
+\r
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */\r
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */\r
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */\r
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */\r
+\r
+#define FREQ_MASK ((1<<FREQ_SH)-1)\r
+\r
+/* envelope output entries */\r
+#define ENV_BITS 10\r
+#define ENV_LEN (1<<ENV_BITS)\r
+#define ENV_STEP (128.0/ENV_LEN)\r
+\r
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-1))-1) /*511*/\r
+#define MIN_ATT_INDEX (0)\r
+\r
+/* sinwave entries */\r
+#define SIN_BITS 10\r
+#define SIN_LEN (1<<SIN_BITS)\r
+#define SIN_MASK (SIN_LEN-1)\r
+\r
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */\r
+\r
+\r
+\r
+/* register number to channel number , slot offset */\r
+#define SLOT1 0\r
+#define SLOT2 1\r
+\r
+/* Envelope Generator phases */\r
+\r
+#define EG_ATT 4\r
+#define EG_DEC 3\r
+#define EG_SUS 2\r
+#define EG_REL 1\r
+#define EG_OFF 0\r
+\r
+\r
+/* save output as raw 16-bit sample */\r
+\r
+/*#define SAVE_SAMPLE*/\r
+\r
+#ifdef SAVE_SAMPLE\r
+INLINE signed int acc_calc(signed int value)\r
+{\r
+ if (value>=0)\r
+ {\r
+ if (value < 0x0200)\r
+ return (value & ~0);\r
+ if (value < 0x0400)\r
+ return (value & ~1);\r
+ if (value < 0x0800)\r
+ return (value & ~3);\r
+ if (value < 0x1000)\r
+ return (value & ~7);\r
+ if (value < 0x2000)\r
+ return (value & ~15);\r
+ if (value < 0x4000)\r
+ return (value & ~31);\r
+ return (value & ~63);\r
+ }\r
+ /*else value < 0*/\r
+ if (value > -0x0200)\r
+ return (~abs(value) & ~0);\r
+ if (value > -0x0400)\r
+ return (~abs(value) & ~1);\r
+ if (value > -0x0800)\r
+ return (~abs(value) & ~3);\r
+ if (value > -0x1000)\r
+ return (~abs(value) & ~7);\r
+ if (value > -0x2000)\r
+ return (~abs(value) & ~15);\r
+ if (value > -0x4000)\r
+ return (~abs(value) & ~31);\r
+ return (~abs(value) & ~63);\r
+}\r
+\r
+\r
+static FILE *sample[1];\r
+ #if 1 /*save to MONO file */\r
+ #define SAVE_ALL_CHANNELS \\r
+ { signed int pom = acc_calc(lt); \\r
+ fputc((unsigned short)pom&0xff,sample[0]); \\r
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \\r
+ }\r
+ #else /*save to STEREO file */\r
+ #define SAVE_ALL_CHANNELS \\r
+ { signed int pom = lt; \\r
+ fputc((unsigned short)pom&0xff,sample[0]); \\r
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \\r
+ pom = rt; \\r
+ fputc((unsigned short)pom&0xff,sample[0]); \\r
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \\r
+ }\r
+ #endif\r
+#endif\r
+\r
+//#define LOG_CYM_FILE 0\r
+//static FILE * cymfile = NULL;\r
+\r
+\r
+\r
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */\r
+#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */\r
+#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */\r
+#define OPL_TYPE_IO 0x08 /* I/O port */\r
+\r
+/* ---------- Generic interface section ---------- */\r
+#define OPL_TYPE_YM3526 (0)\r
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)\r
+#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)\r
+\r
+\r
+\r
+typedef struct\r
+{\r
+ UINT32 ar; /* attack rate: AR<<2 */\r
+ UINT32 dr; /* decay rate: DR<<2 */\r
+ UINT32 rr; /* release rate:RR<<2 */\r
+ UINT8 KSR; /* key scale rate */\r
+ UINT8 ksl; /* keyscale level */\r
+ UINT8 ksr; /* key scale rate: kcode>>KSR */\r
+ UINT8 mul; /* multiple: mul_tab[ML] */\r
+\r
+ /* Phase Generator */\r
+ UINT32 Cnt; /* frequency counter */\r
+ UINT32 Incr; /* frequency counter step */\r
+ UINT8 FB; /* feedback shift value */\r
+ INT32 *connect1; /* slot1 output pointer */\r
+ INT32 op1_out[2]; /* slot1 output for feedback */\r
+ UINT8 CON; /* connection (algorithm) type */\r
+\r
+ /* Envelope Generator */\r
+ UINT8 eg_type; /* percussive/non-percussive mode */\r
+ UINT8 state; /* phase type */\r
+ UINT32 TL; /* total level: TL << 2 */\r
+ INT32 TLL; /* adjusted now TL */\r
+ INT32 volume; /* envelope counter */\r
+ UINT32 sl; /* sustain level: sl_tab[SL] */\r
+ UINT8 eg_sh_ar; /* (attack state) */\r
+ UINT8 eg_sel_ar; /* (attack state) */\r
+ UINT8 eg_sh_dr; /* (decay state) */\r
+ UINT8 eg_sel_dr; /* (decay state) */\r
+ UINT8 eg_sh_rr; /* (release state) */\r
+ UINT8 eg_sel_rr; /* (release state) */\r
+ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */\r
+\r
+ /* LFO */\r
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */\r
+ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/\r
+\r
+ /* waveform select */\r
+ UINT16 wavetable;\r
+} OPL_SLOT;\r
+\r
+typedef struct\r
+{\r
+ OPL_SLOT SLOT[2];\r
+ /* phase generator state */\r
+ UINT32 block_fnum; /* block+fnum */\r
+ UINT32 fc; /* Freq. Increment base */\r
+ UINT32 ksl_base; /* KeyScaleLevel Base step */\r
+ UINT8 kcode; /* key code (for key scaling) */\r
+ UINT8 Muted;\r
+} OPL_CH;\r
+\r
+/* OPL state */\r
+typedef struct fm_opl_f\r
+{\r
+ /* FM channel slots */\r
+ OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/\r
+ UINT8 MuteSpc[6]; /* Mute Special: 5 Rhythm + 1 DELTA-T Channel */\r
+\r
+ UINT32 eg_cnt; /* global envelope generator counter */\r
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */\r
+ UINT32 eg_timer_add; /* step of eg_timer */\r
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */\r
+\r
+ UINT8 rhythm; /* Rhythm mode */\r
+\r
+ UINT32 fn_tab[1024]; /* fnumber->increment counter */\r
+\r
+ /* LFO */\r
+ UINT32 LFO_AM;\r
+ INT32 LFO_PM;\r
+\r
+ UINT8 lfo_am_depth;\r
+ UINT8 lfo_pm_depth_range;\r
+ UINT32 lfo_am_cnt;\r
+ UINT32 lfo_am_inc;\r
+ UINT32 lfo_pm_cnt;\r
+ UINT32 lfo_pm_inc;\r
+\r
+ UINT32 noise_rng; /* 23 bit noise shift register */\r
+ UINT32 noise_p; /* current noise 'phase' */\r
+ UINT32 noise_f; /* current noise period */\r
+\r
+ UINT8 wavesel; /* waveform select enable flag */\r
+\r
+ UINT32 T[2]; /* timer counters */\r
+ UINT32 Trem[2];\r
+ UINT8 st[2]; /* timer enable */\r
+\r
+#if BUILD_Y8950\r
+ /* Delta-T ADPCM unit (Y8950) */\r
+\r
+ YM_DELTAT *deltat;\r
+\r
+ /* Keyboard and I/O ports interface */\r
+ UINT8 portDirection;\r
+ UINT8 portLatch;\r
+ OPL_PORTHANDLER_R porthandler_r;\r
+ OPL_PORTHANDLER_W porthandler_w;\r
+ void * port_param;\r
+ OPL_PORTHANDLER_R keyboardhandler_r;\r
+ OPL_PORTHANDLER_W keyboardhandler_w;\r
+ void * keyboard_param;\r
+#endif\r
+\r
+ /* external event callback handlers */\r
+ OPL_TIMERHANDLER timer_handler; /* TIMER handler */\r
+ void *TimerParam; /* TIMER parameter */\r
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */\r
+ void *IRQParam; /* IRQ parameter */\r
+ OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */\r
+ void *UpdateParam; /* stream update parameter */\r
+\r
+ UINT8 type; /* chip type */\r
+ UINT8 address; /* address register */\r
+ UINT8 status; /* status flag */\r
+ UINT8 statusmask; /* status mask */\r
+ UINT8 mode; /* Reg.08 : CSM,notesel,etc. */\r
+\r
+ UINT32 clock; /* master clock (Hz) */\r
+ UINT32 rate; /* sampling rate (Hz) */\r
+ double freqbase; /* frequency base */\r
+ //attotime TimerBase; /* Timer base time (==sampling time)*/\r
+\r
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */\r
+ signed int output[1];\r
+#if BUILD_Y8950\r
+ INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */\r
+#endif\r
+} FM_OPL;\r
+\r
+\r
+\r
+/* mapping of register number (offset) to slot number used by the emulator */\r
+static const int slot_array[32]=\r
+{\r
+ 0, 2, 4, 1, 3, 5,-1,-1,\r
+ 6, 8,10, 7, 9,11,-1,-1,\r
+ 12,14,16,13,15,17,-1,-1,\r
+ -1,-1,-1,-1,-1,-1,-1,-1\r
+};\r
+\r
+/* key scale level */\r
+/* table is 3dB/octave , DV converts this into 6dB/octave */\r
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */\r
+#define DV (0.1875/2.0)\r
+static const UINT32 ksl_tab[8*16]=\r
+{\r
+ /* OCT 0 */\r
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+ /* OCT 1 */\r
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,\r
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,\r
+ /* OCT 2 */\r
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,\r
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,\r
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,\r
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,\r
+ /* OCT 3 */\r
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,\r
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,\r
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,\r
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,\r
+ /* OCT 4 */\r
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,\r
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,\r
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,\r
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,\r
+ /* OCT 5 */\r
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,\r
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,\r
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,\r
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,\r
+ /* OCT 6 */\r
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,\r
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,\r
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,\r
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,\r
+ /* OCT 7 */\r
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,\r
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,\r
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,\r
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV\r
+};\r
+#undef DV\r
+\r
+/* 0 / 3.0 / 1.5 / 6.0 dB/OCT */\r
+static const UINT32 ksl_shift[4] = { 31, 1, 2, 0 };\r
+\r
+\r
+/* sustain level table (3dB per step) */\r
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/\r
+#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) )\r
+static const UINT32 sl_tab[16]={\r
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),\r
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)\r
+};\r
+#undef SC\r
+\r
+\r
+#define RATE_STEPS (8)\r
+static const unsigned char eg_inc[15*RATE_STEPS]={\r
+\r
+/*cycle:0 1 2 3 4 5 6 7*/\r
+\r
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */\r
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */\r
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */\r
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */\r
+\r
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */\r
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */\r
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */\r
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */\r
+\r
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */\r
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */\r
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */\r
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */\r
+\r
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */\r
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */\r
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */\r
+};\r
+\r
+\r
+#define O(a) (a*RATE_STEPS)\r
+\r
+/*note that there is no O(13) in this table - it's directly in the code */\r
+static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */\r
+/* 16 infinite time rates */\r
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),\r
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),\r
+\r
+/* rates 00-12 */\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+O( 0),O( 1),O( 2),O( 3),\r
+\r
+/* rate 13 */\r
+O( 4),O( 5),O( 6),O( 7),\r
+\r
+/* rate 14 */\r
+O( 8),O( 9),O(10),O(11),\r
+\r
+/* rate 15 */\r
+O(12),O(12),O(12),O(12),\r
+\r
+/* 16 dummy rates (same as 15 3) */\r
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),\r
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),\r
+\r
+};\r
+#undef O\r
+\r
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */\r
+/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */\r
+/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */\r
+\r
+#define O(a) (a*1)\r
+static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */\r
+/* 16 infinite time rates */\r
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),\r
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),\r
+\r
+/* rates 00-12 */\r
+O(12),O(12),O(12),O(12),\r
+O(11),O(11),O(11),O(11),\r
+O(10),O(10),O(10),O(10),\r
+O( 9),O( 9),O( 9),O( 9),\r
+O( 8),O( 8),O( 8),O( 8),\r
+O( 7),O( 7),O( 7),O( 7),\r
+O( 6),O( 6),O( 6),O( 6),\r
+O( 5),O( 5),O( 5),O( 5),\r
+O( 4),O( 4),O( 4),O( 4),\r
+O( 3),O( 3),O( 3),O( 3),\r
+O( 2),O( 2),O( 2),O( 2),\r
+O( 1),O( 1),O( 1),O( 1),\r
+O( 0),O( 0),O( 0),O( 0),\r
+\r
+/* rate 13 */\r
+O( 0),O( 0),O( 0),O( 0),\r
+\r
+/* rate 14 */\r
+O( 0),O( 0),O( 0),O( 0),\r
+\r
+/* rate 15 */\r
+O( 0),O( 0),O( 0),O( 0),\r
+\r
+/* 16 dummy rates (same as 15 3) */\r
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),\r
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),\r
+\r
+};\r
+#undef O\r
+\r
+\r
+/* multiple table */\r
+#define ML 2\r
+static const UINT8 mul_tab[16]= {\r
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */\r
+ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,\r
+ 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML\r
+};\r
+#undef ML\r
+\r
+/* TL_TAB_LEN is calculated as:\r
+* 12 - sinus amplitude bits (Y axis)\r
+* 2 - sinus sign bit (Y axis)\r
+* TL_RES_LEN - sinus resolution (X axis)\r
+*/\r
+#define TL_TAB_LEN (12*2*TL_RES_LEN)\r
+static signed int tl_tab[TL_TAB_LEN];\r
+\r
+#define ENV_QUIET (TL_TAB_LEN>>4)\r
+\r
+/* sin waveform table in 'decibel' scale */\r
+/* four waveforms on OPL2 type chips */\r
+static unsigned int sin_tab[SIN_LEN * 4];\r
+\r
+\r
+/* LFO Amplitude Modulation table (verified on real YM3812)\r
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples\r
+\r
+ Length: 210 elements.\r
+\r
+ Each of the elements has to be repeated\r
+ exactly 64 times (on 64 consecutive samples).\r
+ The whole table takes: 64 * 210 = 13440 samples.\r
+\r
+ When AM = 1 data is used directly\r
+ When AM = 0 data is divided by 4 before being used (losing precision is important)\r
+*/\r
+\r
+#define LFO_AM_TAB_ELEMENTS 210\r
+\r
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {\r
+0,0,0,0,0,0,0,\r
+1,1,1,1,\r
+2,2,2,2,\r
+3,3,3,3,\r
+4,4,4,4,\r
+5,5,5,5,\r
+6,6,6,6,\r
+7,7,7,7,\r
+8,8,8,8,\r
+9,9,9,9,\r
+10,10,10,10,\r
+11,11,11,11,\r
+12,12,12,12,\r
+13,13,13,13,\r
+14,14,14,14,\r
+15,15,15,15,\r
+16,16,16,16,\r
+17,17,17,17,\r
+18,18,18,18,\r
+19,19,19,19,\r
+20,20,20,20,\r
+21,21,21,21,\r
+22,22,22,22,\r
+23,23,23,23,\r
+24,24,24,24,\r
+25,25,25,25,\r
+26,26,26,\r
+25,25,25,25,\r
+24,24,24,24,\r
+23,23,23,23,\r
+22,22,22,22,\r
+21,21,21,21,\r
+20,20,20,20,\r
+19,19,19,19,\r
+18,18,18,18,\r
+17,17,17,17,\r
+16,16,16,16,\r
+15,15,15,15,\r
+14,14,14,14,\r
+13,13,13,13,\r
+12,12,12,12,\r
+11,11,11,11,\r
+10,10,10,10,\r
+9,9,9,9,\r
+8,8,8,8,\r
+7,7,7,7,\r
+6,6,6,6,\r
+5,5,5,5,\r
+4,4,4,4,\r
+3,3,3,3,\r
+2,2,2,2,\r
+1,1,1,1\r
+};\r
+\r
+/* LFO Phase Modulation table (verified on real YM3812) */\r
+static const INT8 lfo_pm_table[8*8*2] = {\r
+\r
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */\r
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/\r
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */\r
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/\r
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */\r
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/\r
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */\r
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/\r
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */\r
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/\r
+4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */\r
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/\r
+5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */\r
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/\r
+6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/\r
+\r
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */\r
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/\r
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/\r
+};\r
+\r
+\r
+/* lock level of common table */\r
+static int num_lock = 0;\r
+\r
+\r
+#define SLOT7_1 (&OPL->P_CH[7].SLOT[SLOT1])\r
+#define SLOT7_2 (&OPL->P_CH[7].SLOT[SLOT2])\r
+#define SLOT8_1 (&OPL->P_CH[8].SLOT[SLOT1])\r
+#define SLOT8_2 (&OPL->P_CH[8].SLOT[SLOT2])\r
+\r
+\r
+\r
+\r
+/*INLINE int limit( int val, int max, int min ) {\r
+ if ( val > max )\r
+ val = max;\r
+ else if ( val < min )\r
+ val = min;\r
+\r
+ return val;\r
+}*/\r
+\r
+\r
+/* status set and IRQ handling */\r
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)\r
+{\r
+ /* set status flag */\r
+ OPL->status |= flag;\r
+ if(!(OPL->status & 0x80))\r
+ {\r
+ if(OPL->status & OPL->statusmask)\r
+ { /* IRQ on */\r
+ OPL->status |= 0x80;\r
+ /* callback user interrupt handler (IRQ is OFF to ON) */\r
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);\r
+ }\r
+ }\r
+}\r
+\r
+/* status reset and IRQ handling */\r
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)\r
+{\r
+ /* reset status flag */\r
+ OPL->status &=~flag;\r
+ if((OPL->status & 0x80))\r
+ {\r
+ if (!(OPL->status & OPL->statusmask) )\r
+ {\r
+ OPL->status &= 0x7f;\r
+ /* callback user interrupt handler (IRQ is ON to OFF) */\r
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);\r
+ }\r
+ }\r
+}\r
+\r
+/* IRQ mask set */\r
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)\r
+{\r
+ OPL->statusmask = flag;\r
+ /* IRQ handling check */\r
+ OPL_STATUS_SET(OPL,0);\r
+ OPL_STATUS_RESET(OPL,0);\r
+}\r
+\r
+\r
+/* advance LFO to next sample */\r
+INLINE void advance_lfo(FM_OPL *OPL)\r
+{\r
+ UINT8 tmp;\r
+\r
+ /* LFO */\r
+ OPL->lfo_am_cnt += OPL->lfo_am_inc;\r
+ if (OPL->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH) ) /* lfo_am_table is 210 elements long */\r
+ OPL->lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH);\r
+\r
+ tmp = lfo_am_table[ OPL->lfo_am_cnt >> LFO_SH ];\r
+\r
+ if (OPL->lfo_am_depth)\r
+ OPL->LFO_AM = tmp;\r
+ else\r
+ OPL->LFO_AM = tmp>>2;\r
+\r
+ OPL->lfo_pm_cnt += OPL->lfo_pm_inc;\r
+ OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range;\r
+}\r
+\r
+INLINE void refresh_eg(FM_OPL* OPL)\r
+{\r
+ OPL_CH *CH;\r
+ OPL_SLOT *op;\r
+ int i;\r
+ int new_vol;\r
+\r
+ for (i=0; i<9*2; i++)\r
+ {\r
+ CH = &OPL->P_CH[i/2];\r
+ op = &CH->SLOT[i&1];\r
+\r
+ // Envelope Generator\r
+ switch(op->state)\r
+ {\r
+ case EG_ATT: // attack phase\r
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )\r
+ {\r
+ new_vol = op->volume + ((~op->volume *\r
+ (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)])\r
+ ) >> 3);\r
+ if (new_vol <= MIN_ATT_INDEX)\r
+ {\r
+ op->volume = MIN_ATT_INDEX;\r
+ op->state = EG_DEC;\r
+ }\r
+ }\r
+ break;\r
+ /*case EG_DEC: // decay phase\r
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )\r
+ {\r
+ new_vol = op->volume + eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)];\r
+\r
+ if ( new_vol >= op->sl )\r
+ op->state = EG_SUS;\r
+ }\r
+ break;\r
+ case EG_SUS: // sustain phase\r
+ if ( !op->eg_type) percussive mode\r
+ {\r
+ new_vol = op->volume + eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];\r
+\r
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )\r
+ {\r
+ if ( new_vol >= MAX_ATT_INDEX )\r
+ op->volume = MAX_ATT_INDEX;\r
+ }\r
+ }\r
+ break;\r
+ case EG_REL: // release phase\r
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )\r
+ {\r
+ new_vol = op->volume + eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];\r
+ if ( new_vol >= MAX_ATT_INDEX )\r
+ {\r
+ op->volume = MAX_ATT_INDEX;\r
+ op->state = EG_OFF;\r
+ }\r
+\r
+ }\r
+ break;\r
+ default:\r
+ break;*/\r
+ }\r
+ }\r
+ \r
+ return;\r
+}\r
+\r
+/* advance to next sample */\r
+INLINE void advance(FM_OPL *OPL)\r
+{\r
+ OPL_CH *CH;\r
+ OPL_SLOT *op;\r
+ int i;\r
+\r
+ OPL->eg_timer += OPL->eg_timer_add;\r
+\r
+ while (OPL->eg_timer >= OPL->eg_timer_overflow)\r
+ {\r
+ OPL->eg_timer -= OPL->eg_timer_overflow;\r
+\r
+ OPL->eg_cnt++;\r
+\r
+ for (i = 0; i < 2; i ++)\r
+ {\r
+ if (OPL->st[i])\r
+ {\r
+ if (! OPL->Trem[i])\r
+ OPLTimerOver(OPL, i);\r
+ OPL->Trem[i] --;\r
+ }\r
+ }\r
+\r
+ for (i=0; i<9*2; i++)\r
+ {\r
+ CH = &OPL->P_CH[i/2];\r
+ op = &CH->SLOT[i&1];\r
+\r
+ /* Envelope Generator */\r
+ switch(op->state)\r
+ {\r
+ case EG_ATT: /* attack phase */\r
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )\r
+ {\r
+ op->volume += (~op->volume *\r
+ (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)])\r
+ ) >>3;\r
+\r
+ if (op->volume <= MIN_ATT_INDEX)\r
+ {\r
+ op->volume = MIN_ATT_INDEX;\r
+ op->state = EG_DEC;\r
+ }\r
+\r
+ }\r
+ break;\r
+\r
+ case EG_DEC: /* decay phase */\r
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )\r
+ {\r
+ op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)];\r
+\r
+ if ( op->volume >= op->sl )\r
+ op->state = EG_SUS;\r
+\r
+ }\r
+ break;\r
+\r
+ case EG_SUS: /* sustain phase */\r
+\r
+ /* this is important behaviour:\r
+ one can change percusive/non-percussive modes on the fly and\r
+ the chip will remain in sustain phase - verified on real YM3812 */\r
+\r
+ if(op->eg_type) /* non-percussive mode */\r
+ {\r
+ /* do nothing */\r
+ }\r
+ else /* percussive mode */\r
+ {\r
+ /* during sustain phase chip adds Release Rate (in percussive mode) */\r
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )\r
+ {\r
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];\r
+\r
+ if ( op->volume >= MAX_ATT_INDEX )\r
+ op->volume = MAX_ATT_INDEX;\r
+ }\r
+ /* else do nothing in sustain phase */\r
+ }\r
+ break;\r
+\r
+ case EG_REL: /* release phase */\r
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )\r
+ {\r
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];\r
+\r
+ if ( op->volume >= MAX_ATT_INDEX )\r
+ {\r
+ op->volume = MAX_ATT_INDEX;\r
+ op->state = EG_OFF;\r
+ }\r
+\r
+ }\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ for (i=0; i<9*2; i++)\r
+ {\r
+ CH = &OPL->P_CH[i/2];\r
+ op = &CH->SLOT[i&1];\r
+\r
+ /* Phase Generator */\r
+ if(op->vib)\r
+ {\r
+ UINT8 block;\r
+ unsigned int block_fnum = CH->block_fnum;\r
+\r
+ unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;\r
+\r
+ signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ];\r
+\r
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */\r
+ {\r
+ block_fnum += lfo_fn_table_index_offset;\r
+ block = (block_fnum&0x1c00) >> 10;\r
+ op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;\r
+ }\r
+ else /* LFO phase modulation = zero */\r
+ {\r
+ op->Cnt += op->Incr;\r
+ }\r
+ }\r
+ else /* LFO phase modulation disabled for this operator */\r
+ {\r
+ op->Cnt += op->Incr;\r
+ }\r
+ }\r
+\r
+ /* The Noise Generator of the YM3812 is 23-bit shift register.\r
+ * Period is equal to 2^23-2 samples.\r
+ * Register works at sampling frequency of the chip, so output\r
+ * can change on every sample.\r
+ *\r
+ * Output of the register and input to the bit 22 is:\r
+ * bit0 XOR bit14 XOR bit15 XOR bit22\r
+ *\r
+ * Simply use bit 22 as the noise output.\r
+ */\r
+\r
+ OPL->noise_p += OPL->noise_f;\r
+ i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */\r
+ OPL->noise_p &= FREQ_MASK;\r
+ while (i)\r
+ {\r
+ /*\r
+ UINT32 j;\r
+ j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1;\r
+ OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1);\r
+ */\r
+\r
+ /*\r
+ Instead of doing all the logic operations above, we\r
+ use a trick here (and use bit 0 as the noise output).\r
+ The difference is only that the noise bit changes one\r
+ step ahead. This doesn't matter since we don't know\r
+ what is real state of the noise_rng after the reset.\r
+ */\r
+\r
+ if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302;\r
+ OPL->noise_rng >>= 1;\r
+\r
+ i--;\r
+ }\r
+}\r
+\r
+\r
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)\r
+{\r
+ UINT32 p;\r
+\r
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];\r
+\r
+ if (p >= TL_TAB_LEN)\r
+ return 0;\r
+ return tl_tab[p];\r
+}\r
+\r
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)\r
+{\r
+ UINT32 p;\r
+\r
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ];\r
+\r
+ if (p >= TL_TAB_LEN)\r
+ return 0;\r
+ return tl_tab[p];\r
+}\r
+\r
+\r
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask))\r
+\r
+/* calculate output */\r
+INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH )\r
+{\r
+ OPL_SLOT *SLOT;\r
+ unsigned int env;\r
+ signed int out;\r
+\r
+ if (CH->Muted)\r
+ return;\r
+\r
+ OPL->phase_modulation = 0;\r
+\r
+ /* SLOT 1 */\r
+ SLOT = &CH->SLOT[SLOT1];\r
+ env = volume_calc(SLOT);\r
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];\r
+ SLOT->op1_out[0] = SLOT->op1_out[1];\r
+ *SLOT->connect1 += SLOT->op1_out[0];\r
+ SLOT->op1_out[1] = 0;\r
+ if( env < ENV_QUIET )\r
+ {\r
+ if (!SLOT->FB)\r
+ out = 0;\r
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );\r
+ }\r
+\r
+ /* SLOT 2 */\r
+ SLOT++;\r
+ env = volume_calc(SLOT);\r
+ if( env < ENV_QUIET )\r
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable);\r
+}\r
+\r
+/*\r
+ operators used in the rhythm sounds generation process:\r
+\r
+ Envelope Generator:\r
+\r
+channel operator register number Bass High Snare Tom Top\r
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal\r
+ 6 / 0 12 50 70 90 f0 +\r
+ 6 / 1 15 53 73 93 f3 +\r
+ 7 / 0 13 51 71 91 f1 +\r
+ 7 / 1 16 54 74 94 f4 +\r
+ 8 / 0 14 52 72 92 f2 +\r
+ 8 / 1 17 55 75 95 f5 +\r
+\r
+ Phase Generator:\r
+\r
+channel operator register number Bass High Snare Tom Top\r
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal\r
+ 6 / 0 12 30 +\r
+ 6 / 1 15 33 +\r
+ 7 / 0 13 31 + + +\r
+ 7 / 1 16 34 ----- n o t u s e d -----\r
+ 8 / 0 14 32 +\r
+ 8 / 1 17 35 + +\r
+\r
+channel operator register number Bass High Snare Tom Top\r
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal\r
+ 6 12,15 B6 A6 +\r
+\r
+ 7 13,16 B7 A7 + + +\r
+\r
+ 8 14,17 B8 A8 + + +\r
+\r
+*/\r
+\r
+/* calculate rhythm */\r
+\r
+INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise )\r
+{\r
+ OPL_SLOT *SLOT;\r
+ signed int out;\r
+ unsigned int env;\r
+\r
+\r
+ /* Bass Drum (verified on real YM3812):\r
+ - depends on the channel 6 'connect' register:\r
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)\r
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored\r
+ - output sample always is multiplied by 2\r
+ */\r
+\r
+ OPL->phase_modulation = 0;\r
+ /* SLOT 1 */\r
+ SLOT = &CH[6].SLOT[SLOT1];\r
+ env = volume_calc(SLOT);\r
+\r
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];\r
+ SLOT->op1_out[0] = SLOT->op1_out[1];\r
+\r
+ if (!SLOT->CON)\r
+ OPL->phase_modulation = SLOT->op1_out[0];\r
+ /* else ignore output of operator 1 */\r
+\r
+ SLOT->op1_out[1] = 0;\r
+ if( env < ENV_QUIET )\r
+ {\r
+ if (!SLOT->FB)\r
+ out = 0;\r
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );\r
+ }\r
+\r
+ /* SLOT 2 */\r
+ SLOT++;\r
+ env = volume_calc(SLOT);\r
+ if( env < ENV_QUIET && ! OPL->MuteSpc[0] )\r
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2;\r
+\r
+\r
+ /* Phase generation is based on: */\r
+ /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */\r
+ /* SD (16) channel 7->slot 1 */\r
+ /* TOM (14) channel 8->slot 1 */\r
+ /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */\r
+\r
+ /* Envelope generation based on: */\r
+ /* HH channel 7->slot1 */\r
+ /* SD channel 7->slot2 */\r
+ /* TOM channel 8->slot1 */\r
+ /* TOP channel 8->slot2 */\r
+\r
+\r
+ /* The following formulas can be well optimized.\r
+ I leave them in direct form for now (in case I've missed something).\r
+ */\r
+\r
+ /* High Hat (verified on real YM3812) */\r
+ env = volume_calc(SLOT7_1);\r
+ if( env < ENV_QUIET && ! OPL->MuteSpc[4] )\r
+ {\r
+\r
+ /* high hat phase generation:\r
+ phase = d0 or 234 (based on frequency only)\r
+ phase = 34 or 2d0 (based on noise)\r
+ */\r
+\r
+ /* base frequency derived from operator 1 in channel 7 */\r
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;\r
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;\r
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;\r
+\r
+ unsigned char res1 = (bit2 ^ bit7) | bit3;\r
+\r
+ /* when res1 = 0 phase = 0x000 | 0xd0; */\r
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */\r
+ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;\r
+\r
+ /* enable gate based on frequency of operator 2 in channel 8 */\r
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;\r
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;\r
+\r
+ unsigned char res2 = (bit3e ^ bit5e);\r
+\r
+ /* when res2 = 0 pass the phase from calculation above (res1); */\r
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */\r
+ if (res2)\r
+ phase = (0x200|(0xd0>>2));\r
+\r
+\r
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */\r
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */\r
+ if (phase&0x200)\r
+ {\r
+ if (noise)\r
+ phase = 0x200|0xd0;\r
+ }\r
+ else\r
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */\r
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */\r
+ {\r
+ if (noise)\r
+ phase = 0xd0>>2;\r
+ }\r
+\r
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_1->wavetable) * 2;\r
+ }\r
+\r
+ /* Snare Drum (verified on real YM3812) */\r
+ env = volume_calc(SLOT7_2);\r
+ if( env < ENV_QUIET && ! OPL->MuteSpc[1] )\r
+ {\r
+ /* base frequency derived from operator 1 in channel 7 */\r
+ unsigned char bit8 = ((SLOT7_1->Cnt>>FREQ_SH)>>8)&1;\r
+\r
+ /* when bit8 = 0 phase = 0x100; */\r
+ /* when bit8 = 1 phase = 0x200; */\r
+ UINT32 phase = bit8 ? 0x200 : 0x100;\r
+\r
+ /* Noise bit XOR'es phase by 0x100 */\r
+ /* when noisebit = 0 pass the phase from calculation above */\r
+ /* when noisebit = 1 phase ^= 0x100; */\r
+ /* in other words: phase ^= (noisebit<<8); */\r
+ if (noise)\r
+ phase ^= 0x100;\r
+\r
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT7_2->wavetable) * 2;\r
+ }\r
+\r
+ /* Tom Tom (verified on real YM3812) */\r
+ env = volume_calc(SLOT8_1);\r
+ if( env < ENV_QUIET && ! OPL->MuteSpc[2] )\r
+ OPL->output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT8_1->wavetable) * 2;\r
+\r
+ /* Top Cymbal (verified on real YM3812) */\r
+ env = volume_calc(SLOT8_2);\r
+ if( env < ENV_QUIET && ! OPL->MuteSpc[3] )\r
+ {\r
+ /* base frequency derived from operator 1 in channel 7 */\r
+ unsigned char bit7 = ((SLOT7_1->Cnt>>FREQ_SH)>>7)&1;\r
+ unsigned char bit3 = ((SLOT7_1->Cnt>>FREQ_SH)>>3)&1;\r
+ unsigned char bit2 = ((SLOT7_1->Cnt>>FREQ_SH)>>2)&1;\r
+\r
+ unsigned char res1 = (bit2 ^ bit7) | bit3;\r
+\r
+ /* when res1 = 0 phase = 0x000 | 0x100; */\r
+ /* when res1 = 1 phase = 0x200 | 0x100; */\r
+ UINT32 phase = res1 ? 0x300 : 0x100;\r
+\r
+ /* enable gate based on frequency of operator 2 in channel 8 */\r
+ unsigned char bit5e= ((SLOT8_2->Cnt>>FREQ_SH)>>5)&1;\r
+ unsigned char bit3e= ((SLOT8_2->Cnt>>FREQ_SH)>>3)&1;\r
+\r
+ unsigned char res2 = (bit3e ^ bit5e);\r
+ /* when res2 = 0 pass the phase from calculation above (res1); */\r
+ /* when res2 = 1 phase = 0x200 | 0x100; */\r
+ if (res2)\r
+ phase = 0x300;\r
+\r
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, SLOT8_2->wavetable) * 2;\r
+ }\r
+}\r
+\r
+\r
+/* generic table initialize */\r
+static int init_tables(void)\r
+{\r
+ signed int i,x;\r
+ signed int n;\r
+ double o,m;\r
+\r
+\r
+ for (x=0; x<TL_RES_LEN; x++)\r
+ {\r
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);\r
+ m = floor(m);\r
+\r
+ /* we never reach (1<<16) here due to the (x+1) */\r
+ /* result fits within 16 bits at maximum */\r
+\r
+ n = (int)m; /* 16 bits here */\r
+ n >>= 4; /* 12 bits here */\r
+ if (n&1) /* round to nearest */\r
+ n = (n>>1)+1;\r
+ else\r
+ n = n>>1;\r
+ /* 11 bits here (rounded) */\r
+ n <<= 1; /* 12 bits here (as in real chip) */\r
+ tl_tab[ x*2 + 0 ] = n;\r
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];\r
+\r
+ for (i=1; i<12; i++)\r
+ {\r
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;\r
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];\r
+ }\r
+ #if 0\r
+ logerror("tl %04i", x*2);\r
+ for (i=0; i<12; i++)\r
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );\r
+ logerror("\n");\r
+ #endif\r
+ }\r
+ /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/\r
+\r
+\r
+ for (i=0; i<SIN_LEN; i++)\r
+ {\r
+ /* non-standard sinus */\r
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */\r
+\r
+ /* we never reach zero here due to ((i*2)+1) */\r
+\r
+ if (m>0.0)\r
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */\r
+ else\r
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */\r
+\r
+ o = o / (ENV_STEP/4);\r
+\r
+ n = (int)(2.0*o);\r
+ if (n&1) /* round to nearest */\r
+ n = (n>>1)+1;\r
+ else\r
+ n = n>>1;\r
+\r
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );\r
+\r
+ /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/\r
+ }\r
+\r
+ for (i=0; i<SIN_LEN; i++)\r
+ {\r
+ /* waveform 1: __ __ */\r
+ /* / \____/ \____*/\r
+ /* output only first half of the sinus waveform (positive one) */\r
+\r
+ if (i & (1<<(SIN_BITS-1)) )\r
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;\r
+ else\r
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];\r
+\r
+ /* waveform 2: __ __ __ __ */\r
+ /* / \/ \/ \/ \*/\r
+ /* abs(sin) */\r
+\r
+ sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1) ];\r
+\r
+ /* waveform 3: _ _ _ _ */\r
+ /* / |_/ |_/ |_/ |_*/\r
+ /* abs(output only first quarter of the sinus waveform) */\r
+\r
+ if (i & (1<<(SIN_BITS-2)) )\r
+ sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;\r
+ else\r
+ sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];\r
+\r
+ /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );\r
+ logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] );\r
+ logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/\r
+ }\r
+ /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/\r
+\r
+\r
+#ifdef SAVE_SAMPLE\r
+ sample[0]=fopen("sampsum.pcm","wb");\r
+#endif\r
+\r
+ return 1;\r
+}\r
+\r
+static void OPLCloseTable( void )\r
+{\r
+#ifdef SAVE_SAMPLE\r
+ fclose(sample[0]);\r
+#endif\r
+}\r
+\r
+\r
+\r
+static void OPL_initalize(FM_OPL *OPL)\r
+{\r
+ int i;\r
+\r
+ /* frequency base */\r
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0;\r
+#if 0\r
+ OPL->rate = (double)OPL->clock / 72.0;\r
+ OPL->freqbase = 1.0;\r
+#endif\r
+\r
+ /*logerror("freqbase=%f\n", OPL->freqbase);*/\r
+\r
+ /* Timer base time */\r
+ //OPL->TimerBase = attotime_mul(ATTOTIME_IN_HZ(OPL->clock), 72);\r
+\r
+ /* make fnumber -> increment counter table */\r
+ for( i=0 ; i < 1024 ; i++ )\r
+ {\r
+ /* opn phase increment counter = 20bit */\r
+ OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */\r
+#if 0\r
+ logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n",\r
+ i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 );\r
+#endif\r
+ }\r
+\r
+#if 0\r
+ for( i=0 ; i < 16 ; i++ )\r
+ {\r
+ logerror("FMOPL.C: sl_tab[%i] = %08x\n",\r
+ i, sl_tab[i] );\r
+ }\r
+ for( i=0 ; i < 8 ; i++ )\r
+ {\r
+ int j;\r
+ logerror("FMOPL.C: ksl_tab[oct=%2i] =",i);\r
+ for (j=0; j<16; j++)\r
+ {\r
+ logerror("%08x ", ksl_tab[i*16+j] );\r
+ }\r
+ logerror("\n");\r
+ }\r
+#endif\r
+\r
+ for(i = 0; i < 9; i ++)\r
+ OPL->P_CH[i].Muted = 0x00;\r
+ for(i = 0; i < 6; i ++)\r
+ OPL->MuteSpc[i] = 0x00;\r
+\r
+\r
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */\r
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */\r
+ OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * OPL->freqbase;\r
+\r
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */\r
+ OPL->lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * OPL->freqbase;\r
+\r
+ /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/\r
+\r
+ /* Noise generator: a step takes 1 sample */\r
+ OPL->noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * OPL->freqbase;\r
+\r
+ OPL->eg_timer_add = (1<<EG_SH) * OPL->freqbase;\r
+ OPL->eg_timer_overflow = ( 1 ) * (1<<EG_SH);\r
+ /*logerror("OPLinit eg_timer_add=%8x eg_timer_overflow=%8x\n", OPL->eg_timer_add, OPL->eg_timer_overflow);*/\r
+\r
+}\r
+\r
+INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set)\r
+{\r
+ if( !SLOT->key )\r
+ {\r
+ /* restart Phase Generator */\r
+ SLOT->Cnt = 0;\r
+ /* phase -> Attack */\r
+ SLOT->state = EG_ATT;\r
+ }\r
+ SLOT->key |= key_set;\r
+}\r
+\r
+INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr)\r
+{\r
+ if( SLOT->key )\r
+ {\r
+ SLOT->key &= key_clr;\r
+\r
+ if( !SLOT->key )\r
+ {\r
+ /* phase -> Release */\r
+ if (SLOT->state>EG_REL)\r
+ SLOT->state = EG_REL;\r
+ }\r
+ }\r
+}\r
+\r
+/* update phase increment counter of operator (also update the EG rates if necessary) */\r
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)\r
+{\r
+ int ksr;\r
+\r
+ /* (frequency) phase increment counter */\r
+ SLOT->Incr = CH->fc * SLOT->mul;\r
+ ksr = CH->kcode >> SLOT->KSR;\r
+\r
+ if( SLOT->ksr != ksr )\r
+ {\r
+ SLOT->ksr = ksr;\r
+\r
+ /* calculate envelope generator rates */\r
+ if ((SLOT->ar + SLOT->ksr) < 16+62)\r
+ {\r
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];\r
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];\r
+ }\r
+ else\r
+ {\r
+ SLOT->eg_sh_ar = 0;\r
+ SLOT->eg_sel_ar = 13*RATE_STEPS;\r
+ }\r
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];\r
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];\r
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];\r
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];\r
+ }\r
+}\r
+\r
+/* set multi,am,vib,EG-TYP,KSR,mul */\r
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)\r
+{\r
+ OPL_CH *CH = &OPL->P_CH[slot/2];\r
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];\r
+\r
+ SLOT->mul = mul_tab[v&0x0f];\r
+ SLOT->KSR = (v&0x10) ? 0 : 2;\r
+ SLOT->eg_type = (v&0x20);\r
+ SLOT->vib = (v&0x40);\r
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;\r
+ CALC_FCSLOT(CH,SLOT);\r
+}\r
+\r
+/* set ksl & tl */\r
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)\r
+{\r
+ OPL_CH *CH = &OPL->P_CH[slot/2];\r
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];\r
+\r
+ SLOT->ksl = ksl_shift[v >> 6];\r
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */\r
+\r
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);\r
+}\r
+\r
+/* set attack rate & decay rate */\r
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)\r
+{\r
+ OPL_CH *CH = &OPL->P_CH[slot/2];\r
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];\r
+\r
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;\r
+\r
+ if ((SLOT->ar + SLOT->ksr) < 16+62)\r
+ {\r
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];\r
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];\r
+ }\r
+ else\r
+ {\r
+ SLOT->eg_sh_ar = 0;\r
+ SLOT->eg_sel_ar = 13*RATE_STEPS;\r
+ }\r
+\r
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;\r
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];\r
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];\r
+}\r
+\r
+/* set sustain level & release rate */\r
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)\r
+{\r
+ OPL_CH *CH = &OPL->P_CH[slot/2];\r
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];\r
+\r
+ SLOT->sl = sl_tab[ v>>4 ];\r
+\r
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;\r
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];\r
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];\r
+}\r
+\r
+\r
+/* write a value v to register r on OPL chip */\r
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)\r
+{\r
+ OPL_CH *CH;\r
+ int slot;\r
+ int block_fnum;\r
+\r
+\r
+ /* adjust bus to 8 bits */\r
+ r &= 0xff;\r
+ v &= 0xff;\r
+\r
+ /*if (LOG_CYM_FILE && (cymfile) && (r!=0) )\r
+ {\r
+ fputc( (unsigned char)r, cymfile );\r
+ fputc( (unsigned char)v, cymfile );\r
+ }*/\r
+\r
+\r
+ switch(r&0xe0)\r
+ {\r
+ case 0x00: /* 00-1f:control */\r
+ switch(r&0x1f)\r
+ {\r
+ case 0x01: /* waveform select enable */\r
+ if(OPL->type&OPL_TYPE_WAVESEL)\r
+ {\r
+ OPL->wavesel = v&0x20;\r
+ /* do not change the waveform previously selected */\r
+ }\r
+ break;\r
+ case 0x02: /* Timer 1 */\r
+ OPL->T[0] = (256-v)*4;\r
+ break;\r
+ case 0x03: /* Timer 2 */\r
+ OPL->T[1] = (256-v)*16;\r
+ break;\r
+ case 0x04: /* IRQ clear / mask and Timer enable */\r
+ if(v&0x80)\r
+ { /* IRQ flag clear */\r
+ OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */\r
+ }\r
+ else\r
+ { /* set IRQ mask ,timer enable*/\r
+ UINT8 st1 = v&1;\r
+ UINT8 st2 = (v>>1)&1;\r
+\r
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */\r
+ OPL_STATUS_RESET(OPL, v & (0x78-0x08) );\r
+ OPL_STATUSMASK_SET(OPL, (~v) & 0x78 );\r
+\r
+ /* timer 2 */\r
+ if(OPL->st[1] != st2)\r
+ {\r
+ //attotime period = st2 ? attotime_mul(OPL->TimerBase, OPL->T[1]) : attotime_zero;\r
+ OPL->st[1] = st2;\r
+ //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period);\r
+ }\r
+ /* timer 1 */\r
+ if(OPL->st[0] != st1)\r
+ {\r
+ //attotime period = st1 ? attotime_mul(OPL->TimerBase, OPL->T[0]) : attotime_zero;\r
+ OPL->st[0] = st1;\r
+ //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period);\r
+ }\r
+ }\r
+ break;\r
+#if BUILD_Y8950\r
+ case 0x06: /* Key Board OUT */\r
+ if(OPL->type&OPL_TYPE_KEYBOARD)\r
+ {\r
+ if(OPL->keyboardhandler_w)\r
+ OPL->keyboardhandler_w(OPL->keyboard_param,v);\r
+#ifdef _DEBUG\r
+ else\r
+ logerror("Y8950: write unmapped KEYBOARD port\n");\r
+#endif\r
+ }\r
+ break;\r
+ case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */\r
+ if(OPL->type&OPL_TYPE_ADPCM)\r
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);\r
+ break;\r
+#endif\r
+ case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */\r
+ OPL->mode = v;\r
+#if BUILD_Y8950\r
+ if(OPL->type&OPL_TYPE_ADPCM)\r
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */\r
+#endif\r
+ break;\r
+\r
+#if BUILD_Y8950\r
+ case 0x09: /* START ADD */\r
+ case 0x0a:\r
+ case 0x0b: /* STOP ADD */\r
+ case 0x0c:\r
+ case 0x0d: /* PRESCALE */\r
+ case 0x0e:\r
+ case 0x0f: /* ADPCM data write */\r
+ case 0x10: /* DELTA-N */\r
+ case 0x11: /* DELTA-N */\r
+ case 0x12: /* ADPCM volume */\r
+ if(OPL->type&OPL_TYPE_ADPCM)\r
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);\r
+ break;\r
+\r
+ case 0x15: /* DAC data high 8 bits (F7,F6...F2) */\r
+ case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */\r
+ case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */\r
+#ifdef _DEBUG\r
+ logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v);\r
+#endif\r
+ break;\r
+\r
+ case 0x18: /* I/O CTRL (Direction) */\r
+ if(OPL->type&OPL_TYPE_IO)\r
+ OPL->portDirection = v&0x0f;\r
+ break;\r
+ case 0x19: /* I/O DATA */\r
+ if(OPL->type&OPL_TYPE_IO)\r
+ {\r
+ OPL->portLatch = v;\r
+ if(OPL->porthandler_w)\r
+ OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);\r
+ }\r
+ break;\r
+#endif\r
+ default:\r
+#ifdef _DEBUG\r
+ logerror("FMOPL.C: write to unknown register: %02x\n",r);\r
+#endif\r
+ break;\r
+ }\r
+ break;\r
+ case 0x20: /* am ON, vib ON, ksr, eg_type, mul */\r
+ slot = slot_array[r&0x1f];\r
+ if(slot < 0) return;\r
+ set_mul(OPL,slot,v);\r
+ break;\r
+ case 0x40:\r
+ slot = slot_array[r&0x1f];\r
+ if(slot < 0) return;\r
+ set_ksl_tl(OPL,slot,v);\r
+ break;\r
+ case 0x60:\r
+ slot = slot_array[r&0x1f];\r
+ if(slot < 0) return;\r
+ set_ar_dr(OPL,slot,v);\r
+ break;\r
+ case 0x80:\r
+ slot = slot_array[r&0x1f];\r
+ if(slot < 0) return;\r
+ set_sl_rr(OPL,slot,v);\r
+ break;\r
+ case 0xa0:\r
+ if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */\r
+ {\r
+ OPL->lfo_am_depth = v & 0x80;\r
+ OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0;\r
+\r
+ OPL->rhythm = v&0x3f;\r
+\r
+ if(OPL->rhythm&0x20)\r
+ {\r
+ /* BD key on/off */\r
+ if(v&0x10)\r
+ {\r
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2);\r
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2);\r
+ }\r
+ else\r
+ {\r
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);\r
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);\r
+ }\r
+ /* HH key on/off */\r
+ if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2);\r
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);\r
+ /* SD key on/off */\r
+ if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2);\r
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);\r
+ /* TOM key on/off */\r
+ if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2);\r
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);\r
+ /* TOP-CY key on/off */\r
+ if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2);\r
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);\r
+ }\r
+ else\r
+ {\r
+ /* BD key off */\r
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);\r
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);\r
+ /* HH key off */\r
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);\r
+ /* SD key off */\r
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);\r
+ /* TOM key off */\r
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);\r
+ /* TOP-CY off */\r
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);\r
+ }\r
+ return;\r
+ }\r
+ /* keyon,block,fnum */\r
+ if( (r&0x0f) > 8) return;\r
+ CH = &OPL->P_CH[r&0x0f];\r
+ if(!(r&0x10))\r
+ { /* a0-a8 */\r
+ block_fnum = (CH->block_fnum&0x1f00) | v;\r
+ }\r
+ else\r
+ { /* b0-b8 */\r
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);\r
+\r
+ if(v&0x20)\r
+ {\r
+ FM_KEYON (&CH->SLOT[SLOT1], 1);\r
+ FM_KEYON (&CH->SLOT[SLOT2], 1);\r
+ }\r
+ else\r
+ {\r
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);\r
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);\r
+ }\r
+ }\r
+ /* update */\r
+ if(CH->block_fnum != block_fnum)\r
+ {\r
+ UINT8 block = block_fnum >> 10;\r
+\r
+ CH->block_fnum = block_fnum;\r
+\r
+ CH->ksl_base = ksl_tab[block_fnum>>6];\r
+ CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block);\r
+\r
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */\r
+ CH->kcode = (CH->block_fnum&0x1c00)>>9;\r
+\r
+ /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */\r
+ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */\r
+ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */\r
+ if (OPL->mode&0x40)\r
+ CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */\r
+ else\r
+ CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */\r
+\r
+ /* refresh Total Level in both SLOTs of this channel */\r
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);\r
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);\r
+\r
+ /* refresh frequency counter in both SLOTs of this channel */\r
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);\r
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);\r
+ }\r
+ break;\r
+ case 0xc0:\r
+ /* FB,C */\r
+ if( (r&0x0f) > 8) return;\r
+ CH = &OPL->P_CH[r&0x0f];\r
+ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;\r
+ CH->SLOT[SLOT1].CON = v&1;\r
+ CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation;\r
+ break;\r
+ case 0xe0: /* waveform select */\r
+ /* simply ignore write to the waveform select register if selecting not enabled in test register */\r
+ if(OPL->wavesel)\r
+ {\r
+ slot = slot_array[r&0x1f];\r
+ if(slot < 0) return;\r
+ CH = &OPL->P_CH[slot/2];\r
+\r
+ CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN;\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+/*static TIMER_CALLBACK( cymfile_callback )\r
+{\r
+ if (cymfile)\r
+ {\r
+ fputc( (unsigned char)0, cymfile );\r
+ }\r
+}*/\r
+\r
+/* lock/unlock for common table */\r
+static int OPL_LockTable(void)\r
+{\r
+ num_lock++;\r
+ if(num_lock>1) return 0;\r
+\r
+ /* first time */\r
+\r
+ /* allocate total level table (128kb space) */\r
+ if( !init_tables() )\r
+ {\r
+ num_lock--;\r
+ return -1;\r
+ }\r
+\r
+ /*if (LOG_CYM_FILE)\r
+ {\r
+ cymfile = fopen("3812_.cym","wb");\r
+ if (cymfile)\r
+ timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); //110 Hz pulse timer\r
+ else\r
+ logerror("Could not create file 3812_.cym\n");\r
+ }*/\r
+\r
+ return 0;\r
+}\r
+\r
+static void OPL_UnLockTable(void)\r
+{\r
+ if(num_lock) num_lock--;\r
+ if(num_lock) return;\r
+\r
+ /* last time */\r
+\r
+ OPLCloseTable();\r
+\r
+ /*if (cymfile)\r
+ fclose (cymfile);\r
+ cymfile = NULL;*/\r
+}\r
+\r
+static void OPLResetChip(FM_OPL *OPL)\r
+{\r
+ int c,s;\r
+ int i;\r
+\r
+ OPL->eg_timer = 0;\r
+ OPL->eg_cnt = 0;\r
+\r
+ OPL->noise_rng = 1; /* noise shift register */\r
+ OPL->mode = 0; /* normal mode */\r
+ OPL_STATUS_RESET(OPL,0x7f);\r
+\r
+ /* reset with register write */\r
+ OPLWriteReg(OPL,0x01,0); /* wavesel disable */\r
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */\r
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */\r
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */\r
+ OPL->Trem[0] = OPL->Trem[1] = 0;\r
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);\r
+\r
+ /* reset operator parameters */\r
+ for( c = 0 ; c < 9 ; c++ )\r
+ {\r
+ OPL_CH *CH = &OPL->P_CH[c];\r
+ for(s = 0 ; s < 2 ; s++ )\r
+ {\r
+ /* wave table */\r
+ CH->SLOT[s].wavetable = 0;\r
+ CH->SLOT[s].state = EG_OFF;\r
+ CH->SLOT[s].volume = MAX_ATT_INDEX;\r
+ }\r
+ }\r
+#if BUILD_Y8950\r
+ if(OPL->type&OPL_TYPE_ADPCM)\r
+ {\r
+ YM_DELTAT *DELTAT = OPL->deltat;\r
+\r
+ DELTAT->freqbase = OPL->freqbase;\r
+ DELTAT->output_pointer = &OPL->output_deltat[0];\r
+ DELTAT->portshift = 5;\r
+ DELTAT->output_range = 1<<23;\r
+ YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL);\r
+ }\r
+#endif\r
+}\r
+\r
+\r
+#if 0\r
+//static STATE_POSTLOAD( OPL_postload )\r
+static void OPL_postload(void* param)\r
+{\r
+ FM_OPL *OPL = (FM_OPL *)param;\r
+ int slot, ch;\r
+\r
+ for( ch=0 ; ch < 9 ; ch++ )\r
+ {\r
+ OPL_CH *CH = &OPL->P_CH[ch];\r
+\r
+ /* Look up key scale level */\r
+ UINT32 block_fnum = CH->block_fnum;\r
+ CH->ksl_base = ksl_tab[block_fnum >> 6];\r
+ CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10));\r
+\r
+ for( slot=0 ; slot < 2 ; slot++ )\r
+ {\r
+ OPL_SLOT *SLOT = &CH->SLOT[slot];\r
+\r
+ /* Calculate key scale rate */\r
+ SLOT->ksr = CH->kcode >> SLOT->KSR;\r
+\r
+ /* Calculate attack, decay and release rates */\r
+ if ((SLOT->ar + SLOT->ksr) < 16+62)\r
+ {\r
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];\r
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];\r
+ }\r
+ else\r
+ {\r
+ SLOT->eg_sh_ar = 0;\r
+ SLOT->eg_sel_ar = 13*RATE_STEPS;\r
+ }\r
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];\r
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];\r
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];\r
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];\r
+\r
+ /* Calculate phase increment */\r
+ SLOT->Incr = CH->fc * SLOT->mul;\r
+\r
+ /* Total level */\r
+ SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);\r
+\r
+ /* Connect output */\r
+ SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation;\r
+ }\r
+ }\r
+#if BUILD_Y8950\r
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )\r
+ {\r
+ // We really should call the postlod function for the YM_DELTAT, but it's hard without registers\r
+ // (see the way the YM2610 does it)\r
+ //YM_DELTAT_postload(OPL->deltat, REGS);\r
+ }\r
+#endif\r
+}\r
+\r
+\r
+static void OPLsave_state_channel(OPL_CH *CH)\r
+{\r
+ int slot, ch;\r
+\r
+ for( ch=0 ; ch < 9 ; ch++, CH++ )\r
+ {\r
+ // channel \r
+ state_save_register_device_item(device, ch, CH->block_fnum);\r
+ state_save_register_device_item(device, ch, CH->kcode);\r
+ // slots \r
+ for( slot=0 ; slot < 2 ; slot++ )\r
+ {\r
+ OPL_SLOT *SLOT = &CH->SLOT[slot];\r
+\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->ar);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->dr);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->rr);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->KSR);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->ksl);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->mul);\r
+\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->Cnt);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->FB);\r
+ state_save_register_device_item_array(device, ch * 2 + slot, SLOT->op1_out);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->CON);\r
+\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->eg_type);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->state);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->TL);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->volume);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->sl);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->key);\r
+\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->AMmask);\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->vib);\r
+\r
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->wavetable);\r
+ }\r
+ }\r
+}\r
+#endif\r
+\r
+\r
+/* Register savestate for a virtual YM3812/YM3526/Y8950 */\r
+\r
+/*static void OPL_save_state(FM_OPL *OPL)\r
+{\r
+ OPLsave_state_channel(device, OPL->P_CH);\r
+\r
+ state_save_register_device_item(device, 0, OPL->eg_cnt);\r
+ state_save_register_device_item(device, 0, OPL->eg_timer);\r
+\r
+ state_save_register_device_item(device, 0, OPL->rhythm);\r
+\r
+ state_save_register_device_item(device, 0, OPL->lfo_am_depth);\r
+ state_save_register_device_item(device, 0, OPL->lfo_pm_depth_range);\r
+ state_save_register_device_item(device, 0, OPL->lfo_am_cnt);\r
+ state_save_register_device_item(device, 0, OPL->lfo_pm_cnt);\r
+\r
+ state_save_register_device_item(device, 0, OPL->noise_rng);\r
+ state_save_register_device_item(device, 0, OPL->noise_p);\r
+\r
+ if( OPL->type & OPL_TYPE_WAVESEL )\r
+ {\r
+ state_save_register_device_item(device, 0, OPL->wavesel);\r
+ }\r
+\r
+ state_save_register_device_item_array(device, 0, OPL->T);\r
+ state_save_register_device_item_array(device, 0, OPL->st);\r
+\r
+#if BUILD_Y8950\r
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )\r
+ {\r
+ YM_DELTAT_savestate(device, OPL->deltat);\r
+ }\r
+\r
+ if ( OPL->type & OPL_TYPE_IO )\r
+ {\r
+ state_save_register_device_item(device, 0, OPL->portDirection);\r
+ state_save_register_device_item(device, 0, OPL->portLatch);\r
+ }\r
+#endif\r
+\r
+ state_save_register_device_item(device, 0, OPL->address);\r
+ state_save_register_device_item(device, 0, OPL->status);\r
+ state_save_register_device_item(device, 0, OPL->statusmask);\r
+ state_save_register_device_item(device, 0, OPL->mode);\r
+\r
+ state_save_register_postload(device->machine, OPL_postload, OPL);\r
+}*/\r
+\r
+\r
+/* Create one of virtual YM3812/YM3526/Y8950 */\r
+/* 'clock' is chip clock in Hz */\r
+/* 'rate' is sampling rate */\r
+static FM_OPL *OPLCreate(UINT32 clock, UINT32 rate, int type)\r
+{\r
+ char *ptr;\r
+ FM_OPL *OPL;\r
+ int state_size;\r
+\r
+ if (OPL_LockTable() == -1) return NULL;\r
+\r
+ /* calculate OPL state size */\r
+ state_size = sizeof(FM_OPL);\r
+\r
+#if BUILD_Y8950\r
+ if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);\r
+#endif\r
+\r
+ /* allocate memory block */\r
+ ptr = (char *)malloc(state_size);\r
+\r
+ if (ptr==NULL)\r
+ return NULL;\r
+\r
+ /* clear */\r
+ memset(ptr,0,state_size);\r
+\r
+ OPL = (FM_OPL *)ptr;\r
+\r
+ ptr += sizeof(FM_OPL);\r
+\r
+#if BUILD_Y8950\r
+ if (type&OPL_TYPE_ADPCM)\r
+ {\r
+ OPL->deltat = (YM_DELTAT *)ptr;\r
+ }\r
+ ptr += sizeof(YM_DELTAT);\r
+#endif\r
+\r
+ OPL->type = type;\r
+ OPL->clock = clock;\r
+ OPL->rate = rate;\r
+\r
+ /* init global tables */\r
+ OPL_initalize(OPL);\r
+\r
+ return OPL;\r
+}\r
+\r
+/* Destroy one of virtual YM3812 */\r
+static void OPLDestroy(FM_OPL *OPL)\r
+{\r
+ OPL_UnLockTable();\r
+ free(OPL);\r
+}\r
+\r
+/* Optional handlers */\r
+\r
+static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param)\r
+{\r
+ OPL->timer_handler = timer_handler;\r
+ OPL->TimerParam = param;\r
+}\r
+static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param)\r
+{\r
+ OPL->IRQHandler = IRQHandler;\r
+ OPL->IRQParam = param;\r
+}\r
+static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param)\r
+{\r
+ OPL->UpdateHandler = UpdateHandler;\r
+ OPL->UpdateParam = param;\r
+}\r
+\r
+static int OPLWrite(FM_OPL *OPL,int a,int v)\r
+{\r
+ if( !(a&1) )\r
+ { /* address port */\r
+ OPL->address = v & 0xff;\r
+ }\r
+ else\r
+ { /* data port */\r
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam/*,0*/);\r
+ OPLWriteReg(OPL,OPL->address,v);\r
+ }\r
+ return OPL->status>>7;\r
+}\r
+\r
+static unsigned char OPLRead(FM_OPL *OPL,int a)\r
+{\r
+ if( !(a&1) )\r
+ {\r
+ /* status port */\r
+\r
+ #if BUILD_Y8950\r
+\r
+ if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */\r
+ {\r
+ return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1);\r
+ }\r
+\r
+ #endif\r
+\r
+ /* OPL and OPL2 */\r
+ return OPL->status & (OPL->statusmask|0x80);\r
+ }\r
+\r
+#if BUILD_Y8950\r
+ /* data port */\r
+ switch(OPL->address)\r
+ {\r
+ case 0x05: /* KeyBoard IN */\r
+ if(OPL->type&OPL_TYPE_KEYBOARD)\r
+ {\r
+ if(OPL->keyboardhandler_r)\r
+ return OPL->keyboardhandler_r(OPL->keyboard_param);\r
+#ifdef _DEBUG\r
+ else\r
+ logerror("Y8950: read unmapped KEYBOARD port\n");\r
+#endif\r
+ }\r
+ return 0;\r
+\r
+ case 0x0f: /* ADPCM-DATA */\r
+ if(OPL->type&OPL_TYPE_ADPCM)\r
+ {\r
+ UINT8 val;\r
+\r
+ val = YM_DELTAT_ADPCM_Read(OPL->deltat);\r
+ /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/\r
+ return val;\r
+ }\r
+ return 0;\r
+\r
+ case 0x19: /* I/O DATA */\r
+ if(OPL->type&OPL_TYPE_IO)\r
+ {\r
+ if(OPL->porthandler_r)\r
+ return OPL->porthandler_r(OPL->port_param);\r
+#ifdef _DEBUG\r
+ else\r
+ logerror("Y8950:read unmapped I/O port\n");\r
+#endif\r
+ }\r
+ return 0;\r
+ case 0x1a: /* PCM-DATA */\r
+ if(OPL->type&OPL_TYPE_ADPCM)\r
+ {\r
+#ifdef _DEBUG\r
+ logerror("Y8950 A/D convertion is accessed but not implemented !\n");\r
+#endif\r
+ return 0x80; /* 2's complement PCM data - result from A/D convertion */\r
+ }\r
+ return 0;\r
+ }\r
+#endif\r
+\r
+ return 0xff;\r
+}\r
+\r
+/* CSM Key Controll */\r
+INLINE void CSMKeyControll(OPL_CH *CH)\r
+{\r
+ FM_KEYON (&CH->SLOT[SLOT1], 4);\r
+ FM_KEYON (&CH->SLOT[SLOT2], 4);\r
+\r
+ /* The key off should happen exactly one sample later - not implemented correctly yet */\r
+\r
+ FM_KEYOFF(&CH->SLOT[SLOT1], ~4);\r
+ FM_KEYOFF(&CH->SLOT[SLOT2], ~4);\r
+}\r
+\r
+\r
+static int OPLTimerOver(FM_OPL *OPL,int c)\r
+{\r
+ if( c )\r
+ { /* Timer B */\r
+ OPL_STATUS_SET(OPL,0x20);\r
+ }\r
+ else\r
+ { /* Timer A */\r
+ OPL_STATUS_SET(OPL,0x40);\r
+ /* CSM mode key,TL controll */\r
+ if( OPL->mode & 0x80 )\r
+ { /* CSM mode total level latch and auto key on */\r
+ int ch;\r
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam/*,0*/);\r
+ for(ch=0; ch<9; ch++)\r
+ CSMKeyControll( &OPL->P_CH[ch] );\r
+ }\r
+ }\r
+ /* reload timer */\r
+ OPL->Trem[c] = OPL->T[c];\r
+ //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,attotime_mul(OPL->TimerBase, OPL->T[c]));\r
+ return OPL->status>>7;\r
+}\r
+\r
+\r
+#define MAX_OPL_CHIPS 2\r
+\r
+\r
+#if (BUILD_YM3812)\r
+\r
+void * ym3812_init(UINT32 clock, UINT32 rate)\r
+{\r
+ /* emulator create */\r
+ FM_OPL *YM3812 = OPLCreate(clock,rate,OPL_TYPE_YM3812);\r
+ if (YM3812)\r
+ {\r
+ //OPL_save_state(YM3812);\r
+ ym3812_reset_chip(YM3812);\r
+ }\r
+ return YM3812;\r
+}\r
+\r
+void ym3812_shutdown(void *chip)\r
+{\r
+ FM_OPL *YM3812 = (FM_OPL *)chip;\r
+ /* emulator shutdown */\r
+ OPLDestroy(YM3812);\r
+}\r
+void ym3812_reset_chip(void *chip)\r
+{\r
+ FM_OPL *YM3812 = (FM_OPL *)chip;\r
+ OPLResetChip(YM3812);\r
+}\r
+\r
+int ym3812_write(void *chip, int a, int v)\r
+{\r
+ FM_OPL *YM3812 = (FM_OPL *)chip;\r
+ return OPLWrite(YM3812, a, v);\r
+}\r
+\r
+unsigned char ym3812_read(void *chip, int a)\r
+{\r
+ FM_OPL *YM3812 = (FM_OPL *)chip;\r
+ /* YM3812 always returns bit2 and bit1 in HIGH state */\r
+ return OPLRead(YM3812, a) | 0x06 ;\r
+}\r
+int ym3812_timer_over(void *chip, int c)\r
+{\r
+ FM_OPL *YM3812 = (FM_OPL *)chip;\r
+ return OPLTimerOver(YM3812, c);\r
+}\r
+\r
+void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)\r
+{\r
+ FM_OPL *YM3812 = (FM_OPL *)chip;\r
+ OPLSetTimerHandler(YM3812, timer_handler, param);\r
+}\r
+void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)\r
+{\r
+ FM_OPL *YM3812 = (FM_OPL *)chip;\r
+ OPLSetIRQHandler(YM3812, IRQHandler, param);\r
+}\r
+void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)\r
+{\r
+ FM_OPL *YM3812 = (FM_OPL *)chip;\r
+ OPLSetUpdateHandler(YM3812, UpdateHandler, param);\r
+}\r
+\r
+\r
+/*\r
+** Generate samples for one of the YM3812's\r
+**\r
+** 'which' is the virtual YM3812 number\r
+** '*buffer' is the output buffer pointer\r
+** 'length' is the number of samples that should be generated\r
+*/\r
+void ym3812_update_one(void *chip, OPLSAMPLE **buffer, int length)\r
+{\r
+ FM_OPL *OPL = (FM_OPL *)chip;\r
+ UINT8 rhythm = OPL->rhythm&0x20;\r
+ OPLSAMPLE *bufL = buffer[0];\r
+ OPLSAMPLE *bufR = buffer[1];\r
+ int i;\r
+\r
+ if (! length)\r
+ {\r
+ refresh_eg(OPL);\r
+ return;\r
+ }\r
+ \r
+ for( i=0; i < length ; i++ )\r
+ {\r
+ int lt;\r
+\r
+ OPL->output[0] = 0;\r
+\r
+ advance_lfo(OPL);\r
+\r
+ /* FM part */\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);\r
+\r
+ if(!rhythm)\r
+ {\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);\r
+ }\r
+ else /* Rhythm part */\r
+ {\r
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );\r
+ }\r
+\r
+ lt = OPL->output[0];\r
+\r
+ lt >>= FINAL_SH;\r
+\r
+ /* limit check */\r
+ //lt = limit( lt , MAXOUT, MINOUT );\r
+\r
+ #ifdef SAVE_SAMPLE\r
+ if (which==0)\r
+ {\r
+ SAVE_ALL_CHANNELS\r
+ }\r
+ #endif\r
+\r
+ /* store to sound buffer */\r
+ bufL[i] = lt;\r
+ bufR[i] = lt;\r
+\r
+ advance(OPL);\r
+ }\r
+\r
+}\r
+#endif /* BUILD_YM3812 */\r
+\r
+\r
+\r
+#if (BUILD_YM3526)\r
+\r
+void *ym3526_init(UINT32 clock, UINT32 rate)\r
+{\r
+ /* emulator create */\r
+ FM_OPL *YM3526 = OPLCreate(clock,rate,OPL_TYPE_YM3526);\r
+ if (YM3526)\r
+ {\r
+ //OPL_save_state(YM3526);\r
+ ym3526_reset_chip(YM3526);\r
+ }\r
+ return YM3526;\r
+}\r
+\r
+void ym3526_shutdown(void *chip)\r
+{\r
+ FM_OPL *YM3526 = (FM_OPL *)chip;\r
+ /* emulator shutdown */\r
+ OPLDestroy(YM3526);\r
+}\r
+void ym3526_reset_chip(void *chip)\r
+{\r
+ FM_OPL *YM3526 = (FM_OPL *)chip;\r
+ OPLResetChip(YM3526);\r
+}\r
+\r
+int ym3526_write(void *chip, int a, int v)\r
+{\r
+ FM_OPL *YM3526 = (FM_OPL *)chip;\r
+ return OPLWrite(YM3526, a, v);\r
+}\r
+\r
+unsigned char ym3526_read(void *chip, int a)\r
+{\r
+ FM_OPL *YM3526 = (FM_OPL *)chip;\r
+ /* YM3526 always returns bit2 and bit1 in HIGH state */\r
+ return OPLRead(YM3526, a) | 0x06 ;\r
+}\r
+int ym3526_timer_over(void *chip, int c)\r
+{\r
+ FM_OPL *YM3526 = (FM_OPL *)chip;\r
+ return OPLTimerOver(YM3526, c);\r
+}\r
+\r
+void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)\r
+{\r
+ FM_OPL *YM3526 = (FM_OPL *)chip;\r
+ OPLSetTimerHandler(YM3526, timer_handler, param);\r
+}\r
+void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)\r
+{\r
+ FM_OPL *YM3526 = (FM_OPL *)chip;\r
+ OPLSetIRQHandler(YM3526, IRQHandler, param);\r
+}\r
+void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)\r
+{\r
+ FM_OPL *YM3526 = (FM_OPL *)chip;\r
+ OPLSetUpdateHandler(YM3526, UpdateHandler, param);\r
+}\r
+\r
+\r
+/*\r
+** Generate samples for one of the YM3526's\r
+**\r
+** 'which' is the virtual YM3526 number\r
+** '*buffer' is the output buffer pointer\r
+** 'length' is the number of samples that should be generated\r
+*/\r
+void ym3526_update_one(void *chip, OPLSAMPLE **buffer, int length)\r
+{\r
+ FM_OPL *OPL = (FM_OPL *)chip;\r
+ UINT8 rhythm = OPL->rhythm&0x20;\r
+ OPLSAMPLE *bufL = buffer[0];\r
+ OPLSAMPLE *bufR = buffer[1];\r
+ int i;\r
+\r
+ for( i=0; i < length ; i++ )\r
+ {\r
+ int lt;\r
+\r
+ OPL->output[0] = 0;\r
+\r
+ advance_lfo(OPL);\r
+\r
+ /* FM part */\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);\r
+\r
+ if(!rhythm)\r
+ {\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);\r
+ }\r
+ else /* Rhythm part */\r
+ {\r
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );\r
+ }\r
+\r
+ lt = OPL->output[0];\r
+\r
+ lt >>= FINAL_SH;\r
+\r
+ /* limit check */\r
+ //lt = limit( lt , MAXOUT, MINOUT );\r
+\r
+ #ifdef SAVE_SAMPLE\r
+ if (which==0)\r
+ {\r
+ SAVE_ALL_CHANNELS\r
+ }\r
+ #endif\r
+\r
+ /* store to sound buffer */\r
+ bufL[i] = lt;\r
+ bufR[i] = lt;\r
+\r
+ advance(OPL);\r
+ }\r
+\r
+}\r
+#endif /* BUILD_YM3526 */\r
+\r
+\r
+\r
+\r
+#if BUILD_Y8950\r
+\r
+static void Y8950_deltat_status_set(void *chip, UINT8 changebits)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ OPL_STATUS_SET(Y8950, changebits);\r
+}\r
+static void Y8950_deltat_status_reset(void *chip, UINT8 changebits)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ OPL_STATUS_RESET(Y8950, changebits);\r
+}\r
+\r
+void *y8950_init(UINT32 clock, UINT32 rate)\r
+{\r
+ /* emulator create */\r
+ FM_OPL *Y8950 = OPLCreate(clock,rate,OPL_TYPE_Y8950);\r
+ if (Y8950)\r
+ {\r
+ Y8950->deltat->status_set_handler = Y8950_deltat_status_set;\r
+ Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset;\r
+ Y8950->deltat->status_change_which_chip = Y8950;\r
+ Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */\r
+ Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */\r
+\r
+ /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */\r
+ /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */\r
+ /* reset */\r
+ //OPL_save_state(Y8950);\r
+ y8950_reset_chip(Y8950);\r
+ }\r
+\r
+ return Y8950;\r
+}\r
+\r
+void y8950_shutdown(void *chip)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ \r
+ free(Y8950->deltat->memory); Y8950->deltat->memory = NULL;\r
+ \r
+ /* emulator shutdown */\r
+ OPLDestroy(Y8950);\r
+}\r
+void y8950_reset_chip(void *chip)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ OPLResetChip(Y8950);\r
+}\r
+\r
+int y8950_write(void *chip, int a, int v)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ return OPLWrite(Y8950, a, v);\r
+}\r
+\r
+unsigned char y8950_read(void *chip, int a)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ return OPLRead(Y8950, a);\r
+}\r
+int y8950_timer_over(void *chip, int c)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ return OPLTimerOver(Y8950, c);\r
+}\r
+\r
+void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ OPLSetTimerHandler(Y8950, timer_handler, param);\r
+}\r
+void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ OPLSetIRQHandler(Y8950, IRQHandler, param);\r
+}\r
+void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ OPLSetUpdateHandler(Y8950, UpdateHandler, param);\r
+}\r
+\r
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size )\r
+{\r
+ FM_OPL *OPL = (FM_OPL *)chip;\r
+ OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr);\r
+ OPL->deltat->memory_size = deltat_mem_size;\r
+}\r
+\r
+void y8950_write_pcmrom(void *chip, offs_t ROMSize, offs_t DataStart,\r
+ offs_t DataLength, const UINT8* ROMData)\r
+{\r
+ FM_OPL *Y8950 = (FM_OPL *)chip;\r
+ \r
+ if (Y8950->deltat->memory_size != ROMSize)\r
+ {\r
+ Y8950->deltat->memory = (UINT8*)realloc(Y8950->deltat->memory, ROMSize);\r
+ Y8950->deltat->memory_size = ROMSize;\r
+ memset(Y8950->deltat->memory, 0xFF, ROMSize);\r
+ YM_DELTAT_calc_mem_mask(Y8950->deltat);\r
+ }\r
+ if (DataStart > ROMSize)\r
+ return;\r
+ if (DataStart + DataLength > ROMSize)\r
+ DataLength = ROMSize - DataStart;\r
+ \r
+ memcpy(Y8950->deltat->memory + DataStart, ROMData, DataLength);\r
+ \r
+ return;\r
+}\r
+\r
+/*\r
+** Generate samples for one of the Y8950's\r
+**\r
+** 'which' is the virtual Y8950 number\r
+** '*buffer' is the output buffer pointer\r
+** 'length' is the number of samples that should be generated\r
+*/\r
+void y8950_update_one(void *chip, OPLSAMPLE **buffer, int length)\r
+{\r
+ int i;\r
+ FM_OPL *OPL = (FM_OPL *)chip;\r
+ UINT8 rhythm = OPL->rhythm&0x20;\r
+ YM_DELTAT *DELTAT = OPL->deltat;\r
+ OPLSAMPLE *bufL = buffer[0];\r
+ OPLSAMPLE *bufR = buffer[1];\r
+\r
+ for( i=0; i < length ; i++ )\r
+ {\r
+ int lt;\r
+\r
+ OPL->output[0] = 0;\r
+ OPL->output_deltat[0] = 0;\r
+\r
+ advance_lfo(OPL);\r
+\r
+ /* deltaT ADPCM */\r
+ if( DELTAT->portstate&0x80 && ! OPL->MuteSpc[5] )\r
+ YM_DELTAT_ADPCM_CALC(DELTAT);\r
+\r
+ /* FM part */\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);\r
+\r
+ if(!rhythm)\r
+ {\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);\r
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);\r
+ }\r
+ else /* Rhythm part */\r
+ {\r
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );\r
+ }\r
+\r
+ lt = OPL->output[0] + (OPL->output_deltat[0]>>11);\r
+\r
+ lt >>= FINAL_SH;\r
+\r
+ /* limit check */\r
+ //lt = limit( lt , MAXOUT, MINOUT );\r
+\r
+ #ifdef SAVE_SAMPLE\r
+ if (which==0)\r
+ {\r
+ SAVE_ALL_CHANNELS\r
+ }\r
+ #endif\r
+\r
+ /* store to sound buffer */\r
+ bufL[i] = lt;\r
+ bufR[i] = lt;\r
+\r
+ advance(OPL);\r
+ }\r
+\r
+}\r
+\r
+void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,void * param)\r
+{\r
+ FM_OPL *OPL = (FM_OPL *)chip;\r
+ OPL->porthandler_w = PortHandler_w;\r
+ OPL->porthandler_r = PortHandler_r;\r
+ OPL->port_param = param;\r
+}\r
+\r
+void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,void * param)\r
+{\r
+ FM_OPL *OPL = (FM_OPL *)chip;\r
+ OPL->keyboardhandler_w = KeyboardHandler_w;\r
+ OPL->keyboardhandler_r = KeyboardHandler_r;\r
+ OPL->keyboard_param = param;\r
+}\r
+\r
+#endif\r
+\r
+void opl_set_mute_mask(void *chip, UINT32 MuteMask)\r
+{\r
+ FM_OPL *opl = (FM_OPL *)chip;\r
+ UINT8 CurChn;\r
+ \r
+ for (CurChn = 0; CurChn < 9; CurChn ++)\r
+ opl->P_CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01;\r
+ for (CurChn = 0; CurChn < 6; CurChn ++)\r
+ opl->MuteSpc[CurChn] = (MuteMask >> (9 + CurChn)) & 0x01;\r
+ \r
+ return;\r
+}\r
--- /dev/null
+// vgmSndDrv.c - VGM Sound Driver for OPL2\r
+// Valley Bell, 2015-07-27\r
+\r
+// Note: This uses quite a few optimizations that assume that your\r
+// machine is Little Endian.\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include <common.h>\r
+#include "vgmSndDrv.h"\r
+\r
+\r
+#define QUICK_READ\r
+\r
+\r
+\r
+#define FCC_VGM 0x206D6756 // 'Vgm '\r
+#define FCC_GD3 0x20336447 // 'Gd3 '\r
+\r
+typedef struct _vgm_file_header_base\r
+{\r
+ UINT32 fccVGM; // 00\r
+ UINT32 lngEOFOffset; // 04\r
+ UINT32 lngVersion; // 08\r
+ UINT32 lngSkip1[2]; // 0C\r
+ UINT32 lngGD3Offset; // 14\r
+ UINT32 lngTotalSamples; // 18\r
+ UINT32 lngLoopOffset; // 1C\r
+ UINT32 lngLoopSamples; // 20\r
+ UINT32 lngRate; // 24\r
+ UINT32 lngSkip2[3]; // 28\r
+ UINT32 lngDataOffset; // 34\r
+ UINT32 lngSkip3[2]; // 38\r
+} VGM_BASE_HDR;\r
+\r
+#define PBMODE_MUSIC 0x00\r
+#define PBMODE_SFX 0x01\r
+typedef struct _vgm_playback\r
+{\r
+ UINT8 pbMode;\r
+ UINT8 vgmEnd; // 00 - running, 01 - finished, FF - not loaded\r
+ UINT16 curLoopCnt;\r
+ UINT32 vgmPos;\r
+ UINT32 vgmSmplPos;\r
+ UINT32 pbSmplPos;\r
+ VGM_FILE* file;\r
+ \r
+ // oplChnMask:\r
+ // Music: mask of channels used/overridden by SFX\r
+ // SFX: ID of channel used by SFX (all commands are forces to it)\r
+ UINT16 oplChnMask;\r
+ UINT8* oplRegCache;\r
+ UINT8 workRAM[0x04];\r
+} VGM_PBK;\r
+\r
+\r
+\r
+INLINE UINT16 ReadLE16(const UINT8* buffer)\r
+{\r
+#ifdef QUICK_READ\r
+ return *(UINT16*)buffer;\r
+#else\r
+ return (buffer[0x00] << 0) | (buffer[0x01] << 8);\r
+#endif\r
+}\r
+\r
+INLINE UINT32 ReadLE32(const UINT8* buffer)\r
+{\r
+#ifdef QUICK_READ\r
+ return *(UINT32*)buffer;\r
+#else\r
+ return (buffer[0x00] << 0) | (buffer[0x01] << 8) |\r
+ (buffer[0x02] << 16) | (buffer[0x03] << 24);\r
+#endif\r
+}\r
+\r
+\r
+// Function Prototypes\r
+//UINT8 OpenVGMFile(const char* FileName, VGM_FILE* vgmFile);\r
+//void FreeVGMFile(VGM_FILE* vgmFile);\r
+\r
+static bool DoVgmLoop(VGM_PBK* vgmPlay);\r
+static void UpdateVGM(VGM_PBK* vgmPlay, UINT16 Samples);\r
+\r
+//void InitEngine(void);\r
+//void DeinitEngine(void);\r
+\r
+//UINT8 PlayMusic(VGM_FILE* vgmFile);\r
+//UINT8 PlaySFX(VGM_FILE* vgmFile, UINT8 sfxChnID);\r
+//UINT8 StopMusic(void);\r
+//UINT8 StopSFX(UINT8 sfxChnID); // Note: sfxChnID == 0xFF -> stop all SFX\r
+//UINT8 PauseMusic(void);\r
+//UINT8 ResumeMusic(void);\r
+static void StartPlayback(VGM_PBK* vgmPb);\r
+static void StopPlayback(VGM_PBK* vgmPb);\r
+\r
+static void ym2413_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data);\r
+static void ym3812_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data);\r
+static void ym3512_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data);\r
+static void ymf262_write(VGM_PBK* vgmPb, UINT8 port, UINT8 reg, UINT8 data);\r
+\r
+//void UpdateSoundEngine(void);\r
+\r
+\r
+// Functions that must be supplied by external library\r
+extern void OPL2_Write(UINT8 reg, UINT8 data);\r
+extern UINT8 OPL2_ReadStatus(void);\r
+\r
+\r
+\r
+\r
+#define SFX_CHN_COUNT 6\r
+\r
+#define TIMER1_RATE 7 // 256-7 = 248; (3579545/72/4) / 248 = 50.12 Hz ~ 50 Hz\r
+#define VGM_UPD_RATE 880 // 880 samples (44.1 KHz) = 50.11 Hz\r
+\r
+static VGM_PBK vgmPbMusic;\r
+static VGM_PBK vgmPbSFX[SFX_CHN_COUNT];\r
+\r
+static UINT8 oplRegs_Music[0x100];\r
+static UINT8 oplRegs_SFX[SFX_CHN_COUNT][0x0D]; // 20 23 40 43 60 63 80 83 E0 E3 C0 A0 B0\r
+\r
+static const UINT8 SFX_REGS[0x0D] =\r
+{ 0x20, 0x23, 0x40, 0x43, 0x60, 0x63, 0x80, 0x83,\r
+ 0xE0, 0xE3, 0xC0, 0xA0, 0xB0};\r
+static const UINT8 SFX_REGS_REV[0x10] = // 20/30 -> 0, 40/50 -> 2, ...\r
+{ 0xFF, 0xFF, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04,\r
+ 0x06, 0x06, 0x0B, 0x0C, 0x0A, 0xFF, 0x08, 0x08};\r
+static const UINT8 CHN_OPMASK[0x09] =\r
+{ 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12};\r
+static const UINT8 CHN_OPMASK_REV[0x20] =\r
+{ 0x00, 0x01, 0x02, 0x80, 0x81, 0x82, 0xFF, 0xFF,\r
+ 0x03, 0x04, 0x05, 0x83, 0x84, 0x85, 0xFF, 0xFF,\r
+ 0x06, 0x07, 0x08, 0x86, 0x87, 0x88, 0xFF, 0xFF,\r
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};\r
+\r
+\r
+UINT8 OpenVGMFile(const char* FileName, VGM_FILE* vgmFile)\r
+{\r
+ size_t hdrSize;\r
+ size_t readEl; // 'elements' read from file\r
+ size_t bytesToRead;\r
+ VGM_BASE_HDR vgmBaseHdr;\r
+ FILE* hFile;\r
+ UINT32 CurPos;\r
+ \r
+ memset(vgmFile, 0x00, sizeof(VGM_FILE));\r
+ \r
+ hFile = fopen(FileName, "rb");\r
+ if (hFile == NULL)\r
+ return 0xFF;\r
+ \r
+ hdrSize = sizeof(VGM_BASE_HDR);\r
+ readEl = fread(&vgmBaseHdr, hdrSize, 0x01, hFile);\r
+ if (readEl <= 0)\r
+ {\r
+ fclose(hFile);\r
+ return 0xFE; // read error\r
+ }\r
+ if (vgmBaseHdr.fccVGM != FCC_VGM)\r
+ {\r
+ fclose(hFile);\r
+ return 0x80; // bad signature\r
+ }\r
+ if (vgmBaseHdr.lngVersion < 0x0150)\r
+ {\r
+ fclose(hFile);\r
+ return 0x81; // We don't support VGM v1.10 and earlier\r
+ }\r
+ \r
+ vgmFile->dataLen = vgmBaseHdr.lngEOFOffset + 0x04;\r
+ vgmFile->data = (UINT8*)malloc(vgmFile->dataLen);\r
+ if (vgmFile->data == NULL)\r
+ {\r
+ fclose(hFile);\r
+ return 0xF0; // malloc error\r
+ }\r
+ memcpy(vgmFile->data, &vgmBaseHdr, hdrSize);\r
+ bytesToRead = vgmFile->dataLen - hdrSize;\r
+ readEl = fread(vgmFile->data + hdrSize, 0x01, bytesToRead, hFile);\r
+ if (readEl < bytesToRead)\r
+ {\r
+ //fclose(hFile);\r
+ //return 0xFE; // read error\r
+ vgmFile->dataLen = hdrSize + readEl;\r
+ }\r
+ \r
+ fclose(hFile);\r
+ \r
+ memcpy(&vgmFile->header, vgmFile->data, sizeof(VGM_HEADER));\r
+ \r
+ // relative -> absolute addresses\r
+ vgmFile->header.lngEOFOffset += 0x04;\r
+ if (vgmFile->header.lngGD3Offset)\r
+ vgmFile->header.lngGD3Offset += 0x14;\r
+ if (vgmFile->header.lngLoopOffset)\r
+ vgmFile->header.lngLoopOffset += 0x1C;\r
+ if (! vgmFile->header.lngDataOffset)\r
+ vgmFile->header.lngDataOffset = 0x0C;\r
+ vgmFile->header.lngDataOffset += 0x34;\r
+ \r
+ CurPos = vgmFile->header.lngDataOffset;\r
+ if (vgmFile->header.lngVersion < 0x0150)\r
+ CurPos = 0x40;\r
+ hdrSize = sizeof(VGM_HEADER);\r
+ if (hdrSize > CurPos)\r
+ memset((UINT8*)&vgmFile->header + CurPos, 0x00, hdrSize - CurPos);\r
+ \r
+ fclose(hFile);\r
+ return 0x00;\r
+}\r
+\r
+void FreeVGMFile(VGM_FILE* vgmFile)\r
+{\r
+ free(vgmFile->data); vgmFile->data = NULL;\r
+ vgmFile->dataLen = 0;\r
+ \r
+ return;\r
+}\r
+\r
+\r
+static bool DoVgmLoop(VGM_PBK* vgmPlay)\r
+{\r
+ const VGM_HEADER* vgmHdr = &vgmPlay->file->header;\r
+ \r
+ if (! vgmHdr->lngLoopOffset)\r
+ return false;\r
+ \r
+ vgmPlay->curLoopCnt ++;\r
+ \r
+ vgmPlay->vgmPos = vgmHdr->lngLoopOffset;\r
+ vgmPlay->vgmSmplPos -= vgmHdr->lngLoopSamples;\r
+ vgmPlay->pbSmplPos -= vgmHdr->lngLoopSamples;\r
+ \r
+ return true;\r
+}\r
+\r
+static void UpdateVGM(VGM_PBK* vgmPlay, UINT16 Samples)\r
+{\r
+ const UINT32 vgmLen = vgmPlay->file->dataLen;\r
+ const UINT8* vgmData = vgmPlay->file->data;\r
+ const UINT8* VGMPnt;\r
+ UINT32 VGMPos;\r
+ UINT32 VGMSmplPos;\r
+ UINT8 Command;\r
+ UINT8 blockType;\r
+ UINT32 blockLen;\r
+ \r
+ vgmPlay->pbSmplPos += Samples;\r
+ VGMPos = vgmPlay->vgmPos;\r
+ VGMSmplPos = vgmPlay->vgmSmplPos;\r
+ while(VGMSmplPos < vgmPlay->pbSmplPos && ! vgmPlay->vgmEnd)\r
+ {\r
+ VGMPnt = &vgmData[VGMPos];\r
+ Command = VGMPnt[0x00];\r
+ switch(Command & 0xF0)\r
+ {\r
+ case 0x70: // small delay (1-16 samples)\r
+ VGMSmplPos += (Command & 0x0F) + 0x01;\r
+ VGMPos += 0x01;\r
+ break;\r
+ case 0x80: // DAC write + small delay (0-15 samples)\r
+ VGMSmplPos += (Command & 0x0F);\r
+ VGMPos += 0x01;\r
+ break;\r
+ case 0x60:\r
+ switch(Command)\r
+ {\r
+ case 0x66: // End Of File\r
+ vgmPlay->vgmPos = VGMPos;\r
+ vgmPlay->vgmSmplPos = VGMSmplPos;\r
+ if (! DoVgmLoop(vgmPlay))\r
+ vgmPlay->vgmEnd = 0x01;\r
+ VGMPos = vgmPlay->vgmPos;\r
+ VGMSmplPos = vgmPlay->vgmSmplPos;\r
+ break;\r
+ case 0x62: // 1/60s delay\r
+ VGMSmplPos += 735;\r
+ VGMPos += 0x01;\r
+ break;\r
+ case 0x63: // 1/50s delay\r
+ VGMSmplPos += 882;\r
+ VGMPos += 0x01;\r
+ break;\r
+ case 0x61: // xx Sample Delay\r
+ VGMSmplPos += ReadLE16(&VGMPnt[0x01]);\r
+ VGMPos += 0x03;\r
+ break;\r
+ case 0x67: // Data Block (PCM Data Stream)\r
+ blockType = VGMPnt[0x02];\r
+ blockLen = ReadLE32(&VGMPnt[0x03]);\r
+ blockLen &= 0x7FFFFFFF;\r
+ VGMPos += 0x07 + blockLen;\r
+ break;\r
+ case 0x68: // PCM RAM write\r
+ VGMPos += 0x0C;\r
+ break;\r
+ default:\r
+ vgmPlay->vgmEnd = 0x01;\r
+ break;\r
+ }\r
+ break;\r
+ case 0x50:\r
+ if (Command == 0x50)\r
+ {\r
+ VGMPos += 0x02; // SN76496 write\r
+ break;\r
+ }\r
+ switch(Command)\r
+ {\r
+ case 0x51: // YM2413 write\r
+ ym2413_write(vgmPlay, VGMPnt[0x01], VGMPnt[0x02]);\r
+ break;\r
+ case 0x5A: // YM3812 write\r
+ ym3812_write(vgmPlay, VGMPnt[0x01], VGMPnt[0x02]);\r
+ break;\r
+ case 0x5B: // YM3526 write\r
+ case 0x5C: // Y8950 write\r
+ ym3512_write(vgmPlay, VGMPnt[0x01], VGMPnt[0x02]);\r
+ break;\r
+ case 0x5E: // YMF262 write, port 0\r
+ case 0x5F: // YMF262 write, port 1\r
+ ymf262_write(vgmPlay, Command & 0x01, VGMPnt[0x01], VGMPnt[0x02]);\r
+ break;\r
+ }\r
+ VGMPos += 0x03;\r
+ break;\r
+ case 0x30:\r
+ VGMPos += 0x02;\r
+ break;\r
+ case 0x40:\r
+ case 0xA0:\r
+ case 0xB0:\r
+ VGMPos += 0x03;\r
+ break;\r
+ case 0xC0:\r
+ case 0xD0:\r
+ VGMPos += 0x04;\r
+ break;\r
+ case 0xE0:\r
+ case 0xF0:\r
+ VGMPos += 0x05;\r
+ break;\r
+ case 0x90:\r
+ switch(Command)\r
+ {\r
+ case 0x90: // DAC Ctrl: Setup Chip\r
+ VGMPos += 0x05;\r
+ break;\r
+ case 0x91: // DAC Ctrl: Set Data\r
+ VGMPos += 0x05;\r
+ break;\r
+ case 0x92: // DAC Ctrl: Set Freq\r
+ VGMPos += 0x06;\r
+ break;\r
+ case 0x93: // DAC Ctrl: Play from Start Pos\r
+ VGMPos += 0x0B;\r
+ break;\r
+ case 0x94: // DAC Ctrl: Stop immediately\r
+ VGMPos += 0x02;\r
+ break;\r
+ case 0x95: // DAC Ctrl: Play Block (small)\r
+ VGMPos += 0x05;\r
+ break;\r
+ default:\r
+ vgmPlay->vgmEnd = 0x01;\r
+ break;\r
+ }\r
+ break;\r
+ default:\r
+ vgmPlay->vgmEnd = 0x01;\r
+ return;\r
+ }\r
+ \r
+ if (VGMPos >= vgmLen)\r
+ vgmPlay->vgmEnd = 0x01;\r
+ }\r
+ vgmPlay->vgmPos = VGMPos;\r
+ vgmPlay->vgmSmplPos = VGMSmplPos;\r
+ if (vgmPlay->vgmEnd)\r
+ StopPlayback(vgmPlay);\r
+ \r
+ return;\r
+}\r
+\r
+\r
+\r
+\r
+void InitEngine(void)\r
+{\r
+ UINT8 curSFX;\r
+ UINT8 curReg;\r
+ \r
+ memset(oplRegs_Music, 0x00, 0x100);\r
+ memset(&vgmPbMusic, 0x00, sizeof(VGM_PBK));\r
+ vgmPbMusic.pbMode = PBMODE_MUSIC;\r
+ vgmPbMusic.vgmEnd = 0xFF;\r
+ vgmPbMusic.oplChnMask = 0x0000;\r
+ vgmPbMusic.oplRegCache = oplRegs_Music;\r
+ \r
+ for (curSFX = 0; curSFX < SFX_CHN_COUNT; curSFX ++)\r
+ {\r
+ memset(&oplRegs_SFX[curSFX], 0x00, sizeof(VGM_PBK));\r
+ memset(&vgmPbSFX[curSFX], 0x00, sizeof(VGM_PBK));\r
+ vgmPbSFX[curSFX].pbMode = PBMODE_SFX;\r
+ vgmPbSFX[curSFX].vgmEnd = 0xFF;\r
+ vgmPbSFX[curSFX].oplChnMask = curSFX;\r
+ vgmPbSFX[curSFX].oplRegCache = oplRegs_SFX[curSFX];\r
+ }\r
+ \r
+ // reset OPL2 chip\r
+ curReg = 0x00;\r
+ do\r
+ {\r
+ curReg --;\r
+ OPL2_Write(curReg, 0x00);\r
+ } while(curReg > 0x20);\r
+ \r
+ OPL2_Write(0x02, TIMER1_RATE); // set Timer 1 Period\r
+ OPL2_Write(0x04, 0x01); // Timer 1 on/unmasked, Timer 2 off\r
+ OPL2_Write(0x04, 0x80); // Reset Timer/IRQ Status Flags\r
+ \r
+ OPL2_Write(0x01, 0x20); // Waveform Select: Enable\r
+ \r
+ return;\r
+}\r
+\r
+void DeinitEngine(void)\r
+{\r
+ UINT8 curSFX;\r
+ \r
+ StopPlayback(&vgmPbMusic);\r
+ for (curSFX = 0; curSFX < SFX_CHN_COUNT; curSFX ++)\r
+ StopPlayback(&vgmPbSFX[curSFX]);\r
+ \r
+ OPL2_Write(0x04, 0x00); // disable all timers\r
+ \r
+ return;\r
+}\r
+\r
+\r
+UINT8 PlayMusic(VGM_FILE* vgmFile)\r
+{\r
+ VGM_PBK* vgmPb = &vgmPbMusic;\r
+ \r
+ if (! vgmPb->vgmEnd)\r
+ StopPlayback(vgmPb);\r
+ \r
+ vgmPb->file = vgmFile;\r
+ \r
+ StartPlayback(vgmPb);\r
+ \r
+ return 0x00;\r
+}\r
+\r
+UINT8 PlaySFX(VGM_FILE* vgmFile, UINT8 sfxChnID)\r
+{\r
+ VGM_PBK* vgmPb;\r
+ \r
+ if (sfxChnID >= SFX_CHN_COUNT)\r
+ return 0xFF;\r
+ \r
+ vgmPb = &vgmPbSFX[sfxChnID];\r
+ \r
+ if (! vgmPb->vgmEnd)\r
+ StopPlayback(vgmPb);\r
+ \r
+ vgmPb->file = vgmFile;\r
+ \r
+ StartPlayback(vgmPb);\r
+ \r
+ return 0x00;\r
+}\r
+\r
+UINT8 StopMusic(void)\r
+{\r
+ StopPlayback(&vgmPbMusic);\r
+ return 0x00;\r
+}\r
+\r
+UINT8 StopSFX(UINT8 sfxChnID)\r
+{\r
+ if (sfxChnID == 0xFF)\r
+ {\r
+ for (sfxChnID = 0; sfxChnID < SFX_CHN_COUNT; sfxChnID ++)\r
+ StopPlayback(&vgmPbSFX[sfxChnID]);\r
+ return 0x00;\r
+ }\r
+ \r
+ if (sfxChnID >= SFX_CHN_COUNT)\r
+ return 0xFF;\r
+ \r
+ StopPlayback(&vgmPbSFX[sfxChnID]);\r
+ return 0x00;\r
+}\r
+\r
+UINT8 PauseMusic(void)\r
+{\r
+ if (vgmPbMusic.vgmEnd == 0xFF)\r
+ return 0xFF; // not playing\r
+ if (vgmPbMusic.vgmEnd == 0x01)\r
+ return 0x80; // finished playing already\r
+ if (vgmPbMusic.vgmEnd == 0x02)\r
+ return 0x01; // is already paused\r
+ \r
+ StopPlayback(&vgmPbMusic);\r
+ vgmPbMusic.vgmEnd = 0x02;\r
+ \r
+ return 0x00;\r
+}\r
+\r
+UINT8 ResumeMusic(void)\r
+{\r
+ if (vgmPbMusic.vgmEnd == 0xFF)\r
+ return 0xFF; // not playing\r
+ if (vgmPbMusic.vgmEnd == 0x01)\r
+ return 0x80; // finished playing already\r
+ if (! (vgmPbMusic.vgmEnd & 0x02))\r
+ return 0x01; // is not paused\r
+ \r
+ vgmPbMusic.vgmEnd &= ~0x02;\r
+ \r
+ return 0x00;\r
+}\r
+\r
+static void StartPlayback(VGM_PBK* vgmPb)\r
+{\r
+ if (vgmPb->file == NULL || vgmPb->file->data == NULL ||\r
+ vgmPb->file->header.fccVGM != FCC_VGM)\r
+ {\r
+ vgmPb->vgmEnd = 0xFF;\r
+ return;\r
+ }\r
+ \r
+ vgmPb->vgmEnd = 0x00; // set to 'running'\r
+ vgmPb->vgmPos = vgmPb->file->header.lngDataOffset;\r
+ vgmPb->vgmSmplPos = 0;\r
+ vgmPb->pbSmplPos = 0;\r
+ vgmPb->curLoopCnt = 0;\r
+ memset(vgmPb->workRAM, 0x00, 0x04);\r
+ \r
+ if (vgmPb->pbMode == PBMODE_SFX)\r
+ {\r
+ UINT8 curReg;\r
+ \r
+ curReg = 0xB0 | vgmPb->oplChnMask;\r
+ if (oplRegs_Music[curReg] & 0x20)\r
+ OPL2_Write(curReg, oplRegs_Music[curReg] & ~0x20); // send Key Off\r
+ \r
+ vgmPbMusic.oplChnMask |= (1 << vgmPb->oplChnMask); // mask out music channel\r
+ }\r
+ \r
+ return;\r
+}\r
+\r
+static void StopPlayback(VGM_PBK* vgmPb)\r
+{\r
+ if (vgmPb->vgmEnd & 0x80)\r
+ return;\r
+ \r
+ if (vgmPb->pbMode == PBMODE_MUSIC)\r
+ {\r
+ UINT8 curReg;\r
+ UINT16 chnMask;\r
+ \r
+ chnMask = 0x0001;\r
+ for (curReg = 0xB0; curReg < 0xB9; curReg ++, chnMask <<= 1)\r
+ {\r
+ if (vgmPb->oplChnMask & chnMask)\r
+ continue; // keep channels used by SFX untouched\r
+ if (vgmPb->oplRegCache[curReg] & 0x20)\r
+ {\r
+ vgmPb->oplRegCache[curReg] &= ~0x20;\r
+ OPL2_Write(curReg, vgmPb->oplRegCache[curReg]); // send Key Off\r
+ }\r
+ }\r
+ curReg = 0xBD; // rhythm register\r
+ if (vgmPb->oplRegCache[curReg] & 0x1F)\r
+ {\r
+ vgmPb->oplRegCache[curReg] &= ~0x1F;\r
+ OPL2_Write(curReg, vgmPb->oplRegCache[curReg]); // send Key Off\r
+ }\r
+ \r
+ vgmPb->vgmEnd = 0x01;\r
+ }\r
+ else //if (vgmPb->pbMode == PBMODE_SFX)\r
+ {\r
+ UINT8 regID;\r
+ UINT8 curReg;\r
+ UINT8 opMask;\r
+ \r
+ curReg = 0xB0 | vgmPb->oplChnMask;\r
+ if (vgmPb->oplRegCache[0x0C] & 0x20)\r
+ {\r
+ vgmPb->oplRegCache[0x0C] &= ~0x20;\r
+ OPL2_Write(curReg, vgmPb->oplRegCache[0x0C]); // send Key Off\r
+ }\r
+ \r
+ vgmPb->vgmEnd = 0x01;\r
+ \r
+ if (! vgmPbMusic.vgmEnd) // if (music is playing)\r
+ {\r
+ opMask = CHN_OPMASK[vgmPb->oplChnMask];\r
+ for (regID = 0x00; regID < 0x0A; regID ++)\r
+ {\r
+ curReg = SFX_REGS[regID] + opMask;\r
+ OPL2_Write(curReg, oplRegs_Music[curReg]); // restore Music register\r
+ }\r
+ for (; regID < 0x0D; regID ++)\r
+ {\r
+ curReg = SFX_REGS[regID] | vgmPb->oplChnMask;\r
+ OPL2_Write(curReg, oplRegs_Music[curReg]); // restore Music register\r
+ }\r
+ \r
+ vgmPbMusic.oplChnMask &= ~(1 << vgmPb->oplChnMask);\r
+ }\r
+ }\r
+ \r
+ return;\r
+}\r
+\r
+\r
+\r
+static void OPL_CachedWrite(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)\r
+{\r
+ UINT8 regChn;\r
+ UINT8 ramOfs;\r
+ \r
+ if (vgmPb->pbMode == PBMODE_MUSIC)\r
+ {\r
+ if (reg == 0x01)\r
+ data |= 0x20; // enforce "Waveform Select Enable" bit\r
+ vgmPb->oplRegCache[reg] = data;\r
+ \r
+ ramOfs = SFX_REGS_REV[reg >> 4];\r
+ if (ramOfs < 0x0A) // Operator 20/40/60/80/E0\r
+ {\r
+ regChn = CHN_OPMASK_REV[reg & 0x1F] & 0x7F;\r
+ if (vgmPb->oplChnMask & (1 << regChn))\r
+ return; // channel overridden by SFX - return\r
+ }\r
+ else if (ramOfs < 0x0D) // Operator C0/A0/B0\r
+ {\r
+ regChn = reg & 0x0F;\r
+ if (vgmPb->oplChnMask & (1 << regChn))\r
+ return; // channel overridden by SFX - return\r
+ }\r
+ }\r
+ else //if (vgmPb->pbMode == PBMODE_SFX)\r
+ {\r
+ if (reg == 0xBD)\r
+ return; // no rhythm register for SFX\r
+ \r
+ ramOfs = SFX_REGS_REV[reg >> 4];\r
+ if (ramOfs == 0xFF)\r
+ return;\r
+ \r
+ if (ramOfs < 0x0A) // Operator 20/40/60/80/E0\r
+ {\r
+ regChn = CHN_OPMASK_REV[reg & 0x1F];\r
+ if (regChn == 0xFF)\r
+ return; // ignore writes to invalid channels/operators\r
+ ramOfs += (regChn & 0x80) >> 7;\r
+ regChn &= 0x7F;\r
+ vgmPb->oplRegCache[ramOfs] = data;\r
+ \r
+ if (regChn != vgmPb->oplChnMask)\r
+ {\r
+ // force command to current channel\r
+ reg = SFX_REGS[ramOfs] + CHN_OPMASK[vgmPb->oplChnMask];\r
+ }\r
+ }\r
+ else // Operator C0/A0/B0\r
+ {\r
+ regChn = CHN_OPMASK_REV[reg & 0x0F];\r
+ if (regChn >= 0x09)\r
+ return; // ignore writes to invalid channels\r
+ vgmPb->oplRegCache[ramOfs] = data;\r
+ \r
+ reg = (reg & 0xF0) | vgmPb->oplChnMask;\r
+ }\r
+ }\r
+ \r
+ OPL2_Write(reg, data);\r
+ return;\r
+}\r
+\r
+\r
+static void ym2413_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)\r
+{\r
+ return; // unsupported for now\r
+}\r
+\r
+static void ym3812_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)\r
+{\r
+ if (reg == 0x01)\r
+ {\r
+ vgmPb->workRAM[0x00] = data & 0x20; // store "Wave Select Enable" bit\r
+ }\r
+ else if ((reg & 0xE0) == 0xE0)\r
+ {\r
+ if (! vgmPb->workRAM[0x00]) // "Wave Select Enable" off?\r
+ data = 0x00; // disable waveforms\r
+ }\r
+ \r
+ OPL_CachedWrite(vgmPb, reg, data);\r
+ return;\r
+}\r
+\r
+static void ym3512_write(VGM_PBK* vgmPb, UINT8 reg, UINT8 data)\r
+{\r
+ if ((reg & 0xE0) == 0xE0)\r
+ {\r
+ data = 0x00; // OPL1 has no waveforms\r
+ }\r
+ if (reg >= 0x07 && reg < 0x20)\r
+ {\r
+ if (reg == 0x08)\r
+ data &= ~0x0F; // mask out Y8950 DeltaT data\r
+ else\r
+ return; // ignore Y8950 DeltaT writes\r
+ }\r
+ \r
+ OPL_CachedWrite(vgmPb, reg, data);\r
+ return;\r
+}\r
+\r
+static void ymf262_write(VGM_PBK* vgmPb, UINT8 port, UINT8 reg, UINT8 data)\r
+{\r
+ return; // unsupported for now\r
+}\r
+\r
+\r
+\r
+void UpdateSoundEngine(void)\r
+{\r
+ UINT8 tmrMask;\r
+ UINT8 curSFX;\r
+ \r
+ tmrMask = OPL2_ReadStatus();\r
+ if (! (tmrMask & 0x40))\r
+ return; // wait for overflow\r
+ OPL2_Write(0x04, 0x80); // Reset Timer/IRQ Status Flags\r
+ \r
+ if (! vgmPbMusic.vgmEnd)\r
+ UpdateVGM(&vgmPbMusic, VGM_UPD_RATE);\r
+ for (curSFX = 0; curSFX < SFX_CHN_COUNT; curSFX ++)\r
+ {\r
+ if (! vgmPbSFX[curSFX].vgmEnd)\r
+ UpdateVGM(&vgmPbSFX[curSFX], VGM_UPD_RATE);\r
+ }\r
+ \r
+ return;\r
+}\r