[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 23/25] fm: YM2608 core forked from MAME 0.59
From: |
TAKEDA, toshiya |
Subject: |
[Qemu-devel] [PATCH v3 23/25] fm: YM2608 core forked from MAME 0.59 |
Date: |
Thu, 29 Oct 2009 01:50:36 +0900 |
diff --git a/qemu/hw/fm.c b/qemu/hw/fm.c
new file mode 100644
index 0000000..0a78bbd
--- /dev/null
+++ b/qemu/hw/fm.c
@@ -0,0 +1,2579 @@
+#define YM2610B_WARNING
+
+/* YM2608 rhythm data is PCM ,not an ADPCM */
+#define YM2608_RHYTHM_PCM
+
+/*
+**
+** File: fm.c -- software implementation of Yamaha FM sound generator
+**
+** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 0.37f
+**
+*/
+
+/*
+** History:
+**
+** 27-10-2009 TAKEDA, toshiya
+** - forked from MAME 0.59
+** - remove any codes not used for YM2608
+**
+** 18-12-2001 Jarek Burczynski:
+** - added SSG-EG support (verified on real chip)
+**
+** 12-08-2001 Jarek Burczynski:
+** - corrected sin_tab and tl_tab data (verified on real chip)
+** - corrected feedback calculations (verified on real chip)
+** - corrected phase generator calculations (verified on real chip)
+** - corrected envelope generator calculations (verified on real chip)
+** - corrected FM volume level (YM2610 and YM2610B).
+** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B,
YM2612) :
+** this was needed to calculate YM2610 FM channels output correctly.
+** (Each FM channel is calculated as in other chips, but the output of the
channel
+** gets shifted right by one *before* sending to accumulator. That was
impossible to do
+** with previous implementation).
+**
+** 23-07-2001 Jarek Burczynski, Nicola Salmoria:
+** - corrected YM2610 ADPCM type A algorithm and tables (verified on real
chip)
+**
+** 11-06-2001 Jarek Burczynski:
+** - corrected end of sample bug in OPNB_ADPCM_CALC_CHA.
+** Real YM2610 checks for equality between current and end addresses (only
20 LSB bits).
+**
+** 08-12-98 hiro-shi:
+** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA
+** move ROM limit check.(CALC_CH? -> 2610Write1/2)
+** test program (ADPCMB_TEST)
+** move ADPCM A/B end check.
+** ADPCMB repeat flag(no check)
+** change ADPCM volume rate (8->16) (32->48).
+**
+** 09-12-98 hiro-shi:
+** change ADPCM volume. (8->16, 48->64)
+** replace ym2610 ch0/3 (YM-2610B)
+** init cur_chip (restart bug fix)
+** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff.
+** add ADPCM_SHIFT_MASK
+** change ADPCMA_DECODE_MIN/MAX.
+*/
+
+
+
+/*
+ TO DO:
+!!!!!!! CORRECT FIRST MISSING CREDIT SOUND IN GIGANDES (DELTA-T module,
when DELTAN register = 0) !!!!!!
+ - use real sample rate and let mixer.c do the sample rate
convertion
+
+ no check:
+ YM2608 rhythm sound
+ YM2151 CSM speech mode
+
+ no support:
+ YM2608 status mask (register :0x110)
+ YM2608 RYTHM sound
+ YM2608 PCM memory data access , DELTA-T-ADPCM with PCM port
+ YM2151 CSM speech mode with internal timer
+
+ preliminary :
+ key scale level rate (?)
+ YM2151 noise mode (CH7.OP4)
+ LFO contoller (YM2612/YM2610/YM2608/YM2151)
+
+ note:
+ OPN OPM
+ fnum fM * 2^20 / (fM/(12*n))
+ TimerOverA ( 12*n)*(1024-NA)/fM 64*(1024-Na)/fM
+ TimerOverB (192*n)*(256-NB)/fM 1024*(256-Nb)/fM
+ output bits 10bit<<3bit 16bit * 2ch
(YM3012=10bit<<3bit)
+ sampling rate fFM / (12*prescaler) fM / 64
+*/
+
+/************************************************************************/
+/* comment of hiro-shi(Hiromitsu Shioya) */
+/* YM2610(B) = OPN-B */
+/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/************************************************************************/
+
+/* This version of fm.c is a fork of the MAME 0.59 one, relicensed under the
LGPL.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define INLINE static inline
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <math.h>
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "ay8910.h"
+#include "fm.h"
+/* include external DELTA-T ADPCM unit */
+#include "ymdeltat.h" /* DELTA-T ADPCM UNIT */
+
+#define logerror(...)
+
+/* for busy flag emulation , function FM_GET_TIME_NOW() should be */
+/* return the present time in second unit with (double) value */
+#if FM_BUSY_FLAG_SUPPORT
+#define FM_GET_TIME_NOW() ((double)qemu_get_clock(rt_clock) / 1000.0)
+#endif
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+/* -------------------- sound quality define selection --------------------- */
+#define FREQ_SH 16 /* 16.16 fixed point (frequency
calculations) */
+#define ENV_SH 16 /* 16.16 fixed point (envelope
calculations) */
+#define LFO_SH 23 /* 9.23 fixed point (LFO calculations)
*/
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations)
*/
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+#define ENV_MASK ((1<<ENV_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+#define ENV_QUIET ((int)(0x68/(ENV_STEP)))
+
+#define MAX_ATT_INDEX ((ENV_LEN<<ENV_SH)-1) /* 1023.ffff */
+#define MIN_ATT_INDEX ( (1<<ENV_SH)-1) /* 0.ffff */
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+/* LFO table entries */
+#define LFO_ENT 512
+#define LFO_RATE 0x10000
+#define PMS_RATE 0x400
+/* LFO runtime work */
+static UINT32 lfo_amd;
+static INT32 lfo_pmd;
+static UINT32 LFOCnt,LFOIncr; /* LFO Phase Generator */
+/* OPN LFO waveform table */
+static INT32 OPN_LFO_wave[LFO_ENT];
+
+/* -------------------- tables --------------------- */
+
+/* sustain level table (3db per step) */
+/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */
+/* 1, 2, 4, 8, 16, 32, 64 (value)*/
+/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/
+
+/* 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<<ENV_SH) )
+static const UINT32 SL_TABLE[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+/* TL_TAB_LEN is calculated as:
+* 13 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (13*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+/* sin waveform table in 'decibel' scale */
+static unsigned int sin_tab[SIN_LEN];
+
+
+
+#define OPM_DTTABLE OPN_DTTABLE
+static UINT8 OPN_DTTABLE[4 * 32]={
+/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point
format)*/
+/* FD=0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* FD=1 */
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+/* FD=2 */
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+ 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16,
+/* FD=3 */
+ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+ 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22
+};
+
+
+
+/* output final shift */
+#if (FM_SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+/* -------------------- local defines , macros --------------------- */
+/* register number to channel number , slot offset */
+#define OPN_CHAN(N) (N&3)
+#define OPN_SLOT(N) ((N>>2)&3)
+#define OPM_CHAN(N) (N&7)
+#define OPM_SLOT(N) ((N>>3)&3)
+
+/* slot number */
+#define SLOT1 0
+#define SLOT2 2
+#define SLOT3 1
+#define SLOT4 3
+
+/* bit0 = Right enable , bit1 = Left enable */
+#define OUTD_RIGHT 1
+#define OUTD_LEFT 2
+#define OUTD_CENTER 3
+
+
+
+/* ---------- debug section ------------------- */
+/* save output as raw 16-bit sample */
+/* #define SAVE_SAMPLE */
+
+#ifdef SAVE_SAMPLE
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+
+/* ---------- OPN / OPM one channel ---------- */
+typedef struct fm_slot {
+ INT32 *DT; /* detune :DT_TABLE[DT]
*/
+ int DT2; /* multiple,Detune2:(DT2<<4)|ML for OPM
*/
+ UINT8 KSR; /* key scale rate :3-KSR
*/
+ UINT8 ARval; /* current AR
*/
+ const UINT32 *AR; /* attack rate :&AR_TABLE[AR<<1] */
+ const UINT32 *DR; /* decay rate :&DR_TABLE[DR<<1] */
+ const UINT32 *SR; /* sustain rate :&DR_TABLE[SR<<1] */
+ const UINT32 *RR; /* release rate :&DR_TABLE[RR<<2+2] */
+ UINT8 ksr; /* key scale rate :kcode>>(3-KSR)
*/
+ UINT32 mul; /* multiple :ML_TABLE[ML]
*/
+
+ /* Phase Generator */
+ UINT32 Cnt; /* phase counter
*/
+ UINT32 Incr; /* phase step
*/
+
+ /* Envelope Generator */
+ UINT8 state; /* phase type
*/
+ UINT32 TL; /* total level :TL << 3
*/
+ 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_sr; /* envelope step for Sustain
*/
+ UINT32 delta_rr; /* envelope step for Release
*/
+ UINT8 SEG; /* SSG-EG waveform
*/
+ UINT8 SEGn; /* SSG-EG negated output
*/
+
+ UINT32 key; /* 0=last key was KEY OFF, 1=KEY ON
*/
+
+ /* LFO */
+ UINT32 amon; /* AMS enable flag
*/
+ UINT32 ams; /* AMS depth level of this SLOT
*/
+}FM_SLOT;
+
+typedef struct fm_chan {
+ FM_SLOT SLOT[4];
+ UINT8 ALGO; /* algorithm
*/
+ UINT8 FB; /* feedback shift
*/
+ INT32 op1_out[2]; /* op1 output for feedback
*/
+ /* algorithm (connection) */
+ INT32 *connect1; /* pointer of SLOT1 output
*/
+ INT32 *connect2; /* pointer of SLOT2 output
*/
+ INT32 *connect3; /* pointer of SLOT3 output
*/
+ INT32 *connect4; /* pointer of SLOT4 output
*/
+ /* LFO */
+ INT32 pms; /* PMS depth channel level
*/
+ UINT32 ams; /* AMS depth channel level
*/
+ /* Phase Generator */
+ UINT32 fc; /* fnum,blk:adjusted to sample rate
*/
+ UINT8 kcode; /* key code:
*/
+} FM_CH;
+
+/* OPN/OPM common state */
+typedef struct fm_state {
+ UINT8 index; /* chip index (number of chip) */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ int TimerPres; /* timer prescaler */
+#if FM_BUSY_FLAG_SUPPORT
+ double BusyExpire; /* ExpireTime of Busy clear */
+#endif
+ UINT8 address; /* address register */
+ UINT8 irq; /* interrupt level */
+ UINT8 irqmask; /* irq mask */
+ UINT8 status; /* status flag */
+ UINT32 mode; /* mode CSM / 3SLOT */
+ UINT8 prescaler_sel;/* prescaler selector */
+ UINT8 fn_h; /* freq latch */
+ int TA; /* timer a
*/
+ int TAC; /* timer a counter */
+ UINT8 TB; /* timer b
*/
+ int TBC; /* timer b counter */
+ /* local time tables */
+ INT32 DT_TABLE[8][32]; /* DeTune table */
+ UINT32 eg_tab [32+64+32]; /* Envelope Generator rates (32 + 64
rates + 32 RKS) */
+ /* Extention Timer and IRQ handler */
+ FM_TIMERHANDLER Timer_Handler;
+ FM_IRQHANDLER IRQ_Handler;
+}FM_ST;
+
+
+/* -------------------- state --------------------- */
+
+/* some globals */
+#define TYPE_SSG 0x01 /* SSG support */
+//#define xxxxxx 0x02 /* not used */
+#define TYPE_LFOPAN 0x04 /* OPN type LFO and PAN */
+#define TYPE_6CH 0x08 /* FM 6CH / 3CH */
+#define TYPE_DAC 0x10 /* YM2612's DAC device */
+#define TYPE_ADPCM 0x20 /* two ADPCM units */
+
+#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM)
+
+/* current chip state */
+static void *cur_chip = 0; /* pointer of current chip struct */
+static FM_ST *State; /* basic status */
+static FM_CH *cch[8]; /* pointer of FM channels */
+
+
+/* runtime work */
+static INT32 out_fm[8]; /* outputs of working channels
*/
+static INT32 out_adpcm[4]; /* channel output NONE,LEFT,RIGHT or
CENTER for YM2608 ADPCM */
+static INT32 out_delta[4]; /* channel output NONE,LEFT,RIGHT or
CENTER for YM2608 DELTAT*/
+static INT32 pg_in2,pg_in3,pg_in4; /* PG input of SLOTs */
+
+
+/* -------------------- log output -------------------- */
+/* log output level */
+#define LOG_ERR 3 /* ERROR */
+#define LOG_WAR 2 /* WARNING */
+#define LOG_INF 1 /* INFORMATION */
+#define LOG_LEVEL LOG_INF
+
+#ifndef __RAINE__
+#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x
+#endif
+
+/* ----- limitter ----- */
+#define Limit(val, max,min) { \
+ if ( val > max ) val = max; \
+ else if ( val < min ) val = min; \
+}
+
+/* ----- buffering one of data(STEREO chip) ----- */
+#if FM_STEREO_MIX
+/* stereo mixing */
+#define FM_BUFFERING_STEREO \
+{
\
+ /* get left & right output with clipping */ \
+ out_ch[OUTD_LEFT] += out_ch[OUTD_CENTER];
\
+ Limit( out_ch[OUTD_LEFT] , MAXOUT, MINOUT ); \
+ out_ch[OUTD_RIGHT] += out_ch[OUTD_CENTER];
\
+ Limit( out_ch[OUTD_RIGHT], MAXOUT, MINOUT ); \
+ /* buffering */
\
+ *bufL++ = out_ch[OUTD_LEFT] >>FINAL_SH; \
+ *bufL++ = out_ch[OUTD_RIGHT]>>FINAL_SH; \
+}
+#else
+/* stereo separate */
+#define FM_BUFFERING_STEREO \
+{
\
+ /* get left & right output with clipping */ \
+ out_ch[OUTD_LEFT] += out_ch[OUTD_CENTER];
\
+ Limit( out_ch[OUTD_LEFT] , MAXOUT, MINOUT ); \
+ out_ch[OUTD_RIGHT] += out_ch[OUTD_CENTER];
\
+ Limit( out_ch[OUTD_RIGHT], MAXOUT, MINOUT ); \
+ /* buffering */
\
+ bufL[i] = out_ch[OUTD_LEFT] >>FINAL_SH; \
+ bufR[i] = out_ch[OUTD_RIGHT]>>FINAL_SH; \
+}
+#endif
+
+#if FM_INTERNAL_TIMER
+/* ----- internal timer mode , update timer */
+
+/* ---------- calculate timer A ---------- */
+ #define INTERNAL_TIMER_A(ST,CSM_CH)
\
+ {
\
+ if( ST->TAC && (ST->Timer_Handler==0) ) \
+ if( (ST->TAC -= (int)(ST->freqbase*4096)) <= 0 )
\
+ {
\
+ TimerAOver( ST );
\
+ /* CSM mode total level latch and auto key on
*/ \
+ if( ST->mode & 0x80 )
\
+ CSMKeyControll( CSM_CH );
\
+ }
\
+ }
+/* ---------- calculate timer B ---------- */
+ #define INTERNAL_TIMER_B(ST,step)
\
+ {
\
+ if( ST->TBC && (ST->Timer_Handler==0) )
\
+ if( (ST->TBC -= (int)(ST->freqbase*4096*step)) <= 0 )
\
+ TimerBOver( ST );
\
+ }
+#else /* FM_INTERNAL_TIMER */
+/* external timer mode */
+#define INTERNAL_TIMER_A(ST,CSM_CH)
+#define INTERNAL_TIMER_B(ST,step)
+#endif /* FM_INTERNAL_TIMER */
+
+/* --------------------- subroutines --------------------- */
+/* status set and IRQ handling */
+INLINE void FM_STATUS_SET(FM_ST *ST,int flag)
+{
+ /* set status flag */
+ ST->status |= flag;
+ if ( !(ST->irq) && (ST->status & ST->irqmask) )
+ {
+ ST->irq = 1;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->index,1);
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void FM_STATUS_RESET(FM_ST *ST,int flag)
+{
+ /* reset status flag */
+ ST->status &=~flag;
+ if ( (ST->irq) && !(ST->status & ST->irqmask) )
+ {
+ ST->irq = 0;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->index,0);
+ }
+}
+
+/* IRQ mask set */
+INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag)
+{
+ ST->irqmask = flag;
+ /* IRQ handling check */
+ FM_STATUS_SET(ST,0);
+ FM_STATUS_RESET(ST,0);
+}
+
+#if FM_BUSY_FLAG_SUPPORT
+INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST)
+{
+ if( ST->BusyExpire )
+ {
+ if( (ST->BusyExpire - FM_GET_TIME_NOW()) > 0)
+ return ST->status | 0x80; /* with busy */
+ /* expire */
+ ST->BusyExpire = 0;
+ }
+ return ST->status;
+}
+INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock )
+{
+ ST->BusyExpire = FM_GET_TIME_NOW() + (busyclock * ST->TimerPres /
ST->clock);
+}
+#define FM_BUSY_CLEAR(ST) ((ST)->BusyExpire = 0)
+#else
+#define FM_STATUS_FLAG(ST) ((ST)->status)
+#define FM_BUSY_SET(ST,bclock) {}
+#define FM_BUSY_CLEAR(ST) {}
+#endif
+
+
+/* Envelope Generator phases */
+
+//#define EG_DEC_SSG_EG 6
+//#define EG_SUS_SSG_EG 5
+//#define EG_HLD_SSG_EG 4
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+
+
+INLINE void FM_KEYON(FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if( !SLOT->key )
+ {
+ SLOT->key = 1;
+ /* restart Phase Generator */
+ SLOT->Cnt = 0;
+ /* phase -> Attack */
+ SLOT->state = EG_ATT;
+ }
+}
+
+INLINE void FM_KEYOFF(FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if( SLOT->key )
+ {
+ SLOT->key = 0;
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+}
+
+/* set algorithm connection */
+static void setup_connection( FM_CH *CH, int ch )
+{
+ INT32 *carrier = &out_fm[ch];
+
+ switch( CH->ALGO ){
+ case 0:
+ /* PG---S1---S2---S3---S4---OUT */
+ CH->connect1 = &pg_in2;
+ CH->connect2 = &pg_in3;
+ CH->connect3 = &pg_in4;
+ break;
+ case 1:
+ /* PG---S1-+-S3---S4---OUT */
+ /* PG---S2-+ */
+ CH->connect1 = &pg_in3;
+ CH->connect2 = &pg_in3;
+ CH->connect3 = &pg_in4;
+ break;
+ case 2:
+ /* PG---S1------+-S4---OUT */
+ /* PG---S2---S3-+ */
+ CH->connect1 = &pg_in4;
+ CH->connect2 = &pg_in3;
+ CH->connect3 = &pg_in4;
+ break;
+ case 3:
+ /* PG---S1---S2-+-S4---OUT */
+ /* PG---S3------+ */
+ CH->connect1 = &pg_in2;
+ CH->connect2 = &pg_in4;
+ CH->connect3 = &pg_in4;
+ break;
+ case 4:
+ /* PG---S1---S2-+--OUT */
+ /* PG---S3---S4-+ */
+ CH->connect1 = &pg_in2;
+ CH->connect2 = carrier;
+ CH->connect3 = &pg_in4;
+ break;
+ case 5:
+ /* +-S2-+ */
+ /* PG---S1-+-S3-+-OUT */
+ /* +-S4-+ */
+ CH->connect1 = 0; /* special case */
+ CH->connect2 = carrier;
+ CH->connect3 = carrier;
+ break;
+ case 6:
+ /* PG---S1---S2-+ */
+ /* PG--------S3-+-OUT */
+ /* PG--------S4-+ */
+ CH->connect1 = &pg_in2;
+ CH->connect2 = carrier;
+ CH->connect3 = carrier;
+ break;
+ case 7:
+ /* PG---S1-+ */
+ /* PG---S2-+-OUT */
+ /* PG---S3-+ */
+ /* PG---S4-+ */
+ CH->connect1 = carrier;
+ CH->connect2 = carrier;
+ CH->connect3 = carrier;
+ }
+ CH->connect4 = carrier;
+}
+
+/* set detune & multiple */
+INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v)
+{
+ SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1;
+ SLOT->DT = ST->DT_TABLE[(v>>4)&7];
+ CH->SLOT[SLOT1].Incr=-1;
+}
+
+/* set total level */
+INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v,int csmflag)
+{
+ SLOT->TL = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
+}
+
+/* set attack rate & key scale */
+INLINE void set_ar_ksr(FM_CH *CH,FM_SLOT *SLOT,int v,UINT32 *eg_tab)
+{
+ SLOT->KSR = 3-(v>>6);
+ SLOT->ARval = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+ SLOT->AR = &eg_tab[ SLOT->ARval ];
+
+ if ((SLOT->ARval + SLOT->ksr) < 32+62)
+ SLOT->delta_ar = SLOT->AR[SLOT->ksr];
+ else
+ SLOT->delta_ar = MAX_ATT_INDEX+1;
+
+ CH->SLOT[SLOT1].Incr=-1; /* Optimize: only set this, if new
SLOT->KSR is different */
+}
+
+/* set decay rate */
+INLINE void set_dr(FM_SLOT *SLOT,int v,UINT32 *eg_tab)
+{
+ SLOT->DR = (v&0x1f) ? &eg_tab[32 + ((v&0x1f)<<1)] : &eg_tab[0];
+ SLOT->delta_dr = SLOT->DR[SLOT->ksr];
+}
+
+/* set sustain rate */
+INLINE void set_sr(FM_SLOT *SLOT,int v,UINT32 *eg_tab)
+{
+ SLOT->SR = (v&0x1f) ? &eg_tab[32 + ((v&0x1f)<<1)] : &eg_tab[0];
+ SLOT->delta_sr = SLOT->SR[SLOT->ksr];
+}
+
+/* set release rate */
+INLINE void set_sl_rr(FM_SLOT *SLOT,int v,UINT32 *eg_tab)
+{
+ SLOT->sl = SL_TABLE[ v>>4 ];
+ SLOT->RR = &eg_tab[34 + ((v&0x0f)<<2)];
+ SLOT->delta_rr = SLOT->RR[SLOT->ksr];
+}
+
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) +
(pm<<15))) >> 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)
+{
+ UINT32 p;
+ INT32 i;
+
+ i = (phase & ~FREQ_MASK) + 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];
+
+/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8)
);*/
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+
+
+
+INLINE unsigned int calc_eg(FM_SLOT *SLOT)
+{
+ unsigned int out;
+ unsigned int swap_flag = 0;
+
+
+ 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<<ENV_SH);
/* adjust by number of levels */
+ do
+ {
+ tmp_volume = tmp_volume - (1<<ENV_SH) -
((tmp_volume>>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->SEG&0x08) /* SSG EG type envelope selected */
+ {
+ INT32 step = SLOT->volume;
+ SLOT->volume += SLOT->delta_dr;
+ step = (((UINT32)SLOT->volume)>>ENV_SH) -
(step>>ENV_SH); /* number of levels passed since last time */
+
+ if ( (SLOT->volume += ((step*3)<<ENV_SH)) >=
SLOT->sl )
+ {
+ SLOT->volume = SLOT->sl; /* this
is not quite correct (checked) */
+ SLOT->state = EG_SUS;
+ }
+ }
+ else
+ {
+ 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 */
+
+ if (SLOT->SEG&0x08) /* SSG EG type envelope selected */
+ {
+ INT32 step = SLOT->volume;
+ SLOT->volume += SLOT->delta_sr;
+ step = (((UINT32)SLOT->volume)>>ENV_SH) -
(step>>ENV_SH); /* number of levels passed since last time */
+
+ if ( (SLOT->volume += ((step*3)<<ENV_SH)) >
MAX_ATT_INDEX )
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+
+ if (SLOT->SEG&0x01) /* bit 0 = hold */
+ {
+ if (SLOT->SEGn&1) /* have we
swapped once ??? */
+ {
+ /* yes, so do nothing, just
hold current level */
+ }
+ else
+ swap_flag = (SLOT->SEG&0x02) |
1 ; /* bit 1 = alternate */
+
+ }
+ else
+ {
+ /* same as KEY-ON operation */
+
+ /* restart of the Phase Generator
should be here,
+ only if AR is not maximum ??? */
+ /*SLOT->Cnt = 0;*/
+
+ /* phase -> Attack */
+ SLOT->state = EG_ATT;
+
+ swap_flag = (SLOT->SEG&0x02); /* bit 1
= alternate */
+ }
+ }
+
+ }
+ else
+ {
+ if ( (SLOT->volume += SLOT->delta_sr) > MAX_ATT_INDEX )
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ /* do not change the EG phase (verified on real
chip) */
+ }
+ };
+ 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;
+
+ }
+
+ out = SLOT->TL + (((unsigned int)SLOT->volume)>>ENV_SH);
+
+ if (SLOT->SEGn&2) /* negate output (changes come from alternate
bit, init comes from attack bit) */
+ out ^= 1023;
+
+ SLOT->SEGn ^= swap_flag;
+
+
+ if(SLOT->ams)
+ out += (SLOT->ams*lfo_amd/LFO_RATE);
+
+ return out;
+}
+
+
+/* ---------- calculate one channel ---------- */
+INLINE void FM_CALC_CH( FM_CH *CH )
+{
+ unsigned int eg_out1,eg_out2,eg_out3,eg_out4; /* envelope output */
+
+ /* Phase Generator */
+ pg_in2 = pg_in3 = pg_in4 = 0;
+
+ /* Envelope Generator */
+ eg_out1 = calc_eg(&CH->SLOT[SLOT1]);
+ eg_out2 = calc_eg(&CH->SLOT[SLOT2]);
+ eg_out3 = calc_eg(&CH->SLOT[SLOT3]);
+ eg_out4 = calc_eg(&CH->SLOT[SLOT4]);
+
+ /* Connection */
+ {
+ INT32 out = CH->op1_out[0] + CH->op1_out[1];
+ CH->op1_out[0] = CH->op1_out[1];
+
+ if( !CH->connect1 ){
+ /* algorithm 5 */
+ pg_in2 = pg_in3 = pg_in4 = CH->op1_out[0];
+ }else{
+ /* other algorithms */
+ *CH->connect1 += CH->op1_out[0];
+ }
+
+ CH->op1_out[1] = 0;
+ if( eg_out1 < ENV_QUIET ) /* SLOT 1 */
+ CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].Cnt, eg_out1,
(out<<CH->FB) );
+ }
+
+ if( eg_out2 < ENV_QUIET ) /* SLOT 2 */
+ *CH->connect2 += op_calc(CH->SLOT[SLOT2].Cnt, eg_out2, pg_in2);
+
+ if( eg_out3 < ENV_QUIET ) /* SLOT 3 */
+ *CH->connect3 += op_calc(CH->SLOT[SLOT3].Cnt, eg_out3, pg_in3);
+
+ if( eg_out4 < ENV_QUIET ) /* SLOT 4 */
+ *CH->connect4 += op_calc(CH->SLOT[SLOT4].Cnt, eg_out4, pg_in4);
+
+
+ /* update phase counters AFTER output calculations */
+ {
+ INT32 pms = lfo_pmd * CH->pms / LFO_RATE;
+ if(pms)
+ {
+ CH->SLOT[SLOT1].Cnt += CH->SLOT[SLOT1].Incr +
(INT32)(pms * CH->SLOT[SLOT1].Incr) / PMS_RATE;
+ CH->SLOT[SLOT2].Cnt += CH->SLOT[SLOT2].Incr +
(INT32)(pms * CH->SLOT[SLOT2].Incr) / PMS_RATE;
+ CH->SLOT[SLOT3].Cnt += CH->SLOT[SLOT3].Incr +
(INT32)(pms * CH->SLOT[SLOT3].Incr) / PMS_RATE;
+ CH->SLOT[SLOT4].Cnt += CH->SLOT[SLOT4].Incr +
(INT32)(pms * CH->SLOT[SLOT4].Incr) / PMS_RATE;
+ }
+ else
+ {
+ CH->SLOT[SLOT1].Cnt += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].Cnt += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].Cnt += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].Cnt += CH->SLOT[SLOT4].Incr;
+ }
+ }
+}
+
+/* ---------- update phase increment counter of operator ---------- */
+INLINE void CALC_FCSLOT(FM_SLOT *SLOT , int fc , int kc )
+{
+ int ksr;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = ((fc+SLOT->DT[kc])*SLOT->mul) >> 1;
+
+ ksr = kc >> SLOT->KSR;
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+ /* calculate envelope generator rates */
+ if ((SLOT->ARval + ksr) < 32+62)
+ SLOT->delta_ar = SLOT->AR[ksr];
+ else
+ SLOT->delta_ar = MAX_ATT_INDEX+1;
+ SLOT->delta_dr = SLOT->DR[ksr];
+ SLOT->delta_sr = SLOT->SR[ksr];
+ SLOT->delta_rr = SLOT->RR[ksr];
+ }
+}
+
+/* ---------- update phase increments counters ---------- */
+INLINE void OPN_CALC_FCOUNT(FM_CH *CH )
+{
+ if( CH->SLOT[SLOT1].Incr==-1){
+ int fc = CH->fc;
+ int kc = CH->kcode;
+ CALC_FCSLOT(&CH->SLOT[SLOT1] , fc , kc );
+ CALC_FCSLOT(&CH->SLOT[SLOT2] , fc , kc );
+ CALC_FCSLOT(&CH->SLOT[SLOT3] , fc , kc );
+ CALC_FCSLOT(&CH->SLOT[SLOT4] , fc , kc );
+ }
+}
+
+/* ----------- initialize time tables ----------- */
+static void init_timetables( FM_ST *ST , UINT8 *DTTABLE )
+{
+ int i,d;
+ double rate;
+
+#if 0
+ logerror("FM.C: samplerate=%8i chip clock=%8i freqbase=%f \n",
+ ST->rate, ST->clock, ST->freqbase );
+#endif
+
+ /* DeTune table */
+ for (d = 0;d <= 3;d++){
+ for (i = 0;i <= 31;i++){
+ rate = ((double)DTTABLE[d*32 + i]) * SIN_LEN *
ST->freqbase * (1<<FREQ_SH) / ((double)(1<<20));
+ ST->DT_TABLE[d][i] = (INT32) rate;
+ ST->DT_TABLE[d+4][i] = (INT32)-rate;
+#if 0
+ logerror("FM.C: DT [%2i %2i] = %8x \n", d, i,
ST->DT_TABLE[d][i] );
+#endif
+ }
+ }
+
+ /* calculate Envelope Generator rate table */
+ for (i=0; i<34; i++)
+ ST->eg_tab[i] = 0;
/* infinity */
+
+ for (i=2; i<64; i++)
+ {
+ rate = ST->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 /= 12.0 * 1024.0;
+ rate *= (double)(1<<ENV_SH);
+ ST->eg_tab[32+i] = rate;
+#if 0
+ logerror("FM.C: Rate %2i %1i Decay [real %11.4f ms][emul
%11.4f ms][d=%08x]\n",i>>2, i&3,
+ ( ((double)(ENV_LEN<<ENV_SH)) / rate )
* (1000.0 / (double)ST->rate),
+ ( ((double)(ENV_LEN<<ENV_SH)) /
(double)ST->eg_tab[32+i] ) * (1000.0 / (double)ST->rate), ST->eg_tab[32+i] );
+#endif
+ }
+
+ for (i=0; i<32; i++)
+ {
+ ST->eg_tab[ 32+64+i ] = ST->eg_tab[32+63];
+ }
+}
+
+/* ---------- reset one channel ---------- */
+static void reset_channel( FM_ST *ST , FM_CH *CH , int chan )
+{
+ int c,s;
+
+ ST->mode = 0; /* normal mode */
+ FM_STATUS_RESET(ST,0xff);
+ ST->TA = 0;
+ ST->TAC = 0;
+ ST->TB = 0;
+ ST->TBC = 0;
+
+ for( c = 0 ; c < chan ; c++ )
+ {
+ CH[c].fc = 0;
+ for(s = 0 ; s < 4 ; s++ )
+ {
+ CH[c].SLOT[s].SEG = 0;
+ CH[c].SLOT[s].SEGn = 0;
+ CH[c].SLOT[s].state= EG_OFF;
+ CH[c].SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* ---------- initialize generic tables ---------- */
+
+static void init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 2; /* 13 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ 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);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %4x", i*2, tl_tab[ x*2
/*+1*/ + i*2*TL_RES_LEN ]);
+ logerror("\n");
+ }
+ #endif
+ }
+ /*logerror("FM.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN,
(int)sizeof(tl_tab));*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * PI / SIN_LEN ); /* checked against the
real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2); /* convert to
'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2); /* convert to
'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /*
round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+ /*logerror("FM.C: sin [%4i]= %4i (tl_tab value=%5i)\n", i,
sin_tab[i],tl_tab[sin_tab[i]]);*/
+ }
+
+ /*logerror("FM.C: ENV_QUIET= %08x\n",ENV_QUIET );*/
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+}
+
+static int FMInitTable( void )
+{
+ return 1;
+}
+
+
+static void FMCloseTable( void )
+{
+#if 0
+ if( tl_tab ) free( tl_tab );
+ tl_tab = 0;
+#endif
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+ return;
+}
+
+/* OPN/OPM Mode Register Write */
+INLINE void FMSetMode( FM_ST *ST, int n, int v )
+{
+ /* b7 = CSM MODE */
+ /* b6 = 3 slot mode */
+ /* b5 = reset b */
+ /* b4 = reset a */
+ /* b3 = timer enable b */
+ /* b2 = timer enable a */
+ /* b1 = load b */
+ /* b0 = load a */
+ ST->mode = v;
+
+ /* reset Timer b flag */
+ if( v & 0x20 )
+ FM_STATUS_RESET(ST,0x02);
+ /* reset Timer a flag */
+ if( v & 0x10 )
+ FM_STATUS_RESET(ST,0x01);
+ /* load b */
+ if( v & 0x02 )
+ {
+ if( ST->TBC == 0 )
+ {
+ ST->TBC = ( 256-ST->TB)<<4;
+ /* External timer handler */
+ if (ST->Timer_Handler) (ST->Timer_Handler)(n,1,ST->TBC
* ST->TimerPres,ST->clock);
+ }
+ }
+ else
+ { /* stop timer b */
+ if( ST->TBC != 0 )
+ {
+ ST->TBC = 0;
+ if (ST->Timer_Handler)
(ST->Timer_Handler)(n,1,0,ST->clock);
+ }
+ }
+ /* load a */
+ if( v & 0x01 )
+ {
+ if( ST->TAC == 0 )
+ {
+ ST->TAC = (1024-ST->TA);
+ /* External timer handler */
+ if (ST->Timer_Handler) (ST->Timer_Handler)(n,0,ST->TAC
* ST->TimerPres,ST->clock);
+ }
+ }
+ else
+ { /* stop timer a */
+ if( ST->TAC != 0 )
+ {
+ ST->TAC = 0;
+ if (ST->Timer_Handler)
(ST->Timer_Handler)(n,0,0,ST->clock);
+ }
+ }
+}
+
+/* Timer A Overflow */
+INLINE void TimerAOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01);
+ /* clear or reload the counter */
+ ST->TAC = (1024-ST->TA);
+ if (ST->Timer_Handler) (ST->Timer_Handler)(ST->index,0,ST->TAC *
ST->TimerPres,ST->clock);
+}
+/* Timer B Overflow */
+INLINE void TimerBOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02);
+ /* clear or reload the counter */
+ ST->TBC = ( 256-ST->TB)<<4;
+ if (ST->Timer_Handler) (ST->Timer_Handler)(ST->index,1,ST->TBC *
ST->TimerPres,ST->clock);
+}
+/* CSM Key Controll */
+INLINE void CSMKeyControll(FM_CH *CH)
+{
+ /* all key off */
+ /* FM_KEYOFF(CH,SLOT1); */
+ /* FM_KEYOFF(CH,SLOT2); */
+ /* FM_KEYOFF(CH,SLOT3); */
+ /* FM_KEYOFF(CH,SLOT4); */
+ /* all key on */
+ FM_KEYON(CH,SLOT1);
+ FM_KEYON(CH,SLOT2);
+ FM_KEYON(CH,SLOT3);
+ FM_KEYON(CH,SLOT4);
+}
+
+static void FMsave_state_channel(QEMUFile* f,FM_CH *CH,int num_ch)
+{
+ int slot , ch;
+
+ for(ch=0;ch<num_ch;ch++,CH++)
+ {
+ /* channel */
+ qemu_put_be32s(f, &CH->op1_out[0]);
+ qemu_put_be32s(f, &CH->op1_out[1]);
+ qemu_put_be32s(f, &CH->fc);
+ /* slots */
+ for(slot=0;slot<4;slot++)
+ {
+ FM_SLOT *SLOT = &CH->SLOT[slot];
+ qemu_put_be32s(f, &SLOT->Cnt);
+ qemu_put_8s(f, &SLOT->state);
+ qemu_put_be32s(f, &SLOT->volume);
+ }
+ }
+}
+
+static int FMload_state_channel(QEMUFile* f,FM_CH *CH,int num_ch)
+{
+ int slot , ch;
+
+ for(ch=0;ch<num_ch;ch++,CH++)
+ {
+ /* channel */
+ qemu_get_be32s(f, &CH->op1_out[0]);
+ qemu_get_be32s(f, &CH->op1_out[1]);
+ qemu_get_be32s(f, &CH->fc);
+ /* slots */
+ for(slot=0;slot<4;slot++)
+ {
+ FM_SLOT *SLOT = &CH->SLOT[slot];
+ qemu_get_be32s(f, &SLOT->Cnt);
+ qemu_get_8s(f, &SLOT->state);
+ qemu_get_be32s(f, &SLOT->volume);
+ }
+ }
+
+ return 0;
+}
+
+static void FMsave_state_st(QEMUFile* f,FM_ST *ST)
+{
+#if FM_BUSY_FLAG_SUPPORT
+ uint64_t busy_expire;
+ memcpy(&busy_expire, &ST->BusyExpire, sizeof(double));
+ qemu_put_be64s(f, &busy_expire);
+#endif
+ qemu_put_8s(f, &ST->address);
+ qemu_put_8s(f, &ST->irq);
+ qemu_put_8s(f, &ST->irqmask);
+ qemu_put_8s(f, &ST->status);
+ qemu_put_be32s(f, &ST->mode);
+ qemu_put_8s(f, &ST->prescaler_sel);
+ qemu_put_8s(f, &ST->fn_h);
+ qemu_put_be32s(f, &ST->TA);
+ qemu_put_be32s(f, &ST->TAC);
+ qemu_put_8s(f, &ST->TB);
+ qemu_put_be32s(f, &ST->TBC);
+}
+
+static int FMload_state_st(QEMUFile* f,FM_ST *ST)
+{
+#if FM_BUSY_FLAG_SUPPORT
+ uint64_t busy_expire;
+ qemu_get_be64s(f, &busy_expire);
+ memcpy(&ST->BusyExpire, &busy_expire, sizeof(double));
+#endif
+ qemu_get_8s(f, &ST->address);
+ qemu_get_8s(f, &ST->irq);
+ qemu_get_8s(f, &ST->irqmask);
+ qemu_get_8s(f, &ST->status);
+ qemu_get_be32s(f, &ST->mode);
+ qemu_get_8s(f, &ST->prescaler_sel);
+ qemu_get_8s(f, &ST->fn_h);
+ qemu_get_be32s(f, &ST->TA);
+ qemu_get_be32s(f, &ST->TAC);
+ qemu_get_8s(f, &ST->TB);
+ qemu_get_be32s(f, &ST->TBC);
+
+ return 0;
+}
+
+/***********************************************************/
+/* OPN unit */
+/***********************************************************/
+
+/* OPN 3slot struct */
+typedef struct opn_3slot {
+ UINT32 fc[3]; /* fnum3,blk3 : calculated
*/
+ UINT8 fn_h; /* freq3 latch
*/
+ UINT8 kcode[3]; /* key code
*/
+}FM_3SLOT;
+
+/* OPN/A/B common state */
+typedef struct opn_f {
+ UINT8 type; /* chip type
*/
+ FM_ST ST; /* general state
*/
+ FM_3SLOT SL3; /* 3 slot mode state
*/
+ FM_CH *P_CH; /* pointer of CH
*/
+ unsigned int PAN[6*2]; /* fm channels output masks (0xffffffff =
enable) */
+
+ UINT32 FN_TABLE[2048]; /* fnumber->increment counter */
+ /* LFO */
+ UINT32 LFOCnt;
+ UINT32 LFOIncr;
+ UINT32 LFO_FREQ[8]; /* LFO FREQ table
*/
+} FM_OPN;
+
+/* OPN key frequency number -> key code follow table */
+/* fnum higher 4bit -> keycode lower 2bit */
+static const UINT8 OPN_FKTABLE[16]={0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3};
+
+//#define LFO_ENT 512
+//#define LFO_SH (32-9)
+//#define LFO_RATE 0x10000
+//#define PMS_RATE 0x400
+
+static int OPNInitTable(void)
+{
+ int i;
+
+ /* LFO wave table */
+ for(i=0; i<LFO_ENT; i++)
+ {
+ OPN_LFO_wave[i]= i<LFO_ENT/2 ? i*LFO_RATE/(LFO_ENT/2) :
+
(LFO_ENT-i)*LFO_RATE/(LFO_ENT/2);
+
+ /*logerror("FM.C: OPN_LFO_wave[%4i]=
%8x\n",i,OPN_LFO_wave[i]);*/
+ /* 0, 0x0100, 0x0200, 0x0300 ... 0xff00, 0x10000,
0xff00..0x0100 */
+ }
+
+ init_tables();
+
+ return FMInitTable();
+}
+
+/* ---------- prescaler set(and make time tables) ---------- */
+static void OPNSetPres(FM_OPN *OPN , int pres , int TimerPres, int SSGpres)
+{
+ int i;
+
+ /* frequency base */
+#if 1
+ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock /
OPN->ST.rate) / pres : 0;
+#else
+ OPN->ST.rate = (double)OPN->ST.clock / pres;
+ OPN->ST.freqbase = 1.0;
+#endif
+
+ /* Timer base time */
+ OPN->ST.TimerPres = TimerPres;
+ /* SSG part prescaler set */
+ if( SSGpres ) SSGClk( OPN->ST.index, OPN->ST.clock * 2 / SSGpres );
+ /* make time tables */
+ init_timetables( &OPN->ST , OPN_DTTABLE );
+ /* calculate fnumber -> increment counter table */
+ for( i=0 ; i < 2048 ; i++ )
+ {
+ /* freq table for octave 7 */
+ /* opn phase increment counter = 20bit */
+ OPN->FN_TABLE[i] = (UINT32)( (double)i * 64 * OPN->ST.freqbase
* (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while
we use 16.16 */
+#if 0
+ logerror("FM.C: FN_TABLE[%4i] = %08x (dec=%8i)\n",
+ i, OPN->FN_TABLE[i]>>6,OPN->FN_TABLE[i]>>6 );
+#endif
+ }
+
+ /* LFO freq. table */
+ {
+ /* 3.98Hz,5.56Hz,6.02Hz,6.37Hz,6.88Hz,9.63Hz,48.1Hz,72.2Hz @
8MHz */
+#define FM_LF(Hz) ((double)LFO_ENT*(1<<LFO_SH)*(Hz)/(8000000.0/144))
+ static const double freq_table[8] = {
FM_LF(3.98),FM_LF(5.56),FM_LF(6.02),FM_LF(6.37),FM_LF(6.88),FM_LF(9.63),FM_LF(48.1),FM_LF(72.2)
};
+#undef FM_LF
+ for(i=0;i<8;i++)
+ {
+ OPN->LFO_FREQ[i] = (UINT32)(freq_table[i] *
OPN->ST.freqbase);
+ }
+ }
+
+/* LOG(LOG_INF,("OPN %d set prescaler %d\n",OPN->ST.index,pres));*/
+}
+
+/* ---------- write a OPN mode register 0x20-0x2f ---------- */
+static void OPNWriteMode(FM_OPN *OPN, int r, int v)
+{
+ UINT8 c;
+ FM_CH *CH;
+
+ switch(r){
+ case 0x21: /* Test */
+ break;
+ case 0x22: /* LFO FREQ (YM2608/YM2612) */
+ if( OPN->type & TYPE_LFOPAN )
+ {
+ OPN->LFOIncr = (v&0x08) ? OPN->LFO_FREQ[v&7] : 0;
+ cur_chip = NULL;
+ }
+ break;
+ case 0x24: /* timer A High 8*/
+ OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2);
+ break;
+ case 0x25: /* timer A Low 2*/
+ OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3);
+ break;
+ case 0x26: /* timer B */
+ OPN->ST.TB = v;
+ break;
+ case 0x27: /* mode , timer controll */
+ FMSetMode( &(OPN->ST),OPN->ST.index,v );
+ break;
+ case 0x28: /* key on / off */
+ c = v&0x03;
+ if( c == 3 ) break;
+ if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3;
+ CH = OPN->P_CH;
+ CH = &CH[c];
+ /* csm mode */
+ /* if( c == 2 && (OPN->ST.mode & 0x80) ) break; */
+ if(v&0x10) FM_KEYON(CH,SLOT1); else FM_KEYOFF(CH,SLOT1);
+ if(v&0x20) FM_KEYON(CH,SLOT2); else FM_KEYOFF(CH,SLOT2);
+ if(v&0x40) FM_KEYON(CH,SLOT3); else FM_KEYOFF(CH,SLOT3);
+ if(v&0x80) FM_KEYON(CH,SLOT4); else FM_KEYOFF(CH,SLOT4);
+ break;
+ }
+}
+
+/* ---------- write a OPN register (0x30-0xff) ---------- */
+static void OPNWriteReg(FM_OPN *OPN, int r, int v)
+{
+ UINT8 c;
+ FM_CH *CH;
+ FM_SLOT *SLOT;
+
+ /* 0x30 - 0xff */
+ if( (c = OPN_CHAN(r)) == 3 ) return; /* 0xX3,0xX7,0xXB,0xXF */
+
+ if( (r >= 0x100) /* && (OPN->type & TYPE_6CH) */ ) c+=3;
+ CH = OPN->P_CH;
+ CH = &CH[c];
+
+ SLOT = &(CH->SLOT[OPN_SLOT(r)]);
+
+ switch( r & 0xf0 ) {
+ case 0x30: /* DET , MUL */
+ set_det_mul(&OPN->ST,CH,SLOT,v);
+ break;
+
+ case 0x40: /* TL */
+ set_tl(CH,SLOT,v,(c == 2) && (OPN->ST.mode & 0x80) );
+ break;
+
+ case 0x50: /* KS, AR */
+ set_ar_ksr(CH,SLOT,v,OPN->ST.eg_tab);
+ break;
+
+ case 0x60: /* DR */
+ /* bit7 = AMS_ON ENABLE(YM2612) */
+ set_dr(SLOT,v,OPN->ST.eg_tab);
+ if( OPN->type & TYPE_LFOPAN)
+ {
+ SLOT->amon = (v&0x80) ? ~0: 0;
+ SLOT->ams = CH->ams & SLOT->amon;
+ }
+ break;
+
+ case 0x70: /* SR */
+ set_sr(SLOT,v,OPN->ST.eg_tab);
+ break;
+
+ case 0x80: /* SL, RR */
+ set_sl_rr(SLOT,v,OPN->ST.eg_tab);
+ break;
+
+ case 0x90: /* SSG-EG */
+
+ SLOT->SEG = v&0x0f;
+ SLOT->SEGn = (v&0x04)>>1; /* bit 1 in SEGn = attack */
+
+ /* SSG-EG envelope shapes :
+
+ E AtAlH
+ 1 0 0 0 \\\\
+
+ 1 0 0 1 \___
+
+ 1 0 1 0 \/\/
+ ___
+ 1 0 1 1 \
+
+ 1 1 0 0 ////
+ ___
+ 1 1 0 1 /
+
+ 1 1 1 0 /\/\
+
+ 1 1 1 1 /___
+
+
+ E = SSG-EG enable
+
+
+ The shapes are generated using Attack, Decay and Sustain phases.
+
+ Each single character in the diagrams above represents this
whole
+ sequence:
+
+ - when KEY-ON = 1, normal Attack phase is generated (*without*
any
+ difference when compared to normal mode),
+
+ - later, when envelope level reaches minimum level (max volume),
+ the EG switches to Decay phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - later when envelope level passes the SL level,
+ the EG swithes to Sustain phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - finally when envelope level reaches maximum level (min
volume),
+ the EG switches to Attack phase again (depends on actual
waveform).
+
+ Important is that when switch to Attack phase occurs, the phase
counter
+ of that operator will be zeroed-out (as in normal KEY-ON) but
not always.
+ (I havent found the rule for that - perhaps only when the
output level is low)
+
+ The difference (when compared to normal Envelope Generator
mode) is
+ that the resolution in Decay and Sustain phases is 4 times
lower;
+ this results in only 256 steps instead of normal 1024.
+ In other words:
+ when SSG-EG is disabled, the step inside of the EG is one,
+ when SSG-EG is enabled, the step is four (in Decay and Sustain
phases).
+
+ Times between the level changes are the same in both modes.
+
+
+ Important:
+ Decay 1 Level (so called SL) is compared to actual SSG-EG
output, so
+ it is the same in both SSG and no-SSG modes, with this
exception:
+
+ when the SSG-EG is enabled and is generating raising levels
+ (when the EG output is inverted) the SL will be found at wrong
level !!!
+ For example, when SL=02:
+ 0 -6 = -6dB in non-inverted EG output
+ 96-6 = -90dB in inverted EG output
+ Which means that EG compares its level to SL as usual, and that
the
+ output is simply inverted afterall.
+
+
+ The Yamaha's manuals say that AR should be set to 0x1f (max
speed).
+ That is not necessary, but then EG will be generating Attack
phase.
+
+ */
+
+
+ break;
+
+ case 0xa0:
+ switch( OPN_SLOT(r) ){
+ case 0: /* 0xa0-0xa2 : FNUM1 */
+ {
+ UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8)
+ v;
+ UINT8 blk = OPN->ST.fn_h>>3;
+ /* keyscale code */
+ CH->kcode = (blk<<2)|OPN_FKTABLE[(fn>>7)];
+ /* phase increment counter */
+ CH->fc = OPN->FN_TABLE[fn]>>(7-blk);
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 1: /* 0xa4-0xa6 : FNUM2,BLK */
+ OPN->ST.fn_h = v&0x3f;
+ break;
+ case 2: /* 0xa8-0xaa : 3CH FNUM1 */
+ if( r < 0x100)
+ {
+ UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) +
v;
+ UINT8 blk = OPN->SL3.fn_h>>3;
+ /* keyscale code */
+ OPN->SL3.kcode[c]=
(blk<<2)|OPN_FKTABLE[(fn>>7)];
+ /* phase increment counter */
+ OPN->SL3.fc[c] = OPN->FN_TABLE[fn]>>(7-blk);
+ (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 3: /* 0xac-0xae : 3CH FNUM2,BLK */
+ if( r < 0x100)
+ OPN->SL3.fn_h = v&0x3f;
+ break;
+ }
+ break;
+
+ case 0xb0:
+ switch( OPN_SLOT(r) ){
+ case 0: /* 0xb0-0xb2 : FB,ALGO */
+ {
+ int feedback = (v>>3)&7;
+ CH->ALGO = v&7;
+ CH->FB = feedback ? feedback+6 : 0;
+ setup_connection( CH, c );
+ }
+ break;
+ case 1: /* 0xb4-0xb6 : L , R , AMS , PMS
(YM2612/YM2610B/YM2610/YM2608) */
+ if( OPN->type & TYPE_LFOPAN)
+ {
+
+ /* b0-2 PMS */
+ /* 0,3.4,6.7,10,14,20,40,80(cent) */
+ static const double
pmd_table[8]={0,3.4,6.7,10,14,20,40,80};
+
+ /* b4-5 AMS */
+ /* 0, 1.4, 5.9, 11.8 (dB) */
+ /* 0, 1.40625, 5.90625, 11.90625 (or 11.8125) */
+ /* 0, 15, , 63 , 127 (or 126)
in internal representation */
+
+ /* bit0, bit1, bit2, bit3, bit4, bit5,
bit6, bit7, bit8, bit9 */
+ /* 1, 2, 4, 8, 16, 32,
64, 128, 256, 512 (internal representation value)*/
+ /* 0.09375, 0.1875, 0.375, 0.75, 1.5, 3, 6,
12, 24, 48 (dB)*/
+ static const int amd_table[4]={ (int)( ((0.0
*4)/3)/ENV_STEP),
+
(int)( ((1.40625*4)/3)/ENV_STEP),
+
(int)( ((5.90625*4)/3)/ENV_STEP),
+
(int)(((11.90625*4)/3)/ENV_STEP) };
+ /* amd_table simply becomes = { 0, 15, 63, 127
} */
+
+ CH->pms = (INT32)( (1.5/1200.0)*pmd_table[v &
7] * PMS_RATE);
+
+ CH->ams = amd_table[(v>>4) & 0x03];
+ CH->SLOT[SLOT1].ams = CH->ams &
CH->SLOT[SLOT1].amon;
+ CH->SLOT[SLOT2].ams = CH->ams &
CH->SLOT[SLOT2].amon;
+ CH->SLOT[SLOT3].ams = CH->ams &
CH->SLOT[SLOT3].amon;
+ CH->SLOT[SLOT4].ams = CH->ams &
CH->SLOT[SLOT4].amon;
+
+ /* PAN : b7 = L, b6 = R */
+ OPN->PAN[ c*2 ] = (v & 0x80) ? ~0 : 0;
+ OPN->PAN[ c*2+1 ] = (v & 0x40) ? ~0 : 0;
+
+ /* LOG(LOG_INF,("OPN %d,%d : PAN %x
%x\n",n,c,OPN->PAN[c*2],OPN->PAN[c*2+1]));*/
+ }
+ break;
+ }
+ break;
+ }
+}
+
+/*
+ prescaler circuit (best guess to verified chip behaviour)
+
+ +--------------+ +-sel2-+
+ | +--|in20 |
+ +---+ | +-sel1-+ | |
+M-CLK -+-|1/2|-+--|in10 | +---+ | out|--INT_CLOCK
+ | +---+ | out|-|1/3|-|in21 |
+ +----------|in11 | +---+ +------+
+ +------+
+
+reg.2d : sel2 = in21 (select sel2)
+reg.2e : sel1 = in11 (select sel1)
+reg.2f : sel1 = in10 , sel2 = in20 (clear selector)
+reset : sel1 = in11 , sel2 = in21 (clear both)
+
+*/
+static void OPNPrescaler_w(FM_OPN *OPN , int addr, int pre_divider)
+{
+ static const int opn_pres[4] = { 2*12 , 2*12 , 6*12 , 3*12 };
+ static const int ssg_pres[4] = { 1 , 1 , 4 , 2 };
+ int sel;
+
+ switch(addr)
+ {
+ case 0: /* when reset */
+ OPN->ST.prescaler_sel = 2;
+ break;
+ case 1: /* when postload */
+ break;
+ case 0x2d: /* divider sel : select 1/1 for 1/3line */
+ OPN->ST.prescaler_sel |= 0x02;
+ break;
+ case 0x2e: /* divider sel , select 1/3line for output */
+ OPN->ST.prescaler_sel |= 0x01;
+ break;
+ case 0x2f: /* divider sel , clear both selector to 1/2,1/2 */
+ OPN->ST.prescaler_sel = 0;
+ break;
+ }
+ sel = OPN->ST.prescaler_sel & 3;
+ /* update prescaler */
+ OPNSetPres( OPN, opn_pres[sel]*pre_divider,
+ opn_pres[sel]*pre_divider,
+ ssg_pres[sel]*pre_divider );
+}
+
+/* adpcm type A struct */
+typedef struct adpcm_state {
+ UINT8 flag; /* port state
*/
+ UINT8 flagMask; /* arrived flag mask
*/
+ UINT8 now_data; /* current ROM data
*/
+ UINT32 now_addr; /* current ROM address
*/
+ UINT32 now_step;
+ UINT32 step;
+ UINT32 start; /* sample data start address*/
+ UINT32 end; /* sample data end address
*/
+ UINT8 IL; /* Instrument Level
*/
+ INT32 adpcm_acc; /* accumulator
*/
+ INT32 adpcm_step; /* step
*/
+ INT32 adpcm_out; /* (speedup) hiro-shi!!
*/
+ INT8 vol_mul; /* volume in "0.75dB" steps
*/
+ UINT8 vol_shift; /* volume in "-6dB" steps
*/
+ INT32 *pan; /* &out_adpcm[OPN_xxxx]
*/
+}ADPCM_CH;
+
+/* here's the virtual YM2608 */
+typedef struct ym2608_f {
+ UINT8 REGS[512]; /* registers
*/
+ FM_OPN OPN; /* OPN state
*/
+ FM_CH CH[6]; /* channel state
*/
+ int address1; /* address
register1 */
+/* ADPCM-A unit */
+ UINT8 *pcmbuf; /* pcm rom buffer
*/
+ UINT32 pcm_size; /* size of pcm rom
*/
+ UINT8 adpcmTL; /* adpcmA total level
*/
+ ADPCM_CH adpcm[6]; /* adpcm channels
*/
+ UINT32 adpcmreg[0x30]; /* registers
*/
+ UINT8 adpcm_arrivedEndAddress;
+ YM_DELTAT deltaT; /* Delta-T ADPCM unit
*/
+} YM2608;
+
+/**** YM2608 ADPCM defines ****/
+#define ADPCM_SHIFT (16) /* frequency step rate */
+#define ADPCMA_ADDRESS_SHIFT 8 /* adpcm A address shift */
+
+static UINT8 *pcmbufA;
+static UINT32 pcmsizeA;
+
+
+/* Algorithm and tables verified on real YM2608 */
+
+/* usual ADPCM table (16 * 1.1^N) */
+static int steps[49] =
+{
+ 16, 17, 19, 21, 23, 25, 28,
+ 31, 34, 37, 41, 45, 50, 55,
+ 60, 66, 73, 80, 88, 97, 107,
+ 118, 130, 143, 157, 173, 190, 209,
+ 230, 253, 279, 307, 337, 371, 408,
+ 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552
+};
+
+/* different from the usual ADPCM table */
+static int step_inc[8] = { -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16
};
+
+/* speedup purposes only */
+static int jedi_table[ 49*16 ];
+
+
+static void InitOPNB_ADPCMATable(void){
+
+ int step, nib;
+
+ for (step = 0; step < 49; step++)
+ {
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ int value = (2*(nib & 0x07) + 1) * steps[step] / 8;
+ jedi_table[step*16 + nib] = (nib&0x08) ? -value : value;
+ }
+ }
+}
+
+/**** ADPCM A (Non control type) ****/
+INLINE void OPNB_ADPCM_CALC_CHA( YM2608 *F2608, ADPCM_CH *ch )
+{
+ UINT32 step;
+ UINT8 data;
+
+ ch->now_step += ch->step;
+ if ( ch->now_step >= (1<<ADPCM_SHIFT) )
+ {
+ step = ch->now_step >> ADPCM_SHIFT;
+ ch->now_step &= (1<<ADPCM_SHIFT)-1;
+ do{
+ /* end check */
+ /* 11-06-2001 JB: corrected comparison. Was > instead
of == */
+ /* YM2608 checks lower 20 bits only, the 4 MSB bits are
sample bank */
+ /* Here we use 1<<21 to compensate for nibble
calculations */
+
+ if ( (ch->now_addr & ((1<<21)-1)) == ((ch->end<<1) &
((1<<21)-1)) )
+ {
+ ch->flag = 0;
+ F2608->adpcm_arrivedEndAddress |= ch->flagMask;
+ return;
+ }
+#if 0
+ if ( ch->now_addr > (pcmsizeA<<1) ) {
+ LOG(LOG_WAR,("YM2608: Attempting to play past
adpcm rom size!\n" ));
+ return;
+ }
+#endif
+ if( ch->now_addr&1 ) data = ch->now_data & 0x0f;
+ else
+ {
+ ch->now_data = *(pcmbufA+(ch->now_addr>>1));
+ data = (ch->now_data >> 4)&0x0f;
+ }
+
+ ch->now_addr++;
+
+ ch->adpcm_acc += jedi_table[ch->adpcm_step + data];
+
+ /* extend 12-bit signed int */
+ if (ch->adpcm_acc & 0x800)
+ ch->adpcm_acc |= ~0xfff;
+ else
+ ch->adpcm_acc &= 0xfff;
+
+ ch->adpcm_step += step_inc[data & 7];
+ Limit( ch->adpcm_step, 48*16, 0*16 );
+
+ }while(--step);
+
+ /**** calc pcm * volume data ****/
+ ch->adpcm_out = ((ch->adpcm_acc * ch->vol_mul) >>
ch->vol_shift) & ~3; /* multiply, shift and mask out 2 LSB bits */
+ }
+
+ /* output for work of output channels (out_adpcm[OPNxxxx])*/
+ *(ch->pan) += ch->adpcm_out;
+}
+
+/* ADPCM type A */
+static void FM_ADPCMAWrite(YM2608 *F2608,int r,int v)
+{
+ ADPCM_CH *adpcm = F2608->adpcm;
+ UINT8 c = r&0x07;
+
+ F2608->adpcmreg[r] = v&0xff; /* stock data */
+ switch( r ){
+ case 0x00: /* DM,--,C5,C4,C3,C2,C1,C0 */
+ /* F2608->port1state = v&0xff; */
+ if( !(v&0x80) ){
+ /* KEY ON */
+ for( c = 0; c < 6; c++ ){
+ if( (1<<c)&v ){
+ /**** start adpcm ****/
+ adpcm[c].step =
(UINT32)((float)(1<<ADPCM_SHIFT)*((float)F2608->OPN.ST.freqbase)/3.0);
+ adpcm[c].now_addr = adpcm[c].start<<1;
+ adpcm[c].now_step = 0;
+ adpcm[c].adpcm_acc = 0;
+ adpcm[c].adpcm_step= 0;
+ adpcm[c].adpcm_out = 0;
+ adpcm[c].flag = 1;
+ if(F2608->pcmbuf==NULL){
/* Check ROM Mapped */
+ LOG(LOG_WAR,("YM2608: ADPCM-A
rom not mapped\n"));
+ adpcm[c].flag = 0;
+ } else{
+ if(adpcm[c].end >=
F2608->pcm_size){ /* Check End in Range */
+ LOG(LOG_WAR,("YM2608:
ADPCM-A end out of range: $%08x\n",adpcm[c].end));
+ /*adpcm[c].end =
F2608->pcm_size-1;*/ /* JB: DO NOT uncomment this, otherwise you will break the
comparison in the ADPCM_CALC_CHA() */
+ }
+ if(adpcm[c].start >=
F2608->pcm_size) /* Check Start in Range */
+ {
+ LOG(LOG_WAR,("YM2608:
ADPCM-A start out of range: $%08x\n",adpcm[c].start));
+ adpcm[c].flag = 0;
+ }
+ }
+ } /*** (1<<c)&v ***/
+ } /**** for loop ****/
+ } else{
+ /* KEY OFF */
+ for( c = 0; c < 6; c++ ){
+ if( (1<<c)&v ) adpcm[c].flag = 0;
+ }
+ }
+ break;
+ case 0x01: /* B0-5 = TL */
+ F2608->adpcmTL = (v & 0x3f) ^ 0x3f;
+ for( c = 0; c < 6; c++ )
+ {
+ int volume = F2608->adpcmTL + adpcm[c].IL;
+
+ if ( volume >= 63 ) /* This is correct, 63 = quiet
*/
+ {
+ adpcm[c].vol_mul = 0;
+ adpcm[c].vol_shift = 0;
+ }
+ else
+ {
+ adpcm[c].vol_mul = 15 - (volume & 7);
/* so called 0.75 dB */
+ adpcm[c].vol_shift = 1 + (volume >> 3);
/* Yamaha engineers used the approximation: each -6 dB is close to divide by
two (shift right) */
+ }
+
+ /**** calc pcm * volume data ****/
+ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc *
adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask
out low 2 bits */
+ }
+ break;
+ default:
+ c = r&0x07;
+ if( c >= 0x06 ) return;
+ switch( r&0x38 ){
+ case 0x08: /* B7=L,B6=R, B4-0=IL */
+ {
+ int volume;
+
+ adpcm[c].IL = (v & 0x1f) ^ 0x1f;
+
+ volume = F2608->adpcmTL + adpcm[c].IL;
+
+ if ( volume >= 63 ) /* This is correct, 63 = quiet
*/
+ {
+ adpcm[c].vol_mul = 0;
+ adpcm[c].vol_shift = 0;
+ }
+ else
+ {
+ adpcm[c].vol_mul = 15 - (volume & 7);
/* so called 0.75 dB */
+ adpcm[c].vol_shift = 1 + (volume >> 3);
/* Yamaha engineers used the approximation: each -6 dB is close to divide by
two (shift right) */
+ }
+
+ adpcm[c].pan = &out_adpcm[(v>>6)&0x03];
+
+ /**** calc pcm * volume data ****/
+ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc *
adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask
out low 2 bits */
+ }
+ break;
+ case 0x10:
+ case 0x18:
+ adpcm[c].start = ( (F2608->adpcmreg[0x18 + c]*0x0100 |
F2608->adpcmreg[0x10 + c]) << ADPCMA_ADDRESS_SHIFT);
+ break;
+ case 0x20:
+ case 0x28:
+ adpcm[c].end = ( (F2608->adpcmreg[0x28 + c]*0x0100 |
F2608->adpcmreg[0x20 + c]) << ADPCMA_ADDRESS_SHIFT);
+ adpcm[c].end += (1<<ADPCMA_ADDRESS_SHIFT) - 1;
+ break;
+ }
+ }
+}
+
+static void FMsave_state_adpcma(QEMUFile* f,ADPCM_CH *adpcm)
+{
+ int ch;
+
+ for(ch=0;ch<6;ch++,adpcm++)
+ {
+ qemu_put_8s(f, &adpcm->flag);
+ qemu_put_8s(f, &adpcm->now_data);
+ qemu_put_be32s(f, &adpcm->now_addr);
+ qemu_put_be32s(f, &adpcm->now_step);
+ qemu_put_be32s(f, &adpcm->adpcm_acc);
+ qemu_put_be32s(f, &adpcm->adpcm_step);
+ qemu_put_be32s(f, &adpcm->adpcm_out);
+ }
+}
+
+static int FMload_state_adpcma(QEMUFile* f,ADPCM_CH *adpcm)
+{
+ int ch;
+
+ for(ch=0;ch<6;ch++,adpcm++)
+ {
+ qemu_get_8s(f, &adpcm->flag);
+ qemu_get_8s(f, &adpcm->now_data);
+ qemu_get_be32s(f, &adpcm->now_addr);
+ qemu_get_be32s(f, &adpcm->now_step);
+ qemu_get_be32s(f, &adpcm->adpcm_acc);
+ qemu_get_be32s(f, &adpcm->adpcm_step);
+ qemu_get_be32s(f, &adpcm->adpcm_out);
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+/* YM2608 local section
*/
+/*****************************************************************************/
+static YM2608 *FM2608=NULL; /* array of YM2608's */
+static int YM2608NumChips; /* total chip */
+
+/* YM2608 Rhythm Number */
+#define RY_BD 0
+#define RY_SD 1
+#define RY_TOP 2
+#define RY_HH 3
+#define RY_TOM 4
+#define RY_RIM 5
+
+#if 0
+/* Get next pcm data */
+INLINE int YM2608ReadADPCM(int n)
+{
+ YM2608 *F2608 = &(FM2608[n]);
+ if( F2608->ADMode & 0x20 )
+ { /* buffer memory */
+ /* F2203->OPN.ST.status |= 0x04; */
+ return 0;
+ }
+ else
+ { /* from PCM data register */
+ FM_STATUS_SET(F2608->OPN.ST,0x08); /* BRDY = 1 */
+ return F2608->ADData;
+ }
+}
+
+/* Put decoded data */
+INLINE void YM2608WriteADPCM(int n,int v)
+{
+ YM2608 *F2608 = &(FM2608[n]);
+ if( F2608->ADMode & 0x20 )
+ { /* for buffer */
+ return;
+ }
+ else
+ { /* for PCM data port */
+ F2608->ADData = v;
+ FM_STATUS_SET(F2608->OPN.ST,0x08) /* BRDY = 1 */
+ }
+}
+#endif
+
+/* ---------- IRQ flag Controll Write 0x110 ---------- */
+INLINE void YM2608IRQFlagWrite(FM_ST *ST,int n,int v)
+{
+ if( v & 0x80 )
+ { /* Reset IRQ flag */
+ FM_STATUS_RESET(ST,0xff);
+ }
+ else
+ { /* Set IRQ mask */
+ /* !!!!!!!!!! pending !!!!!!!!!! */
+ }
+}
+
+/* ---------- compatible mode & IRQ flag Controll Write 0x29 ---------- */
+static void YM2608IRQMaskWrite(FM_OPN *OPN,int v)
+{
+ /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */
+ /* extend 3ch. enable/disable */
+ if(v&0x80) OPN->type |= TYPE_6CH;
+ else OPN->type &= ~TYPE_6CH;
+ /* IRQ MASK */
+ FM_IRQMASK_SET(&OPN->ST,v&0x1f);
+}
+
+#ifdef YM2608_RHYTHM_PCM
+/**** RYTHM (PCM) ****/
+INLINE void YM2608_RYTHM( YM2608 *F2608, ADPCM_CH *ch )
+
+{
+ UINT32 step;
+
+ ch->now_step += ch->step;
+ if ( ch->now_step >= (1<<ADPCM_SHIFT) )
+ {
+ step = ch->now_step >> ADPCM_SHIFT;
+ ch->now_step &= (1<<ADPCM_SHIFT)-1;
+ /* end check */
+ if ( (ch->now_addr+step) > (ch->end<<1) ) { /*most likely
this comparison is wrong */
+ ch->flag = 0;
+ F2608->adpcm_arrivedEndAddress |= ch->flagMask;
+ return;
+ }
+ do{
+ /* get a next pcm data */
+ ch->adpcm_acc = ((short *)pcmbufA)[ch->now_addr];
+ ch->now_addr++;
+ }while(--step);
+ /**** calc pcm * volume data ****/
+ ch->adpcm_out = (ch->adpcm_acc * ch->vol_mul ) >> ch->vol_shift;
+ }
+ /* output for work of output channels (out_adpcm[OPNxxxx])*/
+ *(ch->pan) += ch->adpcm_out;
+}
+#endif /* YM2608_RHYTHM_PCM */
+
+/* ---------- update one of chip ----------- */
+void YM2608UpdateOne(int num, INT16 **buffer, int length)
+{
+ YM2608 *F2608 = &(FM2608[num]);
+ FM_OPN *OPN = &(FM2608[num].OPN);
+ YM_DELTAT *DELTAT = &(F2608[num].deltaT);
+ int i,j;
+ FMSAMPLE *bufL,*bufR;
+
+ /* setup DELTA-T unit */
+ YM_DELTAT_DECODE_PRESET(DELTAT);
+
+ /* set bufer */
+ bufL = buffer[0];
+ bufR = buffer[1];
+
+ if( (void *)F2608 != cur_chip ){
+ cur_chip = (void *)F2608;
+
+ State = &OPN->ST;
+ cch[0] = &F2608->CH[0];
+ cch[1] = &F2608->CH[1];
+ cch[2] = &F2608->CH[2];
+ cch[3] = &F2608->CH[3];
+ cch[4] = &F2608->CH[4];
+ cch[5] = &F2608->CH[5];
+ /* setup adpcm rom address */
+ pcmbufA = F2608->pcmbuf;
+ pcmsizeA = F2608->pcm_size;
+
+ LFOCnt = OPN->LFOCnt;
+ LFOIncr = OPN->LFOIncr;
+ if( !LFOIncr ) lfo_amd = lfo_pmd = 0;
+ }
+ /* update frequency counter */
+ OPN_CALC_FCOUNT( cch[0] );
+ OPN_CALC_FCOUNT( cch[1] );
+ if( (State->mode & 0xc0) ){
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1){
+ /* 3 slot mode */
+ CALC_FCSLOT(&cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] ,
OPN->SL3.kcode[1] );
+ CALC_FCSLOT(&cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] ,
OPN->SL3.kcode[2] );
+ CALC_FCSLOT(&cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] ,
OPN->SL3.kcode[0] );
+ CALC_FCSLOT(&cch[2]->SLOT[SLOT4] , cch[2]->fc ,
cch[2]->kcode );
+ }
+ }else OPN_CALC_FCOUNT( cch[2] );
+ OPN_CALC_FCOUNT( cch[3] );
+ OPN_CALC_FCOUNT( cch[4] );
+ OPN_CALC_FCOUNT( cch[5] );
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+ /* LFO */
+ if( LFOIncr )
+ {
+ lfo_amd = OPN_LFO_wave[(LFOCnt+=LFOIncr)>>LFO_SH];
+ lfo_pmd = lfo_amd-(LFO_RATE/2);
+ }
+
+ /* clear output acc. */
+ out_adpcm[OUTD_LEFT] = out_adpcm[OUTD_RIGHT]=
out_adpcm[OUTD_CENTER] = 0;
+ out_delta[OUTD_LEFT] = out_delta[OUTD_RIGHT]=
out_delta[OUTD_CENTER] = 0;
+ /* clear outputs */
+ out_fm[0] = 0;
+ out_fm[1] = 0;
+ out_fm[2] = 0;
+ out_fm[3] = 0;
+ out_fm[4] = 0;
+ out_fm[5] = 0;
+
+ /* calculate FM */
+ FM_CALC_CH( cch[0] );
+ FM_CALC_CH( cch[1] );
+ FM_CALC_CH( cch[2] );
+ FM_CALC_CH( cch[3] );
+ FM_CALC_CH( cch[4] );
+ FM_CALC_CH( cch[5] );
+
+ /**** deltaT ADPCM ****/
+ if( DELTAT->portstate )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ for( j = 0; j < 6; j++ )
+ {
+ /**** ADPCM ****/
+ if( F2608->adpcm[j].flag )
+#ifdef YM2608_RHYTHM_PCM
+ YM2608_RYTHM(F2608, &F2608->adpcm[j]);
+#else
+ OPNB_ADPCM_CALC_CHA( F2608, &F2608->adpcm[j]);
+#endif
+ }
+
+ /* buffering */
+ {
+ int lt,rt;
+
+ lt = out_adpcm[OUTD_LEFT] + out_adpcm[OUTD_CENTER];
+ rt = out_adpcm[OUTD_RIGHT] + out_adpcm[OUTD_CENTER];
+ lt += (out_delta[OUTD_LEFT] +
out_delta[OUTD_CENTER])>>8;
+ rt += (out_delta[OUTD_RIGHT] +
out_delta[OUTD_CENTER])>>8;
+
+ lt += ((out_fm[0]>>0) & OPN->PAN[0]); /* we need to
find real level on real chip */
+ rt += ((out_fm[0]>>0) & OPN->PAN[1]);
+ lt += ((out_fm[1]>>0) & OPN->PAN[2]);
+ rt += ((out_fm[1]>>0) & OPN->PAN[3]);
+ lt += ((out_fm[2]>>0) & OPN->PAN[4]);
+ rt += ((out_fm[2]>>0) & OPN->PAN[5]);
+ lt += ((out_fm[3]>>0) & OPN->PAN[6]);
+ rt += ((out_fm[3]>>0) & OPN->PAN[7]);
+ lt += ((out_fm[4]>>0) & OPN->PAN[8]);
+ rt += ((out_fm[4]>>0) & OPN->PAN[9]);
+ lt += ((out_fm[5]>>0) & OPN->PAN[10]);
+ rt += ((out_fm[5]>>0) & OPN->PAN[11]);
+
+ lt >>= FINAL_SH;
+ rt >>= FINAL_SH;
+
+ Limit( lt, MAXOUT, MINOUT );
+ Limit( rt, MAXOUT, MINOUT );
+ /* buffering */
+ bufL[i] = lt;
+ bufR[i] = rt;
+ }
+
+ /* timer A controll */
+ INTERNAL_TIMER_A( State , cch[2] )
+ }
+ INTERNAL_TIMER_B(State,length)
+ /* check IRQ for DELTA-T arrived flag */
+ FM_STATUS_SET(State, 0);
+
+ OPN->LFOCnt = LFOCnt;
+
+}
+void YM2608_postload(int num)
+{
+ int r;
+
+// for(num=0;num<YM2608NumChips;num++)
+// {
+ YM2608 *F2608 = &(FM2608[num]);
+ /* prescaler */
+ OPNPrescaler_w(&F2608->OPN,1,2);
+ F2608->deltaT.freqbase = F2608->OPN.ST.freqbase;
+ /* IRQ mask / mode */
+ YM2608IRQMaskWrite(&F2608->OPN,F2608->REGS[0x29]);
+ /* SSG registers */
+ for(r=0;r<16;r++)
+ {
+ SSGWrite(num,0,r);
+ SSGWrite(num,1,F2608->REGS[r]);
+ }
+
+ /* OPN registers */
+ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR ,
SSG-EG */
+ for(r=0x30;r<0x9e;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]);
+
OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]);
+ }
+ /* FB / CONNECT , L / R / AMS / PMS */
+ for(r=0xb0;r<0xb6;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2608->OPN,r,F2608->REGS[r]);
+
OPNWriteReg(&F2608->OPN,r|0x100,F2608->REGS[r|0x100]);
+ }
+ /* FM channels */
+ /*FM_channel_postload(F2608->CH,6);*/
+ /* rhythm(ADPCMA) */
+ FM_ADPCMAWrite(F2608,1,F2608->REGS[0x111]);
+ for( r=0x08 ; r<0x0c ; r++)
+ FM_ADPCMAWrite(F2608,r,F2608->REGS[r+0x110]);
+ /* Delta-T ADPCM unit */
+ YM_DELTAT_postload(&F2608->deltaT , &F2608->REGS[0x100] );
+// }
+ cur_chip = NULL;
+}
+
+void YM2608_save_state(QEMUFile* f, int num)
+{
+ YM2608 *F2608 = &(FM2608[num]);
+
+ qemu_put_buffer(f, F2608->REGS, 512);
+ FMsave_state_st(f, &FM2608[num].OPN.ST);
+ FMsave_state_channel(f, FM2608[num].CH,6);
+ /* 3slots */
+ qemu_put_be32s(f, &F2608->OPN.SL3.fc[0]);
+ qemu_put_be32s(f, &F2608->OPN.SL3.fc[1]);
+ qemu_put_be32s(f, &F2608->OPN.SL3.fc[2]);
+ qemu_put_8s(f, &F2608->OPN.SL3.fn_h);
+ qemu_put_buffer(f, F2608->OPN.SL3.kcode, 3);
+ /* address register1 */
+ qemu_put_be32s(f, &F2608->address1);
+ /* rythm(ADPCMA) */
+ FMsave_state_adpcma(f, F2608->adpcm);
+ /* Delta-T ADPCM unit */
+ YM_DELTAT_savestate(f, &FM2608[num].deltaT);
+}
+
+int YM2608_load_state(QEMUFile* f, int num)
+{
+ YM2608 *F2608 = &(FM2608[num]);
+
+ qemu_get_buffer(f, F2608->REGS, 512);
+ FMload_state_st(f, &FM2608[num].OPN.ST);
+ FMload_state_channel(f, FM2608[num].CH,6);
+ /* 3slots */
+ qemu_get_be32s(f, &F2608->OPN.SL3.fc[0]);
+ qemu_get_be32s(f, &F2608->OPN.SL3.fc[1]);
+ qemu_get_be32s(f, &F2608->OPN.SL3.fc[2]);
+ qemu_get_8s(f, &F2608->OPN.SL3.fn_h);
+ qemu_get_buffer(f, F2608->OPN.SL3.kcode, 3);
+ /* address register1 */
+ qemu_get_be32s(f, &F2608->address1);
+ /* rythm(ADPCMA) */
+ FMload_state_adpcma(f, F2608->adpcm);
+ /* Delta-T ADPCM unit */
+ YM_DELTAT_loadstate(f, &FM2608[num].deltaT);
+
+ return 0;
+}
+
+/* -------------------------- YM2608(OPNA) ----------------------------------
*/
+int YM2608Init(int num, int clock, int rate,
+ void **pcmrom,int *pcmsize,short *rhythmrom,int *rhythmpos,
+ FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler)
+{
+ int i,j;
+
+ if (FM2608) return (-1); /* duplicate init. */
+ cur_chip = NULL; /* hiro-shi!! */
+
+ YM2608NumChips = num;
+
+ /* allocate extend state space */
+ if( (FM2608 = (YM2608 *)malloc(sizeof(YM2608) * YM2608NumChips))==NULL)
+ return (-1);
+ /* clear */
+ memset(FM2608,0,sizeof(YM2608) * YM2608NumChips);
+ /* allocate total level table (128kb space) */
+ if( !OPNInitTable() )
+ {
+ free( FM2608 );
+ return (-1);
+ }
+
+ for ( i = 0 ; i < YM2608NumChips; i++ ) {
+ FM2608[i].OPN.ST.index = i;
+ FM2608[i].OPN.type = TYPE_YM2608;
+ FM2608[i].OPN.P_CH = FM2608[i].CH;
+ FM2608[i].OPN.ST.clock = clock;
+ FM2608[i].OPN.ST.rate = rate;
+ /* FM2608[i].OPN.ST.irq = 0; */
+ /* FM2608[i].OPN.ST.status = 0; */
+ /* Extend handler */
+ FM2608[i].OPN.ST.Timer_Handler = TimerHandler;
+ FM2608[i].OPN.ST.IRQ_Handler = IRQHandler;
+ /* DELTA-T */
+ FM2608[i].deltaT.memory = (UINT8 *)(pcmrom[i]);
+ FM2608[i].deltaT.memory_size = pcmsize[i];
+ FM2608[i].deltaT.arrivedFlagPtr = &FM2608[i].OPN.ST.status;
+ FM2608[i].deltaT.flagMask = 0x04; /* status flag.bit3 */
+ /* ADPCM(Rythm) */
+ FM2608[i].pcmbuf = (UINT8 *)rhythmrom;
+#ifdef YM2608_RHYTHM_PCM
+ /* rhythm sound setup (PCM) */
+ for(j=0;j<6;j++)
+ {
+ /* rhythm sound */
+ FM2608[i].adpcm[j].start = rhythmpos[j];
+ FM2608[i].adpcm[j].end = rhythmpos[j+1]-1;
+ }
+ FM2608[i].pcm_size = rhythmpos[6];
+#else
+ /* rhythm sound setup (ADPCM) */
+ FM2608[i].pcm_size = rhythmsize;
+#endif
+ YM2608ResetChip(i);
+ }
+ InitOPNB_ADPCMATable();
+ return 0;
+}
+
+/* ---------- shut down emulator ----------- */
+void YM2608Shutdown(void)
+{
+ if (!FM2608) return;
+
+ FMCloseTable();
+ free(FM2608);
+ FM2608 = NULL;
+}
+
+/* ---------- reset one of chips ---------- */
+void YM2608ResetChip(int num)
+{
+ int i;
+ YM2608 *F2608 = &(FM2608[num]);
+ FM_OPN *OPN = &(FM2608[num].OPN);
+ YM_DELTAT *DELTAT = &(F2608[num].deltaT);
+
+ /* Reset Prescaler */
+ OPNPrescaler_w(OPN , 0 , 2);
+ F2608->deltaT.freqbase = OPN->ST.freqbase;
+ /* reset SSG section */
+ SSGReset(OPN->ST.index);
+ /* status clear */
+ FM_IRQMASK_SET(&OPN->ST,0x1f);
+ FM_BUSY_CLEAR(&OPN->ST);
+ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
+
+ /* extend 3ch. disable */
+ /*OPN->type &= (~TYPE_6CH);*/
+
+ reset_channel( &OPN->ST , F2608->CH , 6 );
+ /* reset OPerator paramater */
+ for(i = 0xb6 ; i >= 0xb4 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0xc0);
+ OPNWriteReg(OPN,i|0x100,0xc0);
+ }
+ for(i = 0xb2 ; i >= 0x30 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0);
+ OPNWriteReg(OPN,i|0x100,0);
+ }
+ for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
+ /* reset ADPCM unit */
+ /**** ADPCM work initial ****/
+ for( i = 0; i < 6; i++ ){ //this was i < 6+1 which must
be a bug ???
+ F2608->adpcm[i].now_addr = 0;
+ F2608->adpcm[i].now_step = 0;
+ F2608->adpcm[i].step = 0;
+ F2608->adpcm[i].start = 0;
+ F2608->adpcm[i].end = 0;
+ /* F2608->adpcm[i].delta = 21866; */
+ F2608->adpcm[i].vol_mul = 0;
+ F2608->adpcm[i].pan = &out_adpcm[OUTD_CENTER]; /* default
center */
+ F2608->adpcm[i].flagMask = 0; //(i == 6) ? 0x20 : 0;
+ F2608->adpcm[i].flag = 0;
+ F2608->adpcm[i].adpcm_acc = 0;
+ F2608->adpcm[i].adpcm_step= 0;
+ F2608->adpcm[i].adpcm_out = 0;
+ }
+ F2608->adpcmTL = 0x3f;
+ /* F2608->port1state = -1; */
+ F2608->adpcm_arrivedEndAddress = 0; /* don't used */
+
+ /* DELTA-T unit */
+ DELTAT->freqbase = OPN->ST.freqbase;
+ DELTAT->output_pointer = out_delta;
+ DELTAT->portshift = 5; /* allways 5bits shift */ /* ASG */
+ DELTAT->output_range = 1<<23;
+ YM_DELTAT_ADPCM_Reset(DELTAT,OUTD_CENTER);
+}
+
+/* YM2608 write */
+/* n = number */
+/* a = address */
+/* v = value */
+int YM2608Write(int n, int a,UINT8 v)
+{
+ YM2608 *F2608 = &(FM2608[n]);
+ FM_OPN *OPN = &(FM2608[n].OPN);
+ int addr;
+
+ switch(a&3){
+ case 0: /* address port 0 */
+ OPN->ST.address = (v &= 0xff);
+ /* Write register to SSG emulator */
+ if( v < 16 ) SSGWrite(n,0,v);
+ /* prescaler selecter : 2d,2e,2f */
+ if( v >= 0x2d && v <= 0x2f )
+ {
+ OPNPrescaler_w(OPN , v , 2);
+ F2608->deltaT.freqbase = OPN->ST.freqbase;
+ }
+ break;
+ case 1: /* data port 0 */
+ addr = OPN->ST.address;
+ F2608->REGS[addr] = v;
+ switch(addr & 0xf0)
+ {
+ case 0x00: /* SSG section */
+ /* Write data to SSG emulator */
+ SSGWrite(n,a,v);
+ break;
+ case 0x10: /* 0x10-0x1f : Rhythm section */
+ YM2608UpdateReq(n);
+ FM_ADPCMAWrite(F2608,addr-0x10,v);
+ break;
+ case 0x20: /* Mode Register */
+ switch(addr)
+ {
+ case 0x29: /* SCH,xirq mask */
+ YM2608IRQMaskWrite(OPN,v);
+ break;
+ default:
+ YM2608UpdateReq(n);
+ OPNWriteMode(OPN,addr,v);
+ }
+ break;
+ default: /* OPN section */
+ YM2608UpdateReq(n);
+ OPNWriteReg(OPN,addr,v);
+ }
+ break;
+ case 2: /* address port 1 */
+ F2608->address1 = v & 0xff;
+ break;
+ case 3: /* data port 1 */
+ addr = F2608->address1;
+ F2608->REGS[addr+0x100] = v;
+ YM2608UpdateReq(n);
+ switch( addr & 0xf0 )
+ {
+ case 0x00: /* DELTAT PORT */
+ switch( addr )
+ {
+ case 0x0c: /* Limit address L */
+ /*F2608->ADLimit = (F2608->ADLimit & 0xff00) |
v; */
+ /*break;*/
+ case 0x0d: /* Limit address H */
+ /*F2608->ADLimit = (F2608->ADLimit & 0x00ff) |
(v<<8);*/
+ /*break;*/
+ case 0x0e: /* DAC data */
+ /*break;*/
+ case 0x0f: /* PCM data port */
+ /*F2608->ADData = v;*/
+ /*FM_STATUS_RESET(F2608->OPN.ST,0x08);*/
+ break;
+ default:
+ /* 0x00-0x0b */
+ YM_DELTAT_ADPCM_Write(&F2608->deltaT,addr,v);
+ }
+ break;
+ case 0x10: /* IRQ Flag controll */
+ if( addr == 0x10 )
+ YM2608IRQFlagWrite(&(OPN->ST),n,v);
+ break;
+ default:
+ OPNWriteReg(OPN,addr+0x100,v);
+ }
+ }
+ return OPN->ST.irq;
+}
+UINT8 YM2608Read(int n,int a)
+{
+ YM2608 *F2608 = &(FM2608[n]);
+ int addr = F2608->OPN.ST.address;
+ int ret = 0;
+
+ switch( a&3 ){
+ case 0: /* status 0 : YM2203 compatible */
+ /* BUSY:x:x:x:x:x:FLAGB:FLAGA */
+ if(addr==0xff) ret = 0x00; /* ID code */
+ else ret = FM_STATUS_FLAG(&F2608->OPN.ST)&0x83;
+ break;
+ case 1: /* status 0 */
+ if( addr < 16 ) ret = SSGRead(n);
+ break;
+ case 2: /* status 1 : + ADPCM status */
+ /* BUSY:x:PCMBUSY:ZERO:BRDY:EOS:FLAGB:FLAGA */
+ if(addr==0xff) ret = 0x00; /* ID code */
+ else ret = FM_STATUS_FLAG(&F2608->OPN.ST) |
(F2608->adpcm[6].flag ? 0x20 : 0);
+ break;
+ case 3:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+int YM2608TimerOver(int n,int c)
+{
+ YM2608 *F2608 = &(FM2608[n]);
+
+ if( c )
+ { /* Timer B */
+ TimerBOver( &(F2608->OPN.ST) );
+ }
+ else
+ { /* Timer A */
+ YM2608UpdateReq(n);
+ /* timer update */
+ TimerAOver( &(F2608->OPN.ST) );
+ /* CSM mode key,TL controll */
+ if( F2608->OPN.ST.mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ CSMKeyControll( &(F2608->CH[2]) );
+ }
+ }
+ return FM2608->OPN.ST.irq;
+}
+
diff --git a/qemu/hw/fm.h b/qemu/hw/fm.h
new file mode 100644
index 0000000..edbe2be
--- /dev/null
+++ b/qemu/hw/fm.h
@@ -0,0 +1,96 @@
+/*
+ File: fm.h -- header file for software emulation for FM sound generator
+
+*/
+#ifndef _H_FM_FM_
+#define _H_FM_FM_
+
+#include "hw.h"
+#include "fm_def.h"
+
+/* --- system optimize --- */
+/* select stereo output buffer : 1=mixing / 0=separate */
+#define FM_STEREO_MIX 0
+/* select bit size of output : 8 or 16 */
+#define FM_SAMPLE_BITS 16
+/* select timer system internal or external */
+#define FM_INTERNAL_TIMER 0
+
+/* --- speedup optimize --- */
+/* busy flag emulation , The definition of FM_GET_TIME_NOW() is necessary. */
+#define FM_BUSY_FLAG_SUPPORT 0
+
+/* --- external SSG(YM2149/AY-3-8910)emulator interface port */
+/* used by YM2203,YM2608,and YM2610 */
+
+/* SSGClk : Set SSG Clock */
+/* int n = chip number */
+/* int clk = MasterClock(Hz) */
+/* int rate = sample rate(Hz) */
+#define SSGClk(chip,clock) AY8910_set_clock((chip)+ay8910_index_ym,clock)
+
+/* SSGWrite : Write SSG port */
+/* int n = chip number */
+/* int a = address */
+/* int v = data */
+#define SSGWrite(n,a,v) AY8910Write((n)+ay8910_index_ym,a,v)
+
+/* SSGRead : Read SSG port */
+/* int n = chip number */
+/* return = Read data */
+#define SSGRead(n) AY8910Read((n)+ay8910_index_ym)
+
+/* SSGReset : Reset SSG chip */
+/* int n = chip number */
+#define SSGReset(chip) AY8910_reset((chip)+ay8910_index_ym)
+
+/* --- external callback funstions for realtime update --- */
+
+/* in 2608intf.c */
+#define YM2608UpdateReq(chip) YM2608UpdateRequest(chip);
+
+#if FM_STEREO_MIX
+ #define YM2608_NUMBUF 1
+#else
+ #define YM2608_NUMBUF 2 /* FM L+R+ADPCM+RYTHM */
+#endif
+
+#if (FM_SAMPLE_BITS==16)
+typedef INT16 FMSAMPLE;
+typedef unsigned long FMSAMPLE_MIX;
+#endif
+#if (FM_SAMPLE_BITS==8)
+typedef unsigned char FMSAMPLE;
+typedef unsigned short FMSAMPLE_MIX;
+#endif
+
+//typedef void (*FM_TIMERHANDLER)(int n,int c,int cnt,double stepTime);
+typedef void (*FM_TIMERHANDLER)(int n,int c,int cnt,int clock);
+typedef void (*FM_IRQHANDLER)(int n,int irq);
+/* FM_TIMERHANDLER : Stop or Start timer */
+/* int n = chip number */
+/* int c = Channel 0=TimerA,1=TimerB */
+/* int count = timer count (0=stop) */
+/* doube stepTime = step time of one count (sec.)*/
+
+/* FM_IRQHHANDLER : IRQ level changing sense */
+/* int n = chip number */
+/* int irq = IRQ level 0=OFF,1=ON */
+
+/* -------------------- YM2608(OPNA) Interface -------------------- */
+int YM2608Init(int num, int baseclock, int rate,
+ void **pcmroma,int *pcmsizea,short *rhythmrom,int *rhythmpos,
+ FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler);
+void YM2608Shutdown(void);
+void YM2608ResetChip(int num);
+void YM2608UpdateOne(int num, INT16 **buffer, int length);
+
+void YM2608_postload(int num);
+void YM2608_save_state(QEMUFile* f, int num);
+int YM2608_load_state(QEMUFile* f, int num);
+
+int YM2608Write(int n, int a,unsigned char v);
+unsigned char YM2608Read(int n,int a);
+int YM2608TimerOver(int n, int c );
+
+#endif /* _H_FM_FM_ */
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Qemu-devel] [PATCH v3 23/25] fm: YM2608 core forked from MAME 0.59,
TAKEDA, toshiya <=