Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Andrea Odetti 2017-10-29 16:27:20 +00:00
commit ea99fb0584
11 changed files with 346 additions and 304 deletions

View file

@ -9,8 +9,8 @@ https://github.com/AppleWin/AppleWin/issues/new
Tom Charlesworth
1.26.3.1 (Experimental) - 29 Sep 2017
------------------------------------
1.26.3.2 (Experimental) - 21 Oct 2017
-------------------------------------
Changes:
. [Bug #469] New -no-printscreen-key switch to prevent the PrintScreen key from being registered.
. [Bug #488] New -fs-height=<best|nnnn> switch.
@ -24,7 +24,11 @@ Changes:
. [PR #441] Update zlib to latest version 1.2.11
Fixes:
. [Bug #499] Flush current track (if dirty) before 'Send to CiderPress'
. [Bug #496] Support polling of Mockingboard's & Phasor's 6522 IFR.Timer1
. [Bug #492] UI fixes for full-screen & 2x windowed-mode
. [Bug #486] Peripheral card's expansion ROM ($C800-CFFF, eg. SSC) wasn't being correctly restored from a save-state
. [Bug #464] Fix for full-screen: bottom line missing when vertical resolution is 768
. [Bug #460] Debugger: F2 whilst debugger active will immediately stop the drive spinning (allowing disk swap)
. [Bug #456] Fix strange speaker clicks when changing configuration - DirectSound wasn't being uninitialised on a restart
. [Bug #452] Fix for unmounted HD now gives "NO DEVICE CONNECTED" (before gave "I/O ERROR")

View file

@ -252,8 +252,8 @@ DISK_ICON ICON "DISK.ICO"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,26,3,1
PRODUCTVERSION 1,26,3,1
FILEVERSION 1,26,3,4
PRODUCTVERSION 1,26,3,4
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -271,12 +271,12 @@ BEGIN
VALUE "Comments", "https://github.com/AppleWin"
VALUE "CompanyName", "AppleWin"
VALUE "FileDescription", "Apple //e Emulator for Windows"
VALUE "FileVersion", "1, 26, 3, 1"
VALUE "FileVersion", "1, 26, 3, 4"
VALUE "InternalName", "APPLEWIN"
VALUE "LegalCopyright", " 1994-2017 Michael O'Brien, Oliver Schmidt, Tom Charlesworth, Michael Pohoreski, Nick Westgate, Linards Ticmanis"
VALUE "OriginalFilename", "APPLEWIN.EXE"
VALUE "ProductName", "Apple //e Emulator"
VALUE "ProductVersion", "1, 26, 3, 1"
VALUE "ProductVersion", "1, 26, 3, 4"
END
END
BLOCK "VarFileInfo"

View file

@ -32,7 +32,6 @@
#include "AY8910.h"
#include "Applewin.h" // For g_fh
#include "Mockingboard.h" // For g_uTimer1IrqCount
#include "YamlHelper.h"
/* The AY white noise RNG algorithm is based on info from MAME's ay8910.c -

View file

@ -539,6 +539,7 @@ DWORD CpuExecute(const DWORD uCycles, const bool bVideoUpdate)
const DWORD uExecutedCycles = InternalCpuExecute(uCycles, bVideoUpdate);
MB_UpdateCycles(uExecutedCycles); // Update 6522s (NB. Do this before updating g_nCumulativeCycles below)
// NB. Ensures that 6522 regs are up-to-date for any potential save-state
UpdateEmulationTime(uExecutedCycles);
//

View file

@ -212,3 +212,5 @@ inline bool IsOriginal2E(void)
enum eBUTTON {BUTTON0=0, BUTTON1};
enum eBUTTONSTATE {BUTTON_UP=0, BUTTON_DOWN};
enum {IDEVENT_TIMER_MOUSE=1, IDEVENT_TIMER_100MSEC};

View file

@ -117,7 +117,6 @@ static int buttondown = -1;
static int buttonover = -1;
static int buttonx = BUTTONX;
static int buttony = BUTTONY;
static HRGN clipregion = (HRGN)0;
static HDC g_hFrameDC = (HDC)0;
static RECT framerect = {0,0,0,0};
@ -130,10 +129,13 @@ static BOOL helpquit = 0;
static BOOL g_bPaintingWindow = 0;
static HFONT smallfont = (HFONT)0;
static HWND tooltipwindow = (HWND)0;
static BOOL g_bUsingCursor = 0; // 1=AppleWin is using (hiding) the mouse-cursor
static BOOL g_bUsingCursor = FALSE; // TRUE = AppleWin is using (hiding) the mouse-cursor && restricting cursor to window - see SetUsingCursor()
static int viewportx = VIEWPORTX; // Default to Normal (non-FullScreen) mode
static int viewporty = VIEWPORTY; // Default to Normal (non-FullScreen) mode
static UINT_PTR g_TimerIDEvent_100msec = 0;
static UINT g_uCount100msec = 0;
static bool g_bShowingCursor = true;
static bool g_bLastCursorInAppleViewport = false;
@ -144,7 +146,7 @@ void RelayEvent (UINT message, WPARAM wparam, LPARAM lparam);
void ResetMachineState ();
void SetFullScreenMode ();
void SetNormalMode ();
void SetUsingCursor (BOOL);
static void SetUsingCursor(BOOL);
static bool FileExists(std::string strFilename);
bool g_bScrollLock_FullSpeed = false;
@ -164,11 +166,13 @@ static FULLSCREEN_SCALE_TYPE g_win_fullscreen_scale = 1;
static int g_win_fullscreen_offsetx = 0;
static int g_win_fullscreen_offsety = 0;
static bool g_bFrameActive = false;
// __ Prototypes __________________________________________________________________________________
static void DrawCrosshairs (int x, int y);
static void UpdateMouseInAppleViewport(int iOutOfBoundsX, int iOutOfBoundsY, int x=0, int y=0);
static void ScreenWindowResize(const bool bCtrlKey);
static void FrameResizeWindow(int nNewScale);
void DrawCrosshairs (int x, int y);
void UpdateMouseInAppleViewport(int iOutOfBoundsX, int iOutOfBoundsY, int x=0, int y=0);
void ScreenWindowResize(const bool bCtrlKey);
void FrameResizeWindow(int nNewScale);
// ==========================================================================
@ -307,7 +311,7 @@ static void FrameShowCursor(BOOL bShow)
// Called when:
// . Ctrl-Left mouse button
// . PAUSE pressed (when MODE_RUNNING)
// . AppleWin's main window is deactivated
// . AppleWin's main window is activated/deactivated
static void RevealCursor()
{
if (!sg_Mouse.IsActiveAndEnabled())
@ -326,6 +330,25 @@ static void RevealCursor()
g_bLastCursorInAppleViewport = false;
}
// Called when:
// . WM_MOUSEMOVE event
// . Switch from full-screen to normal (windowed) mode
// . AppleWin's main window is activated/deactivated
static void FullScreenRevealCursor(void)
{
if (!g_bIsFullScreen)
return;
if (sg_Mouse.IsActive())
return;
if (!g_bUsingCursor && !g_bShowingCursor)
{
FrameShowCursor(TRUE);
g_uCount100msec = 0;
}
}
//===========================================================================
#define LOADBUTTONBITMAP(bitmapname) LoadImage(g_hInstance,bitmapname, \
@ -795,7 +818,7 @@ void FrameDrawDiskStatus( HDC passdc )
HDC dc = (passdc ? passdc : GetDC(g_hFrameWindow));
int x = buttonx;
int y = buttony+BUTTONS*BUTTONCY+1;
int y = buttony+BUTTONS*BUTTONCY+4;
SelectObject(dc,smallfont);
SetBkMode(dc,OPAQUE);
@ -841,12 +864,12 @@ void FrameDrawDiskStatus( HDC passdc )
SetBkMode(dc,TRANSPARENT);
sprintf( text, "T%s", g_sTrackDrive1 );
TextOut(dc,x+6 ,y+32,text, strlen(text) );
TextOut(dc,x+6, y+32, text, strlen(text) );
sprintf( text, "S%s", g_sSectorDrive1 );
TextOut(dc,x+ 6,y+42, text, strlen(text) );
TextOut(dc,x+6, y+42, text, strlen(text) );
sprintf( text, "T%s", g_sTrackDrive2 );
TextOut(dc,x+26,y+32,text, strlen(text) );
TextOut(dc,x+26,y+32, text, strlen(text) );
sprintf( text, "S%s", g_sSectorDrive2 );
TextOut(dc,x+26,y+42, text, strlen(text) );
}
@ -1025,8 +1048,10 @@ LRESULT CALLBACK FrameWndProc (
case WM_ACTIVATE: // Sent when window is activated/deactivated. wParam indicates WA_ACTIVE, WA_INACTIVE, etc
// Eg. Deactivate when Config dialog is active, AppleWin app loses focus, etc
JoyReset();
SetUsingCursor(0);
SetUsingCursor(FALSE);
RevealCursor();
FullScreenRevealCursor();
g_bFrameActive = (wparam != WA_INACTIVE);
break;
case WM_ACTIVATEAPP: // Sent when different app's window is activated/deactivated.
@ -1045,11 +1070,17 @@ LRESULT CALLBACK FrameWndProc (
RegSaveValue(TEXT(REG_PREFS), TEXT(REGVALUE_PREF_WINDOW_X_POS), 1, framerect.left);
RegSaveValue(TEXT(REG_PREFS), TEXT(REGVALUE_PREF_WINDOW_Y_POS), 1, framerect.top);
FrameReleaseDC();
SetUsingCursor(0);
SetUsingCursor(FALSE);
if (helpquit) {
helpquit = 0;
HtmlHelp(NULL,NULL,HH_CLOSE_ALL,0);
}
if (g_TimerIDEvent_100msec)
{
BOOL bRes = KillTimer(g_hFrameWindow, g_TimerIDEvent_100msec);
LogFileOutput("KillTimer(g_TimerIDEvent_100msec), res=%d\n", bRes ? 1 : 0);
g_TimerIDEvent_100msec = 0;
}
LogFileOutput("WM_CLOSE (done)\n");
break;
@ -1233,7 +1264,7 @@ LRESULT CALLBACK FrameWndProc (
// Process is done in WM_KEYUP: VK_F1 VK_F2 VK_F3 VK_F4 VK_F5 VK_F6 VK_F7 VK_F8
if ((wparam >= VK_F1) && (wparam <= VK_F8) && (buttondown == -1))
{
SetUsingCursor(0);
SetUsingCursor(FALSE);
buttondown = wparam-VK_F1;
if (g_bIsFullScreen && (buttonover != -1)) {
if (buttonover != buttondown)
@ -1312,7 +1343,7 @@ LRESULT CALLBACK FrameWndProc (
}
else if (wparam == VK_PAUSE)
{
SetUsingCursor(0);
SetUsingCursor(FALSE);
switch (g_nAppMode)
{
case MODE_RUNNING:
@ -1362,8 +1393,7 @@ LRESULT CALLBACK FrameWndProc (
}
else
{
SetUsingCursor(0);
return 0; // TC: Why return early?
SetUsingCursor(FALSE);
}
}
break;
@ -1407,7 +1437,7 @@ LRESULT CALLBACK FrameWndProc (
{
if (wparam & (MK_CONTROL | MK_SHIFT))
{
SetUsingCursor(0);
SetUsingCursor(FALSE);
}
else
{
@ -1416,7 +1446,7 @@ LRESULT CALLBACK FrameWndProc (
}
else if ( ((x < buttonx) && JoyUsingMouse() && ((g_nAppMode == MODE_RUNNING) || (g_nAppMode == MODE_STEPPING))) )
{
SetUsingCursor(1);
SetUsingCursor(TRUE);
}
else if (sg_Mouse.IsActive())
{
@ -1525,6 +1555,8 @@ LRESULT CALLBACK FrameWndProc (
UpdateMouseInAppleViewport(iOutOfBoundsX, iOutOfBoundsY, x, y);
}
FullScreenRevealCursor();
RelayEvent(WM_MOUSEMOVE,wparam,lparam);
break;
}
@ -1549,6 +1581,21 @@ LRESULT CALLBACK FrameWndProc (
UpdateMouseInAppleViewport(iOutOfBoundsX, iOutOfBoundsY);
}
}
else if (wparam == IDEVENT_TIMER_100MSEC) // GH#504
{
if (g_bIsFullScreen
&& !sg_Mouse.IsActive() // Don't interfere if there's a mousecard present!
&& !g_bUsingCursor // Using mouse for joystick emulation (or mousecard restricted to window)
&& g_bShowingCursor
&& g_bFrameActive) // Frame inactive when eg. Config or 'Select Disk Image' dialogs are opened
{
g_uCount100msec++;
if (g_uCount100msec > 20) // Hide every 2sec of mouse inactivity
{
FrameShowCursor(FALSE);
}
}
}
break;
// VSCROLL
@ -1644,13 +1691,12 @@ LRESULT CALLBACK FrameWndProc (
}
}
}
if (g_bUsingCursor)
{
if (sg_Mouse.IsActive())
sg_Mouse.SetButton(BUTTON1, (message == WM_RBUTTONDOWN) ? BUTTON_DOWN : BUTTON_UP);
else
JoySetButton(BUTTON1, (message == WM_RBUTTONDOWN) ? BUTTON_DOWN : BUTTON_UP);
}
if (g_bUsingCursor && !sg_Mouse.IsActive())
JoySetButton(BUTTON1, (message == WM_RBUTTONDOWN) ? BUTTON_DOWN : BUTTON_UP);
else if (sg_Mouse.IsActive())
sg_Mouse.SetButton(BUTTON1, (message == WM_RBUTTONDOWN) ? BUTTON_DOWN : BUTTON_UP);
RelayEvent(message,wparam,lparam);
break;
@ -2215,6 +2261,8 @@ void SetFullScreenMode ()
//===========================================================================
void SetNormalMode ()
{
FullScreenRevealCursor(); // Do before clearing g_bIsFullScreen flag
buttonover = -1;
buttonx = BUTTONX;
buttony = BUTTONY;
@ -2242,8 +2290,12 @@ void SetUsingCursor (BOOL bNewValue)
return;
g_bUsingCursor = bNewValue;
if (g_bUsingCursor)
{
// Set TRUE when:
// . Using mouse for joystick emulation
// . Using mousecard and mouse is restricted to window
SetCapture(g_hFrameWindow);
RECT rect = { viewportx+2, // left
viewporty+2, // top
@ -2454,6 +2506,10 @@ void FrameCreateWindow(void)
g_hInstance,NULL );
SetupTooltipControls();
_ASSERT(g_TimerIDEvent_100msec == 0);
g_TimerIDEvent_100msec = SetTimer(g_hFrameWindow, IDEVENT_TIMER_100MSEC, 100, NULL);
LogFileOutput("FrameCreateWindow: SetTimer(), id=0x%08X\n", g_TimerIDEvent_100msec);
}
//===========================================================================

View file

@ -36,10 +36,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "YamlHelper.h"
#include "Video.h" // Needed by TK3000 //e, to refresh the frame at each |Mode| change
static bool g_bKeybBufferEnable = false;
#define KEY_OLD
static BYTE asciicode[2][10] = {
{0x08,0x0D,0x15,0x2F,0x00,0x00,0x00,0x00,0x00,0x00},
{0x08,0x0B,0x15,0x0A,0x00,0x00,0x00,0x00,0x00,0x7F}
@ -56,26 +52,7 @@ static bool g_bP8CapsLock = true; //Caps lock key of Pravets 8A/C
static int lastvirtkey = 0; // Current PC keycode
static BYTE keycode = 0; // Current Apple keycode
#ifdef KEY_OLD
// Original
static BOOL keywaiting = 0;
#else
// Buffered key input:
// - Needed on faster PCs where aliasing occurs during short/fast bursts of 6502 code.
// - Keyboard only sampled during 6502 execution, so if it's run too fast then key presses will be missed.
const int KEY_BUFFER_MIN_SIZE = 1;
const int KEY_BUFFER_MAX_SIZE = 2;
static int g_nKeyBufferSize = KEY_BUFFER_MAX_SIZE; // Circ key buffer size
static int g_nNextInIdx = 0;
static int g_nNextOutIdx = 0;
static int g_nKeyBufferCnt = 0;
static struct
{
int nVirtKey;
BYTE nAppleKey;
} g_nKeyBuffer[KEY_BUFFER_MAX_SIZE];
#endif
static BYTE g_nLastKey = 0x00;
@ -87,34 +64,9 @@ static BYTE g_nLastKey = 0x00;
void KeybReset()
{
#ifdef KEY_OLD
keywaiting = 0;
#else
g_nNextInIdx = 0;
g_nNextOutIdx = 0;
g_nKeyBufferCnt = 0;
g_nLastKey = 0x00;
g_nKeyBufferSize = g_bKeybBufferEnable ? KEY_BUFFER_MAX_SIZE : KEY_BUFFER_MIN_SIZE;
#endif
}
//===========================================================================
//void KeybSetBufferMode(bool bNewKeybBufferEnable)
//{
// if(g_bKeybBufferEnable == bNewKeybBufferEnable)
// return;
//
// g_bKeybBufferEnable = bNewKeybBufferEnable;
// KeybReset();
//}
//
//bool KeybGetBufferMode()
//{
// return g_bKeybBufferEnable;
//}
//===========================================================================
bool KeybGetAltStatus ()
{
@ -329,9 +281,6 @@ void KeybQueueKeypress (int key, BOOL bASCII)
// Note: VK_CANCEL is Control-Break
if ((key == VK_CANCEL) && (GetKeyState(VK_CONTROL) < 0))
{
#ifndef KEY_OLD
g_nNextInIdx = g_nNextOutIdx = g_nKeyBufferCnt = 0;
#endif
g_bFreshReset = true;
CtrlReset();
return;
@ -360,23 +309,8 @@ void KeybQueueKeypress (int key, BOOL bASCII)
keycode = asciicode[IS_APPLE2 ? 0 : 1][key - VK_LEFT]; // Convert to Apple arrow keycode
lastvirtkey = key;
}
#ifdef KEY_OLD
keywaiting = 1;
#else
bool bOverflow = false;
if(g_nKeyBufferCnt < g_nKeyBufferSize)
g_nKeyBufferCnt++;
else
bOverflow = true;
g_nKeyBuffer[g_nNextInIdx].nVirtKey = lastvirtkey;
g_nKeyBuffer[g_nNextInIdx].nAppleKey = keycode;
g_nNextInIdx = (g_nNextInIdx + 1) % g_nKeyBufferSize;
if(bOverflow)
g_nNextOutIdx = (g_nNextOutIdx + 1) % g_nKeyBufferSize;
#endif
}
//===========================================================================
@ -472,21 +406,7 @@ BYTE __stdcall KeybReadData (WORD, WORD, BYTE, BYTE, ULONG)
//
#ifdef KEY_OLD
return keycode | (keywaiting ? 0x80 : 0);
#else
BYTE nKey = g_nKeyBufferCnt ? 0x80 : 0;
if(g_nKeyBufferCnt)
{
nKey |= g_nKeyBuffer[g_nNextOutIdx].nAppleKey;
g_nLastKey = g_nKeyBuffer[g_nNextOutIdx].nAppleKey;
}
else
{
nKey |= g_nLastKey;
}
return nKey;
#endif
}
//===========================================================================
@ -506,19 +426,9 @@ BYTE __stdcall KeybReadFlag (WORD, WORD, BYTE, BYTE, ULONG)
//
#ifdef KEY_OLD
keywaiting = 0;
return keycode | ((GetKeyState(lastvirtkey) < 0) ? 0x80 : 0);
#else
BYTE nKey = (GetKeyState(g_nKeyBuffer[g_nNextOutIdx].nVirtKey) < 0) ? 0x80 : 0;
nKey |= g_nKeyBuffer[g_nNextOutIdx].nAppleKey;
if(g_nKeyBufferCnt)
{
g_nKeyBufferCnt--;
g_nNextOutIdx = (g_nNextOutIdx + 1) % g_nKeyBufferSize;
}
return nKey;
#endif
}
//===========================================================================

View file

@ -117,13 +117,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#define Phasor_SY6522A_Offset (1<<Phasor_SY6522A_CS)
#define Phasor_SY6522B_Offset (1<<Phasor_SY6522B_CS)
enum MockingboardUnitState_e {AY_NOP0, AY_NOP1, AY_INACTIVE, AY_READ, AY_NOP4, AY_NOP5, AY_WRITE, AY_LATCH};
struct SY6522_AY8910
{
SY6522 sy6522;
BYTE nAY8910Number;
BYTE nAYCurrentRegister;
BYTE nTimerStatus;
bool bTimer1Active;
bool bTimer2Active;
SSI263A SpeechChip;
MockingboardUnitState_e state; // Where a unit is a 6522+AY8910 pair (or for Phasor: 6522+2xAY8910)
};
@ -152,8 +156,8 @@ static SY6522_AY8910 g_MB[NUM_AY8910];
// Timer vars
static ULONG g_n6522TimerPeriod = 0;
static const UINT TIMERDEVICE_INVALID = -1;
static UINT g_nMBTimerDevice = TIMERDEVICE_INVALID; // SY6522 device# which is generating timer IRQ
static const UINT kTIMERDEVICE_INVALID = -1;
static UINT g_nMBTimerDevice = kTIMERDEVICE_INVALID; // SY6522 device# which is generating timer IRQ
static UINT64 g_uLastCumulativeCycles = 0;
// SSI263 vars:
@ -203,51 +207,65 @@ static DWORD g_dwMaxPhonemeLen = 0;
// When 6522 IRQ is *not* active use 60Hz update freq for MB voices
static const double g_f6522TimerPeriod_NoIRQ = CLK_6502 / 60.0; // Constant whatever the CLK is set to
//---------------------------------------------------------------------------
// External global vars:
bool g_bMBTimerIrqActive = false;
#ifdef _DEBUG
UINT32 g_uTimer1IrqCount = 0; // DEBUG
#endif
static bool g_bCritSectionValid = false; // Deleting CritialSection when not valid causes crash on Win98
static CRITICAL_SECTION g_CriticalSection; // To guard 6522's IFR
//---------------------------------------------------------------------------
// Forward refs:
static DWORD WINAPI SSI263Thread(LPVOID);
static void Votrax_Write(BYTE nDevice, BYTE nValue);
static double MB_GetFramePeriod(void);
//---------------------------------------------------------------------------
static void StartTimer(SY6522_AY8910* pMB)
static void StartTimer1(SY6522_AY8910* pMB)
{
// if((pMB->nAY8910Number & 1) != SY6522_DEVICE_A)
// return;
if((pMB->sy6522.IER & IxR_TIMER1) == 0x00)
return;
USHORT nPeriod = pMB->sy6522.TIMER1_LATCH.w;
// if(nPeriod <= 0xff) // Timer1L value has been written (but TIMER1H hasn't)
// return;
pMB->nTimerStatus = 1;
pMB->bTimer1Active = true;
// 6522 CLK runs at same speed as 6502 CLK
g_n6522TimerPeriod = nPeriod;
g_n6522TimerPeriod = pMB->sy6522.TIMER1_LATCH.w;
if (pMB->sy6522.IER & IxR_TIMER1) // Using 6522 interrupt
g_nMBTimerDevice = pMB->nAY8910Number;
else if (pMB->sy6522.ACR & RM_FREERUNNING) // Polling 6522 IFR
g_nMBTimerDevice = pMB->nAY8910Number;
}
// The assumption was that timer1 was only active if IER.TIMER1=1
// . Not true, since IFR can be polled (with IER.TIMER1=0)
static void StartTimer1_LoadStateV1(SY6522_AY8910* pMB)
{
if ((pMB->sy6522.IER & IxR_TIMER1) == 0x00)
return;
pMB->bTimer1Active = true;
// 6522 CLK runs at same speed as 6502 CLK
g_n6522TimerPeriod = pMB->sy6522.TIMER1_LATCH.w;
g_bMBTimerIrqActive = true;
g_nMBTimerDevice = pMB->nAY8910Number;
}
static void StopTimer1(SY6522_AY8910* pMB)
{
pMB->bTimer1Active = false;
g_nMBTimerDevice = kTIMERDEVICE_INVALID;
}
//-----------------------------------------------------------------------------
static void StopTimer(SY6522_AY8910* pMB)
static void StartTimer2(SY6522_AY8910* pMB)
{
pMB->nTimerStatus = 0;
g_bMBTimerIrqActive = false;
g_nMBTimerDevice = TIMERDEVICE_INVALID;
pMB->bTimer2Active = true;
// NB. Can't mimic StartTimer1() as that would stomp on global state
// TODO: Switch to per-device state
}
static void StopTimer2(SY6522_AY8910* pMB)
{
pMB->bTimer2Active = false;
}
//-----------------------------------------------------------------------------
@ -256,10 +274,11 @@ static void ResetSY6522(SY6522_AY8910* pMB)
{
memset(&pMB->sy6522,0,sizeof(SY6522));
if(pMB->nTimerStatus)
StopTimer(pMB);
StopTimer1(pMB);
StopTimer2(pMB);
pMB->nAYCurrentRegister = 0;
pMB->state = AY_INACTIVE;
}
//-----------------------------------------------------------------------------
@ -269,7 +288,7 @@ static void AY8910_Write(BYTE nDevice, BYTE nReg, BYTE nValue, BYTE nAYDevice)
g_bMB_RegAccessedFlag = true;
SY6522_AY8910* pMB = &g_MB[nDevice];
if((nValue & 4) == 0)
if ((nValue & 4) == 0)
{
// RESET: Reset AY8910 only
AY8910_reset(nDevice+2*nAYDevice);
@ -281,45 +300,59 @@ static void AY8910_Write(BYTE nDevice, BYTE nReg, BYTE nValue, BYTE nAYDevice)
const int nBC2 = 1; // Hardwired to +5V
int nBC1 = nValue & 1;
int nAYFunc = (nBDIR<<2) | (nBC2<<1) | nBC1;
enum {AY_NOP0, AY_NOP1, AY_INACTIVE, AY_READ, AY_NOP4, AY_NOP5, AY_WRITE, AY_LATCH};
MockingboardUnitState_e nAYFunc = (MockingboardUnitState_e) ((nBDIR<<2) | (nBC2<<1) | nBC1);
switch(nAYFunc)
if (pMB->state == AY_INACTIVE) // GH#320: functions only work from inactive state
{
case AY_INACTIVE: // 4: INACTIVE
break;
switch (nAYFunc)
{
case AY_INACTIVE: // 4: INACTIVE
break;
case AY_READ: // 5: READ FROM PSG (need to set DDRA to input)
break;
case AY_READ: // 5: READ FROM PSG (need to set DDRA to input)
break;
case AY_WRITE: // 6: WRITE TO PSG
_AYWriteReg(nDevice+2*nAYDevice, pMB->nAYCurrentRegister, pMB->sy6522.ORA);
break;
case AY_WRITE: // 6: WRITE TO PSG
_AYWriteReg(nDevice+2*nAYDevice, pMB->nAYCurrentRegister, pMB->sy6522.ORA);
break;
case AY_LATCH: // 7: LATCH ADDRESS
// http://www.worldofspectrum.org/forums/showthread.php?t=23327
// Selecting an unused register number above 0x0f puts the AY into a state where
// any values written to the data/address bus are ignored, but can be read back
// within a few tens of thousands of cycles before they decay to zero.
if(pMB->sy6522.ORA <= 0x0F)
pMB->nAYCurrentRegister = pMB->sy6522.ORA & 0x0F;
// else Pro-Mockingboard (clone from HK)
break;
case AY_LATCH: // 7: LATCH ADDRESS
// http://www.worldofspectrum.org/forums/showthread.php?t=23327
// Selecting an unused register number above 0x0f puts the AY into a state where
// any values written to the data/address bus are ignored, but can be read back
// within a few tens of thousands of cycles before they decay to zero.
if(pMB->sy6522.ORA <= 0x0F)
pMB->nAYCurrentRegister = pMB->sy6522.ORA & 0x0F;
// else Pro-Mockingboard (clone from HK)
break;
}
}
pMB->state = nAYFunc;
}
}
static void UpdateIFR(SY6522_AY8910* pMB)
static void UpdateIFR(SY6522_AY8910* pMB, BYTE clr_ifr, BYTE set_ifr=0)
{
pMB->sy6522.IFR &= 0x7F;
// Need critical section to avoid data-race: main thread & SSI263Thread can both access IFR
// . NB. Loading a save-state just directly writes into 6522.IFR (which is fine)
_ASSERT(g_bCritSectionValid);
if (g_bCritSectionValid) EnterCriticalSection(&g_CriticalSection);
{
pMB->sy6522.IFR &= ~clr_ifr;
pMB->sy6522.IFR |= set_ifr;
if(pMB->sy6522.IFR & pMB->sy6522.IER & 0x7F)
pMB->sy6522.IFR |= 0x80;
if (pMB->sy6522.IFR & pMB->sy6522.IER & 0x7F)
pMB->sy6522.IFR |= 0x80;
else
pMB->sy6522.IFR &= 0x7F;
}
if (g_bCritSectionValid) LeaveCriticalSection(&g_CriticalSection);
// Now update the IRQ signal from all 6522s
// . OR-sum of all active TIMER1, TIMER2 & SPEECH sources (from all 6522s)
UINT bIRQ = 0;
for(UINT i=0; i<NUM_SY6522; i++)
for (UINT i=0; i<NUM_SY6522; i++)
bIRQ |= g_MB[i].sy6522.IFR & 0x80;
// NB. Mockingboard generates IRQ on both 6522s:
@ -329,13 +362,9 @@ static void UpdateIFR(SY6522_AY8910* pMB)
// . I assume Phasor's 6522s just generate 6502 IRQs (not NMIs)
if (bIRQ)
{
CpuIrqAssert(IS_6522);
}
else
{
CpuIrqDeassert(IS_6522);
}
}
static void SY6522_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
@ -392,30 +421,29 @@ static void SY6522_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
/* Initiates timer1 & clears time-out of timer1 */
// Clear Timer Interrupt Flag.
pMB->sy6522.IFR &= ~IxR_TIMER1;
UpdateIFR(pMB);
UpdateIFR(pMB, IxR_TIMER1);
pMB->sy6522.TIMER1_LATCH.h = nValue;
pMB->sy6522.TIMER1_COUNTER.w = pMB->sy6522.TIMER1_LATCH.w;
StartTimer(pMB);
StartTimer1(pMB);
break;
case 0x07: // TIMER1H_LATCH
// Clear Timer1 Interrupt Flag.
UpdateIFR(pMB, IxR_TIMER1);
pMB->sy6522.TIMER1_LATCH.h = nValue;
pMB->sy6522.IFR &= ~IxR_TIMER1;
UpdateIFR(pMB);
break;
case 0x08: // TIMER2L
pMB->sy6522.TIMER2_LATCH.l = nValue;
break;
case 0x09: // TIMER2H
// Clear Timer2 Interrupt Flag.
pMB->sy6522.IFR &= ~IxR_TIMER2;
UpdateIFR(pMB);
UpdateIFR(pMB, IxR_TIMER2);
pMB->sy6522.TIMER2_LATCH.h = nValue;
pMB->sy6522.TIMER2_COUNTER.w = pMB->sy6522.TIMER2_LATCH.w;
StartTimer2(pMB);
break;
case 0x0a: // SERIAL_SHIFT
break;
@ -428,10 +456,7 @@ static void SY6522_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
case 0x0d: // IFR
// - Clear those bits which are set in the lower 7 bits.
// - Can't clear bit 7 directly.
nValue |= 0x80; // Set high bit
nValue ^= 0x7F; // Make mask
pMB->sy6522.IFR &= nValue;
UpdateIFR(pMB);
UpdateIFR(pMB, nValue);
break;
case 0x0e: // IER
if(!(nValue & 0x80))
@ -439,25 +464,28 @@ static void SY6522_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
// Clear those bits which are set in the lower 7 bits.
nValue ^= 0x7F;
pMB->sy6522.IER &= nValue;
UpdateIFR(pMB);
UpdateIFR(pMB, 0);
// Check if timer has been disabled.
if(pMB->sy6522.IER & IxR_TIMER1)
break;
// Check if active timer has been disabled:
if (((pMB->sy6522.IER & IxR_TIMER1) == 0) && pMB->bTimer1Active)
StopTimer1(pMB);
if(pMB->nTimerStatus == 0)
break;
// Stop timer
StopTimer(pMB);
if (((pMB->sy6522.IER & IxR_TIMER2) == 0) && pMB->bTimer2Active)
StopTimer2(pMB);
}
else
{
// Set those bits which are set in the lower 7 bits.
nValue &= 0x7F;
pMB->sy6522.IER |= nValue;
UpdateIFR(pMB);
StartTimer(pMB);
UpdateIFR(pMB, 0);
// Check if active timer changed from non-interrupt (polling IFR) to interrupt:
if ((pMB->sy6522.IER & IxR_TIMER1) && pMB->bTimer1Active)
StartTimer1(pMB);
if ((pMB->sy6522.IER & IxR_TIMER2) && pMB->bTimer2Active)
StartTimer2(pMB);
}
break;
case 0x0f: // ORA_NO_HS
@ -491,8 +519,7 @@ static BYTE SY6522_Read(BYTE nDevice, BYTE nReg)
break;
case 0x04: // TIMER1L_COUNTER
nValue = pMB->sy6522.TIMER1_COUNTER.l;
pMB->sy6522.IFR &= ~IxR_TIMER1; // Also clears Timer1 Interrupt Flag
UpdateIFR(pMB);
UpdateIFR(pMB, IxR_TIMER1);
break;
case 0x05: // TIMER1H_COUNTER
nValue = pMB->sy6522.TIMER1_COUNTER.h;
@ -505,8 +532,7 @@ static BYTE SY6522_Read(BYTE nDevice, BYTE nReg)
break;
case 0x08: // TIMER2L
nValue = pMB->sy6522.TIMER2_COUNTER.l;
pMB->sy6522.IFR &= ~IxR_TIMER2; // Also clears Timer2 Interrupt Flag
UpdateIFR(pMB);
UpdateIFR(pMB, IxR_TIMER2);
break;
case 0x09: // TIMER2H
nValue = pMB->sy6522.TIMER2_COUNTER.h;
@ -599,8 +625,7 @@ static void SSI263_Write(BYTE nDevice, BYTE nReg, BYTE nValue)
}
else
{
pMB->sy6522.IFR &= ~IxR_PERIPHERAL;
UpdateIFR(pMB);
UpdateIFR(pMB, IxR_PERIPHERAL);
}
pMB->SpeechChip.CurrentMode &= ~1; // Clear SSI263's D7 pin
@ -729,8 +754,7 @@ static void Votrax_Write(BYTE nDevice, BYTE nValue)
// !A/R: Acknowledge receipt of phoneme data (signal goes from high to low)
SY6522_AY8910* pMB = &g_MB[nDevice];
pMB->sy6522.IFR &= ~IxR_VOTRAX;
UpdateIFR(pMB);
UpdateIFR(pMB, IxR_VOTRAX);
g_nSSI263Device = nDevice;
@ -739,6 +763,9 @@ static void Votrax_Write(BYTE nDevice, BYTE nValue)
//===========================================================================
// Called by:
// . MB_UpdateCycles() - when g_nMBTimerDevice == {0,1,2,3}
// . MB_EndOfVideoFrame() - when g_nMBTimerDevice == kTIMERDEVICE_INVALID
static void MB_Update()
{
//char szDbg[200];
@ -977,8 +1004,7 @@ static DWORD WINAPI SSI263Thread(LPVOID lpParameter)
{
if((pMB->SpeechChip.CurrentMode != MODE_IRQ_DISABLED) && (pMB->sy6522.PCR == 0x0C))
{
pMB->sy6522.IFR |= IxR_PERIPHERAL;
UpdateIFR(pMB);
UpdateIFR(pMB, 0, IxR_PERIPHERAL);
pMB->SpeechChip.CurrentMode |= 1; // Set SSI263's D7 pin
}
}
@ -989,8 +1015,7 @@ static DWORD WINAPI SSI263Thread(LPVOID lpParameter)
{
// !A/R: Time-out of old phoneme (signal goes from low to high)
pMB->sy6522.IFR |= IxR_VOTRAX;
UpdateIFR(pMB);
UpdateIFR(pMB, 0, IxR_VOTRAX);
g_bVotraxPhoneme = false;
}
@ -1379,6 +1404,9 @@ void MB_Initialize()
MB_Reset();
LogFileOutput("MB_Initialize: MB_Reset()\n");
}
InitializeCriticalSection(&g_CriticalSection);
g_bCritSectionValid = true;
}
//-----------------------------------------------------------------------------
@ -1396,8 +1424,14 @@ void MB_Destroy()
{
MB_DSUninit();
for(int i=0; i<NUM_VOICES; i++)
for (int i=0; i<NUM_VOICES; i++)
delete [] ppAYVoiceBuffer[i];
if (g_bCritSectionValid)
{
DeleteCriticalSection(&g_CriticalSection);
g_bCritSectionValid = false;
}
}
//-----------------------------------------------------------------------------
@ -1405,7 +1439,7 @@ void MB_Destroy()
static void ResetState()
{
g_n6522TimerPeriod = 0;
g_nMBTimerDevice = TIMERDEVICE_INVALID;
g_nMBTimerDevice = kTIMERDEVICE_INVALID;
g_uLastCumulativeCycles = 0;
g_nSSI263Device = 0;
@ -1647,16 +1681,19 @@ void MB_StartOfCpuExecute()
// Called by ContinueExecution() at the end of every video frame
void MB_EndOfVideoFrame()
{
if(g_SoundcardType == CT_Empty)
if (g_SoundcardType == CT_Empty)
return;
if(!g_bMBTimerIrqActive)
if (g_nMBTimerDevice == kTIMERDEVICE_INVALID)
MB_Update();
}
//-----------------------------------------------------------------------------
// Called by CpuExecute() after every N opcodes (N = ~1000 @ 1MHz)
// Called by:
// . CpuExecute() every ~1000 @ 1MHz
// . CheckInterruptSources() every 128 cycles
// . MB_Read() / MB_Write()
void MB_UpdateCycles(ULONG uExecutedCycles)
{
if(g_SoundcardType == CT_Empty)
@ -1682,41 +1719,54 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
bool bTimer1Underflow = (!(OldTimer1 & 0x8000) && (pMB->sy6522.TIMER1_COUNTER.w & 0x8000));
bool bTimer2Underflow = (!(OldTimer2 & 0x8000) && (pMB->sy6522.TIMER2_COUNTER.w & 0x8000));
if( bTimer1Underflow && (g_nMBTimerDevice == i) && g_bMBTimerIrqActive )
if (!pMB->bTimer1Active && bTimer1Underflow)
{
#ifdef _DEBUG
g_uTimer1IrqCount++; // DEBUG
#endif
if ( (g_nMBTimerDevice == kTIMERDEVICE_INVALID) // StopTimer1() has been called
&& (pMB->sy6522.IFR & IxR_TIMER1) // Counter underflowed
&& ((pMB->sy6522.ACR & RUNMODE) == RM_ONESHOT) ) // One-shot mode
{
// Fix for Willy Byte - need to confirm that 6522 really does this!
// . It never accesses IER/IFR/TIMER1 regs to clear IRQ
UpdateIFR(pMB, IxR_TIMER1); // Deassert the TIMER IRQ
}
}
pMB->sy6522.IFR |= IxR_TIMER1;
UpdateIFR(pMB);
if (pMB->bTimer1Active && bTimer1Underflow)
{
UpdateIFR(pMB, 0, IxR_TIMER1);
// Do MB_Update() before StopTimer1()
if (g_nMBTimerDevice == i)
MB_Update();
if((pMB->sy6522.ACR & RUNMODE) == RM_ONESHOT)
{
// One-shot mode
// - Phasor's playback code uses one-shot mode
// - Willy Byte sets to one-shot to stop the timer IRQ
StopTimer(pMB);
StopTimer1(pMB);
}
else
{
// Free-running mode
// - Ultima4/5 change ACCESS_TIMER1 after a couple of IRQs into tune
pMB->sy6522.TIMER1_COUNTER.w = pMB->sy6522.TIMER1_LATCH.w;
StartTimer(pMB);
StartTimer1(pMB);
}
MB_Update();
}
else if ( bTimer1Underflow
&& !g_bMBTimerIrqActive // StopTimer() has been called
&& (pMB->sy6522.IFR & IxR_TIMER1) // IRQ
&& ((pMB->sy6522.ACR & RUNMODE) == RM_ONESHOT) ) // One-shot mode
else if (pMB->bTimer2Active && bTimer2Underflow)
{
// Fix for Willy Byte - need to confirm that 6522 really does this!
// . It never accesses IER/IFR/TIMER1 regs to clear IRQ
pMB->sy6522.IFR &= ~IxR_TIMER1; // Deassert the TIMER IRQ
UpdateIFR(pMB);
UpdateIFR(pMB, 0, IxR_TIMER2);
if((pMB->sy6522.ACR & RUNMODE) == RM_ONESHOT)
{
StopTimer2(pMB);
}
else
{
pMB->sy6522.TIMER2_COUNTER.w = pMB->sy6522.TIMER2_LATCH.w;
StartTimer2(pMB);
}
}
}
}
@ -1730,7 +1780,6 @@ SS_CARDTYPE MB_GetSoundcardType()
void MB_SetSoundcardType(SS_CARDTYPE NewSoundcardType)
{
// if ((NewSoundcardType == SC_UNINIT) || (g_SoundcardType == NewSoundcardType))
if (g_SoundcardType == NewSoundcardType)
return;
@ -1744,18 +1793,33 @@ void MB_SetSoundcardType(SS_CARDTYPE NewSoundcardType)
//-----------------------------------------------------------------------------
double MB_GetFramePeriod()
static double MB_GetFramePeriod(void)
{
return (g_bMBTimerIrqActive||(g_MB[0].sy6522.IFR & IxR_TIMER1)) ? (double)g_n6522TimerPeriod : g_f6522TimerPeriod_NoIRQ;
// TODO: Ideally remove this (slot-4) Phasor-IFR check: [*1]
// . It's for Phasor music player, which runs in one-shot mode:
// . MB_UpdateCycles()
// -> Timer1 underflows & StopTimer1() is called, which sets g_nMBTimerDevice == kTIMERDEVICE_INVALID
// . MB_EndOfVideoFrame(), and g_nMBTimerDevice == kTIMERDEVICE_INVALID
// -> MB_Update()
// -> MB_GetFramePeriod()
// NB. Removing this Phasor-IFR check means the occasional 'g_f6522TimerPeriod_NoIRQ' gets returned.
if ((g_nMBTimerDevice != kTIMERDEVICE_INVALID) ||
(g_bPhasorEnable && (g_MB[0].sy6522.IFR & IxR_TIMER1))) // [*1]
{
return (double)g_n6522TimerPeriod;
}
else
{
return g_f6522TimerPeriod_NoIRQ;
}
}
bool MB_IsActive()
{
if(!MockingboardVoice.bActive)
if (!MockingboardVoice.bActive)
return false;
// Ignore /g_bMBTimerIrqActive/ as timer's irq handler will access 6522 regs affecting /g_bMB_Active/
return g_bMB_Active;
}
@ -1825,8 +1889,9 @@ int MB_SetSnapshot_v1(const SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD /*dw
memcpy(AY8910_GetRegsPtr(nDeviceNum), &pSS->Unit[i].RegsAY8910, 16);
memcpy(&pMB->SpeechChip, &pSS->Unit[i].RegsSSI263, sizeof(SSI263A));
pMB->nAYCurrentRegister = pSS->Unit[i].nAYCurrentRegister;
pMB->state = AY_INACTIVE;
StartTimer(pMB); // Attempt to start timer
StartTimer1_LoadStateV1(pMB); // Attempt to start timer
//
@ -1840,8 +1905,7 @@ int MB_SetSnapshot_v1(const SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD /*dw
if((pMB->SpeechChip.CurrentMode != MODE_IRQ_DISABLED) && (pMB->sy6522.PCR == 0x0C) && (pMB->sy6522.IER & IxR_PERIPHERAL))
{
pMB->sy6522.IFR |= IxR_PERIPHERAL;
UpdateIFR(pMB);
UpdateIFR(pMB, 0, IxR_PERIPHERAL);
pMB->SpeechChip.CurrentMode |= 1; // Set SSI263's D7 pin
}
}
@ -1855,40 +1919,10 @@ int MB_SetSnapshot_v1(const SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD /*dw
//===========================================================================
static UINT DoWriteFile(const HANDLE hFile, const void* const pData, const UINT Length)
{
DWORD dwBytesWritten;
BOOL bRes = WriteFile( hFile,
pData,
Length,
&dwBytesWritten,
NULL);
if(!bRes || (dwBytesWritten != Length))
{
//dwError = GetLastError();
throw std::string("Card: save error");
}
return dwBytesWritten;
}
static UINT DoReadFile(const HANDLE hFile, void* const pData, const UINT Length)
{
DWORD dwBytesRead;
BOOL bRes = ReadFile( hFile,
pData,
Length,
&dwBytesRead,
NULL);
if (dwBytesRead != Length)
throw std::string("Card: file corrupt");
return dwBytesRead;
}
//===========================================================================
// Unit version history:
// 2: Added: Timer1 & Timer2 active
// 3: Added: Unit state
const UINT kUNIT_VERSION = 3;
const UINT NUM_MB_UNITS = 2;
const UINT NUM_PHASOR_UNITS = 2;
@ -1916,9 +1950,12 @@ const UINT NUM_PHASOR_UNITS = 2;
#define SS_YAML_KEY_SSI263_REG_FILTER_FREQ "Filter Frequency"
#define SS_YAML_KEY_SSI263_REG_CURRENT_MODE "Current Mode"
#define SS_YAML_KEY_AY_CURR_REG "AY Current Register"
#define SS_YAML_KEY_MB_UNIT_STATE "Unit State"
#define SS_YAML_KEY_TIMER1_IRQ "Timer1 IRQ Pending"
#define SS_YAML_KEY_TIMER2_IRQ "Timer2 IRQ Pending"
#define SS_YAML_KEY_SPEECH_IRQ "Speech IRQ Pending"
#define SS_YAML_KEY_TIMER1_ACTIVE "Timer1 Active"
#define SS_YAML_KEY_TIMER2_ACTIVE "Timer2 Active"
#define SS_YAML_KEY_PHASOR_UNIT "Unit"
#define SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR "Clock Scale Factor"
@ -1974,7 +2011,7 @@ void MB_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
UINT nDeviceNum = nMbCardNum*2;
SY6522_AY8910* pMB = &g_MB[nDeviceNum];
YamlSaveHelper::Slot slot(yamlSaveHelper, MB_GetSnapshotCardName(), uSlot, 1); // fixme: object should be just 1 Mockingboard card & it will know its slot
YamlSaveHelper::Slot slot(yamlSaveHelper, MB_GetSnapshotCardName(), uSlot, kUNIT_VERSION); // fixme: object should be just 1 Mockingboard card & it will know its slot
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
@ -1986,10 +2023,13 @@ void MB_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
AY8910_SaveSnapshot(yamlSaveHelper, nDeviceNum, std::string(""));
SaveSnapshotSSI263(yamlSaveHelper, pMB->SpeechChip);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_AY_CURR_REG, pMB->nAYCurrentRegister);
yamlSaveHelper.Save("%s: %s # Not supported\n", SS_YAML_KEY_TIMER1_IRQ, "false");
yamlSaveHelper.Save("%s: %s # Not supported\n", SS_YAML_KEY_TIMER2_IRQ, "false");
yamlSaveHelper.Save("%s: %s # Not supported\n", SS_YAML_KEY_SPEECH_IRQ, "false");
yamlSaveHelper.SaveBool(SS_YAML_KEY_TIMER1_ACTIVE, pMB->bTimer1Active);
yamlSaveHelper.SaveBool(SS_YAML_KEY_TIMER2_ACTIVE, pMB->bTimer2Active);
nDeviceNum++;
pMB++;
@ -2039,7 +2079,7 @@ bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
if (slot != 4 && slot != 5) // fixme
throw std::string("Card: wrong slot");
if (version != 1)
if (version < 1 || version > kUNIT_VERSION)
throw std::string("Card: wrong version");
AY8910UpdateSetCycles();
@ -2067,11 +2107,29 @@ bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
yamlLoadHelper.LoadBool(SS_YAML_KEY_TIMER2_IRQ); // Consume
yamlLoadHelper.LoadBool(SS_YAML_KEY_SPEECH_IRQ); // Consume
if (version >= 2)
{
pMB->bTimer1Active = yamlLoadHelper.LoadBool(SS_YAML_KEY_TIMER1_ACTIVE);
pMB->bTimer2Active = yamlLoadHelper.LoadBool(SS_YAML_KEY_TIMER2_ACTIVE);
}
pMB->state = AY_INACTIVE;
if (version >= 3)
pMB->state = (MockingboardUnitState_e) (yamlLoadHelper.LoadUint(SS_YAML_KEY_MB_UNIT_STATE) & 7);
yamlLoadHelper.PopMap();
//
StartTimer(pMB); // Attempt to start timer
if (version == 1)
{
StartTimer1_LoadStateV1(pMB); // Attempt to start timer
}
else // version >= 2
{
if (pMB->bTimer1Active)
StartTimer1(pMB); // Attempt to start timer
}
// Crude - currently only support a single speech chip
// FIX THIS:
@ -2083,8 +2141,7 @@ bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
if((pMB->SpeechChip.CurrentMode != MODE_IRQ_DISABLED) && (pMB->sy6522.PCR == 0x0C) && (pMB->sy6522.IER & IxR_PERIPHERAL))
{
pMB->sy6522.IFR |= IxR_PERIPHERAL;
UpdateIFR(pMB);
UpdateIFR(pMB, 0, IxR_PERIPHERAL);
pMB->SpeechChip.CurrentMode |= 1; // Set SSI263's D7 pin
}
}
@ -2110,7 +2167,7 @@ void Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
UINT nDeviceNum = 0;
SY6522_AY8910* pMB = &g_MB[0]; // fixme: Phasor uses MB's slot4(2x6522), slot4(2xSSI263), but slot4+5(4xAY8910)
YamlSaveHelper::Slot slot(yamlSaveHelper, Phasor_GetSnapshotCardName(), uSlot, 1); // fixme: object should be just 1 Mockingboard card & it will know its slot
YamlSaveHelper::Slot slot(yamlSaveHelper, Phasor_GetSnapshotCardName(), uSlot, kUNIT_VERSION); // fixme: object should be just 1 Mockingboard card & it will know its slot
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
@ -2126,10 +2183,13 @@ void Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
AY8910_SaveSnapshot(yamlSaveHelper, nDeviceNum+1, std::string("-B"));
SaveSnapshotSSI263(yamlSaveHelper, pMB->SpeechChip);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_AY_CURR_REG, pMB->nAYCurrentRegister);
yamlSaveHelper.Save("%s: %s # Not supported\n", SS_YAML_KEY_TIMER1_IRQ, "false");
yamlSaveHelper.Save("%s: %s # Not supported\n", SS_YAML_KEY_TIMER2_IRQ, "false");
yamlSaveHelper.Save("%s: %s # Not supported\n", SS_YAML_KEY_SPEECH_IRQ, "false");
yamlSaveHelper.SaveBool(SS_YAML_KEY_TIMER1_ACTIVE, pMB->bTimer1Active);
yamlSaveHelper.SaveBool(SS_YAML_KEY_TIMER2_ACTIVE, pMB->bTimer2Active);
nDeviceNum += 2;
pMB++;
@ -2141,7 +2201,7 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
if (slot != 4) // fixme
throw std::string("Card: wrong slot");
if (version != 1)
if (version < 1 || version > kUNIT_VERSION)
throw std::string("Card: wrong version");
g_PhasorClockScaleFactor = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR);
@ -2172,11 +2232,29 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
yamlLoadHelper.LoadBool(SS_YAML_KEY_TIMER2_IRQ); // Consume
yamlLoadHelper.LoadBool(SS_YAML_KEY_SPEECH_IRQ); // Consume
if (version >= 2)
{
pMB->bTimer1Active = yamlLoadHelper.LoadBool(SS_YAML_KEY_TIMER1_ACTIVE);
pMB->bTimer2Active = yamlLoadHelper.LoadBool(SS_YAML_KEY_TIMER2_ACTIVE);
}
pMB->state = AY_INACTIVE;
if (version >= 3)
pMB->state = (MockingboardUnitState_e) (yamlLoadHelper.LoadUint(SS_YAML_KEY_MB_UNIT_STATE) & 7);
yamlLoadHelper.PopMap();
//
StartTimer(pMB); // Attempt to start timer
if (version == 1)
{
StartTimer1_LoadStateV1(pMB); // Attempt to start timer
}
else // version >= 2
{
if (pMB->bTimer1Active)
StartTimer1(pMB); // Attempt to start timer
}
// Crude - currently only support a single speech chip
// FIX THIS:
@ -2188,8 +2266,7 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
if((pMB->SpeechChip.CurrentMode != MODE_IRQ_DISABLED) && (pMB->sy6522.PCR == 0x0C) && (pMB->sy6522.IER & IxR_PERIPHERAL))
{
pMB->sy6522.IFR |= IxR_PERIPHERAL;
UpdateIFR(pMB);
UpdateIFR(pMB, 0, IxR_PERIPHERAL);
pMB->SpeechChip.CurrentMode |= 1; // Set SSI263's D7 pin
}
}

View file

@ -1,10 +1,5 @@
#pragma once
extern bool g_bMBTimerIrqActive;
#ifdef _DEBUG
extern UINT32 g_uTimer1IrqCount; // DEBUG
#endif
void MB_Initialize();
void MB_Reinitialize();
void MB_Destroy();
@ -18,7 +13,6 @@ void MB_CheckIRQ();
void MB_UpdateCycles(ULONG uExecutedCycles);
SS_CARDTYPE MB_GetSoundcardType();
void MB_SetSoundcardType(SS_CARDTYPE NewSoundcardType);
double MB_GetFramePeriod();
bool MB_IsActive();
DWORD MB_GetVolume();
void MB_SetVolume(DWORD dwVolume, DWORD dwVolumeMax);

View file

@ -891,6 +891,7 @@ namespace DIMouse
return hr;
// Setup timer to read mouse position
_ASSERT(g_TimerIDEvent == 0);
g_TimerIDEvent = SetTimer(hDlg, IDEVENT_TIMER_MOUSE, 8, NULL); // 120Hz timer
LogFileOutput("DirectInputInit: SetTimer(), id=0x%08X\n", g_TimerIDEvent);
if (g_TimerIDEvent == 0)

View file

@ -101,8 +101,6 @@ protected:
UINT m_uSlot;
};
#define IDEVENT_TIMER_MOUSE 1
namespace DIMouse
{
HRESULT DirectInputInit( HWND hDlg );