/***********************************************************************************
 **
 ** Atari++ emulator (c) 2002 THOR-Software, Thomas Richter
 **
 ** $Id: pokey.hpp,v 1.62 2015/12/11 16:27:36 thor Exp $
 **
 ** In this module: Pokey emulation
 **
 **
 ** CREDIT NOTES:
 ** This pokey emulation uses a sound emulation process that works aLONG the same
 ** idea as Ron Fries' pokey emulator, though it is not directly based on, but 
 ** influenced by the original code. 
 ** Specifically, it differs significantly in its implementation of the high-pass 
 ** filters, polycounter implementation, channel muting to cut down complexity
 ** and VOLONLY emulation. It additionally implements a sound anti-alias filtering 
 ** and an interface towards various data types the sound front-end may provide.
 **********************************************************************************/

#ifndef POKEY_HPP
#define POKEY_HPP

/// Includes
#include "types.h"
#include "types.hpp"
#include "page.hpp"
#include "machine.hpp"
#include "argparser.hpp"
#include "hbiaction.hpp"
#include "vbiaction.hpp"
#include "chip.hpp"
#include "irqsource.hpp"
#include "saveable.hpp"
#include "cycleaction.hpp"
///

/// Forward declarations
class Monitor;
class IRQSource;
class SIO;
class Keyboard;
class Sound;
struct AudioBufferBase;
///

/// Class Pokey
// This class emulates (undoubtfully) the Pokey sound/UART/Keyboard
// controller of the Atari's.
class Pokey : public Chip, public Page, public Saveable, private VBIAction, private HBIAction, private CycleAction, private IRQSource {
  //
  // Links to other system components
  class Sound     *sound;
  class Keyboard  *keyboard;
  class SIO       *sio;
  //
  // Various constants
  //
  // Time base divisor constants
  static const int Base64kHz     INIT(28);     // Divisor from 1.79Mhz to 64 KHz
  static const int Base15kHz     INIT(114);    // Divisor from 1.79Mhz to 15 Khz
  //
  // Sizes of the poly counters
#ifdef HAS_MEMBER_INIT
  static const int Poly4Size = (1<<4) -1; // The size of the 4 bit poly counter
  static const int Poly5Size = (1<<5) -1; // The size of the 5 bit poly counter
  static const int Poly9Size = (1<<9) -1; // The size of the 9 bit poly counter
  static const int Poly17Size= (1<<17)-1; // The size of the 17 bit poly counter
#else
#define            Poly4Size  ((1<<4) -1)
#define            Poly5Size  ((1<<5) -1)
#define            Poly9Size  ((1<<9) -1)
#define            Poly17Size ((1<<17)-1)
#endif
  //
  // First all the chip registers we need for emulation
  //
  struct Channel {
    UBYTE AudioF;        // Audio frequency register (write only)
    UBYTE AudioC;        // Audio control register   (write only) 
    UBYTE AudioV;        // Audio volumne (internal)
    UBYTE AudioP;        // Audio polycounter (internal)
    UBYTE OutBit;        // current state of the output: on or off: 0 for off, 0x0f for on
    UBYTE HiFlop;        // state of the D-FlipFlop for the "Audio HighPass Filter"  
    UBYTE ChannelOn;     // Disables/Enables channels selectively
    UBYTE cludge1[3];    // for LW alignment: eight bytes so far.
    //
    LONG *HiPtr;         // pointer to the corresponding high-part of a 16 bit counter
    LONG  DivNCnt;       // Counter for sample frequency
    LONG  DivNMax;       // Computed counter maximum
    LONG  DivNIRQ;       // Counter for IRQ purposes
    LONG  DivFullMax;    // The base maximum frequency if this the low-part of a 16bit counter
    LONG  Zero;          // Must always be 0
    //
    // Reset the state after a reset
    void Reset(void)
    {   
      HiFlop     = 0x00;
      DivNMax    = 0;
      DivNCnt    = 0;
      DivFullMax = 0;
      DivNMax    = 0;
      DivNIRQ    = 0;
      Zero       = 0;
      OutBit     = 0;
      ChannelOn  = false;
      HiFlop     = false;
      HiPtr      = &Zero;  // High-counter is always zero.
      //
      // We also reset frequencies and control registers.
      // This need not to be strictly true.
      AudioF    = 0;
      AudioC    = 0;
    }
  }       Ch[4];         // the four audio channels
  //
  // Audio global control (XMOD)
  UBYTE AudioCtrl;
  // Serial port status
  UBYTE SkStat;
  // Serial port control
  UBYTE SkCtrl;
  //
  // Miscellaneous: Keyboard, IRQ
  UBYTE IRQStat;          // pending IRQs generated by Pokey
  UBYTE IRQEnable;        // IRQ enable mask
  //
  UBYTE SerOut_BitCounter;   // number of bits already shifted out of the serial port.
  UBYTE SerOut_Buffer;       // where the user data goes before it is reloaded into the output shift register.
  UWORD SerOut_Register;     // output register of the serial port.
  //
  LONG  SerIn_Counter;       // counts down until SerIn IRQ appears
  LONG  SerOut_Counter;      // counts down to next SerOut IRQ
  LONG  SerXmtDone_Counter;  // counts down to next transmission done IRQ
  LONG  SerBitOut_Counter;   // clock for individual bits of the serial port for two-tone mode.
  //
  // Frequency/timer support
  LONG  Frequency17Mhz;   // Base frequency (default: 1789790 or 1787520)
  LONG  TimeBase;         // Base frequency divisor of the sound support module
  LONG  Output;           // accumulated output amplitude
  LONG  Outcnt;           // accumulated sample counter
  //
  //
  // Polycounter patterns used by the pokey sound processor, to be initialized
  // on startup
  static const UBYTE    PolyCounterNone[1];
  static const UBYTE    PolyCounter4[Poly4Size];
  static const UBYTE    PolyCounter5[Poly5Size];
  // Audio poly counter outputs, computed, not pre-set
  UBYTE *PolyCounter9;
  UBYTE *PolyCounter17;
  // RANDOM poly counter outputs
  UBYTE *Random9;
  UBYTE *Random17;
  //
  // Position pointers within the poly counter sample 
  LONG          PolyAdjust;
  // Pointer into the polycounter arrays
  const UBYTE  *PolyNonePtr;
  const UBYTE  *Poly4Ptr;
  const UBYTE  *Poly5Ptr;
  const UBYTE  *Poly9Ptr;
  const UBYTE  *Poly17Ptr;
  const UBYTE  *Random9Ptr;
  const UBYTE  *Random17Ptr;
  // Pointer to the end of the polynoms (at the first position that is no longer legal)
  // This is used for fast pointer-wrap-around
  const UBYTE  *PolyNoneEnd;
  const UBYTE  *Poly4End;
  const UBYTE  *Poly5End;
  const UBYTE  *Poly9End;
  const UBYTE  *Poly17End;
  const UBYTE  *Random9End;
  const UBYTE  *Random17End;
  // The following array defines the poly counter to address for a given
  // AUDIOCTRL value.
  const UBYTE **PolyPointerFirst[8];
  const UBYTE **PolyPointerSecond[8];
  // Sample maximum counter: This encodes the timing base for the
  // sound processor.
  UQUAD         SampleCnt; // fractional encoded sample frequency counter
  //
  // Potentiometer counter and maximal values. These are here
  // to emulate the POT reading. I don't know which programs really
  // require these, but who knows?
  UBYTE         PotNCnt[8],PotNMax[8];
  //
  // AllPot flag byte for the potentiometer counter.
  UBYTE         AllPot;
  //
  // The following table converts input offsets into the output
  // volume. This is the last step in the pokey sound generation.
  BYTE         *OutputMapping;
  //
  // Output DC level shift to avoid a DC offset.
  LONG          DCLevelShift;
  // Long-range DC average.
  LONG          DCAverage;
  // Filter constant for the low-pass filter above.
  LONG          DCFilterConstant;
  //
  // For concurrent reading mode: The byte that will get read next.
  UBYTE         ConcurrentInput;
  // Whether the concurrent input is busy. If not, we may try to
  // read the next character from the port.
  bool          ConcurrentBusy;
  //
  // Unit number of this pokey chip in case there's more than one
  // in the system.
  int           Unit;
  //
  // Preferences:
  //
  LONG  SerIn_Delay;        // delay up to serial in IRQ, defined by external source.
  UWORD SerIn_Clock;        // input base as expected by pokey.
  LONG  SerOut_Delay;       // delay up to serial out IRQ
  UWORD SerOut_Clock;       // output base as generated by pokey.
  LONG  SerXmtDone_Delay;   // delay up to serial transmission IRQ
  LONG  SerBitOut_Delay;    // timing of individual bits for two-tone emulation
  LONG  Gamma;              // gamma value for audio output mapping in percent (100% => gamma = 1.0)
  LONG  Volume;             // volume gain for output mapping in percent (100% => full volume)
  bool  NTSC;               // PAL/NTSC switch, true for NTSC
  bool  isAuto;             // true in case the video mode comes from the machine.
  bool  SIOSound;           // Enable emulation of serial transfer sound
  bool  CycleTimers;        // Pokey timers are cycle-precise.
  //
  // Serial input buffer: Position of the buffer, and number of bytes
  // in there, and the rate in 1.79MHz cycles.
  UBYTE *SerInBuffer;
  int    SerInBytes;
  int    SerInRate;
  bool   SerInManual;      // set if the serial input was parsed off manually.
  //
  // The SAP record from last recording. Whenever Pokey changes its stage,
  // the SAP record is written to disk.
  UBYTE  SAPRecord[9];
  //
  // In case we're recording POKEY output in a SAP type-R file, here is the
  // file handle for it.
  FILE  *SAPOutput;
  //
  // A couple of additional information for SAP files. Name and author.
  char  *SongName;
  char  *AuthorName;
  bool   EnableSAP;
  //  
  // Initialize the poly counter for audio usage and the bit-output of
  // the random generator, composed of the uppermost bits of the
  // generator. Arguments are the random output, the audio output,
  // the size of the polycounter in bits (and also the last tap) and
  // the first tap.
  void InitPolyCounter(UBYTE *rand,UBYTE *audio,int size,int tap);
  //
  // Generate a pokey IRQ of the given bits
  // in positive logic.
  void GenerateIRQ(UBYTE bits);
  //
  // Advance the measurement of the potentiometer (A/D converter) input
  // by N steps. Depending on the measurement mode, this is either a
  // line-based or a cycle-based measurement.
  void UpdatePots(int steps);
  //
  // Reading and writing bytes to Pokey
  virtual UBYTE ComplexRead(ADR mem);
  virtual void  ComplexWrite(ADR mem,UBYTE val);
  //
  // Register read and write methods
  UBYTE KBCodeRead(void);
  UBYTE IRQStatRead(void);
  UBYTE PotNRead(int pot);
  UBYTE RandomRead(void);
  UBYTE SerInRead(void);
  UBYTE SkStatRead(void);
  UBYTE AllPotRead(void);
  // 
  void AudioFWrite(int channel,UBYTE val);
  void AudioCWrite(int channel,UBYTE val);
  void AudioCtrlWrite(UBYTE val);
  void IRQEnWrite(UBYTE val);
  void SerOutWrite(UBYTE val);
  void STimerWrite(void);
  void SkCtrlWrite(UBYTE val);
  void SkStatClear(void);
  void PotGoWrite(void);
  //
  // Recompute the output mapping now.
  void UpdateAudioMapping(void);
  //
  // Internals for the sound state machine follow here:
  // Update the internal state machine, to be called
  // after an audio specific update.
  void UpdateSound(UBYTE channelmask);  
  //
  // Update the pokey times by N steps.
  void GoNSteps(int steps);
  //
  // VBI activity: This is only used here for dumping POKEY registers to generate
  // SAP files if this feature is enabled.
  virtual void VBI(class Timer *time,bool quick,bool pause);
  //
  // Generate a scanline: This must be called each horizontal blank
  // to trigger pending IRQs. (Not as precise as the real thing, but 
  // good enough for most applications)
  virtual void HBI(void);
  //
  // Run a single CPU step, advance by a single cycle.
  virtual void Step(void);
  //
public:
  // Construct pokey. If we have more than one pokey in the
  // system, the argument describes which one we have.
  Pokey(class Machine *mach,int unit = 0);
  ~Pokey(void);
  //
  // Coldstart and Warmstart Pokey
  virtual void ColdStart(void);
  virtual void WarmStart(void);
  //  
  // Read or set the internal status
  virtual void State(class SnapShot *);
  //
  // Private for the keyboard class: Call this to notify pokey about
  // a new keyboard event. 
  void SignalKeyboardEvent(void);
  //
  // Private for the sound generator: Generate a given number
  // of new samples for a given sampling rate, given the audio buffer
  // to collect them and an offset to emulate the GTIA console speaker.
  // NOTE: The former interface specified the number of bytes, we changed
  // this to the number of samples.
  void ComputeSamples(struct AudioBufferBase *target,int size,int samplerate,UBYTE offset = 0);
  //
  // Private for the sound subsystem: Get the base horizontal blanking frequency
  // This rather much depends on the base frequency of the system, and hence on
  // the video mode.
  int BaseFrequency(void) const
  {
    return machine->ScaleFrequency((NTSC)?(15700):(15556));
  }
  //
  // Private for SIO:
  // Signal the arrival of a serial byte after
  // n 15Khz steps steps or after the default delay,
  // and with a baud rate with the indicated timer constant
  // measured in the period length in 1.79Mhz clocks.
  void SignalSerialBytes(UBYTE *buffer,int num,UWORD delay = 0,UWORD cycles = 47);
  //
  // Signal that a command frame has been signaled and that we therefore
  // abort incoming IO traffic. This is a hack to enforce resynchronization
  // and it shoudn't do anything if all goes right.
  void SignalCommandFrame(void);
  //
  // Return the serial output speed as cycles of the 1.79Mhz clock.
  UWORD SerialTransmitSpeed(void) const;
  //
  // Return the serial input speed as cycles of the 1.79Mhz clock.
  UWORD SerialReceiveSpeed(void) const;
  //
  // Parse off command line arguments here.
  virtual void ParseArgs(class ArgParser *arg);
  //
  // Print the internal status of pokey for the monitor
  virtual void DisplayStatus(class Monitor *mon);
};
///

///
#endif

