/*****************************************************************************/
/*                                                                           */
/*   VECTREX HP3000 Software Emulator              Copyright K.Wilkins 1996  */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*   Title      : AY-3-8192 Sound Chip emulation module                      */
/*                                                                           */
/*   File Name  : SOUNDEMU.C                                                 */
/*                                                                           */
/*   Author     : Keith Wilkins  (ADLIB Sound code by Marcel De Kogel)       */
/*                                                                           */
/*   Version    : 1.00                                                       */
/*                                                                           */
/*   Desciption : This file contains the functions that emulate the AY-3-8192*/
/*                Chip. Any emulation functions calling the read write       */
/*                functions will cause an immediate update to the chip and   */
/*                and all associated variables.                              */
/*                                                                           */
/*   Functions  : fSoundInit()      - Init the AY-3-8192 Sound chip          */
/*                fSoundTick()      - Emulate one time tick of the chip      */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*   Revision History:                                                       */
/*                                                                           */
/*   Version    Date    Who  Description of changes                          */
/*   -------    ----    ---  ----------------------                          */
/*                                                                           */
/*    0.01    14/03/93  K.W  Creation of empty file and descriptions         */
/*    1.00    05/08/96  K.W  Public release of DVE V1.0                      */
/*                                                                           */
/*****************************************************************************/

#ifdef _NO_DEBUG_INFORMATION_

#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include <standard.h>
#include <tools.h>

#include "vdebug.h"
#include "vectrex.h"
#include "emu6809.h"
#include "emu6522.h"
#include "joystick.h"
#include "keyboard.h"
#include "verror.h"
#include "sequence.h"
#include "sound.h"
#include "adlibsnd.h"

// Sound variables
#define NUM_CHANNELS            18
#define PSG_VOICE               0
#define SCC_VOICE               9
#define PSG_VOICE2              14

static T_INT     adlib_is_init=0;
static T_UCHAR   kbf[NUM_CHANNELS];                 /* KEY_BLOCK_F-NUMBER */
static T_UWORD   ChannelFreq[NUM_CHANNELS];
static T_UCHAR   tone_on[NUM_CHANNELS];
static T_UCHAR   key_on[NUM_CHANNELS];
static T_UWORD   FMPoort=0x388;
static T_INT     sound_on=T_TRUE;
static T_INT     sound_is_on=T_TRUE;
static T_UCHAR   vol_is_nul[NUM_CHANNELS];
static T_PUCHAR  channel_voice[NUM_CHANNELS];
static T_INT     OPL3=T_TRUE;


T_SCHAR digidata=0;

static T_UWORD OpAd[]=
{ 0x000,0x001,0x002,0x008,0x009,0x00A,0x010,0x011,0x012,
  0x100,0x101,0x102,0x108,0x109,0x10A,0x110,0x111,0x112 };

static T_UWORD ChanAd[]=
{ 0x000,0x001,0x002,0x003,0x004,0x005,0x006,0x007,0x008,
  0x100,0x101,0x102,0x103,0x104,0x105,0x106,0x107,0x108 };

static T_UCHAR PSG_Melodic1[11]=
{
  0x23,0x21,    /* AM_VIB_EG-TYP_KSR_MULTI */
  0x1E,0x08,    /* KSL_TL                  */
  0xFF,0xFF,    /* AR_DR                   */
  0x08,0x08,    /* SL_RR                   */
  0x00,0x00,    /* WS                      */
  0x0E          /* FB_FM                   */
};

static T_UCHAR PSG_Melodic2[11]=
{
  0x21,0x21,    /* AM_VIB_EG-TYP_KSR_MULTI */
  0x1E,0x0A,    /* KSL_TL                  */
  0xFF,0xFF,    /* AR_DR                   */
  0x08,0x08,    /* SL_RR                   */
  0x00,0x00,    /* WS                      */
  0x0F          /* FB_FM                   */
};

static T_UCHAR PSG_Noise[11]=
{
  0x2E,0x2E,    /* AM_VIB_EG-TYP_KSR_MULTI */
  0x40,0x00,    /* KSL_TL                  */
  0xF0,0xF0,    /* AR_DR                   */
  0xFA,0xFA,    /* SL_RR                   */
  0x00,0x03,    /* WS                      */
  0x0E          /* FB_FM                   */
};

static T_UWORD PSG_NoiseFreqs[32]=
{
 0,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,
 930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,930,
};

static T_UWORD FMFreqs[] =
{
#include "FMFreqs.h"
};
T_UWORD sb_port;          // sb base port
T_UWORD sb_ver;           // DSP version number
T_UWORD   PSG_Reg[15];
static digi_sound_possible=T_FALSE;

// Private functions
static T_VOID    WriteFM_OPL2(T_UCHAR value,T_UCHAR index);
static T_VOID    WriteFM_OPL3_1(T_UCHAR value,T_UCHAR index);
static T_VOID    WriteFM_OPL3_2(T_UCHAR value,T_UCHAR index);
static T_VOID    WriteFM(T_UCHAR value,T_UWORD index);
static T_UCHAR   ReadFM(T_VOID);
static T_VOID    Adlib_ChannelOff(T_UCHAR channel);
static T_VOID    Adlib_ChannelOn(T_UCHAR channel);
static T_VOID    WriteVolume(T_UCHAR vol,T_UCHAR reg,T_UWORD index);
static T_VOID    Adlib_SetVolume(T_UCHAR channel,T_UCHAR vol);
static T_VOID    Adlib_SetVibrato(T_UCHAR channel);
static T_VOID    Adlib_ResetVibrato(T_UCHAR channel);
static T_VOID    Adlib_SetFreq(T_UCHAR channel,T_UWORD freq);
static T_VOID    Adlib_SetVoice(T_PUCHAR voice,T_SWORD channel);
static T_VOID    Adlib_SetVoices(T_VOID);
static T_INT     Reset_Adlib(T_VOID);
static T_VOID    Adlib_ToneOn(T_UCHAR channel,T_UCHAR on);
static T_UWORD   GetPSGVolume(T_UCHAR v);

T_INT   SB_WaitDSPWrite(T_VOID);
T_INT   SB_WaitDSPRead(T_VOID);
T_INT   SB_WriteDSP(T_UCHAR data);
T_UWORD SB_ReadDSP(T_VOID);
T_INT   SB_Ping(T_VOID);
T_UWORD SB_GetDSPVersion(T_VOID);
T_VOID  SB_ResetDSP(T_VOID);
T_INT   SB_IsThere(T_VOID);
T_INT   SB_Init(T_VOID);
T_VOID  SB_Exit(T_VOID);


//
//
// PRIVATE FUNCTIONS
//
//
static T_VOID WriteFM_OPL2(T_UCHAR value,T_UCHAR index)
{
 T_SWORD i;
 outp(FMPoort,index);
 for(i=0;i<6;++i)
  inp(FMPoort);
 outp(FMPoort+1,value);
 for(i=0;i<35;++i)
  inp(FMPoort);
}

static T_VOID WriteFM_OPL3_1(T_UCHAR value,T_UCHAR index)
{
 outp(FMPoort,index);
 inp(FMPoort);
 outp(FMPoort+1,value);
 inp(FMPoort);
 inp(FMPoort);
}

static T_VOID WriteFM_OPL3_2(T_UCHAR value,T_UCHAR index)
{
 outp(FMPoort+2,index);
 inp(FMPoort);
 outp(FMPoort+3,value);
 inp(FMPoort);
 inp(FMPoort);
}

static T_VOID WriteFM(T_UCHAR value,T_UWORD index)
{
 if(OPL3)
 {
  if (index&0x100)
   WriteFM_OPL3_2 (value,(T_UCHAR)index);
  else
   WriteFM_OPL3_1 (value,(T_UCHAR)index);
 }
 else
 {
 if ((index&0x100)==0)
  WriteFM_OPL2(value,(T_UCHAR)index);
 }
}

static T_UCHAR ReadFM(T_VOID)
{
 return inp(FMPoort);
}

static T_VOID Adlib_ChannelOff(T_UCHAR channel)
{
 WriteFM(kbf[channel],0xB0+ChanAd[channel]);
 key_on[channel]=0;
}

static T_VOID Adlib_ChannelOn(T_UCHAR channel)
{
 if (!sound_on)
  return;
 if (vol_is_nul[channel])
  return;
 WriteFM(kbf[channel]|32,0xB0+ChanAd[channel]);
 key_on[channel]=1;
}

static T_VOID WriteVolume(T_UCHAR vol,T_UCHAR reg,T_UWORD index)
{
 T_UWORD tl;
 tl=63-(reg&63);
 tl*=(64-2*(15-(vol&15)));
 tl/=64;
 if(tl>=64) tl=63;
 tl=63-tl;
 WriteFM((reg&0xC0)|tl,index);
}

static T_VOID Adlib_SetVolume(T_UCHAR channel,T_UCHAR vol)
{
 if (vol==0)
 {
  vol_is_nul[channel]=1;
  if (tone_on[channel])
   Adlib_ChannelOff(channel);
  return;
 }
 if (vol_is_nul[channel])
 {
  vol_is_nul[channel]=0;
  if (tone_on[channel])
   Adlib_ChannelOn (channel);
 }
 if (*(channel_voice[channel]+10)&1)  /* additive synthesis */
  WriteVolume(vol,*(channel_voice[channel]+2),0x40+OpAd[channel]);
 WriteVolume(vol,*(channel_voice[channel]+3),0x43+OpAd[channel]);
}

static T_VOID Adlib_SetVibrato(T_UCHAR channel)
{
 if (*(channel_voice[channel]+10)&1)
  WriteFM (*(channel_voice[channel]+0)|0x80,0x20+OpAd[channel]);
 WriteFM (*(channel_voice[channel]+1)|0x80,0x23+OpAd[channel]);
}

static T_VOID Adlib_ResetVibrato(T_UCHAR channel)
{
 if (*(channel_voice[channel]+10)&1)
  WriteFM (*(channel_voice[channel]+0),0x20+OpAd[channel]);
 WriteFM (*(channel_voice[channel]+1),0x23+OpAd[channel]);
}

static T_VOID Adlib_SetFreq(T_UCHAR channel,T_UWORD freq)
{
 T_SWORD block,f;
 ChannelFreq[channel]=freq;
 f=FMFreqs[freq*2+1];
 block=FMFreqs[freq*2];
 WriteFM(f&0xFF,0xA0+ChanAd[channel]);
 kbf[channel]=(f>>8)+(block<<2);
 if (tone_on[channel])
 {
  if (f)
   Adlib_ChannelOn(channel);
  else
   Adlib_ChannelOff(channel);
 }
}

static T_VOID Adlib_SetVoice(T_UCHAR *voice,T_SWORD channel)
{
 WriteFM(voice[0],0x20+OpAd[channel]);
 WriteFM(voice[1],0x23+OpAd[channel]);
 WriteFM(voice[2],0x40+OpAd[channel]);
 WriteFM(voice[3],0x43+OpAd[channel]);
 WriteFM(voice[4],0x60+OpAd[channel]);
 WriteFM(voice[5],0x63+OpAd[channel]);
 WriteFM(voice[6],0x80+OpAd[channel]);
 WriteFM(voice[7],0x83+OpAd[channel]);
 WriteFM(voice[8],0xE0+OpAd[channel]);
 WriteFM(voice[9],0xE3+OpAd[channel]);
 if (OPL3)
  WriteFM(voice[10]|0x30,0xC0+ChanAd[channel]);
 else
  WriteFM(voice[10],0xC0+ChanAd[channel]);
 channel_voice[channel]=voice;
}

static T_VOID Adlib_SetVoices(T_VOID)
{
 T_SWORD i;
 for (i=PSG_VOICE;i<PSG_VOICE+3;++i)
 {
  Adlib_SetVoice(PSG_Melodic1,i);
  Adlib_SetVolume(i,0);
 }
 if (OPL3)
 {
  for (i=PSG_VOICE2;i<PSG_VOICE2+3;++i)
  {
   Adlib_SetVoice(PSG_Melodic2,i);
   Adlib_SetVolume(i,15);
  }
 }
 for (i=PSG_VOICE+3;i<PSG_VOICE+6;++i)
 {
  Adlib_SetVoice(PSG_Noise,i);
  Adlib_SetVolume(i,15);
 }
}

static T_INT Reset_Adlib(T_VOID)
{
 T_UCHAR tmp1,tmp2;
 T_SWORD i;

 WriteFM(0,1);
 WriteFM(0x60,4);
 WriteFM(0x80,4);
 tmp1=ReadFM();
 WriteFM(0xFF,2);
 WriteFM(0x21,4);
 for (i=0;i<300;++i) ReadFM();
 tmp2=ReadFM();
 WriteFM(0x60,4);
 WriteFM(0x80,4);
 if ((tmp1&0xE0)!=0)
  return T_FALSE;
 if ((tmp2&0xE0)!=0xC0)
  return T_FALSE;
 WriteFM(0,1);
 return T_TRUE;
}

static T_VOID Adlib_ToneOn(T_UCHAR channel,T_UCHAR on)
{
 on=(on)?0:1;
 if (tone_on[channel]!=on)
 {
  tone_on[channel]=on;
  if (tone_on[channel])
   Adlib_ChannelOn(channel);
  else
   Adlib_ChannelOff(channel);
 }
}

static T_UWORD GetPSGVolume(T_UCHAR v)
{
 T_UWORD retval;
 retval=v&0x0F;
 if (v&0x10)
  retval+=0x100;
 return retval;
}

T_INT SB_WaitDSPWrite(T_VOID)
/*
 Waits until the DSP is ready to be written to.

 returns T_FALSE on timeout
*/
{
 T_UWORD timeout=32767;
 while(timeout--)
 {
  if (!(inp(DSP_WRITE_STATUS)&0x80))
   return T_TRUE;
 }
 return T_FALSE;
}

T_INT SB_WaitDSPRead(T_VOID)
/*
 Waits until the DSP is ready to read from.

 returns T_FALSE on timeout
*/
{
 T_UWORD timeout=32767;
 while (timeout--)
 {
  if (inp(DSP_DATA_AVAIL)&0x80)
   return T_TRUE;
 }
 return T_FALSE;
}

T_INT SB_WriteDSP(T_UCHAR data)
/*
 Writes char 'data' to the DSP.

 returns T_FALSE on timeout.
*/
{
 if (!SB_WaitDSPWrite())
  return T_FALSE;
 outp(DSP_WRITE_DATA,data);
 return T_TRUE;
}

T_UWORD SB_ReadDSP(T_VOID)
/*
 Reads a char from the DSP.

 returns 0xffff on timeout.
*/
{
 if (!SB_WaitDSPRead())
  return 0xffff;
 return (inp(DSP_READ_DATA));
}

T_INT SB_Ping(T_VOID)
/*
 Checks if a SB is present at the current baseport by
 resetting the DSP and checking if it returned the value 0xaa.

 returns: T_TRUE   => SB is present
          T_FALSE  => No SB detected
*/
{
 SB_ResetDSP();
 if (SB_ReadDSP()==0xaa)
  return T_TRUE;
 return T_FALSE;
}

T_UWORD SB_GetDSPVersion(T_VOID)
/*
 Gets SB-dsp version. returns 0xffff if dsp didn't respond.
*/
{
 T_UWORD hi,lo;
 if (!SB_WriteDSP(0xe1))
  return 0xffff;

 hi=SB_ReadDSP();
 lo=SB_ReadDSP();
 return ((hi<<8)|lo);
}

T_VOID SB_ResetDSP(T_VOID)
/*
 Resets the DSP.
*/
{
 // reset the DSP by sending 1, (delay), then 0
 outp(DSP_RESET,1);
 inp(DSP_RESET);
 inp(DSP_RESET);
 inp(DSP_RESET);
 inp(DSP_RESET);
 inp(DSP_RESET);
 inp(DSP_RESET);
 inp(DSP_RESET);
 inp(DSP_RESET);
 outp(DSP_RESET,0);
}

T_INT SB_IsThere(T_VOID)
{
 T_PCHAR envptr;
 T_PCHAR endptr;
 T_CHAR c;

 sb_port =0xffff;

 if ((envptr=getenv("BLASTER"))==NULL)
  return T_FALSE;

 while(1)
 {
  // skip whitespace
  do
   c=*(envptr++);
  while(c==' ' || c=='\t');

  // reached end of string? -> exit
  if (c==0)
   break;
  switch(c)
  {
   case 'a':
   case 'A':
    sb_port=strtol(envptr,&endptr,16);
    break;
   default:
    strtol(envptr,&endptr,16);
    break;
  }
  envptr=endptr;
 }
 if (sb_port==0xffff)
  return T_FALSE;
 if (!SB_Ping())
  return T_FALSE;
 // get dsp version.
 if ((sb_ver=SB_GetDSPVersion())==0xffff)
  return T_FALSE;
 return T_TRUE;
}

T_INT SB_Init(T_VOID)
{
 if (!SB_IsThere())
 {
  // fixme
  t_printf(E_VERBOSE_NONE,"\nNo such hardware detected, check your 'BLASTER' env. variable");
  return T_FALSE;
 }
 SB_ResetDSP();
 return T_TRUE;
}

T_VOID SB_Exit(T_VOID)
{
 SB_ResetDSP();
}
/*****************************************************************************/
/*                                                                           */
/*   Function    : fTimerTick()                                              */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Timer tick T_INTerrupt  18.2 Hz                             */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

static T_VOID __far __interrupt fTimerTick(T_VOID)
{
 timer_tick++;
//   (dos_timer_handler)();      // Chain the DOS timer tick
}
T_INT adlib_timer=0;

#endif //#ifdef _NO_DEBUG_INFORMATION_

#ifndef _NO_DEBUG_INFORMATION_

/*****************************************************************************/
/*                                                                           */
/*   Function    : fSoundInit()                                              */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Initialise the sound emulation functions                  */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_INT fadlibSoundInit(T_VOID)
{
 adlib_is_init=1;
 fSoundReset();
 return(T_TRUE);
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fSoundReset()                                             */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Do a hardware reset of the sound chip                     */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_VOID fadlibSoundReset(T_VOID)
{
 /* If Adlib is not to be used, don't initialize */
 if (SB_Init()==T_TRUE)
 {
  digi_sound_possible=T_TRUE;
  writedac(SPEAKER_ON);
  if (bGblDisplayVerbose==T_TRUE)
   t_printf(E_VERBOSE_NONE,"\nSoundblaster init OK, digisound possible...");
 }
 else
 {
  digi_sound_possible=T_FALSE;
  if (bGblDisplayVerbose==T_TRUE)
   t_printf(E_VERBOSE_NONE,"\nSoundblaster init Failed, no digisound...");
 }
 if(!Reset_Adlib())
 {
  if (bGblDisplayVerbose==T_TRUE)
   t_printf(E_VERBOSE_NONE,"\nAdlib sound init failed...");
  return;
 }
 if (bGblDisplayVerbose==T_TRUE)
  t_printf(E_VERBOSE_NONE,"\nAdlib sound init OK...");
 if (OPL3)
 {
  if((ReadFM()&6)==0)
  {
   WriteFM(1,0x105);
   WriteFM(0,0x104);
  }
  else
  {
   OPL3=0;
  }
 }
 memset(vol_is_nul,0,sizeof(vol_is_nul));
 memset(tone_on,1,sizeof(tone_on));
 WriteFM(0x00,0xBD);
 WriteFM(0,8);
 Adlib_SetVoices();
 return;
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fSoundRestore()                                           */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Shutdown the sound chip before exit.                      */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_VOID fadlibSoundRestore(T_VOID)
{
 T_SWORD i;
 if (adlib_is_init==0)
  return;
 adlib_is_init=0;
 for(i=PSG_VOICE+0;i<PSG_VOICE+6;++i)
 {
  Adlib_ChannelOff(i);
  Adlib_SetVolume(i,0);
 }
 if(OPL3)
 {
  for(i=PSG_VOICE2+0;i<PSG_VOICE2+3;++i)
  {
   Adlib_ChannelOff(i);
   Adlib_SetVolume(i,0);
  }
 }
 if (OPL3)
  WriteFM(0,0x105);
 OPL3=0;
 Reset_Adlib();
 WriteFM(0x00,0xBD);
 WriteFM(0,8);
 if (digi_sound_possible==T_TRUE)
  SB_Exit();
}

/*****************************************************************************/
/*                                                                           */
/*   Function    : fSoundTick()                                              */
/*                                                                           */
/*   Author      : K.W                                                       */
/*                                                                           */
/*   Description : Do one unit of sound chip emulation.                      */
/*                                                                           */
/*   Accepts     : nothing                                                   */
/*                                                                           */
/*   Returns     : nothing                                                   */
/*                                                                           */
/*****************************************************************************/

T_VOID fadlibSoundTick(T_VOID)
{
  T_UCHAR tTmp;
  if ((digi_sound_possible==T_TRUE)&&((digisound==T_TRUE)&&((tOLD_BDIR|tOLD_BC1)==0)))
  {
   T_UCHAR tData=(T_UCHAR)((((T_SCHAR)(t6522ORA))+(T_SCHAR)(128)));
   tData=(tData*wGblDigitalVolume)/100;
   /****************************************/
   /* Use ... for speed regulator checking */
   /****************************************/
   if (lwGblSystemTickTotal>tick_timeout)
   {
#ifndef DEBUG_MSG
    if (!tTicker--)
    {
     tTicker=KBD_WAIT;
     if (KBDKeyPressed)
     {
      fKBDdokey();
     }
    }
#endif
    while (last_timer_tick==timer_tick)
    {
     /****************************************************/
     /* Pause until timer rollover, check for timeout ?? */
     /****************************************************/
     ;
    }
    last_timer_tick=timer_tick;
    tick_timeout=lwGblSystemTickTotal+lwGblMaxTicks;
   }
   if (last_was_digi_sound==T_FALSE)
   {
    last_was_digi_sound=T_TRUE;
   }
   writedac(DIRECT_DAC);
   writedac(tData);
   tOLD_BC1=tSIGDEF_AY3BC1;
   tOLD_BDIR=tSIGDEF_AY3BDIR;
   return;
  }
  if (tOLD_BC1!=tSIGDEF_AY3BC1 || tOLD_BDIR!=tSIGDEF_AY3BDIR)
  {
   if (last_was_digi_sound==T_TRUE)
   {
    last_was_digi_sound=T_FALSE;
   }
   tTmp=tOLD_BDIR|tOLD_BC1;
   fDebug(MED,("fSoundTick() - %02x",tTmp));
   switch(tTmp)
   {
    case 0x00:   // Back to the inactive state
     break;
    case 0x08:   // Read from AY-3-8192
     switch(tAddr)
     {
      case 0x0e:
      // Build the key char
       tKeyWord =(fKBDisdown(wGblPly2Button4))?0:1<<7;
       tKeyWord|=(fKBDisdown(wGblPly2Button3))?0:1<<6;
       tKeyWord|=(fKBDisdown(wGblPly2Button2))?0:1<<5;
       tKeyWord|=(fKBDisdown(wGblPly2Button1))?0:1<<4;
       tKeyWord|=(fKBDisdown(wGblPly1Button4))?0:1<<3;
       tKeyWord|=(fKBDisdown(wGblPly1Button3))?0:1<<2;
       tKeyWord|=(fKBDisdown(wGblPly1Button2))?0:1<<1;
       tKeyWord|=(fKBDisdown(wGblPly1Button1))?0:1<<0;

      if (wGblPCJoystick)
      {
       if (button1a)
        tKeyWord&=255-0x08;
       if (button1b)
        tKeyWord&=255-0x04;
       if (button2a)
        tKeyWord&=255-0x80;
       if (button2b)
        tKeyWord&=255-0x40;
      }
      if (!tKeyWord&0x80)
      {
       t6522CA1=1; // muesste hier gesetzt werden und im 6522 abgefragt werden!
       t6522IFLG|=E6522IFLG_CA1;
//       t6522AUX_PA_LE=1;
       if (t6522IER&E6522IFLG_CA1)
        tsCPURegisters.tIRQFlag=T_TRUE; /* CA1 -> keyboard 2 normaler IRQ ??? */
      }
      else
      {
       t6522CA1=0;
       t6522IFLG&=(255-E6522IFLG_CA1);
//       t6522AUX_PA_LE=0;
      }
      if (!tKeyWord&0x08)
      {
       tsCPURegisters.tFIRQFlag=T_TRUE;
      }
      fDebug(MED,("fSoundTick() - Joypad button returned %02x",tKeyWord));
      t6522IRA=tKeyWord;
       break;
      default:
       fDebug(MED,("fSoundTick() - Bad register select"));
       break;
     }
     break;
    case 0x10:   // Write to AY-3-8192
     {
      T_UCHAR tData=t6522ORA;
      T_UWORD tmp;
      if (tAddr>14)
       break;
      switch(tAddr)
      {
       case 0:
       case 2:
       case 4:
        Adlib_SetFreq((tAddr>>1)+PSG_VOICE,tData+(PSG_Reg[tAddr+1]<<8));
        if (OPL3)
         Adlib_SetFreq((tAddr>>1)+PSG_VOICE2,tData+(PSG_Reg[tAddr+1]<<8));
        break;
       case 1:
       case 3:
       case 5:
        tData&=0x0F;
        Adlib_SetFreq((tAddr>>1)+PSG_VOICE,(tData<<8)+PSG_Reg[tAddr-1]);
        if(OPL3) Adlib_SetFreq((tAddr>>1)+PSG_VOICE2,(tData<<8)+PSG_Reg[tAddr-1]);
        break;
       case 6:
        tData&=0x1F;
        Adlib_SetFreq(PSG_VOICE+3,PSG_NoiseFreqs[tData]);
        Adlib_SetFreq(PSG_VOICE+4,PSG_NoiseFreqs[tData]);
        Adlib_SetFreq(PSG_VOICE+5,PSG_NoiseFreqs[tData]);
        break;
       case 7:
        Adlib_ToneOn(0+PSG_VOICE,tData&0x01);
        Adlib_ToneOn(1+PSG_VOICE,tData&0x02);
        Adlib_ToneOn(2+PSG_VOICE,tData&0x04);
        if(OPL3)
        {
         Adlib_ToneOn(0+PSG_VOICE2,tData&0x01);
         Adlib_ToneOn(1+PSG_VOICE2,tData&0x02);
         Adlib_ToneOn(2+PSG_VOICE2,tData&0x04);
        }
        Adlib_ToneOn(3+PSG_VOICE,tData&0x08);
        Adlib_ToneOn(4+PSG_VOICE,tData&0x10);
        Adlib_ToneOn(5+PSG_VOICE,tData&0x20);
        break;
       case 8:
       case 9:
       case 10:
        tData&=0x1F;
        tmp=GetPSGVolume(tData);
        if (tmp<256)
        {
         Adlib_ResetVibrato(tAddr-8+PSG_VOICE);
         Adlib_ResetVibrato(tAddr-8+3+PSG_VOICE);
         Adlib_SetVolume(tAddr-8+PSG_VOICE,tmp);
         Adlib_SetVolume(tAddr-8+3+PSG_VOICE,tmp);
         if (OPL3)
         {
          Adlib_ResetVibrato(tAddr-8+PSG_VOICE2);
          Adlib_SetVolume(tAddr-8+PSG_VOICE2,tmp);
         }
        }
        else
        {
         Adlib_SetVolume(tAddr-8+PSG_VOICE,12);
         if (OPL3) Adlib_SetVolume(tAddr-8+PSG_VOICE2,12);
         Adlib_SetVolume(tAddr-8+3+PSG_VOICE,12);
         switch (tmp&0x0f)
         {
          case 0x00:
          case 0x01:
          case 0x02:
          case 0x03:
          case 0x09:
          case 0x04:
          case 0x05:
          case 0x06:
          case 0x07:
          case 0x0F:
           Adlib_ResetVibrato(tAddr-8+PSG_VOICE);
           if(OPL3) Adlib_ResetVibrato(tAddr-8+PSG_VOICE);
           Adlib_ResetVibrato(tAddr-8+3+PSG_VOICE);
           if (tone_on[tAddr-8+PSG_VOICE])
           {
            if (key_on[tAddr-8+PSG_VOICE]==0)
            {
             Adlib_ChannelOn(tAddr-8+PSG_VOICE);
             if(OPL3) Adlib_ChannelOn(tAddr-8+PSG_VOICE2);
            }
            Adlib_SetVolume(tAddr-8+PSG_VOICE,0);
            if(OPL3) Adlib_SetVolume(tAddr-8+PSG_VOICE2,0);
           }
           if (tone_on[tAddr-8+3+PSG_VOICE])
           {
            if(key_on[tAddr-8+3+PSG_VOICE]==0) Adlib_ChannelOn(tAddr-8+3+PSG_VOICE);
            Adlib_SetVolume(tAddr-8+3+PSG_VOICE,0);
           }
           break;
          case 0x08:
          case 0x0A:
          case 0x0C:
          case 0x0E:
           Adlib_SetVibrato(tAddr-8+PSG_VOICE);
           if(OPL3) Adlib_SetVibrato (tAddr-8+PSG_VOICE2);
           Adlib_SetVibrato(tAddr-8+3+PSG_VOICE);
           break;
          default:
           Adlib_ResetVibrato(tAddr-8+PSG_VOICE);
           if(OPL3) Adlib_ResetVibrato (tAddr-8+PSG_VOICE2);
           Adlib_ResetVibrato (tAddr-8+3+PSG_VOICE);
           break;
         }
        }
        break;
       case 11:              /* bits 7-0 vd snelheid vh volumeverloop */
        break;
       case 12:              /* bits 15-8 */
        break;
       case 13:              /* soort volumeverloop */
        tData&=0x0F;
        break;
       case 14: // port a (only goggles)
        {
         static T_ULONG last=0;
         if ((tKeyOut&(255-128)))
         {
          if (t6522ORA!=255)
          {
           imager_pulse_ticks=lwGblSystemTickTotal-last;
          }
         }
         last=lwGblSystemTickTotal;
         tKeyOut=t6522ORA;
        }
        break;
       }
       PSG_Reg[tAddr]=tData;
     }
     fDebug(MED,("fSoundTick() - Write"));
     break;
    case 0x18:   // Latch address to AY-3-8192
     fDebug(MED,("fSoundTick() - Latch Address"));
     tAddr=t6522ORA&0x0f;
     break;
    default:
     break;
  }
 }
 tOLD_BC1=tSIGDEF_AY3BC1;
 tOLD_BDIR=tSIGDEF_AY3BDIR;
}

T_VOID fadlibSoundMute(T_VOID)
{
 T_SWORD i;
 sound_is_on=T_FALSE;
 for(i=PSG_VOICE+0;i<PSG_VOICE+6;++i)
  Adlib_ChannelOff (i);
 if(OPL3) for(i=PSG_VOICE2+0;i<PSG_VOICE2+3;++i)
  Adlib_ChannelOff (i);
}

T_VOID fadlibSoundResume(T_VOID)
{
 T_SWORD i;
 if (!sound_on)
  return;
 sound_is_on=T_TRUE;
 for (i=PSG_VOICE+0;i<PSG_VOICE+6;++i)
  if (tone_on[i])
   Adlib_ChannelOn(i);
 if (OPL3)
  for (i=PSG_VOICE2+0;i<PSG_VOICE2+3;++i)
   if (tone_on[i])
    Adlib_ChannelOn(i);
}

T_VOID adlibDoneTimerHandler(T_VOID)
{
 if (adlib_timer==1)
 {
  adlib_timer=0;
  outp(0x43, 0x34);
  outp(0x40, 0x00);
  outp(0x40, 0x00);
  _dos_setvect(DOS_TIMER_IRQ, dos_timer_handler);
 }
}

T_VOID adlibInitTimerHandler(T_VOID)
{
 if (adlib_timer==0)
 {
  adlib_timer=1;
  dos_timer_handler = _dos_getvect(DOS_TIMER_IRQ);
  _dos_setvect(DOS_TIMER_IRQ, fTimerTick);
  outp(0x43, 0x34);
  outp(0x40, T_LOW_BYTE(TIMER_RATE));
  outp(0x40, T_HIGH_BYTE(TIMER_RATE));
 }
}
#endif //#ifndef _NO_DEBUG_INFORMATION_
