bk-emulator/emu2149.c
2019-11-03 14:52:47 +01:00

311 lines
6.3 KiB
C

/****************************************************************************
emu2149.c -- YM2149/AY-3-8910 emulator by Mitsutaka Okazaki 2001
2001 04-28 : Version 1.00beta -- 1st Beta Release.
2001 08-14 : Version 1.10
2001 10-03 : Version 1.11 -- Added PSG_set_quality().
References:
psg.vhd -- 2000 written by Kazuhiro Tsujikawa.
s_fme7.c -- 1999,2000 written by Mamiya (NEZplug).
ay8910.c -- 1998-2001 Author unknown (MAME).
MSX-Datapack -- 1991 ASCII Corp.
AY-3-8910 data sheet
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "emu2149.h"
#if defined(_MSC_VER)
#define INLINE __forceinline
#elif defined(__GNUC__)
#define INLINE __inline__
#else
#define INLINE
#endif
#define EMU2149_VOL_DEFAULT 1
#define EMU2149_VOL_YM2149 0
#define EMU2149_VOL_AY_3_8910 1
static const e_uint32 voltbl[2][32]=
{
{ 0x00,0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x09,0x0B,0x0D,0x0F,0x12,
0x16,0x1A,0x1F,0x25,0x2D,0x35,0x3F,0x4C,0x5A,0x6A,0x7F,0x97,0xB4,0xD6,0xFF,0xFF },
{ 0x00,0x00,0x01,0x01,0x02,0x02,0x03,0x03,0x05,0x05,0x07,0x07,0x0B,0x0B,0x0F,0x0F,
0x16,0x16,0x1F,0x1F,0x2D,0x2D,0x3F,0x3F,0x5A,0x5A,0x7F,0x7F,0xB4,0xB4,0xFF,0xFF }
} ;
#define GETA_BITS 24
static e_uint32 clk, rate, base_incr, HQ ;
EMU2149_API void PSG_init(e_uint32 c, e_uint32 r)
{
clk = c ;
rate = r ;
PSG_set_quality(0) ;
}
EMU2149_API void PSG_set_quality(e_uint32 q)
{
if(q)
base_incr = 1<<GETA_BITS ;
else if(rate!=0)
base_incr = (e_uint32)((double)clk * (1<<GETA_BITS) / (16*rate) ) ;
/* assert( base_incr < (1<<32) ) */
HQ = q ;
}
EMU2149_API PSG *PSG_new()
{
PSG *psg ;
psg = malloc(sizeof(PSG)) ;
if( psg == NULL ) return NULL ;
PSG_setVolumeMode(psg,EMU2149_VOL_DEFAULT) ;
return psg ;
}
EMU2149_API void PSG_setVolumeMode(PSG *psg, int type)
{
switch(type)
{
case 1:
psg->voltbl = voltbl[EMU2149_VOL_YM2149] ;
break ;
case 2:
psg->voltbl = voltbl[EMU2149_VOL_AY_3_8910] ;
break ;
default:
psg->voltbl = voltbl[EMU2149_VOL_DEFAULT] ;
break ;
}
}
EMU2149_API void PSG_reset(PSG *psg)
{
int i ;
psg->base_count = 0 ;
for(i=0;i<3;i++){
psg->count[i] = 0x1000 ;
psg->freq[i] = 0 ;
psg->edge[i] = 0 ;
psg->volume[i] = 0 ;
psg->mute[i] = 0 ;
}
for(i=0;i<16;i++) psg->reg[i] = 0 ;
psg->adr = 0 ;
psg->realstep = (e_uint32)((1<<31)/rate) ;
psg->psgstep = (e_uint32)((1<<31)/(clk/16)) ;
psg->psgtime = 0 ;
psg->noise_seed = 0xffff ;
psg->noise_count = 0x40 ;
psg->noise_freq = 0 ;
psg->env_volume = 0 ;
psg->env_ptr = 0 ;
psg->env_reverse = 0 ;
psg->env_freq = 0 ;
psg->env_count = 0 ;
psg->env_enable = 0 ;
psg->out = 0 ;
}
EMU2149_API void PSG_delete(PSG *psg)
{
free(psg) ;
}
EMU2149_API void PSG_close()
{
}
EMU2149_API e_uint8 PSG_readIO(PSG *psg)
{
return (e_uint8)(psg->reg[psg->adr]) ;
}
EMU2149_API e_uint8 PSG_readReg(PSG *psg, e_uint32 reg)
{
return (e_uint8)(psg->reg[reg&0x1f]) ;
}
EMU2149_API void PSG_writeIO(PSG *psg, e_uint32 adr, e_uint32 val)
{
if(adr & 1) PSG_writeReg(psg, psg->adr, val) ;
else psg->adr = val & 0x1f ;
}
EMU2149_API void PSG_writeReg(PSG *psg, e_uint32 reg, e_uint32 val)
{
int c ;
if (reg>15) return ;
psg->reg[reg] = (e_uint8)(val & 0xff) ;
switch(reg)
{
case 0: case 2: case 4:
case 1: case 3: case 5:
c = reg >> 1 ;
psg->freq[c] = ( (psg->reg[c*2+1]&15) << 8 ) + psg->reg[c*2] ;
break ;
case 6:
psg->noise_freq = val==0 ? 1 : ((val & 31) << 1) ;
break ;
case 7:
psg->tmask[0] = (val&1) ;
psg->tmask[1] = (val&2) ;
psg->tmask[2] = (val&4) ;
psg->nmask[0] = (val&8) ;
psg->nmask[1] = (val&16) ;
psg->nmask[2] = (val&32) ;
break ;
case 8:
case 9:
case 10:
psg->volume[reg-8] = val << 1 ;
if(val&16) psg->env_enable = 1 ;
break ;
case 11:
case 12:
psg->env_freq = (psg->reg[12] << 8) + psg->reg[11] ;
break ;
case 13:
val = val&15 ;
if(val<4) val = 9 ;
else if(val<8) val = 15 ;
psg->env_count = 0x10000 - psg->env_freq ;
psg->env_ptr = 0 ;
psg->env_hold = (val&1)^1 ;
psg->env_reverse = ((val>>2)&1)^1 ;
psg->env_alt = ((val>>1)&1)^(val&1) ;
break ;
case 14:
case 15:
default:
break;
}
return ;
}
INLINE static e_int16 calc(PSG *psg)
{
int i,noise ;
e_uint32 incr ;
e_int16 mix = 0 ;
psg->base_count += base_incr ;
incr = (psg->base_count >> GETA_BITS) ;
psg->base_count &= (1 << GETA_BITS) - 1 ;
/* Envelope */
if(psg->env_enable)
{
psg->env_count += incr ;
if(psg->env_count&0x10000)
{
psg->env_ptr = (psg->env_ptr + 1)&31 ;
if(psg->env_ptr == 0)
{
psg->env_enable = psg->env_hold ;
psg->env_reverse = psg->env_reverse^psg->env_alt ;
}
psg->env_count -= psg->env_freq ;
}
}
/* Noise */
psg->noise_count += incr ;
if(psg->noise_count&0x40)
{
if(psg->noise_seed&1) psg->noise_seed^= 0x24000 ;
psg->noise_seed >>= 1 ;
psg->noise_count -= psg->noise_freq ;
}
noise = psg->noise_seed & 1 ;
/* Tone */
for(i=0;i<3;i++)
{
psg->count[i] += incr ;
if(psg->count[i]&0x1000)
{
if(psg->freq[i]>1)
{
psg->edge[i] = !psg->edge[i] ;
psg->count[i] -= psg->freq[i] ;
}
else
{
psg->edge[i] = 1 ;
}
}
if(psg->mute[i]) continue ;
if((psg->tmask[i]||psg->edge[i])&&(psg->nmask[i]||noise))
{
if((psg->volume[i]&32)==0)
mix += psg->voltbl[psg->volume[i]&31] ;
else if(psg->env_reverse)
mix += psg->voltbl[31-psg->env_ptr] ;
else
mix += psg->voltbl[psg->env_ptr] ;
}
else if(psg->tmask[i]&&psg->nmask[i])
{
mix += psg->voltbl[psg->volume[i]&31] ;
}
}
return mix ;
}
EMU2149_API e_int16 PSG_calc(PSG *psg)
{
if(!HQ) return calc(psg) << 4 ;
/* Simple rate converter */
while (psg->realstep > psg->psgtime)
{
psg->psgtime += psg->psgstep ;
psg->out += calc(psg) ;
psg->out >>= 1 ;
}
psg->psgtime = psg->psgtime - psg->realstep ;
return (e_int16)( psg->out << 4 ) ;
}