//  ---------------------------------------------------------------------------
//  This file is part of reSID, a MOS6581 SID emulator engine.
//  Copyright (C) 2004  Dag Lem <resid@nimrod.no>
//
//  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__
