emu48-mirror/Sources/Emu48/EXTERNAL.C
Gwenhael Le Moine 27520054a1
2012-09-15: Updated to version 1.53
Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
2024-03-19 23:35:30 +01:00

226 lines
5.8 KiB
C

/*
* external.c
*
* This file is part of Emu48
*
* Copyright (C) 1995 Sebastien Carlier
* Copyright (C) 2005 Christoph Gießelink
*
*/
#include "pch.h"
#include "Emu48.h"
#include "ops.h"
#define MUSIC_FREQ 11025 // this can be adjusted for quality
//| 38G | 39G | 40G | 48SX | 48GX | 49G | Name
//#F0E4F #80F0F #80F0F #706D2 #80850 #80F0F =SFLAG53_56
// memory address for flags -53 to -56
#define SFLAG53_56 ( (cCurrentRomType=='6') \
? 0xE0E4F \
: ( (cCurrentRomType=='A') \
? 0xF0E4F \
: ( (cCurrentRomType!='E' && cCurrentRomType!='X') \
? ( (cCurrentRomType=='S') \
? 0x706D2 \
: 0x80850 \
) \
: 0x80F0F \
) \
) \
)
BOOL bWaveBeep = FALSE; // PC speaker
DWORD dwWaveVol = 64; // wave sound volume
static __inline VOID BeepWave(DWORD dwFrequency,DWORD dwDuration)
{
HWAVEOUT hSoundDevice;
WAVEFORMATEX wf;
WAVEHDR wh;
HANDLE hEventSound;
DWORD i;
if (dwFrequency == 0) // this is just a delay
{
Sleep(dwDuration);
return;
}
hEventSound = CreateEvent(NULL,FALSE,FALSE,NULL);
wf.wFormatTag = WAVE_FORMAT_PCM;
wf.nChannels = 1;
wf.nSamplesPerSec = MUSIC_FREQ;
wf.nAvgBytesPerSec = MUSIC_FREQ;
wf.nBlockAlign = 1;
wf.wBitsPerSample = 8;
wf.cbSize = 0;
if (waveOutOpen(&hSoundDevice,WAVE_MAPPER,&wf,(DWORD_PTR)hEventSound,0,CALLBACK_EVENT) != 0)
{
CloseHandle(hEventSound); // no sound available
return;
}
// (samp/sec) * msecs * (secs/msec) = samps
wh.dwBufferLength = (DWORD) ((QWORD) MUSIC_FREQ * dwDuration / 1000);
VERIFY(wh.lpData = malloc(wh.dwBufferLength));
wh.dwBytesRecorded = 0;
wh.dwUser = 0;
wh.dwFlags = 0;
wh.dwLoops = 0;
for (i = 0; i < wh.dwBufferLength; ++i) // generate square wave
{
wh.lpData[i] = (BYTE) ((((QWORD) 2 * dwFrequency * i / MUSIC_FREQ) & 1) * dwWaveVol);
}
VERIFY(waveOutPrepareHeader(hSoundDevice,&wh,sizeof(wh)) == MMSYSERR_NOERROR);
ResetEvent(hEventSound); // prepare event for finishing
VERIFY(waveOutWrite(hSoundDevice,&wh,sizeof(wh)) == MMSYSERR_NOERROR);
WaitForSingleObject(hEventSound,INFINITE); // wait for finishing
VERIFY(waveOutUnprepareHeader(hSoundDevice,&wh,sizeof(wh)) == MMSYSERR_NOERROR);
VERIFY(waveOutClose(hSoundDevice) == MMSYSERR_NOERROR);
free(wh.lpData);
CloseHandle(hEventSound);
return;
}
static __inline VOID BeepWin9x(DWORD dwFrequency,DWORD dwDuration)
{
#if !defined _WIN64
#define PIT8254_CNT2 0x42
#define PIT8254_MCR 0x43
#define PPI8255_PBO 0x61
BYTE bySpk = _inp(PPI8255_PBO); // get current status
if (dwFrequency != 0)
{
WORD wCount;
// limit low frequency
if (lFreq.QuadPart / 65535 >= dwFrequency)
dwFrequency = (DWORD) (lFreq.QuadPart / 65535) + 1;
// determine the timer frequency
wCount = (WORD) (lFreq.QuadPart / dwFrequency);
_outp(PIT8254_MCR,0xB6); // set up the timer
_outp(PIT8254_CNT2,wCount&0xff);
_outp(PIT8254_CNT2,wCount>>8);
_outp(PPI8255_PBO,bySpk | 0x03); // turn on the speaker
}
Sleep(dwDuration);
_outp(PPI8255_PBO,bySpk & 0xFC); // turn off the speaker
return;
#undef PIT8254_CNT2
#undef PIT8254_MCR
#undef PPI8255_PBO
#endif
}
static VOID Beeper(DWORD freq,DWORD dur)
{
if (bWaveBeep)
{
BeepWave(freq,dur); // wave output over sound card
}
else
{
OSVERSIONINFO version;
version.dwOSVersionInfoSize = sizeof(version);
GetVersionEx(&version);
if (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
{
BeepWin9x(freq,dur); // do it the hard way on '9x / Me
}
else // VER_PLATFORM_WIN32_NT
{
if (freq < 37) freq = 37; // low limit of freqency (NT)
_ASSERT(freq >= 0x25 && freq <= 0x7FFF);
Beep(freq,dur); // NT: ok, Windows 95: default sound or standard system beep
}
}
return;
}
VOID External(CHIPSET* w) // Beep patch
{
BYTE fbeep;
DWORD freq,dur;
freq = Npack(w->D,5); // frequency in Hz
dur = Npack(w->C,5); // duration in ms
Nread(&fbeep,SFLAG53_56,1); // fetch system flags -53 to -56
w->carry = TRUE; // setting of no beep
if (!(fbeep & 0x8) && freq) // bit -56 clear and frequency > 0 Hz
{
if (freq > 4400) freq = 4400; // high limit of HP (SX)
Beeper(freq,dur); // beeping
// estimate cpu cycles for beeping time (2MHz / 4MHz)
w->cycles += dur * ((cCurrentRomType=='S') ? 2000 : 4000);
// original routine return with...
w->P = 0; // P=0
w->intk = TRUE; // INTON
w->carry = FALSE; // RTNCC
}
w->pc = rstkpop();
return;
}
VOID RCKBp(CHIPSET* w) // ROM Check Beep patch
{
DWORD dw2F,dwCpuFreq;
DWORD freq,dur;
BYTE f,d;
f = w->C[1]; // f = freq ctl
d = w->C[0]; // d = duration ctl
if (cCurrentRomType == 'S') // Clarke chip with 48S ROM
{
// CPU strobe frequency @ RATE 14 = 1.97MHz
dwCpuFreq = ((14 + 1) * 524288) >> 2;
dw2F = f * 126 + 262; // F=f*63+131
}
else // York chip with 48G and later ROM
{
// CPU strobe frequency @ RATE 27 = 3.67MHz
// CPU strobe frequency @ RATE 29 = 3.93MHz
dwCpuFreq = ((27 + 1) * 524288) >> 2;
dw2F = f * 180 + 367; // F=f*90+183.5
}
freq = dwCpuFreq / dw2F;
dur = (dw2F * (256 - 16 * d)) * 1000 / 2 / dwCpuFreq;
if (freq > 4400) freq = 4400; // high limit of HP
Beeper(freq,dur); // beeping
// estimate cpu cycles for beeping time (2MHz / 4MHz)
w->cycles += dur * ((cCurrentRomType=='S') ? 2000 : 4000);
w->P = 0; // P=0
w->carry = FALSE; // RTNCC
w->pc = rstkpop();
return;
}