AxibugEmuOnline/References/virtuanessrc097-master/NES/ApuEX/APU_FME7.cpp
2024-08-05 17:58:53 +08:00

330 lines
17 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//////////////////////////////////////////////////////////////////////////
// //
// SunSoft FME7 //
// Norix //
// written 2001/09/18 //
// last modify ----/--/-- //
//////////////////////////////////////////////////////////////////////////
#include "APU_FME7.h"
#define CHANNEL_VOL_SHIFT 8
// Envelope tables
BYTE APU_FME7::envelope_pulse0[] = {
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
};
BYTE APU_FME7::envelope_pulse1[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x00
};
BYTE APU_FME7::envelope_pulse2[] = {
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x1F
};
BYTE APU_FME7::envelope_pulse3[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x1F
};
SBYTE APU_FME7::envstep_pulse[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0
};
BYTE APU_FME7::envelope_sawtooth0[] = {
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
};
BYTE APU_FME7::envelope_sawtooth1[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
SBYTE APU_FME7::envstep_sawtooth[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, -15
};
BYTE APU_FME7::envelope_triangle0[] = {
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};
BYTE APU_FME7::envelope_triangle1[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
};
SBYTE APU_FME7::envstep_triangle[] = {
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, -31
};
LPBYTE APU_FME7::envelope_table[16] = {
envelope_pulse0, envelope_pulse0, envelope_pulse0, envelope_pulse0,
envelope_pulse1, envelope_pulse1, envelope_pulse1, envelope_pulse1,
envelope_sawtooth0, envelope_pulse0, envelope_triangle0, envelope_pulse2,
envelope_sawtooth1, envelope_pulse3, envelope_triangle1, envelope_pulse1
};
LPSBYTE APU_FME7::envstep_table[16] = {
envstep_pulse, envstep_pulse, envstep_pulse, envstep_pulse,
envstep_pulse, envstep_pulse, envstep_pulse, envstep_pulse,
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse,
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse
};
APU_FME7::APU_FME7()
{
// 仮設定
Reset( APU_CLOCK, 22050 );
}
APU_FME7::~APU_FME7()
{
}
void APU_FME7::Reset( FLOAT fClock, INT nRate )
{
INT i;
::ZeroMemory( &envelope, sizeof(envelope) );
::ZeroMemory( &noise, sizeof(noise) );
for( i = 0; i < 3; i++ ) {
::ZeroMemory( &op[i], sizeof(op[i]) );
}
envelope.envtbl = envelope_table[0];
envelope.envstep = envstep_table[0];
noise.noiserange = 1;
noise.noiseout = 0xFF;
address = 0;
// Volume to voltage
double out = 0x1FFF;
for( i = 31; i > 1; i-- ) {
vol_table[i] = (INT)(out+0.5);
out /= 1.188502227; /* = 10 ^ (1.5/20) = 1.5dB */
}
vol_table[1] = 0;
vol_table[0] = 0;
Setup( fClock, nRate );
}
void APU_FME7::Setup( FLOAT fClock, INT nRate )
{
cpu_clock = fClock;
cycle_rate = (INT)((fClock/16.0f)*(1<<16)/nRate);
}
void APU_FME7::Write( WORD addr, BYTE data )
{
if( addr == 0xC000 ) {
address = data;
} else if( addr == 0xE000 ) {
BYTE chaddr = address;
switch( chaddr ) {
case 0x00: case 0x01:
case 0x02: case 0x03:
case 0x04: case 0x05:
{
CHANNEL& ch = op[chaddr>>1];
ch.reg[chaddr&0x01] = data;
ch.freq = INT2FIX(((INT)(ch.reg[1]&0x0F)<<8)+ch.reg[0]+1);
}
break;
case 0x06:
noise.freq = INT2FIX((INT)(data&0x1F)+1);
break;
case 0x07:
{
for( INT i = 0; i < 3; i++ ) {
op[i].enable = data&(1<<i);
op[i].noise_on = data&(8<<i);
}
}
break;
case 0x08:
case 0x09:
case 0x0A:
{
CHANNEL& ch = op[chaddr&3];
ch.reg[2] = data;
ch.env_on = data & 0x10;
ch.volume = (data&0x0F)*2;
}
break;
case 0x0B:
case 0x0C:
envelope.reg[chaddr-0x0B] = data;
envelope.freq = INT2FIX(((INT)(envelope.reg[1]&0x0F)<<8)+envelope.reg[0]+1);
break;
case 0x0D:
envelope.envtbl = envelope_table[data&0x0F];
envelope.envstep = envstep_table [data&0x0F];
envelope.envadr = 0;
break;
}
}
}
INT APU_FME7::Process( INT channel )
{
if( channel < 3 ) {
return ChannelRender( op[channel] );
} else if( channel == 3 ) {
// 必ずch3を1回呼んでからch0-2を呼ぶ事
EnvelopeRender();
NoiseRender();
}
return 0;
}
INT APU_FME7::GetFreq( INT channel )
{
if( channel < 3 ) {
CHANNEL* ch = &op[channel];
if( ch->enable || !ch->freq )
return 0;
if( ch->env_on ) {
if( !envelope.volume )
return 0;
} else {
if( !ch->volume )
return 0;
}
return (INT)(256.0f*cpu_clock/((FLOAT)FIX2INT(ch->freq)*16.0f));
}
return 0;
}
void APU_FME7::EnvelopeRender()
{
if( !envelope.freq )
return;
envelope.phaseacc -= cycle_rate;
if( envelope.phaseacc >= 0 )
return;
while( envelope.phaseacc < 0 ) {
envelope.phaseacc += envelope.freq;
envelope.envadr += envelope.envstep[envelope.envadr];
}
envelope.volume = envelope.envtbl[envelope.envadr];
}
void APU_FME7::NoiseRender()
{
if( !noise.freq )
return;
noise.phaseacc -= cycle_rate;
if( noise.phaseacc >= 0 )
return;
while( noise.phaseacc < 0 ) {
noise.phaseacc += noise.freq;
if( (noise.noiserange+1) & 0x02 )
noise.noiseout = ~noise.noiseout;
if( noise.noiserange & 0x01 )
noise.noiserange ^= 0x28000;
noise.noiserange >>= 1;
}
}
INT APU_FME7::ChannelRender( CHANNEL& ch )
{
INT output, volume;
if( ch.enable )
return 0;
if( !ch.freq )
return 0;
ch.phaseacc -= cycle_rate;
while( ch.phaseacc < 0 ) {
ch.phaseacc += ch.freq;
ch.adder++;
}
output = volume = 0;
volume = ch.env_on?vol_table[envelope.volume]:vol_table[ch.volume+1];
if( ch.adder & 0x01 ) {
output += volume;
} else {
output -= volume;
}
if( !ch.noise_on ) {
if( noise.noiseout )
output += volume;
else
output -= volume;
}
ch.output_vol = output;
return ch.output_vol;
}
INT APU_FME7::GetStateSize()
{
return sizeof(BYTE) + sizeof(envelope) + sizeof(noise) + 3*sizeof(op);
}
void APU_FME7::SaveState( LPBYTE p )
{
SETBYTE( p, address );
SETBLOCK( p, &envelope, sizeof(envelope) );
SETBLOCK( p, &noise, sizeof(noise) );
SETBLOCK( p, op, 3*sizeof(op) );
}
void APU_FME7::LoadState( LPBYTE p )
{
GETBYTE( p, address );
GETBLOCK( p, &envelope, sizeof(envelope) );
GETBLOCK( p, &noise, sizeof(noise) );
GETBLOCK( p, op, 3*sizeof(op) );
}