From b3edd4eb904c2ff826e8f45281a3cf3925fe1a33 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Wed, 11 Dec 2002 22:05:15 +0000 Subject: [PATCH] Updated to version 0.60 of fmopl by Jarek Burczynski Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@569 --- src/hardware/adlib.cpp | 43 +- src/hardware/fmopl.c | 1499 +++++++++++++++++++++++++++------------- src/hardware/fmopl.h | 196 ++---- 3 files changed, 1112 insertions(+), 626 deletions(-) diff --git a/src/hardware/adlib.cpp b/src/hardware/adlib.cpp index 8552a0ba..dc8bb48c 100644 --- a/src/hardware/adlib.cpp +++ b/src/hardware/adlib.cpp @@ -32,15 +32,30 @@ namespace MAME { /* Defines */ # define logerror(x) - /* Disable recurring warnings */ -#pragma warning ( disable : 4018 ) -#pragma warning ( disable : 4244 ) - /* Bring in Tatsuyuki Satoh's OPL emulation */ -#define HAS_YM3812 1 -#include "fmopl.c" + /* Disable recurring warnings */ +# pragma warning ( disable : 4018 ) +# pragma warning ( disable : 4244 ) + + /* Work around ANSI compliance problem (see driver.h) */ + struct __MALLOCPTR { + void* m_ptr; + + __MALLOCPTR(void) : m_ptr(NULL) { } + __MALLOCPTR(void* src) : m_ptr(src) { } + void* operator=(void* rhs) { return (m_ptr = rhs); } + operator int*() const { return (int*)m_ptr; } + operator int**() const { return (int**)m_ptr; } + operator char*() const { return (char*)m_ptr; } + }; + + /* Bring in the MAME OPL emulation */ +# define HAS_YM3812 1 +# include "fmopl.c" + } + struct OPLTimer_t { bool isEnabled; bool isMasked; @@ -50,9 +65,12 @@ struct OPLTimer_t { }; static OPLTimer_t timer1,timer2; -static MAME::FM_OPL * myopl; static Bit8u regsel; + #define OPL_INTERNAL_FREQ 3600000 // The OPL operates at 3.6MHz +#define OPL_NUM_CHIPS 1 // Number of OPL chips +#define OPL_CHIP0 0 + static MIXER_Channel * adlib_chan; static void ADLIB_CallBack(Bit8u *stream, Bit32u len) { @@ -60,7 +78,7 @@ static void ADLIB_CallBack(Bit8u *stream, Bit32u len) { /* Calculate teh machine ms we are at now */ /* update 1 ms of data */ - MAME::YM3812UpdateOne(myopl,(MAME::INT16 *)stream,len); + MAME::YM3812UpdateOne(0,(MAME::INT16 *)stream,len); } static Bit8u read_p388(Bit32u port) { @@ -120,9 +138,9 @@ static void write_p389(Bit32u port,Bit8u val) { } else timer2.isEnabled=false; return; default: /* Normal OPL call queue it */ - MAME::OPLWriteReg(myopl,regsel,val); + /* Use a little hack to directly write to the register */ + MAME::OPLWriteReg(MAME::OPL_YM3812[0],regsel,val); } - } static bool adlib_enabled; @@ -168,7 +186,10 @@ void ADLIB_Init(Section* sec) { timer2.isOverflowed=false; #define ADLIB_FREQ 22050 - myopl=MAME::OPLCreate(0,OPL_INTERNAL_FREQ,ADLIB_FREQ); + if (MAME::YM3812Init(OPL_NUM_CHIPS,OPL_INTERNAL_FREQ,ADLIB_FREQ)) { + E_Exit("Can't create adlib OPL Emulator"); + }; + adlib_chan=MIXER_AddChannel(ADLIB_CallBack,ADLIB_FREQ,"ADLIB"); MIXER_SetMode(adlib_chan,MIXER_16MONO); diff --git a/src/hardware/fmopl.c b/src/hardware/fmopl.c index 2f73d7db..ebc97920 100644 --- a/src/hardware/fmopl.c +++ b/src/hardware/fmopl.c @@ -4,17 +4,26 @@ ** types OPL and OPL2 ** ** Copyright (C) 1999,2000 Tatsuyuki Satoh , MultiArcadeMachineEmulator development -** (c) 2002 Jarek Burczynski +** Copyright (C) 2002 Jarek Burczynski ** -** Version 0.58 +** Version 0.60 ** Revision History: +04-28-2002 Jarek Burczynski: + - binary exact Envelope Generator (verified on real YM3812); + compared to YM2151: the EG clock is equal to internal_clock, + rates are 2 times slower and volume resolution is one bit less + - modified interface functions (they no longer return pointer - + that's internal to the emulator now): + - new wrapper functions for OPLCreate: YM3526Init(), YM3812Init() and Y8950Init() + - corrected 'off by one' error in feedback calculations (when feedback is off) + - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22) + - speeded up noise generator calculations (Nicola Salmoria) + 03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip) - Complete rewrite (all verified on real YM3812): - - corrected sin_tab and tl_tab data - corrected operator output calculations - corrected waveform_select_enable register; @@ -35,7 +44,6 @@ Revision History: - fixed subscription range of attack/decay tables - To do: add delay before key off in CSM mode (see CSMKeyControll) verify volume of the FM part on the Y8950 @@ -55,8 +63,6 @@ Revision History: - - /* output final shift */ #if (OPL_SAMPLE_BITS==16) #define FINAL_SH (0) @@ -70,20 +76,19 @@ Revision History: #define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ -#define ENV_SH 16 /* 16.16 fixed point (envelope calculations) */ +#define EG_SH 16 /* 16.16 fixed point (EG timing) */ #define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ #define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ #define FREQ_MASK ((1<>KSR */ + UINT8 mul; /* multiple: mul_tab[ML] */ + + /* Phase Generator */ + UINT32 Cnt; /* frequency counter */ + UINT32 Incr; /* frequency counter step */ + UINT8 FB; /* feedback shift value */ + INT32 *connect1; /* slot1 output pointer */ + INT32 op1_out[2]; /* slot1 output for feedback */ + UINT8 CON; /* connection (algorithm) type */ + + /* Envelope Generator */ + UINT8 eg_type; /* percussive/non-percussive mode */ + UINT8 state; /* phase type */ + UINT32 TL; /* total level: TL << 2 */ + INT32 TLL; /* adjusted now TL */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level: sl_tab[SL] */ + + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_dr; /* (decay state) */ + UINT8 eg_sel_dr; /* (decay state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + + UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ + + /* LFO */ + UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ + UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ + + /* waveform select */ + unsigned int wavetable; +} OPL_SLOT; + +typedef struct{ + OPL_SLOT SLOT[2]; + /* phase generator state */ + UINT32 block_fnum; /* block+fnum */ + UINT32 fc; /* Freq. Increment base */ + UINT32 ksl_base; /* KeyScaleLevel Base step */ + UINT8 kcode; /* key code (for key scaling) */ +} OPL_CH; + +/* OPL state */ +typedef struct fm_opl_f { + /* FM channel slots */ + OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */ + + UINT8 rhythm; /* Rhythm mode */ + + UINT32 fn_tab[1024]; /* fnumber->increment counter */ + + /* LFO */ + UINT8 lfo_am_depth; + UINT8 lfo_pm_depth_range; + UINT32 lfo_am_cnt; + UINT32 lfo_am_inc; + UINT32 lfo_pm_cnt; + UINT32 lfo_pm_inc; + + UINT32 noise_rng; /* 23 bit noise shift register */ + UINT32 noise_p; /* current noise 'phase' */ + UINT32 noise_f; /* current noise period */ + + UINT8 wavesel; /* waveform select enable flag */ + + int T[2]; /* timer counters */ + UINT8 st[2]; /* timer enable */ + +#if BUILD_Y8950 + /* Delta-T ADPCM unit (Y8950) */ + + YM_DELTAT *deltat; + + /* Keyboard / I/O interface unit*/ + UINT8 portDirection; + UINT8 portLatch; + OPL_PORTHANDLER_R porthandler_r; + OPL_PORTHANDLER_W porthandler_w; + int port_param; + OPL_PORTHANDLER_R keyboardhandler_r; + OPL_PORTHANDLER_W keyboardhandler_w; + int keyboard_param; +#endif + + /* external event callback handlers */ + OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ + int TimerParam; /* TIMER parameter */ + OPL_IRQHANDLER IRQHandler; /* IRQ handler */ + int IRQParam; /* IRQ parameter */ + OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */ + int UpdateParam; /* stream update parameter */ + + UINT8 type; /* chip type */ + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + UINT8 statusmask; /* status mask */ + UINT8 mode; /* Reg.08 : CSM,notesel,etc. */ + + int clock; /* master clock (Hz) */ + int rate; /* sampling rate (Hz) */ + double freqbase; /* frequency base */ + double TimerBase; /* Timer base time (==sampling time)*/ +} FM_OPL; + + + /* mapping of register number (offset) to slot number used by the emulator */ static const int slot_array[32]= { @@ -149,8 +288,8 @@ static const int slot_array[32]= /* key scale level */ /* table is 3dB/octave , DV converts this into 6dB/octave */ -/* 0.09375 is bit 0 weight expressed in the 'decibel' scale */ -#define DV (0.09375/2.0) +/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */ +#define DV (0.1875/2.0) static const UINT32 ksl_tab[8*16]= { /* OCT 0 */ @@ -196,15 +335,123 @@ static const UINT32 ksl_tab[8*16]= }; #undef DV -/* sustain lebel table (3dB per step) */ +/* sustain level table (3dB per step) */ /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ -#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) * (1<>3) +#define ENV_QUIET (TL_TAB_LEN>>4) /* sin waveform table in 'decibel' scale */ /* four waveforms on OPL2 type chips */ @@ -230,6 +477,7 @@ static unsigned int sin_tab[SIN_LEN * 4]; /* LFO Amplitude Modulation table (verified on real YM3812) + 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples Length: 210 elements. @@ -237,8 +485,8 @@ static unsigned int sin_tab[SIN_LEN * 4]; exactly 64 times (on 64 consecutive samples). The whole table takes: 64 * 210 = 13440 samples. - When AM = 1 data is multiplied by 2 - When AM = 0 data is divided by 4 and then multiplied by 2 (loosing precision is important) + When AM = 1 data is used directly + When AM = 0 data is divided by 4 before being used (loosing precision is important) */ #define LFO_AM_TAB_ELEMENTS 210 @@ -354,17 +602,6 @@ static INT32 LFO_PM; -/* log output level */ -#define LOG_ERR 3 /* ERROR */ -#define LOG_WAR 2 /* WARNING */ -#define LOG_INF 1 /* INFORMATION */ - -#define LOG_LEVEL LOG_INF - -#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x - - - INLINE int limit( int val, int max, int min ) { if ( val > max ) val = max; @@ -375,7 +612,6 @@ INLINE int limit( int val, int max, int min ) { } - /* status set and IRQ handling */ INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag) { @@ -431,104 +667,119 @@ INLINE void advance_lfo(FM_OPL *OPL) tmp = lfo_am_table[ OPL->lfo_am_cnt >> LFO_SH ]; if (OPL->lfo_am_depth) - LFO_AM = (tmp) * 2; + LFO_AM = tmp; else - LFO_AM = (tmp>>2) * 2; + LFO_AM = tmp>>2; OPL->lfo_pm_cnt += OPL->lfo_pm_inc; - LFO_PM = ( (OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range; + LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range; } /* advance to next sample */ INLINE void advance(FM_OPL *OPL) { OPL_CH *CH; - OPL_SLOT *SLOT; + OPL_SLOT *op; int i; + OPL->eg_timer += OPL->eg_timer_add; + + while (OPL->eg_timer >= OPL->eg_timer_overflow) + { + OPL->eg_timer -= OPL->eg_timer_overflow; + + OPL->eg_cnt++; + + for (i=0; i<9*2; i++) + { + CH = &OPL->P_CH[i/2]; + op = &CH->SLOT[i&1]; + + /* Envelope Generator */ + switch(op->state) + { + case EG_ATT: /* attack phase */ + { + + if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) ) + { + op->volume += (~op->volume * + (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)]) + ) >>3; + + if (op->volume <= MIN_ATT_INDEX) + { + op->volume = MIN_ATT_INDEX; + op->state = EG_DEC; + } + + } + + } + break; + + case EG_DEC: /* decay phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)]; + + if ( op->volume >= op->sl ) + op->state = EG_SUS; + + } + break; + + case EG_SUS: /* sustain phase */ + + /* this is important behaviour: + one can change percusive/non-percussive modes on the fly and + the chip will remain in sustain phase - verified on real YM3812 */ + + if(op->eg_type) /* non-percussive mode */ + { + /* do nothing */ + } + else /* percussive mode */ + { + /* during sustain phase chip adds Release Rate (in percussive mode) */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + op->volume = MAX_ATT_INDEX; + } + /* else do nothing in sustain phase */ + } + break; + + case EG_REL: /* release phase */ + if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) ) + { + op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)]; + + if ( op->volume >= MAX_ATT_INDEX ) + { + op->volume = MAX_ATT_INDEX; + op->state = EG_OFF; + } + + } + break; + + default: + break; + } + } + } for (i=0; i<9*2; i++) { - CH = &OPL->P_CH[i/2]; - SLOT = &CH->SLOT[i&1]; - - /* Envelope Generator */ - switch(SLOT->state) - { - case EG_ATT: /* attack phase */ - { - INT32 step = SLOT->volume; - - SLOT->volume -= SLOT->delta_ar; - step = (step>>ENV_SH) - (((UINT32)SLOT->volume)>>ENV_SH); /* number of levels passed since last time */ - if (step > 0) - { - INT32 tmp_volume = SLOT->volume + (step<>4) & ~ENV_MASK); - if (tmp_volume <= MIN_ATT_INDEX) - break; - step--; - }while(step); - SLOT->volume = tmp_volume; - } - - if (SLOT->volume <= MIN_ATT_INDEX) - { - if (SLOT->volume < 0) - SLOT->volume = 0; /* this is not quite correct (checked) */ - - SLOT->state = EG_DEC; - } - } - break; - - case EG_DEC: /* decay phase */ - - if ( (SLOT->volume += SLOT->delta_dr) >= SLOT->sl ) - { - SLOT->volume = SLOT->sl; /* this is not quite correct (checked) */ - SLOT->state = EG_SUS; - } - break; - - case EG_SUS: /* sustain phase */ - - /* this is important behaviour: - one can change percusive/non-percussive modes on the fly and - the chip will remain in sustain phase - verified on real YM3812 */ - - if(SLOT->eg_type) /* non-percussive mode */ - { - /* do nothing */ - } - else /* percussive mode */ - { - /* during sustain phase chip adds Release Rate (in percussive mode) */ - - if ( (SLOT->volume += SLOT->delta_rr) > MAX_ATT_INDEX ) - { - SLOT->volume = MAX_ATT_INDEX; - } - /* else do nothing in sustain phase */ - } - break; - - case EG_REL: /* release phase */ - if ( (SLOT->volume += SLOT->delta_rr) > MAX_ATT_INDEX ) - { - SLOT->volume = MAX_ATT_INDEX; - SLOT->state = EG_OFF; - } - break; - - default: - break; - } + CH = &OPL->P_CH[i/2]; + op = &CH->SLOT[i&1]; /* Phase Generator */ - if(SLOT->vib) + if(op->vib) { UINT8 block; unsigned int block_fnum = CH->block_fnum; @@ -541,16 +792,16 @@ INLINE void advance(FM_OPL *OPL) { block_fnum += lfo_fn_table_index_offset; block = (block_fnum&0x1c00) >> 10; - SLOT->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * SLOT->mul;//ok + op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;//ok } else /* LFO phase modulation = zero */ { - SLOT->Cnt += SLOT->Incr; + op->Cnt += op->Incr; } } else /* LFO phase modulation disabled for this operator */ { - SLOT->Cnt += SLOT->Incr; + op->Cnt += op->Incr; } } @@ -570,26 +821,40 @@ INLINE void advance(FM_OPL *OPL) OPL->noise_p &= FREQ_MASK; while (i) { + /* UINT32 j; j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1; OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1); + */ + + /* + Instead of doing all the logic operations above, we + use a trick here (and use bit 0 as the noise output). + The difference is only that the noise bit changes one + step ahead. This doesn't matter since we don't know + what is real state of the noise_rng after the reset. + */ + + if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302; + OPL->noise_rng >>= 1; + i--; } } -INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm) +INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) { UINT32 p; - p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK ]; + p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ]; if (p >= TL_TAB_LEN) return 0; return tl_tab[p]; } -INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) +INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab) { UINT32 p; INT32 i; @@ -598,7 +863,7 @@ INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) /*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, phase>>FREQ_SH, pm);*/ - p = (env<<3) + sin_tab[ (i>>FREQ_SH) & SIN_MASK]; + p = (env<<4) + sin_tab[ wave_tab + ((i>>FREQ_SH) & SIN_MASK)]; /*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/ @@ -608,7 +873,7 @@ INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) } -#define volume_calc(OP) ((OP)->TLL + (((UINT32)(OP)->volume)>>ENV_SH) + (LFO_AM & (OP)->AMmask)) +#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (LFO_AM & (OP)->AMmask)) /* calculate output */ INLINE void OPL_CALC_CH( OPL_CH *CH ) @@ -622,18 +887,22 @@ INLINE void OPL_CALC_CH( OPL_CH *CH ) /* SLOT 1 */ SLOT = &CH->SLOT[SLOT1]; env = volume_calc(SLOT); - out = CH->op1_out[0] + CH->op1_out[1]; - CH->op1_out[0] = CH->op1_out[1]; - *CH->connect1 += CH->op1_out[0]; - CH->op1_out[1] = 0; + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + *SLOT->connect1 += SLOT->op1_out[0]; + SLOT->op1_out[1] = 0; if( env < ENV_QUIET ) - CH->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB) ); + { + if (!SLOT->FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); + } /* SLOT 2 */ SLOT++; env = volume_calc(SLOT); if( env < ENV_QUIET ) - output[0] += op_calc(SLOT->Cnt, env, phase_modulation); + output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable); } /* @@ -691,23 +960,27 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise ) /* SLOT 1 */ SLOT = &CH[6].SLOT[SLOT1]; env = volume_calc(SLOT); + + out = SLOT->op1_out[0] + SLOT->op1_out[1]; + SLOT->op1_out[0] = SLOT->op1_out[1]; + + if (!SLOT->CON) + phase_modulation = SLOT->op1_out[0]; + //else ignore output of operator 1 + + SLOT->op1_out[1] = 0; + if( env < ENV_QUIET ) { - out = CH[6].op1_out[0] + CH[6].op1_out[1]; - CH[6].op1_out[0] = CH[6].op1_out[1]; - - if (!CH[6].CON) - phase_modulation = CH[6].op1_out[0]; - //else ignore output of operator 1 - - CH[6].op1_out[1] = 0; - if( env < ENV_QUIET ) - CH[6].op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB) + out = 0; + SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable ); } + /* SLOT 2 */ SLOT++; env = volume_calc(SLOT); if( env < ENV_QUIET ) - output[0] += op_calc(SLOT->Cnt, env, phase_modulation) * 2; + output[0] += op_calc(SLOT->Cnt, env, phase_modulation, SLOT->wavetable) * 2; /* Phase generation is based on: */ @@ -775,7 +1048,7 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise ) phase = 0xd0>>2; } - output[0] += op_calc(phase<wavetable) * 2; } /* Snare Drum (verified on real YM3812) */ @@ -796,13 +1069,13 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise ) if (noise) phase ^= 0x100; - output[0] += op_calc(phase<wavetable) * 2; } /* Tom Tom (verified on real YM3812) */ env = volume_calc(SLOT8_1); if( env < ENV_QUIET ) - output[0] += op_calc(SLOT8_1->Cnt, env, 0) * 2; + output[0] += op_calc(SLOT8_1->Cnt, env, 0, SLOT->wavetable) * 2; /* Top Cymbal (verified on real YM3812) */ env = volume_calc(SLOT8_2); @@ -829,69 +1102,11 @@ INLINE void OPL_CALC_RH( OPL_CH *CH, unsigned int noise ) if (res2) phase = 0x300; - output[0] += op_calc(phase<wavetable) * 2; } } -/* initialize time tables */ -static void init_timetables( FM_OPL *OPL ) -{ - int i; - double rate; - - /* make attack rate & decay rate tables */ - for (i = 0;i < 16+4;i++) - OPL->eg_tab[i] = 0; - - for (i = 4;i < 64;i++) - { - rate = OPL->freqbase; /* frequency rate */ - if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ - rate *= 1 << (i>>2); /* b2-5 : shift bit */ - rate /= /*576*/ 8 * 1024.0; - rate *= (double)(1<eg_tab[16+i] = rate; -#if 0 - logerror("FMOPL.C: Rate %2i %1i Decay [real %11.4f ms][emul %11.4f ms][d=%08x]\n",i>>2, i&3, - ( ((double)(ENV_LEN<rate), - ( ((double)(ENV_LEN<eg_tab[16+i]) * (1000.0 / (double)OPL->rate), OPL->eg_tab[16+i] ); -#endif - } - - for (i = 0; i < 16; i++) - { - OPL->eg_tab[16+64+i] = OPL->eg_tab[16+63]; - } - -#if 0 - for (i = 4; i < 64 ; i++) /* test */ - { - UINT32 vol = 0; - UINT32 vol_step = OPL->eg_tab[16+i]; - int j=0, change_no=0; - int lastchange=-1; - - logerror("FMOPL.C - EG TEST: Rate %2i %1i [d=%08x]\n",i>>2, i&3, vol_step ); - logerror(" -> changes every samples: "); - - while (change_no<16) - { - UINT32 tmp_vol = vol>>ENV_SH; - vol += vol_step; - if (tmp_vol!=(vol>>ENV_SH)) - { - //display the distance (in number of samples) since last level change - logerror("%i ",j-lastchange); - lastchange = j; - change_no++; - } - j++; - } - logerror("\n"); - } -#endif -} /* generic table initialize */ static int init_tables(void) @@ -926,9 +1141,9 @@ static int init_tables(void) tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; } #if 0 - logerror("tl %04i", x); + logerror("tl %04i", x*2); for (i=0; i<12; i++) - logerror(", [%02i] %4i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); + logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] ); logerror("\n"); #endif } @@ -962,19 +1177,25 @@ static int init_tables(void) for (i=0; i>1) ]; - /* waveform 3: /- /- /- /- */ + /* waveform 3: _ _ _ _ */ + /* / |_/ |_/ |_/ |_*/ /* abs(output only first quarter of the sinus waveform) */ + if (i & (1<<(SIN_BITS-2)) ) sin_tab[3*SIN_LEN+i] = TL_TAB_LEN; else @@ -1018,9 +1239,6 @@ static void OPL_initalize(FM_OPL *OPL) /* Timer base time */ OPL->TimerBase = 1.0 / ((double)OPL->clock / 72.0 ); - /* make time tables */ - init_timetables( OPL ); - /* make fnumber -> increment counter table */ for( i=0 ; i < 1024 ; i++ ) { @@ -1051,8 +1269,8 @@ static void OPL_initalize(FM_OPL *OPL) #endif - /* Amplitude modulation: 26 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ - /* In our LFO_AM_TABLE one entry lasts for 64 samples */ + /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */ + /* One entry from LFO_AM_TABLE lasts for 64 samples */ OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase; /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */ @@ -1062,6 +1280,11 @@ static void OPL_initalize(FM_OPL *OPL) /* Noise generator: a step takes 1 sample */ OPL->noise_f = (1.0 / 1.0) * (1<freqbase; + + OPL->eg_timer_add = (1<freqbase; + OPL->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, OPL->eg_timer_overflow);*/ + } INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set) @@ -1105,12 +1328,20 @@ INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT) SLOT->ksr = ksr; /* calculate envelope generator rates */ - if ((SLOT->ARval + SLOT->ksr) < 16+60) - SLOT->delta_ar = SLOT->AR[ SLOT->ksr ]; + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } else - SLOT->delta_ar = MAX_ATT_INDEX+1; - SLOT->delta_dr = SLOT->DR[ SLOT->ksr ]; - SLOT->delta_rr = SLOT->RR[ SLOT->ksr ]; + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; } } @@ -1136,7 +1367,7 @@ INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v) int ksl = v>>6; /* 0 / 1.5 / 3.0 / 6.0 dB/OCT */ SLOT->ksl = ksl ? 3-ksl : 31; - SLOT->TL = (v&0x3f)<<(ENV_BITS-7); /* 7 bits TL (bit 6 = always 0) */ + SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl); } @@ -1146,17 +1377,23 @@ INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - int DRval = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; - SLOT->ARval = (v>>4) ? 16 + ((v>>4) <<2) : 0; - SLOT->AR = &OPL->eg_tab[ SLOT->ARval ]; + SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0; - if ((SLOT->ARval + SLOT->ksr) < 16+60) - SLOT->delta_ar = SLOT->AR[ SLOT->ksr ]; + if ((SLOT->ar + SLOT->ksr) < 16+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ]; + } else - SLOT->delta_ar = MAX_ATT_INDEX+1; - SLOT->DR = &OPL->eg_tab[DRval]; - SLOT->delta_dr = SLOT->DR[ SLOT->ksr ]; + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 13*RATE_STEPS; + } + + SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ]; + SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ]; } /* set sustain level & release rate */ @@ -1164,12 +1401,12 @@ INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v) { OPL_CH *CH = &OPL->P_CH[slot/2]; OPL_SLOT *SLOT = &CH->SLOT[slot&1]; - int RRval = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; SLOT->sl = sl_tab[ v>>4 ]; - SLOT->RR = &OPL->eg_tab[RRval]; - SLOT->delta_rr = SLOT->RR[ SLOT->ksr ]; + SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ]; + SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ]; } @@ -1247,7 +1484,7 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v) if(OPL->keyboardhandler_w) OPL->keyboardhandler_w(OPL->keyboard_param,v); else - LOG(LOG_WAR,("OPL:write unmapped KEYBOARD port\n")); + logerror("OPL:write unmapped KEYBOARD port\n"); } break; case 0x07: /* DELTA-T controll : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */ @@ -1295,22 +1532,22 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v) break; case 0x20: /* am ON, vib ON, ksr, eg_type, mul */ slot = slot_array[r&0x1f]; - if(slot == -1) return; + if(slot < 0) return; set_mul(OPL,slot,v); break; case 0x40: slot = slot_array[r&0x1f]; - if(slot == -1) return; + if(slot < 0) return; set_ksl_tl(OPL,slot,v); break; case 0x60: slot = slot_array[r&0x1f]; - if(slot == -1) return; + if(slot < 0) return; set_ar_dr(OPL,slot,v); break; case 0x80: slot = slot_array[r&0x1f]; - if(slot == -1) return; + if(slot < 0) return; set_sl_rr(OPL,slot,v); break; case 0xa0: @@ -1402,7 +1639,7 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v) /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */ if (OPL->mode&0x40) - CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ + CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */ else CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */ @@ -1419,19 +1656,19 @@ static void OPLWriteReg(FM_OPL *OPL, int r, int v) /* FB,C */ if( (r&0x0f) > 8) return; CH = &OPL->P_CH[r&0x0f]; - CH->FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; - CH->CON = v&1; - CH->connect1 = CH->CON ? &output[0] : &phase_modulation; + CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0; + CH->SLOT[SLOT1].CON = v&1; + CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &output[0] : &phase_modulation; break; case 0xe0: /* waveform select */ /* simply ignore write to the waveform select register if selecting not enabled in test register */ if(OPL->wavesel) { slot = slot_array[r&0x1f]; - if(slot == -1) return; + if(slot < 0) return; CH = &OPL->P_CH[slot/2]; - CH->SLOT[slot&1].wavetable = &sin_tab[(v&0x03)*SIN_LEN]; + CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN; } break; } @@ -1491,22 +1728,310 @@ static void OPL_UnLockTable(void) } -#if (BUILD_YM3812 || BUILD_YM3526) -/*******************************************************************************/ -/* YM3812 local section */ -/*******************************************************************************/ +static void OPLResetChip(FM_OPL *OPL) +{ + int c,s; + int i; -/* Generate samples for one of the YM3812's -* -* '*OPL' is pointer to the virtual YM3812 -* '*buffer' is pointer to the sample buffer -* 'length' is the number of samples that should be generated -*/ -void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) + OPL->eg_timer = 0; + OPL->eg_cnt = 0; + + OPL->noise_rng = 1; /* noise shift register */ + OPL->mode = 0; /* normal mode */ + OPL_STATUS_RESET(OPL,0x7f); + + /* reset with register write */ + OPLWriteReg(OPL,0x01,0); /* wavesel disable */ + OPLWriteReg(OPL,0x02,0); /* Timer1 */ + OPLWriteReg(OPL,0x03,0); /* Timer2 */ + OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ + for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); + + /* reset operator parameters */ + for( c = 0 ; c < 9 ; c++ ) + { + OPL_CH *CH = &OPL->P_CH[c]; + for(s = 0 ; s < 2 ; s++ ) + { + /* wave table */ + CH->SLOT[s].wavetable = 0; + CH->SLOT[s].state = EG_OFF; + CH->SLOT[s].volume = MAX_ATT_INDEX; + } + } +#if BUILD_Y8950 + if(OPL->type&OPL_TYPE_ADPCM) + { + YM_DELTAT *DELTAT = OPL->deltat; + + DELTAT->freqbase = OPL->freqbase; + DELTAT->output_pointer = &output_deltat[0]; + DELTAT->portshift = 5; + DELTAT->output_range = 1<<23; + YM_DELTAT_ADPCM_Reset(DELTAT,0); + } +#endif +} + +/* Create one of virtual YM3812 */ +/* 'clock' is chip clock in Hz */ +/* 'rate' is sampling rate */ +static FM_OPL *OPLCreate(int type, int clock, int rate) +{ + char *ptr; + FM_OPL *OPL; + int state_size; + + if (OPL_LockTable() ==-1) return NULL; + + /* calculate OPL state size */ + state_size = sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT); +#endif + + /* allocate memory block */ + ptr = (char *)malloc(state_size); + + if (ptr==NULL) + return NULL; + + /* clear */ + memset(ptr,0,state_size); + + OPL = (FM_OPL *)ptr; + + ptr += sizeof(FM_OPL); + +#if BUILD_Y8950 + if (type&OPL_TYPE_ADPCM) + OPL->deltat = (YM_DELTAT *)ptr; + ptr += sizeof(YM_DELTAT); +#endif + + OPL->type = type; + OPL->clock = clock; + OPL->rate = rate; + + /* init global tables */ + OPL_initalize(OPL); + + /* reset chip */ + OPLResetChip(OPL); + return OPL; +} + +/* Destroy one of virtual YM3812 */ +static void OPLDestroy(FM_OPL *OPL) +{ + OPL_UnLockTable(); + free(OPL); +} + +/* Option handlers */ + +static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset) +{ + OPL->TimerHandler = TimerHandler; + OPL->TimerParam = channelOffset; +} +static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param) +{ + OPL->IRQHandler = IRQHandler; + OPL->IRQParam = param; +} +static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPL->UpdateHandler = UpdateHandler; + OPL->UpdateParam = param; +} + +/* YM3812 I/O interface */ +static int OPLWrite(FM_OPL *OPL,int a,int v) +{ + if( !(a&1) ) + { /* address port */ + OPL->address = v & 0xff; + } + else + { /* data port */ + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); + OPLWriteReg(OPL,OPL->address,v); + } + return OPL->status>>7; +} + +static unsigned char OPLRead(FM_OPL *OPL,int a) +{ + if( !(a&1) ) + { + /* status port */ + return OPL->status & (OPL->statusmask|0x80); + } + +#if BUILD_Y8950 + /* data port */ + switch(OPL->address) + { + case 0x05: /* KeyBoard IN */ + if(OPL->type&OPL_TYPE_KEYBOARD) + { + if(OPL->keyboardhandler_r) + return OPL->keyboardhandler_r(OPL->keyboard_param); + else + logerror("OPL:read unmapped KEYBOARD port\n"); + } + return 0; +#if 0 + case 0x0f: /* ADPCM-DATA */ + return 0; +#endif + case 0x19: /* I/O DATA */ + if(OPL->type&OPL_TYPE_IO) + { + if(OPL->porthandler_r) + return OPL->porthandler_r(OPL->port_param); + else + logerror("OPL:read unmapped I/O port\n"); + } + return 0; + case 0x1a: /* PCM-DATA */ + return 0; + } +#endif + + return 0xff; +} + +/* CSM Key Controll */ +INLINE void CSMKeyControll(OPL_CH *CH) +{ + FM_KEYON (&CH->SLOT[SLOT1], 4); + FM_KEYON (&CH->SLOT[SLOT2], 4); + + /* The key off should happen exactly one sample later - not implemented correctly yet */ + + FM_KEYOFF(&CH->SLOT[SLOT1], ~4); + FM_KEYOFF(&CH->SLOT[SLOT2], ~4); +} + + +static int OPLTimerOver(FM_OPL *OPL,int c) +{ + if( c ) + { /* Timer B */ + OPL_STATUS_SET(OPL,0x20); + } + else + { /* Timer A */ + OPL_STATUS_SET(OPL,0x40); + /* CSM mode key,TL controll */ + if( OPL->mode & 0x80 ) + { /* CSM mode total level latch and auto key on */ + int ch; + if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); + for(ch=0; ch<9; ch++) + CSMKeyControll( &OPL->P_CH[ch] ); + } + } + /* reload timer */ + if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase); + return OPL->status>>7; +} + + +#define MAX_OPL_CHIPS 2 + + +#if (BUILD_YM3812) + +static FM_OPL *OPL_YM3812[MAX_OPL_CHIPS]; /* array of pointers to the YM3812's */ +static int YM3812NumChips = 0; /* number of chips */ + +int YM3812Init(int num, int clock, int rate) { int i; - OPLSAMPLE *buf = buffer; - UINT8 rhythm = OPL->rhythm&0x20; + + if (YM3812NumChips) + return -1; /* duplicate init. */ + + YM3812NumChips = num; + + for (i = 0;i < YM3812NumChips; i++) + { + /* emulator create */ + OPL_YM3812[i] = OPLCreate(OPL_TYPE_YM3812,clock,rate); + if(OPL_YM3812[i] == NULL) + { + /* it's really bad - we run out of memeory */ + YM3812NumChips = 0; + return -1; + } + } + + return 0; +} + +void YM3812Shutdown(void) +{ + int i; + + for (i = 0;i < YM3812NumChips; i++) + { + /* emulator shutdown */ + OPLDestroy(OPL_YM3812[i]); + OPL_YM3812[i] = NULL; + } + YM3812NumChips = 0; +} +void YM3812ResetChip(int which) +{ + OPLResetChip(OPL_YM3812[which]); +} + +int YM3812Write(int which, int a, int v) +{ + return OPLWrite(OPL_YM3812[which], a, v); +} + +unsigned char YM3812Read(int which, int a) +{ + /* YM3812 always returns bit2 and bit1 in HIGH state */ + return OPLRead(OPL_YM3812[which], a) | 0x06 ; +} +int YM3812TimerOver(int which, int c) +{ + return OPLTimerOver(OPL_YM3812[which], c); +} + +void YM3812SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset) +{ + OPLSetTimerHandler(OPL_YM3812[which], TimerHandler, channelOffset); +} +void YM3812SetIRQHandler(int which,OPL_IRQHANDLER IRQHandler,int param) +{ + OPLSetIRQHandler(OPL_YM3812[which], IRQHandler, param); +} +void YM3812SetUpdateHandler(int which,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPLSetUpdateHandler(OPL_YM3812[which], UpdateHandler, param); +} + + +/* +** Generate samples for one of the YM3812's +** +** 'which' is the virtual YM3812 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void YM3812UpdateOne(int which, INT16 *buffer, int length) +{ + FM_OPL *OPL = OPL_YM3812[which]; + UINT8 rhythm = OPL->rhythm&0x20; + OPLSAMPLE *buf = buffer; + int i; if( (void *)OPL != cur_chip ){ cur_chip = (void *)OPL; @@ -1540,7 +2065,7 @@ void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) } else /* Rhythm part */ { - OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>22)&1 ); + OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); } lt = output[0]; @@ -1558,20 +2083,253 @@ void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) buf[i] = lt; advance(OPL); - } } -#endif /* (BUILD_YM3812 || BUILD_YM3526) */ +#endif /* BUILD_YM3812 */ + + + +#if (BUILD_YM3526) + +static FM_OPL *OPL_YM3526[MAX_OPL_CHIPS]; /* array of pointers to the YM3526's */ +static int YM3526NumChips = 0; /* number of chips */ + +int YM3526Init(int num, int clock, int rate) +{ + int i; + + if (YM3526NumChips) + return -1; /* duplicate init. */ + + YM3526NumChips = num; + + for (i = 0;i < YM3526NumChips; i++) + { + /* emulator create */ + OPL_YM3526[i] = OPLCreate(OPL_TYPE_YM3526,clock,rate); + if(OPL_YM3526[i] == NULL) + { + /* it's really bad - we run out of memeory */ + YM3526NumChips = 0; + return -1; + } + } + + return 0; +} + +void YM3526Shutdown(void) +{ + int i; + + for (i = 0;i < YM3526NumChips; i++) + { + /* emulator shutdown */ + OPLDestroy(OPL_YM3526[i]); + OPL_YM3526[i] = NULL; + } + YM3526NumChips = 0; +} +void YM3526ResetChip(int which) +{ + OPLResetChip(OPL_YM3526[which]); +} + +int YM3526Write(int which, int a, int v) +{ + return OPLWrite(OPL_YM3526[which], a, v); +} + +unsigned char YM3526Read(int which, int a) +{ + return OPLRead(OPL_YM3526[which], a); +} +int YM3526TimerOver(int which, int c) +{ + return OPLTimerOver(OPL_YM3526[which], c); +} + +void YM3526SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset) +{ + OPLSetTimerHandler(OPL_YM3526[which], TimerHandler, channelOffset); +} +void YM3526SetIRQHandler(int which,OPL_IRQHANDLER IRQHandler,int param) +{ + OPLSetIRQHandler(OPL_YM3526[which], IRQHandler, param); +} +void YM3526SetUpdateHandler(int which,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPLSetUpdateHandler(OPL_YM3526[which], UpdateHandler, param); +} + + +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void YM3526UpdateOne(int which, INT16 *buffer, int length) +{ + FM_OPL *OPL = OPL_YM3526[which]; + UINT8 rhythm = OPL->rhythm&0x20; + OPLSAMPLE *buf = buffer; + int i; + + if( (void *)OPL != cur_chip ){ + cur_chip = (void *)OPL; + /* rhythm slots */ + SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1]; + SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2]; + SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1]; + SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2]; + } + for( i=0; i < length ; i++ ) + { + int lt; + + output[0] = 0; + + advance_lfo(OPL); + + /* FM part */ + OPL_CALC_CH(&OPL->P_CH[0]); + OPL_CALC_CH(&OPL->P_CH[1]); + OPL_CALC_CH(&OPL->P_CH[2]); + OPL_CALC_CH(&OPL->P_CH[3]); + OPL_CALC_CH(&OPL->P_CH[4]); + OPL_CALC_CH(&OPL->P_CH[5]); + + if(!rhythm) + { + OPL_CALC_CH(&OPL->P_CH[6]); + OPL_CALC_CH(&OPL->P_CH[7]); + OPL_CALC_CH(&OPL->P_CH[8]); + } + else /* Rhythm part */ + { + OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); + } + + lt = output[0]; + + lt >>= FINAL_SH; + + /* limit check */ + lt = limit( lt , MAXOUT, MINOUT ); + + #ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS + #endif + + /* store to sound buffer */ + buf[i] = lt; + + advance(OPL); + } + +} +#endif /* BUILD_YM3526 */ + + + #if BUILD_Y8950 -void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) +static FM_OPL *OPL_Y8950[MAX_OPL_CHIPS]; /* array of pointers to the Y8950's */ +static int Y8950NumChips = 0; /* number of chips */ + +int Y8950Init(int num, int clock, int rate) { int i; - OPLSAMPLE *buf = buffer; - UINT8 rhythm = OPL->rhythm&0x20; - YM_DELTAT *DELTAT = OPL->deltat; + + if (Y8950NumChips) + return -1; /* duplicate init. */ + + Y8950NumChips = num; + + for (i = 0;i < Y8950NumChips; i++) + { + /* emulator create */ + OPL_Y8950[i] = OPLCreate(OPL_TYPE_Y8950,clock,rate); + if(OPL_Y8950[i] == NULL) + { + /* it's really bad - we run out of memeory */ + Y8950NumChips = 0; + return -1; + } + } + + return 0; +} + +void Y8950Shutdown(void) +{ + int i; + + for (i = 0;i < Y8950NumChips; i++) + { + /* emulator shutdown */ + OPLDestroy(OPL_Y8950[i]); + OPL_Y8950[i] = NULL; + } + Y8950NumChips = 0; +} +void Y8950ResetChip(int which) +{ + OPLResetChip(OPL_Y8950[which]); +} + +int Y8950Write(int which, int a, int v) +{ + return OPLWrite(OPL_Y8950[which], a, v); +} + +unsigned char Y8950Read(int which, int a) +{ + return OPLRead(OPL_Y8950[which], a); +} +int Y8950TimerOver(int which, int c) +{ + return OPLTimerOver(OPL_Y8950[which], c); +} + +void Y8950SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset) +{ + OPLSetTimerHandler(OPL_Y8950[which], TimerHandler, channelOffset); +} +void Y8950SetIRQHandler(int which,OPL_IRQHANDLER IRQHandler,int param) +{ + OPLSetIRQHandler(OPL_Y8950[which], IRQHandler, param); +} +void Y8950SetUpdateHandler(int which,OPL_UPDATEHANDLER UpdateHandler,int param) +{ + OPLSetUpdateHandler(OPL_Y8950[which], UpdateHandler, param); +} + +void Y8950SetDeltaTMemory(int which, void * deltat_rom, int deltat_rom_size ) +{ + FM_OPL *OPL = OPL_Y8950[which]; + OPL->deltat->memory = (UINT8 *)(deltat_rom); + OPL->deltat->memory_size = deltat_rom_size; +} + +/* +** Generate samples for one of the Y8950's +** +** 'which' is the virtual Y8950 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void Y8950UpdateOne(int which, INT16 *buffer, int length) +{ + int i; + FM_OPL *OPL = OPL_Y8950[which]; + UINT8 rhythm = OPL->rhythm&0x20; + YM_DELTAT *DELTAT = OPL->deltat; + OPLSAMPLE *buf = buffer; /* setup DELTA-T unit */ YM_DELTAT_DECODE_PRESET(DELTAT); @@ -1614,7 +2372,7 @@ void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) } else /* Rhythm part */ { - OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>22)&1 ); + OPL_CALC_RH(&OPL->P_CH[0], (OPL->noise_rng>>0)&1 ); } lt = output[0] + (output_deltat[0]>>11); @@ -1644,235 +2402,22 @@ void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length) OPL->status |= 0x10; } } -#endif - -void OPLResetChip(FM_OPL *OPL) -{ - int c,s; - int i; - - OPL->noise_rng = 1; /* noise shift register */ - OPL->mode = 0; /* normal mode */ - OPL_STATUS_RESET(OPL,0x7f); - - /* reset with register write */ - OPLWriteReg(OPL,0x01,0); /* wavesel disable */ - OPLWriteReg(OPL,0x02,0); /* Timer1 */ - OPLWriteReg(OPL,0x03,0); /* Timer2 */ - OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */ - for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0); - - /* reset operator parameters */ - for( c = 0 ; c < 9 ; c++ ) - { - OPL_CH *CH = &OPL->P_CH[c]; - for(s = 0 ; s < 2 ; s++ ) - { - /* wave table */ - CH->SLOT[s].wavetable = &sin_tab[0]; - CH->SLOT[s].state = EG_OFF; - CH->SLOT[s].volume = MAX_ATT_INDEX; - } - } -#if BUILD_Y8950 - if(OPL->type&OPL_TYPE_ADPCM) - { - YM_DELTAT *DELTAT = OPL->deltat; - - DELTAT->freqbase = OPL->freqbase; - DELTAT->output_pointer = &output_deltat[0]; - DELTAT->portshift = 5; - DELTAT->output_range = 1<<23; - YM_DELTAT_ADPCM_Reset(DELTAT,0); - } -#endif -} - -/* Create one of virtual YM3812 */ -/* 'clock' is chip clock in Hz */ -/* 'rate' is sampling rate */ -FM_OPL *OPLCreate(int type, int clock, int rate) -{ - union { - char *ptr; - void *ptr_void; - }; - FM_OPL *OPL; - int state_size; - - if (OPL_LockTable() ==-1) return NULL; - - /* calculate OPL state size */ - state_size = sizeof(FM_OPL); - -#if BUILD_Y8950 - if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT); -#endif - - /* allocate memory block */ - ptr_void = malloc(state_size); - - if (ptr==NULL) - return NULL; - - /* clear */ - memset(ptr,0,state_size); - - OPL = (FM_OPL *)ptr; - - ptr += sizeof(FM_OPL); - -#if BUILD_Y8950 - if (type&OPL_TYPE_ADPCM) - OPL->deltat = (YM_DELTAT *)ptr; - ptr += sizeof(YM_DELTAT); -#endif - - OPL->type = type; - OPL->clock = clock; - OPL->rate = rate; - - /* init global tables */ - OPL_initalize(OPL); - - /* reset chip */ - OPLResetChip(OPL); - return OPL; -} - -/* Destroy one of virtual YM3812 */ -void OPLDestroy(FM_OPL *OPL) -{ - OPL_UnLockTable(); - free(OPL); -} - -/* Option handlers */ - -void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset) -{ - OPL->TimerHandler = TimerHandler; - OPL->TimerParam = channelOffset; -} -void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param) -{ - OPL->IRQHandler = IRQHandler; - OPL->IRQParam = param; -} -void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param) -{ - OPL->UpdateHandler = UpdateHandler; - OPL->UpdateParam = param; -} - -#if BUILD_Y8950 -void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param) +void Y8950SetPortHandler(int which,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param) { + FM_OPL *OPL = OPL_Y8950[which]; OPL->porthandler_w = PortHandler_w; OPL->porthandler_r = PortHandler_r; OPL->port_param = param; } -void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param) +void Y8950SetKeyboardHandler(int which,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param) { + FM_OPL *OPL = OPL_Y8950[which]; OPL->keyboardhandler_w = KeyboardHandler_w; OPL->keyboardhandler_r = KeyboardHandler_r; OPL->keyboard_param = param; } + #endif -/* YM3812 I/O interface */ -int OPLWrite(FM_OPL *OPL,int a,int v) -{ - if( !(a&1) ) - { /* address port */ - OPL->address = v & 0xff; - } - else - { /* data port */ - if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); - OPLWriteReg(OPL,OPL->address,v); - } - return OPL->status>>7; -} - -unsigned char OPLRead(FM_OPL *OPL,int a) -{ - if( !(a&1) ) - { - /* YM3812 returns 0x06 (bit2 and bit1 are HIGH) */ - - /* status port */ - return OPL->status & (OPL->statusmask|0x80); - } - -#if BUILD_Y8950 - /* data port */ - switch(OPL->address) - { - case 0x05: /* KeyBoard IN */ - if(OPL->type&OPL_TYPE_KEYBOARD) - { - if(OPL->keyboardhandler_r) - return OPL->keyboardhandler_r(OPL->keyboard_param); - else - LOG(LOG_WAR,("OPL:read unmapped KEYBOARD port\n")); - } - return 0; -#if 0 - case 0x0f: /* ADPCM-DATA */ - return 0; -#endif - case 0x19: /* I/O DATA */ - if(OPL->type&OPL_TYPE_IO) - { - if(OPL->porthandler_r) - return OPL->porthandler_r(OPL->port_param); - else - LOG(LOG_WAR,("OPL:read unmapped I/O port\n")); - } - return 0; - case 0x1a: /* PCM-DATA */ - return 0; - } -#endif - - return 0xff; -} - -/* CSM Key Controll */ -INLINE void CSMKeyControll(OPL_CH *CH) -{ - FM_KEYON (&CH->SLOT[SLOT1], 4); - FM_KEYON (&CH->SLOT[SLOT2], 4); - - /* The key off should happen exactly one sample later - not implemented correctly yet */ - - FM_KEYOFF(&CH->SLOT[SLOT1], ~4); - FM_KEYOFF(&CH->SLOT[SLOT2], ~4); -} - - -int OPLTimerOver(FM_OPL *OPL,int c) -{ - if( c ) - { /* Timer B */ - OPL_STATUS_SET(OPL,0x20); - } - else - { /* Timer A */ - OPL_STATUS_SET(OPL,0x40); - /* CSM mode key,TL controll */ - if( OPL->mode & 0x80 ) - { /* CSM mode total level latch and auto key on */ - int ch; - if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0); - for(ch=0; ch<9; ch++) - CSMKeyControll( &OPL->P_CH[ch] ); - } - } - /* reload timer */ - if (OPL->TimerHandler) (OPL->TimerHandler)(OPL->TimerParam+c,(double)OPL->T[c]*OPL->TimerBase); - return OPL->status>>7; -} diff --git a/src/hardware/fmopl.h b/src/hardware/fmopl.h index 46c5c15e..4ba2b6b8 100644 --- a/src/hardware/fmopl.h +++ b/src/hardware/fmopl.h @@ -6,8 +6,7 @@ #define BUILD_YM3526 (HAS_YM3526) #define BUILD_Y8950 (HAS_Y8950) -/* --- system optimize --- */ -/* select bit size of output : 8 or 16 */ +/* select output bits size of output : 8 or 16 */ #define OPL_SAMPLE_BITS 16 /* compiler dependence */ @@ -25,167 +24,88 @@ typedef signed int INT32; /* signed 32bit */ typedef INT16 OPLSAMPLE; #endif #if (OPL_SAMPLE_BITS==8) -typedef unsigned char OPLSAMPLE; +typedef INT8 OPLSAMPLE; #endif -#if BUILD_Y8950 -#include "ymdeltat.h" -#endif - typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec); typedef void (*OPL_IRQHANDLER)(int param,int irq); typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us); typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data); typedef unsigned char (*OPL_PORTHANDLER_R)(int param); -/* !!!!! here is private section , do not access there member direct !!!!! */ -#define OPL_TYPE_WAVESEL 0x01 /* waveform select */ -#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */ -#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */ -#define OPL_TYPE_IO 0x08 /* I/O port */ +#if BUILD_YM3812 -/* Saving is necessary for member of the 'R' mark for suspend/resume */ -/* ---------- OPL slot ---------- */ -typedef struct fm_opl_slot { - const UINT32 *AR; /* attack rate tab :&eg_table[AR<<2]*/ - const UINT32 *DR; /* decay rate tab :&eg_table[DR<<2]*/ - const UINT32 *RR; /* release rate tab:&eg_table[RR<<2]*/ - UINT8 KSR; /* key scale rate */ - UINT8 ARval; /* current AR */ - UINT8 ksl; /* keyscale level */ - UINT8 ksr; /* key scale rate :kcode>>KSR */ - UINT8 mul; /* multiple :ML_TABLE[ML] */ +int YM3812Init(int num, int clock, int rate); +void YM3812Shutdown(void); +void YM3812ResetChip(int which); +int YM3812Write(int which, int a, int v); +unsigned char YM3812Read(int which, int a); +int YM3812TimerOver(int which, int c); +void YM3812UpdateOne(int which, INT16 *buffer, int length); - /* Phase Generator */ - UINT32 Cnt; /* frequency count */ - UINT32 Incr; /* frequency step */ +void YM3812SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset); +void YM3812SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param); +void YM3812SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param); - /* Envelope Generator */ - UINT8 eg_type; /* percussive/non-percussive mode */ - UINT8 state; /* phase type */ - UINT32 TL; /* total level :TL << 3 */ - INT32 TLL; /* adjusted now TL */ - INT32 volume; /* envelope counter */ - UINT32 sl; /* sustain level :SL_TABLE[SL] */ - UINT32 delta_ar; /* envelope step for Attack */ - UINT32 delta_dr; /* envelope step for Decay */ - UINT32 delta_rr; /* envelope step for Release */ +#endif - UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */ - /* LFO */ - UINT32 AMmask; /* LFO Amplitude Modulation enable mask */ - UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/ +#if BUILD_YM3526 - /* waveform select */ - unsigned int *wavetable; -}OPL_SLOT; +/* +** Initialize YM3526 emulator(s). +** +** 'num' is the number of virtual YM3526's to allocate +** 'clock' is the chip clock in Hz +** 'rate' is sampling rate +*/ +int YM3526Init(int num, int clock, int rate); +/* shutdown the YM3526 emulators*/ +void YM3526Shutdown(void); +void YM3526ResetChip(int which); +int YM3526Write(int which, int a, int v); +unsigned char YM3526Read(int which, int a); +int YM3526TimerOver(int which, int c); +/* +** Generate samples for one of the YM3526's +** +** 'which' is the virtual YM3526 number +** '*buffer' is the output buffer pointer +** 'length' is the number of samples that should be generated +*/ +void YM3526UpdateOne(int which, INT16 *buffer, int length); -/* ---------- OPL one of channel ---------- */ -typedef struct fm_opl_channel { - OPL_SLOT SLOT[2]; - UINT8 FB; /* feedback shift value */ - INT32 *connect1; /* slot1 output pointer */ - INT32 op1_out[2]; /* slot1 output for feedback */ +void YM3526SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset); +void YM3526SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param); +void YM3526SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param); - /* phase generator state */ - UINT32 block_fnum; /* block+fnum */ - UINT32 fc; /* Freq. Increment base */ - UINT32 ksl_base; /* KeyScaleLevel Base step */ - UINT8 kcode; /* key code (for key scaling) */ +#endif - UINT8 CON; /* connection (algorithm) type */ -} OPL_CH; - -/* OPL state */ -typedef struct fm_opl_f { - /* FM channel slots */ - OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels */ - - UINT8 rhythm; /* Rhythm mode */ - - UINT32 eg_tab[16+64+16]; /* EG rate table: 16 (dummy) + 64 rates + 16 RKS */ - UINT32 fn_tab[1024]; /* fnumber -> increment counter */ - - /* LFO */ - UINT8 lfo_am_depth; - UINT8 lfo_pm_depth_range; - UINT32 lfo_am_cnt; - UINT32 lfo_am_inc; - UINT32 lfo_pm_cnt; - UINT32 lfo_pm_inc; - - UINT32 noise_rng; /* 23 bit noise shift register */ - UINT32 noise_p; /* current noise 'phase' */ - UINT32 noise_f; /* current noise period */ - - UINT8 wavesel; /* waveform select enable flag */ - - int T[2]; /* timer counters */ - UINT8 st[2]; /* timer enable */ #if BUILD_Y8950 - /* Delta-T ADPCM unit (Y8950) */ - YM_DELTAT *deltat; +#include "ymdeltat.h" - /* Keyboard / I/O interface unit*/ - UINT8 portDirection; - UINT8 portLatch; - OPL_PORTHANDLER_R porthandler_r; - OPL_PORTHANDLER_W porthandler_w; - int port_param; - OPL_PORTHANDLER_R keyboardhandler_r; - OPL_PORTHANDLER_W keyboardhandler_w; - int keyboard_param; -#endif - - /* external event callback handlers */ - OPL_TIMERHANDLER TimerHandler; /* TIMER handler */ - int TimerParam; /* TIMER parameter */ - OPL_IRQHANDLER IRQHandler; /* IRQ handler */ - int IRQParam; /* IRQ parameter */ - OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */ - int UpdateParam; /* stream update parameter */ - - UINT8 type; /* chip type */ - UINT8 address; /* address register */ - UINT8 status; /* status flag */ - UINT8 statusmask; /* status mask */ - UINT8 mode; /* Reg.08 : CSM,notesel,etc. */ - - int clock; /* master clock (Hz) */ - int rate; /* sampling rate (Hz) */ - double freqbase; /* frequency base */ - double TimerBase; /* Timer base time (==sampling time)*/ -} FM_OPL; - - -/* ---------- Generic interface section ---------- */ -#define OPL_TYPE_YM3526 (0) -#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL) -#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO) - -FM_OPL *OPLCreate(int type, int clock, int rate); -void OPLDestroy(FM_OPL *OPL); -void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset); -void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param); -void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param); /* Y8950 port handlers */ -void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param); -void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param); +void Y8950SetPortHandler(int which, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, int param); +void Y8950SetKeyboardHandler(int which, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, int param); +void Y8950SetDeltaTMemory(int which, void * deltat_rom, int deltat_rom_size ); -void OPLResetChip(FM_OPL *OPL); -int OPLWrite(FM_OPL *OPL,int a,int v); -unsigned char OPLRead(FM_OPL *OPL,int a); -int OPLTimerOver(FM_OPL *OPL,int c); +int Y8950Init (int num, int clock, int rate); +void Y8950Shutdown (void); +void Y8950ResetChip (int which); +int Y8950Write (int which, int a, int v); +unsigned char Y8950Read (int which, int a); +int Y8950TimerOver (int which, int c); +void Y8950UpdateOne (int which, INT16 *buffer, int length); + +void Y8950SetTimerHandler (int which, OPL_TIMERHANDLER TimerHandler, int channelOffset); +void Y8950SetIRQHandler (int which, OPL_IRQHANDLER IRQHandler, int param); +void Y8950SetUpdateHandler (int which, OPL_UPDATEHANDLER UpdateHandler, int param); + +#endif - -/* YM3626/YM3812 local section */ -void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); - -void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length); #endif