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

📄 apu_internal.cpp

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