// --------------------------------------------------------------------------- // This file is part of reSID, a MOS6581 SID emulator engine. // Copyright (C) 2004 Dag Lem // // This program 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 2 of the License, or // (at your option) any later version. // // This program 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, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // --------------------------------------------------------------------------- #ifndef __WAVE_H__ #define __WAVE_H__ #include "siddefs-fp.h" // ---------------------------------------------------------------------------- // A 24 bit accumulator is the basis for waveform generation. FREQ is added to // the lower 16 bits of the accumulator each cycle. // The accumulator is set to zero when TEST is set, and starts counting // when TEST is cleared. // The noise waveform is taken from intermediate bits of a 23 bit shift // register. This register is clocked by bit 19 of the accumulator. // ---------------------------------------------------------------------------- class WaveformGeneratorFP { public: WaveformGeneratorFP(); void set_sync_source(WaveformGeneratorFP*); void set_chip_model(chip_model model); RESID_INLINE void clock(); RESID_INLINE void synchronize(); void reset(); void writeFREQ_LO(reg8); void writeFREQ_HI(reg8); void writePW_LO(reg8); void writePW_HI(reg8); void writeCONTROL_REG(reg8); reg8 readOSC(); // 12-bit waveform output. RESID_INLINE reg12 output(); protected: const WaveformGeneratorFP* sync_source; WaveformGeneratorFP* sync_dest; // Tell whether the accumulator MSB was set high on this cycle. bool msb_rising; reg24 accumulator; reg24 shift_register; reg12 previous, noise_output_cached; int noise_overwrite_delay; // Fout = (Fn*Fclk/16777216)Hz reg16 freq; // PWout = (PWn/40.95)%, also the same << 12 for direct comparison against acc reg12 pw; reg24 pw_acc_scale; // The control register right-shifted 4 bits; used for output function // table lookup. reg8 waveform; // The remaining control register bits. reg8 test; reg8 ring_mod; reg8 sync; // The gate bit is handled by the EnvelopeGenerator. // 16 possible combinations of waveforms. RESID_INLINE reg12 output___T(); RESID_INLINE reg12 output__S_(); RESID_INLINE reg12 output__ST(); RESID_INLINE reg12 output_P__(); RESID_INLINE reg12 output_P_T(); RESID_INLINE reg12 output_PS_(); RESID_INLINE reg12 output_PST(); RESID_INLINE reg12 outputN___(); RESID_INLINE reg12 outputN__T(); RESID_INLINE reg12 outputN_S_(); RESID_INLINE reg12 outputN_ST(); RESID_INLINE reg12 outputNP__(); RESID_INLINE reg12 outputNP_T(); RESID_INLINE reg12 outputNPS_(); RESID_INLINE reg12 outputNPST(); // Sample data for combinations of waveforms. static reg8 wave6581__ST[]; static reg8 wave6581_P_T[]; static reg8 wave6581_PS_[]; static reg8 wave6581_PST[]; static reg8 wave8580__ST[]; static reg8 wave8580_P_T[]; static reg8 wave8580_PS_[]; static reg8 wave8580_PST[]; reg8* wave__ST; reg8* wave_P_T; reg8* wave_PS_; reg8* wave_PST; friend class VoiceFP; friend class SIDFP; }; // ---------------------------------------------------------------------------- // SID clocking - 1 cycle. // ---------------------------------------------------------------------------- RESID_INLINE void WaveformGeneratorFP::clock() { /* no digital operation if test bit is set. Only emulate analog fade. */ if (test) { if (noise_overwrite_delay != 0) { if (-- noise_overwrite_delay == 0) { shift_register |= 0x7ffffc; noise_output_cached = outputN___(); } } return; } reg24 accumulator_prev = accumulator; // Calculate new accumulator value; accumulator += freq; accumulator &= 0xffffff; // Check whether the MSB became set high. This is used for synchronization. msb_rising = !(accumulator_prev & 0x800000) && (accumulator & 0x800000); // Shift noise register once for each time accumulator bit 19 is set high. if (!(accumulator_prev & 0x080000) && (accumulator & 0x080000)) { reg24 bit0 = ((shift_register >> 22) ^ (shift_register >> 17)) & 0x1; shift_register <<= 1; // optimization: fall into the bit bucket //shift_register &= 0x7fffff; shift_register |= bit0; /* since noise changes relatively infrequently, we'll avoid the relatively * expensive bit shuffling at output time. */ noise_output_cached = outputN___(); } // clear output bits of shift register if noise and other waveforms // are selected simultaneously if (waveform > 8) { shift_register &= 0x7fffff^(1<<22)^(1<<20)^(1<<16)^(1<<13)^(1<<11)^(1<<7)^(1<<4)^(1<<2); noise_output_cached = outputN___(); } } // ---------------------------------------------------------------------------- // Synchronize oscillators. // This must be done after all the oscillators have been clock()'ed since the // oscillators operate in parallel. // Note that the oscillators must be clocked exactly on the cycle when the // MSB is set high for hard sync to operate correctly. See SID::clock(). // ---------------------------------------------------------------------------- RESID_INLINE void WaveformGeneratorFP::synchronize() { // A special case occurs when a sync source is synced itself on the same // cycle as when its MSB is set high. In this case the destination will // not be synced. This has been verified by sampling OSC3. if (msb_rising && sync_dest->sync && !(sync && sync_source->msb_rising)) { sync_dest->accumulator = 0; } } // ---------------------------------------------------------------------------- // Output functions. // NB! The output from SID 8580 is delayed one cycle compared to SID 6581, // this is not modeled. // ---------------------------------------------------------------------------- // Triangle: // The upper 12 bits of the accumulator are used. // The MSB is used to create the falling edge of the triangle by inverting // the lower 11 bits. The MSB is thrown away and the lower 11 bits are // left-shifted (half the resolution, full amplitude). // Ring modulation substitutes the MSB with MSB EOR sync_source MSB. // RESID_INLINE reg12 WaveformGeneratorFP::output___T() { reg24 msb = (ring_mod ? accumulator ^ sync_source->accumulator : accumulator) & 0x800000; return ((msb ? ~accumulator : accumulator) >> 11) & 0xfff; } // Sawtooth: // The output is identical to the upper 12 bits of the accumulator. // RESID_INLINE reg12 WaveformGeneratorFP::output__S_() { return accumulator >> 12; } // Pulse: // The upper 12 bits of the accumulator are used. // These bits are compared to the pulse width register by a 12 bit digital // comparator; output is either all one or all zero bits. // NB! The output is actually delayed one cycle after the compare. // This is not modeled. // // The test bit, when set to one, holds the pulse waveform output at 0xfff // regardless of the pulse width setting. // RESID_INLINE reg12 WaveformGeneratorFP::output_P__() { return (test || accumulator >= pw_acc_scale) ? 0xfff : 0x000; } // Noise: // The noise output is taken from intermediate bits of a 23-bit shift register // which is clocked by bit 19 of the accumulator. // NB! The output is actually delayed 2 cycles after bit 19 is set high. // This is not modeled. // // Operation: Calculate EOR result, shift register, set bit 0 = result. // // ----------------------->--------------------- // | | // ----EOR---- | // | | | // 2 2 2 1 1 1 1 1 1 1 1 1 1 | // Register bits: 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 <--- // | | | | | | | | // OSC3 bits : 7 6 5 4 3 2 1 0 // // Since waveform output is 12 bits the output is left-shifted 4 times. // RESID_INLINE reg12 WaveformGeneratorFP::outputN___() { return ((shift_register & 0x400000) >> 11) | ((shift_register & 0x100000) >> 10) | ((shift_register & 0x010000) >> 7) | ((shift_register & 0x002000) >> 5) | ((shift_register & 0x000800) >> 4) | ((shift_register & 0x000080) >> 1) | ((shift_register & 0x000010) << 1) | ((shift_register & 0x000004) << 2); } // Combined waveforms: // By combining waveforms, the bits of each waveform are effectively short // circuited. A zero bit in one waveform will result in a zero output bit // (thus the infamous claim that the waveforms are AND'ed). // However, a zero bit in one waveform will also affect the neighboring bits // in the output. The reason for this has not been determined. // // Example: // // 1 1 // Bit # 1 0 9 8 7 6 5 4 3 2 1 0 // ----------------------- // Sawtooth 0 0 0 1 1 1 1 1 1 0 0 0 // // Triangle 0 0 1 1 1 1 1 1 0 0 0 0 // // AND 0 0 0 1 1 1 1 1 0 0 0 0 // // Output 0 0 0 0 1 1 1 0 0 0 0 0 // // // This behavior would be quite difficult to model exactly, since the SID // in this case does not act as a digital state machine. Tests show that minor // (1 bit) differences can actually occur in the output from otherwise // identical samples from OSC3 when waveforms are combined. To further // complicate the situation the output changes slightly with time (more // neighboring bits are successively set) when the 12-bit waveform // registers are kept unchanged. // // It is probably possible to come up with a valid model for the // behavior, however this would be far too slow for practical use since it // would have to be based on the mutual influence of individual bits. // // The output is instead approximated by using the upper bits of the // accumulator as an index to look up the combined output in a table // containing actual combined waveform samples from OSC3. // These samples are 8 bit, so 4 bits of waveform resolution is lost. // All OSC3 samples are taken with FREQ=0x1000, adding a 1 to the upper 12 // bits of the accumulator each cycle for a sample period of 4096 cycles. // // Sawtooth+Triangle: // The sawtooth output is used to look up an OSC3 sample. // // Pulse+Triangle: // The triangle output is right-shifted and used to look up an OSC3 sample. // The sample is output if the pulse output is on. // The reason for using the triangle output as the index is to handle ring // modulation. Only the first half of the sample is used, which should be OK // since the triangle waveform has half the resolution of the accumulator. // // Pulse+Sawtooth: // The sawtooth output is used to look up an OSC3 sample. // The sample is output if the pulse output is on. // // Pulse+Sawtooth+Triangle: // The sawtooth output is used to look up an OSC3 sample. // The sample is output if the pulse output is on. // RESID_INLINE reg12 WaveformGeneratorFP::output__ST() { return wave__ST[output__S_()] << 4; } RESID_INLINE reg12 WaveformGeneratorFP::output_P_T() { /* ring modulation does something odd with this waveform. But I don't know * how to emulate it. */ return (wave_P_T[output___T() >> 1] << 4) & output_P__(); } RESID_INLINE reg12 WaveformGeneratorFP::output_PS_() { return (wave_PS_[output__S_()] << 4) & output_P__(); } RESID_INLINE reg12 WaveformGeneratorFP::output_PST() { return (wave_PST[output__S_()] << 4) & output_P__(); } // Combined waveforms including noise: // All waveform combinations including noise output zero after a few cycles. // NB! The effects of such combinations are not fully explored. It is claimed // that the shift register may be filled with zeroes and locked up, which // seems to be true. // We have not attempted to model this behavior, suffice to say that // there is very little audible output from waveform combinations including // noise. We hope that nobody is actually using it. // RESID_INLINE reg12 WaveformGeneratorFP::outputN__T() { return 0; } RESID_INLINE reg12 WaveformGeneratorFP::outputN_S_() { return 0; } RESID_INLINE reg12 WaveformGeneratorFP::outputN_ST() { return 0; } RESID_INLINE reg12 WaveformGeneratorFP::outputNP__() { return 0; } RESID_INLINE reg12 WaveformGeneratorFP::outputNP_T() { return 0; } RESID_INLINE reg12 WaveformGeneratorFP::outputNPS_() { return 0; } RESID_INLINE reg12 WaveformGeneratorFP::outputNPST() { return 0; } // ---------------------------------------------------------------------------- // Select one of 16 possible combinations of waveforms. // ---------------------------------------------------------------------------- RESID_INLINE reg12 WaveformGeneratorFP::output() { switch (waveform) { case 0x1: previous = output___T(); break; case 0x2: previous = output__S_(); break; case 0x3: previous = output__ST(); break; case 0x4: previous = output_P__(); break; case 0x5: previous = output_P_T(); break; case 0x6: previous = output_PS_(); break; case 0x7: previous = output_PST(); break; case 0x8: previous = noise_output_cached; break; case 0x9: previous = outputN__T(); break; case 0xa: previous = outputN_S_(); break; case 0xb: previous = outputN_ST(); break; case 0xc: previous = outputNP__(); break; case 0xd: previous = outputNP_T(); break; case 0xe: previous = outputNPS_(); break; case 0xf: previous = outputNPST(); break; default: break; } return previous; } #endif // not __WAVE_H__