/* * Copyright (C) 2012 Fabio Olimpieri * Copyright 2003-2009 (C) Raster Software Vigo (Sergio Costas) * This file is part of FBZX Wii * * FBZX Wii is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * FBZX Wii is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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" #include "sound.h" #include /* 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; // 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 if (ordenador.tst_ay > 15) { ordenador.tst_ay -= 16; //Envelope //Envelope frequency is 1/(256*envelop_period) of AY-3-8912 frequency env_period=((unsigned int) ordenador.ay_registers[11]) + 256 * ((unsigned int) ordenador.ay_registers[12]); if (!env_period) env_period = 1; //It should be the half period if (ordenador.aych_envel>= 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; // Changes to R6 take effect only when internal counter reaches 0 noise_period= ((unsigned int) ordenador.ay_registers[6]) & 0x1F; if (!noise_period) noise_period = 1; } //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.ayval_a || (ordenador.ay_registers[7] & 0x01))&&(ordenador.ayval_n || (ordenador.ay_registers[7] & 0x08))) { if (ordenador.ay_registers[8] & 0x10) ordenador.vol_a = levels[ordenador.ay_envel_value]; else ordenador.vol_a = levels[ordenador.ay_registers[8] &0x0F]; } else ordenador.vol_a = 0; if ((ordenador.ayval_b || (ordenador.ay_registers[7] & 0x02))&&(ordenador.ayval_n || (ordenador.ay_registers[7] & 0x10))) { if (ordenador.ay_registers[9] & 0x10) ordenador.vol_b = levels[ordenador.ay_envel_value]; else ordenador.vol_b = levels[ordenador.ay_registers[9] &0x0F]; } else ordenador.vol_b = 0; if ((ordenador.ayval_c || (ordenador.ay_registers[7] & 0x04))&&(ordenador.ayval_n || (ordenador.ay_registers[7] & 0x20))) { if (ordenador.ay_registers[10] & 0x10) ordenador.vol_c = levels[ordenador.ay_envel_value]; else ordenador.vol_c = levels[ordenador.ay_registers[10] & 0x0F]; } else ordenador.vol_c = 0; } } /* Creates the sound buffer during the TSTADOS tstate that the Z80 used to execute last instruction */ inline void play_sound (unsigned int tstados) { int value, lvalue, rvalue; ordenador.tstados_counter_sound += tstados; while (ordenador.tstados_counter_sound >= ordenador.tst_sample) { ordenador.tstados_counter_sound -= ordenador.tst_sample; if (ordenador.sound_bit) value=ordenador.volume*6; //Sound bit volume max 96 else value=0; //Each channel max 51 if (ordenador.ay_emul) { switch (ordenador.audio_mode) { case 0: //Mono lvalue = value + (ordenador.vol_a + ordenador.vol_b +ordenador.vol_c)*ordenador.volume/80; rvalue = value + (ordenador.vol_a + ordenador.vol_b +ordenador.vol_c)*ordenador.volume/80; break; case 1: //ABC lvalue = value + (ordenador.vol_a*2 + ordenador.vol_b)*ordenador.volume/80; rvalue = value + (ordenador.vol_b + ordenador.vol_c*2)*ordenador.volume/80; break; case 2: //ACB lvalue = value + (ordenador.vol_a*2 + ordenador.vol_c)*ordenador.volume/80; rvalue = value + (ordenador.vol_c + ordenador.vol_b*2)*ordenador.volume/80; break; case 3: //BAC lvalue = value + (ordenador.vol_b*2 + ordenador.vol_a)*ordenador.volume/80; rvalue = value + (ordenador.vol_a + ordenador.vol_c*2)*ordenador.volume/80; break; default: //No emulation rvalue = value; lvalue = value; break; } } else { rvalue = value; lvalue = value; } /* if (rvalue > 255) rvalue = 255; if (lvalue > 255) lvalue = 255; */ *ordenador.current_buffer = (unsigned char)(rvalue - (unsigned int)ordenador.sign); ordenador.current_buffer++; *ordenador.current_buffer = (unsigned char)(lvalue - (unsigned int)ordenador.sign); ordenador.current_buffer++; ordenador.sound_cuantity++; if (ordenador.sound_cuantity == ordenador.buffer_len) { // buffer filled sound_play(); ordenador.sound_cuantity = 0; } } }