636 lines
15 KiB
C++
636 lines
15 KiB
C++
|
//////////////////////////////////////////////////////////////////////////
|
|||
|
// //
|
|||
|
// NES APU core //
|
|||
|
// Norix //
|
|||
|
// written 2002/06/27 //
|
|||
|
// last modify ----/--/-- //
|
|||
|
//////////////////////////////////////////////////////////////////////////
|
|||
|
#include "DebugOut.h"
|
|||
|
#include "App.h"
|
|||
|
#include "Config.h"
|
|||
|
|
|||
|
#include "nes.h"
|
|||
|
#include "mmu.h"
|
|||
|
#include "cpu.h"
|
|||
|
#include "ppu.h"
|
|||
|
#include "rom.h"
|
|||
|
#include "apu.h"
|
|||
|
|
|||
|
// Volume adjust
|
|||
|
// Internal sounds
|
|||
|
#define RECTANGLE_VOL (0x0F0)
|
|||
|
#define TRIANGLE_VOL (0x130)
|
|||
|
#define NOISE_VOL (0x0C0)
|
|||
|
#define DPCM_VOL (0x0F0)
|
|||
|
// Extra sounds
|
|||
|
#define VRC6_VOL (0x0F0)
|
|||
|
#define VRC7_VOL (0x130)
|
|||
|
#define FDS_VOL (0x0F0)
|
|||
|
#define MMC5_VOL (0x0F0)
|
|||
|
#define N106_VOL (0x088)
|
|||
|
#define FME7_VOL (0x130)
|
|||
|
|
|||
|
APU::APU( NES* parent )
|
|||
|
{
|
|||
|
exsound_select = 0;
|
|||
|
|
|||
|
nes = parent;
|
|||
|
internal.SetParent( parent );
|
|||
|
|
|||
|
last_data = last_diff = 0;
|
|||
|
|
|||
|
ZEROMEMORY( m_SoundBuffer, sizeof(m_SoundBuffer) );
|
|||
|
|
|||
|
ZEROMEMORY( lowpass_filter, sizeof(lowpass_filter) );
|
|||
|
ZEROMEMORY( &queue, sizeof(queue) );
|
|||
|
ZEROMEMORY( &exqueue, sizeof(exqueue) );
|
|||
|
|
|||
|
for( INT i = 0; i < 16; i++ ) {
|
|||
|
m_bMute[i] = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
APU::~APU()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
void APU::SetQueue( INT writetime, WORD addr, BYTE data )
|
|||
|
{
|
|||
|
queue.data[queue.wrptr].time = writetime;
|
|||
|
queue.data[queue.wrptr].addr = addr;
|
|||
|
queue.data[queue.wrptr].data = data;
|
|||
|
queue.wrptr++;
|
|||
|
queue.wrptr&=QUEUE_LENGTH-1;
|
|||
|
if( queue.wrptr == queue.rdptr ) {
|
|||
|
DEBUGOUT( "queue overflow.\n" );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
BOOL APU::GetQueue( INT writetime, QUEUEDATA& ret )
|
|||
|
{
|
|||
|
if( queue.wrptr == queue.rdptr ) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if( queue.data[queue.rdptr].time <= writetime ) {
|
|||
|
ret = queue.data[queue.rdptr];
|
|||
|
queue.rdptr++;
|
|||
|
queue.rdptr&=QUEUE_LENGTH-1;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
void APU::SetExQueue( INT writetime, WORD addr, BYTE data )
|
|||
|
{
|
|||
|
exqueue.data[exqueue.wrptr].time = writetime;
|
|||
|
exqueue.data[exqueue.wrptr].addr = addr;
|
|||
|
exqueue.data[exqueue.wrptr].data = data;
|
|||
|
exqueue.wrptr++;
|
|||
|
exqueue.wrptr&=QUEUE_LENGTH-1;
|
|||
|
if( exqueue.wrptr == exqueue.rdptr ) {
|
|||
|
DEBUGOUT( "exqueue overflow.\n" );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
BOOL APU::GetExQueue( INT writetime, QUEUEDATA& ret )
|
|||
|
{
|
|||
|
if( exqueue.wrptr == exqueue.rdptr ) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
if( exqueue.data[exqueue.rdptr].time <= writetime ) {
|
|||
|
ret = exqueue.data[exqueue.rdptr];
|
|||
|
exqueue.rdptr++;
|
|||
|
exqueue.rdptr&=QUEUE_LENGTH-1;
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
void APU::QueueClear()
|
|||
|
{
|
|||
|
ZEROMEMORY( &queue, sizeof(queue) );
|
|||
|
ZEROMEMORY( &exqueue, sizeof(exqueue) );
|
|||
|
}
|
|||
|
|
|||
|
void APU::QueueFlush()
|
|||
|
{
|
|||
|
while( queue.wrptr != queue.rdptr ) {
|
|||
|
WriteProcess( queue.data[queue.rdptr].addr, queue.data[queue.rdptr].data );
|
|||
|
queue.rdptr++;
|
|||
|
queue.rdptr&=QUEUE_LENGTH-1;
|
|||
|
}
|
|||
|
|
|||
|
while( exqueue.wrptr != exqueue.rdptr ) {
|
|||
|
WriteExProcess( exqueue.data[exqueue.rdptr].addr, exqueue.data[exqueue.rdptr].data );
|
|||
|
exqueue.rdptr++;
|
|||
|
exqueue.rdptr&=QUEUE_LENGTH-1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void APU::SoundSetup()
|
|||
|
{
|
|||
|
FLOAT fClock = nes->nescfg->CpuClock;
|
|||
|
INT nRate = (INT)Config.sound.nRate;
|
|||
|
internal.Setup( fClock, nRate );
|
|||
|
vrc6.Setup( fClock, nRate );
|
|||
|
vrc7.Setup( fClock, nRate );
|
|||
|
mmc5.Setup( fClock, nRate );
|
|||
|
fds.Setup ( fClock, nRate );
|
|||
|
n106.Setup( fClock, nRate );
|
|||
|
fme7.Setup( fClock, nRate );
|
|||
|
}
|
|||
|
|
|||
|
void APU::Reset()
|
|||
|
{
|
|||
|
ZEROMEMORY( &queue, sizeof(queue) );
|
|||
|
ZEROMEMORY( &exqueue, sizeof(exqueue) );
|
|||
|
|
|||
|
elapsed_time = 0;
|
|||
|
|
|||
|
FLOAT fClock = nes->nescfg->CpuClock;
|
|||
|
INT nRate = (INT)Config.sound.nRate;
|
|||
|
internal.Reset( fClock, nRate );
|
|||
|
vrc6.Reset( fClock, nRate );
|
|||
|
vrc7.Reset( fClock, nRate );
|
|||
|
mmc5.Reset( fClock, nRate );
|
|||
|
fds.Reset ( fClock, nRate );
|
|||
|
n106.Reset( fClock, nRate );
|
|||
|
fme7.Reset( fClock, nRate );
|
|||
|
|
|||
|
SoundSetup();
|
|||
|
}
|
|||
|
|
|||
|
void APU::SelectExSound( BYTE data )
|
|||
|
{
|
|||
|
exsound_select = data;
|
|||
|
}
|
|||
|
|
|||
|
BYTE APU::Read( WORD addr )
|
|||
|
{
|
|||
|
return internal.SyncRead( addr );
|
|||
|
}
|
|||
|
|
|||
|
void APU::Write( WORD addr, BYTE data )
|
|||
|
{
|
|||
|
// $4018<31><38>VirtuaNES<45>ŗL<C597>|<7C>[<5B>g
|
|||
|
if( addr >= 0x4000 && addr <= 0x401F ) {
|
|||
|
internal.SyncWrite( addr, data );
|
|||
|
SetQueue( nes->cpu->GetTotalCycles(), addr, data );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
BYTE APU::ExRead( WORD addr )
|
|||
|
{
|
|||
|
BYTE data = 0;
|
|||
|
|
|||
|
if( exsound_select & 0x10 ) {
|
|||
|
if( addr == 0x4800 ) {
|
|||
|
SetExQueue( nes->cpu->GetTotalCycles(), 0, 0 );
|
|||
|
}
|
|||
|
}
|
|||
|
if( exsound_select & 0x04 ) {
|
|||
|
if( addr >= 0x4040 && addr < 0x4100 ) {
|
|||
|
data = fds.SyncRead( addr );
|
|||
|
}
|
|||
|
}
|
|||
|
if( exsound_select & 0x08 ) {
|
|||
|
if( addr >= 0x5000 && addr <= 0x5015 ) {
|
|||
|
data = mmc5.SyncRead( addr );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return data;
|
|||
|
}
|
|||
|
|
|||
|
void APU::ExWrite( WORD addr, BYTE data )
|
|||
|
{
|
|||
|
SetExQueue( nes->cpu->GetTotalCycles(), addr, data );
|
|||
|
|
|||
|
if( exsound_select & 0x04 ) {
|
|||
|
if( addr >= 0x4040 && addr < 0x4100 ) {
|
|||
|
fds.SyncWrite( addr, data );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( exsound_select & 0x08 ) {
|
|||
|
if( addr >= 0x5000 && addr <= 0x5015 ) {
|
|||
|
mmc5.SyncWrite( addr, data );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void APU::Sync()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
void APU::SyncDPCM( INT cycles )
|
|||
|
{
|
|||
|
internal.Sync( cycles );
|
|||
|
|
|||
|
if( exsound_select & 0x04 ) {
|
|||
|
fds.Sync( cycles );
|
|||
|
}
|
|||
|
if( exsound_select & 0x08 ) {
|
|||
|
mmc5.Sync( cycles );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void APU::WriteProcess( WORD addr, BYTE data )
|
|||
|
{
|
|||
|
// $4018<31><38>VirtuaNES<45>ŗL<C597>|<7C>[<5B>g
|
|||
|
if( addr >= 0x4000 && addr <= 0x401F ) {
|
|||
|
internal.Write( addr, data );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void APU::WriteExProcess( WORD addr, BYTE data )
|
|||
|
{
|
|||
|
if( exsound_select & 0x01 ) {
|
|||
|
vrc6.Write( addr, data );
|
|||
|
}
|
|||
|
if( exsound_select & 0x02 ) {
|
|||
|
vrc7.Write( addr, data );
|
|||
|
}
|
|||
|
if( exsound_select & 0x04 ) {
|
|||
|
fds.Write( addr, data );
|
|||
|
}
|
|||
|
if( exsound_select & 0x08 ) {
|
|||
|
mmc5.Write( addr, data );
|
|||
|
}
|
|||
|
if( exsound_select & 0x10 ) {
|
|||
|
if( addr == 0x0000 ) {
|
|||
|
BYTE dummy = n106.Read( addr );
|
|||
|
} else {
|
|||
|
n106.Write( addr, data );
|
|||
|
}
|
|||
|
}
|
|||
|
if( exsound_select & 0x20 ) {
|
|||
|
fme7.Write( addr, data );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void APU::Process( LPBYTE lpBuffer, DWORD dwSize )
|
|||
|
{
|
|||
|
INT nBits = Config.sound.nBits;
|
|||
|
DWORD dwLength = dwSize / (nBits/8);
|
|||
|
INT output;
|
|||
|
QUEUEDATA q;
|
|||
|
DWORD writetime;
|
|||
|
|
|||
|
LPSHORT pSoundBuf = m_SoundBuffer;
|
|||
|
INT nCcount = 0;
|
|||
|
|
|||
|
INT nFilterType = Config.sound.nFilterType;
|
|||
|
|
|||
|
if( !Config.sound.bEnable ) {
|
|||
|
::FillMemory( lpBuffer, dwSize, (BYTE)(Config.sound.nRate==8?128:0) );
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Volume setup
|
|||
|
// 0:Master
|
|||
|
// 1:Rectangle 1
|
|||
|
// 2:Rectangle 2
|
|||
|
// 3:Triangle
|
|||
|
// 4:Noise
|
|||
|
// 5:DPCM
|
|||
|
// 6:VRC6
|
|||
|
// 7:VRC7
|
|||
|
// 8:FDS
|
|||
|
// 9:MMC5
|
|||
|
// 10:N106
|
|||
|
// 11:FME7
|
|||
|
INT vol[24];
|
|||
|
BOOL* bMute = m_bMute;
|
|||
|
SHORT* nVolume = Config.sound.nVolume;
|
|||
|
|
|||
|
INT nMasterVolume = bMute[0]?nVolume[0]:0;
|
|||
|
|
|||
|
// Internal
|
|||
|
vol[ 0] = bMute[1]?(RECTANGLE_VOL*nVolume[1]*nMasterVolume)/(100*100):0;
|
|||
|
vol[ 1] = bMute[2]?(RECTANGLE_VOL*nVolume[2]*nMasterVolume)/(100*100):0;
|
|||
|
vol[ 2] = bMute[3]?(TRIANGLE_VOL *nVolume[3]*nMasterVolume)/(100*100):0;
|
|||
|
vol[ 3] = bMute[4]?(NOISE_VOL *nVolume[4]*nMasterVolume)/(100*100):0;
|
|||
|
vol[ 4] = bMute[5]?(DPCM_VOL *nVolume[5]*nMasterVolume)/(100*100):0;
|
|||
|
|
|||
|
// VRC6
|
|||
|
vol[ 5] = bMute[6]?(VRC6_VOL*nVolume[6]*nMasterVolume)/(100*100):0;
|
|||
|
vol[ 6] = bMute[7]?(VRC6_VOL*nVolume[6]*nMasterVolume)/(100*100):0;
|
|||
|
vol[ 7] = bMute[8]?(VRC6_VOL*nVolume[6]*nMasterVolume)/(100*100):0;
|
|||
|
|
|||
|
// VRC7
|
|||
|
vol[ 8] = bMute[6]?(VRC7_VOL*nVolume[7]*nMasterVolume)/(100*100):0;
|
|||
|
|
|||
|
// FDS
|
|||
|
vol[ 9] = bMute[6]?(FDS_VOL*nVolume[8]*nMasterVolume)/(100*100):0;
|
|||
|
|
|||
|
// MMC5
|
|||
|
vol[10] = bMute[6]?(MMC5_VOL*nVolume[9]*nMasterVolume)/(100*100):0;
|
|||
|
vol[11] = bMute[7]?(MMC5_VOL*nVolume[9]*nMasterVolume)/(100*100):0;
|
|||
|
vol[12] = bMute[8]?(MMC5_VOL*nVolume[9]*nMasterVolume)/(100*100):0;
|
|||
|
|
|||
|
// N106
|
|||
|
vol[13] = bMute[ 6]?(N106_VOL*nVolume[10]*nMasterVolume)/(100*100):0;
|
|||
|
vol[14] = bMute[ 7]?(N106_VOL*nVolume[10]*nMasterVolume)/(100*100):0;
|
|||
|
vol[15] = bMute[ 8]?(N106_VOL*nVolume[10]*nMasterVolume)/(100*100):0;
|
|||
|
vol[16] = bMute[ 9]?(N106_VOL*nVolume[10]*nMasterVolume)/(100*100):0;
|
|||
|
vol[17] = bMute[10]?(N106_VOL*nVolume[10]*nMasterVolume)/(100*100):0;
|
|||
|
vol[18] = bMute[11]?(N106_VOL*nVolume[10]*nMasterVolume)/(100*100):0;
|
|||
|
vol[19] = bMute[12]?(N106_VOL*nVolume[10]*nMasterVolume)/(100*100):0;
|
|||
|
vol[20] = bMute[13]?(N106_VOL*nVolume[10]*nMasterVolume)/(100*100):0;
|
|||
|
|
|||
|
// FME7
|
|||
|
vol[21] = bMute[6]?(FME7_VOL*nVolume[11]*nMasterVolume)/(100*100):0;
|
|||
|
vol[22] = bMute[7]?(FME7_VOL*nVolume[11]*nMasterVolume)/(100*100):0;
|
|||
|
vol[23] = bMute[8]?(FME7_VOL*nVolume[11]*nMasterVolume)/(100*100):0;
|
|||
|
|
|||
|
// double cycle_rate = ((double)FRAME_CYCLES*60.0/12.0)/(double)Config.sound.nRate;
|
|||
|
double cycle_rate = ((double)nes->nescfg->FrameCycles*60.0/12.0)/(double)Config.sound.nRate;
|
|||
|
|
|||
|
// CPU<50>T<EFBFBD>C<EFBFBD>N<EFBFBD><4E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[<5B>v<EFBFBD><76><EFBFBD>Ă<EFBFBD><C482>܂<EFBFBD><DC82><EFBFBD><EFBFBD><EFBFBD><EFBFBD>̑<CE8D>
|
|||
|
if( elapsed_time > nes->cpu->GetTotalCycles() ) {
|
|||
|
QueueFlush();
|
|||
|
}
|
|||
|
|
|||
|
while( dwLength-- ) {
|
|||
|
writetime = (DWORD)elapsed_time;
|
|||
|
|
|||
|
while( GetQueue( writetime, q ) ) {
|
|||
|
WriteProcess( q.addr, q.data );
|
|||
|
}
|
|||
|
|
|||
|
while( GetExQueue( writetime, q ) ) {
|
|||
|
WriteExProcess( q.addr, q.data );
|
|||
|
}
|
|||
|
|
|||
|
// 0-4:internal 5-7:VRC6 8:VRC7 9:FDS 10-12:MMC5 13-20:N106 21-23:FME7
|
|||
|
output = 0;
|
|||
|
output += internal.Process( 0 )*vol[0];
|
|||
|
output += internal.Process( 1 )*vol[1];
|
|||
|
output += internal.Process( 2 )*vol[2];
|
|||
|
output += internal.Process( 3 )*vol[3];
|
|||
|
output += internal.Process( 4 )*vol[4];
|
|||
|
|
|||
|
if( exsound_select & 0x01 ) {
|
|||
|
output += vrc6.Process( 0 )*vol[5];
|
|||
|
output += vrc6.Process( 1 )*vol[6];
|
|||
|
output += vrc6.Process( 2 )*vol[7];
|
|||
|
}
|
|||
|
if( exsound_select & 0x02 ) {
|
|||
|
output += vrc7.Process( 0 )*vol[8];
|
|||
|
}
|
|||
|
if( exsound_select & 0x04 ) {
|
|||
|
output += fds.Process( 0 )*vol[9];
|
|||
|
}
|
|||
|
if( exsound_select & 0x08 ) {
|
|||
|
output += mmc5.Process( 0 )*vol[10];
|
|||
|
output += mmc5.Process( 1 )*vol[11];
|
|||
|
output += mmc5.Process( 2 )*vol[12];
|
|||
|
}
|
|||
|
if( exsound_select & 0x10 ) {
|
|||
|
output += n106.Process( 0 )*vol[13];
|
|||
|
output += n106.Process( 1 )*vol[14];
|
|||
|
output += n106.Process( 2 )*vol[15];
|
|||
|
output += n106.Process( 3 )*vol[16];
|
|||
|
output += n106.Process( 4 )*vol[17];
|
|||
|
output += n106.Process( 5 )*vol[18];
|
|||
|
output += n106.Process( 6 )*vol[19];
|
|||
|
output += n106.Process( 7 )*vol[20];
|
|||
|
}
|
|||
|
if( exsound_select & 0x20 ) {
|
|||
|
fme7.Process( 3 ); // Envelope & Noise
|
|||
|
output += fme7.Process( 0 )*vol[21];
|
|||
|
output += fme7.Process( 1 )*vol[22];
|
|||
|
output += fme7.Process( 2 )*vol[23];
|
|||
|
}
|
|||
|
|
|||
|
output >>= 8;
|
|||
|
|
|||
|
if( nFilterType == 1 ) {
|
|||
|
//<2F><><EFBFBD>[<5B>p<EFBFBD>X<EFBFBD>t<EFBFBD>B<EFBFBD><42><EFBFBD>^<5E>[TYPE 1(Simple)
|
|||
|
output = (lowpass_filter[0]+output)/2;
|
|||
|
lowpass_filter[0] = output;
|
|||
|
} else if( nFilterType == 2 ) {
|
|||
|
//<2F><><EFBFBD>[<5B>p<EFBFBD>X<EFBFBD>t<EFBFBD>B<EFBFBD><42><EFBFBD>^<5E>[TYPE 2(Weighted type 1)
|
|||
|
output = (lowpass_filter[1]+lowpass_filter[0]+output)/3;
|
|||
|
lowpass_filter[1] = lowpass_filter[0];
|
|||
|
lowpass_filter[0] = output;
|
|||
|
} else if( nFilterType == 3 ) {
|
|||
|
//<2F><><EFBFBD>[<5B>p<EFBFBD>X<EFBFBD>t<EFBFBD>B<EFBFBD><42><EFBFBD>^<5E>[TYPE 3(Weighted type 2)
|
|||
|
output = (lowpass_filter[2]+lowpass_filter[1]+lowpass_filter[0]+output)/4;
|
|||
|
lowpass_filter[2] = lowpass_filter[1];
|
|||
|
lowpass_filter[1] = lowpass_filter[0];
|
|||
|
lowpass_filter[0] = output;
|
|||
|
} else if( nFilterType == 4 ) {
|
|||
|
//<2F><><EFBFBD>[<5B>p<EFBFBD>X<EFBFBD>t<EFBFBD>B<EFBFBD><42><EFBFBD>^<5E>[TYPE 4(Weighted type 3)
|
|||
|
output = (lowpass_filter[1]+lowpass_filter[0]*2+output)/4;
|
|||
|
lowpass_filter[1] = lowpass_filter[0];
|
|||
|
lowpass_filter[0] = output;
|
|||
|
}
|
|||
|
|
|||
|
#if 0
|
|||
|
// DC<44><43><EFBFBD><EFBFBD><EFBFBD>̃J<CC83>b<EFBFBD>g
|
|||
|
{
|
|||
|
static double ave = 0.0, max=0.0, min=0.0;
|
|||
|
double delta;
|
|||
|
delta = (max-min)/32768.0;
|
|||
|
max -= delta;
|
|||
|
min += delta;
|
|||
|
if( output > max ) max = output;
|
|||
|
if( output < min ) min = output;
|
|||
|
ave -= ave/1024.0;
|
|||
|
ave += (max+min)/2048.0;
|
|||
|
output -= (INT)ave;
|
|||
|
}
|
|||
|
#endif
|
|||
|
#if 1
|
|||
|
// DC<44><43><EFBFBD><EFBFBD><EFBFBD>̃J<CC83>b<EFBFBD>g(HPF TEST)
|
|||
|
{
|
|||
|
// static double cutoff = (2.0*3.141592653579*40.0/44100.0);
|
|||
|
static double cutofftemp = (2.0*3.141592653579*40.0);
|
|||
|
double cutoff = cutofftemp/(double)Config.sound.nRate;
|
|||
|
static double tmp = 0.0;
|
|||
|
double in, out;
|
|||
|
|
|||
|
in = (double)output;
|
|||
|
out = (in - tmp);
|
|||
|
tmp = tmp + cutoff * out;
|
|||
|
|
|||
|
output = (INT)out;
|
|||
|
}
|
|||
|
#endif
|
|||
|
#if 0
|
|||
|
// <20>X<EFBFBD>p<EFBFBD>C<EFBFBD>N<EFBFBD>m<EFBFBD>C<EFBFBD>Y<EFBFBD>̏<EFBFBD><CC8F><EFBFBD>(AGC TEST)
|
|||
|
{
|
|||
|
INT diff = abs(output-last_data);
|
|||
|
if( diff > 0x4000 ) {
|
|||
|
output /= 4;
|
|||
|
} else
|
|||
|
if( diff > 0x3000 ) {
|
|||
|
output /= 3;
|
|||
|
} else
|
|||
|
if( diff > 0x2000 ) {
|
|||
|
output /= 2;
|
|||
|
}
|
|||
|
last_data = output;
|
|||
|
}
|
|||
|
#endif
|
|||
|
// Limit
|
|||
|
if( output > 0x7FFF ) {
|
|||
|
output = 0x7FFF;
|
|||
|
} else if( output < -0x8000 ) {
|
|||
|
output = -0x8000;
|
|||
|
}
|
|||
|
|
|||
|
if( nBits != 8 ) {
|
|||
|
*(SHORT*)lpBuffer = (SHORT)output;
|
|||
|
lpBuffer += sizeof(SHORT);
|
|||
|
} else {
|
|||
|
*lpBuffer++ = (output>>8)^0x80;
|
|||
|
}
|
|||
|
|
|||
|
if( nCcount < 0x0100 )
|
|||
|
pSoundBuf[nCcount++] = (SHORT)output;
|
|||
|
|
|||
|
// elapsedtime += cycle_rate;
|
|||
|
elapsed_time += cycle_rate;
|
|||
|
}
|
|||
|
|
|||
|
#if 1
|
|||
|
if( elapsed_time > ((nes->nescfg->FrameCycles/24)+nes->cpu->GetTotalCycles()) ) {
|
|||
|
elapsed_time = nes->cpu->GetTotalCycles();
|
|||
|
}
|
|||
|
if( (elapsed_time+(nes->nescfg->FrameCycles/6)) < nes->cpu->GetTotalCycles() ) {
|
|||
|
elapsed_time = nes->cpu->GetTotalCycles();
|
|||
|
}
|
|||
|
#else
|
|||
|
elapsed_time = nes->cpu->GetTotalCycles();
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
// <20>`<60><><EFBFBD><EFBFBD><EFBFBD>l<EFBFBD><6C><EFBFBD>̎<EFBFBD><CC8E>g<EFBFBD><67><EFBFBD>擾<EFBFBD>T<EFBFBD>u<EFBFBD><75><EFBFBD>[<5B>`<60><>(NSF<53>p)
|
|||
|
INT APU::GetChannelFrequency( INT no )
|
|||
|
{
|
|||
|
if( !m_bMute[0] )
|
|||
|
return 0;
|
|||
|
|
|||
|
// Internal
|
|||
|
if( no < 5 ) {
|
|||
|
return m_bMute[no+1]?internal.GetFreq( no ):0;
|
|||
|
}
|
|||
|
// VRC6
|
|||
|
if( (exsound_select & 0x01) && no >= 0x0100 && no < 0x0103 ) {
|
|||
|
return m_bMute[6+(no&0x03)]?vrc6.GetFreq( no & 0x03 ):0;
|
|||
|
}
|
|||
|
// FDS
|
|||
|
if( (exsound_select & 0x04) && no == 0x300 ) {
|
|||
|
return m_bMute[6]?fds.GetFreq( 0 ):0;
|
|||
|
}
|
|||
|
// MMC5
|
|||
|
if( (exsound_select & 0x08) && no >= 0x0400 && no < 0x0402 ) {
|
|||
|
return m_bMute[6+(no&0x03)]?mmc5.GetFreq( no & 0x03 ):0;
|
|||
|
}
|
|||
|
// N106
|
|||
|
if( (exsound_select & 0x10) && no >= 0x0500 && no < 0x0508 ) {
|
|||
|
return m_bMute[6+(no&0x07)]?n106.GetFreq( no & 0x07 ):0;
|
|||
|
}
|
|||
|
// FME7
|
|||
|
if( (exsound_select & 0x20) && no >= 0x0600 && no < 0x0603 ) {
|
|||
|
return m_bMute[6+(no&0x03)]?fme7.GetFreq( no & 0x03 ):0;
|
|||
|
}
|
|||
|
// VRC7
|
|||
|
if( (exsound_select & 0x02) && no >= 0x0700 && no < 0x0709 ) {
|
|||
|
return m_bMute[6]?vrc7.GetFreq(no&0x0F):0;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
// State Save/Load
|
|||
|
void APU::SaveState( LPBYTE p )
|
|||
|
{
|
|||
|
#ifdef _DEBUG
|
|||
|
LPBYTE pold = p;
|
|||
|
#endif
|
|||
|
|
|||
|
// <20><><EFBFBD>Ԏ<EFBFBD><D48E><EFBFBD><F093AF8A><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Flush<73><68><EFBFBD><EFBFBD>
|
|||
|
QueueFlush();
|
|||
|
|
|||
|
internal.SaveState( p );
|
|||
|
p += (internal.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
|
|||
|
// VRC6
|
|||
|
if( exsound_select & 0x01 ) {
|
|||
|
vrc6.SaveState( p );
|
|||
|
p += (vrc6.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// VRC7 (not support)
|
|||
|
if( exsound_select & 0x02 ) {
|
|||
|
vrc7.SaveState( p );
|
|||
|
p += (vrc7.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// FDS
|
|||
|
if( exsound_select & 0x04 ) {
|
|||
|
fds.SaveState( p );
|
|||
|
p += (fds.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// MMC5
|
|||
|
if( exsound_select & 0x08 ) {
|
|||
|
mmc5.SaveState( p );
|
|||
|
p += (mmc5.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// N106
|
|||
|
if( exsound_select & 0x10 ) {
|
|||
|
n106.SaveState( p );
|
|||
|
p += (n106.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// FME7
|
|||
|
if( exsound_select & 0x20 ) {
|
|||
|
fme7.SaveState( p );
|
|||
|
p += (fme7.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
|
|||
|
#ifdef _DEBUG
|
|||
|
DEBUGOUT( "SAVE APU SIZE:%d\n", p-pold );
|
|||
|
#endif
|
|||
|
}
|
|||
|
|
|||
|
void APU::LoadState( LPBYTE p )
|
|||
|
{
|
|||
|
// <20><><EFBFBD>Ԏ<EFBFBD><D48E><EFBFBD><F093AF8A><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ׂɏ<D782><C98F><EFBFBD>
|
|||
|
QueueClear();
|
|||
|
|
|||
|
internal.LoadState( p );
|
|||
|
p += (internal.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
|
|||
|
// VRC6
|
|||
|
if( exsound_select & 0x01 ) {
|
|||
|
vrc6.LoadState( p );
|
|||
|
p += (vrc6.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// VRC7 (not support)
|
|||
|
if( exsound_select & 0x02 ) {
|
|||
|
vrc7.LoadState( p );
|
|||
|
p += (vrc7.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// FDS
|
|||
|
if( exsound_select & 0x04 ) {
|
|||
|
fds.LoadState( p );
|
|||
|
p += (fds.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// MMC5
|
|||
|
if( exsound_select & 0x08 ) {
|
|||
|
mmc5.LoadState( p );
|
|||
|
p += (mmc5.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// N106
|
|||
|
if( exsound_select & 0x10 ) {
|
|||
|
n106.LoadState( p );
|
|||
|
p += (n106.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
// FME7
|
|||
|
if( exsound_select & 0x20 ) {
|
|||
|
fme7.LoadState( p );
|
|||
|
p += (fme7.GetStateSize()+15)&(~0x0F); // Padding
|
|||
|
}
|
|||
|
}
|