📄 apu_internal.cpp
字号:
if( ch.len_count && !ch.holdnote ) {
// Holdnote
if( ch.len_count ) {
ch.len_count--;
}
}
// Update Sweep
if( ch.swp_on && ch.swp_shift ) {
if( ch.swp_count ) {
ch.swp_count--;
}
if( ch.swp_count == 0 ) {
ch.swp_count = ch.swp_decay;
if( ch.swp_inc ) {
// Sweep increment(to higher frequency)
if( !ch.complement )
ch.freq += ~(ch.freq >> ch.swp_shift); // CH 0
else
ch.freq -= (ch.freq >> ch.swp_shift); // CH 1
} else {
// Sweep decrement(to lower frequency)
ch.freq += (ch.freq >> ch.swp_shift);
}
}
}
}
// Update Envelope
if( ch.env_count ) {
ch.env_count--;
}
if( ch.env_count == 0 ) {
ch.env_count = ch.env_decay;
// Holdnote
if( ch.holdnote ) {
ch.env_vol = (ch.env_vol-1)&0x0F;
} else if( ch.env_vol ) {
ch.env_vol--;
}
}
if( !ch.env_fixed ) {
ch.nowvolume = ch.env_vol<<RECTANGLE_VOL_SHIFT;
}
}
// Sync Write Rectangle
void APU_INTERNAL::SyncWriteRectangle( INT no, WORD addr, BYTE data )
{
RECTANGLE& ch = (no==0)?ch0:ch1;
ch.sync_reg[addr&3] = data;
switch( addr&3 ) {
case 0:
ch.sync_holdnote = data&0x20;
break;
case 1:
case 2:
break;
case 3: // Master
ch.sync_len_count = vbl_length[data>>3]*2;
if( sync_reg4015&(1<<no) )
ch.sync_enable = 0xFF;
break;
}
}
// Sync Update Rectangle
void APU_INTERNAL::SyncUpdateRectangle( RECTANGLE& ch, INT type )
{
if( !ch.sync_enable || ch.sync_len_count <= 0 )
return;
// Update Length
if( ch.sync_len_count && !ch.sync_holdnote ) {
if( (type&1) && ch.sync_len_count ) {
ch.sync_len_count--;
}
}
}
// Render Rectangle
INT APU_INTERNAL::RenderRectangle( RECTANGLE& ch )
{
if( !ch.enable || ch.len_count <= 0 )
return 0;
// Channel disable?
if( (ch.freq < 8) || (!ch.swp_inc && ch.freq > ch.freqlimit) ) {
return 0;
}
if( ch.env_fixed ) {
ch.nowvolume = ch.volume<<RECTANGLE_VOL_SHIFT;
}
INT volume = ch.nowvolume;
if( !(Config.sound.bChangeTone && ChannelTone[(!ch.complement)?0:1][ch.reg[0]>>6]) ) {
// 曗娫張棟
double total;
double sample_weight = ch.phaseacc;
if( sample_weight > cycle_rate ) {
sample_weight = cycle_rate;
}
total = (ch.adder < ch.duty)?sample_weight:-sample_weight;
INT freq = INT2FIX( ch.freq+1 );
ch.phaseacc -= cycle_rate;
while( ch.phaseacc < 0 ) {
ch.phaseacc += freq;
ch.adder = (ch.adder+1)&0x0F;
sample_weight = freq;
if( ch.phaseacc > 0 ) {
sample_weight -= ch.phaseacc;
}
total += (ch.adder < ch.duty)?sample_weight:-sample_weight;
}
return (INT)floor( volume*total/cycle_rate + 0.5 );
} else {
INT* pTone = ToneTable[ChannelTone[(!ch.complement)?0:1][ch.reg[0]>>6]-1];
// 峏怴柍偟
ch.phaseacc -= cycle_rate*2;
if( ch.phaseacc >= 0 ) {
return pTone[ch.adder&0x1F]*volume/((1<<RECTANGLE_VOL_SHIFT)/2);
}
// 1僗僥僢僾偩偗峏怴
INT freq = INT2FIX( ch.freq+1 );
if( freq > cycle_rate*2 ) {
ch.phaseacc += freq;
ch.adder = (ch.adder+1)&0x1F;
return pTone[ch.adder&0x1F]*volume/((1<<RECTANGLE_VOL_SHIFT)/2);
}
// 壛廳暯嬒
INT num_times, total;
num_times = total = 0;
while( ch.phaseacc < 0 ) {
ch.phaseacc += freq;
ch.adder = (ch.adder+1)&0x1F;
total += pTone[ch.adder&0x1F]*volume/((1<<RECTANGLE_VOL_SHIFT)/2);
num_times++;
}
return total/num_times;
}
}
/////////////
// Write Triangle
void APU_INTERNAL::WriteTriangle( WORD addr, BYTE data )
{
ch2.reg[addr&3] = data;
switch( addr&3 ) {
case 0:
if( ch2.counter_start & 0x80 ) {
ch2.lin_count = data&0x7F;
ch2.holdnote = data&0x80;
}
if( !(data&0x7F) ) {
ch2.lin_count = 0;
}
ch2.counter_start = data&0x80;
break;
case 1: // Unused
break;
case 2:
ch2.freq = INT2FIX( ((((INT)ch2.reg[3]&0x07)<<8)+(INT)data+1) );
break;
case 3: // Master
ch2.freq = INT2FIX( ((((INT)data&0x07)<<8)+(INT)ch2.reg[2]+1) );
ch2.len_count = vbl_length[data>>3]*2;
ch2.lin_count = ch2.reg[0]&0x7F;
ch2.holdnote = ch2.reg[0]&0x80;
ch2.counter_start = 0x80;
if( reg4015&(1<<2) )
ch2.enable = 0xFF;
break;
}
}
// Update Triangle
void APU_INTERNAL::UpdateTriangle( INT type )
{
if( !ch2.enable )
return;
// Update Length/Linear
if( !ch2.holdnote ) {
if( (type&1) && ch2.len_count ) {
ch2.len_count--;
}
ch2.counter_start = 0;
if( ch2.lin_count ) {
ch2.lin_count--;
}
if( (ch2.len_count <= 0) || (ch2.lin_count <= 0) ) {
ch2.lin_count = 0;
}
}
}
// Sync Write Triangle
void APU_INTERNAL::SyncWriteTriangle( WORD addr, BYTE data )
{
ch2.sync_reg[addr&3] = data;
switch( addr&3 ) {
case 0:
if( ch2.sync_counter_start & 0x80 ) {
ch2.sync_lin_count = data&0x7F;
ch2.sync_holdnote = data&0x80;
}
if( !(data&0x7F) ) {
ch2.sync_lin_count = 0;
}
ch2.sync_counter_start = data&0x80;
break;
case 1:
break;
case 2:
break;
case 3: // Master
ch2.sync_len_count = vbl_length[ch2.sync_reg[3]>>3]*2;
ch2.sync_lin_count = ch2.sync_reg[0]&0x7F;
ch2.sync_holdnote = ch2.sync_reg[0]&0x80;
ch2.sync_counter_start = 0x80;
if( sync_reg4015&(1<<2) )
ch2.sync_enable = 0xFF;
break;
}
}
// Sync Update Triangle
void APU_INTERNAL::SyncUpdateTriangle( INT type )
{
if( !ch2.sync_enable )
return;
// Update Length/Linear
if( !ch2.sync_holdnote ) {
if( (type&1) && ch2.sync_len_count ) {
ch2.sync_len_count--;
}
ch2.sync_counter_start = 0;
if( ch2.sync_lin_count ) {
ch2.sync_lin_count--;
}
}
}
// Render Triangle
INT APU_INTERNAL::RenderTriangle()
{
INT vol;
if( Config.sound.bDisableVolumeEffect ) {
vol = 256;
} else {
vol = 256-(INT)((ch4.reg[1]&0x01)+ch4.dpcm_value*2);
}
if( !ch2.enable || (ch2.len_count <= 0) || (ch2.lin_count <= 0) ) {
return ch2.nowvolume*vol/256;
}
if( ch2.freq < INT2FIX(8) ) {
return ch2.nowvolume*vol/256;
}
// if( !ch2.holdnote ) {
// ch2.counter_start = 0xFF;
// }
if( !(Config.sound.bChangeTone && ChannelTone[2][0]) ) {
ch2.phaseacc -= cycle_rate;
if( ch2.phaseacc >= 0 ) {
return ch2.nowvolume*vol/256;
}
if( ch2.freq > cycle_rate ) {
ch2.phaseacc += ch2.freq;
ch2.adder = (ch2.adder+1)&0x1F;
if( ch2.adder < 0x10 ) {
ch2.nowvolume = (ch2.adder&0x0F)<<TRIANGLE_VOL_SHIFT;
} else {
ch2.nowvolume = (0x0F-(ch2.adder&0x0F))<<TRIANGLE_VOL_SHIFT;
}
return ch2.nowvolume*vol/256;
}
// 壛廳暯嬒
INT num_times, total;
num_times = total = 0;
while( ch2.phaseacc < 0 ) {
ch2.phaseacc += ch2.freq;
ch2.adder = (ch2.adder+1)&0x1F;
if( ch2.adder < 0x10 ) {
ch2.nowvolume = (ch2.adder&0x0F)<<TRIANGLE_VOL_SHIFT;
} else {
ch2.nowvolume = (0x0F-(ch2.adder&0x0F))<<TRIANGLE_VOL_SHIFT;
}
total += ch2.nowvolume;
num_times++;
}
return (total/num_times)*vol/256;
} else {
INT* pTone = ToneTable[ChannelTone[2][0]-1];
ch2.phaseacc -= cycle_rate;
if( ch2.phaseacc >= 0 ) {
return ch2.nowvolume*vol/256;
}
if( ch2.freq > cycle_rate ) {
ch2.phaseacc += ch2.freq;
ch2.adder = (ch2.adder+1)&0x1F;
ch2.nowvolume = pTone[ch2.adder&0x1F]*0x0F;
return ch2.nowvolume*vol/256;
}
// 壛廳暯嬒
INT num_times, total;
num_times = total = 0;
while( ch2.phaseacc < 0 ) {
ch2.phaseacc += ch2.freq;
ch2.adder = (ch2.adder+1)&0x1F;
total += pTone[ch2.adder&0x1F]*0x0F;
num_times++;
}
return (total/num_times)*vol/256;
}
}
//////////////
// Write Noise
void APU_INTERNAL::WriteNoise( WORD addr, BYTE data )
{
ch3.reg[addr&3] = data;
switch( addr&3 ) {
case 0:
ch3.holdnote = data&0x20;
ch3.volume = data&0x0F;
ch3.env_fixed = data&0x10;
ch3.env_decay = (data&0x0F)+1;
break;
case 1: // Unused
break;
case 2:
ch3.freq = INT2FIX(noise_freq[data&0x0F]);
ch3.xor_tap = (data&0x80)?0x40:0x02;
break;
case 3: // Master
ch3.len_count = vbl_length[data>>3]*2;
ch3.env_vol = 0x0F;
ch3.env_count = ch3.env_decay+1;
if( reg4015&(1<<3) )
ch3.enable = 0xFF;
break;
}
}
// Update Noise
void APU_INTERNAL::UpdateNoise( INT type )
{
if( !ch3.enable || ch3.len_count <= 0 )
return;
// Update Length
if( !ch3.holdnote ) {
// Holdnote
if( (type&1) && ch3.len_count ) {
ch3.len_count--;
}
}
// Update Envelope
if( ch3.env_count ) {
ch3.env_count--;
}
if( ch3.env_count == 0 ) {
ch3.env_count = ch3.env_decay;
// Holdnote
if( ch3.holdnote ) {
ch3.env_vol = (ch3.env_vol-1)&0x0F;
} else if( ch3.env_vol ) {
ch3.env_vol--;
}
}
if( !ch3.env_fixed ) {
ch3.nowvolume = ch3.env_vol<<RECTANGLE_VOL_SHIFT;
}
}
// Sync Write Noise
void APU_INTERNAL::SyncWriteNoise( WORD addr, BYTE data )
{
ch3.sync_reg[addr&3] = data;
switch( addr&3 ) {
case 0:
ch3.sync_holdnote = data&0x20;
break;
case 1:
break;
case 2:
break;
case 3: // Master
ch3.sync_len_count = vbl_length[data>>3]*2;
if( sync_reg4015&(1<<3) )
ch3.sync_enable = 0xFF;
break;
}
}
// Sync Update Noise
void APU_INTERNAL::SyncUpdateNoise( INT type )
{
if( !ch3.sync_enable || ch3.sync_len_count <= 0 )
return;
// Update Length
if( ch3.sync_len_count && !ch3.sync_holdnote ) {
if( (type&1) && ch3.sync_len_count ) {
ch3.sync_len_count--;
}
}
}
// Noise ShiftRegister
BYTE APU_INTERNAL::NoiseShiftreg( BYTE xor_tap )
{
int bit0, bit14;
bit0 = ch3.shift_reg & 1;
if( ch3.shift_reg & xor_tap ) bit14 = bit0^1;
else bit14 = bit0^0;
ch3.shift_reg >>= 1;
ch3.shift_reg |= (bit14<<14);
return (bit0^1);
}
// Render Noise
INT APU_INTERNAL::RenderNoise()
{
if( !ch3.enable || ch3.len_count <= 0 )
return 0;
if( ch3.env_fixed ) {
ch3.nowvolume = ch3.volume<<RECTANGLE_VOL_SHIFT;
}
INT vol = 256-(INT)((ch4.reg[1]&0x01)+ch4.dpcm_value*2);
ch3.phaseacc -= cycle_rate;
if( ch3.phaseacc >= 0 )
return ch3.output*vol/256;
if( ch3.freq > cycle_rate ) {
ch3.phaseacc += ch3.freq;
if( NoiseShiftreg(ch3.xor_tap) )
ch3.output = ch3.nowvolume;
else
ch3.output = -ch3.nowvolume;
return ch3.output*vol/256;
}
INT num_times, total;
num_times = total = 0;
while( ch3.phaseacc < 0 ) {
ch3.phaseacc += ch3.freq;
if( NoiseShiftreg(ch3.xor_tap) )
ch3.output = ch3.nowvolume;
else
ch3.output = -ch3.nowvolume;
total += ch3.output;
num_times++;
}
return (total/num_times)*vol/256;
}
//////////
void APU_INTERNAL::WriteDPCM( WORD addr, BYTE data )
{
ch4.reg[addr&3] = data;
switch( addr&3 ) {
case 0:
ch4.freq = INT2FIX( dpcm_cycles[data&0x0F] );
ch4.looping = data&0x40;
break;
case 1:
ch4.dpcm_value = (data&0x7F)>>1;
break;
case 2:
ch4.cache_addr = 0xC000+(WORD)(data<<6);
break;
case 3:
ch4.cache_dmalength = ((data<<4)+1)<<3;
break;
}
}
INT APU_INTERNAL::RenderDPCM()
{
if( ch4.dmalength ) {
ch4.phaseacc -= cycle_rate;
while( ch4.phaseacc < 0 ) {
ch4.phaseacc += ch4.freq;
if( !(ch4.dmalength&7) ) {
ch4.cur_byte = nes->Read( ch4.address );
if( 0xFFFF == ch4.address )
ch4.address = 0x8000;
else
ch4.address++;
}
if( !(--ch4.dmalength) ) {
if( ch4.looping ) {
ch4.address = ch4.cache_addr;
ch4.dmalength = ch4.cache_dmalength;
} else {
ch4.enable = 0;
break;
}
}
// positive delta
if( ch4.cur_byte&(1<<((ch4.dmalength&7)^7)) ) {
if( ch4.dpcm_value < 0x3F )
ch4.dpcm_value += 1;
} else {
// negative delta
if( ch4.dpcm_value > 1 )
ch4.dpcm_value -= 1;
}
}
}
#if 1
// 僀儞僠僉廘偄僾僠僲僀僘僇僢僩(TEST)
ch4.dpcm_output_real = (INT)((ch4.reg[1]&0x01)+ch4.dpcm_value*2)-0x40;
if( abs(ch4.dpcm_output_real-ch4.dpcm_output_fake) <= 8 ) {
ch4.dpcm_output_fake = ch4.dpcm_output_real;
ch4.output = (INT)ch4.dpcm_output_real<<DPCM_VOL_SHIFT;
} else {
if( ch4.dpcm_output_real > ch4.dpcm_output_fake )
ch4.dpcm_output_fake += 8;
else
ch4.dpcm_output_fake -= 8;
ch4.output = (INT)ch4.dpcm_output_fake<<DPCM_VOL_SHIFT;
}
#else
ch4.output = (((INT)ch4.reg[1]&0x01)+(INT)ch4.dpcm_value*2)<<DPCM_VOL_SHIFT;
// ch4.output = ((((INT)ch4.reg[1]&0x01)+(INT)ch4.dpcm_value*2)-0x40)<<DPCM_VOL_SHIFT;
#endif
return ch4.output;
}
void APU_INTERNAL::SyncWriteDPCM( WORD addr, BYTE data )
{
ch4.reg[addr&3] = data;
switch( addr&3 ) {
case 0:
ch4.sync_cache_cycles = dpcm_cycles[data&0x0F];
ch4.sync_cycles = 0;
ch4.sync_looping = data&0x40;
ch4.sync_irq_gen = data&0x80;
if( !ch4.sync_irq_gen ) {
ch4.sync_irq_enable = 0;
nes->cpu->ClrIRQ( IRQ_DPCM );
}
break;
case 1:
break;
case 2:
break;
case 3:
ch4.sync_cache_dmalength = ((data<<4)+1)<<3;
break;
}
}
BOOL APU_INTERNAL::SyncUpdateDPCM( INT cycles )
{
BOOL bIRQ = FALSE;
if( ch4.sync_enable ) {
ch4.sync_cycles -= cycles;
while( ch4.sync_cycles < 0 ) {
ch4.sync_cycles += ch4.sync_cache_cycles;
if( ch4.sync_dmalength ) {
if( !(--ch4.sync_dmalength) ) {
if( ch4.sync_looping ) {
ch4.sync_dmalength = ch4.sync_cache_dmalength;
} else if( ch4.sync_irq_gen ) {
ch4.sync_irq_enable = 0xFF;
nes->cpu->SetIRQ( IRQ_DPCM );
}
}
}
}
}
if( ch4.sync_irq_enable ) {
bIRQ = TRUE;
}
return bIRQ;
}
INT APU_INTERNAL::GetStateSize()
{
return 4*sizeof(BYTE) + 3*sizeof(INT)
+ sizeof(ch0) + sizeof(ch1)
+ sizeof(ch2) + sizeof(ch3)
+ sizeof(ch4);
}
void APU_INTERNAL::SaveState( LPBYTE p )
{
SETBYTE( p, reg4015 );
SETBYTE( p, sync_reg4015 );
SETINT( p, FrameCycle );
SETINT( p, FrameCount );
SETINT( p, FrameType );
SETBYTE( p, FrameIRQ );
SETBYTE( p, FrameIRQoccur );
SETBLOCK( p, &ch0, sizeof(ch0) );
SETBLOCK( p, &ch1, sizeof(ch1) );
SETBLOCK( p, &ch2, sizeof(ch2) );
SETBLOCK( p, &ch3, sizeof(ch3) );
SETBLOCK( p, &ch4, sizeof(ch4) );
}
void APU_INTERNAL::LoadState( LPBYTE p )
{
GETBYTE( p, reg4015 );
GETBYTE( p, sync_reg4015 );
GETINT( p, FrameCycle );
GETINT( p, FrameCount );
GETINT( p, FrameType );
GETBYTE( p, FrameIRQ );
GETBYTE( p, FrameIRQoccur );
GETBLOCK( p, &ch0, sizeof(ch0) );
GETBLOCK( p, &ch1, sizeof(ch1) );
GETBLOCK( p, &ch2, sizeof(ch2) );
GETBLOCK( p, &ch3, sizeof(ch3) );
// p += sizeof(ch3);
GETBLOCK( p, &ch4, sizeof(ch4) );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -