/*
 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
 *
 * (c) Copyright 1996, 1997, 1998 Gary Henderson (gary@daniver.demon.co.uk) and
 *                                Jerremy Koot (jkoot@euronet.nl)
 *
 * Super FX C emulator code 
 * (c) Copyright 1997, 1998 Lestat (lstat@hotmail.com) and
 *                          Gary Henderson.
 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
 *
 * DSP1 emulator code (c) Copyright 1998 Lestat and Gary Henderson.
 * DOS port code contains the works of other authors. See headers in
 * individual files.
 *
 * Permission to use, copy, modify and distribute Snes9x in both binary and
 * source form, for non-commercial purposes, is hereby granted without fee,
 * providing that this license information and copyright notice appear with
 * all copies and any derived work.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event shall the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Snes9x is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Snes9x or software derived from Snes9x.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so everyone can benefit from the modifications
 * in future versions.
 *
 * Super NES and Super Nintendo Entertainment System are trademarks of
 * Nintendo Co., Limited and its subsidiary companies.
 */

#include "snes9x.h"
#include "memmap.h"
#include "cpuops.h"
#include "ppu.h"
#include "cpuexec.h"
#include "gfx.h"
#include "missing.h"
#include "apu.h"
#include "dma.h"

struct SSettings Settings = {
    AUTO_FRAMERATE, ((uint16) (63.5e-6 / (1 / 3580000.0)) * 6), 
	256 * ((uint16) (63.5e-6 / (1 / 3580000.0)) * 6) / SNES_HCOUNTER_MAX,
	FALSE, FALSE,
	FALSE, FALSE,
	FALSE, 0,
	FALSE, FALSE,
	FALSE, FALSE,
	FALSE, FALSE, 
	FALSE, FALSE, 
	FALSE, FALSE, 
	FALSE, FALSE
};

struct Missing missing;

char rom_name[ROM_NAME_LEN];

const char *S9xGetSRAMFilename( void)
{
char drive[ _MAX_DRIVE];
char dir[ _MAX_DIR];
char fname[ _MAX_FNAME];
char ext[ _MAX_EXT];
static char path[ _MAX_PATH];


	_splitpath( Memory.ROMFilename, drive, dir, fname, ext);
	_makepath( path, drive, dir, fname, "SRM");
	return path;
}

const char *GetSaveFilename( void)
{
char drive[ _MAX_DRIVE];
char dir[ _MAX_DIR];
char fname[ _MAX_FNAME];
char ext[ _MAX_EXT];
static char path[ _MAX_PATH];


	_splitpath( Memory.ROMFilename, drive, dir, fname, ext);
	_makepath( path, drive, dir, fname, "S9X");
	return path;
}

const char *basename (const char *f)
{
    const char *p;
    if (p = strrchr (f, '/'))
	return (p + 1);

    return (f);
}

bool8 S9xOpenSnapshotFile (const char *fname, bool8 read_only, STREAM *file)
{
	if( read_only)
	{	*file = gzopen( fname, "rb");	}
	else
	{	*file = gzopen( fname, "wb");	}

	if( *file)
		return true;

	return false;
}

bool8 S9xChooseFilename (bool8 read_only, STREAM *file)
{
	return S9xOpenSnapshotFile( GetSaveFilename(), read_only, file);
}


void S9xCloseSnapshotFile (STREAM file)
{
	gzclose( file);
}

/*
    RAM = new uint8[2 * 0x10000];
    SRAM = new uint8 [0x10000];
    FillRAM = new uint8 [0x8000];
    VRAM = new uint8[0x10000];
	ROM = new uint8[0x40 * 0x10000 + 0x200];
    
    IPPU.TileCache [TILE_2BIT] = new uint8 [MAX_2BIT_TILES * 64];
    IPPU.TileCache [TILE_4BIT] = new uint8 [MAX_4BIT_TILES * 64];
    IPPU.TileCache [TILE_8BIT] = new uint8 [MAX_8BIT_TILES * 64];
    
    IPPU.TileCached [TILE_2BIT] = new uint8 [MAX_2BIT_TILES];
    IPPU.TileCached [TILE_4BIT] = new uint8 [MAX_4BIT_TILES];
    IPPU.TileCached [TILE_8BIT] = new uint8 [MAX_8BIT_TILES];

    if( !RAM || !SRAM || !FillRAM || !VRAM || !ROM || !IPPU.TileCache [TILE_2BIT] ||
		!IPPU.TileCache [TILE_4BIT] || !IPPU.TileCache [TILE_8BIT] ||
		!IPPU.TileCached [TILE_2BIT] || !IPPU.TileCached [TILE_4BIT] ||
		!IPPU.TileCached [TILE_8BIT])
		return (FALSE);
	
    ZeroMemory (IPPU.TileCached [TILE_2BIT], MAX_2BIT_TILES);
    ZeroMemory (IPPU.TileCached [TILE_4BIT], MAX_4BIT_TILES);
    ZeroMemory (IPPU.TileCached [TILE_8BIT], MAX_8BIT_TILES);
*/

/*
    if( RAM) delete RAM;
    if( SRAM) delete SRAM;
    if( FillRAM) delete FillRAM;
    if( VRAM) delete VRAM;
	if( ROM) delete ROM;
    
    if( IPPU.TileCache [TILE_2BIT]) delete IPPU.TileCache [TILE_2BIT];
    if( IPPU.TileCache [TILE_4BIT]) delete IPPU.TileCache [TILE_4BIT];
    if( IPPU.TileCache [TILE_8BIT]) delete IPPU.TileCache [TILE_8BIT];
    
    if( IPPU.TileCached [TILE_2BIT]) delete IPPU.TileCached [TILE_2BIT];
    if( IPPU.TileCached [TILE_4BIT]) delete IPPU.TileCached [TILE_4BIT];
    if( IPPU.TileCached [TILE_8BIT]) delete IPPU.TileCached [TILE_8BIT];
*/


/*
#ifdef __sun
static int Rates[8] =
{
    0, 8000, 11025, 16000, 22050, 32000, 37800, 44100
};
#else
#ifndef _WIN32
static int Rates[8] =
{
    0, 8192, 11025, 16500, 22050, 29300, 36600, 44000
};
#endif
#endif

#ifndef _WIN32
static int Sizes [8] =
{
    0, 256, 256, 256, 512, 512, 1024, 1024
};
#else
static int Rates[8] =
{
    0, 8192, 11025, 16000, 22050, 29300, 36600, 44000
};
static int Sizes [8] =
{
    0, 164, 221, 320, 441, 586, 732, 880
};
static int Timer [8] =
{
	0, 20, 20, 20, 20, 20, 20, 20
};
#define BUFFER_SIZE 16
#define TEST_SOUNDMODE 4
#endif

#ifdef _WIN32
#include "..\DirectX.H"
extern FILE *Wave;
extern bool MenuActive;
WAVEFORMATEX wfx;

UINT TimerID;

#define SIXTEEN_BIT true

LPDIRECTSOUND lpDS;
LPDIRECTSOUNDBUFFER lpDSBPrimary;
LPDIRECTSOUNDBUFFER lpDSB;
uint32 dwWrite = 0;

int CloseSoundDevice();
int S9xOpenSoundDevice(int, bool8, int);

bool ResyncSound = false;
volatile uint32 FrameTimer;

void S9xSyncSpeed ()
{
    if (Settings.SkipFrames == AUTO_FRAMERATE)
    {
	while (FrameTimer == 0)
	{
#if defined (__linux) || defined(__sun)
	    pause ();
#ifdef SOUND
	    if (CPU.Flags & PROCESS_SOUND_FLAG)
		S9xUnixProcessSound ();
#endif
#endif
//	    S9xProcessEvents (FALSE);
	}
	static uint8 SkippedFrames = 0;
	if (FrameTimer > 1 && SkippedFrames < 10)
	{
	    SkippedFrames++;
	    IPPU.FrameSkip = 0;
	}
	else
	{
	    IPPU.FrameSkip = Settings.SkipFrames + 1;
	    SkippedFrames = 0;
	}
	FrameTimer--;
    }
}

bool GenerateTone;
__int64 TimeCalled = 0;
volatile bool TimerBusy;
volatile __int64 TimeFreq;
volatile __int64 TimerStart;
VOID CALLBACK TimeProc( UINT idEvent, UINT uMsg, uint32 dwUser, uint32 dw1, uint32 dw2)
{
	if( TimerID != idEvent) return;

	if( TimerBusy) return;

	if( ResyncSound)
	{
		ResyncSound = false;

		lpDSB -> Stop();

		dwWrite = 0;
		return;
	}

	if( ((__int64)timeGetTime()-TimerStart) < TimeFreq)
		return;
	
	TimerStart += TimeFreq;

	FrameTimer ++;

	TimerBusy = true;
	WindowsProcessSound();
	TimerBusy = false;
}

// Wait until the timer has finished WindowsProcessSound
void WindowsWaitForSound( void)
{	
	while( CPU.Flags & PROCESS_SOUND_FLAG);
}

int CloseSoundDevice ()
{
	timeKillEvent( TimerID);
	if( timeEndPeriod( 1) != TIMERR_NOERROR) Beep( 1000, 100);

	if( lpDS != NULL)
	{
		if( lpDSBPrimary != NULL)
		{
			lpDSBPrimary -> Release();
			lpDSBPrimary = NULL;
		}

		if( lpDSB != NULL)
		{
			lpDSB -> Release();
			lpDSB = NULL;
		}
	}
	return true;
}

int S9xOpenSoundDevice (int mode, bool8 stereo, int buffer_size)
{
HRESULT hResult;
DSBUFFERDESC dsbd;
UINT wTimerRes;
TIMECAPS tc;

	dwWrite = 0;

	wfx.wFormatTag		= WAVE_FORMAT_PCM;
	wfx.nChannels		= stereo ? 2 : 1;
	wfx.nSamplesPerSec	= Rates [mode & 7];
	wfx.nBlockAlign		= stereo ? (SIXTEEN_BIT ? 4 : 2) : (SIXTEEN_BIT ? 2 : 1);
	wfx.wBitsPerSample	= SIXTEEN_BIT ? 16 : 8;
    wfx.nAvgBytesPerSec	= wfx.nSamplesPerSec * wfx.nBlockAlign;
	wfx.cbSize			= 0;

    so.playback_rate = wfx.nSamplesPerSec;
    so.stereo = wfx.nChannels == 2 ? true : false;
    so.sixteen_bit = wfx.wBitsPerSample == 16 ? true : false;
    so.encoded = FALSE;

	so.buffer_size = buffer_size;
	if(!so.buffer_size) so.buffer_size = Sizes [mode & 7];
    if(so.sixteen_bit) so.buffer_size *= 2;
    if(so.stereo) so.buffer_size *= 2;

	if( Wave)
	{
	uint32 dwRiff, dwLength, dwType;

		dwRiff = mmioFOURCC( 'R', 'I', 'F', 'F');
		dwLength = 0;
		dwType = mmioFOURCC( 'W', 'A', 'V', 'E');
		
		fwrite( &dwRiff, 4, 1, Wave);
		fwrite( &dwLength, 4, 1, Wave);
		fwrite( &dwType, 4, 1, Wave);

		dwType = mmioFOURCC( 'f', 'm', 't', ' ');
		dwLength = sizeof(WAVEFORMATEX);
		fwrite( &dwType, 4, 1, Wave);
		fwrite( &dwLength, 4, 1, Wave);
		fwrite( &wfx, sizeof(WAVEFORMATEX), 1, Wave);

		dwType = mmioFOURCC( 'd', 'a', 't', 'a');
		dwLength = 0;
		fwrite( &dwType, 4, 1, Wave);
		fwrite( &dwLength, 4, 1, Wave);
	}

	hResult = DirectSoundCreate( NULL, &lpDS, NULL );
    if( hResult != DS_OK)
	{
		Beep( 1000, 100);
		return FALSE;
	}

	hResult = lpDS -> SetCooperativeLevel( GUI.hWnd, DSSCL_EXCLUSIVE);
    if( hResult != DS_OK)
	{
		Beep( 1000, 100);
		return FALSE;
	};

    // Set up DSBUFFERDESC structure.
    ZeroMemory( &dsbd, sizeof(DSBUFFERDESC));  // Zero it out. 
    dsbd.dwSize              = sizeof(DSBUFFERDESC);
    dsbd.dwFlags             = DSBCAPS_CTRLDEFAULT | DSBCAPS_LOCSOFTWARE;
    dsbd.dwBufferBytes       = so.buffer_size*BUFFER_SIZE;
    dsbd.lpwfxFormat         = &wfx;

	hResult = lpDS -> CreateSoundBuffer( &dsbd, &lpDSB, NULL);
    if( hResult != DS_OK)
	{
		Beep( 1000, 100);
		return FALSE;
	}

    ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
    dsbd.dwSize = sizeof(dsbd);
    dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;

    hResult = lpDS -> CreateSoundBuffer(&dsbd, &lpDSBPrimary, NULL);
    if( hResult != DS_OK)
	{
		Beep( 1000, 100);
		return FALSE;
	}

    hResult = lpDSBPrimary -> SetFormat( &wfx);
    if( hResult != DS_OK) Beep( 1000, 100);

	hResult = lpDSBPrimary -> Play(0, 0, DSBPLAY_LOOPING);
    if( hResult != DS_OK) Beep( 1000, 100);

	if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) Beep( 1000, 100);
	wTimerRes = min(max(tc.wPeriodMin, 1), tc.wPeriodMax);
	if( timeBeginPeriod( wTimerRes) != TIMERR_NOERROR) Beep( 1000, 100);

	TimerStart = timeGetTime();

	TimeFreq = Timer [mode & 7];
	TimerID = timeSetEvent( 1, 0, (LPTIMECALLBACK) &TimeProc, 0, TIME_PERIODIC);
	if( TimerID == NULL) Beep( 1000, 100);

	return (TRUE);
}

#include <math.h>
double b = 0, e = 0.05, pi = 3.1;
void WindowsProcessSound( void)
{
HRESULT hResult;
uint8  *B, *B1, *B2;
uint32 S1, S2;

	if( !lpDSB)
	{
		Beep( 1000, 10);
		return;
	}

	if( !GenerateTone)
	{	B = new uint8[ so.buffer_size];	}
	else
	{	B = new uint8[ so.buffer_size<<1];	}
	if( !B)
	{
		Beep( 1000, 10);
		return;
	}

    int sample_count = so.buffer_size;
	if( so.sixteen_bit) sample_count >>= 1;

    CPU.Flags |= PROCESS_SOUND_FLAG;

	if( MenuActive)
		ZeroMemory( B, so.buffer_size);

	if( MenuActive)
	if( GenerateTone)
		ZeroMemory( B+so.buffer_size, so.buffer_size);

	if( !MenuActive)
	if( !GenerateTone)
	{	S9xMixSamples( B, sample_count);	}
	else
	{
		ZeroMemory( B+so.buffer_size, so.buffer_size);
		for( short c = 0; c != so.buffer_size; c ++, b += e)
		{
			if( so.sixteen_bit)
			{
				*((uint16 *)(B+c)) = (uint16) ((sin( b)+1)*0x1FFF);
				*((uint16 *)(B+c)) ^= 0x8000;

				c++;
			}
			else
			{
				*((uint8 *)(B+c)) = (uint8) ((sin( b)+1)*32);
			}
		}
	}
    CPU.Flags &= ~PROCESS_SOUND_FLAG;

	if( !MenuActive)
	if( Wave) fwrite( B, 1, so.buffer_size, Wave);

	if( !GenerateTone)
	{	hResult = lpDSB -> Lock( dwWrite, so.buffer_size, &B1, &S1, &B2, &S2, 0);	}
	else
	{	hResult = lpDSB -> Lock( dwWrite, so.buffer_size<<1, &B1, &S1, &B2, &S2, 0);	}

	if( hResult != DS_OK)
	{
		Beep( 1000, 10);
		return;
	}

	if( B1)
	{	MoveMemory( B1, B, S1);	}
	if( B2)
	{	MoveMemory( B2, B+S1, S2);	}

	hResult = lpDSB -> Unlock( B1, S1, B2, S2);
	if( hResult != DS_OK)
	{
		Beep( 1000, 10);
		return;
	}

	if( B) delete B;

	uint32 Status;
	hResult = lpDSB -> GetStatus( &Status);
	if( !Status)
	{
		lpDSB -> SetCurrentPosition( dwWrite);

		hResult = lpDSB -> Play( 0, 0, DSBPLAY_LOOPING);
		if( hResult != DS_OK) Beep( 1000, 10);
	}

	dwWrite += so.buffer_size;
	if( dwWrite == (uint32) so.buffer_size*BUFFER_SIZE)
		dwWrite = 0;
}
#endif

  */

static int Rates[8] =
{
    0, 8192, 11025, 16000, 22050, 29300, 36600, 44000
};
static int Sizes [8] =
{
    0, 164, 221, 320, 441, 586, 732, 880
};
static int Timer [8] =
{
	0, 20, 20, 20, 20, 20, 20, 20
};
#define BUFFER_SIZE 16
#define TEST_SOUNDMODE 4

#ifdef _WIN32
#include "..\DirectX.H"
extern FILE *Wave;
extern bool MenuActive;
WAVEFORMATEX wfx;

UINT TimerID;

#define SIXTEEN_BIT true

LPDIRECTSOUND lpDS;
LPDIRECTSOUNDBUFFER lpDSBPrimary;
LPDIRECTSOUNDBUFFER lpDSB;
uint32 dwWrite = 0;

int CloseSoundDevice();
int S9xOpenSoundDevice(int, bool8, int);

bool ResyncSound = false;
volatile uint32 FrameTimer;

void S9xSyncSpeed ()
{
    if (Settings.SkipFrames == AUTO_FRAMERATE)
    {
	while (FrameTimer == 0)
	{
#if defined (__linux)
	    pause ();
#ifdef SOUND
	    if (CPU.Flags & PROCESS_SOUND_FLAG)
		S9xUnixProcessSound ();
#endif
#endif
//	    S9xProcessEvents (FALSE);
	}
	static uint8 SkippedFrames = 0;
	if (FrameTimer > 1 && SkippedFrames < 10)
	{
	    SkippedFrames++;
	    IPPU.FrameSkip = 0;
	}
	else
	{
	    IPPU.FrameSkip = Settings.SkipFrames + 1;
	    SkippedFrames = 0;
	}
	FrameTimer--;
    }
}

bool GenerateTone;
__int64 TimeCalled = 0;
volatile bool TimerBusy;
volatile __int64 TimeFreq;
volatile __int64 TimerStart;
VOID CALLBACK TimeProc( UINT idEvent, UINT uMsg, uint32 dwUser, uint32 dw1, uint32 dw2)
{
	if( TimerID != idEvent) return;

	if( TimerBusy) return;

	if( ResyncSound)
	{
		ResyncSound = false;

		lpDSB -> Stop();

		dwWrite = 0;
		return;
	}

	if( ((__int64)timeGetTime()-TimerStart) < TimeFreq)
		return;
	
	TimerStart += TimeFreq;

	FrameTimer ++;

	TimerBusy = true;
	WindowsProcessSound();
	TimerBusy = false;
}

// Wait until the timer has finished WindowsProcessSound
void WindowsWaitForSound( void)
{	
	while( CPU.Flags & PROCESS_SOUND_FLAG);
}

int CloseSoundDevice ()
{
	timeKillEvent( TimerID);
	if( timeEndPeriod( 1) != TIMERR_NOERROR) Beep( 1000, 100);

	if( lpDS != NULL)
	{
		if( lpDSBPrimary != NULL)
		{
			lpDSBPrimary -> Release();
			lpDSBPrimary = NULL;
		}

		if( lpDSB != NULL)
		{
			lpDSB -> Release();
			lpDSB = NULL;
		}
	}
	return true;
}

int S9xOpenSoundDevice (int mode, bool8 stereo, int buffer_size)
{
HRESULT hResult;
DSBUFFERDESC dsbd;
UINT wTimerRes;
TIMECAPS tc;

	dwWrite = 0;

	wfx.wFormatTag		= WAVE_FORMAT_PCM;
	wfx.nChannels		= stereo ? 2 : 1;
	wfx.nSamplesPerSec	= Rates [mode & 7];
	wfx.nBlockAlign		= stereo ? (SIXTEEN_BIT ? 4 : 2) : (SIXTEEN_BIT ? 2 : 1);
	wfx.wBitsPerSample	= SIXTEEN_BIT ? 16 : 8;
    wfx.nAvgBytesPerSec	= wfx.nSamplesPerSec * wfx.nBlockAlign;
	wfx.cbSize			= 0;

    so.playback_rate = wfx.nSamplesPerSec;
    so.stereo = wfx.nChannels == 2 ? true : false;
    so.sixteen_bit = wfx.wBitsPerSample == 16 ? true : false;
    so.encoded = FALSE;

	so.buffer_size = buffer_size;
	if(!so.buffer_size) so.buffer_size = Sizes [mode & 7];
    if(so.sixteen_bit) so.buffer_size *= 2;
    if(so.stereo) so.buffer_size *= 2;

	if( Wave)
	{
	uint32 dwRiff, dwLength, dwType;

		dwRiff = mmioFOURCC( 'R', 'I', 'F', 'F');
		dwLength = 0;
		dwType = mmioFOURCC( 'W', 'A', 'V', 'E');
		
		fwrite( &dwRiff, 4, 1, Wave);
		fwrite( &dwLength, 4, 1, Wave);
		fwrite( &dwType, 4, 1, Wave);

		dwType = mmioFOURCC( 'f', 'm', 't', ' ');
		dwLength = sizeof(WAVEFORMATEX);
		fwrite( &dwType, 4, 1, Wave);
		fwrite( &dwLength, 4, 1, Wave);
		fwrite( &wfx, sizeof(WAVEFORMATEX), 1, Wave);

		dwType = mmioFOURCC( 'd', 'a', 't', 'a');
		dwLength = 0;
		fwrite( &dwType, 4, 1, Wave);
		fwrite( &dwLength, 4, 1, Wave);
	}

	hResult = DirectSoundCreate( NULL, &lpDS, NULL );
    if( hResult != DS_OK)
	{
		Beep( 1000, 100);
		return FALSE;
	}

	hResult = lpDS -> SetCooperativeLevel( GUI.hWnd, DSSCL_EXCLUSIVE);
    if( hResult != DS_OK)
	{
		Beep( 1000, 100);
		return FALSE;
	};

    // Set up DSBUFFERDESC structure.
    ZeroMemory( &dsbd, sizeof(DSBUFFERDESC));  // Zero it out. 
    dsbd.dwSize              = sizeof(DSBUFFERDESC);
    dsbd.dwFlags             = DSBCAPS_CTRLDEFAULT | DSBCAPS_LOCSOFTWARE;
    dsbd.dwBufferBytes       = so.buffer_size*BUFFER_SIZE;
    dsbd.lpwfxFormat         = &wfx;

	hResult = lpDS -> CreateSoundBuffer( &dsbd, &lpDSB, NULL);
    if( hResult != DS_OK)
	{
		Beep( 1000, 100);
		return FALSE;
	}

    ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
    dsbd.dwSize = sizeof(dsbd);
    dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;

    hResult = lpDS -> CreateSoundBuffer(&dsbd, &lpDSBPrimary, NULL);
    if( hResult != DS_OK)
	{
		Beep( 1000, 100);
		return FALSE;
	}

    hResult = lpDSBPrimary -> SetFormat( &wfx);
    if( hResult != DS_OK) Beep( 1000, 100);

	hResult = lpDSBPrimary -> Play(0, 0, DSBPLAY_LOOPING);
    if( hResult != DS_OK) Beep( 1000, 100);

	if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) Beep( 1000, 100);
	wTimerRes = min(max(tc.wPeriodMin, 1), tc.wPeriodMax);
	if( timeBeginPeriod( wTimerRes) != TIMERR_NOERROR) Beep( 1000, 100);

	TimerStart = timeGetTime();

	TimeFreq = Timer [mode & 7];
	TimerID = timeSetEvent( 1, 0, (LPTIMECALLBACK) &TimeProc, 0, TIME_PERIODIC);
	if( TimerID == NULL) Beep( 1000, 100);

	return (TRUE);
}

#include <math.h>
double b = 0, e = 0.05, pi = 3.1;
void WindowsProcessSound( void)
{
HRESULT hResult;
uint8  *B, *B1, *B2;
uint32 S1, S2;

	if( !lpDSB)
	{
		Beep( 1000, 10);
		return;
	}

	if( !GenerateTone)
	{	B = new uint8[ so.buffer_size];	}
	else
	{	B = new uint8[ so.buffer_size<<1];	}
	if( !B)
	{
		Beep( 1000, 10);
		return;
	}

    int sample_count = so.buffer_size;
	if( so.sixteen_bit) sample_count >>= 1;

    CPU.Flags |= PROCESS_SOUND_FLAG;

	if( MenuActive)
		ZeroMemory( B, so.buffer_size);

	if( MenuActive)
	if( GenerateTone)
		ZeroMemory( B+so.buffer_size, so.buffer_size);

	if( !MenuActive)
	if( !GenerateTone)
	{	S9xMixSamples( B, sample_count);	}
	else
	{
		ZeroMemory( B+so.buffer_size, so.buffer_size);
		for( short c = 0; c != so.buffer_size; c ++, b += e)
		{
			if( so.sixteen_bit)
			{
				*((uint16 *)(B+c)) = (uint16) ((sin( b)+1)*0x1FFF);
				*((uint16 *)(B+c)) ^= 0x8000;

				c++;
			}
			else
			{
				*((uint8 *)(B+c)) = (uint8) ((sin( b)+1)*32);
			}
		}
	}
    CPU.Flags &= ~PROCESS_SOUND_FLAG;

	if( !MenuActive)
	if( Wave) fwrite( B, 1, so.buffer_size, Wave);

	if( !GenerateTone)
	{	hResult = lpDSB -> Lock( dwWrite, so.buffer_size, &B1, &S1, &B2, &S2, 0);	}
	else
	{	hResult = lpDSB -> Lock( dwWrite, so.buffer_size<<1, &B1, &S1, &B2, &S2, 0);	}

	if( hResult != DS_OK)
	{
		Beep( 1000, 10);
		return;
	}

	if( B1)
	{	MoveMemory( B1, B, S1);	}
	if( B2)
	{	MoveMemory( B2, B+S1, S2);	}

	hResult = lpDSB -> Unlock( B1, S1, B2, S2);
	if( hResult != DS_OK)
	{
		Beep( 1000, 10);
		return;
	}

	if( B) delete B;

	uint32 Status;
	hResult = lpDSB -> GetStatus( &Status);
	if( !Status)
	{
		lpDSB -> SetCurrentPosition( dwWrite);

		hResult = lpDSB -> Play( 0, 0, DSBPLAY_LOOPING);
		if( hResult != DS_OK) Beep( 1000, 10);
	}

	dwWrite += so.buffer_size;
	if( dwWrite == (uint32) so.buffer_size*BUFFER_SIZE)
		dwWrite = 0;
}
#endif

