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++;
}