⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 apu.cpp

📁 著名的任天堂FC游戏机模拟器VirtuaNes 085版的源码!
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//////////////////////////////////////////////////////////////////////////
//                                                                      //
//      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偼VirtuaNES屌桳億乕僩
	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偼VirtuaNES屌桳億乕僩
	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;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -