diff --git a/src/spk_ay.c b/src/spk_ay.c index 509408d..2e8aa8e 100644 --- a/src/spk_ay.c +++ b/src/spk_ay.c @@ -16,6 +16,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * + * 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" @@ -25,21 +27,53 @@ /* emulates the AY-3-8912 during TSTADOS tstates */ 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) return; ordenador.tst_ay += tstados; ordenador.tst_ay2 += tstados; - - if (ordenador.tst_ay2 > 255) { - ordenador.tst_ay2 -= 256; - if ((ordenador.ay_registers[11]) - || (ordenador.ay_registers[12])) { - if (ordenador.aych_envel) - ordenador.aych_envel--; + + // A note about the period of tones, noise and envelope: careful studies of the chip + // output prove that it counts up from 0 until the counter becomes + // greater or equal to the period. This is an important when the + // program is rapidly changing the period to modulate the sound. + // Also, note that period = 0 is the same as period = 1. This is mentioned + // 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= 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; - - if ((ordenador.ay_registers[0]) - || (ordenador.ay_registers[1])) - { - if (ordenador.aych_a) - 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; - } - } + + tone_period_a= ((unsigned int) ordenador.ay_registers[0]) + 256 * ((unsigned int) ((ordenador.ay_registers[1]) & 0x0F)); + 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)); + noise_period= ((unsigned int) ordenador.ay_registers[6]) & 0x1F; + + + if (!tone_period_a) tone_period_a = 1; + if (!tone_period_b) tone_period_b = 1; + if (!tone_period_c) tone_period_c = 1; + if (!noise_period) noise_period = 1; + + if (ordenador.aych_a 16) - ordenador.aych_n = 16; - } + ordenador.ayval_c = !ordenador.ayval_c; + ordenador.aych_c =0; } + + + if (ordenador.aych_n>= 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) ordenador.vol_a = - (unsigned - char) ((((unsigned int) ordenador. - ay_envel_value)) * - (unsigned int) ordenador.volume) / 10; + (unsigned char) (levels[ordenador.ay_envel_value]*(unsigned int) ordenador.volume/80); else ordenador.vol_a = - (unsigned - char) ((((unsigned int) (ordenador. - ay_registers[8] & - 0x0F)) * - (unsigned int) ordenador.volume) / - 10); + (unsigned char) (levels[ordenador.ay_registers[8] &0x0F]*(unsigned int) ordenador.volume/80); if (ordenador.ay_registers[10] & 0x10) ordenador.vol_c = - (unsigned - char) ((((unsigned int) ordenador. - ay_envel_value)) * - (unsigned int) ordenador.volume) / 10; + (unsigned char) (levels[ordenador.ay_envel_value] *(unsigned int) ordenador.volume/80); else ordenador.vol_c = - (unsigned - char) ((((unsigned int) (ordenador. - ay_registers[10] & - 0x0F)) * - (unsigned int) ordenador.volume) / - 10); + (unsigned char) (levels[ordenador.ay_registers[10] & 0x0F] *(unsigned int) ordenador.volume/80); if (ordenador.ay_registers[9] & 0x10) ordenador.vol_b = - (unsigned - char) ((((unsigned int) ordenador. - ay_envel_value)) * - (unsigned int) ordenador.volume) / 10; + (unsigned char) (levels[ordenador.ay_envel_value] *(unsigned int) ordenador.volume/80); else ordenador.vol_b = - (unsigned - char) ((((unsigned int) (ordenador. - ay_registers[9] & - 0x0F)) * - (unsigned int) ordenador.volume) / - 10); + (unsigned char)(levels[ordenador.ay_registers[9] &0x0F] *(unsigned int) ordenador.volume/80); + } } @@ -291,34 +300,40 @@ inline void play_sound (unsigned int tstados) { if (sound_type!=1) //!SOUND_OSS for (bucle = 0; bucle < ordenador.increment; 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; else ordenador.sound_current_value=0; 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.ayval_a) && (sample_v) - && (!(ordenador.ay_registers[7] & 0x01))) + //ordenador.ayval_n = 1; + if (sample_v &&((ordenador.ayval_a || (ordenador.ay_registers[7] & 0x01))&&(ordenador.ayval_n || (ordenador.ay_registers[7] & 0x08)))) value += (int) ordenador.vol_a; - if ((ordenador.ayval_b) && (sample_v) - && (!(ordenador.ay_registers[7] & 0x02))) + if (sample_v &&((ordenador.ayval_b || (ordenador.ay_registers[7] & 0x02))&&(ordenador.ayval_n || (ordenador.ay_registers[7] & 0x10)))) value += (int) ordenador.vol_b; - if ((ordenador.ayval_c) && (sample_v) - && (!(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))) + if (sample_v &&((ordenador.ayval_c || (ordenador.ay_registers[7] & 0x04))&&(ordenador.ayval_n || (ordenador.ay_registers[7] & 0x20)))) value += (int) ordenador.vol_c; + } if (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++; }