Simplified and moved main-loop video update logic into Video.cpp.

Removed complex case below for:
. VideoHasRefreshed(), 'anyupdates'
. VideoCheckPage()

Detailed notes below.

---

Video updates in ContinueExecution() loop:

'anyupdates' gets set if there were any page-flip(s) in last ~17030 cycles:
	anyupdates |= VideoHasRefreshed();
ie. VideoRefreshScreen() was called outside of this loop.

If there's been a call to VideoRefreshScreen() outside of this loop,
  and then the video framebuffer gets written to, ie. VideoApparentlyDirty() returns TRUE,
then don't call VideoRefreshScreen() from this loop for 3 frames.
(If a VideoRefreshScreen() is called outside of this loop then restart the 3 frame count.)

So..
 if the game is flipping, the VideoApparentlyDirty() will return FALSE (since game writes to other framebuffer).
 if the game is not flipping, then VideoHasRefreshed() will return FALSE (since no flips occur).

Therefore this complex case above probably only arises at a boundary eg. when the game is transitioning between these 2 modes,
and so if the emulator does the very occasional screen update in this main loop, it is of no consequence.

(I guess this extra logic was to throttle video updates on very old slow machines)

---

VideoCheckPage(BOOL bForce) was called twice in main-loop:

UnexpectedPage if g_bVideoDisplayPage2 != SW_PAGE2

Once each time through the loop (ie. every 1ms), with bForce=0
	if UnexpectedPage && >500ms since last flip then VideoRefreshScreen()

Once each video frame (ie. ~17030 cycles) when not flipping, with bForce=1
	if UnexpectedPage then VideoRefreshScreen()

Basically this was all about supporting FullSpeed mode, and limiting the calls to VideoRefreshScreen().
This commit is contained in:
tomcw 2014-09-13 22:22:27 +01:00
parent d1dddbe813
commit bc45812f4e
5 changed files with 134 additions and 145 deletions

View File

@ -254,41 +254,13 @@ void ContinueExecution(void)
emulmsec_frac %= CLKS_PER_MS;
}
// DETERMINE WHETHER THE SCREEN WAS UPDATED THIS CLOCKTICK
static BOOL anyupdates = 0;
VideoCheckPage(0); // force=0
anyupdates |= VideoHasRefreshed(); // Only called from here. Returns & clears 'hasrefreshed' flag
//
if (g_dwCyclesThisFrame >= dwClksPerFrame)
{
g_dwCyclesThisFrame -= dwClksPerFrame;
VideoUpdateFlash();
static BOOL lastupdates[2] = {0,0};
if (!anyupdates && !lastupdates[0] && !lastupdates[1] && VideoApparentlyDirty())
{
VideoCheckPage(1); // force=1
static DWORD lasttime = 0;
DWORD currtime = GetTickCount();
if ((!g_bFullSpeed) ||
(currtime-lasttime >= (DWORD)(g_bGraphicsMode ? 100 : 25)))
{
VideoRefreshScreen();
lasttime = currtime;
}
}
lastupdates[1] = lastupdates[0];
lastupdates[0] = anyupdates;
anyupdates = 0;
VideoEndOfVideoFrame();
MB_EndOfVideoFrame();
}
//
if (!g_bFullSpeed)
{
SysClk_WaitTimer();

View File

@ -4711,9 +4711,9 @@ size_t Util_GetTextScreen ( char* &pText_ )
g_nTextScreen = 0;
memset( pBeg, 0, sizeof( g_aTextScreen ) );
int bBank2 = g_bVideoDisplayPage2;
LPBYTE g_pTextBank1 = MemGetAuxPtr (0x400 << (int)bBank2);
LPBYTE g_pTextBank0 = MemGetMainPtr(0x400 << (int)bBank2);
unsigned int uBank2 = VideoGetSWPAGE2() ? 1 : 0;
LPBYTE g_pTextBank1 = MemGetAuxPtr (0x400 << uBank2);
LPBYTE g_pTextBank0 = MemGetMainPtr(0x400 << uBank2);
for( int y = 0; y < 24; y++ )
{
@ -4724,7 +4724,7 @@ size_t Util_GetTextScreen ( char* &pText_ )
{
char c; // TODO: FormatCharTxtCtrl() ?
if ( g_bVideoMode & VF_80COL )
if ( VideoGetSW80COL() )
{ // AUX
c = g_pTextBank1[ nAddressStart ] & 0x7F;
c = RemapChar(c);
@ -4792,7 +4792,7 @@ int CmdTextSave (int nArgs)
_tcscpy( g_sMemoryLoadSaveFileName, g_aArgs[ 1 ].sArg );
else
{
if( g_bVideoMode & VF_80COL )
if( VideoGetSW80COL() )
sprintf( g_sMemoryLoadSaveFileName, "AppleWin_Text80.txt" );
else
sprintf( g_sMemoryLoadSaveFileName, "AppleWin_Text40.txt" );
@ -5997,11 +5997,11 @@ Update_t _ViewOutput( ViewVideoPage_t iPage, VideoUpdateFuncPtr_t pfUpdate );
Update_t _ViewOutput( ViewVideoPage_t iPage, VideoUpdateFuncPtr_t pfUpdate )
{
g_VideoForceFullRedraw = true;
VideoSetForceFullRedraw();
_Video_Dirty();
switch( iPage )
{
case VIEW_PAGE_X: _Video_SetupBanks( g_bVideoDisplayPage2 ); break; // Page Current
case VIEW_PAGE_X: _Video_SetupBanks( VideoGetSWPAGE2() ); break; // Page Current
case VIEW_PAGE_1: _Video_SetupBanks( false ); break; // Page 1
case VIEW_PAGE_2: _Video_SetupBanks( true ); break; // Page 2 !
default:

View File

@ -2735,38 +2735,38 @@ void DrawSoftSwitches( int iSoftSwitch )
// GR / TEXT
// GRAPH/TEXT
// TEXT ON/OFF
sprintf( sText, !(g_bVideoMode & VF_TEXT) ? "GR / ----" : "-- / TEXT" );
sprintf( sText, !VideoGetSWTEXT() ? "GR / ----" : "-- / TEXT" );
PrintTextCursorY( sText, rect );
// $C052 / $C053 = MIXEDOFF/MIXEDON = SW.MIXCLR/SW.MIXSET
// FULL/MIXED
// MIX OFF/ON
sprintf( sText, !(g_bVideoMode & VF_MIXED) ? "FULL/-----" : "----/MIXED" );
sprintf( sText, !VideoGetSWMIXED() ? "FULL/-----" : "----/MIXED" );
PrintTextCursorY( sText, rect );
// $C054 / $C055 = PAGE1/PAGE2 = PAGE2OFF/PAGE2ON = SW.LOWSCR/SW.HISCR
// PAGE 1 / 2
sprintf( sText, !(g_bVideoMode & VF_PAGE2) ? "PAGE 1 / -" : "PAGE - / 2" );
sprintf( sText, !VideoGetSWPAGE2() ? "PAGE 1 / -" : "PAGE - / 2" );
PrintTextCursorY( sText, rect );
// $C056 / $C057 LORES/HIRES = HIRESOFF/HIRESON = SW.LORES/SW.HIRES
// LO / HIRES
// LO / -----
// -- / HIRES
sprintf( sText, !(g_bVideoMode & VF_HIRES) ? "LO /-- RES" : "---/HI RES" );
sprintf( sText, !VideoGetSWHIRES() ? "LO /-- RES" : "---/HI RES" );
PrintTextCursorY( sText, rect );
PrintTextCursorY( "", rect );
// Extended soft switches
sprintf( sText, !(g_bVideoMode & VF_80COL) ? "40 / -- COL" : "-- / 80 COL" );
sprintf( sText, !VideoGetSW80COL() ? "40 / -- COL" : "-- / 80 COL" );
PrintTextCursorY( sText, rect );
sprintf(sText, (g_nAltCharSetOffset == 0) ? "ASCII/-----" : "-----/MOUSE" );
sprintf(sText, VideoGetSWAltCharSet() ? "ASCII/-----" : "-----/MOUSE" );
PrintTextCursorY( sText, rect );
// 280/560 HGR
sprintf(sText, !(g_bVideoMode & VF_DHIRES) ? "HGR / ----" : "--- / DHGR" );
sprintf(sText, !VideoGetSWDHIRES() ? "HGR / ----" : "--- / DHGR" );
PrintTextCursorY( sText, rect );
#else //SOFTSWITCH_OLD
// See: VideoSetMode()
@ -2778,25 +2778,25 @@ void DrawSoftSwitches( int iSoftSwitch )
bool bSet;
// $C050 / $C051 = TEXTOFF/TEXTON = SW.TXTCLR/SW.TXTSET
bSet = !(g_bVideoMode & VF_TEXT);
bSet = !VideoGetSWTEXT();
_DrawSoftSwitch( rect, 0xC050, bSet, NULL, "GR.", "TEXT" );
// $C052 / $C053 = MIXEDOFF/MIXEDON = SW.MIXCLR/SW.MIXSET
// FULL/MIXED
// MIX OFF/ON
bSet = !(g_bVideoMode & VF_MIXED);
bSet = !VideoGetSWMIXED();
_DrawSoftSwitch( rect, 0xC052, bSet, NULL, "FULL", "MIX" );
// $C054 / $C055 = PAGE1/PAGE2 = PAGE2OFF/PAGE2ON = SW.LOWSCR/SW.HISCR
// PAGE 1 / 2
bSet = !(g_bVideoMode & VF_PAGE2);
bSet = !VideoGetSWPAGE2();
_DrawSoftSwitch( rect, 0xC054, bSet, "PAGE ", "1", "2" );
// $C056 / $C057 LORES/HIRES = HIRESOFF/HIRESON = SW.LORES/SW.HIRES
// LO / HIRES
// LO / -----
// -- / HIRES
bSet = !(g_bVideoMode & VF_HIRES);
bSet = !VideoGetSWHIRES();
_DrawSoftSwitch( rect, 0xC056, bSet, NULL, "LO", "HI", "RES" );
DebuggerSetColorBG( DebuggerGetColor( BG_INFO ));
@ -2805,20 +2805,20 @@ void DrawSoftSwitches( int iSoftSwitch )
// 280/560 HGR
// C05E = ON, C05F = OFF
bSet = (g_bVideoMode & VF_DHIRES) ? true : false;
bSet = VideoGetSWDHIRES();
_DrawSoftSwitch( rect, 0xC05E, bSet, NULL, "DHGR", "HGR" );
// Extended soft switches
// C00C = off, C00D = on
bSet = !(g_bVideoMode & VF_80COL);
bSet = !VideoGetSW80COL();
_DrawSoftSwitch( rect, 0xC00C, bSet, "Col", "40", "80" );
// C00E = off, C00F = on
bSet = (g_nAltCharSetOffset == 0);
bSet = VideoGetSWAltCharSet();
_DrawSoftSwitch( rect, 0xC00E, bSet, NULL, "ASC", "MOUS" ); // ASCII/MouseText
// C000 = 80STOREOFF, C001 = 80STOREON
bSet = !(g_bVideoMode & VF_MASK2);
bSet = !VideoGetSW80STORE();
_DrawSoftSwitch( rect, 0xC000, bSet, "80Sto", "0", "1" );
#endif // SOFTSWITCH_OLD
}

View File

@ -197,10 +197,21 @@ const BYTE DoubleHiresPalIndex[16] = {
const int SRCOFFS_DHIRES = (SRCOFFS_HIRES + 512); // 1168
const int SRCOFFS_TOTAL = (SRCOFFS_DHIRES + 2560); // 3278
enum VideoFlag_e
{
VF_80COL = 0x00000001,
VF_DHIRES = 0x00000002,
VF_HIRES = 0x00000004,
VF_80STORE= 0x00000008,
VF_MIXED = 0x00000010,
VF_PAGE2 = 0x00000020,
VF_TEXT = 0x00000040
};
#define SW_80COL (g_bVideoMode & VF_80COL)
#define SW_DHIRES (g_bVideoMode & VF_DHIRES)
#define SW_HIRES (g_bVideoMode & VF_HIRES)
#define SW_MASK2 (g_bVideoMode & VF_MASK2)
#define SW_80STORE (g_bVideoMode & VF_80STORE)
#define SW_MIXED (g_bVideoMode & VF_MIXED)
#define SW_PAGE2 (g_bVideoMode & VF_PAGE2)
#define SW_TEXT (g_bVideoMode & VF_TEXT)
@ -261,24 +272,21 @@ static BYTE colormixbuffer[6];
static WORD colormixmap[6][6][6];
//
int g_nAltCharSetOffset = 0; // alternate character set
static int g_nAltCharSetOffset = 0; // alternate character set
bool g_bVideoDisplayPage2 = 0;
/*bool*/ UINT g_VideoForceFullRedraw = 1;
static /*bool*/ UINT g_VideoForceFullRedraw = 1;
static LPBYTE framebufferaddr = (LPBYTE)0;
static LONG g_nFrameBufferPitch = 0;
BOOL g_bGraphicsMode = 0;
static BOOL hasrefreshed = 0;
static DWORD lastpageflip = 0;
COLORREF monochrome = RGB(0xC0,0xC0,0xC0);
static BOOL rebuiltsource = 0;
static LPBYTE vidlastmem = NULL;
int g_bVideoMode = VF_TEXT;
static int g_bVideoMode = VF_TEXT;
DWORD g_eVideoType = VT_COLOR_TVEMU;
DWORD g_uHalfScanLines = true; // drop 50% scan lines for a more authentic look
DWORD g_uHalfScanLines = 1; // drop 50% scan lines for a more authentic look
static bool g_bTextFlashState = false;
@ -1924,8 +1932,8 @@ BOOL VideoApparentlyDirty ()
return 1;
DWORD address = (SW_HIRES && !SW_TEXT)
? (0x20 << (int)g_bVideoDisplayPage2)
: (0x04 << (int)g_bVideoDisplayPage2);
? (0x20 << (SW_PAGE2 ? 1 : 0))
: (0x04 << (SW_PAGE2 ? 1 : 0));
DWORD length = (SW_HIRES && !SW_TEXT) ? 0x20 : 0x4;
while (length--)
if (*(memdirty+(address++)) & 2)
@ -1938,7 +1946,7 @@ BOOL VideoApparentlyDirty ()
// Scan visible text page for any flashing chars
if((SW_TEXT || SW_MIXED) && (g_nAltCharSetOffset == 0))
{
BYTE* pnMemText = MemGetMainPtr(0x400 << (int)g_bVideoDisplayPage2);
BYTE* pnMemText = MemGetMainPtr(0x400 << (SW_PAGE2 ? 1 : 0));
// Scan 8 long-lines of 120 chars (at 128 char offsets):
// . Skip 8-char holes in TEXT
@ -2154,25 +2162,6 @@ BYTE VideoCheckMode (WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles)
//===========================================================================
// Check if we should call VideoRefreshScreen() based on unexpected page
// - Only called from 2 places in main ContinueExecution() loop
void VideoCheckPage(BOOL force)
{
const bool bUnexpectedPage = (g_bVideoDisplayPage2 != (SW_PAGE2 != 0));
//_ASSERT(!bUnexpectedPage); // [TC] Q: When does this happen? A: EG. When page-flipping && Scroll-Lock is pressed
if (bUnexpectedPage && // Unexpected page &&
(force || (emulmsec-lastpageflip > 500))) // force || >500ms since last flip
{
g_bVideoDisplayPage2 = (SW_PAGE2 != 0);
VideoRefreshScreen();
hasrefreshed = 1;
lastpageflip = emulmsec;
}
}
//===========================================================================
/*
// Drol expects = 80
68DE A5 02 LDX #02
@ -2384,13 +2373,6 @@ void VideoDisplayLogo ()
DeleteObject(font);
}
//===========================================================================
BOOL VideoHasRefreshed () {
BOOL result = hasrefreshed;
hasrefreshed = 0;
return result;
}
//===========================================================================
void VideoRealizePalette(HDC dc)
{
@ -2505,7 +2487,7 @@ VideoUpdateFuncPtr_t VideoRefreshScreen ()
// IN THE FRAME BUFFER. MARK CELLS IN WHICH REDRAWING HAS TAKEN PLACE AS
// DIRTY.
_Video_Dirty();
_Video_SetupBanks( g_bVideoDisplayPage2 );
_Video_SetupBanks( SW_PAGE2 != 0 );
VideoUpdateFuncPtr_t pfUpdate = SW_TEXT
? SW_80COL
@ -2694,15 +2676,8 @@ void VideoReinitialize ()
void VideoResetState ()
{
g_nAltCharSetOffset = 0;
g_bVideoDisplayPage2 = 0;
g_bVideoMode = VF_TEXT;
g_VideoForceFullRedraw = 1;
#if 0 // Debug HGR2 without having to exec 6502 code
g_bVideoDisplayPage2 = 1;
g_bVideoMode = VF_TEXT | VF_HIRES;
#endif
}
@ -2711,10 +2686,12 @@ BYTE VideoSetMode (WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCycles)
{
address &= 0xFF;
DWORD oldpage2 = SW_PAGE2;
int oldvalue = g_nAltCharSetOffset+(int)(g_bVideoMode & ~(VF_MASK2 | VF_PAGE2));
switch (address) {
case 0x00: g_bVideoMode &= ~VF_MASK2; break;
case 0x01: g_bVideoMode |= VF_MASK2; break;
int oldvalue = g_nAltCharSetOffset+(int)(g_bVideoMode & ~(VF_80STORE | VF_PAGE2));
switch (address)
{
case 0x00: g_bVideoMode &= ~VF_80STORE; break;
case 0x01: g_bVideoMode |= VF_80STORE; break;
case 0x0C: if (!IS_APPLE2) g_bVideoMode &= ~VF_80COL; break;
case 0x0D: if (!IS_APPLE2) g_bVideoMode |= VF_80COL; break;
case 0x0E: if (!IS_APPLE2) g_nAltCharSetOffset = 0; break; // Alternate char set off
@ -2730,29 +2707,21 @@ BYTE VideoSetMode (WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCycles)
case 0x5E: if (!IS_APPLE2) g_bVideoMode |= VF_DHIRES; break;
case 0x5F: if (!IS_APPLE2) g_bVideoMode &= ~VF_DHIRES; break;
}
if (SW_MASK2)
if (SW_80STORE)
g_bVideoMode &= ~VF_PAGE2;
if (oldvalue != g_nAltCharSetOffset+(int)(g_bVideoMode & ~(VF_MASK2 | VF_PAGE2))) {
g_bGraphicsMode = !SW_TEXT;
g_VideoForceFullRedraw = 1;
}
if (g_bFullSpeed && oldpage2 && !SW_PAGE2) {
static DWORD lasttime = 0;
DWORD currtime = GetTickCount();
if (currtime-lasttime >= 20)
lasttime = currtime;
else
oldpage2 = SW_PAGE2;
if (oldvalue != g_nAltCharSetOffset+(int)(g_bVideoMode & ~(VF_80STORE | VF_PAGE2)))
{
g_VideoForceFullRedraw = 1;
}
if (oldpage2 != SW_PAGE2)
{
g_bVideoDisplayPage2 = (SW_PAGE2 != 0);
if (!g_VideoForceFullRedraw)
{
#if 1
VideoRefreshScreen();
hasrefreshed = 1;
#else
g_VideoForceFullRedraw = 1; // GH#129,GH204: Defer the redraw until the main ContinueExecution() loop (TODO: What effect does this have on other games?)
#endif
@ -2766,7 +2735,7 @@ BYTE VideoSetMode (WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCycles)
//===========================================================================
// Called at 60Hz (every 16.666ms)
void VideoUpdateFlash()
static void VideoUpdateFlash()
{
static UINT nTextFlashCnt = 0;
@ -2788,11 +2757,75 @@ void VideoUpdateFlash()
//===========================================================================
bool VideoGetSW80COL()
// Called from main-loop every 17030 cycles (ie. 60Hz when CPU = 1MHz)
void VideoEndOfVideoFrame(void)
{
VideoUpdateFlash(); // TODO: Flash rate should be constant (regardless of CPU speed)
if (!VideoApparentlyDirty())
return;
// Apple II is not page flipping...
static DWORD dwLastTime = 0;
DWORD dwCurrTime = GetTickCount();
if (!g_bFullSpeed ||
(dwCurrTime-dwLastTime >= 100)) // FullSpeed: update every 100ms
{
VideoRefreshScreen();
dwLastTime = dwCurrTime;
}
}
//===========================================================================
bool VideoGetSW80COL(void)
{
return SW_80COL ? true : false;
}
bool VideoGetSWDHIRES(void)
{
return SW_DHIRES ? true : false;
}
bool VideoGetSWHIRES(void)
{
return SW_HIRES ? true : false;
}
bool VideoGetSW80STORE(void)
{
return SW_80STORE ? true : false;
}
bool VideoGetSWMIXED(void)
{
return SW_MIXED ? true : false;
}
bool VideoGetSWPAGE2(void)
{
return SW_PAGE2 ? true : false;
}
bool VideoGetSWTEXT(void)
{
return SW_TEXT ? true : false;
}
bool VideoGetSWAltCharSet(void)
{
return g_nAltCharSetOffset == 0;
}
//===========================================================================
void VideoSetForceFullRedraw(void)
{
g_VideoForceFullRedraw = 1;
}
//===========================================================================
DWORD VideoGetSnapshot(SS_IO_Video* pSS)
@ -2808,12 +2841,6 @@ DWORD VideoSetSnapshot(SS_IO_Video* pSS)
{
g_nAltCharSetOffset = !pSS->bAltCharSet ? 0 : 256;
g_bVideoMode = pSS->dwVidMode;
//
g_bGraphicsMode = !SW_TEXT;
g_bVideoDisplayPage2 = (SW_PAGE2 != 0);
return 0;
}

View File

@ -19,17 +19,6 @@
extern TCHAR g_aVideoChoices[];
extern char *g_apVideoModeDesc[ NUM_VIDEO_MODES ];
enum VideoFlag_e
{
VF_80COL = 0x00000001,
VF_DHIRES = 0x00000002,
VF_HIRES = 0x00000004,
VF_MASK2 = 0x00000008,
VF_MIXED = 0x00000010,
VF_PAGE2 = 0x00000020,
VF_TEXT = 0x00000040
};
enum AppleFont_e
{
// 40-Column mode is 1x Zoom (default)
@ -57,15 +46,11 @@ enum AppleFont_e
extern HBITMAP g_hLogoBitmap;
extern BOOL g_bGraphicsMode;
extern COLORREF monochrome; // saved
extern DWORD g_eVideoType; // saved
extern DWORD g_uHalfScanLines; // saved
extern LPBYTE g_pFramebufferbits;
extern int g_nAltCharSetOffset;
extern int g_bVideoMode; // g_bVideoMode
typedef bool (*VideoUpdateFuncPtr_t)(int,int,int,int,int);
// Prototypes _______________________________________________________
@ -74,12 +59,10 @@ void CreateColorMixMap();
BOOL VideoApparentlyDirty ();
void VideoBenchmark ();
void VideoCheckPage (BOOL);
void VideoChooseColor ();
void VideoDestroy ();
void VideoDrawLogoBitmap(HDC hDstDC, int xoff, int yoff, int srcw, int srch, int scale);
void VideoDisplayLogo ();
BOOL VideoHasRefreshed ();
void VideoInitialize ();
void VideoRealizePalette (HDC);
VideoUpdateFuncPtr_t VideoRedrawScreen (UINT);
@ -89,15 +72,22 @@ void VideoReinitialize ();
void VideoResetState ();
WORD VideoGetScannerAddress(bool* pbVblBar_OUT, const DWORD uExecutedCycles);
bool VideoGetVbl(DWORD uExecutedCycles);
void VideoUpdateFlash();
bool VideoGetSW80COL();
void VideoEndOfVideoFrame(void);
bool VideoGetSW80COL(void);
bool VideoGetSWDHIRES(void);
bool VideoGetSWHIRES(void);
bool VideoGetSW80STORE(void);
bool VideoGetSWMIXED(void);
bool VideoGetSWPAGE2(void);
bool VideoGetSWTEXT(void);
bool VideoGetSWAltCharSet(void);
void VideoSetForceFullRedraw(void);
DWORD VideoGetSnapshot(SS_IO_Video* pSS);
DWORD VideoSetSnapshot(SS_IO_Video* pSS);
extern bool g_bVideoDisplayPage2;
extern /*bool*/ UINT g_VideoForceFullRedraw;
void _Video_Dirty();
void _Video_RedrawScreen( VideoUpdateFuncPtr_t update, bool bMixed = false );
void _Video_SetupBanks( bool bBank2 );