New code for Y-3-8912 emulation

This commit is contained in:
fabio.olimpieri 2012-06-09 06:39:15 +00:00
parent d7a9a96840
commit d6812d2c9b

View File

@ -16,6 +16,8 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* The AY white noise RNG algorithm is based on info from MAME's ay8910.c -
* MAME's licence explicitly permits free use of info (even encourages it).
*/ */
#include "emulator.h" #include "emulator.h"
@ -26,20 +28,52 @@
inline void play_ay (unsigned int tstados) { inline void play_ay (unsigned int tstados) {
static unsigned int noise = 1; // init value assigned on AY chip reset
static unsigned int env_period, noise_period, tone_period_a,tone_period_b,tone_period_c;
/* AY output doesn't match the claimed levels; these levels are based
* on the measurements posted to comp.sys.sinclair in Dec 2001 by
* Matthew Westcott, adjusted as Philip Kendall described in a followup to his post,
* then scaled to 0..0xff. Taken from FUSE.
*/
static const unsigned int levels[16] = {
0x00, 0x03, 0x05, 0x07,
0x0A, 0x0F, 0x15, 0x23,
0x2B, 0x43, 0x5A, 0x73,
0x92, 0xAF, 0xD9, 0xFF
};
if (!ordenador.ay_emul) if (!ordenador.ay_emul)
return; return;
ordenador.tst_ay += tstados; ordenador.tst_ay += tstados;
ordenador.tst_ay2 += tstados; ordenador.tst_ay2 += tstados;
if (ordenador.tst_ay2 > 255) { // A note about the period of tones, noise and envelope: careful studies of the chip
ordenador.tst_ay2 -= 256; // output prove that it counts up from 0 until the counter becomes
if ((ordenador.ay_registers[11]) // greater or equal to the period. This is an important when the
|| (ordenador.ay_registers[12])) { // program is rapidly changing the period to modulate the sound.
if (ordenador.aych_envel) // Also, note that period = 0 is the same as period = 1. This is mentioned
ordenador.aych_envel--; // in the YM2203 data sheets. However, this does NOT apply to the Envelope
// period. In that case, period = 0 is half as period = 1.
//The frequency of AY-3-8912 is half the ZX Spectrum frequency
//Envelope
//Envelope frequency is 1/(256*envelop_period) of AY-3-8912 frequency
if (ordenador.tst_ay2 > 127) {
ordenador.tst_ay2 -= 128;
env_period=2*((unsigned int) ordenador.ay_registers[11]) + 256 * ((unsigned int) (ordenador.ay_registers[12]));
if (!env_period) env_period = 1;
if (ordenador.aych_envel<env_period) // to check
ordenador.aych_envel++;
else { else {
ordenador.aych_envel = (((unsigned int) ordenador.ay_registers[11]) + 256 * ((unsigned int) (ordenador.ay_registers[12]))); ordenador.aych_envel = 0;
if (ordenador.ay_envel_way & 0x02) // start cycle? if (ordenador.ay_envel_way & 0x02) // start cycle?
switch ((ordenador. switch ((ordenador.
ay_registers[13]) & 0x0F) ay_registers[13]) & 0x0F)
@ -59,8 +93,8 @@ inline void play_ay (unsigned int tstados) {
ordenador.ay_envel_way = 5; // cycle started and incrementing ordenador.ay_envel_way = 5; // cycle started and incrementing
ordenador.ay_envel_value = -1; ordenador.ay_envel_value = -1;
} }
if (ordenador.ay_envel_way & 0x04) if (ordenador.ay_envel_way & 0x04) // cycle started?
{ // cycle started? {
switch ((ordenador. switch ((ordenador.
ay_registers[13]) & 0x0F) ay_registers[13]) & 0x0F)
{ {
@ -99,16 +133,16 @@ inline void play_ay (unsigned int tstados) {
case 10: case 10:
case 14: case 14:
if (ordenador.ay_envel_way & 0x01) if (ordenador.ay_envel_way & 0x01) //incrementing?
ordenador.ay_envel_value++; ordenador.ay_envel_value++;
else else
ordenador.ay_envel_value--; ordenador.ay_envel_value--;
if (ordenador.ay_envel_value == 16) { if (ordenador.ay_envel_value == 16) {
ordenador.ay_envel_value = 14; ordenador.ay_envel_value = 15;
ordenador.ay_envel_way =4; ordenador.ay_envel_way =4;
} }
if (ordenador.ay_envel_value == -1) { if (ordenador.ay_envel_value == -1) {
ordenador.ay_envel_value = 1; ordenador.ay_envel_value = 0;
ordenador.ay_envel_way = 5; ordenador.ay_envel_way = 5;
} }
break; break;
@ -135,140 +169,115 @@ inline void play_ay (unsigned int tstados) {
} }
} }
} }
}
else
ordenador.ay_envel_value = 15;
} }
while (ordenador.tst_ay >= 16) //Tone and noise
//Tone frequency is 1/(16*tone_period) of AY-3-8912 frequency
//Noise frequency is 1/(16*noise_period) of AY-3-8912 frequency
while (ordenador.tst_ay > 15)
{ {
ordenador.tst_ay -= 16; ordenador.tst_ay -= 16;
if ((ordenador.ay_registers[0]) tone_period_a= ((unsigned int) ordenador.ay_registers[0]) + 256 * ((unsigned int) ((ordenador.ay_registers[1]) & 0x0F));
|| (ordenador.ay_registers[1])) tone_period_b= ((unsigned int) ordenador.ay_registers[2]) + 256 * ((unsigned int) ((ordenador.ay_registers[3]) & 0x0F));
{ tone_period_c= ((unsigned int) ordenador.ay_registers[4]) + 256 * ((unsigned int) ((ordenador.ay_registers[5]) & 0x0F));
if (ordenador.aych_a) noise_period= ((unsigned int) ordenador.ay_registers[6]) & 0x1F;
ordenador.aych_a--;
else
{
ordenador.ayval_a = 1 - ordenador.ayval_a;
ordenador.aych_a =
(((unsigned int) ordenador.
ay_registers[0]) +
256 *
((unsigned
int) ((ordenador.
ay_registers[1]) & 0x0F))) /
2;
}
}
else
ordenador.ayval_a = 0;
if ((ordenador.ay_registers[2])
|| (ordenador.ay_registers[3]))
{
if (ordenador.aych_b)
ordenador.aych_b--;
else
{
ordenador.ayval_b = 1 - ordenador.ayval_b;
ordenador.aych_b =
(((unsigned int) ordenador.
ay_registers[2]) +
256 *
((unsigned
int) ((ordenador.
ay_registers[3]) & 0x0F))) /
2;
}
}
else
ordenador.ayval_b = 0;
if ((ordenador.ay_registers[4]) if (!tone_period_a) tone_period_a = 1;
|| (ordenador.ay_registers[5])) if (!tone_period_b) tone_period_b = 1;
{ if (!tone_period_c) tone_period_c = 1;
if (ordenador.aych_c) if (!noise_period) noise_period = 1;
ordenador.aych_c--;
else
{
ordenador.ayval_c = 1 - ordenador.ayval_c;
ordenador.aych_c =
(((unsigned int) ordenador.
ay_registers[4]) +
256 *
((unsigned
int) ((ordenador.
ay_registers[5]) & 0x0F))) /
2;
}
}
else
ordenador.ayval_c = 0;
if (ordenador.ay_registers[6]) if (ordenador.aych_a<tone_period_a)
{ ordenador.aych_a++;
if (ordenador.aych_n)
ordenador.aych_n--;
else
{
ordenador.ayval_n = 1 - ordenador.ayval_n;
ordenador.aych_n =
((((unsigned int) ordenador.
ay_registers[6]) & 0x1F) +
(rand () % 3)) / 2;
if (ordenador.aych_n > 16)
ordenador.aych_n = 16;
}
}
else else
ordenador.ayval_n = 0; {
ordenador.ayval_a = !ordenador.ayval_a;
ordenador.aych_a =0;
}
if (ordenador.aych_b<tone_period_b)
ordenador.aych_b++;
else
{
ordenador.ayval_b = !ordenador.ayval_b;
ordenador.aych_b =0;
}
if (ordenador.aych_c<tone_period_c)
ordenador.aych_c++;
else
{
ordenador.ayval_c = !ordenador.ayval_c;
ordenador.aych_c =0;
}
if (ordenador.aych_n<noise_period)
ordenador.aych_n++;
else
{
//from FUSE
//rng is 17-bit shift reg, bit 0 is output.
//input is bit 0 xor bit 2.
/*
if( ( noise & 1 ) ^ ( ( noise & 2 ) ? 1 : 0 ) ) ordenador.ayval_n = !ordenador.ayval_n;
noise |= ( ( noise & 1 ) ^ ( ( noise & 4 ) ? 1 : 0 ) ) ? 0x20000 : 0;
noise >>= 1;
*/
//From MAME AY
/* The Random Number Generator of the 8910 is a 17-bit shift */
/* register. The input to the shift register is bit0 XOR bit3 */
/* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */
/* The following is a fast way to compute bit17 = bit0^bit3. */
/* Instead of doing all the logic operations, we only check */
/* bit0, relying on the fact that after three shifts of the */
/* register, what now is bit3 will become bit0, and will */
/* invert, if necessary, bit14, which previously was bit17. */
if ((noise+1)&2) ordenador.ayval_n = !ordenador.ayval_n; //xor bit 1 and 2
if( noise & 1 ) {
noise ^= 0x24000 ;
}
noise >>= 1 ;
ordenador.aych_n =0;
}
// Volume
//Each channel max 51
if (ordenador.ay_registers[8] & 0x10) if (ordenador.ay_registers[8] & 0x10)
ordenador.vol_a = ordenador.vol_a =
(unsigned (unsigned char) (levels[ordenador.ay_envel_value]*(unsigned int) ordenador.volume/80);
char) ((((unsigned int) ordenador.
ay_envel_value)) *
(unsigned int) ordenador.volume) / 10;
else else
ordenador.vol_a = ordenador.vol_a =
(unsigned (unsigned char) (levels[ordenador.ay_registers[8] &0x0F]*(unsigned int) ordenador.volume/80);
char) ((((unsigned int) (ordenador.
ay_registers[8] &
0x0F)) *
(unsigned int) ordenador.volume) /
10);
if (ordenador.ay_registers[10] & 0x10) if (ordenador.ay_registers[10] & 0x10)
ordenador.vol_c = ordenador.vol_c =
(unsigned (unsigned char) (levels[ordenador.ay_envel_value] *(unsigned int) ordenador.volume/80);
char) ((((unsigned int) ordenador.
ay_envel_value)) *
(unsigned int) ordenador.volume) / 10;
else else
ordenador.vol_c = ordenador.vol_c =
(unsigned (unsigned char) (levels[ordenador.ay_registers[10] & 0x0F] *(unsigned int) ordenador.volume/80);
char) ((((unsigned int) (ordenador.
ay_registers[10] &
0x0F)) *
(unsigned int) ordenador.volume) /
10);
if (ordenador.ay_registers[9] & 0x10) if (ordenador.ay_registers[9] & 0x10)
ordenador.vol_b = ordenador.vol_b =
(unsigned (unsigned char) (levels[ordenador.ay_envel_value] *(unsigned int) ordenador.volume/80);
char) ((((unsigned int) ordenador.
ay_envel_value)) *
(unsigned int) ordenador.volume) / 10;
else else
ordenador.vol_b = ordenador.vol_b =
(unsigned (unsigned char)(levels[ordenador.ay_registers[9] &0x0F] *(unsigned int) ordenador.volume/80);
char) ((((unsigned int) (ordenador.
ay_registers[9] &
0x0F)) *
(unsigned int) ordenador.volume) /
10);
} }
} }
@ -291,34 +300,40 @@ inline void play_sound (unsigned int tstados) {
if (sound_type!=1) //!SOUND_OSS if (sound_type!=1) //!SOUND_OSS
for (bucle = 0; bucle < ordenador.increment; bucle++) { for (bucle = 0; bucle < ordenador.increment; bucle++) {
sample_v = ordenador.sample1b[bucle]; sample_v = ordenador.sample1b[bucle];
if ((ordenador.sound_bit) && (sample_v)) if (ordenador.sound_bit && sample_v)
//Sound bit volume max 96
ordenador.sound_current_value=ordenador.volume*6; ordenador.sound_current_value=ordenador.volume*6;
else ordenador.sound_current_value=0; else ordenador.sound_current_value=0;
value = ordenador.sound_current_value; value = ordenador.sound_current_value;
//Mixer
// The 8912 has three outputs, each output is the mix of one of the three
// tone generators and of the (single) noise generator. The two are mixed
// BEFORE going into the DAC. The formula to mix each channel is:
// (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable).
// Note that this means that if both tone and noise are disabled, the output
// is 1, not 0, and can be modulated changing the volume.
// If the channels are disabled, set their output to 1, and increase the
// counter, if necessary, so they will not be inverted during this update.
// Setting the output to 1 is necessary because a disabled channel is locked
// into the ON state (see above); and it has no effect if the volume is 0.
// If the volume is 0, increase the counter, but don't touch the output.
if (ordenador.ay_emul) { // if emulation is ON, emulate it if (ordenador.ay_emul) { // if emulation is ON, emulate it
if ((ordenador.ayval_a) && (sample_v) //ordenador.ayval_n = 1;
&& (!(ordenador.ay_registers[7] & 0x01))) if (sample_v &&((ordenador.ayval_a || (ordenador.ay_registers[7] & 0x01))&&(ordenador.ayval_n || (ordenador.ay_registers[7] & 0x08))))
value += (int) ordenador.vol_a; value += (int) ordenador.vol_a;
if ((ordenador.ayval_b) && (sample_v) if (sample_v &&((ordenador.ayval_b || (ordenador.ay_registers[7] & 0x02))&&(ordenador.ayval_n || (ordenador.ay_registers[7] & 0x10))))
&& (!(ordenador.ay_registers[7] & 0x02)))
value += (int) ordenador.vol_b; value += (int) ordenador.vol_b;
if ((ordenador.ayval_c) && (sample_v) if (sample_v &&((ordenador.ayval_c || (ordenador.ay_registers[7] & 0x04))&&(ordenador.ayval_n || (ordenador.ay_registers[7] & 0x20))))
&& (!(ordenador.ay_registers[7] & 0x04)))
value += (int) ordenador.vol_c;
if ((ordenador.ayval_n) && (sample_v)
&& (!(ordenador.ay_registers[7] & 0x08)))
value += (int) ordenador.vol_a;
if ((ordenador.ayval_n) && (sample_v)
&& (!(ordenador.ay_registers[7] & 0x10)))
value += (int) ordenador.vol_b;
if ((ordenador.ayval_n) && (sample_v)
&& (!(ordenador.ay_registers[7] & 0x20)))
value += (int) ordenador.vol_c; value += (int) ordenador.vol_c;
} }
if (value > 255) if (value > 255)
value = 255; value = 255;
sample_v = (char)(value - (unsigned int)ordenador.sign); sample_v = (unsigned char)(value - (unsigned int)ordenador.sign);
*ordenador.current_buffer = sample_v; *ordenador.current_buffer = sample_v;
ordenador.current_buffer++; ordenador.current_buffer++;
} }