Add files via upload

This commit is contained in:
djmaximum17 2022-04-19 07:14:12 -05:00 committed by tildearrow
parent 2c9bad3b3d
commit 6c517292dd
32 changed files with 6550 additions and 0 deletions

22
extern/NSFplay/legacy/2413tone.h vendored Normal file
View file

@ -0,0 +1,22 @@
/* YM2413 tone by Mitsutaka Okazaki, 2020 */
/* https://github.com/digital-sound-antiques/emu2413/blob/d2b9c8dfc5e84b7f8a535fdfee0149ac7bd84ca2/emu2413.c#L34
*/
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0: Original
0x71, 0x61, 0x1e, 0x17, 0xd0, 0x78, 0x00, 0x17, // 1: Violin
0x13, 0x41, 0x1a, 0x0d, 0xd8, 0xf7, 0x23, 0x13, // 2: Guitar
0x13, 0x01, 0x99, 0x00, 0xf2, 0xc4, 0x21, 0x23, // 3: Piano
0x11, 0x61, 0x0e, 0x07, 0x8d, 0x64, 0x70, 0x27, // 4: Flute
0x32, 0x21, 0x1e, 0x06, 0xe1, 0x76, 0x01, 0x28, // 5: Clarinet
0x31, 0x22, 0x16, 0x05, 0xe0, 0x71, 0x00, 0x18, // 6: Oboe
0x21, 0x61, 0x1d, 0x07, 0x82, 0x81, 0x11, 0x07, // 7: Trumpet
0x33, 0x21, 0x2d, 0x13, 0xb0, 0x70, 0x00, 0x07, // 8: Organ
0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17, // 9: Horn
0x41, 0x61, 0x0b, 0x18, 0x85, 0xf0, 0x81, 0x07, // A: Synthesizer
0x33, 0x01, 0x83, 0x11, 0xea, 0xef, 0x10, 0x04, // B: Harpsichord
0x17, 0xc1, 0x24, 0x07, 0xf8, 0xf8, 0x22, 0x12, // C: Vibraphone
0x61, 0x50, 0x0c, 0x05, 0xd2, 0xf5, 0x40, 0x42, // D: Synthsizer Bass
0x01, 0x01, 0x55, 0x03, 0xe9, 0x90, 0x03, 0x02, // E: Acoustic Bass
0x41, 0x41, 0x89, 0x03, 0xf1, 0xe4, 0xc0, 0x13, // F: Electric Guitar
0x01, 0x01, 0x18, 0x0f, 0xdf, 0xf8, 0x6a, 0x6d, // R: Bass Drum (from VRC7)
0x01, 0x01, 0x00, 0x00, 0xc8, 0xd8, 0xa7, 0x68, // R: High-Hat(M) / Snare Drum(C) (from VRC7)
0x05, 0x01, 0x00, 0x00, 0xf8, 0xaa, 0x59, 0x55, // R: Tom-tom(M) / Top Cymbal(C) (from VRC7)

20
extern/NSFplay/legacy/281btone.h vendored Normal file
View file

@ -0,0 +1,20 @@
/* YMF281B tone by Chabin (4/10/2004) */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x62,0x21,0x1a,0x07,0xf0,0x6f,0x00,0x16,
0x00,0x10,0x44,0x02,0xf6,0xf4,0x54,0x23,
0x03,0x01,0x97,0x04,0xf3,0xf3,0x13,0xf3,
0x01,0x61,0x0a,0x0f,0xfa,0x64,0x70,0x17,
0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28,
0x00,0x61,0x8a,0x0e,0xc0,0x61,0x00,0x07,
0x21,0x61,0x1b,0x07,0x84,0x80,0x17,0x17,
0x37,0x32,0xc9,0x01,0x66,0x64,0x40,0x28,
0x01,0x21,0x06,0x03,0xa5,0x71,0x51,0x07,
0x06,0x11,0x5e,0x07,0xf3,0xf2,0xf6,0x11,
0x00,0x20,0x18,0x06,0xf5,0xf3,0x20,0x26,
0x97,0x41,0x20,0x07,0xff,0xf4,0x22,0x22,
0x65,0x61,0x15,0x00,0xf7,0xf3,0x16,0xf4,
0x01,0x31,0x0e,0x07,0xfa,0xf3,0xff,0xff,
0x48,0x61,0x09,0x07,0xf1,0x94,0xf0,0xf5,
0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8,
0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7,
0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55,

363
extern/NSFplay/legacy/emu2149.c vendored Normal file
View file

@ -0,0 +1,363 @@
/****************************************************************************
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().
2002 03-02 : Version 1.12 -- Removed PSG_init & PSG_close.
2002 10-13 : Version 1.14 -- Fixed the envelope unit.
2003 09-19 : Version 1.15 -- Added PSG_setMask and PSG_toggleMask
2004 01-11 : Version 1.16 -- Fixed an envelope problem where the envelope
frequency register is written before key-on.
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"
static 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, 0xEB, 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 void
internal_refresh (PSG * psg)
{
if (psg->quality)
{
psg->base_incr = 1 << GETA_BITS;
psg->realstep = (e_uint32) ((1 << 31) / psg->rate);
psg->psgstep = (e_uint32) ((1 << 31) / (psg->clk / 16));
psg->psgtime = 0;
}
else
{
psg->base_incr =
(e_uint32) ((double) psg->clk * (1 << GETA_BITS) / (16 * psg->rate));
}
}
EMU2149_API void
PSG_set_rate (PSG * psg, e_uint32 r)
{
psg->rate = r ? r : 44100;
internal_refresh (psg);
}
EMU2149_API void
PSG_set_quality (PSG * psg, e_uint32 q)
{
psg->quality = q;
internal_refresh (psg);
}
EMU2149_API PSG *
PSG_new (e_uint32 c, e_uint32 r)
{
PSG *psg;
psg = (PSG *) malloc (sizeof (PSG));
if (psg == NULL)
return NULL;
PSG_setVolumeMode (psg, EMU2149_VOL_DEFAULT);
psg->clk = c;
psg->rate = r ? r : 44100;
PSG_set_quality (psg, 0);
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 e_uint32
PSG_setMask (PSG *psg, e_uint32 mask)
{
e_uint32 ret = 0;
if(psg)
{
ret = psg->mask;
psg->mask = mask;
}
return ret;
}
EMU2149_API e_uint32
PSG_toggleMask (PSG *psg, e_uint32 mask)
{
e_uint32 ret = 0;
if(psg)
{
ret = psg->mask;
psg->mask ^= mask;
}
return ret;
}
EMU2149_API void
PSG_reset (PSG * psg)
{
int i;
psg->base_count = 0;
for (i = 0; i < 3; i++)
{
psg->cout[i] = 0;
psg->count[i] = 0x1000;
psg->freq[i] = 0;
psg->edge[i] = 0;
psg->volume[i] = 0;
}
psg->mask = 0;
for (i = 0; i < 16; i++)
psg->reg[i] = 0;
psg->adr = 0;
psg->noise_seed = 0xffff;
psg->noise_count = 0x40;
psg->noise_freq = 0;
psg->env_volume = 0;
psg->env_ptr = 0;
psg->env_freq = 0;
psg->env_count = 0;
psg->env_pause = 1;
psg->out = 0;
}
EMU2149_API void
PSG_delete (PSG * psg)
{
free (psg);
}
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;
}
INLINE static e_int16
calc (PSG * psg)
{
int i, noise;
e_uint32 incr;
e_int32 mix = 0;
psg->base_count += psg->base_incr;
incr = (psg->base_count >> GETA_BITS);
psg->base_count &= (1 << GETA_BITS) - 1;
/* Envelope */
psg->env_count += incr;
while (psg->env_count>=0x10000 && psg->env_freq!=0)
{
if (!psg->env_pause)
{
if(psg->env_face)
psg->env_ptr = (psg->env_ptr + 1) & 0x3f ;
else
psg->env_ptr = (psg->env_ptr + 0x3f) & 0x3f;
}
if (psg->env_ptr & 0x20) /* if carry or borrow */
{
if (psg->env_continue)
{
if (psg->env_alternate^psg->env_hold) psg->env_face ^= 1;
if (psg->env_hold) psg->env_pause = 1;
psg->env_ptr = psg->env_face?0:0x1f;
}
else
{
psg->env_pause = 1;
psg->env_ptr = 0;
}
}
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;
}
}
psg->cout[i] = 0; // maintaining cout for stereo mix
if (psg->mask&PSG_MASK_CH(i))
continue;
if ((psg->tmask[i] || psg->edge[i]) && (psg->nmask[i] || noise))
{
if (!(psg->volume[i] & 32))
psg->cout[i] = psg->voltbl[psg->volume[i] & 31];
else
psg->cout[i] = psg->voltbl[psg->env_ptr];
mix += psg->cout[i];
}
}
return (e_int16) mix;
}
EMU2149_API e_int16
PSG_calc (PSG * psg)
{
if (!psg->quality)
return (e_int16) (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);
}
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 1:
case 2:
case 3:
case 4:
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;
break;
case 11:
case 12:
psg->env_freq = (psg->reg[12] << 8) + psg->reg[11];
break;
case 13:
psg->env_continue = (val >> 3) & 1;
psg->env_attack = (val >> 2) & 1;
psg->env_alternate = (val >> 1) & 1;
psg->env_hold = val & 1;
psg->env_face = psg->env_attack;
psg->env_pause = 0;
psg->env_count = 0x10000 - psg->env_freq;
psg->env_ptr = psg->env_face?0:0x1f;
break;
case 14:
case 15:
default:
break;
}
return;
}

94
extern/NSFplay/legacy/emu2149.h vendored Normal file
View file

@ -0,0 +1,94 @@
/* emu2149.h */
#ifndef _EMU2149_H_
#define _EMU2149_H_
#include "emutypes.h"
#ifdef EMU2149_DLL_EXPORTS
#define EMU2149_API __declspec(dllexport)
#elif EMU2149_DLL_IMPORTS
#define EMU2149_API __declspec(dllimport)
#else
#define EMU2149_API
#endif
#define EMU2149_VOL_DEFAULT 1
#define EMU2149_VOL_YM2149 0
#define EMU2149_VOL_AY_3_8910 1
#define PSG_MASK_CH(x) (1<<(x))
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct __PSG
{
/* Volume Table */
e_uint32 *voltbl;
e_uint8 reg[0x20];
e_int32 out;
e_int32 cout[3];
e_uint32 clk, rate, base_incr, quality;
e_uint32 count[3];
e_uint32 volume[3];
e_uint32 freq[3];
e_uint32 edge[3];
e_uint32 tmask[3];
e_uint32 nmask[3];
e_uint32 mask;
e_uint32 base_count;
e_uint32 env_volume;
e_uint32 env_ptr;
e_uint32 env_face;
e_uint32 env_continue;
e_uint32 env_attack;
e_uint32 env_alternate;
e_uint32 env_hold;
e_uint32 env_pause;
e_uint32 env_reset;
e_uint32 env_freq;
e_uint32 env_count;
e_uint32 noise_seed;
e_uint32 noise_count;
e_uint32 noise_freq;
/* rate converter */
e_uint32 realstep;
e_uint32 psgtime;
e_uint32 psgstep;
/* I/O Ctrl */
e_uint32 adr;
}
PSG;
EMU2149_API void PSG_set_quality (PSG * psg, e_uint32 q);
EMU2149_API void PSG_set_rate (PSG * psg, e_uint32 r);
EMU2149_API PSG *PSG_new (e_uint32 clk, e_uint32 rate);
EMU2149_API void PSG_reset (PSG *);
EMU2149_API void PSG_delete (PSG *);
EMU2149_API void PSG_writeReg (PSG *, e_uint32 reg, e_uint32 val);
EMU2149_API void PSG_writeIO (PSG * psg, e_uint32 adr, e_uint32 val);
EMU2149_API e_uint8 PSG_readReg (PSG * psg, e_uint32 reg);
EMU2149_API e_uint8 PSG_readIO (PSG * psg);
EMU2149_API e_int16 PSG_calc (PSG *);
EMU2149_API void PSG_setVolumeMode (PSG * psg, int type);
EMU2149_API e_uint32 PSG_setMask (PSG *, e_uint32 mask);
EMU2149_API e_uint32 PSG_toggleMask (PSG *, e_uint32 mask);
#ifdef __cplusplus
}
#endif
#endif

485
extern/NSFplay/legacy/emu2212.c vendored Normal file
View file

@ -0,0 +1,485 @@
/****************************************************************************
emu2212.c -- S.C.C. emulator by Mitsutaka Okazaki 2001
2001 09-30 : Version 1.00
2001 10-03 : Version 1.01 -- Added SCC_set_quality().
2002 02-14 : Version 1.10 -- Added SCC_writeReg(), SCC_set_type().
Fixed SCC_write().
2002 02-17 : Version 1.11 -- Fixed SCC_write().
2002 03-02 : Version 1.12 -- Removed SCC_init & SCC_close.
2003 09-19 : Version 1.13 -- Added SCC_setMask() and SCC_toggleMask()
2004 10-21 : Version 1.14 -- Fixed the problem where SCC+ is disabled.
Registar map for SCC_writeReg()
$00-1F : WaveTable CH.A
$20-3F : WaveTable CH.B
$40-5F : WaveTable CH.C
$60-7F : WaveTable CH.D&E(SCC), CH.D(SCC+)
$80-9F : WaveTable CH.E
$C0 : CH.A Freq(L)
$C1 : CH.A Freq(H)
$C2 : CH.B Freq(L)
$C3 : CH.B Freq(H)
$C4 : CH.C Freq(L)
$C5 : CH.C Freq(H)
$C6 : CH.D Freq(L)
$C7 : CH.D Freq(H)
$C8 : CH.E Freq(L)
$C9 : CH.E Freq(H)
$D0 : CH.A Volume
$D1 : CH.B Volume
$D2 : CH.C Volume
$D3 : CH.D Volume
$D4 : CH.E Volume
$E0 : Bit0 = 0:SCC, 1:SCC+
$E1 : CH mask
$E2 : Extra Flags
*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "emu2212.h"
#define GETA_BITS 22
static void
internal_refresh (SCC * scc)
{
if (scc->quality)
{
scc->base_incr = 2 << GETA_BITS;
scc->realstep = (e_uint32) ((1 << 31) / scc->rate);
scc->sccstep = (e_uint32) ((1 << 31) / (scc->clk / 2));
scc->scctime = 0;
}
else
{
scc->base_incr = (e_uint32) ((double) scc->clk * (1 << GETA_BITS) / scc->rate);
}
}
EMU2212_API e_uint32
SCC_setMask (SCC *scc, e_uint32 mask)
{
e_uint32 ret = 0;
if(scc)
{
ret = scc->mask;
scc->mask = mask;
}
return ret;
}
EMU2212_API e_uint32
SCC_toggleMask (SCC *scc, e_uint32 mask)
{
e_uint32 ret = 0;
if(scc)
{
ret = scc->mask;
scc->mask ^= mask;
}
return ret;
}
EMU2212_API void
SCC_set_quality (SCC * scc, e_uint32 q)
{
scc->quality = q;
internal_refresh (scc);
}
EMU2212_API void
SCC_set_rate (SCC * scc, e_uint32 r)
{
scc->rate = r ? r : 44100;
internal_refresh (scc);
}
EMU2212_API SCC *
SCC_new (e_uint32 c, e_uint32 r)
{
SCC *scc;
scc = (SCC *) malloc (sizeof (SCC));
if (scc == NULL)
return NULL;
memset(scc, 0, sizeof (SCC));
scc->clk = c;
scc->rate = r ? r : 44100;
SCC_set_quality (scc, 0);
scc->type = SCC_ENHANCED;
return scc;
}
EMU2212_API void
SCC_reset (SCC * scc)
{
int i, j;
if (scc == NULL)
return;
scc->mode = 0;
scc->active = 0;
scc->base_adr = 0x9000;
for (i = 0; i < 5; i++)
{
for (j = 0; j < 5; j++)
scc->wave[i][j] = 0;
scc->count[i] = 0;
scc->freq[i] = 0;
scc->phase[i] = 0;
scc->volume[i] = 0;
scc->offset[i] = 0;
scc->rotate[i] = 0;
}
memset(scc->reg,0,0x100-0xC0);
scc->mask = 0;
scc->ch_enable = 0xff;
scc->ch_enable_next = 0xff;
scc->cycle_4bit = 0;
scc->cycle_8bit = 0;
scc->refresh = 0;
scc->out = 0;
scc->prev = 0;
scc->next = 0;
return;
}
EMU2212_API void
SCC_delete (SCC * scc)
{
if (scc != NULL)
free (scc);
}
INLINE static e_int16
calc (SCC * scc)
{
int i;
e_int32 mix = 0;
for (i = 0; i < 5; i++)
{
scc->count[i] = (scc->count[i] + scc->incr[i]);
if (scc->count[i] & (1 << (GETA_BITS + 5)))
{
scc->count[i] &= ((1 << (GETA_BITS + 5)) - 1);
scc->offset[i] = (scc->offset[i] + 31) & scc->rotate[i];
scc->ch_enable &= ~(1 << i);
scc->ch_enable |= scc->ch_enable_next & (1 << i);
}
if (scc->ch_enable & (1 << i))
{
scc->phase[i] = ((scc->count[i] >> (GETA_BITS)) + scc->offset[i]) & 0x1F;
if(!(scc->mask&SCC_MASK_CH(i)))
mix += ((((e_int8) (scc->wave[i][scc->phase[i]]) * (e_int8) scc->volume[i]))) >> 4;
}
}
return (e_int16) (mix << 4);
}
EMU2212_API e_int16
SCC_calc (SCC * scc)
{
if (!scc->quality)
return calc (scc);
while (scc->realstep > scc->scctime)
{
scc->scctime += scc->sccstep;
scc->prev = scc->next;
scc->next = calc (scc);
}
scc->scctime -= scc->realstep;
scc->out = (e_int16) (((double) scc->next * (scc->sccstep - scc->scctime) + (double) scc->prev * scc->scctime) / scc->sccstep);
return (e_int16) (scc->out);
}
EMU2212_API e_uint32
SCC_readReg (SCC * scc, e_uint32 adr)
{
if (adr < 0xA0)
return scc->wave[adr >> 5][adr & 0x1f];
else if( 0xC0 < adr && adr < 0xF0 )
return scc->reg[adr-0xC0];
else
return 0;
}
EMU2212_API void
SCC_writeReg (SCC * scc, e_uint32 adr, e_uint32 val)
{
int ch;
e_uint32 freq;
adr &= 0xFF;
if (adr < 0xA0)
{
ch = (adr & 0xF0) >> 5;
if (!scc->rotate[ch])
{
scc->wave[ch][adr & 0x1F] = (e_int8) val;
if (scc->mode == 0 && ch == 3)
scc->wave[4][adr & 0x1F] = (e_int8) val;
}
}
else if (0xC0 <= adr && adr <= 0xC9)
{
scc->reg[adr-0xC0] = val;
ch = (adr & 0x0F) >> 1;
if (adr & 1)
scc->freq[ch] = ((val & 0xF) << 8) | (scc->freq[ch] & 0xFF);
else
scc->freq[ch] = (scc->freq[ch] & 0xF00) | (val & 0xFF);
if (scc->refresh)
scc->count[ch] = 0;
freq = scc->freq[ch];
if (scc->cycle_8bit)
freq &= 0xFF;
if (scc->cycle_4bit)
freq >>= 8;
if (freq <= 8)
scc->incr[ch] = 0;
else
scc->incr[ch] = scc->base_incr / (freq + 1);
}
else if (0xD0 <= adr && adr <= 0xD4)
{
scc->reg[adr-0xC0] = val;
scc->volume[adr & 0x0F] = (e_uint8) (val & 0xF);
}
else if (adr == 0xE0)
{
scc->reg[adr-0xC0] = val;
scc->mode = (e_uint8) val & 1;
}
else if (adr == 0xE1)
{
scc->reg[adr-0xC0] = val;
scc->ch_enable_next = (e_uint8) val & 0x1F;
}
else if (adr == 0xE2)
{
scc->reg[adr-0xC0] = val;
scc->cycle_4bit = val & 1;
scc->cycle_8bit = val & 2;
scc->refresh = val & 32;
if (val & 64)
for (ch = 0; ch < 5; ch++)
scc->rotate[ch] = 0x1F;
else
for (ch = 0; ch < 5; ch++)
scc->rotate[ch] = 0;
if (val & 128)
scc->rotate[3] = scc->rotate[4] = 0x1F;
}
return;
}
INLINE static void
write_standard (SCC * scc, e_uint32 adr, e_uint32 val)
{
adr &= 0xFF;
if (adr < 0x80) /* wave */
{
SCC_writeReg (scc, adr, val);
}
else if (adr < 0x8A) /* freq */
{
SCC_writeReg (scc, adr + 0xC0 - 0x80, val);
}
else if (adr < 0x8F) /* volume */
{
SCC_writeReg (scc, adr + 0xD0 - 0x8A, val);
}
else if (adr == 0x8F) /* ch enable */
{
SCC_writeReg (scc, 0xE1, val);
}
else if (0xE0 <= adr) /* flags */
{
SCC_writeReg (scc, 0xE2, val);
}
}
INLINE static void
write_enhanced (SCC * scc, e_uint32 adr, e_uint32 val)
{
adr &= 0xFF;
if (adr < 0xA0) /* wave */
{
SCC_writeReg (scc, adr, val);
}
else if (adr < 0xAA) /* freq */
{
SCC_writeReg (scc, adr + 0xC0 - 0xA0, val);
}
else if (adr < 0xAF) /* volume */
{
SCC_writeReg (scc, adr + 0xD0 - 0xAA, val);
}
else if (adr == 0xAF) /* ch enable */
{
SCC_writeReg (scc, 0xE1, val);
}
else if (0xC0 <= adr && adr <= 0xDF) /* flags */
{
SCC_writeReg (scc, 0xE2, val);
}
}
INLINE static e_uint32
read_enhanced (SCC * scc, e_uint32 adr)
{
adr &= 0xFF;
if (adr < 0xA0)
return SCC_readReg (scc, adr);
else if (adr < 0xAA)
return SCC_readReg (scc, adr + 0xC0 - 0xA0);
else if (adr < 0xAF)
return SCC_readReg (scc, adr + 0xD0 - 0xAA);
else if (adr == 0xAF)
return SCC_readReg (scc, 0xE1);
else if (0xC0 <= adr && adr <= 0xDF)
return SCC_readReg (scc, 0xE2);
else
return 0;
}
INLINE static e_uint32
read_standard (SCC * scc, e_uint32 adr)
{
adr &= 0xFF;
if(adr<0x80)
return SCC_readReg (scc, adr);
else if (0xA0<=adr&&adr<=0xBF)
return SCC_readReg (scc, 0x80+(adr&0x1F));
else if (adr < 0x8A)
return SCC_readReg (scc, adr + 0xC0 - 0x80);
else if (adr < 0x8F)
return SCC_readReg (scc, adr + 0xD0 - 0x8A);
else if (adr == 0x8F)
return SCC_readReg (scc, 0xE1);
else if (0xE0 <= adr)
return SCC_readReg (scc, 0xE2);
else return 0;
}
EMU2212_API e_uint32
SCC_read (SCC * scc, e_uint32 adr)
{
if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE )
return (scc->base_adr>>8)&0x20;
if( adr < scc->base_adr ) return 0;
adr -= scc->base_adr;
if( adr == 0 )
{
if(scc->mode) return 0x80; else return 0x3F;
}
if(!scc->active||adr<0x800||0x8FF<adr) return 0;
switch (scc->type)
{
case SCC_STANDARD:
return read_standard (scc, adr);
break;
case SCC_ENHANCED:
if(!scc->mode)
return read_standard (scc, adr);
else
return read_enhanced (scc, adr);
break;
default:
break;
}
return 0;
}
EMU2212_API void
SCC_write (SCC * scc, e_uint32 adr, e_uint32 val)
{
val = val & 0xFF;
if( scc->type == SCC_ENHANCED && (adr&0xFFFE) == 0xBFFE )
{
scc->base_adr = 0x9000 | ((val&0x20)<<8);
return;
}
if( adr < scc->base_adr ) return;
adr -= scc->base_adr;
if(adr == 0)
{
if( val == 0x3F )
{
scc->mode = 0;
scc->active = 1;
}
else if( val&0x80 && scc->type == SCC_ENHANCED)
{
scc->mode = 1;
scc->active = 1;
}
else
{
scc->mode = 0;
scc->active = 0;
}
return;
}
if(!scc->active||adr<0x800||0x8FF<adr) return;
switch (scc->type)
{
case SCC_STANDARD:
write_standard (scc, adr, val);
break;
case SCC_ENHANCED:
if(scc->mode)
write_enhanced (scc, adr, val);
else
write_standard (scc, adr, val);
default:
break;
}
return;
}
EMU2212_API void
SCC_set_type (SCC * scc, e_uint32 type)
{
scc->type = type;
}

77
extern/NSFplay/legacy/emu2212.h vendored Normal file
View file

@ -0,0 +1,77 @@
#ifndef _EMU2212_H_
#define _EMU2212_H_
#ifdef EMU2212_DLL_EXPORTS
#define EMU2212_API __declspec(dllexport)
#elif defined(EMU2212_DLL_IMPORTS)
#define EMU2212_API __declspec(dllimport)
#else
#define EMU2212_API
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include "emutypes.h"
#define SCC_STANDARD 0
#define SCC_ENHANCED 1
#define SCC_MASK_CH(x) (1<<(x))
typedef struct __SCC {
e_uint32 clk, rate ,base_incr, quality ;
e_int32 out, prev, next;
e_uint32 type ;
e_uint32 mode ;
e_uint32 active;
e_uint32 base_adr;
e_uint32 mask ;
e_uint32 realstep ;
e_uint32 scctime ;
e_uint32 sccstep ;
e_uint32 incr[5] ;
e_int8 wave[5][32] ;
e_uint32 count[5] ;
e_uint32 freq[5] ;
e_uint32 phase[5] ;
e_uint32 volume[5] ;
e_uint32 offset[5] ;
e_uint8 reg[0x100-0xC0];
int ch_enable ;
int ch_enable_next ;
int cycle_4bit ;
int cycle_8bit ;
int refresh ;
int rotate[5] ;
} SCC ;
EMU2212_API SCC *SCC_new(e_uint32 c, e_uint32 r) ;
EMU2212_API void SCC_reset(SCC *scc) ;
EMU2212_API void SCC_set_rate(SCC *scc, e_uint32 r);
EMU2212_API void SCC_set_quality(SCC *scc, e_uint32 q) ;
EMU2212_API void SCC_set_type(SCC *scc, e_uint32 type) ;
EMU2212_API void SCC_delete(SCC *scc) ;
EMU2212_API e_int16 SCC_calc(SCC *scc) ;
EMU2212_API void SCC_write(SCC *scc, e_uint32 adr, e_uint32 val) ;
EMU2212_API void SCC_writeReg(SCC *scc, e_uint32 adr, e_uint32 val) ;
EMU2212_API e_uint32 SCC_read(SCC *scc, e_uint32 adr) ;
EMU2212_API e_uint32 SCC_setMask(SCC *scc, e_uint32 adr) ;
EMU2212_API e_uint32 SCC_toggleMask(SCC *scc, e_uint32 adr) ;
#ifdef __cplusplus
}
#endif
#endif

1461
extern/NSFplay/legacy/emu2413.c vendored Normal file

File diff suppressed because it is too large Load diff

247
extern/NSFplay/legacy/emu2413.h vendored Normal file
View file

@ -0,0 +1,247 @@
#ifndef _EMU2413_H_
#define _EMU2413_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define OPLL_DEBUG 0
enum OPLL_TONE_ENUM {
OPLL_VRC7_TONE = 0,
OPLL_VRC7_RW_TONE,
OPLL_VRC7_FT36_TONE,
OPLL_VRC7_FT35_TONE,
OPLL_VRC7_MO_TONE,
OPLL_VRC7_KT2_TONE,
OPLL_VRC7_KT1_TONE,
OPLL_2413_TONE,
OPLL_281B_TONE
};
/* voice data */
typedef struct __OPLL_PATCH {
uint32_t TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WS;
} OPLL_PATCH;
/* slot */
typedef struct __OPLL_SLOT {
uint8_t number;
/* type flags:
* 000000SM
* |+-- M: 0:modulator 1:carrier
* +--- S: 0:normal 1:single slot mode (sd, tom, hh or cym)
*/
uint8_t type;
OPLL_PATCH *patch; /* voice parameter */
/* slot output */
int32_t output[2]; /* output value, latest and previous. */
/* phase generator (pg) */
uint16_t *wave_table; /* wave table */
uint32_t pg_phase; /* pg phase */
uint32_t pg_out; /* pg output, as index of wave table */
uint8_t pg_keep; /* if 1, pg_phase is preserved when key-on */
uint16_t blk_fnum; /* (block << 9) | f-number */
uint16_t fnum; /* f-number (9 bits) */
uint8_t blk; /* block (3 bits) */
/* envelope generator (eg) */
uint8_t eg_state; /* current state */
int32_t volume; /* current volume */
uint8_t key_flag; /* key-on flag 1:on 0:off */
uint8_t sus_flag; /* key-sus option 1:on 0:off */
uint16_t tll; /* total level + key scale level*/
uint8_t rks; /* key scale offset (rks) for eg speed */
uint8_t eg_rate_h; /* eg speed rate high 4bits */
uint8_t eg_rate_l; /* eg speed rate low 2bits */
uint32_t eg_shift; /* shift for eg global counter, controls envelope speed */
uint32_t eg_out; /* eg output */
uint32_t update_requests; /* flags to debounce update */
#if OPLL_DEBUG
uint8_t last_eg_state;
#endif
} OPLL_SLOT;
/* mask */
#define OPLL_MASK_CH(x) (1 << (x))
#define OPLL_MASK_HH (1 << (9))
#define OPLL_MASK_CYM (1 << (10))
#define OPLL_MASK_TOM (1 << (11))
#define OPLL_MASK_SD (1 << (12))
#define OPLL_MASK_BD (1 << (13))
#define OPLL_MASK_RHYTHM (OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD)
/* rate conveter */
typedef struct __OPLL_RateConv {
int ch;
double timer;
double f_ratio;
int16_t *sinc_table;
int16_t **buf;
} OPLL_RateConv;
OPLL_RateConv *OPLL_RateConv_new(double f_inp, double f_out, int ch);
void OPLL_RateConv_reset(OPLL_RateConv *conv);
void OPLL_RateConv_putData(OPLL_RateConv *conv, int ch, int16_t data);
int16_t OPLL_RateConv_getData(OPLL_RateConv *conv, int ch);
void OPLL_RateConv_delete(OPLL_RateConv *conv);
typedef struct __OPLL {
uint32_t clk;
uint32_t rate;
uint8_t chip_type;
uint32_t adr;
uint32_t inp_step;
uint32_t out_step;
uint32_t out_time;
uint8_t reg[0x40];
uint8_t test_flag;
uint32_t slot_key_status;
uint8_t rhythm_mode;
uint32_t eg_counter;
uint32_t pm_phase;
int32_t am_phase;
uint8_t lfo_am;
uint32_t noise;
uint8_t short_noise;
int32_t patch_number[9];
OPLL_SLOT slot[18];
OPLL_PATCH patch[19 * 2];
uint8_t pan[16];
float pan_fine[16][2];
uint32_t mask;
/* channel output */
/* 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym */
int16_t ch_out[14];
int16_t mix_out[2];
OPLL_RateConv *conv;
} OPLL;
OPLL *OPLL_new(uint32_t clk, uint32_t rate);
void OPLL_delete(OPLL *);
void OPLL_reset(OPLL *);
void OPLL_resetPatch(OPLL *, uint8_t);
/**
* Set output wave sampling rate.
* @param rate sampling rate. If clock / 72 (typically 49716 or 49715 at 3.58MHz) is set, the internal rate converter is
* disabled.
*/
void OPLL_setRate(OPLL *opll, uint32_t rate);
/**
* Set internal calcuration quality. Currently no effects, just for compatibility.
* >= v1.0.0 always synthesizes internal output at clock/72 Hz.
*/
void OPLL_setQuality(OPLL *opll, uint8_t q);
/**
* Set pan pot (extra function - not YM2413 chip feature)
* @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved
* @param pan 0:mute 1:right 2:left 3:center
* ```
* pan: 76543210
* |+- bit 1: enable Left output
* +-- bit 0: enable Right output
* ```
*/
void OPLL_setPan(OPLL *opll, uint32_t ch, uint8_t pan);
/**
* Set fine-grained panning
* @param ch 0..8:tone 9:bd 10:hh 11:sd 12:tom 13:cym 14,15:reserved
* @param pan output strength of left/right channel.
* pan[0]: left, pan[1]: right. pan[0]=pan[1]=1.0f for center.
*/
void OPLL_setPanFine(OPLL *opll, uint32_t ch, float pan[2]);
/**
* Set chip type. If vrc7 is selected, r#14 is ignored.
* This method not change the current ROM patch set.
* To change ROM patch set, use OPLL_resetPatch.
* @param type 0:YM2413 1:VRC7
*/
void OPLL_setChipType(OPLL *opll, uint8_t type);
void OPLL_writeIO(OPLL *opll, uint32_t reg, uint8_t val);
void OPLL_writeReg(OPLL *opll, uint32_t reg, uint8_t val);
/**
* Calculate one sample
*/
int16_t OPLL_calc(OPLL *opll);
/**
* Calulate stereo sample
*/
void OPLL_calcStereo(OPLL *opll, int32_t out[2]);
void OPLL_setPatch(OPLL *, const uint8_t *dump);
void OPLL_copyPatch(OPLL *, int32_t, OPLL_PATCH *);
/**
* Force to refresh.
* External program should call this function after updating patch parameters.
*/
void OPLL_forceRefresh(OPLL *);
void OPLL_dumpToPatch(const uint8_t *dump, OPLL_PATCH *patch);
void OPLL_patchToDump(const OPLL_PATCH *patch, uint8_t *dump);
void OPLL_getDefaultPatch(int32_t type, int32_t num, OPLL_PATCH *);
/**
* Set channel mask
* @param mask mask flag: OPLL_MASK_* can be used.
* - bit 0..8: mask for ch 1 to 9 (OPLL_MASK_CH(i))
* - bit 9: mask for Hi-Hat (OPLL_MASK_HH)
* - bit 10: mask for Top-Cym (OPLL_MASK_CYM)
* - bit 11: mask for Tom (OPLL_MASK_TOM)
* - bit 12: mask for Snare Drum (OPLL_MASK_SD)
* - bit 13: mask for Bass Drum (OPLL_MASK_BD)
*/
uint32_t OPLL_setMask(OPLL *, uint32_t mask);
/**
* Toggler channel mask flag
*/
uint32_t OPLL_toggleMask(OPLL *, uint32_t mask);
/* for compatibility */
#define OPLL_set_rate OPLL_setRate
#define OPLL_set_quality OPLL_setQuality
#define OPLL_set_pan OPLL_setPan
#define OPLL_set_pan_fine OPLL_setPanFine
#define OPLL_calc_stereo OPLL_calcStereo
#define OPLL_reset_patch OPLL_resetPatch
#define OPLL_dump2patch OPLL_dumpToPatch
#define OPLL_patch2dump OPLL_patchToDump
#define OPLL_setChipMode OPLL_setChipType
#ifdef __cplusplus
}
#endif
#endif

41
extern/NSFplay/legacy/emutypes.h vendored Normal file
View file

@ -0,0 +1,41 @@
#ifndef _EMUTYPES_H_
#define _EMUTYPES_H_
#if defined(_MSC_VER)
#define INLINE __forceinline
#elif defined(__GNUC__)
#define INLINE __inline__
#elif defined(_MWERKS_)
#define INLINE inline
#else
#define INLINE
#endif
#if defined(EMU_DLL_IMPORTS)
#define EMU2149_DLL_IMPORTS
#define EMU2212_DLL_IMPORTS
#define EMU2413_DLL_IMPORTS
#define EMU8950_DLL_IMPORTS
#define EMU76489_DLL_IMPORTS
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef unsigned int e_uint;
typedef signed int e_int;
typedef unsigned char e_uint8 ;
typedef signed char e_int8 ;
typedef unsigned short e_uint16 ;
typedef signed short e_int16 ;
typedef unsigned int e_uint32 ;
typedef signed int e_int32 ;
#ifdef __cplusplus
}
#endif
#endif

20
extern/NSFplay/legacy/vrc7tone_ft35.h vendored Normal file
View file

@ -0,0 +1,20 @@
// patch set by Mitsutaka Okazaki used in FamiTracker 0.3.5 and prior (6/24/2001)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x33, 0x01, 0x09, 0x0e, 0x94, 0x90, 0x40, 0x01,
0x13, 0x41, 0x0f, 0x0d, 0xce, 0xd3, 0x43, 0x13,
0x01, 0x12, 0x1b, 0x06, 0xff, 0xd2, 0x00, 0x32,
0x61, 0x61, 0x1b, 0x07, 0xaf, 0x63, 0x20, 0x28,
0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x08, 0x28,
0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xf8,
0x21, 0x61, 0x1c, 0x07, 0x82, 0x81, 0x10, 0x17,
0x23, 0x21, 0x20, 0x1f, 0xc0, 0x71, 0x07, 0x47,
0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xf8,
0x17, 0x21, 0x28, 0x07, 0xff, 0x83, 0x02, 0xf8,
0x97, 0x81, 0x25, 0x07, 0xcf, 0xc8, 0x02, 0x14,
0x21, 0x21, 0x54, 0x0f, 0x80, 0x7f, 0x07, 0x07,
0x01, 0x01, 0x56, 0x03, 0xd3, 0xb2, 0x43, 0x58,
0x31, 0x21, 0x0c, 0x03, 0x82, 0xc0, 0x40, 0x07,
0x21, 0x01, 0x0c, 0x03, 0xd4, 0xd3, 0x40, 0x84,
0x04, 0x21, 0x28, 0x00, 0xdf, 0xf8, 0xff, 0xf8,
0x23, 0x22, 0x00, 0x00, 0xa8, 0xf8, 0xf8, 0xf8,
0x25, 0x18, 0x00, 0x00, 0xf8, 0xa9, 0xf8, 0x55,

21
extern/NSFplay/legacy/vrc7tone_ft36.h vendored Normal file
View file

@ -0,0 +1,21 @@
// patch set by quietust (1/18/2004), used in FamiTracker 0.3.6
// Source: http://nesdev.com/cgi-bin/wwwthreads/showpost.pl?Board=NESemdev&Number=1440
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17,
0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12,
0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02,
0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27,
0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28,
0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95,
0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07,
0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15,
0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11,
0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12,
0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4,
0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16,
0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51,
0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05,
0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

21
extern/NSFplay/legacy/vrc7tone_kt1.h vendored Normal file
View file

@ -0,0 +1,21 @@
// patch set 1 by kevtris (11/14/1999)
// http://kevtris.org/nes/vrcvii.txt
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x05, 0x03, 0x10, 0x06, 0x74, 0xA1, 0x13, 0xF4,
0x05, 0x01, 0x16, 0x00, 0xF9, 0xA2, 0x15, 0xF5,
0x01, 0x41, 0x11, 0x00, 0xA0, 0xA0, 0x83, 0x95,
0x01, 0x41, 0x17, 0x00, 0x60, 0xF0, 0x83, 0x95,
0x24, 0x41, 0x1F, 0x00, 0x50, 0xB0, 0x94, 0x94,
0x05, 0x01, 0x0B, 0x04, 0x65, 0xA0, 0x54, 0x95,
0x11, 0x41, 0x0E, 0x04, 0x70, 0xC7, 0x13, 0x10,
0x02, 0x44, 0x16, 0x06, 0xE0, 0xE0, 0x31, 0x35,
0x48, 0x22, 0x22, 0x07, 0x50, 0xA1, 0xA5, 0xF4,
0x05, 0xA1, 0x18, 0x00, 0xA2, 0xA2, 0xF5, 0xF5,
0x07, 0x81, 0x2B, 0x05, 0xA5, 0xA5, 0x03, 0x03,
0x01, 0x41, 0x08, 0x08, 0xA0, 0xA0, 0x83, 0x95,
0x21, 0x61, 0x12, 0x00, 0x93, 0x92, 0x74, 0x75,
0x21, 0x62, 0x21, 0x00, 0x84, 0x85, 0x34, 0x15,
0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x34, 0x15,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

21
extern/NSFplay/legacy/vrc7tone_kt2.h vendored Normal file
View file

@ -0,0 +1,21 @@
// patch set 2 by kevtris (11/15/1999)
// http://kevtris.org/nes/vrcvii.txt
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x31, 0x22, 0x23, 0x07, 0xF0, 0xF0, 0xE8, 0xF7,
0x03, 0x31, 0x68, 0x05, 0xF2, 0x74, 0x79, 0x9C,
0x01, 0x51, 0x72, 0x04, 0xF1, 0xD3, 0x9D, 0x8B,
0x22, 0x61, 0x1B, 0x05, 0xC0, 0xA1, 0xF8, 0xE8,
0x22, 0x61, 0x2C, 0x03, 0xD2, 0xA1, 0xA7, 0xE8,
0x31, 0x22, 0xFA, 0x01, 0xF1, 0xF1, 0xF4, 0xEE,
0x21, 0x61, 0x28, 0x06, 0xF1, 0xF1, 0xCE, 0x9B,
0x27, 0x61, 0x60, 0x00, 0xF0, 0xF0, 0xFF, 0xFD,
0x60, 0x21, 0x2B, 0x06, 0x85, 0xF1, 0x79, 0x9D,
0x31, 0xA1, 0xFF, 0x0A, 0x53, 0x62, 0x5E, 0xAF,
0x03, 0xA1, 0x70, 0x0F, 0xD4, 0xA3, 0x94, 0xBE,
0x2B, 0x61, 0xE4, 0x07, 0xF6, 0x93, 0xBD, 0xAC,
0x21, 0x63, 0xED, 0x07, 0x77, 0xF1, 0xC7, 0xE8,
0x21, 0x61, 0x2A, 0x03, 0xF3, 0xE2, 0xB6, 0xD9,
0x21, 0x63, 0x37, 0x03, 0xF3, 0xE2, 0xB6, 0xD9,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

20
extern/NSFplay/legacy/vrc7tone_mo.h vendored Normal file
View file

@ -0,0 +1,20 @@
/* VRC7 TONES by okazaki@angel.ne.jp (4/10/2004) */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x33,0x01,0x09,0x0e,0x94,0x90,0x40,0x01,
0x13,0x41,0x0f,0x0d,0xce,0xd3,0x43,0x13,
0x01,0x12,0x1b,0x06,0xff,0xd2,0x00,0x32,
0x61,0x61,0x1b,0x07,0xaf,0x63,0x20,0x28,
0x22,0x21,0x1e,0x06,0xf0,0x76,0x08,0x28,
0x66,0x21,0x15,0x00,0x93,0x94,0x20,0xf8,
0x21,0x61,0x1c,0x07,0x82,0x81,0x10,0x17,
0x23,0x21,0x20,0x1f,0xc0,0x71,0x07,0x47,
0x25,0x31,0x26,0x05,0x64,0x41,0x18,0xf8,
0x17,0x21,0x28,0x07,0xff,0x83,0x02,0xf8,
0x97,0x81,0x25,0x07,0xcf,0xc8,0x02,0x14,
0x21,0x21,0x54,0x0f,0x80,0x7f,0x07,0x07,
0x01,0x01,0x56,0x03,0xd3,0xb2,0x43,0x58,
0x31,0x21,0x0c,0x03,0x82,0xc0,0x40,0x07,
0x21,0x01,0x0c,0x03,0xd4,0xd3,0x40,0x84,
0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8,
0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7,
0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55,

21
extern/NSFplay/legacy/vrc7tone_nuke.h vendored Normal file
View file

@ -0,0 +1,21 @@
// patch set by Nuke.YKT (3/15/2019)
// https://siliconpr0n.org/archive/doku.php?id=vendor:yamaha:opl2#ym2413_instruments
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x21, 0x05, 0x06, 0xE8, 0x81, 0x42, 0x27,
0x13, 0x41, 0x14, 0x0D, 0xD8, 0xF6, 0x23, 0x12,
0x11, 0x11, 0x08, 0x08, 0xFA, 0xB2, 0x20, 0x12,
0x31, 0x61, 0x0C, 0x07, 0xA8, 0x64, 0x61, 0x27,
0x32, 0x21, 0x1E, 0x06, 0xE1, 0x76, 0x01, 0x28,
0x02, 0x01, 0x06, 0x00, 0xA3, 0xE2, 0xF4, 0xF4,
0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x11, 0x07,
0x23, 0x21, 0x22, 0x17, 0xA2, 0x72, 0x01, 0x17,
0x35, 0x11, 0x25, 0x00, 0x40, 0x73, 0x72, 0x01,
0xB5, 0x01, 0x0F, 0x0F, 0xA8, 0xA5, 0x51, 0x02,
0x17, 0xC1, 0x24, 0x07, 0xF8, 0xF8, 0x22, 0x12,
0x71, 0x23, 0x11, 0x06, 0x65, 0x74, 0x18, 0x16,
0x01, 0x02, 0xD3, 0x05, 0xC9, 0x95, 0x03, 0x02,
0x61, 0x63, 0x0C, 0x00, 0x94, 0xC0, 0x33, 0xF6,
0x21, 0x72, 0x0D, 0x00, 0xC1, 0xD5, 0x56, 0x06,
0x01, 0x01, 0x18, 0x0F, 0xDF, 0xF8, 0x6A, 0x6D,
0x01, 0x01, 0x00, 0x00, 0xC8, 0xD8, 0xA7, 0x68,
0x05, 0x01, 0x00, 0x00, 0xF8, 0xAA, 0x59, 0x55,

21
extern/NSFplay/legacy/vrc7tone_rw.h vendored Normal file
View file

@ -0,0 +1,21 @@
// patch set by rainwarrior (8/01/2012)
// http://forums.nesdev.com/viewtopic.php?f=6&t=9141
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x21, 0x05, 0x06, 0xB8, 0x82, 0x42, 0x27,
0x13, 0x41, 0x13, 0x0D, 0xD8, 0xD6, 0x23, 0x12,
0x31, 0x11, 0x08, 0x08, 0xFA, 0x9A, 0x22, 0x02,
0x31, 0x61, 0x18, 0x07, 0x78, 0x64, 0x30, 0x27,
0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28,
0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0xF5,
0x21, 0x61, 0x1D, 0x07, 0x82, 0x81, 0x16, 0x07,
0x23, 0x21, 0x1A, 0x17, 0xCF, 0x72, 0x25, 0x17,
0x15, 0x11, 0x25, 0x00, 0x4F, 0x71, 0x00, 0x11,
0x85, 0x01, 0x12, 0x0F, 0x99, 0xA2, 0x40, 0x02,
0x07, 0xC1, 0x69, 0x07, 0xF3, 0xF5, 0xA7, 0x12,
0x71, 0x23, 0x0D, 0x06, 0x66, 0x75, 0x23, 0x16,
0x01, 0x02, 0xD3, 0x05, 0xA3, 0x92, 0xF7, 0x52,
0x61, 0x63, 0x0C, 0x00, 0x94, 0xAF, 0x34, 0x06,
0x21, 0x62, 0x0D, 0x00, 0xB1, 0xA0, 0x54, 0x17,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

400
extern/NSFplay/nes_apu.cpp vendored Normal file
View file

@ -0,0 +1,400 @@
//
// NES 2A03
//
#include <assert.h>
#include "nes_apu.h"
namespace xgm
{
void NES_APU::sweep_sqr (int i)
{
int shifted = freq[i] >> sweep_amount[i];
if (i == 0 && sweep_mode[i]) shifted += 1;
sfreq[i] = freq[i] + (sweep_mode[i] ? -shifted : shifted);
//DEBUG_OUT("shifted[%d] = %d (%d >> %d)\n",i,shifted,freq[i],sweep_amount[i]);
}
void NES_APU::FrameSequence(int s)
{
//DEBUG_OUT("FrameSequence(%d)\n",s);
if (s > 3) return; // no operation in step 4
// 240hz clock
for (int i=0; i < 2; ++i)
{
bool divider = false;
if (envelope_write[i])
{
envelope_write[i] = false;
envelope_counter[i] = 15;
envelope_div[i] = 0;
}
else
{
++envelope_div[i];
if (envelope_div[i] > envelope_div_period[i])
{
divider = true;
envelope_div[i] = 0;
}
}
if (divider)
{
if (envelope_loop[i] && envelope_counter[i] == 0)
envelope_counter[i] = 15;
else if (envelope_counter[i] > 0)
--envelope_counter[i];
}
}
// 120hz clock
if ((s&1) == 0)
for (int i=0; i < 2; ++i)
{
if (!envelope_loop[i] && (length_counter[i] > 0))
--length_counter[i];
if (sweep_enable[i])
{
//DEBUG_OUT("Clock sweep: %d\n", i);
--sweep_div[i];
if (sweep_div[i] <= 0)
{
sweep_sqr(i); // calculate new sweep target
//DEBUG_OUT("sweep_div[%d] (0/%d)\n",i,sweep_div_period[i]);
//DEBUG_OUT("freq[%d]=%d > sfreq[%d]=%d\n",i,freq[i],i,sfreq[i]);
if (freq[i] >= 8 && sfreq[i] < 0x800 && sweep_amount[i] > 0) // update frequency if appropriate
{
freq[i] = sfreq[i] < 0 ? 0 : sfreq[i];
}
sweep_div[i] = sweep_div_period[i] + 1;
//DEBUG_OUT("freq[%d]=%d\n",i,freq[i]);
}
if (sweep_write[i])
{
sweep_div[i] = sweep_div_period[i] + 1;
sweep_write[i] = false;
}
}
}
}
INT32 NES_APU::calc_sqr (int i, UINT32 clocks)
{
static const INT16 sqrtbl[4][16] = {
{0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
{1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};
scounter[i] -= clocks;
while (scounter[i] < 0)
{
sphase[i] = (sphase[i] + 1) & 15;
scounter[i] += freq[i] + 1;
}
INT32 ret = 0;
if (length_counter[i] > 0 &&
freq[i] >= 8 &&
sfreq[i] < 0x800
)
{
int v = envelope_disable[i] ? volume[i] : envelope_counter[i];
ret = sqrtbl[duty[i]][sphase[i]] ? v : 0;
}
return ret;
}
bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id)
{
if (0x4000 <= adr && adr < 0x4008)
{
val |= reg[adr&0x7];
return true;
}
else if(adr==0x4015)
{
val |= (length_counter[1]?2:0)|(length_counter[0]?1:0);
return true;
}
else
return false;
}
void NES_APU::Tick (UINT32 clocks)
{
out[0] = calc_sqr(0, clocks);
out[1] = calc_sqr(1, clocks);
}
// 生成される波形の振幅は0-8191
UINT32 NES_APU::Render (INT32 b[2])
{
out[0] = (mask & 1) ? 0 : out[0];
out[1] = (mask & 2) ? 0 : out[1];
INT32 m[2];
if(option[OPT_NONLINEAR_MIXER])
{
INT32 voltage = square_table[out[0] + out[1]];
m[0] = out[0] << 6;
m[1] = out[1] << 6;
INT32 ref = m[0] + m[1];
if (ref > 0)
{
m[0] = (m[0] * voltage) / ref;
m[1] = (m[1] * voltage) / ref;
}
else
{
m[0] = voltage;
m[1] = voltage;
}
}
else
{
m[0] = (out[0] * square_linear) / 15;
m[1] = (out[1] * square_linear) / 15;
}
b[0] = m[0] * sm[0][0];
b[0] += m[1] * sm[0][1];
b[0] >>= 7;
b[1] = m[0] * sm[1][0];
b[1] += m[1] * sm[1][1];
b[1] >>= 7;
return 2;
}
NES_APU::NES_APU ()
{
SetClock (DEFAULT_CLOCK);
SetRate (DEFAULT_RATE);
option[OPT_UNMUTE_ON_RESET] = true;
option[OPT_PHASE_REFRESH] = true;
option[OPT_NONLINEAR_MIXER] = true;
option[OPT_DUTY_SWAP] = false;
option[OPT_NEGATE_SWEEP_INIT] = false;
square_table[0] = 0;
for(int i=1;i<32;i++)
square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100));
square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear
for(int c=0;c<2;++c)
for(int t=0;t<2;++t)
sm[c][t] = 128;
}
NES_APU::~NES_APU ()
{
}
void NES_APU::Reset ()
{
int i;
gclock = 0;
mask = 0;
for (int i=0; i<2; ++i)
{
scounter[i] = 0;
sphase[i] = 0;
duty[i] = 0;
volume[i] = 0;
freq[i] = 0;
sfreq[i] = 0;
sweep_enable[i] = 0;
sweep_mode[i] = 0;
sweep_write[i] = 0;
sweep_div_period[i] = 0;
sweep_div[i] = 1;
sweep_amount[i] = 0;
envelope_disable[i] = 0;
envelope_loop[i] = 0;
envelope_write[i] = 0;
envelope_div_period[i] = 0;
envelope_div[0] = 0;
envelope_counter[i] = 0;
length_counter[i] = 0;
enable[i] = 0;
}
for (i = 0x4000; i < 0x4008; i++)
Write (i, 0);
Write (0x4015, 0);
if (option[OPT_UNMUTE_ON_RESET])
Write (0x4015, 0x0f);
if (option[OPT_NEGATE_SWEEP_INIT])
{
Write (0x4001, 0x08);
Write (0x4005, 0x08);
}
for (i = 0; i < 2; i++)
out[i] = 0;
SetRate(rate);
}
void NES_APU::SetOption (int id, int val)
{
if(id<OPT_END) option[id] = val;
}
void NES_APU::SetClock (double c)
{
clock = c;
}
void NES_APU::SetRate (double r)
{
rate = r ? r : DEFAULT_RATE;
}
void NES_APU::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr)
{
if (trk < 0) return;
if (trk > 1) return;
sm[0][trk] = mixl;
sm[1][trk] = mixr;
}
ITrackInfo *NES_APU::GetTrackInfo(int trk)
{
trkinfo[trk]._freq = freq[trk];
if(freq[trk])
trkinfo[trk].freq = clock/16/(freq[trk] + 1);
else
trkinfo[trk].freq = 0;
trkinfo[trk].output = out[trk];
trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0);
trkinfo[trk].key =
enable[trk] &&
length_counter[trk] > 0 &&
freq[trk] >= 8 &&
sfreq[trk] < 0x800 &&
(envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0));
trkinfo[trk].tone = duty[trk];
trkinfo[trk].max_volume = 15;
return &trkinfo[trk];
}
bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id)
{
int ch;
static const UINT8 length_table[32] = {
0x0A, 0xFE,
0x14, 0x02,
0x28, 0x04,
0x50, 0x06,
0xA0, 0x08,
0x3C, 0x0A,
0x0E, 0x0C,
0x1A, 0x0E,
0x0C, 0x10,
0x18, 0x12,
0x30, 0x14,
0x60, 0x16,
0xC0, 0x18,
0x48, 0x1A,
0x10, 0x1C,
0x20, 0x1E
};
if (0x4000 <= adr && adr < 0x4008)
{
//DEBUG_OUT("$%04X = %02X\n",adr,val);
adr &= 0xf;
ch = adr >> 2;
switch (adr)
{
case 0x0:
case 0x4:
volume[ch] = val & 15;
envelope_disable[ch] = (val >> 4) & 1;
envelope_loop[ch] = (val >> 5) & 1;
envelope_div_period[ch] = (val & 15);
duty[ch] = (val >> 6) & 3;
if (option[OPT_DUTY_SWAP])
{
if (duty[ch] == 1) duty[ch] = 2;
else if (duty[ch] == 2) duty[ch] = 1;
}
break;
case 0x1:
case 0x5:
sweep_enable[ch] = (val >> 7) & 1;
sweep_div_period[ch] = (((val >> 4) & 7));
sweep_mode[ch] = (val >> 3) & 1;
sweep_amount[ch] = val & 7;
sweep_write[ch] = true;
sweep_sqr(ch);
break;
case 0x2:
case 0x6:
freq[ch] = val | (freq[ch] & 0x700) ;
sweep_sqr(ch);
break;
case 0x3:
case 0x7:
freq[ch] = (freq[ch] & 0xFF) | ((val & 0x7) << 8) ;
if (option[OPT_PHASE_REFRESH])
sphase[ch] = 0;
envelope_write[ch] = true;
if (enable[ch])
{
length_counter[ch] = length_table[(val >> 3) & 0x1f];
}
sweep_sqr(ch);
break;
default:
return false;
}
reg[adr] = val;
return true;
}
else if (adr == 0x4015)
{
enable[0] = (val & 1) ? true : false;
enable[1] = (val & 2) ? true : false;
if (!enable[0])
length_counter[0] = 0;
if (!enable[1])
length_counter[1] = 0;
reg[adr-0x4000] = val;
return true;
}
// 4017 is handled in nes_dmc.cpp
//else if (adr == 0x4017)
//{
//}
return false;
}
} // namespace xgm;

88
extern/NSFplay/nes_apu.h vendored Normal file
View file

@ -0,0 +1,88 @@
#ifndef _NES_APU_H_
#define _NES_APU_H_
#include "../device.h"
#include "nes_dmc.h"
namespace xgm
{
/** Upper half of APU **/
class NES_APU : public ISoundChip
{
public:
enum
{
OPT_UNMUTE_ON_RESET=0,
OPT_PHASE_REFRESH,
OPT_NONLINEAR_MIXER,
OPT_DUTY_SWAP,
OPT_NEGATE_SWEEP_INIT,
OPT_END };
enum
{ SQR0_MASK = 1, SQR1_MASK = 2, };
protected:
int option[OPT_END]; // ŠeŽíƒIƒvƒVƒ‡ƒ“
int mask;
INT32 sm[2][2];
UINT32 gclock;
UINT8 reg[0x20];
INT32 out[2];
double rate, clock;
INT32 square_table[32]; // nonlinear mixer
INT32 square_linear; // linear mix approximation
int scounter[2]; // frequency divider
int sphase[2]; // phase counter
int duty[2];
int volume[2];
int freq[2];
int sfreq[2];
bool sweep_enable[2];
bool sweep_mode[2];
bool sweep_write[2];
int sweep_div_period[2];
int sweep_div[2];
int sweep_amount[2];
bool envelope_disable[2];
bool envelope_loop[2];
bool envelope_write[2];
int envelope_div_period[2];
int envelope_div[2];
int envelope_counter[2];
int length_counter[2];
bool enable[2];
void sweep_sqr (int ch); // calculates target sweep frequency
INT32 calc_sqr (int ch, UINT32 clocks);
TrackInfoBasic trkinfo[2];
public:
NES_APU ();
~NES_APU ();
void FrameSequence(int s);
virtual void Reset ();
virtual void Tick (UINT32 clocks);
virtual UINT32 Render (INT32 b[2]);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0);
virtual void SetRate (double rate);
virtual void SetClock (double clock);
virtual void SetOption (int id, int b);
virtual void SetMask(int m){ mask = m; }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
};
} // namespace
#endif

771
extern/NSFplay/nes_dmc.cpp vendored Normal file
View file

@ -0,0 +1,771 @@
#include "nes_dmc.h"
#include "nes_apu.h"
#include <cstdlib>
namespace xgm
{
const UINT32 NES_DMC::wavlen_table[2][16] = {
{ // NTSC
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068
},
{ // PAL
4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778
}};
const UINT32 NES_DMC::freq_table[2][16] = {
{ // NTSC
428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54
},
{ // PAL
398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50
}};
const UINT32 BITREVERSE[256] = {
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
};
NES_DMC::NES_DMC () : GETA_BITS (20)
{
SetClock (DEFAULT_CLOCK);
SetRate (DEFAULT_RATE);
SetPal (false);
option[OPT_ENABLE_4011] = 1;
option[OPT_ENABLE_PNOISE] = 1;
option[OPT_UNMUTE_ON_RESET] = 1;
option[OPT_DPCM_ANTI_CLICK] = 0;
option[OPT_NONLINEAR_MIXER] = 1;
option[OPT_RANDOMIZE_NOISE] = 1;
option[OPT_RANDOMIZE_TRI] = 1;
option[OPT_TRI_MUTE] = 1;
option[OPT_DPCM_REVERSE] = 0;
tnd_table[0][0][0][0] = 0;
tnd_table[1][0][0][0] = 0;
apu = NULL;
frame_sequence_count = 0;
frame_sequence_length = 7458;
frame_sequence_steps = 4;
for(int c=0;c<2;++c)
for(int t=0;t<3;++t)
sm[c][t] = 128;
}
NES_DMC::~NES_DMC ()
{
}
void NES_DMC::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr)
{
if (trk < 0) return;
if (trk > 2) return;
sm[0][trk] = mixl;
sm[1][trk] = mixr;
}
ITrackInfo *NES_DMC::GetTrackInfo(int trk)
{
switch(trk)
{
case 0:
trkinfo[trk].max_volume = 255;
trkinfo[0].key = (linear_counter>0 && length_counter[0]>0 && enable[0]);
trkinfo[0].volume = 0;
trkinfo[0]._freq = tri_freq;
if(trkinfo[0]._freq)
trkinfo[0].freq = clock/32/(trkinfo[0]._freq + 1);
else
trkinfo[0].freq = 0;
trkinfo[0].tone = -1;
trkinfo[0].output = out[0];
break;
case 1:
trkinfo[1].max_volume = 15;
trkinfo[1].volume = noise_volume+(envelope_disable?0:0x10)+(envelope_loop?0x20:0);
trkinfo[1].key = length_counter[1]>0 && enable[1] &&
(envelope_disable ? (noise_volume>0) : (envelope_counter>0));
trkinfo[1]._freq = reg[0x400e - 0x4008]&0xF;
trkinfo[1].freq = clock/double(wavlen_table[pal][trkinfo[1]._freq] * ((noise_tap&(1<<6)) ? 93 : 1));
trkinfo[1].tone = noise_tap & (1<<6);
trkinfo[1].output = out[1];
break;
case 2:
trkinfo[2].max_volume = 127;
trkinfo[2].volume = reg[0x4011 - 0x4008]&0x7F;
trkinfo[2].key = dlength > 0;
trkinfo[2]._freq = reg[0x4010 - 0x4008]&0xF;
trkinfo[2].freq = clock/double(freq_table[pal][trkinfo[2]._freq]);
trkinfo[2].tone = (0xc000|(adr_reg<<6));
trkinfo[2].output = (damp<<1)|dac_lsb;
break;
default:
return NULL;
}
return &trkinfo[trk];
}
void NES_DMC::FrameSequence(int s)
{
//DEBUG_OUT("FrameSequence: %d\n",s);
if (s > 3) return; // no operation in step 4
if (apu)
{
apu->FrameSequence(s);
}
if (s == 0 && (frame_sequence_steps == 4))
{
if (frame_irq_enable) frame_irq = true;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, frame_irq & frame_irq_enable);
}
// 240hz clock
{
// triangle linear counter
if (linear_counter_halt)
{
linear_counter = linear_counter_reload;
}
else
{
if (linear_counter > 0) --linear_counter;
}
if (!linear_counter_control)
{
linear_counter_halt = false;
}
// noise envelope
bool divider = false;
if (envelope_write)
{
envelope_write = false;
envelope_counter = 15;
envelope_div = 0;
}
else
{
++envelope_div;
if (envelope_div > envelope_div_period)
{
divider = true;
envelope_div = 0;
}
}
if (divider)
{
if (envelope_loop && envelope_counter == 0)
envelope_counter = 15;
else if (envelope_counter > 0)
--envelope_counter;
}
}
// 120hz clock
if ((s&1) == 0)
{
// triangle length counter
if (!linear_counter_control && (length_counter[0] > 0))
--length_counter[0];
// noise length counter
if (!envelope_loop && (length_counter[1] > 0))
--length_counter[1];
}
}
// 三角波チャンネルの計算 戻り値は0-15
UINT32 NES_DMC::calc_tri (UINT32 clocks)
{
static UINT32 tritbl[32] =
{
15,14,13,12,11,10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 0,
0, 1, 2, 3, 4, 5, 6, 7,
8, 9,10,11,12,13,14,15,
};
if (linear_counter > 0 && length_counter[0] > 0
&& (!option[OPT_TRI_MUTE] || tri_freq > 0))
{
counter[0] -= clocks;
while (counter[0] < 0)
{
tphase = (tphase + 1) & 31;
counter[0] += (tri_freq + 1);
}
}
UINT32 ret = tritbl[tphase];
return ret;
}
// ノイズチャンネルの計算 戻り値は0-127
// 低サンプリングレートで合成するとエイリアスノイズが激しいので
// ノイズだけはこの関数内で高クロック合成し、簡易なサンプリングレート
// 変換を行っている。
UINT32 NES_DMC::calc_noise(UINT32 clocks)
{
UINT32 env = envelope_disable ? noise_volume : envelope_counter;
if (length_counter[1] < 1) env = 0;
UINT32 last = (noise & 0x4000) ? 0 : env;
if (clocks < 1) return last;
// simple anti-aliasing (noise requires it, even when oversampling is off)
UINT32 count = 0;
UINT32 accum = counter[1] * last; // samples pending from previous calc
UINT32 accum_clocks = counter[1];
#ifdef _DEBUG
INT32 start_clocks = counter[1];
#endif
if (counter[1] < 0) // only happens on startup when using the randomize noise option
{
accum = 0;
accum_clocks = 0;
}
counter[1] -= clocks;
assert (nfreq > 0); // prevent infinite loop
while (counter[1] < 0)
{
// tick the noise generator
UINT32 feedback = (noise&1) ^ ((noise&noise_tap)?1:0);
noise = (noise>>1) | (feedback<<14);
last = (noise & 0x4000) ? 0 : env;
accum += (last * nfreq);
counter[1] += nfreq;
++count;
accum_clocks += nfreq;
}
if (count < 1) // no change over interval, don't anti-alias
{
return last;
}
accum -= (last * counter[1]); // remove these samples which belong in the next calc
accum_clocks -= counter[1];
#ifdef _DEBUG
if (start_clocks >= 0) assert(accum_clocks == clocks); // these should be equal
#endif
UINT32 average = accum / accum_clocks;
assert(average <= 15); // above this would indicate overflow
return average;
}
// Tick the DMC for the number of clocks, and return output counter;
UINT32 NES_DMC::calc_dmc (UINT32 clocks)
{
counter[2] -= clocks;
assert (dfreq > 0); // prevent infinite loop
while (counter[2] < 0)
{
counter[2] += dfreq;
if ( data > 0x100 ) // data = 0x100 when shift register is empty
{
if (!empty)
{
if ((data & 1) && (damp < 63))
damp++;
else if (!(data & 1) && (0 < damp))
damp--;
}
data >>=1;
}
if ( data <= 0x100 ) // shift register is empty
{
if (dlength > 0)
{
memory->Read (daddress, data);
cpu->StealCycles(4); // DMC read takes 3 or 4 CPU cycles, usually 4
// (checking for the 3-cycle case would require sub-instruction emulation)
data &= 0xFF; // read 8 bits
if (option[OPT_DPCM_REVERSE]) data = BITREVERSE[data];
data |= 0x10000; // use an extra bit to signal end of data
empty = false;
daddress = ((daddress+1)&0xFFFF)|0x8000 ;
--dlength;
if (dlength == 0)
{
if (mode & 1) // looped DPCM = auto-reload
{
daddress = ((adr_reg<<6)|0xC000);
dlength = (len_reg<<4)+1;
}
else if (mode & 2) // IRQ and not looped
{
irq = true;
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, true);
}
}
}
else
{
data = 0x10000; // DMC will do nothing
empty = true;
}
}
}
return (damp<<1) + dac_lsb;
}
void NES_DMC::TickFrameSequence (UINT32 clocks)
{
frame_sequence_count += clocks;
while (frame_sequence_count > frame_sequence_length)
{
FrameSequence(frame_sequence_step);
frame_sequence_count -= frame_sequence_length;
++frame_sequence_step;
if(frame_sequence_step >= frame_sequence_steps)
frame_sequence_step = 0;
}
}
void NES_DMC::Tick (UINT32 clocks)
{
out[0] = calc_tri(clocks);
out[1] = calc_noise(clocks);
out[2] = calc_dmc(clocks);
}
UINT32 NES_DMC::Render (INT32 b[2])
{
out[0] = (mask & 1) ? 0 : out[0];
out[1] = (mask & 2) ? 0 : out[1];
out[2] = (mask & 4) ? 0 : out[2];
INT32 m[3];
m[0] = tnd_table[0][out[0]][0][0];
m[1] = tnd_table[0][0][out[1]][0];
m[2] = tnd_table[0][0][0][out[2]];
if (option[OPT_NONLINEAR_MIXER])
{
INT32 ref = m[0] + m[1] + m[2];
INT32 voltage = tnd_table[1][out[0]][out[1]][out[2]];
if (ref)
{
for (int i=0; i < 3; ++i)
m[i] = (m[i] * voltage) / ref;
}
else
{
for (int i=0; i < 3; ++i)
m[i] = voltage;
}
}
// anti-click nullifies any 4011 write but preserves nonlinearity
if (option[OPT_DPCM_ANTI_CLICK])
{
if (dmc_pop) // $4011 will cause pop this frame
{
// adjust offset to counteract pop
dmc_pop_offset += dmc_pop_follow - m[2];
dmc_pop = false;
// prevent overflow, keep headspace at edges
const INT32 OFFSET_MAX = (1 << 30) - (4 << 16);
if (dmc_pop_offset > OFFSET_MAX) dmc_pop_offset = OFFSET_MAX;
if (dmc_pop_offset < -OFFSET_MAX) dmc_pop_offset = -OFFSET_MAX;
}
dmc_pop_follow = m[2]; // remember previous position
m[2] += dmc_pop_offset; // apply offset
// TODO implement this in a better way
// roll off offset (not ideal, but prevents overflow)
if (dmc_pop_offset > 0) --dmc_pop_offset;
else if (dmc_pop_offset < 0) ++dmc_pop_offset;
}
b[0] = m[0] * sm[0][0];
b[0] += m[1] * sm[0][1];
b[0] += m[2] * sm[0][2];
b[0] >>= 7;
b[1] = m[0] * sm[1][0];
b[1] += m[1] * sm[1][1];
b[1] += m[2] * sm[1][2];
b[1] >>= 7;
return 2;
}
void NES_DMC::SetClock (double c)
{
clock = c;
}
void NES_DMC::SetRate (double r)
{
rate = (UINT32)(r?r:DEFAULT_RATE);
}
void NES_DMC::SetPal (bool is_pal)
{
pal = (is_pal ? 1 : 0);
// set CPU cycles in frame_sequence
frame_sequence_length = is_pal ? 8314 : 7458;
}
void NES_DMC::SetAPU (NES_APU* apu_)
{
apu = apu_;
}
// Initializing TRI, NOISE, DPCM mixing table
void NES_DMC::InitializeTNDTable(double wt, double wn, double wd) {
// volume adjusted by 0.95 based on empirical measurements
const double MASTER = 8192.0 * 0.95;
// truthfully, the nonlinear curve does not appear to match well
// with my tests. Do more testing of the APU/DMC DAC later.
// this value keeps the triangle consistent with measured levels,
// but not necessarily the rest of this APU channel,
// because of the lack of a good DAC model, currently.
{ // Linear Mixer
for(int t=0; t<16 ; t++) {
for(int n=0; n<16; n++) {
for(int d=0; d<128; d++) {
tnd_table[0][t][n][d] = (UINT32)(MASTER*(3.0*t+2.0*n+d)/208.0);
}
}
}
}
{ // Non-Linear Mixer
tnd_table[1][0][0][0] = 0;
for(int t=0; t<16 ; t++) {
for(int n=0; n<16; n++) {
for(int d=0; d<128; d++) {
if(t!=0||n!=0||d!=0)
tnd_table[1][t][n][d] = (UINT32)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd)));
}
}
}
}
}
void NES_DMC::Reset ()
{
int i;
mask = 0;
InitializeTNDTable(8227,12241,22638);
counter[0] = 0;
counter[1] = 0;
counter[2] = 0;
tphase = 0;
nfreq = wavlen_table[0][0];
dfreq = freq_table[0][0];
tri_freq = 0;
linear_counter = 0;
linear_counter_reload = 0;
linear_counter_halt = 0;
linear_counter_control = 0;
noise_volume = 0;
noise = 0;
noise_tap = 0;
envelope_loop = 0;
envelope_disable = 0;
envelope_write = 0;
envelope_div_period = 0;
envelope_div = 0;
envelope_counter = 0;
enable[0] = 0;
enable[1] = 0;
length_counter[0] = 0;
length_counter[1] = 0;
frame_irq = false;
frame_irq_enable = false;
frame_sequence_count = 0;
frame_sequence_steps = 4;
frame_sequence_step = 0;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false);
for (i = 0; i < 0x0F; i++)
Write (0x4008 + i, 0);
Write (0x4017, 0x40);
irq = false;
Write (0x4015, 0x00);
if (option[OPT_UNMUTE_ON_RESET])
Write (0x4015, 0x0f);
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false);
out[0] = out[1] = out[2] = 0;
damp = 0;
dmc_pop = false;
dmc_pop_offset = 0;
dmc_pop_follow = 0;
dac_lsb = 0;
data = 0x100;
empty = true;
adr_reg = 0;
dlength = 0;
len_reg = 0;
daddress = 0;
noise = 1;
noise_tap = (1<<1);
if (option[OPT_RANDOMIZE_NOISE])
{
noise |= ::rand();
counter[1] = -(rand() & 511);
}
if (option[OPT_RANDOMIZE_TRI])
{
tphase = ::rand() & 31;
counter[0] = -(rand() & 2047);
}
SetRate(rate);
}
void NES_DMC::SetMemory (IDevice * r)
{
memory = r;
}
void NES_DMC::SetOption (int id, int val)
{
if(id<OPT_END)
{
option[id] = val;
if(id==OPT_NONLINEAR_MIXER)
InitializeTNDTable(8227,12241,22638);
}
}
bool NES_DMC::Write (UINT32 adr, UINT32 val, UINT32 id)
{
static const UINT8 length_table[32] = {
0x0A, 0xFE,
0x14, 0x02,
0x28, 0x04,
0x50, 0x06,
0xA0, 0x08,
0x3C, 0x0A,
0x0E, 0x0C,
0x1A, 0x0E,
0x0C, 0x10,
0x18, 0x12,
0x30, 0x14,
0x60, 0x16,
0xC0, 0x18,
0x48, 0x1A,
0x10, 0x1C,
0x20, 0x1E
};
if (adr == 0x4015)
{
enable[0] = (val & 4) ? true : false;
enable[1] = (val & 8) ? true : false;
if (!enable[0])
{
length_counter[0] = 0;
}
if (!enable[1])
{
length_counter[1] = 0;
}
if ((val & 16) && dlength == 0)
{
daddress = (0xC000 | (adr_reg << 6));
dlength = (len_reg << 4) + 1;
}
else if (!(val & 16))
{
dlength = 0;
}
irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false);
reg[adr-0x4008] = val;
return true;
}
if (adr == 0x4017)
{
//DEBUG_OUT("4017 = %02X\n", val);
frame_irq_enable = ((val & 0x40) != 0x40);
if (frame_irq_enable) frame_irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false);
frame_sequence_count = 0;
if (val & 0x80)
{
frame_sequence_steps = 5;
frame_sequence_step = 0;
FrameSequence(frame_sequence_step);
++frame_sequence_step;
}
else
{
frame_sequence_steps = 4;
frame_sequence_step = 1;
}
}
if (adr<0x4008||0x4013<adr)
return false;
reg[adr-0x4008] = val&0xff;
//DEBUG_OUT("$%04X %02X\n", adr, val);
switch (adr)
{
// tri
case 0x4008:
linear_counter_control = (val >> 7) & 1;
linear_counter_reload = val & 0x7F;
break;
case 0x4009:
break;
case 0x400a:
tri_freq = val | (tri_freq & 0x700) ;
break;
case 0x400b:
tri_freq = (tri_freq & 0xff) | ((val & 0x7) << 8) ;
linear_counter_halt = true;
if (enable[0])
{
length_counter[0] = length_table[(val >> 3) & 0x1f];
}
break;
// noise
case 0x400c:
noise_volume = val & 15;
envelope_div_period = val & 15;
envelope_disable = (val >> 4) & 1;
envelope_loop = (val >> 5) & 1;
break;
case 0x400d:
break;
case 0x400e:
if (option[OPT_ENABLE_PNOISE])
noise_tap = (val & 0x80) ? (1<<6) : (1<<1);
else
noise_tap = (1<<1);
nfreq = wavlen_table[pal][val&15];
break;
case 0x400f:
if (enable[1])
{
length_counter[1] = length_table[(val >> 3) & 0x1f];
}
envelope_write = true;
break;
// dmc
case 0x4010:
mode = (val >> 6) & 3;
if (!(mode & 2))
{
irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false);
}
dfreq = freq_table[pal][val&15];
break;
case 0x4011:
if (option[OPT_ENABLE_4011])
{
damp = (val >> 1) & 0x3f;
dac_lsb = val & 1;
dmc_pop = true;
}
break;
case 0x4012:
adr_reg = val&0xff;
// ここでdaddressは更新されない
break;
case 0x4013:
len_reg = val&0xff;
// ここでlengthは更新されない
break;
default:
return false;
}
return true;
}
bool NES_DMC::Read (UINT32 adr, UINT32 & val, UINT32 id)
{
if (adr == 0x4015)
{
val |=(irq ? 0x80 : 0)
| (frame_irq ? 0x40 : 0)
| ((dlength>0) ? 0x10 : 0)
| (length_counter[1] ? 0x08 : 0)
| (length_counter[0] ? 0x04 : 0)
;
frame_irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false);
return true;
}
else if (0x4008<=adr&&adr<=0x4014)
{
val |= reg[adr-0x4008];
return true;
}
else
return false;
}
// IRQ support requires CPU read access
void NES_DMC::SetCPU(NES_CPU* cpu_)
{
cpu = cpu_;
}
} // namespace

129
extern/NSFplay/nes_dmc.h vendored Normal file
View file

@ -0,0 +1,129 @@
#ifndef _NES_DMC_H_
#define _NES_DMC_H_
#include "../device.h"
#include "../Audio/MedianFilter.h"
#include "../CPU/nes_cpu.h"
namespace xgm
{
class NES_APU; // forward declaration
/** Bottom Half of APU **/
class NES_DMC:public ISoundChip
{
public:
enum
{
OPT_ENABLE_4011=0,
OPT_ENABLE_PNOISE,
OPT_UNMUTE_ON_RESET,
OPT_DPCM_ANTI_CLICK,
OPT_NONLINEAR_MIXER,
OPT_RANDOMIZE_NOISE,
OPT_TRI_MUTE,
OPT_RANDOMIZE_TRI,
OPT_DPCM_REVERSE,
OPT_END
};
protected:
const int GETA_BITS;
static const UINT32 freq_table[2][16];
static const UINT32 wavlen_table[2][16];
UINT32 tnd_table[2][16][16][128];
int option[OPT_END];
int mask;
INT32 sm[2][3];
UINT8 reg[0x10];
UINT32 len_reg;
UINT32 adr_reg;
IDevice *memory;
UINT32 out[3];
UINT32 daddress;
UINT32 dlength;
UINT32 data;
bool empty;
INT16 damp;
int dac_lsb;
bool dmc_pop;
INT32 dmc_pop_offset;
INT32 dmc_pop_follow;
double clock;
UINT32 rate;
int pal;
int mode;
bool irq;
INT32 counter[3]; // frequency dividers
int tphase; // triangle phase
UINT32 nfreq; // noise frequency
UINT32 dfreq; // DPCM frequency
UINT32 tri_freq;
int linear_counter;
int linear_counter_reload;
bool linear_counter_halt;
bool linear_counter_control;
int noise_volume;
UINT32 noise, noise_tap;
// noise envelope
bool envelope_loop;
bool envelope_disable;
bool envelope_write;
int envelope_div_period;
int envelope_div;
int envelope_counter;
bool enable[2]; // tri/noise enable
int length_counter[2]; // 0=tri, 1=noise
TrackInfoBasic trkinfo[3];
// frame sequencer
NES_APU* apu; // apu is clocked by DMC's frame sequencer
int frame_sequence_count; // current cycle count
int frame_sequence_length; // CPU cycles per FrameSequence
int frame_sequence_step; // current step of frame sequence
int frame_sequence_steps; // 4/5 steps per frame
bool frame_irq;
bool frame_irq_enable;
NES_CPU* cpu; // IRQ needs CPU access
inline UINT32 calc_tri (UINT32 clocks);
inline UINT32 calc_dmc (UINT32 clocks);
inline UINT32 calc_noise (UINT32 clocks);
public:
NES_DMC ();
~NES_DMC ();
void InitializeTNDTable(double wt, double wn, double wd);
void SetPal (bool is_pal);
void SetAPU (NES_APU* apu_);
void SetMemory (IDevice * r);
void FrameSequence(int s);
int GetDamp(){ return (damp<<1)|dac_lsb ; }
void TickFrameSequence (UINT32 clocks);
virtual void Reset ();
virtual void Tick (UINT32 clocks);
virtual UINT32 Render (INT32 b[2]);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0);
virtual void SetRate (double rate);
virtual void SetClock (double rate);
virtual void SetOption (int, int);
virtual void SetMask(int m){ mask = m; }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
void SetCPU(NES_CPU* cpu_);
};
}
#endif

397
extern/NSFplay/nes_fds.cpp vendored Normal file
View file

@ -0,0 +1,397 @@
#include <cstring>
#include "nes_fds.h"
namespace xgm {
const int RC_BITS = 12;
NES_FDS::NES_FDS ()
{
option[OPT_CUTOFF] = 2000;
option[OPT_4085_RESET] = 0;
option[OPT_WRITE_PROTECT] = 0; // not used here, see nsfplay.cpp
rc_k = 0;
rc_l = (1<<RC_BITS);
SetClock (DEFAULT_CLOCK);
SetRate (DEFAULT_RATE);
sm[0] = 128;
sm[1] = 128;
Reset();
}
NES_FDS::~NES_FDS ()
{
}
void NES_FDS::SetStereoMix(int trk, INT16 mixl, INT16 mixr)
{
if (trk < 0) return;
if (trk > 1) return;
sm[0] = mixl;
sm[1] = mixr;
}
ITrackInfo *NES_FDS::GetTrackInfo(int trk)
{
trkinfo.max_volume = 32;
trkinfo.volume = last_vol;
trkinfo.key = last_vol > 0;
trkinfo._freq = last_freq;
trkinfo.freq = (double(last_freq) * clock) / (65536.0 * 64.0);
trkinfo.tone = env_out[EMOD];
for(int i=0;i<64;i++)
trkinfo.wave[i] = wave[TWAV][i];
return &trkinfo;
}
void NES_FDS::SetClock (double c)
{
clock = c;
}
void NES_FDS::SetRate (double r)
{
rate = r;
// configure lowpass filter
double cutoff = double(option[OPT_CUTOFF]);
double leak = 0.0;
if (cutoff > 0)
leak = ::exp(-2.0 * 3.14159 * cutoff / rate);
rc_k = INT32(leak * double(1<<RC_BITS));
rc_l = (1<<RC_BITS) - rc_k;
}
void NES_FDS::SetOption (int id, int val)
{
if(id<OPT_END) option[id] = val;
// update cutoff immediately
if (id == OPT_CUTOFF) SetRate(rate);
}
void NES_FDS::Reset ()
{
master_io = true;
master_vol = 0;
last_freq = 0;
last_vol = 0;
rc_accum = 0;
for (int i=0; i<2; ++i)
{
::memset(wave[i], 0, sizeof(wave[i]));
freq[i] = 0;
phase[i] = 0;
}
wav_write = false;
wav_halt = true;
env_halt = true;
mod_halt = true;
mod_pos = 0;
mod_write_pos = 0;
for (int i=0; i<2; ++i)
{
env_mode[i] = false;
env_disable[i] = true;
env_timer[i] = 0;
env_speed[i] = 0;
env_out[i] = 0;
}
master_env_speed = 0xFF;
// NOTE: the FDS BIOS reset only does the following related to audio:
// $4023 = $00
// $4023 = $83 enables master_io
// $4080 = $80 output volume = 0, envelope disabled
// $408A = $E8 master envelope speed
Write(0x4023, 0x00);
Write(0x4023, 0x83);
Write(0x4080, 0x80);
Write(0x408A, 0xE8);
// reset other stuff
Write(0x4082, 0x00); // wav freq 0
Write(0x4083, 0x80); // wav disable
Write(0x4084, 0x80); // mod strength 0
Write(0x4085, 0x00); // mod position 0
Write(0x4086, 0x00); // mod freq 0
Write(0x4087, 0x80); // mod disable
Write(0x4089, 0x00); // wav write disable, max global volume}
}
void NES_FDS::Tick (UINT32 clocks)
{
// clock envelopes
if (!env_halt && !wav_halt && (master_env_speed != 0))
{
for (int i=0; i<2; ++i)
{
if (!env_disable[i])
{
env_timer[i] += clocks;
UINT32 period = ((env_speed[i]+1) * master_env_speed) << 3;
while (env_timer[i] >= period)
{
// clock the envelope
if (env_mode[i])
{
if (env_out[i] < 32) ++env_out[i];
}
else
{
if (env_out[i] > 0 ) --env_out[i];
}
env_timer[i] -= period;
}
}
}
}
// clock the mod table
if (!mod_halt)
{
// advance phase, adjust for modulator
UINT32 start_pos = phase[TMOD] >> 16;
phase[TMOD] += (clocks * freq[TMOD]);
UINT32 end_pos = phase[TMOD] >> 16;
// wrap the phase to the 64-step table (+ 16 bit accumulator)
phase[TMOD] = phase[TMOD] & 0x3FFFFF;
// execute all clocked steps
for (UINT32 p = start_pos; p < end_pos; ++p)
{
INT32 wv = wave[TMOD][p & 0x3F];
if (wv == 4) // 4 resets mod position
mod_pos = 0;
else
{
const INT32 BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 };
mod_pos += BIAS[wv];
mod_pos &= 0x7F; // 7-bit clamp
}
}
}
// clock the wav table
if (!wav_halt)
{
// complex mod calculation
INT32 mod = 0;
if (env_out[EMOD] != 0) // skip if modulator off
{
// convert mod_pos to 7-bit signed
INT32 pos = (mod_pos < 64) ? mod_pos : (mod_pos-128);
// multiply pos by gain,
// shift off 4 bits but with odd "rounding" behaviour
INT32 temp = pos * env_out[EMOD];
INT32 rem = temp & 0x0F;
temp >>= 4;
if ((rem > 0) && ((temp & 0x80) == 0))
{
if (pos < 0) temp -= 1;
else temp += 2;
}
// wrap if range is exceeded
while (temp >= 192) temp -= 256;
while (temp < -64) temp += 256;
// multiply result by pitch,
// shift off 6 bits, round to nearest
temp = freq[TWAV] * temp;
rem = temp & 0x3F;
temp >>= 6;
if (rem >= 32) temp += 1;
mod = temp;
}
// advance wavetable position
INT32 f = freq[TWAV] + mod;
phase[TWAV] = phase[TWAV] + (clocks * f);
phase[TWAV] = phase[TWAV] & 0x3FFFFF; // wrap
// store for trackinfo
last_freq = f;
}
// output volume caps at 32
INT32 vol_out = env_out[EVOL];
if (vol_out > 32) vol_out = 32;
// final output
if (!wav_write)
fout = wave[TWAV][(phase[TWAV]>>16)&0x3F] * vol_out;
// NOTE: during wav_halt, the unit still outputs (at phase 0)
// and volume can affect it if the first sample is nonzero.
// haven't worked out 100% of the conditions for volume to
// effect (vol envelope does not seem to run, but am unsure)
// but this implementation is very close to correct
// store for trackinfo
last_vol = vol_out;
}
UINT32 NES_FDS::Render (INT32 b[2])
{
// 8 bit approximation of master volume
const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223)
const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol
const INT32 MASTER[4] = {
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) };
INT32 v = fout * MASTER[master_vol] >> 8;
// lowpass RC filter
INT32 rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS;
rc_accum = rc_out;
v = rc_out;
// output mix
INT32 m = mask ? 0 : v;
b[0] = (m * sm[0]) >> 7;
b[1] = (m * sm[1]) >> 7;
return 2;
}
bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id)
{
// $4023 master I/O enable/disable
if (adr == 0x4023)
{
master_io = ((val & 2) != 0);
return true;
}
if (!master_io)
return false;
if (adr < 0x4040 || adr > 0x408A)
return false;
if (adr < 0x4080) // $4040-407F wave table write
{
if (wav_write)
wave[TWAV][adr - 0x4040] = val & 0x3F;
return true;
}
switch (adr & 0x00FF)
{
case 0x80: // $4080 volume envelope
env_disable[EVOL] = ((val & 0x80) != 0);
env_mode[EVOL] = ((val & 0x40) != 0);
env_timer[EVOL] = 0;
env_speed[EVOL] = val & 0x3F;
if (env_disable[EVOL])
env_out[EVOL] = env_speed[EVOL];
return true;
case 0x81: // $4081 ---
return false;
case 0x82: // $4082 wave frequency low
freq[TWAV] = (freq[TWAV] & 0xF00) | val;
return true;
case 0x83: // $4083 wave frequency high / enables
freq[TWAV] = (freq[TWAV] & 0x0FF) | ((val & 0x0F) << 8);
wav_halt = ((val & 0x80) != 0);
env_halt = ((val & 0x40) != 0);
if (wav_halt)
phase[TWAV] = 0;
if (env_halt)
{
env_timer[EMOD] = 0;
env_timer[EVOL] = 0;
}
return true;
case 0x84: // $4084 mod envelope
env_disable[EMOD] = ((val & 0x80) != 0);
env_mode[EMOD] = ((val & 0x40) != 0);
env_timer[EMOD] = 0;
env_speed[EMOD] = val & 0x3F;
if (env_disable[EMOD])
env_out[EMOD] = env_speed[EMOD];
return true;
case 0x85: // $4085 mod position
mod_pos = val & 0x7F;
// not hardware accurate., but prevents detune due to cycle inaccuracies
// (notably in Bio Miracle Bokutte Upa)
if (option[OPT_4085_RESET])
phase[TMOD] = mod_write_pos << 16;
return true;
case 0x86: // $4086 mod frequency low
freq[TMOD] = (freq[TMOD] & 0xF00) | val;
return true;
case 0x87: // $4087 mod frequency high / enable
freq[TMOD] = (freq[TMOD] & 0x0FF) | ((val & 0x0F) << 8);
mod_halt = ((val & 0x80) != 0);
if (mod_halt)
phase[TMOD] = phase[TMOD] & 0x3F0000; // reset accumulator phase
return true;
case 0x88: // $4088 mod table write
if (mod_halt)
{
// writes to current playback position (there is no direct way to set phase)
wave[TMOD][(phase[TMOD] >> 16) & 0x3F] = val & 0x07;
phase[TMOD] = (phase[TMOD] + 0x010000) & 0x3FFFFF;
wave[TMOD][(phase[TMOD] >> 16) & 0x3F] = val & 0x07;
phase[TMOD] = (phase[TMOD] + 0x010000) & 0x3FFFFF;
mod_write_pos = phase[TMOD] >> 16; // used by OPT_4085_RESET
}
return true;
case 0x89: // $4089 wave write enable, master volume
wav_write = ((val & 0x80) != 0);
master_vol = val & 0x03;
return true;
case 0x8A: // $408A envelope speed
master_env_speed = val;
// haven't tested whether this register resets phase on hardware,
// but this ensures my inplementation won't spam envelope clocks
// if this value suddenly goes low.
env_timer[EMOD] = 0;
env_timer[EVOL] = 0;
return true;
default:
return false;
}
return false;
}
bool NES_FDS::Read (UINT32 adr, UINT32 & val, UINT32 id)
{
if (adr >= 0x4040 && adr <= 0x407F)
{
// TODO: if wav_write is not enabled, the
// read address may not be reliable? need
// to test this on hardware.
val = wave[TWAV][adr - 0x4040];
return true;
}
if (adr == 0x4090) // $4090 read volume envelope
{
val = env_out[EVOL] | 0x40;
return true;
}
if (adr == 0x4092) // $4092 read mod envelope
{
val = env_out[EMOD] | 0x40;
return true;
}
return false;
}
} // namespace

83
extern/NSFplay/nes_fds.h vendored Normal file
View file

@ -0,0 +1,83 @@
#ifndef _NES_FDS_H_
#define _NES_FDS_H_
#include "../device.h"
namespace xgm {
class TrackInfoFDS : public TrackInfoBasic
{
public:
INT16 wave[64];
virtual IDeviceInfo *Clone(){ return new TrackInfoFDS(*this); }
};
class NES_FDS : public ISoundChip
{
public:
enum
{
OPT_CUTOFF=0,
OPT_4085_RESET,
OPT_WRITE_PROTECT,
OPT_END
};
protected:
double rate, clock;
int mask;
INT32 sm[2]; // stereo mix
INT32 fout; // current output
TrackInfoFDS trkinfo;
int option[OPT_END];
bool master_io;
UINT32 master_vol;
UINT32 last_freq; // for trackinfo
UINT32 last_vol; // for trackinfo
// two wavetables
enum { TMOD=0, TWAV=1 };
INT32 wave[2][64];
UINT32 freq[2];
UINT32 phase[2];
bool wav_write;
bool wav_halt;
bool env_halt;
bool mod_halt;
UINT32 mod_pos;
UINT32 mod_write_pos;
// two ramp envelopes
enum { EMOD=0, EVOL=1 };
bool env_mode[2];
bool env_disable[2];
UINT32 env_timer[2];
UINT32 env_speed[2];
UINT32 env_out[2];
UINT32 master_env_speed;
// 1-pole RC lowpass filter
INT32 rc_accum;
INT32 rc_k;
INT32 rc_l;
public:
NES_FDS ();
virtual ~ NES_FDS ();
virtual void Reset ();
virtual void Tick (UINT32 clocks);
virtual UINT32 Render (INT32 b[2]);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0);
virtual void SetRate (double);
virtual void SetClock (double);
virtual void SetOption (int, int);
virtual void SetMask(int m){ mask = m&1; }
virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
};
} // namespace xgm
#endif

186
extern/NSFplay/nes_fme7.cpp vendored Normal file
View file

@ -0,0 +1,186 @@
#include "nes_fme7.h"
using namespace xgm;
const int DIVIDER = 8; // TODO this is not optimal, rewrite PSG output
NES_FME7::NES_FME7 ()
{
psg = PSG_new ((e_uint32)DEFAULT_CLOCK, DEFAULT_RATE);
divider = 0;
for(int c=0;c<2;++c)
for(int t=0;t<3;++t)
sm[c][t] = 128;
}
NES_FME7::~NES_FME7 ()
{
if (psg)
PSG_delete (psg);
}
void NES_FME7::SetClock (double c)
{
this->clock = c * 2.0;
}
void NES_FME7::SetRate (double r)
{
//rate = r ? r : DEFAULT_RATE;
rate = DEFAULT_CLOCK / double(DIVIDER); // TODO rewrite PSG to integrate with clock
if (psg)
PSG_set_rate (psg, (e_uint32)rate);
}
void NES_FME7::SetOption (int id, int val)
{
if(id<OPT_END)
{
//option[id] = val;
}
}
void NES_FME7::Reset ()
{
for (int i=0; i<16; ++i) // blank all registers
{
Write(0xC000,i);
Write(0xE000,0);
}
Write(0xC000,0x07); // disable all tones
Write(0xE000,0x3F);
divider = 0;
if (psg)
PSG_reset (psg);
}
bool NES_FME7::Write (xgm::UINT32 adr, xgm::UINT32 val, xgm::UINT32 id)
{
if (adr == 0xC000)
{
if (psg)
PSG_writeIO (psg, 0, val);
return true;
}
if (adr == 0xE000)
{
if (psg)
PSG_writeIO (psg, 1, val);
return true;
}
else
return false;
}
bool NES_FME7::Read (xgm::UINT32 adr, xgm::UINT32 & val, xgm::UINT32 id)
{
// not sure why this was here - BS
//if (psg)
// val = PSG_readIO (psg);
return false;
}
void NES_FME7::Tick (xgm::UINT32 clocks)
{
divider += clocks;
while (divider >= DIVIDER)
{
divider -= DIVIDER;
if (psg) PSG_calc(psg);
}
}
xgm::UINT32 NES_FME7::Render (xgm::INT32 b[2])
{
b[0] = b[1] = 0;
for (int i=0; i < 3; ++i)
{
// note negative polarity
b[0] -= psg->cout[i] * sm[0][i];
b[1] -= psg->cout[i] * sm[1][i];
}
b[0] >>= (7-4);
b[1] >>= (7-4);
// master volume adjustment
const INT32 MASTER = INT32(0.64 * 256.0);
b[0] = (b[0] * MASTER) >> 8;
b[1] = (b[1] * MASTER) >> 8;
return 2;
}
void NES_FME7::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr)
{
if (trk < 0) return;
if (trk > 2) return;
sm[0][trk] = mixl;
sm[1][trk] = mixr;
}
ITrackInfo *NES_FME7::GetTrackInfo(int trk)
{
assert(trk<5);
if(psg)
{
if (trk<3)
{
trkinfo[trk]._freq = psg->freq[trk];
if(psg->freq[trk])
trkinfo[trk].freq = psg->clk/32.0/psg->freq[trk];
else
trkinfo[trk].freq = 0;
trkinfo[trk].output = psg->cout[trk];
trkinfo[trk].max_volume = 15;
trkinfo[trk].volume = psg->volume[trk] >> 1;
//trkinfo[trk].key = (psg->cout[trk]>0)?true:false;
trkinfo[trk].key = !(psg->tmask[trk]);
trkinfo[trk].tone = (psg->tmask[trk]?2:0)+(psg->nmask[trk]?1:0);
}
else if (trk == 3) // envelope
{
trkinfo[trk]._freq = psg->env_freq;
if(psg->env_freq)
trkinfo[trk].freq = psg->clk/512.0/psg->env_freq;
else
trkinfo[trk].freq = 0;
if (psg->env_continue && psg->env_alternate && !psg->env_hold) // triangle wave
{
trkinfo[trk].freq *= 0.5f; // sounds an octave down
}
trkinfo[trk].output = psg->voltbl[psg->env_ptr];
trkinfo[trk].max_volume = 0;
trkinfo[trk].volume = 0;
trkinfo[trk].key = (((psg->volume[0]|psg->volume[1]|psg->volume[2])&32) != 0);
trkinfo[trk].tone =
(psg->env_continue ?8:0) |
(psg->env_attack ?4:0) |
(psg->env_alternate?2:0) |
(psg->env_hold ?1:0) ;
}
else if (trk == 4) // noise
{
trkinfo[trk]._freq = psg->noise_freq >> 1;
if(trkinfo[trk]._freq > 0)
trkinfo[trk].freq = psg->clk/16.0/psg->noise_freq;
else
trkinfo[trk].freq = 0;
trkinfo[trk].output = psg->noise_seed & 1;
trkinfo[trk].max_volume = 0;
trkinfo[trk].volume = 0;
//trkinfo[trk].key = ((psg->nmask[0]&psg->nmask[1]&psg->nmask[2]) == 0);
trkinfo[trk].key = false;
trkinfo[trk].tone = 0;
}
}
return &trkinfo[trk];
}

42
extern/NSFplay/nes_fme7.h vendored Normal file
View file

@ -0,0 +1,42 @@
#ifndef _NES_FME7_H_
#define _NES_FME7_H_
#include "../device.h"
#include "legacy/emu2149.h"
namespace xgm
{
class NES_FME7:public ISoundChip
{
public:
enum
{
OPT_END
};
protected:
//int option[OPT_END];
INT32 sm[2][3]; // stereo mix
INT16 buf[2];
PSG *psg;
int divider; // clock divider
double clock, rate;
TrackInfoBasic trkinfo[5];
public:
NES_FME7 ();
~NES_FME7 ();
virtual void Reset ();
virtual void Tick (UINT32 clocks);
virtual UINT32 Render (INT32 b[2]);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0);
virtual void SetClock (double);
virtual void SetRate (double);
virtual void SetOption (int, int);
virtual void SetMask (int m){ if(psg) PSG_setMask(psg,m); }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
};
} // namespace
#endif

422
extern/NSFplay/nes_mmc5.cpp vendored Normal file
View file

@ -0,0 +1,422 @@
#include "nes_mmc5.h"
namespace xgm
{
NES_MMC5::NES_MMC5 ()
{
cpu = NULL;
SetClock (DEFAULT_CLOCK);
SetRate (DEFAULT_RATE);
option[OPT_NONLINEAR_MIXER] = true;
option[OPT_PHASE_REFRESH] = true;
frame_sequence_count = 0;
// square nonlinear mix, same as 2A03
square_table[0] = 0;
for(int i=1;i<32;i++)
square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100));
// 2A03 style nonlinear pcm mix with double the bits
//pcm_table[0] = 0;
//INT32 wd = 22638;
//for(int d=1;d<256; ++d)
// pcm_table[d] = (INT32)((8192.0*159.79)/(100.0+1.0/((double)d/wd)));
// linear pcm mix (actual hardware seems closer to this)
pcm_table[0] = 0;
double pcm_scale = 32.0;
for (int d=1; d<256; ++d)
pcm_table[d] = (INT32)(double(d) * pcm_scale);
// stereo mix
for(int c=0;c<2;++c)
for(int t=0;t<3;++t)
sm[c][t] = 128;
}
NES_MMC5::~NES_MMC5 ()
{
}
void NES_MMC5::Reset ()
{
int i;
scounter[0] = 0;
scounter[1] = 0;
sphase[0] = 0;
sphase[1] = 0;
envelope_div[0] = 0;
envelope_div[1] = 0;
length_counter[0] = 0;
length_counter[1] = 0;
envelope_counter[0] = 0;
envelope_counter[1] = 0;
frame_sequence_count = 0;
for (i = 0; i < 8; i++)
Write (0x5000 + i, 0);
Write(0x5015, 0);
for (i = 0; i < 3; ++i)
out[i] = 0;
mask = 0;
pcm = 0; // PCM channel
pcm_mode = false; // write mode
SetRate(rate);
}
void NES_MMC5::SetOption (int id, int val)
{
if(id<OPT_END) option[id] = val;
}
void NES_MMC5::SetClock (double c)
{
this->clock = c;
}
void NES_MMC5::SetRate (double r)
{
rate = r ? r : DEFAULT_RATE;
}
void NES_MMC5::FrameSequence ()
{
// 240hz clock
for (int i=0; i < 2; ++i)
{
bool divider = false;
if (envelope_write[i])
{
envelope_write[i] = false;
envelope_counter[i] = 15;
envelope_div[i] = 0;
}
else
{
++envelope_div[i];
if (envelope_div[i] > envelope_div_period[i])
{
divider = true;
envelope_div[i] = 0;
}
}
if (divider)
{
if (envelope_loop[i] && envelope_counter[i] == 0)
envelope_counter[i] = 15;
else if (envelope_counter[i] > 0)
--envelope_counter[i];
}
}
// MMC5 length counter is clocked at 240hz, unlike 2A03
for (int i=0; i < 2; ++i)
{
if (!envelope_loop[i] && (length_counter[i] > 0))
--length_counter[i];
}
}
INT32 NES_MMC5::calc_sqr (int i, UINT32 clocks)
{
static const INT16 sqrtbl[4][16] = {
{0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
{1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};
scounter[i] += clocks;
while (scounter[i] > freq[i])
{
sphase[i] = (sphase[i] + 1) & 15;
scounter[i] -= (freq[i] + 1);
}
INT32 ret = 0;
if (length_counter[i] > 0)
{
// note MMC5 does not silence the highest 8 frequencies like APU,
// because this is done by the sweep unit.
int v = envelope_disable[i] ? volume[i] : envelope_counter[i];
ret = sqrtbl[duty[i]][sphase[i]] ? v : 0;
}
return ret;
}
void NES_MMC5::TickFrameSequence (UINT32 clocks)
{
frame_sequence_count += clocks;
while (frame_sequence_count > 7458)
{
FrameSequence();
frame_sequence_count -= 7458;
}
}
void NES_MMC5::Tick (UINT32 clocks)
{
out[0] = calc_sqr(0, clocks);
out[1] = calc_sqr(1, clocks);
out[2] = pcm;
}
UINT32 NES_MMC5::Render (INT32 b[2])
{
out[0] = (mask & 1) ? 0 : out[0];
out[1] = (mask & 2) ? 0 : out[1];
out[2] = (mask & 4) ? 0 : out[2];
INT32 m[3];
if(option[OPT_NONLINEAR_MIXER])
{
// squares nonlinear
INT32 voltage = square_table[out[0] + out[1]];
m[0] = out[0] << 6;
m[1] = out[1] << 6;
INT32 ref = m[0] + m[1];
if (ref > 0)
{
m[0] = (m[0] * voltage) / ref;
m[1] = (m[1] * voltage) / ref;
}
else
{
m[0] = voltage;
m[1] = voltage;
}
// pcm nonlinear
m[2] = pcm_table[out[2]];
}
else
{
// squares
m[0] = out[0] << 6;
m[1] = out[1] << 6;
// pcm channel
m[2] = out[2] << 5;
}
// note polarity is flipped on output
b[0] = m[0] * -sm[0][0];
b[0] += m[1] * -sm[0][1];
b[0] += m[2] * -sm[0][2];
b[0] >>= 7;
b[1] = m[0] * -sm[1][0];
b[1] += m[1] * -sm[1][1];
b[1] += m[2] * -sm[1][2];
b[1] >>= 7;
return 2;
}
bool NES_MMC5::Write (UINT32 adr, UINT32 val, UINT32 id)
{
int ch;
static const UINT8 length_table[32] = {
0x0A, 0xFE,
0x14, 0x02,
0x28, 0x04,
0x50, 0x06,
0xA0, 0x08,
0x3C, 0x0A,
0x0E, 0x0C,
0x1A, 0x0E,
0x0C, 0x10,
0x18, 0x12,
0x30, 0x14,
0x60, 0x16,
0xC0, 0x18,
0x48, 0x1A,
0x10, 0x1C,
0x20, 0x1E
};
if ((0x5c00 <= adr) && (adr < 0x5ff0))
{
ram[adr & 0x3ff] = val;
return true;
}
else if ((0x5000 <= adr) && (adr < 0x5008))
{
reg[adr & 0x7] = val;
}
switch (adr)
{
case 0x5000:
case 0x5004:
ch = (adr >> 2) & 1;
volume[ch] = val & 15;
envelope_disable[ch] = (val >> 4) & 1;
envelope_loop[ch] = (val >> 5) & 1;
envelope_div_period[ch] = (val & 15);
duty[ch] = (val >> 6) & 3;
break;
case 0x5002:
case 0x5006:
ch = (adr >> 2) & 1;
freq[ch] = val + (freq[ch] & 0x700);
if (scounter[ch] > freq[ch]) scounter[ch] = freq[ch];
break;
case 0x5003:
case 0x5007:
ch = (adr >> 2) & 1;
freq[ch] = (freq[ch] & 0xff) + ((val & 7) << 8);
if (scounter[ch] > freq[ch]) scounter[ch] = freq[ch];
// phase reset
if (option[OPT_PHASE_REFRESH])
sphase[ch] = 0;
envelope_write[ch] = true;
if (enable[ch])
{
length_counter[ch] = length_table[(val >> 3) & 0x1f];
}
break;
// PCM channel control
case 0x5010:
pcm_mode = ((val & 1) != 0); // 0 = write, 1 = read
break;
// PCM channel control
case 0x5011:
if (!pcm_mode)
{
val &= 0xFF;
if (val != 0) pcm = val;
}
break;
case 0x5015:
enable[0] = (val & 1) ? true : false;
enable[1] = (val & 2) ? true : false;
if (!enable[0])
length_counter[0] = 0;
if (!enable[1])
length_counter[1] = 0;
break;
case 0x5205:
mreg[0] = val;
break;
case 0x5206:
mreg[1] = val;
break;
default:
return false;
}
return true;
}
bool NES_MMC5::Read (UINT32 adr, UINT32 & val, UINT32 id)
{
// in PCM read mode, reads from $8000-$C000 automatically load the PCM output
if (pcm_mode && (0x8000 <= adr) && (adr < 0xC000) && cpu)
{
pcm_mode = false; // prevent recursive entry
UINT32 pcm_read;
cpu->Read(adr, pcm_read);
pcm_read &= 0xFF;
if (pcm_read != 0)
pcm = pcm_read;
pcm_mode = true;
}
if ((0x5000 <= adr) && (adr < 0x5008))
{
val = reg[adr&0x7];
return true;
}
else if(adr == 0x5015)
{
val = (enable[1]?2:0)|(enable[0]?1:0);
return true;
}
if ((0x5c00 <= adr) && (adr < 0x5ff0))
{
val = ram[adr & 0x3ff];
return true;
}
else if (adr == 0x5205)
{
val = (mreg[0] * mreg[1]) & 0xff;
return true;
}
else if (adr == 0x5206)
{
val = (mreg[0] * mreg[1]) >> 8;
return true;
}
return false;
}
void NES_MMC5::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr)
{
if (trk < 0) return;
if (trk > 2) return;
sm[0][trk] = mixl;
sm[1][trk] = mixr;
}
ITrackInfo *NES_MMC5::GetTrackInfo(int trk)
{
assert(trk<3);
if (trk < 2) // square
{
trkinfo[trk]._freq = freq[trk];
if(freq[trk])
trkinfo[trk].freq = clock/16/(freq[trk] + 1);
else
trkinfo[trk].freq = 0;
trkinfo[trk].output = out[trk];
trkinfo[trk].max_volume = 15;
trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10);
trkinfo[trk].key = (envelope_disable[trk]?(volume[trk]>0): (envelope_counter[trk]>0));
trkinfo[trk].tone = duty[trk];
}
else // pcm
{
trkinfo[trk]._freq = 0;
trkinfo[trk].freq = 0;
trkinfo[trk].output = out[2];
trkinfo[trk].max_volume = 255;
trkinfo[trk].volume = pcm;
trkinfo[trk].key = 0;
trkinfo[trk].tone = pcm_mode ? 1 : 0;
}
return &trkinfo[trk];
}
// pcm read mode requires CPU read access
void NES_MMC5::SetCPU(NES_CPU* cpu_)
{
cpu = cpu_;
}
}// namespace

74
extern/NSFplay/nes_mmc5.h vendored Normal file
View file

@ -0,0 +1,74 @@
#ifndef _NES_MMC5_H_
#define _NES_MMC5_H_
#include "../device.h"
#include "../CPU/nes_cpu.h"
namespace xgm
{
class NES_MMC5:public ISoundChip
{
public:
enum
{ OPT_NONLINEAR_MIXER=0, OPT_PHASE_REFRESH, OPT_END };
protected:
int option[OPT_END];
int mask;
INT32 sm[2][3]; // stereo panning
UINT8 ram[0x6000 - 0x5c00];
UINT8 reg[8];
UINT8 mreg[2];
UINT8 pcm; // PCM channel
bool pcm_mode; // PCM channel
NES_CPU* cpu; // PCM channel reads need CPU access
UINT32 scounter[2]; // frequency divider
UINT32 sphase[2]; // phase counter
UINT32 duty[2];
UINT32 volume[2];
UINT32 freq[2];
INT32 out[3];
bool enable[2];
bool envelope_disable[2]; // エンベロープ有効フラグ
bool envelope_loop[2]; // エンベロープループ
bool envelope_write[2];
int envelope_div_period[2];
int envelope_div[2];
int envelope_counter[2];
int length_counter[2];
int frame_sequence_count;
double clock, rate;
INT32 calc_sqr (int i, UINT32 clocks);
INT32 square_table[32];
INT32 pcm_table[256];
TrackInfoBasic trkinfo[3];
public:
NES_MMC5 ();
~NES_MMC5 ();
void FrameSequence ();
void TickFrameSequence (UINT32 clocks);
virtual void Reset ();
virtual void Tick (UINT32 clocks);
virtual UINT32 Render (INT32 b[2]);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0);
virtual void SetOption (int id, int b);
virtual void SetClock (double);
virtual void SetRate (double);
virtual void SetMask (int m){ mask = m; }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
void SetCPU(NES_CPU* cpu_);
};
}
#endif

367
extern/NSFplay/nes_n106.cpp vendored Normal file
View file

@ -0,0 +1,367 @@
#include <cstring>
#include "nes_n106.h"
namespace xgm {
NES_N106::NES_N106 ()
{
option[OPT_SERIAL] = 0;
option[OPT_PHASE_READ_ONLY] = 0;
option[OPT_LIMIT_WAVELENGTH] = 0;
SetClock (DEFAULT_CLOCK);
SetRate (DEFAULT_RATE);
for (int i=0; i < 8; ++i)
{
sm[0][i] = 128;
sm[1][i] = 128;
}
Reset();
}
NES_N106::~NES_N106 ()
{
}
void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr)
{
if (trk < 0 || trk >= 8) return;
trk = 7-trk; // displayed channels are inverted
sm[0][trk] = mixl;
sm[1][trk] = mixr;
}
ITrackInfo *NES_N106::GetTrackInfo (int trk)
{
int channels = get_channels();
int channel = 7-trk; // invert the track display
TrackInfoN106* t = &trkinfo[channel];
if (trk >= channels)
{
t->max_volume = 15;
t->volume = 0;
t->_freq = 0;
t->wavelen = 0;
t->tone = -1;
t->output = 0;
t->key = false;
t->freq = 0;
}
else
{
t->max_volume = 15;
t->volume = get_vol(channel);
t->_freq = get_freq(channel);
t->wavelen = get_len(channel);
t->tone = get_off(channel);
t->output = fout[channel];
t->key = (t->volume > 0) && (t->_freq > 0);
t->freq = (double(t->_freq) * clock) / double(15 * 65536 * channels * t->wavelen);
for (int i=0; i < t->wavelen; ++i)
t->wave[i] = get_sample((i+t->tone)&0xFF);
}
return t;
}
void NES_N106::SetClock (double c)
{
clock = c;
}
void NES_N106::SetRate (double r)
{
rate = r;
}
void NES_N106::SetMask (int m)
{
// bit reverse the mask,
// N163 waves are displayed in reverse order
mask = 0
| ((m & (1<<0)) ? (1<<7) : 0)
| ((m & (1<<1)) ? (1<<6) : 0)
| ((m & (1<<2)) ? (1<<5) : 0)
| ((m & (1<<3)) ? (1<<4) : 0)
| ((m & (1<<4)) ? (1<<3) : 0)
| ((m & (1<<5)) ? (1<<2) : 0)
| ((m & (1<<6)) ? (1<<1) : 0)
| ((m & (1<<7)) ? (1<<0) : 0);
}
void NES_N106::SetOption (int id, int val)
{
if (id<OPT_END) option[id] = val;
}
void NES_N106::Reset ()
{
master_disable = false;
::memset(reg, 0, sizeof(reg));
reg_select = 0;
reg_advance = false;
tick_channel = 0;
tick_clock = 0;
render_channel = 0;
render_clock = 0;
render_subclock = 0;
for (int i=0; i<8; ++i) fout[i] = 0;
Write(0xE000, 0x00); // master disable off
Write(0xF800, 0x80); // select $00 with auto-increment
for (unsigned int i=0; i<0x80; ++i) // set all regs to 0
{
Write(0x4800, 0x00);
}
Write(0xF800, 0x00); // select $00 without auto-increment
}
void NES_N106::Tick (UINT32 clocks)
{
if (master_disable) return;
int channels = get_channels();
tick_clock += clocks;
render_clock += clocks; // keep render in sync
while (tick_clock > 0)
{
int channel = 7-tick_channel;
UINT32 phase = get_phase(channel);
UINT32 freq = get_freq(channel);
UINT32 len = get_len(channel);
UINT32 off = get_off(channel);
INT32 vol = get_vol(channel);
// accumulate 24-bit phase
phase = (phase + freq) & 0x00FFFFFF;
// wrap phase if wavelength exceeded
UINT32 hilen = len << 16;
while (phase >= hilen) phase -= hilen;
// write back phase
set_phase(phase, channel);
// fetch sample (note: N163 output is centred at 8, and inverted w.r.t 2A03)
INT32 sample = 8 - get_sample(((phase >> 16) + off) & 0xFF);
fout[channel] = sample * vol;
// cycle to next channel every 15 clocks
tick_clock -= 15;
++tick_channel;
if (tick_channel >= channels)
tick_channel = 0;
}
}
UINT32 NES_N106::Render (INT32 b[2])
{
b[0] = 0;
b[1] = 0;
if (master_disable) return 2;
int channels = get_channels();
if (option[OPT_SERIAL]) // hardware accurate serial multiplexing
{
// this could be made more efficient than going clock-by-clock
// but this way is simpler
int clocks = render_clock;
while (clocks > 0)
{
int c = 7-render_channel;
if (0 == ((mask >> c) & 1))
{
b[0] += fout[c] * sm[0][c];
b[1] += fout[c] * sm[1][c];
}
++render_subclock;
if (render_subclock >= 15) // each channel gets a 15-cycle slice
{
render_subclock = 0;
++render_channel;
if (render_channel >= channels)
render_channel = 0;
}
--clocks;
}
// increase output level by 1 bits (7 bits already added from sm)
b[0] <<= 1;
b[1] <<= 1;
// average the output
if (render_clock > 0)
{
b[0] /= render_clock;
b[1] /= render_clock;
}
render_clock = 0;
}
else // just mix all channels
{
for (int i = (8-channels); i<8; ++i)
{
if (0 == ((mask >> i) & 1))
{
b[0] += fout[i] * sm[0][i];
b[1] += fout[i] * sm[1][i];
}
}
// mix together, increase output level by 8 bits, roll off 7 bits from sm
INT32 MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 };
b[0] = (b[0] * MIX[channels]) >> 7;
b[1] = (b[1] * MIX[channels]) >> 7;
// when approximating the serial multiplex as a straight mix, once the
// multiplex frequency gets below the nyquist frequency an average mix
// begins to sound too quiet. To approximate this effect, I don't attenuate
// any further after 6 channels are active.
}
// 8 bit approximation of master volume
// max N163 vol vs max APU square
// unfortunately, games have been measured as low as 3.4x and as high as 8.5x
// with higher volumes on Erika, King of Kings, and Rolling Thunder
// and lower volumes on others. Using 6.0x as a rough "one size fits all".
const double MASTER_VOL = 6.0 * 1223.0;
const double MAX_OUT = 15.0 * 15.0 * 256.0; // max digital value
const INT32 GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f);
b[0] = (b[0] * GAIN) >> 8;
b[1] = (b[1] * GAIN) >> 8;
return 2;
}
bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id)
{
if (adr == 0xE000) // master disable
{
master_disable = ((val & 0x40) != 0);
return true;
}
else if (adr == 0xF800) // register select
{
reg_select = (val & 0x7F);
reg_advance = (val & 0x80) != 0;
return true;
}
else if (adr == 0x4800) // register write
{
if (option[OPT_PHASE_READ_ONLY]) // old emulators didn't know phase was stored here
{
int c = 15 - (reg_select/8);
int r = reg_select & 7;
if (c < get_channels() &&
(r == 1 ||
r == 3 ||
r == 5))
{
if (reg_advance)
reg_select = (reg_select + 1) & 0x7F;
return true;
}
}
if (option[OPT_LIMIT_WAVELENGTH]) // old emulators ignored top 3 bits of length
{
int c = 15 - (reg_select/8);
int r = reg_select & 7;
if (c < get_channels() && r == 4)
{
val |= 0xE0;
}
}
reg[reg_select] = val;
if (reg_advance)
reg_select = (reg_select + 1) & 0x7F;
return true;
}
return false;
}
bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id)
{
if (adr == 0x4800) // register read
{
val = reg[reg_select];
if (reg_advance)
reg_select = (reg_select + 1) & 0x7F;
return true;
}
return false;
}
//
// register decoding/encoding functions
//
inline UINT32 NES_N106::get_phase (int channel)
{
// 24-bit phase stored in channel regs 1/3/5
channel = channel << 3;
return (reg[0x41 + channel] )
+ (reg[0x43 + channel] << 8 )
+ (reg[0x45 + channel] << 16);
}
inline UINT32 NES_N106::get_freq (int channel)
{
// 19-bit frequency stored in channel regs 0/2/4
channel = channel << 3;
return ( reg[0x40 + channel] )
+ ( reg[0x42 + channel] << 8 )
+ ((reg[0x44 + channel] & 0x03) << 16);
}
inline UINT32 NES_N106::get_off (int channel)
{
// 8-bit offset stored in channel reg 6
channel = channel << 3;
return reg[0x46 + channel];
}
inline UINT32 NES_N106::get_len (int channel)
{
// 6-bit<<3 length stored obscurely in channel reg 4
channel = channel << 3;
return 256 - (reg[0x44 + channel] & 0xFC);
}
inline INT32 NES_N106::get_vol (int channel)
{
// 4-bit volume stored in channel reg 7
channel = channel << 3;
return reg[0x47 + channel] & 0x0F;
}
inline INT32 NES_N106::get_sample (UINT32 index)
{
// every sample becomes 2 samples in regs
return (index&1) ?
((reg[index>>1] >> 4) & 0x0F) :
( reg[index>>1] & 0x0F) ;
}
inline int NES_N106::get_channels ()
{
// 3-bit channel count stored in reg 0x7F
return ((reg[0x7F] >> 4) & 0x07) + 1;
}
inline void NES_N106::set_phase (UINT32 phase, int channel)
{
// 24-bit phase stored in channel regs 1/3/5
channel = channel << 3;
reg[0x41 + channel] = phase & 0xFF;
reg[0x43 + channel] = (phase >> 8 ) & 0xFF;
reg[0x45 + channel] = (phase >> 16) & 0xFF;
}
} //namespace

74
extern/NSFplay/nes_n106.h vendored Normal file
View file

@ -0,0 +1,74 @@
#ifndef _NES_N106_H_
#define _NES_N106_H_
#include "../device.h"
namespace xgm {
class TrackInfoN106 : public TrackInfoBasic
{
public:
int wavelen;
INT16 wave[256];
virtual IDeviceInfo *Clone(){ return new TrackInfoN106(*this); }
};
class NES_N106:public ISoundChip
{
public:
enum
{
OPT_SERIAL = 0,
OPT_PHASE_READ_ONLY = 1,
OPT_LIMIT_WAVELENGTH = 2,
OPT_END
};
protected:
double rate, clock;
int mask;
INT32 sm[2][8]; // stereo mix
INT32 fout[8]; // current output
TrackInfoN106 trkinfo[8];
int option[OPT_END];
bool master_disable;
UINT32 reg[0x80]; // all state is contained here
unsigned int reg_select;
bool reg_advance;
int tick_channel;
int tick_clock;
int render_channel;
int render_clock;
int render_subclock;
// convenience functions to interact with regs
inline UINT32 get_phase (int channel);
inline UINT32 get_freq (int channel);
inline UINT32 get_off (int channel);
inline UINT32 get_len (int channel);
inline INT32 get_vol (int channel);
inline INT32 get_sample (UINT32 index);
inline int get_channels ();
// for storing back the phase after modifying
inline void set_phase (UINT32 phase, int channel);
public:
NES_N106 ();
~NES_N106 ();
virtual void Reset ();
virtual void Tick (UINT32 clocks);
virtual UINT32 Render (INT32 b[2]);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0);
virtual void SetRate (double);
virtual void SetClock (double);
virtual void SetOption (int, int);
virtual void SetMask (int m);
virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
};
} // namespace xgm
#endif

264
extern/NSFplay/nes_vrc6.cpp vendored Normal file
View file

@ -0,0 +1,264 @@
#include "nes_vrc6.h"
namespace xgm
{
NES_VRC6::NES_VRC6 ()
{
SetClock (DEFAULT_CLOCK);
SetRate (DEFAULT_RATE);
halt = false;
freq_shift = 0;
for(int c=0;c<2;++c)
for(int t=0;t<3;++t)
sm[c][t] = 128;
}
NES_VRC6::~NES_VRC6 ()
{
}
void NES_VRC6::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr)
{
if (trk < 0) return;
if (trk > 2) return;
sm[0][trk] = mixl;
sm[1][trk] = mixr;
}
ITrackInfo *NES_VRC6::GetTrackInfo(int trk)
{
if(trk<2)
{
trkinfo[trk].max_volume = 15;
trkinfo[trk].volume = volume[trk];
trkinfo[trk]._freq = freq2[trk];
trkinfo[trk].freq = freq2[trk]?clock/16/(freq2[trk]+1):0;
trkinfo[trk].tone = duty[trk];
trkinfo[trk].key = (volume[trk]>0)&&enable[trk]&&!gate[trk];
return &trkinfo[trk];
}
else if(trk==2)
{
trkinfo[2].max_volume = 255;
trkinfo[2].volume = volume[2];
trkinfo[2]._freq = freq2[2];
trkinfo[2].freq = freq2[2]?clock/14/(freq2[2]+1):0;
trkinfo[2].tone = -1;
trkinfo[2].key = (enable[2]>0);
return &trkinfo[2];
}
else
return NULL;
}
void NES_VRC6::SetClock (double c)
{
clock = c;
}
void NES_VRC6::SetRate (double r)
{
rate = r ? r : DEFAULT_RATE;
}
void NES_VRC6::SetOption (int id, int val)
{
if(id<OPT_END)
{
//option[id] = val;
}
}
void NES_VRC6::Reset ()
{
Write (0x9003, 0);
for (int i = 0; i < 3; i++)
{
Write (0x9000 + i, 0);
Write (0xa000 + i, 0);
Write (0xb000 + i, 0);
}
count14 = 0;
mask = 0;
counter[0] = 0;
counter[1] = 0;
counter[2] = 0;
phase[0] = 0;
phase[0] = 1;
phase[0] = 2;
}
INT16 NES_VRC6::calc_sqr (int i, UINT32 clocks)
{
static const INT16 sqrtbl[8][16] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}
};
if (!enable[i])
return 0;
if (!halt)
{
counter[i] += clocks;
while(counter[i] > freq2[i])
{
phase[i] = (phase[i] + 1) & 15;
counter[i] -= (freq2[i] + 1);
}
}
return (gate[i]
|| sqrtbl[duty[i]][phase[i]])? volume[i] : 0;
}
INT16 NES_VRC6::calc_saw (UINT32 clocks)
{
if (!enable[2])
return 0;
if (!halt)
{
counter[2] += clocks;
while(counter[2] > freq2[2])
{
counter[2] -= (freq2[2] + 1);
// accumulate saw
++count14;
if (count14 >= 14)
{
count14 = 0;
phase[2] = 0;
}
else if (0 == (count14 & 1)) // only accumulate on even ticks
{
phase[2] = (phase[2] + volume[2]) & 0xFF; // note 8-bit wrapping behaviour
}
}
}
// only top 5 bits of saw are output
return phase[2] >> 3;
}
void NES_VRC6::Tick (UINT32 clocks)
{
out[0] = calc_sqr(0,clocks);
out[1] = calc_sqr(1,clocks);
out[2] = calc_saw(clocks);
}
UINT32 NES_VRC6::Render (INT32 b[2])
{
INT32 m[3];
m[0] = out[0];
m[1] = out[1];
m[2] = out[2];
// note: signal is inverted compared to 2A03
m[0] = (mask & 1) ? 0 : -m[0];
m[1] = (mask & 2) ? 0 : -m[1];
m[2] = (mask & 4) ? 0 : -m[2];
b[0] = m[0] * sm[0][0];
b[0] += m[1] * sm[0][1];
b[0] += m[2] * sm[0][2];
//b[0] >>= (7 - 7);
b[1] = m[0] * sm[1][0];
b[1] += m[1] * sm[1][1];
b[1] += m[2] * sm[1][2];
//b[1] >>= (7 - 7);
// master volume adjustment
const INT32 MASTER = INT32(256.0 * 1223.0 / 1920.0);
b[0] = (b[0] * MASTER) >> 8;
b[1] = (b[1] * MASTER) >> 8;
return 2;
}
bool NES_VRC6::Write (UINT32 adr, UINT32 val, UINT32 id)
{
int ch, cmap[4] = { 0, 0, 1, 2 };
switch (adr)
{
case 0x9000:
case 0xa000:
ch = cmap[(adr >> 12) & 3];
volume[ch] = val & 15;
duty[ch] = (val >> 4) & 7;
gate[ch] = (val >> 7) & 1;
break;
case 0xb000:
volume[2] = val & 63;
break;
case 0x9001:
case 0xa001:
case 0xb001:
ch = cmap[(adr >> 12) & 3];
freq[ch] = (freq[ch] & 0xf00) | val;
freq2[ch] = (freq[ch] >> freq_shift);
if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch];
break;
case 0x9002:
case 0xa002:
case 0xb002:
ch = cmap[(adr >> 12) & 3];
freq[ch] = ((val & 0xf) << 8) + (freq[ch] & 0xff);
freq2[ch] = (freq[ch] >> freq_shift);
if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch];
if (!enable[ch]) // if enable is being turned on, phase should be reset
{
if (ch == 2)
{
count14 = 0; // reset saw
}
phase[ch] = 0;
}
enable[ch] = (val >> 7) & 1;
break;
case 0x9003:
halt = val & 1;
freq_shift =
(val & 4) ? 8 :
(val & 2) ? 4 :
0;
freq2[0] = (freq[0] >> freq_shift);
freq2[1] = (freq[1] >> freq_shift);
freq2[2] = (freq[2] >> freq_shift);
if (counter[0] > freq2[0]) counter[0] = freq2[0];
if (counter[1] > freq2[1]) counter[1] = freq2[1];
if (counter[2] > freq2[2]) counter[2] = freq2[2];
break;
default:
return false;
}
return true;
}
bool NES_VRC6::Read (UINT32 adr, UINT32 & val, UINT32 id)
{
return false;
}
} // namespace

56
extern/NSFplay/nes_vrc6.h vendored Normal file
View file

@ -0,0 +1,56 @@
#ifndef _NES_VRC6_H_
#define _NES_VRC6_H_
#include "../device.h"
namespace xgm
{
class NES_VRC6:public ISoundChip
{
public:
enum
{
OPT_END
};
protected:
UINT32 counter[3]; // frequency divider
UINT32 phase[3]; // phase counter
UINT32 freq2[3]; // adjusted frequency
int count14; // saw 14-stage counter
//int option[OPT_END];
int mask;
INT32 sm[2][3]; // stereo mix
int duty[2];
int volume[3];
int enable[3];
int gate[3];
UINT32 freq[3];
INT16 calc_sqr (int i, UINT32 clocks);
INT16 calc_saw (UINT32 clocks);
bool halt;
int freq_shift;
double clock, rate;
INT32 out[3];
TrackInfoBasic trkinfo[3];
public:
NES_VRC6 ();
~NES_VRC6 ();
virtual void Reset ();
virtual void Tick (UINT32 clocks);
virtual UINT32 Render (INT32 b[2]);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0);
virtual void SetClock (double);
virtual void SetRate (double);
virtual void SetOption (int, int);
virtual void SetMask (int m){ mask = m; }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
};
} // namespace
#endif

189
extern/NSFplay/nes_vrc7.cpp vendored Normal file
View file

@ -0,0 +1,189 @@
#include <cstring>
#include "nes_vrc7.h"
namespace xgm
{
NES_VRC7::NES_VRC7 ()
{
use_all_channels = false;
patch_set = OPLL_VRC7_TONE;
patch_custom = NULL;
divider = 0;
opll = OPLL_new ( 3579545, DEFAULT_RATE);
OPLL_reset_patch (opll, patch_set);
SetClock(DEFAULT_CLOCK);
for(int c=0;c<2;++c)
//for(int t=0;t<6;++t)
for(int t=0;t<9;++t) // HACK for YM2413 support
sm[c][t] = 128;
}
NES_VRC7::~NES_VRC7 ()
{
OPLL_delete (opll);
}
void NES_VRC7::UseAllChannels(bool b)
{
use_all_channels = b;
}
void NES_VRC7::SetPatchSet(int p)
{
patch_set = p;
}
void NES_VRC7::SetPatchSetCustom (const UINT8* pset)
{
patch_custom = pset;
}
void NES_VRC7::SetClock (double c)
{
clock = c / 36;
}
void NES_VRC7::SetRate (double r)
{
//rate = r ? r : DEFAULT_RATE;
(void)r; // rate is ignored
rate = 49716;
OPLL_set_quality(opll, 1); // quality always on (not really a CPU hog)
OPLL_set_rate(opll,(uint32_t)rate);
}
void NES_VRC7::SetOption (int id, int val)
{
if(id<OPT_END)
{
option[id] = val;
}
}
void NES_VRC7::Reset ()
{
for (int i=0; i < 0x40; ++i)
{
Write(0x9010,i);
Write(0x9030,0);
}
divider = 0;
OPLL_reset_patch (opll, patch_set);
if (patch_custom)
{
uint8_t dump[19 * 8];
memcpy(dump, patch_custom, 16 * 8);
memset(dump + 16 * 8, 0, 3 * 8);
OPLL_setPatch(opll, dump);
}
OPLL_reset (opll);
}
void NES_VRC7::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr)
{
if (trk < 0) return;
//if (trk > 5) return;
if (trk > 8) return; // HACK YM2413
sm[0][trk] = mixl;
sm[1][trk] = mixr;
}
ITrackInfo *NES_VRC7::GetTrackInfo(int trk)
{
//if(opll&&trk<6)
if(opll&&trk<9) // HACK YM2413 (percussion mode isn't very diagnostic this way though)
{
trkinfo[trk].max_volume = 15;
trkinfo[trk].volume = 15 - ((opll->reg[0x30+trk])&15);
trkinfo[trk]._freq = opll->reg[0x10+trk]+((opll->reg[0x20+trk]&1)<<8);
int blk = (opll->reg[0x20+trk]>>1)&7;
trkinfo[trk].freq = clock*trkinfo[trk]._freq/(double)(0x80000>>blk);
trkinfo[trk].tone = (opll->reg[0x30+trk]>>4)&15;
//trkinfo[trk].key = (opll->reg[0x20+trk]&0x10)?true:false;
trkinfo[trk].key = opll->slot_key_status & (3 << trk)?true:false;
return &trkinfo[trk];
}
else
return NULL;
}
bool NES_VRC7::Write (UINT32 adr, UINT32 val, UINT32 id)
{
if (adr == 0x9010)
{
OPLL_writeIO (opll, 0, val);
return true;
}
if (adr == 0x9030)
{
OPLL_writeIO (opll, 1, val);
return true;
}
else
return false;
}
bool NES_VRC7::Read (UINT32 adr, UINT32 & val, UINT32 id)
{
return false;
}
void NES_VRC7::Tick (UINT32 clocks)
{
divider += clocks;
while (divider >= 36)
{
divider -= 36;
OPLL_calc(opll);
}
}
UINT32 NES_VRC7::Render (INT32 b[2])
{
b[0] = b[1] = 0;
for (int i=0; i < 6; ++i)
{
INT32 val = (mask & (1<<i)) ? 0 : opll->ch_out[i] >> 4;
b[0] += val * sm[0][i];
b[1] += val * sm[1][i];
}
// HACK for YM2413 support
if (use_all_channels)
{
for (int i=6; i < 9; ++i)
{
if (mask & (1<<i)) continue;
INT32 val;
if (opll->patch_number[i] > 15) // rhytm mode
{
if (i == 6) val = opll->ch_out[9]; // BD
else if (i == 7) val = opll->ch_out[10] + opll->ch_out[11]; // HH&SD
else val = opll->ch_out[12] + opll->ch_out[13]; // TOM&CYM
/* (i == 8) is implied */
}
else
{
val = opll->ch_out[i];
}
val >>= 4;
b[0] += val * sm[0][i];
b[1] += val * sm[1][i];
}
}
b[0] >>= (7 - 4);
b[1] >>= (7 - 4);
// master volume adjustment
const INT32 MASTER = INT32(1.15 * 256.0);
b[0] = (b[0] * MASTER) >> 8;
b[1] = (b[1] * MASTER) >> 8;
return 2;
}
}

53
extern/NSFplay/nes_vrc7.h vendored Normal file
View file

@ -0,0 +1,53 @@
#ifndef _NES_VRC7_H_
#define _NES_VRC7_H_
#include "../device.h"
#include "legacy/emu2413.h"
namespace xgm
{
class NES_VRC7 : public ISoundChip
{
public:
enum
{
OPT_OPLL=0,
OPT_END
};
protected:
int option[OPT_END];
int mask;
int patch_set;
const UINT8* patch_custom;
//INT32 sm[2][6]; // stereo mix
INT32 sm[2][9]; // stereo mix temporary HACK to support YM2413
INT16 buf[2];
OPLL *opll;
UINT32 divider; // clock divider
double clock, rate;
//TrackInfoBasic trkinfo[6];
TrackInfoBasic trkinfo[9]; // HACK to support YM2413
bool use_all_channels;
public:
NES_VRC7 ();
~NES_VRC7 ();
virtual void Reset ();
virtual void Tick (UINT32 clocks);
virtual UINT32 Render (INT32 b[2]);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0);
virtual void UseAllChannels (bool b);
virtual void SetPatchSet (int p);
virtual void SetPatchSetCustom (const UINT8* pset);
virtual void SetClock (double);
virtual void SetRate (double);
virtual void SetOption (int, int);
virtual void SetMask (int m){ mask = m; if(opll) OPLL_setMask(opll, m); }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
};
} // namespace
#endif