📄 apu.cpp
字号:
//////////////////////////////////////////////////////////////////////////
// //
// 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 + -