diff --git a/src/main/java/jace/hardware/mockingboard/AY8910_old.java b/src/main/java/jace/hardware/mockingboard/AY8910_old.java deleted file mode 100644 index e1fc0ca..0000000 --- a/src/main/java/jace/hardware/mockingboard/AY8910_old.java +++ /dev/null @@ -1,638 +0,0 @@ -/* - * Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ -package jace.hardware.mockingboard; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import jace.hardware.CardMockingboard; - -// Port of AY code from AppleWin -- not used buy kept for reference. - -/*************************************************************************** - * - * ay8910.c - * - * - * Emulation of the AY-3-8910 / YM2149 sound chip. - * - * Based on various code snippets by Ville Hallik, Michael Cuddy, - * Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria. - * - ***************************************************************************/ - -// -// From mame.txt (http://www.mame.net/readme.html) -// -// VI. Reuse of Source Code -// -------------------------- -// This chapter might not apply to specific portions of MAME (e.g. CPU -// emulators) which bear different copyright notices. -// The source code cannot be used in a commercial product without the written -// authorization of the authors. Use in non-commercial products is allowed, and -// indeed encouraged. If you use portions of the MAME source code in your -// program, however, you must make the full source code freely available as -// well. -// Usage of the _information_ contained in the source code is free for any use. -// However, given the amount of time and energy it took to collect this -// information, if you find new information we would appreciate if you made it -// freely available as well. -// - -public class AY8910_old { - static final int MAX_OUTPUT = 0x007fff; - static final int MAX_AY8910 = 2; - static final int CLOCK = 1789770; - static final int SAMPLE_RATE = 44100; - -// See AY8910_set_clock() for definition of STEP - static final int STEP = 0x008000; - static int num = 0, ym_num = 0; - int SampleRate = 0; - - /* register id's */ - public enum Reg { - AFine(0, 255), - ACoarse(1, 15), - BFine(2, 255), - BCoarse(3, 15), - CFine(4, 255), - CCoarse(5, 15), - NoisePeriod(6, 31), - Enable(7, 255), - AVol(8, 31), - BVol(9, 31), - CVol(10, 31), - EnvFine(11, 255), - EnvCoarse(12, 255), - EnvShape(13, 15), - PortA(14, 255), - PortB(15, 255); - - public final int registerNumber; - public final int max; - Reg(int number, int maxValue) { - registerNumber = number; - max=maxValue; - } - static Reg get(int number) { - for (Reg r:Reg.values()) - if (r.registerNumber == number) return r; - return null; - } - static public Reg[] preferredOrder = new Reg[]{ - Enable,EnvShape,EnvCoarse,EnvFine,NoisePeriod,AVol,BVol,CVol, - AFine,ACoarse,BFine,BCoarse,CFine,CCoarse}; - } - - public AY8910_old() { - chips = new ArrayList(); - for (int i=0; i < MAX_AY8910; i++) { - PSG chip = new PSG(); - chips.add(chip); - } - initAll(CLOCK, SAMPLE_RATE); - } - -/////////////////////////////////////////////////////////// - private List chips; - - static int[] VolTable; - static { - buildMixerTable(); - } - - private class PSG { - int Channel; - int register_latch; - Map registers; - - int lastEnable; - int UpdateStep; - int PeriodA,PeriodB,PeriodC,PeriodN,PeriodE; - int CountA,CountB,CountC,CountN,CountE; - int VolA,VolB,VolC,VolE; - int EnvelopeA,EnvelopeB,EnvelopeC; - int OutputA,OutputB,OutputC,OutputN; - int CountEnv; - int Hold,Alternate,Attack,Holding; - int RNG; - - public PSG() { - registers = new HashMap(); - for (Reg r: Reg.values()) - setReg(r, 0); - } - - public void reset() { - register_latch = 0; - RNG = 1; - OutputA = 0; - OutputB = 0; - OutputC = 0; - OutputN = 0x00ff; - lastEnable = -1; /* force a write */ - for (Reg r: Reg.values()) - writeReg(r, 0); - } - - public void setClock(int clock) { - /* the step clock for the tone and noise generators is the chip clock */ - /* divided by 8; for the envelope generator of the AY-3-8910, it is half */ - /* that much (clock/16), but the envelope of the YM2149 goes twice as */ - /* fast, therefore again clock/8. */ - /* Here we calculate the number of steps which happen during one sample */ - /* at the given sample rate. No. of events = sample rate / (clock/8). */ - /* STEP is a multiplier used to turn the fraction into a fixed point */ - /* number. */ - double clk = clock; - double smprate = SampleRate; - UpdateStep = (int) ((STEP * smprate * 8.0 + clk/2.0) / clk); - } - - public void setReg(Reg r, int value) { - registers.put(r,value); - } - public int getReg(Reg r) { - return registers.get(r); - } - - public void writeReg(Reg r, int value) { - value &= r.max; - setReg(r, value); - int old; - /* A note about the period of tones, noise and envelope: for speed reasons,*/ - /* we count down from the period to 0, but careful studies of the chip */ - /* output prove that it instead counts up from 0 until the counter becomes */ - /* greater or equal to the period. This is an important difference when the*/ - /* program is rapidly changing the period to modulate the sound. */ - /* To compensate for the difference, when the period is changed we adjust */ - /* our internal counter. */ - /* 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. */ - switch(r) { - case ACoarse: - case AFine: - old = PeriodA; - PeriodA = (getReg(Reg.AFine) + 256 * getReg(Reg.ACoarse)) * UpdateStep; - if (PeriodA == 0) PeriodA = UpdateStep; - CountA += PeriodA - old; - if (CountA <= 0) CountA = 1; - break; - case BCoarse: - case BFine: - old = PeriodB; - PeriodB = (getReg(Reg.BFine) + 256 * getReg(Reg.BCoarse)) * UpdateStep; - if (PeriodB == 0) PeriodB = UpdateStep; - CountB += PeriodB - old; - if (CountB <= 0) CountB = 1; - break; - case CCoarse: - case CFine: - setReg(Reg.CCoarse, getReg(Reg.CCoarse) & 0x0f); - old = PeriodC; - PeriodA = (getReg(Reg.CFine) + 256 * getReg(Reg.CCoarse)) * UpdateStep; - if (PeriodC == 0) PeriodC = UpdateStep; - CountC += PeriodC - old; - if (CountC <= 0) CountC = 1; - break; - case NoisePeriod: - old = PeriodN; - PeriodN = getReg(Reg.NoisePeriod) * UpdateStep; - if (PeriodN == 0) PeriodN = UpdateStep; - CountN += PeriodN - old; - if (CountN <= 0) CountN = 1; - break; - case Enable: - lastEnable = value; - break; - case AVol: - EnvelopeA = value & 0x10; - if (EnvelopeA > 0) - VolA = VolE; - else { - if (value > 0) - VolA = CardMockingboard.VolTable[value]; - else - VolA = CardMockingboard.VolTable[0]; - } - break; - case BVol: - EnvelopeB = value & 0x10; - if (EnvelopeB > 0) - VolB = VolE; - else { - if (value > 0) - VolB = CardMockingboard.VolTable[value]; - else - VolB = CardMockingboard.VolTable[0]; - } - break; - case CVol: - EnvelopeC = value & 0x10; - if (EnvelopeC > 0) - VolC = VolE; - else { - if (value > 0) - VolC = CardMockingboard.VolTable[value]; - else - VolC = CardMockingboard.VolTable[0]; - } - break; - case EnvFine: - case EnvCoarse: - old = PeriodE; - PeriodE = ((getReg(Reg.EnvFine) + 256 * getReg(Reg.EnvCoarse))) * UpdateStep; - if (PeriodE == 0) PeriodE = UpdateStep / 2; - CountE += PeriodE - old; - if (CountE <= 0) CountE = 1; - if (PeriodE <= 0) PeriodE = 1; - break; - case EnvShape: - /* envelope shapes: - C AtAlH - 0 0 x x \___ - 0 1 x x /|__ - 1 0 0 0 \\\\ - 1 0 0 1 \___ - 1 0 1 0 \/\/ - __ - 1 0 1 1 \| - 1 1 0 0 //// - ___ - 1 1 0 1 / - 1 1 1 0 /\/\ - 1 1 1 1 /|__ - - The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it - has twice the steps, happening twice as fast. Since the end result is - just a smoother curve, we always use the YM2149 behaviour. - */ - Attack = (value & 0x04) != 0 ? 0x1f : 0x00; - if ( (value & 0x08) == 0) { - /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */ - Hold = 1; - Alternate = Attack; - } else { - Hold = value & 0x01; - Alternate = value & 0x02; - } - CountE = PeriodE; - CountEnv = 0x1f; - Holding = 0; - VolE = CardMockingboard.VolTable[CountEnv ^ Attack]; - if (EnvelopeA != 0) VolA = VolE; - if (EnvelopeB != 0) VolB = VolE; - if (EnvelopeC != 0) VolC = VolE; - break; - case PortA: - case PortB: - break; - } - } - - void update(int[][] buffer, int length) { - int[] buf1, buf2, buf3; - int outn; - - buf1 = buffer[0]; - buf2 = buffer[1]; - buf3 = buffer[2]; - - - /* The 8910 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 ( (getReg(Reg.Enable) & 0x01) != 0) { - if (CountA <= length*STEP) CountA += length*STEP; - OutputA = 1; - } else if (getReg(Reg.AVol) == 0) { - /* note that I do count += length, NOT count = length + 1. You might think */ - /* it's the same since the volume is 0, but doing the latter could cause */ - /* interferencies when the program is rapidly modulating the volume. */ - if (CountA <= length*STEP) CountA += length*STEP; - } - - if ( (getReg(Reg.Enable) & 0x02) != 0) { - if (CountB <= length*STEP) CountB += length*STEP; - OutputB = 1; - } else if (getReg(Reg.BVol) == 0) { - if (CountB <= length*STEP) CountB += length*STEP; - } - - if ( (getReg(Reg.Enable) & 0x04) != 0) { - if (CountC <= length*STEP) CountC += length*STEP; - OutputC = 1; - } else if (getReg(Reg.CVol) == 0) { - if (CountC <= length*STEP) CountC += length*STEP; - } - - /* for the noise channel we must not touch OutputN - it's also not necessary */ - /* since we use outn. */ - if ((getReg(Reg.Enable) & 0x38) == 0x38) /* all off */ - if (CountN <= length*STEP) CountN += length*STEP; - - outn = (OutputN | getReg(Reg.Enable)); - int index = 0; - //System.out.println("Length:"+length); - /* buffering loop */ - while (length != 0) { - int vola,volb,volc; - int left; - - /* vola, volb and volc keep track of how long each square wave stays */ - /* in the 1 position during the sample period. */ - vola = volb = volc = 0; - //System.out.println("STEP:"+STEP); - - left = STEP; - do { - int nextevent; - - if (CountN < left) nextevent = CountN; - else nextevent = left; - - if ( (outn & 0x08) != 0) { - if (OutputA != 0) vola += CountA; - CountA -= nextevent; - /* PeriodA is the half period of the square wave. Here, in each */ - /* loop I add PeriodA twice, so that at the end of the loop the */ - /* square wave is in the same status (0 or 1) it was at the start. */ - /* vola is also incremented by PeriodA, since the wave has been 1 */ - /* exactly half of the time, regardless of the initial position. */ - /* If we exit the loop in the middle, OutputA has to be inverted */ - /* and vola incremented only if the exit status of the square */ - /* wave is 1. */ - while (CountA <= 0 && PeriodA > 0) { - CountA += PeriodA; - if (CountA > 0) { - OutputA ^= 1; - if (OutputA != 0) vola += PeriodA; - break; - } - CountA += PeriodA; - vola += PeriodA; - } - if (OutputA != 0) vola -= CountA; - } else { - CountA -= nextevent; - while (CountA <= 0 && PeriodA > 0) { - CountA += PeriodA; - if (CountA > 0) { - OutputA ^= 1; - break; - } - CountA += PeriodA; - } - } - - if ((outn & 0x10) != 0) { - if (OutputB != 0) volb += CountB; - CountB -= nextevent; - while (CountB <= 0 && PeriodB > 0) { - CountB += PeriodB; - if (CountB > 0) { - OutputB ^= 1; - if (OutputB != 0) volb += PeriodB; - break; - } - CountB += PeriodB; - volb += PeriodB; - } - if (OutputB != 0) volb -= CountB; - } else { - CountB -= nextevent; - while (CountB <= 0 && PeriodB > 0) { - CountB += PeriodB; - if (CountB > 0) { - OutputB ^= 1; - break; - } - CountB += PeriodB; - } - } - - if ( (outn & 0x20) != 0) { - if (OutputC != 0) volc += CountC; - CountC -= nextevent; - while (CountC <= 0 && PeriodC > 0) { - CountC += PeriodC; - if (CountC > 0) { - OutputC ^= 1; - if (OutputC != 0) volc += PeriodC; - break; - } - CountC += PeriodC; - volc += PeriodC; - } - if (OutputC != 0) volc -= CountC; - } else { - CountC -= nextevent; - while (CountC <= 0 && PeriodC > 0) { - CountC += PeriodC; - if (CountC > 0) { - OutputC ^= 1; - break; - } - CountC += PeriodC; - } - } - - CountN -= nextevent; - if (CountN <= 0 && PeriodN > 0) { - /* Is noise output going to change? */ - /* (bit0^bit1)? */ - if (((RNG + 1) & 2) != 0) { - OutputN ^= 0x0FF; - outn = (OutputN | getReg(Reg.Enable)); - } - - /* 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 ((RNG & 1) != 0) RNG ^= 0x0024000; /* This version is called the "Galois configuration". */ - RNG >>= 1; - CountN += PeriodN; - } - - left -= nextevent; - } while (left > 0); - // System.out.println("End left loop"); - - /* update envelope */ - if (Holding == 0) { - CountE -= STEP; - if (CountE <= 0) { - do { - CountEnv--; - CountE += PeriodE; - } while (CountE <= 0); - - /* check envelope current position */ - if (CountEnv < 0) { - if (Hold != 0) { - if (Alternate != 0) - Attack ^= 0x1f; - Holding = 1; - CountEnv = 0; - } else { - /* if CountEnv has looped an odd number of times (usually 1), */ - /* invert the output. */ - if ( (Alternate != 0) && ((CountEnv & 0x20) != 0)) - Attack ^= 0x1f; - CountEnv &= 0x1f; - } - } - - VolE = VolTable[CountEnv ^ Attack]; - /* reload volume */ - if (EnvelopeA != 0) VolA = VolE; - if (EnvelopeB != 0) VolB = VolE; - if (EnvelopeC != 0) VolC = VolE; - } - } - - // Output PCM wave [-32768...32767] instead of MAME's voltage level [0...32767] - // - This allows for better s/w mixing -buf1[index] = (vola * VolA) / STEP; -buf2[index] = (volb * VolB) / STEP; -buf3[index] = (volc * VolC) / STEP; -/* - if(VolA != 0) { - if (vola != 0) buf1[index] = (vola * VolA) / STEP; - else buf1[index] = -VolA; - } else { - buf1[index] = 0; - } - - // - - if(VolB != 0) { - if (volb != 0) buf2[index] = (volb * VolB) / STEP; - else buf2[index] = -VolB; - } else - buf2[index] = 0; - - // - - if(VolC != 0) { - if (volc != 0) buf3[index] = (volc * VolC) / STEP; - else buf3[index] = -VolC; - } else - buf3[index] = 0; - */ - index++; - length--; - } - } - }; - - public void writeReg(int chipNumber, int register, int value) { - Reg r = Reg.get(register); - writeReg(chipNumber, r, value); - } - - public void writeReg(int chipNumber, Reg register, int value) { - chips.get(chipNumber).writeReg(register, value); - } - - // /length/ is the number of samples we require - // NB. This should be called at twice the 6522 IRQ rate or (eg) 60Hz if no IRQ. - public void update(int chipNumber,int[][] buffer,int length) { - chips.get(chipNumber).update(buffer, length); - } - - int[][] buffers; - int bufferLength = -1; - public int[][] getBuffers(int length) { - if (buffers == null || bufferLength != length) { - buffers = new int[3][length]; - bufferLength = length; - } - return buffers; - } - - public void playSound(int size, int[] left, int[] right) { - int[][] buffers = getBuffers(left.length); - update(0, buffers, size); - mixDown(left, buffers, size); - update(1, buffers, size); - mixDown(right, buffers, size); - } - - public void mixDown(int[] out, int[][] in, int size) { - for (int i=0; i < size; i++) { - int sample = (in[0][i] + in[1][i] + in[2][i]) / 3; - out[i] = sample; - } - } - - public void setClock(int chipNumber,int clock) { - chips.get(chipNumber).setClock(clock); - } - - public void reset(int chipNumber) { - chips.get(chipNumber).reset(); - } - - public void initAll(int nClock, int nSampleRate) { - SampleRate = nSampleRate; - for (PSG p:chips) { - p.setClock(nClock); - p.reset(); - } - } - - public void initClock(int nClock) { - for (PSG p:chips) p.setClock(nClock); - } - - static void buildMixerTable() { - VolTable = new int[32]; - int SampleRate; - - /* calculate the volume->voltage conversion table */ - /* The AY-3-8910 has 16 levels, in a logarithmic scale (3dB per step) */ - /* The YM2149 still has 16 levels for the tone generators, but 32 for */ - /* the envelope generator (1.5dB per step). */ - double out = MAX_OUTPUT; - for (int i = 31;i > 0;i--) { - VolTable[i] = (int) (out + 0.5); /* round to nearest */ // [TC: unsigned int cast] - out /= 1.188502227; /* = 10 ^ (1.5/20) = 1.5dB */ - } - VolTable[0] = 0; - } -} \ No newline at end of file