Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
ea99fb0584
11 changed files with 346 additions and 304 deletions
|
@ -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")
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 -
|
||||
|
|
|
@ -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);
|
||||
|
||||
//
|
||||
|
|
|
@ -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};
|
||||
|
|
110
source/Frame.cpp
110
source/Frame.cpp
|
@ -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);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -101,8 +101,6 @@ protected:
|
|||
UINT m_uSlot;
|
||||
};
|
||||
|
||||
#define IDEVENT_TIMER_MOUSE 1
|
||||
|
||||
namespace DIMouse
|
||||
{
|
||||
HRESULT DirectInputInit( HWND hDlg );
|
||||
|
|
Loading…
Add table
Reference in a new issue