【模拟器代码问题】模拟器如何通过代码控制Rom的背景音乐
求助:模拟器源码中通过哪段代码控制Rom背景音乐的播放?PS:看过一些模拟器的源码,大概都分为APU、PPU、NES那样几个版块。请大侠告知是哪个模块。感激不尽~~ 看了下VirtualNes的源码,是APU那部分,全称应该是Audio Process Unit 能否再详细一些呢?本人比较熟悉上层的编程思想,但对于NES的底层算法机制基本是彻头彻尾的外行。
楼上的大侠可否帮我准确定位一下,是哪个.cpp文件中的哪个函数,感激不尽~~
这里有相应的模拟器源码,就当送给大侠了~~
http://kenkao.qupan.com/5096520.html 原帖由 独孤残云 于 2009-11-8 09:56 发表 http://bbs.emu618.com/forum/images/common/back.gif
能否再详细一些呢?本人比较熟悉上层的编程思想,但对于NES的底层算法机制基本是彻头彻尾的外行。
楼上的大侠可否帮我准确定位一下,是哪个.cpp文件中的哪个函数,感激不尽~~
这里有相应的模拟器源码,就当送给大侠 ...
聲音部分(Audoi Process Unit = APU):
.\NES\APU.cpp
.\NES\APU.h
影像處理部份(Picture Processing Unit = PPU):
.\NES\PPU.cpp
.\NES\PPU.h
如果原碼用C跟ASM混搭也不錯 楼上是高手 哈哈 可以的话,希望可以得到krizal团长大人或者哪位大侠的详细赐教,就是指Apu模块下这些函数的具体含义和作用。我对这些NES机制的算法很感兴趣。
感激不尽~~ 这是我手中源码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 = TRUE;
}
}
APU::~APU()
{
}
void APU::SetQueue( INT writetime, WORD addr, BYTE data )
{
queue.data.time = writetime;
queue.data.addr = addr;
queue.data.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.time <= writetime ) {
ret = queue.data;
queue.rdptr++;
queue.rdptr&=QUEUE_LENGTH-1;
return TRUE;
}
return FALSE;
}
void APU::SetExQueue( INT writetime, WORD addr, BYTE data )
{
exqueue.data.time = writetime;
exqueue.data.addr = addr;
exqueue.data.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.time <= writetime ) {
ret = exqueue.data;
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.addr, queue.data.data );
queue.rdptr++;
queue.rdptr&=QUEUE_LENGTH-1;
}
while( exqueue.wrptr != exqueue.rdptr ) {
WriteExProcess( exqueue.data.addr, exqueue.data.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;
BOOL* bMute = m_bMute;
SHORT* nVolume = Config.sound.nVolume;
INT nMasterVolume = bMute?nVolume:0;
// Internal
vol[ 0] = bMute?(RECTANGLE_VOL*nVolume*nMasterVolume)/(100*100):0;
vol[ 1] = bMute?(RECTANGLE_VOL*nVolume*nMasterVolume)/(100*100):0;
vol[ 2] = bMute?(TRIANGLE_VOL *nVolume*nMasterVolume)/(100*100):0;
vol[ 3] = bMute?(NOISE_VOL *nVolume*nMasterVolume)/(100*100):0;
vol[ 4] = bMute?(DPCM_VOL *nVolume*nMasterVolume)/(100*100):0;
// VRC6
vol[ 5] = bMute?(VRC6_VOL*nVolume*nMasterVolume)/(100*100):0;
vol[ 6] = bMute?(VRC6_VOL*nVolume*nMasterVolume)/(100*100):0;
vol[ 7] = bMute?(VRC6_VOL*nVolume*nMasterVolume)/(100*100):0;
// VRC7
vol[ 8] = bMute?(VRC7_VOL*nVolume*nMasterVolume)/(100*100):0;
// FDS
vol[ 9] = bMute?(FDS_VOL*nVolume*nMasterVolume)/(100*100):0;
// MMC5
vol = bMute?(MMC5_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute?(MMC5_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute?(MMC5_VOL*nVolume*nMasterVolume)/(100*100):0;
// N106
vol = bMute[ 6]?(N106_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute[ 7]?(N106_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute[ 8]?(N106_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute[ 9]?(N106_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute?(N106_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute?(N106_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute?(N106_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute?(N106_VOL*nVolume*nMasterVolume)/(100*100):0;
// FME7
vol = bMute?(FME7_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute?(FME7_VOL*nVolume*nMasterVolume)/(100*100):0;
vol = bMute?(FME7_VOL*nVolume*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僒僀僋儖悢偑儖乕僾偟偰偟傑偭偨帪偺懳嶔張棟
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;
output += internal.Process( 1 )*vol;
output += internal.Process( 2 )*vol;
output += internal.Process( 3 )*vol;
output += internal.Process( 4 )*vol;
if( exsound_select & 0x01 ) {
output += vrc6.Process( 0 )*vol;
output += vrc6.Process( 1 )*vol;
output += vrc6.Process( 2 )*vol;
}
if( exsound_select & 0x02 ) {
output += vrc7.Process( 0 )*vol;
}
if( exsound_select & 0x04 ) {
output += fds.Process( 0 )*vol;
}
if( exsound_select & 0x08 ) {
output += mmc5.Process( 0 )*vol;
output += mmc5.Process( 1 )*vol;
output += mmc5.Process( 2 )*vol;
}
if( exsound_select & 0x10 ) {
output += n106.Process( 0 )*vol;
output += n106.Process( 1 )*vol;
output += n106.Process( 2 )*vol;
output += n106.Process( 3 )*vol;
output += n106.Process( 4 )*vol;
output += n106.Process( 5 )*vol;
output += n106.Process( 6 )*vol;
output += n106.Process( 7 )*vol;
}
if( exsound_select & 0x20 ) {
fme7.Process( 3 ); // Envelope & Noise
output += fme7.Process( 0 )*vol;
output += fme7.Process( 1 )*vol;
output += fme7.Process( 2 )*vol;
}
output >>= 8;
if( nFilterType == 1 ) {
//儘乕僷僗僼傿儖僞乕TYPE 1(Simple)
output = (lowpass_filter+output)/2;
lowpass_filter = output;
} else if( nFilterType == 2 ) {
//儘乕僷僗僼傿儖僞乕TYPE 2(Weighted type 1)
output = (lowpass_filter+lowpass_filter+output)/3;
lowpass_filter = lowpass_filter;
lowpass_filter = output;
} else if( nFilterType == 3 ) {
//儘乕僷僗僼傿儖僞乕TYPE 3(Weighted type 2)
output = (lowpass_filter+lowpass_filter+lowpass_filter+output)/4;
lowpass_filter = lowpass_filter;
lowpass_filter = lowpass_filter;
lowpass_filter = output;
} else if( nFilterType == 4 ) {
//儘乕僷僗僼傿儖僞乕TYPE 4(Weighted type 3)
output = (lowpass_filter+lowpass_filter*2+output)/4;
lowpass_filter = lowpass_filter;
lowpass_filter = output;
}
#if 0
// DC惉暘偺僇僢僩
{
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惉暘偺僇僢僩(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
// 僗僷僀僋僲僀僘偺彍嫀(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 = (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
}
// 僠儍儞僱儖偺廃攇悢庢摼僒僽儖乕僠儞(NSF梡)
INT APU::GetChannelFrequency( INT no )
{
if( !m_bMute )
return 0;
// Internal
if( no < 5 ) {
return m_bMute?internal.GetFreq( no ):0;
}
// VRC6
if( (exsound_select & 0x01) && no >= 0x0100 && no < 0x0103 ) {
return m_bMute?vrc6.GetFreq( no & 0x03 ):0;
}
// FDS
if( (exsound_select & 0x04) && no == 0x300 ) {
return m_bMute?fds.GetFreq( 0 ):0;
}
// MMC5
if( (exsound_select & 0x08) && no >= 0x0400 && no < 0x0402 ) {
return m_bMute?mmc5.GetFreq( no & 0x03 ):0;
}
// N106
if( (exsound_select & 0x10) && no >= 0x0500 && no < 0x0508 ) {
return m_bMute?n106.GetFreq( no & 0x07 ):0;
}
// FME7
if( (exsound_select & 0x20) && no >= 0x0600 && no < 0x0603 ) {
return m_bMute?fme7.GetFreq( no & 0x03 ):0;
}
// VRC7
if( (exsound_select & 0x02) && no >= 0x0700 && no < 0x0709 ) {
return m_bMute?vrc7.GetFreq(no&0x0F):0;
}
return 0;
}
// State Save/Load
void APU::SaveState( LPBYTE p )
{
#ifdef _DEBUG
LPBYTE pold = p;
#endif
// 帪娫幉傪摨婜偝偣傞堊Flush偡傞
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 )
{
// 帪娫幉傪摨婜偝偣傞堊偵徚偡
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
}
} 原帖由 独孤残云 于 2009-11-8 14:38 发表 http://bbs.emu618.com/forum/images/common/back.gif
可以的话,希望可以得到krizal团长大人或者哪位大侠的详细赐教,就是指Apu模块下这些函数的具体含义和作用。我对这些NES机制的算法很感兴趣。
感激不尽~~
恩 我對模擬器不是很有研究,
雖然要了解源碼內容,可能不是很困難,
不過還是要花時間,個人目前蠻忙碌的。
給你一個朋友的MSN,你可以跟他討論看看,
他本身是程式設計師,也對FC模擬器很有興趣。
MSN我就PM到你的信箱了。
希望你能有所得。 呵…… 谢过团长大人~~ 团长的朋友都是神,那团长就是神的boss。 原帖由 独孤残云 于 2009-11-9 13:23 发表 http://bbs.emu618.com/forum/images/common/back.gif
呵…… 谢过团长大人~~
哈 不客氣,算是順便幫他找個伴,大家可以一起玩。 原帖由 李伟 于 2009-11-9 16:02 发表 http://bbs.emu618.com/forum/images/common/back.gif
团长的朋友都是神,那团长就是神的boss。
哈 不敢當,我只是個平凡人,
要吃飯喝水,光吸空氣是不會飽的。。。。 :) FC模拟器的部分有个人可以帮你忙
ZYH
QQ:414734306
Mail:zyh-01@126.com
他是ZYH Emulator这个模拟器的作者,只是他用的开发平台是VB,不过就6502的实现原理来说是一样的 再次对团长大人和悠悠哥的无私帮助表示感谢~~ 原帖由 独孤残云 于 2009-11-27 09:48 发表 http://bbs.emu618.com/forum/images/common/back.gif
再次对团长大人和悠悠哥的无私帮助表示感谢~~
不客氣^_^
页:
[1]