Re: Aw: Re: [stella] The future of Stella (i.e. debugger needed)

Subject: Re: Aw: Re: [stella] The future of Stella (i.e. debugger needed)
From: Stephen Anthony <stephena@xxxxxxxxxxxxxxxxxxxxx>
Date: Thu, 20 May 2004 10:33:32 -0230
On May 18, 2004 10:44 am, Thomas Jentzsch wrote:
> Steven wrote:
> > Yes, both are GPL.  But right now, z26 is in x86 assembly, and
> > Stella is in C++.  I don't know enough ASM to do the conversion. 
> > Besides, it's not just the sound code itself, but timing and
> > resampling issues.
> >
> > I really need someone who knows more about sound programming than I
> > do to look at it .  Perhaps they'd find the problem to be quite
> > easy to fix.
> >
> > Heck, even if someone took a look at the current way of doing it
> > and made some recommendations, that could help.  I could probably
> > do the actual coding; it's just the underlying timing/resampling
> > *algorithm* that's eluded me.
>
> I suppose we are just to lazy (I am!) to download the whole package
> (incl. compiler etc.) and search for the code.
>
> So maybe if you could just post the code with the problem (and maybe
> the link to the matching z26 code), someone like Manuel or me could
> use their Assembler knowledge to find the differences.

OK, to all those that asked for it, here's the first problem.  I'm using 
the TIAsound code which is essentially still the same as that which Ron 
Fries released years ago.  According to my sources, the sound code in 
z26 is no longer really the same as Ron Fries code, and as had 
extensive modifications.  So if someone were to port *that* code to 
C/C++ (or any high-level language), it would be a great help.

The main problem I'm having is that (1) not enough samples are being 
produced per frame to account for Pitfall2 and Quadrun (digital sound), 
and (2) in Windows, the sound is dropping out because the buffer size I 
use, and If I use a larger buffer size, the sound changes pitch.

One partial solution is in the attached 'SoundSDL.cxx' file.  In the 
SoundSDL::set() method, in the section '#if defined(DIGITAL_SOUND)', we 
see there is a way to generate the correct # of samples.  This is the 
way Stella 1.3 worked.

The problem with this approach is that now, *too many* samples are 
created per second.  If I let the SDL audio callback process them as it 
needs them, the sounds come out correct, but we lose a/v sync, since 
the audio callback falls behind the video rendering.  My guess would be 
that some sort of resampling has to be done, so that the # of samples 
created is precisely what SDL wants, but it still includes all the 
sample information as before (a resampling compression algorithm, if 
you will).  My guess is this is what z26 does.

So, I'm including the following files from Stella:

1)  SoundSDL.cxx - this one is the main sound dispatching code.  It 
takes requests from other parts of the system and sends them to the 
TIAsound functions.  It also maintains a queue.  Some parts of this 
class are quite similar to 'soundq.asm' from z26.

2)  TIAsound.c - the TIA sound routines, originally from Ron Fries.  
This is what 'tiasnd.asm' from z26 is based on.

And from z26:

1)  soundq.asm - creates a sound queue for the SDL audio callback.  
Always keeps the queue full to capacity.  I suspect there is resampling 
going on here.

2)  tiasnd.asm - The actual sound generation routines based on code from 
Ron Fries.


Any help is appreciated.  I can try to provide more info if required.

Thanks,
Steve
//============================================================================
//
//   SSSS    tt          lll  lll       
//  SS  SS   tt           ll   ll        
//  SS     tttttt  eeee   ll   ll   aaaa 
//   SSSS    tt   ee  ee  ll   ll      aa
//      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
//  SS  SS   tt   ee      ll   ll  aa  aa
//   SSSS     ttt  eeeee llll llll  aaaaa
//
// Copyright (c) 1995-2002 by Bradford W. Mott
//
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: SoundSDL.cxx,v 1.10 2004/04/27 00:50:52 stephena Exp $
//============================================================================

#include <SDL.h>

#include "TIASound.h"
#include "Serializer.hxx"
#include "Deserializer.hxx"
#include "System.hxx"

#include "SoundSDL.hxx"

//#define DIGITAL_SOUND

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SoundSDL(uInt32 fragsize, uInt32 queuesize)
    : myCurrentVolume(SDL_MIX_MAXVOLUME),
      myFragmentSize(fragsize),
      myIsInitializedFlag(false),
      myIsMuted(false),
      mySampleRate(31400),
      mySampleQueue(queuesize)
{
  if(1)
  {
    if(SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
    {
      cerr << "WARNING: Couldn't initialize SDL audio system! " << endl;
      cerr << "         " << SDL_GetError() << endl;
      myIsInitializedFlag = false;
      mySampleRate = 0;
      return;
    }

    SDL_AudioSpec desired;
    desired.freq = mySampleRate;
    desired.format = AUDIO_U8;
    desired.channels = 1;
    desired.samples = myFragmentSize;
    desired.callback = callback;
    desired.userdata = (void*)this;

    if(SDL_OpenAudio(&desired, &myHardwareSpec) < 0)
    {
      cerr << "WARNING: Couldn't open SDL audio system! " << endl;
      cerr << "         " << SDL_GetError() << endl;
      myIsInitializedFlag = false;
      mySampleRate = 0;
      return;
    }

    // Make sure the sample buffer isn't to big (if it is the sound code
    // will not work so we'll need to disable the audio support)
    if(((float)myHardwareSpec.size / (float)myHardwareSpec.freq) >= 0.25)
    {
      cerr << "WARNING: Audio device doesn't support real time audio! Make ";
      cerr << "sure a sound" << endl;
      cerr << "         server isn't running.  Audio is disabled..." << endl;

      SDL_CloseAudio();

      myIsInitializedFlag = false;
      mySampleRate = 0;
      return;
    }

    myIsInitializedFlag = true;
    myIsMuted = false;
    mySampleRate = myHardwareSpec.freq;
    myFragmentSize = myHardwareSpec.samples;

//    cerr << "Freq: " << (int)mySampleRate << endl;
//    cerr << "Format: " << (int)myHardwareSpec.format << endl;
//    cerr << "Channels: " << (int)myHardwareSpec.channels << endl;
//    cerr << "Silence: " << (int)myHardwareSpec.silence << endl;
//    cerr << "Samples: " << (int)myHardwareSpec.samples << endl;
//    cerr << "Size: " << (int)myHardwareSpec.size << endl;

    // Now initialize the TIASound object which will actually generate sound
    Tia_sound_init(31400, mySampleRate);

    // And start the SDL sound subsystem ...
    SDL_PauseAudio(0);
  }
  else
  {
    myIsInitializedFlag = false;
    myIsMuted = true;
    mySampleRate = 0;
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::~SoundSDL()
{
  if(myIsInitializedFlag)
  {
    SDL_CloseAudio();
  }

  myIsInitializedFlag = false;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::isSuccessfullyInitialized() const
{
  return myIsInitializedFlag;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::mute(bool state)
{
  if(!myIsInitializedFlag)
  {
    return;
  }

  // Ignore multiple calls to do the same thing
  if(myIsMuted == state)
  {
    return;
  }

  myIsMuted = state;

  SDL_PauseAudio(myIsMuted ? 1 : 0);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::setVolume(Int32 percent)
{
  if(myIsInitializedFlag)
  {
    if((percent >= 0) && (percent <= 100))
    {
      SDL_LockAudio();
      myCurrentVolume = (uInt32)(((float)percent / 100.0) * SDL_MIX_MAXVOLUME);
      SDL_UnlockAudio();
    }
    else if(percent == -1)   // If -1 has been specified, play sound at default volume
    {
      myCurrentVolume = SDL_MIX_MAXVOLUME;
    }
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::update()
{
#if !defined(DIGITAL_SOUND)
  if(!myPauseStatus && myIsInitializedFlag)
  {
    // Make sure we have exclusive access to the sample queue
//    SDL_LockAudio();

    // Generate enough samples to keep the sample queue full to capacity
    uInt32 numbytes = mySampleQueue.capacity() - mySampleQueue.size();
    uInt8 buffer[numbytes];
    Tia_process(buffer, numbytes);
    mySampleQueue.enqueue(buffer, numbytes);

    // Release lock on the sample queue
//    SDL_UnlockAudio();
  }
#endif
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::set(uInt16 addr, uInt8 value)
{
#if defined(DIGITAL_SOUND)
  // Calculate the number of samples that need to be generated based on the
  // number of CPU cycles which have passed since the last sound update
  uInt32 samplesToGenerate =
         (mySampleRate * (mySystem->cycles() - myLastSoundUpdateCycle)) / 1190000;

  // Update counters and create samples if there's one sample to generate
  // TODO: This doesn't handle rounding quite right (10/08/2002)
  if(samplesToGenerate >= 1)
  {
    uInt8 buffer[1024];

    for(Int32 sg = (Int32)samplesToGenerate; sg > 0; sg -= 1024)
    {
      Tia_process(buffer, ((sg >= 1024) ? 1024 : sg));
      mySampleQueue.enqueue(buffer, ((sg >= 1024) ? 1024 : sg));
    }
 	 	
    myLastSoundUpdateCycle = myLastSoundUpdateCycle +
        ((samplesToGenerate * 1190000) / mySampleRate);
  }

  if(addr != 0)
  {
    Update_tia_sound(addr, value);
  }
#else
  Update_tia_sound(addr, value);
#endif
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::save(Serializer& out)
{
  string device = "TIASound";

  try
  {
    out.putString(device);

    uInt8 reg1 = 0, reg2 = 0, reg3 = 0, reg4 = 0, reg5 = 0, reg6 = 0;

    // Only get the TIA sound registers if sound is enabled
    if(myIsInitializedFlag)
      Tia_get_registers(&reg1, &reg2, &reg3, &reg4, &reg5, &reg6);

    out.putLong(reg1);
    out.putLong(reg2);
    out.putLong(reg3);
    out.putLong(reg4);
    out.putLong(reg5);
    out.putLong(reg6);

    out.putLong(myLastSoundUpdateCycle);
  }
  catch(char *msg)
  {
    cerr << msg << endl;
    return false;
  }
  catch(...)
  {
    cerr << "Unknown error in save state for " << device << endl;
    return false;
  }

  return true;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool SoundSDL::load(Deserializer& in)
{
  string device = "TIASound";

  try
  {
    if(in.getString() != device)
      return false;

    uInt8 reg1 = 0, reg2 = 0, reg3 = 0, reg4 = 0, reg5 = 0, reg6 = 0;
    reg1 = (uInt8) in.getLong();
    reg2 = (uInt8) in.getLong();
    reg3 = (uInt8) in.getLong();
    reg4 = (uInt8) in.getLong();
    reg5 = (uInt8) in.getLong();
    reg6 = (uInt8) in.getLong();

    myLastSoundUpdateCycle = (Int32) in.getLong();

    // Only update the TIA sound registers if sound is enabled
    if(myIsInitializedFlag)
      Tia_set_registers(reg1, reg2, reg3, reg4, reg5, reg6);
  }
  catch(char *msg)
  {
    cerr << msg << endl;
    return false;
  }
  catch(...)
  {
    cerr << "Unknown error in load state for " << device << endl;
    return false;
  }

  return true;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::callback(void* udata, uInt8* stream, int len)
{
  SoundSDL* sound = (SoundSDL*)udata;

  if(!sound->isSuccessfullyInitialized())
  {
    return;
  }

  if(sound->mySampleQueue.size() > 0)
  {
    Int32 offset;
    uInt8 buffer[2048];
    for(offset = 0; (offset < len) && (sound->mySampleQueue.size() > 0); )
    {
      uInt32 s = sound->mySampleQueue.dequeue(buffer, 
          (2048 > (len - offset) ? (len - offset) : 2048));
      SDL_MixAudio(stream + offset, buffer, s, sound->myCurrentVolume);
      offset += s;
    }
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SampleQueue::SampleQueue(uInt32 capacity)
    : myCapacity(capacity),
      myBuffer(0),
      mySize(0),
      myHead(0),
      myTail(0)
{
  myBuffer = new uInt8[myCapacity];
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
SoundSDL::SampleQueue::~SampleQueue()
{
  delete[] myBuffer;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::SampleQueue::clear()
{
  myHead = myTail = mySize = 0;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL::SampleQueue::dequeue(uInt8* buffer, uInt32 size)
{
  // We can only dequeue up to the number of items in the queue
  if(size > mySize)
  {
    size = mySize;
  }

  if((myHead + size) < myCapacity)
  {
    memcpy((void*)buffer, (const void*)(myBuffer + myHead), size);
    myHead += size;
  }
  else
  {
    uInt32 s1 = myCapacity - myHead;
    uInt32 s2 = size - s1;
    memcpy((void*)buffer, (const void*)(myBuffer + myHead), s1);
    memcpy((void*)(buffer + s1), (const void*)myBuffer, s2);
    myHead = (myHead + size) % myCapacity;
  }

  mySize -= size;

  return size;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void SoundSDL::SampleQueue::enqueue(uInt8* buffer, uInt32 size)
{
  // If an attempt is made to enqueue more than the queue can hold then
  // we'll only enqueue the last myCapacity elements.
  if(size > myCapacity)
  {
    buffer += (size - myCapacity);
    size = myCapacity;
  }

  if((myTail + size) < myCapacity)
  {
    memcpy((void*)(myBuffer + myTail), (const void*)buffer, size);
    myTail += size;
  }
  else
  {
    uInt32 s1 = myCapacity - myTail;
    uInt32 s2 = size - s1;
    memcpy((void*)(myBuffer + myTail), (const void*)buffer, s1);
    memcpy((void*)myBuffer, (const void*)(buffer + s1), s2);
    myTail = (myTail + size) % myCapacity;
  }

  if((mySize + size) > myCapacity)
  {
    myHead = (myHead + ((mySize + size) - myCapacity)) % myCapacity;
    mySize = myCapacity;
  }
  else
  {
    mySize += size;
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 SoundSDL::SampleQueue::size() const
{
  return mySize;
}
/*****************************************************************************/
/*                                                                           */
/* Module:  TIA Chip Sound Simulator                                         */
/* Purpose: To emulate the sound generation hardware of the Atari TIA chip.  */
/* Author:  Ron Fries                                                        */
/*                                                                           */
/* Revision History:                                                         */
/*    10-Sep-96 - V1.0 - Initial Release                                     */
/*    14-Jan-97 - V1.1 - Cleaned up sound output by eliminating counter      */
/*                       reset.                                              */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*                 License Information and Copyright Notice                  */
/*                 ========================================                  */
/*                                                                           */
/* TiaSound is Copyright(c) 1996 by Ron Fries                                */
/*                                                                           */
/* This library is free software; you can redistribute it and/or modify it   */
/* under the terms of version 2 of the GNU Library General Public License    */
/* as published by the Free Software Foundation.                             */
/*                                                                           */
/* 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 Library */
/* General Public License for more details.                                  */
/* To obtain a copy of the GNU Library General Public License, write to the  */
/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   */
/*                                                                           */
/* Any permitted reproduction of these routines, in whole or in part, must   */
/* bear this legend.                                                         */
/*                                                                           */
/*****************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <time.h>


/* define some data types to keep it platform independent */
#ifdef WIN32
#define int8  char
#define int16 short
#define int32 int
#else
#define int8  char
#define int16 int
#define int32 long
#endif

#define uint8  unsigned int8 
#define uint16 unsigned int16
#define uint32 unsigned int32


/* CONSTANT DEFINITIONS */

/* definitions for AUDCx (15, 16) */
#define SET_TO_1     0x00      /* 0000 */
#define POLY4        0x01      /* 0001 */
#define DIV31_POLY4  0x02      /* 0010 */
#define POLY5_POLY4  0x03      /* 0011 */
#define PURE         0x04      /* 0100 */
#define PURE2        0x05      /* 0101 */
#define DIV31_PURE   0x06      /* 0110 */
#define POLY5_2      0x07      /* 0111 */
#define POLY9        0x08      /* 1000 */
#define POLY5        0x09      /* 1001 */
#define DIV31_POLY5  0x0a      /* 1010 */
#define POLY5_POLY5  0x0b      /* 1011 */
#define DIV3_PURE    0x0c      /* 1100 */
#define DIV3_PURE2   0x0d      /* 1101 */
#define DIV93_PURE   0x0e      /* 1110 */
#define DIV3_POLY5   0x0f      /* 1111 */
                 
#define DIV3_MASK    0x0c                 
                 
#define AUDC0        0x15
#define AUDC1        0x16
#define AUDF0        0x17
#define AUDF1        0x18
#define AUDV0        0x19
#define AUDV1        0x1a

/* the size (in entries) of the 4 polynomial tables */
#define POLY4_SIZE  0x000f
#define POLY5_SIZE  0x001f
#define POLY9_SIZE  0x01ff

/* channel definitions */
#define CHAN1       0
#define CHAN2       1

#define FALSE       0
#define TRUE        1


/* LOCAL GLOBAL VARIABLE DEFINITIONS */

/* structures to hold the 6 tia sound control bytes */
static uint8 AUDC[2];    /* AUDCx (15, 16) */
static uint8 AUDF[2];    /* AUDFx (17, 18) */
static uint8 AUDV[2];    /* AUDVx (19, 1A) */

static uint8 Outvol[2];  /* last output volume for each channel */


/* Initialze the bit patterns for the polynomials. */

/* The 4bit and 5bit patterns are the identical ones used in the tia chip. */
/* Though the patterns could be packed with 8 bits per byte, using only a */
/* single bit per byte keeps the math simple, which is important for */
/* efficient processing. */

static uint8 Bit4[POLY4_SIZE] =
      { 1,1,0,1,1,1,0,0,0,0,1,0,1,0,0 };

static uint8 Bit5[POLY5_SIZE] =
      { 0,0,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,1,0,1,1,1,0,1,0,1,0,0,0,0,1 };

/* I've treated the 'Div by 31' counter as another polynomial because of */
/* the way it operates.  It does not have a 50% duty cycle, but instead */
/* has a 13:18 ratio (of course, 13+18 = 31).  This could also be */
/* implemented by using counters. */

static uint8 Div31[POLY5_SIZE] =
      { 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 };

/* Rather than have a table with 511 entries, I use a random number */
/* generator. */

static uint8 Bit9[POLY9_SIZE];

static uint8  P4[2]; /* Position pointer for the 4-bit POLY array */
static uint8  P5[2]; /* Position pointer for the 5-bit POLY array */
static uint16 P9[2]; /* Position pointer for the 9-bit POLY array */

static uint8 Div_n_cnt[2];  /* Divide by n counter. one for each channel */
static uint8 Div_n_max[2];  /* Divide by n maximum, one for each channel */


/* In my routines, I treat the sample output as another divide by N counter. */
/* For better accuracy, the Samp_n_cnt has a fixed binary decimal point */
/* which has 8 binary digits to the right of the decimal point. */

static uint16 Samp_n_max; /* Sample max, multiplied by 256 */
static uint16 Samp_n_cnt; /* Sample cnt. */



/*****************************************************************************/
/* Module:  Tia_sound_init()                                                 */
/* Purpose: to handle the power-up initialization functions                  */
/*          these functions should only be executed on a cold-restart        */
/*                                                                           */
/* Author:  Ron Fries                                                        */
/* Date:    September 10, 1996                                               */
/*                                                                           */
/* Inputs:  sample_freq - the value for the '30 Khz' Tia audio clock         */
/*          playback_freq - the playback frequency in samples per second     */
/*                                                                           */
/* Outputs: Adjusts local globals - no return value                          */
/*                                                                           */
/*****************************************************************************/

void Tia_sound_init (uint16 sample_freq, uint16 playback_freq)
{
   uint8 chan;
   int16 n;

   /* fill the 9bit polynomial with random bits */
   for (n=0; n<POLY9_SIZE; n++)
   {
      Bit9[n] = rand() & 0x01;       /* fill poly9 with random bits */
   }

   /* calculate the sample 'divide by N' value based on the playback freq. */
   Samp_n_max = (uint16)(((uint32)sample_freq<<8)/playback_freq);
   Samp_n_cnt = 0;  /* initialize all bits of the sample counter */

   /* initialize the local globals */
   for (chan = CHAN1; chan <= CHAN2; chan++)
   {
      Outvol[chan] = 0;
      Div_n_cnt[chan] = 0;
      Div_n_max[chan] = 0;
      AUDC[chan] = 0;
      AUDF[chan] = 0;
      AUDV[chan] = 0;
      P4[chan] = 0;
      P5[chan] = 0;
      P9[chan] = 0;
   }
}


/*****************************************************************************/
/* Module:  Update_tia_sound()                                               */
/* Purpose: To process the latest control values stored in the AUDF, AUDC,   */
/*          and AUDV registers.  It pre-calculates as much information as    */
/*          possible for better performance.  This routine has not been      */
/*          optimized.                                                       */
/*                                                                           */
/* Author:  Ron Fries                                                        */
/* Date:    January 14, 1997                                                 */
/*                                                                           */
/* Inputs:  addr - the address of the parameter to be changed                */
/*          val - the new value to be placed in the specified address        */
/*                                                                           */
/* Outputs: Adjusts local globals - no return value                          */
/*                                                                           */
/*****************************************************************************/

void Update_tia_sound (uint16 addr, uint8 val)
{
    uint16 new_val = 0;
    uint8 chan;

    /* determine which address was changed */
    switch (addr)
    {
       case AUDC0:
          AUDC[0] = val & 0x0f;
          chan = 0;
          break;

       case AUDC1:
          AUDC[1] = val & 0x0f;
          chan = 1;
          break;

       case AUDF0:
          AUDF[0] = val & 0x1f;
          chan = 0;
          break;

       case AUDF1:
          AUDF[1] = val & 0x1f;
          chan = 1;
          break;

       case AUDV0:
          AUDV[0] = (val & 0x0f) << 3;
          chan = 0;
          break;

       case AUDV1:
          AUDV[1] = (val & 0x0f) << 3;
          chan = 1;
          break;

       default:
          chan = 255;
          break;
    }

    /* if the output value changed */
    if (chan != 255)
    {
       /* an AUDC value of 0 is a special case */
       if (AUDC[chan] == SET_TO_1)
       {
          /* indicate the clock is zero so no processing will occur */
          new_val = 0;

          /* and set the output to the selected volume */
          Outvol[chan] = AUDV[chan];
       }
       else
       {
          /* otherwise calculate the 'divide by N' value */
          new_val = AUDF[chan] + 1;

          /* if bits 2 & 3 are set, then multiply the 'div by n' count by 3 */
          if ((AUDC[chan] & DIV3_MASK) == DIV3_MASK)
          {
             new_val *= 3;
          }
       }

       /* only reset those channels that have changed */
       if (new_val != Div_n_max[chan])
       {
          /* reset the divide by n counters */
          Div_n_max[chan] = new_val;

          /* if the channel is now volume only or was volume only */
          if ((Div_n_cnt[chan] == 0) || (new_val == 0))
          {
             /* reset the counter (otherwise let it complete the previous) */
             Div_n_cnt[chan] = new_val;
          }
       }
    }
}


/*****************************************************************************/
/* Module:  Tia_process_2()                                                  */
/* Purpose: To fill the output buffer with the sound output based on the     */
/*          tia chip parameters.  This routine has not been optimized.       */
/*          Though it is not used by the program, I've left it for reference.*/
/*                                                                           */
/* Author:  Ron Fries                                                        */
/* Date:    September 10, 1996                                               */
/*                                                                           */
/* Inputs:  *buffer - pointer to the buffer where the audio output will      */
/*                    be placed                                              */
/*          n - size of the playback buffer                                  */
/*                                                                           */
/* Outputs: the buffer will be filled with n bytes of audio - no return val  */
/*                                                                           */
/*****************************************************************************/

void Tia_process_2 (register unsigned char *buffer, register uint16 n)
{
    register uint8 chan;

    /* loop until the buffer is filled */
    while (n)
    {
       /* loop through the channels */
       for (chan = CHAN1; chan <= CHAN2; chan++)
       {
          /* NOTE: this routine intentionally does not count down to zero */
          /* since 0 is used as a special case - no clock */

          /* if the divide by N counter can count down */
          if (Div_n_cnt[chan] > 1)
          {
             /* decrement and loop */
             Div_n_cnt[chan]--;
          }
          /* otherwise if we've reached the bottom */
          else if (Div_n_cnt[chan] == 1)
          {
             /* reset the counter */
             Div_n_cnt[chan] = Div_n_max[chan];

             /* the P5 counter has multiple uses, so we inc it here */
             P5[chan]++;
             if (P5[chan] == POLY5_SIZE)
                P5[chan] = 0;

             /* check clock modifier for clock tick */

             /* if we're using pure tones OR
                   we're using DIV31 and the DIV31 bit is set OR
                   we're using POLY5 and the POLY5 bit is set */
             if  (((AUDC[chan] & 0x02) == 0) ||
                 (((AUDC[chan] & 0x01) == 0) && Div31[P5[chan]]) ||
                 (((AUDC[chan] & 0x01) == 1) &&  Bit5[P5[chan]]))
             {
                if (AUDC[chan] & 0x04)       /* pure modified clock selected */
                {
                   if (Outvol[chan])         /* if the output was set */
                      Outvol[chan] = 0;      /* turn it off */
                   else
                      Outvol[chan] = AUDV[chan];   /* else turn it on */
                }
                else if (AUDC[chan] & 0x08)  /* check for p5/p9 */
                {
                   if (AUDC[chan] == POLY9)  /* check for poly9 */
                   {
                      /* inc the poly9 counter */
                      P9[chan]++;
                      if (P9[chan] == POLY9_SIZE)
                         P9[chan] = 0;

                      if (Bit9[P9[chan]])    /* if poly9 bit is set */
                         Outvol[chan] = AUDV[chan];
                      else
                         Outvol[chan] = 0;
                   }
                   else                      /* must be poly5 */
                   {
                      if (Bit5[P5[chan]])
                         Outvol[chan] = AUDV[chan];
                      else
                         Outvol[chan] = 0;
                   }
                }
                else  /* poly4 is the only remaining option */
                {
                   /* inc the poly4 counter */
                   P4[chan]++;
                   if (P4[chan] == POLY4_SIZE)
                      P4[chan] = 0;

                   if (Bit4[P4[chan]])
                      Outvol[chan] = AUDV[chan];
                   else
                      Outvol[chan] = 0;
                }
             }
          }
       }

       /* decrement the sample counter - value is 256 since the lower
          byte contains the fractional part */
       Samp_n_cnt -= 256;

       /* if the count down has reached zero */
       if (Samp_n_cnt < 256)
       {
          /* adjust the sample counter */
          Samp_n_cnt += Samp_n_max;

          /* calculate the latest output value and place in buffer */
          *(buffer++) = Outvol[0] + Outvol[1];

          /* and indicate one less byte to process */
          n--;
       }
    }
}


/*****************************************************************************/
/* Module:  Tia_process()                                                    */
/* Purpose: To fill the output buffer with the sound output based on the     */
/*          tia chip parameters.  This routine has been optimized.           */
/*                                                                           */
/* Author:  Ron Fries                                                        */
/* Date:    September 10, 1996                                               */
/*                                                                           */
/* Inputs:  *buffer - pointer to the buffer where the audio output will      */
/*                    be placed                                              */
/*          n - size of the playback buffer                                  */
/*                                                                           */
/* Outputs: the buffer will be filled with n bytes of audio - no return val  */
/*                                                                           */
/*****************************************************************************/

void Tia_process (register unsigned char *buffer, register uint16 n)
{
    register uint8 audc0,audv0,audc1,audv1;
    register uint8 div_n_cnt0,div_n_cnt1;
    register uint8 p5_0, p5_1,outvol_0,outvol_1;

    audc0 = AUDC[0];
    audv0 = AUDV[0];
    audc1 = AUDC[1];
    audv1 = AUDV[1];

    /* make temporary local copy */
    p5_0 = P5[0];
    p5_1 = P5[1];
    outvol_0 = Outvol[0];
    outvol_1 = Outvol[1];
    div_n_cnt0 = Div_n_cnt[0];
    div_n_cnt1 = Div_n_cnt[1];

    /* loop until the buffer is filled */
    while (n)
    {
       /* Process channel 0 */
       if (div_n_cnt0 > 1)
       {
          div_n_cnt0--;
       }
       else if (div_n_cnt0 == 1)
       {
          div_n_cnt0 = Div_n_max[0];

          /* the P5 counter has multiple uses, so we inc it here */
          p5_0++;
          if (p5_0 == POLY5_SIZE)
             p5_0 = 0;

          /* check clock modifier for clock tick */
          if  (((audc0 & 0x02) == 0) ||
              (((audc0 & 0x01) == 0) && Div31[p5_0]) ||
              (((audc0 & 0x01) == 1) &&  Bit5[p5_0]))
          {
             if (audc0 & 0x04)       /* pure modified clock selected */
             {
                if (outvol_0)        /* if the output was set */
                   outvol_0 = 0;     /* turn it off */
                else
                   outvol_0 = audv0; /* else turn it on */
             }
             else if (audc0 & 0x08)    /* check for p5/p9 */
             {
                if (audc0 == POLY9)    /* check for poly9 */
                {
                   /* inc the poly9 counter */
                   P9[0]++;
                   if (P9[0] == POLY9_SIZE)
                      P9[0] = 0;

                   if (Bit9[P9[0]])
                      outvol_0 = audv0;
                   else
                      outvol_0 = 0;
                }
                else                        /* must be poly5 */
                {
                   if (Bit5[p5_0])
                      outvol_0 = audv0;
                   else
                      outvol_0 = 0;
                }
             }
             else  /* poly4 is the only remaining option */
             {
                /* inc the poly4 counter */
                P4[0]++;
                if (P4[0] == POLY4_SIZE)
                   P4[0] = 0;

                if (Bit4[P4[0]])
                   outvol_0 = audv0;
                else
                   outvol_0 = 0;
             }
          }
       }


       /* Process channel 1 */
       if (div_n_cnt1 > 1)
       {
          div_n_cnt1--;
       }
       else if (div_n_cnt1 == 1)
       {
          div_n_cnt1 = Div_n_max[1];

          /* the P5 counter has multiple uses, so we inc it here */
          p5_1++;
          if (p5_1 == POLY5_SIZE)
             p5_1 = 0;

          /* check clock modifier for clock tick */
          if  (((audc1 & 0x02) == 0) ||
              (((audc1 & 0x01) == 0) && Div31[p5_1]) ||
              (((audc1 & 0x01) == 1) &&  Bit5[p5_1]))
          {
             if (audc1 & 0x04)       /* pure modified clock selected */
             {
                if (outvol_1)        /* if the output was set */
                   outvol_1 = 0;     /* turn it off */
                else
                   outvol_1 = audv1; /* else turn it on */
             }
             else if (audc1 & 0x08)    /* check for p5/p9 */
             {
                if (audc1 == POLY9)    /* check for poly9 */
                {
                   /* inc the poly9 counter */
                   P9[1]++;
                   if (P9[1] == POLY9_SIZE)
                      P9[1] = 0;

                   if (Bit9[P9[1]])
                      outvol_1 = audv1;
                   else
                      outvol_1 = 0;
                }
                else                        /* must be poly5 */
                {
                   if (Bit5[p5_1])
                      outvol_1 = audv1;
                   else
                      outvol_1 = 0;
                }
             }
             else  /* poly4 is the only remaining option */
             {
                /* inc the poly4 counter */
                P4[1]++;
                if (P4[1] == POLY4_SIZE)
                   P4[1] = 0;

                if (Bit4[P4[1]])
                   outvol_1 = audv1;
                else
                   outvol_1 = 0;
             }
          }
       }

       /* decrement the sample counter - value is 256 since the lower
          byte contains the fractional part */
       Samp_n_cnt -= 256;

       /* if the count down has reached zero */
       if (Samp_n_cnt < 256)
       {
          /* adjust the sample counter */
          Samp_n_cnt += Samp_n_max;

          /* calculate the latest output value and place in buffer */
#ifdef MAC_OSX		  
          *(buffer++) = (outvol_0 + outvol_1)/2 + 128;
#else
          *(buffer++) = outvol_0 + outvol_1;
#endif		  
          /* and indicate one less byte to process */
          n--;
       }
    }

    /* save for next round */
    P5[0] = p5_0;
    P5[1] = p5_1;
    Outvol[0] = outvol_0;
    Outvol[1] = outvol_1;
    Div_n_cnt[0] = div_n_cnt0;
    Div_n_cnt[1] = div_n_cnt1;

}


/*****************************************************************************/
/* Module:  Tia_get_registers()                                              */
/* Purpose: Returns the 6 TIA sound registers for use in state               */
/*          loading and saving.                                              */
/*                                                                           */
/* Author:  Stephen Anthony                                                  */
/* Date:    October 31, 2002                                                 */
/*                                                                           */
/* Inputs:  reg .. reg6 - pointers to the variables where the registers      */
/*          will be placed                                                   */
/*                                                                           */
/*****************************************************************************/

void Tia_get_registers (unsigned char *reg1, unsigned char *reg2, unsigned char *reg3,
                        unsigned char *reg4, unsigned char *reg5, unsigned char *reg6)
{
    *reg1 = AUDC[0];
    *reg2 = AUDC[1];
    *reg3 = AUDF[0];
    *reg4 = AUDF[1];
    *reg5 = AUDV[0];
    *reg6 = AUDV[1];
}


/*****************************************************************************/
/* Module:  Tia_set_registers()                                              */
/* Purpose: Sets the 6 TIA sound registers for use in state                  */
/*          loading and saving.                                              */
/*                                                                           */
/* Author:  Stephen Anthony                                                  */
/* Date:    October 31, 2002                                                 */
/*                                                                           */
/* Inputs:  reg .. reg6 - the registers to be set                            */
/*                                                                           */
/*****************************************************************************/

void Tia_set_registers (unsigned char reg1, unsigned char reg2, unsigned char reg3,
                        unsigned char reg4, unsigned char reg5, unsigned char reg6)
{
    AUDC[0] = reg1;
    AUDC[1] = reg2;
    AUDF[0] = reg3;
    AUDF[1] = reg4;
    AUDV[0] = reg5;
    AUDV[1] = reg6;
}

#ifdef __cplusplus
}
#endif
;*
;* z26 sound stuff
;*
;* it's up to the *operating system* to empty the sound queue
;* it's up to the z26 core to fill it up
;*
;
; z26 is Copyright 1997-2003 by John Saeger and is a derived work with many
; contributors.	 z26 is released subject to the terms and conditions of the 
; GNU General Public License Version 2 (GPL).  z26 comes with no warranty.
; Please see COPYING.TXT for details.


[section .data]

;; ;_SQ_Max		dd	3072*3
;; SQ_Start:  ; byte		; <-- start clearing here
;; 
;; SQ_Input	dd  0	 ;  point to next avail byte for storing
;; SQ_Output	dd  0	 ;  point to next avail byte for fetching
;; SQ_Count	dd  0
;; SQ_Top		dd  0
;; 
;; ;SQ_MAX = 3072*3			; 3072, 1024
;; 
;; ;_SoundQ	times 65000 db 0
;; SQ_End:  ; byte			; <-- finish clearing here

[section .code]

;*
;* Initialize sound queue
;*

;; Init_SoundQ:
;; 	clear_mem  SQ_Start,  SQ_End
;; 
;;  	mov	dword [SQ_Input], SoundQ ; initialize sound queue stuff
;;  	mov	dword [SQ_Output], SoundQ
;;  	mov	dword [SQ_Count],0
;; 	mov	eax, SoundQ
;; 	inc	eax
;;  	add	eax,dword [SQ_Max]
;;  	mov	dword [SQ_Top],eax
;; ;	mov	[SQ_Top], SoundQ + SQ_MAX + 1
;; 
;; 	ret

;*
;* routine to get status of sound queue
;*
;* returns:
;*
;*    -1 if sound queue full and no room for more output
;*     0 if there's too much room      (less than 1/2 full)
;*     1 if there's just enough room   (more than 1/2 full)
;*

SQ_Test:
 	cmp	byte [quiet],0 ; doing sound at all?
	jnz near SQ_Just_Right		 ;    no, pretend queue is *just right*

 	mov	eax,dword [SQ_Max]
 	cmp	dword [SQ_Count],eax ; sound queue already full?
	jae near SQ_Full			 ;    yes, don't queue anything
	shr	eax,1
 	cmp	dword [SQ_Count],eax ; less than 1/2 full?
	jbe near SQ_Empty		 ;    yes

SQ_Just_Right:
	mov	eax,1			 ;    no
	ret

SQ_Empty:
	mov	eax,0
	ret

SQ_Full:
	mov	eax,-1
	ret


;*
;* routine to put byte in al in the sound queue
;* always make sure SQ_Count < SQ_MAX before calling
;*

;;; extern debug_sound_byte ;;;DEBUG

SQ_Store:

	;;; pushad ;;; DEBUG
	;;; push eax ;;; DEBUG
	;;; call debug_sound_byte ;;; DEBUG
	;;; add esp, 4
	;;; popad ;;;DEBUG

	;;; what goes into the queue is what comes out - error in tiasnd, not here

 	cmp	byte [quiet],0 ; doing sound at all?
	jnz near SQS_skip_store		 ;    no, don't store sound byte

;	pushad
;	call	srv_lock_audio
;	popad

 	mov	esi,dword [SQ_Input]
 	mov	byte [esi],al
	inc	esi
 	inc	dword [SQ_Count]
 	cmp	esi,dword [SQ_Top]
	jb near SQS_done
	mov	esi, SoundQ
SQS_done:
 	mov	dword [SQ_Input],esi

;	pushad
;	call	srv_unlock_audio
;	popad

SQS_skip_store:
	ret


;*
;* routine to put the sound in the sound buffer
;*
global Tia_process

Tia_process:
	pushad
 	mov	eax,dword [bufsize]
 	cmp	dword [SQ_Count],eax ; enough sound available?
	ja near Sound_Enough		 ;    yes

	pushad				 ;    no, make sure there's enough
	call	QueueSoundBytes
	popad

Sound_Enough:
 	mov	ecx,dword [bufsize] ; # of bytes

 	mov	edi,dword [DMABuf]
 	mov	esi,dword [SQ_Output]

SoundFillLoop:
 	mov	al,byte [esi]
	inc	esi
 	cmp	esi,dword [SQ_Top]
	jb near SF_done
	mov	esi, SoundQ
SF_done: mov	byte [edi],al ; put it in soundblaster buffer
	inc	edi
	dec	ecx			 ;  more room in soundblaster buffer?
	jnz near SoundFillLoop		 ;    yes, get more

 	mov	eax,dword [bufsize]
 	sub	dword [SQ_Count],eax
 	mov	dword [SQ_Output],esi
	
SoundBufferBail:
	popad
	ret



;*
;* put a byte in the sound queue
;*

QueueSoundByte:
	call	SQ_Test
	cmp	eax,-1			 ;  sound queue already full?
	jne near QSB_room		 ;    no, there's room

;        cmp     [NoRetrace],-1         ; synchronizing to monitor?
         cmp     byte [SyncToSoundBuffer],1 ; syncronizing to sound buffer?
        jne near SoundQueueFull           ;    no, don't queue anything

	pushad				 ;    no, wait for room
	call	srv_Events
	popad
	jmp	QueueSoundByte

QSB_room:
	call	KidVid_Sound_Byte	 ;    no, get kidvid sample
	push	eax
	call	TIA_Sound_Byte		 ;  get TIA sample
	pop	ebx			 ;  kidvid sample

	add     eax,ebx			 ;  mix the samples
	shr     eax,1

;;; 20040416 bkw: DIRTY HACK ALERT!
;;; Something's wrong with the TIA code, causing the sound output to
;;; be too quiet for humans to hear (at least on Linux). Blindly
;;; adding 0x70 to the sound byte makes it audible (though probably
;;; wildly inaccurate). For one thing, we only ever get 3 values out
;;; of TIA_Sound_Byte (that's 0x00, 0x10, and 0x20, with 0x00 occurring
;;; only at the beginning of emulation!). That means we're effectively
;;; working at 1.5 bits of sample resolution!
;;; Until I can fix whatever's wrong with TIA_Sound_Byte, this will
;;; at least let people hear something. Note that I have NO IDEA
;;; what this does to the Windows version.
	;;;	  add eax, 70h

	call	SQ_Store		 ;  put it in the sound queue

SoundQueueFull:
	ret

;*
;* put sound bytes into buffer
;* called once per scan line
;*

QueueSoundBytes:  ;  proc near


SoundQueueLoop:
	call	QueueSoundByte
	call	QueueSoundByte

	call	SQ_Test
	cmp	eax,0			 ;  sound queue at least 1/2 full?
	je near SoundQueueLoop		 ;    no

        ret

; QueueSoundBytes endp



; tiasnd.asm -- z26 sound generation routines
;               based on TIASound (c) 1996-1997 by Ron Fries
;		please see the end of this file for Ron's banner

; z26 is Copyright 1997-2003 by John Saeger and is a derived work with many
; contributors.  z26 is released subject to the terms and conditions of the 
; GNU General Public License Version 2 (GPL).  z26 comes with no warranty.
; Please see COPYING.TXT for details.

; 04-08-98 -- first release
; 04-19-99 -- added sound queue
; 07-27-02 -- 32-bit
; 12-08-03 -- polynomials adapted to Adam Wozniak's description

[section .data]

sreg	dd	1		 ;  initialize shift register to non-zero val

; Initialze the bit patterns for the polynomials.

; The 4bit and 5bit patterns are the identical ones used in the tia chip.
; Though the patterns could be packed with 8 bits per byte, using only a
; single bit per byte keeps the math simple, which is important for
; efficient processing.

;Bit4    db      1,1,0,1,1,1,0,0,0,0,1,0,1,0,0
;Bit5    db      0,0,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,1,0,1,1,1,0,1,0,1,0,0,0,0,1
Bit4    db      0,1,1,0,0,1,0,1,0,0,0,0,1,1,1
Bit5    db      0,0,0,0,0,1,1,1,0,0,1,0,0,0,1,0,1,0,1,1,1,1,0,1,1,0,1,0,0,1,1

; 1 = toggle output in 5 bit poly - used when poly5 clocks other outputs
Bit5T   db      1,0,0,0,0,1,0,0,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,1,0,1,1,1,0,1,0

; The 'Div by 31' counter is treated as another polynomial because of
; the way it operates.  It does not have a 50% duty cycle, but instead
; has a 13:18 ratio (of course, 13+18 = 31).  This could also be
; implemented by using counters.

Div31   db      0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0

Div6    db      0,1,0,0,1,0

; The sample output is treated as another divide by N counter.
; For better accuracy, the Samp_n_cnt has a fixed binary decimal point
; which has 8 binary digits to the right of the decimal point.

Samp_n_cnt	dd	0
Samp_n_max	dd	0
TS_Start:  ; byte			; <-- start clearing here

D6              db      0,0
P4              db      0,0
P5		db	0,0
AUDC		db	0,0
AUDF		db	0,0
AUDV		db	0,0
Outvol		db	0,0
Div_n_cnt 	db	0,0
Div_n_max 	db	0,0
TS_End:  ; byte			; <-- finish clearing here

P9_sreg		dd	1,1


new_val		dd	0

prev_sample	dd	0
next_sample	db	0
AUDC_Jumptab:  ; dword	; HEX  D3 D2 D1 D0    Clock Source    Clock Modifier    Source Pattern
				 ;  --- -------------  --------------  ----------------  ----------------
	dd	TSB_Ch0done	 ;   0    0  0  0  0    3.58 MHz/114 ->  none  (pure)  ->      none
	dd	TSB_Poly4	 ;   1    0  0  0  1    3.58 MHz/114 ->  none  (pure)  ->   4-bit poly
	dd	TSB_Div31_Poly4	 ;   2    0  0  1  0    3.58 MHz/114 ->  divide by 31  ->   4-bit poly
	dd	TSB_Poly5_Poly4	 ;   3    0  0  1  1    3.58 MHz/114 ->   5-bit poly   ->   4-bit poly
	dd	TSB_Pure	 ;   4    0  1  0  0    3.58 MHz/114 ->  none  (pure)  ->   pure  (~Q)
	dd	TSB_Pure	 ;   5    0  1  0  1    3.58 MHz/114 ->  none  (pure)  ->   pure  (~Q)
	dd	TSB_Div31_Pure	 ;   6    0  1  1  0    3.58 MHz/114 ->  divide by 31  ->   pure  (~Q)
;        dd      TSB_Poly5_Pure  ;  7    0  1  1  1    3.58 MHz/114 ->   5-bit poly   ->   pure  (~Q)
        dd      TSB_Poly5        ;   7    0  1  1  1    3.58 MHz/114 ->  none  (pure)  ->   5-bit poly
	dd	TSB_Poly9	 ;   8    1  0  0  0    3.58 MHz/114 ->  none  (pure)  ->   9-bit poly
	dd	TSB_Poly5	 ;   9    1  0  0  1    3.58 MHz/114 ->  none  (pure)  ->   5-bit poly
;        dd      TSB_Div31_Poly5 ;  A    1  0  1  0    3.58 MHz/114 ->  divide by 31  ->   5-bit poly
        dd      TSB_Div31_Pure   ;   A    1  0  1  0    3.58 MHz/114 ->  divide by 31  ->   pure  (~Q)
	dd	TSB_Poly5_Poly5	 ;   B    1  0  1  1    3.58 MHz/114 ->   5-bit poly   ->   5-bit poly
        dd      TSB_Div6_Pure    ;   C    1  1  0  0    3.58 MHz/114 ->  divide by 6   ->   pure  (~Q)
        dd      TSB_Div6_Pure    ;   D    1  1  0  1    3.58 MHz/114 ->  divide by 6   ->   pure  (~Q)
        dd      TSB_Div31_Div6   ;   E    1  1  1  0    3.58 MHz/114 ->  divide by 31  ->   divide by 6
        dd      TSB_Poly5_Div6   ;   F    1  1  1  1    3.58 MHz/114 ->   5-bit poly   ->   divide by 6

[section .code]
;*
;* handle the power-up initialization functions
;* these functions should only be executed on a cold-restart
;*

Init_Tiasnd:

; calculate the sample 'divide by N' value based on the playback freq

	mov	eax,31400
	shl	eax,8
	mov	ebx,31400
	xor	edx,edx
	div	ebx			 ;  ax = (_sample_freq<<8)/_playback_freq
 	mov	dword [Samp_n_max],eax
 	mov	dword [Samp_n_cnt],0

	clear_mem  TS_Start,  TS_End

 	mov	dword [P9_sreg],1
 	mov	dword [P9_sreg+4],1

	ret	


;*
;* routine to get kid-vid sound byte
;*

KidVid_Sound_Byte:

         test    byte [KidVid],0ffH
        jz near NoSamplePlay

	pushad
        call    kv_GetNextSampleByte
	popad
 	movzx	eax,byte [SampleByte]

NoSamplePlay:

	ret


;*
;* generate a sequence of pseudo-random bits 511 bits long
;* by emulating a 9-bit shift register with feedback taps at
;* positions 5 and 9.
;*

ShiftRegister9:
 	mov	eax,dword [sreg]
	mov	edx,eax
	and	eax,1		 ;  bit 9 (register output & return val)
	and	edx,16
	shr	edx,4		 ;  position bit 5 at bottom
	xor	edx,eax		 ;  xor with bit 9 = new bit 1
 	shr	dword [sreg],1 ; shift the register
	shl	edx,8		 ;  position the feedback bit
 	or	dword [sreg],edx ; or it in
	ret	

;*
;* update TIA sound registers
;*

H_AUDC0:
 	mov	al,byte [WByte]
	and	al,15
 	mov	byte [AUDC],al
	jmp	UTS_Chan0

H_AUDC1:
 	mov	al,byte [WByte]
	and	al,15
 	mov	byte [AUDC+1],al
	jmp	UTS_Chan1

H_AUDF0:
 	mov	al,byte [WByte]
	and	al,31
 	mov	byte [AUDF],al
	jmp	UTS_Chan0

H_AUDF1:
 	mov	al,byte [WByte]
	and	al,31
 	mov	byte [AUDF+1],al
	jmp	UTS_Chan1

H_AUDV0:
 	mov	al,byte [WByte]
	and	al,15
	shl	al,3
 	mov	byte [AUDV],al

UTS_Chan0:
	xor	ebx,ebx
	jmp	UTS_RegSet

H_AUDV1:
 	mov	al,byte [WByte]
	and	al,15
	shl	al,3
 	mov	byte [AUDV+1],al

UTS_Chan1:
	mov	ebx,1

; the output value has changed

UTS_RegSet:
 	cmp	byte [AUDC+ebx],0 ; AUDC value of zero is a special case
	jne near UTS_rs1
 	mov	dword [new_val],0 ; indicate clock is zero so ...
 	mov	al,byte [AUDV+ebx] ; ... no processing will occur
 	mov	byte [Outvol+ebx],al ; and set output to selected volume

	jmp	UTS_rs2

UTS_rs1:
 	movzx	eax,byte [AUDF+ebx] ; calc the 'div by N' value
	inc	eax
 	mov	dword [new_val],eax
;        mov     al,[AUDC+ebx]
;        and     al,12
;        cmp     al,12
;        jne near UTS_rs2                 ; if bits 2 and 3 are set ...
;        mov     eax,[_new_val]          ; ... multiply by three
;        add     eax,eax
;        add     [_new_val],eax

UTS_rs2: movzx	eax,byte [Div_n_max+ebx] ; only reset channels that have changed
 	cmp	eax,dword [new_val]
	je near UTS_Done
 	mov	al,byte [new_val]
 	mov	byte [Div_n_max+ebx],al ; reset 'div by N' counters
 	cmp	byte [Div_n_cnt+ebx],0
	je near UTS_rs3			 ;  if channel is now volume only ...
 	cmp	dword [new_val],0
	jne near UTS_Done		 ;  ... or was volume only ...

UTS_rs3: mov	al,byte [new_val]
 	mov	byte [Div_n_cnt+ebx],al ; ... reset the counter 
				 	 ;      (otherwise complete previous)

UTS_Done:
	ret	

;*
;* generate a sound byte based on the TIA chip parameters
;*

;;; converted macro:
%macro inc_mod 2
; local ; done

 	inc	byte [%1] ;;; GUESSED dword
 	cmp	byte [%1],%2 ;;; GUESSED dword
	jne near %%done
 	mov	byte [%1],0 ;;; GUESSED dword
%%done:
%endmacro



TIA_Sound_Byte:

TSB_ProcessLoop:

	xor	edi,edi			 ;  process channel 0 first

 	cmp	byte [Pitfall2],0 ; doing Pitfall2?
	jz near TSB_ProcessChannel	 ;    no
	inc	edi			 ;    yes, only do channel 1

TSB_ProcessChannel:
 	cmp	byte [Div_n_cnt + edi],1 ; if div by N counter can count down ...
	jb near TSB_Ch0done		 ;    zero is special case, means AUDC==0 -- fast exit
	je near TSB_1
 	dec	byte [Div_n_cnt + edi] ; ... decrement ...
	jmp	TSB_Ch0done		 ;  ... and do next channel

TSB_1: mov	al,byte [Div_n_max + edi] ; otherwise reset the counter and process this channel
 	mov	byte [Div_n_cnt + edi],al

 	movzx	esi,byte [AUDC + edi] ; AUDC = index into branch table

	inc_mod  P5+edi,31 ;  P5 channel has multiple uses (Div31 & P5), inc it here
 	movzx	ebx,byte [P5 + edi]

 	jmp	dword [AUDC_Jumptab + esi*4] ; process sound changes based on AUDC


TSB_Div6_Pure:
        inc_mod  D6+edi,6 ;  inc Div6 counter
         movzx   ebx,byte [D6 + edi]
         cmp     byte [Div6+ebx],0 ; if div 6 bit set ...
	jnz near TSB_Pure		 ;  ... do pure
	jmp	TSB_Ch0done

TSB_Div31_Div6:
 	cmp	byte [Div31+ebx],0 ; if div 31 bit set ...
        jnz near TSB_Div6_Pure            ;  ... do div 6
        jmp     TSB_Ch0done

TSB_Div31_Pure:
 	cmp	byte [Div31+ebx],0 ; if div 31 bit set ...
	jnz near TSB_Pure		 ;  ... do pure
	jmp	TSB_Ch0done

;TSB_Poly5_Pure:
;        cmp     [Bit5+ebx],0             ; if div 5 bit set ...
;        jz near TSB_Ch0done             ; ... do pure

TSB_Pure:
 	cmp	byte [Outvol + edi],0 ; toggle the output
	je near TSB_VolumeOn
	jmp	TSB_VolumeOff


TSB_Poly9:	
 	mov	edx,dword [P9_sreg+4*edi]
 	mov	dword [sreg],edx ; set shift reg to this channel's shift reg
	call	ShiftRegister9		 ;  calculate next bit
 	mov	edx,dword [sreg] ; save shift reg to our channel
 	mov	dword [P9_sreg+4*edi],edx
	test	al,1			 ;  shift reg bit on?
	je near TSB_VolumeOff		 ;    no
	jmp	TSB_VolumeOn		 ;    yes


;TSB_Div31_Poly5:
;        cmp     [Div31+ebx],0            ; if div 31 bit set ...
;        jnz near TSB_Poly5               ; ... do Poly5
;        jmp     TSB_Ch0done

TSB_Poly5_Div6:
         cmp     byte [Bit5T+ebx],0 ; if Bit5 output changed ...
        jnz near TSB_Div6_Pure            ;  ... do Div 6
	jmp	TSB_Ch0done

TSB_Poly5_Poly5:
 	cmp	byte [Bit5+ebx],0 ; if Poly5 bit set ...
	jz near TSB_Ch0done		 ;  ... do Poly5

TSB_Poly5:
 	movzx	ebx,byte [P5 + edi] ; set the output bit
 	cmp	byte [Bit5+ebx],0
	je near TSB_VolumeOff
	jmp	TSB_VolumeOn


TSB_Div31_Poly4:
 	cmp	byte [Div31+ebx],0 ; if div 31 bit set ...
	jnz near TSB_Poly4		 ;  ... do Poly4
	jmp	TSB_Ch0done

TSB_Poly5_Poly4:                         ;  changed from Bit5 to Bit5T *EST*
         cmp     byte [Bit5T+ebx],0 ; if Poly5 bit set ...
	jz near TSB_Ch0done		 ;  ... do Poly4

TSB_Poly4:
	inc_mod  P4+edi,15 ;  inc P4 counter
 	movzx	ebx,byte [P4 + edi]

 	cmp	byte [Bit4+ebx],0 ; and set the output bit
	je near TSB_VolumeOff

TSB_VolumeOn:
 	mov	al,byte [AUDV + edi]
 	mov	byte [Outvol + edi],al
	jmp	TSB_Ch0done

TSB_VolumeOff:
 	mov	byte [Outvol + edi],0

TSB_Ch0done:
	inc	edi			 ;  to next channel
	cmp	edi,1			 ;  done ?
	jbe near TSB_ProcessChannel	 ;    not yet

 	sub	dword [Samp_n_cnt],256 ; decrement sample count
					 ;  (256 since lower byte is 
					 ;   fractional part)
 	cmp	dword [Samp_n_cnt],256 ; if count has reached zero ...
	jae near TSB_ProcessLoop
 	mov	eax,dword [Samp_n_max] ; ... adjust the sample counter
 	add	dword [Samp_n_cnt],eax

 	cmp	byte [Pitfall2],0 ; running Pitfall 2?
	jz near TSB_NotPitfall2		 ;    no

	call	Clock_Pitfall2		 ;    yes, clock P2 music clock (and build AUDV)
 	mov	al,byte [Outvol+1] ; channel 1
 	mov	ah,byte [P2_AUDV]
	and	ah,15
	shl	ah,3
	add	al,ah			 ;  add in Pitfall 2 AUDV byte
	jmp	TSB_Pitfall2_Done

TSB_NotPitfall2:
 	mov	al,byte [Outvol+0] ; not Pitfall 2, do normal mixing
 	add	al,byte [Outvol+1] ; sum the channels

TSB_Pitfall2_Done:
 	cmp	byte [GamePaused],0 ; if game paused
	jz near TSB_NoSilence
	mov	al,080h			 ;  fill buffer with silence
TSB_NoSilence:

 	test	byte [dsp],0ffh ; doing digital signal processing ?
	jz near TSB_ProcessDone		 ;    no, just store it
 	mov	byte [next_sample],al ; yes, take edge off square wave
	xor	eax,eax
 	mov	al,byte [next_sample]
 	add	eax,dword [prev_sample]
	shr	eax,1
 	mov	dword [prev_sample],eax ; dsp=2, scaled moving average

 	cmp	byte [dsp],1
	jne near TSB_ProcessDone
 	movzx	esi,byte [next_sample] ; dsp=1, simple moving average
 	mov	dword [prev_sample],esi

TSB_ProcessDone:
	and	eax,0ffh		 ;  return 32-bit sample
	ret	

; /*****************************************************************************/
; /*                                                                           */
; /*                 License Information and Copyright Notice                  */
; /*                 ========================================                  */
; /*                                                                           */
; /* TiaSound is Copyright(c) 1996 by Ron Fries                                */
; /*                                                                           */
; /* This library is free software; you can redistribute it and/or modify it   */
; /* under the terms of version 2 of the GNU Library General Public License    */
; /* as published by the Free Software Foundation.                             */
; /*                                                                           */
; /* 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 Library */
; /* General Public License for more details.                                  */
; /* To obtain a copy of the GNU Library General Public License, write to the  */
; /* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   */
; /*                                                                           */
; /* Any permitted reproduction of these routines, in whole or in part, must   */
; /* bear this legend.                                                         */
; /*                                                                           */
; /*****************************************************************************/



Current Thread