diff --git a/AppleWinExpress2015.vcxproj b/AppleWinExpress2015.vcxproj
index da8de854..ad61ff30 100644
--- a/AppleWinExpress2015.vcxproj
+++ b/AppleWinExpress2015.vcxproj
@@ -369,6 +369,7 @@
true
source\cpu;source\emulator;source\debugger;zlib;zip_lib;libyaml\include;%(AdditionalIncludeDirectories)
MultiThreadedDebug
+ Default
Windows
diff --git a/bin/History.txt b/bin/History.txt
index cce7af1e..fdf84d96 100644
--- a/bin/History.txt
+++ b/bin/History.txt
@@ -8,10 +8,17 @@ https://github.com/AppleWin/AppleWin/issues/new
Tom Charlesworth
+1.28.8.0 - 28 Jun 2019
+----------------------
+. [Change #648] Support 50Hz(PAL) video refresh rate and implicitly PAL 1.018MHz.
+ - NB. TV video modes still use NTSC rendering.
+. [Bug #656] Fix for PAGE1/2 ($C054/55) not having a 1 cycle delay.
+
+
1.28.7.0 - 15 Jun 2019
----------------------
. [Bug #654] Fix for Sather's "Little Text Window" not rendering correctly.
-. [Bug #654] Fix for 6522 TIMER1's period to be N+2 cycles.
+. [Bug #652] Fix for 6522 TIMER1's period to be N+2 cycles.
1.28.6.0 - 2 Jun 2019
diff --git a/resource/Applewin.rc b/resource/Applewin.rc
index 2e2f4a68..a20b23a0 100644
--- a/resource/Applewin.rc
+++ b/resource/Applewin.rc
@@ -111,6 +111,7 @@ BEGIN
CTEXT "2.0",IDC_2_0_MHz,96,180,20,10
RTEXT "Fastest",IDC_MAX_MHz,150,180,29,10
PUSHBUTTON "&Benchmark Emulator",IDC_BENCHMARK,15,194,85,15
+ CONTROL "50Hz video",IDC_CHECK_50HZ_VIDEO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,142,141,51,10
END
IDD_PROPPAGE_INPUT DIALOGEX 0, 0, 210, 215
diff --git a/resource/resource.h b/resource/resource.h
index 3b6df8e2..7c6b5402 100644
--- a/resource/resource.h
+++ b/resource/resource.h
@@ -116,6 +116,7 @@
#define IDC_COMBO_DISK2 1081
#define IDC_CHECK_FS_SHOW_SUBUNIT_STATUS 1082
#define IDC_CHECK_VERTICAL_BLEND 1083
+#define IDC_CHECK_50HZ_VIDEO 1084
#define IDM_EXIT 40001
#define IDM_HELP 40002
#define IDM_ABOUT 40003
diff --git a/resource/version.h b/resource/version.h
index 57effbfb..bb51944c 100644
--- a/resource/version.h
+++ b/resource/version.h
@@ -1,4 +1,4 @@
-#define APPLEWIN_VERSION 1,28,7,0
+#define APPLEWIN_VERSION 1,28,8,0
#define xstr(a) str(a)
#define str(a) #a
diff --git a/source/Applewin.cpp b/source/Applewin.cpp
index 2a879c3e..17270177 100644
--- a/source/Applewin.cpp
+++ b/source/Applewin.cpp
@@ -91,7 +91,7 @@ bool g_bRestart = false;
bool g_bRestartFullScreen = false;
DWORD g_dwSpeed = SPEED_NORMAL; // Affected by Config dialog's speed slider bar
-double g_fCurrentCLK6502 = CLK_6502; // Affected by Config dialog's speed slider bar
+double g_fCurrentCLK6502 = CLK_6502_NTSC; // Affected by Config dialog's speed slider bar
static double g_fMHz = 1.0; // Affected by Config dialog's speed slider bar
int g_nCpuCyclesFeedback = 0;
@@ -345,6 +345,7 @@ static void ContinueExecution(void)
//
+ const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame();
if (g_dwCyclesThisFrame >= dwClksPerFrame)
{
g_dwCyclesThisFrame -= dwClksPerFrame;
@@ -376,14 +377,21 @@ void SingleStep(bool bReinit)
//===========================================================================
+double Get6502BaseClock(void)
+{
+ return (GetVideoRefreshRate() == VR_50HZ) ? CLK_6502_PAL : CLK_6502_NTSC;
+}
+
void SetCurrentCLK6502(void)
{
static DWORD dwPrevSpeed = (DWORD) -1;
+ static VideoRefreshRate_e prevVideoRefreshRate = VR_NONE;
- if(dwPrevSpeed == g_dwSpeed)
+ if (dwPrevSpeed == g_dwSpeed && GetVideoRefreshRate() == prevVideoRefreshRate)
return;
dwPrevSpeed = g_dwSpeed;
+ prevVideoRefreshRate = GetVideoRefreshRate();
// SPEED_MIN = 0 = 0.50 MHz
// SPEED_NORMAL = 10 = 1.00 MHz
@@ -396,7 +404,7 @@ void SetCurrentCLK6502(void)
else
g_fMHz = (double)g_dwSpeed / 10.0;
- g_fCurrentCLK6502 = CLK_6502 * g_fMHz;
+ g_fCurrentCLK6502 = Get6502BaseClock() * g_fMHz;
//
// Now re-init modules that are dependent on /g_fCurrentCLK6502/
@@ -622,17 +630,15 @@ void LoadConfiguration(void)
}
REGLOAD(TEXT(REGVALUE_EMULATION_SPEED) ,&g_dwSpeed);
+ Config_Load_Video();
+ SetCurrentCLK6502(); // Pre: g_dwSpeed && Config_Load_Video()->SetVideoRefreshRate()
DWORD dwEnhanceDisk;
REGLOAD(TEXT(REGVALUE_ENHANCE_DISK_SPEED), &dwEnhanceDisk);
sg_Disk2Card.SetEnhanceDisk(dwEnhanceDisk ? true : false);
- Config_Load_Video();
-
REGLOAD(TEXT("Uthernet Active") ,(DWORD *)&tfe_enabled);
- SetCurrentCLK6502();
-
//
DWORD dwTmp;
@@ -1181,6 +1187,7 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int)
int newVideoType = -1;
int newVideoStyleEnableMask = 0;
int newVideoStyleDisableMask = 0;
+ VideoRefreshRate_e newVideoRefreshRate = VR_NONE;
LPSTR szScreenshotFilename = NULL;
while (*lpCmdLine)
@@ -1427,6 +1434,14 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int)
szScreenshotFilename = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
}
+ else if (_stricmp(lpCmdLine, "-50hz") == 0) // (case-insensitive)
+ {
+ newVideoRefreshRate = VR_50HZ;
+ }
+ else if (_stricmp(lpCmdLine, "-60hz") == 0) // (case-insensitive)
+ {
+ newVideoRefreshRate = VR_60HZ;
+ }
else // unsupported
{
LogFileOutput("Unsupported arg: %s\n", lpCmdLine);
@@ -1537,9 +1552,19 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int)
LogFileOutput("Main: LoadConfiguration()\n");
if (newVideoType >= 0)
+ {
SetVideoType( (VideoType_e)newVideoType );
+ newVideoType = -1; // Don't reapply after a restart
+ }
SetVideoStyle( (VideoStyle_e) ((GetVideoStyle() | newVideoStyleEnableMask) & ~newVideoStyleDisableMask) );
+ if (newVideoRefreshRate != VR_NONE)
+ {
+ SetVideoRefreshRate(newVideoRefreshRate);
+ newVideoRefreshRate = VR_NONE; // Don't reapply after a restart
+ SetCurrentCLK6502();
+ }
+
// Apply the memory expansion switches after loading the Apple II machine type
#ifdef RAMWORKS
if (uRamWorksExPages)
diff --git a/source/Applewin.h b/source/Applewin.h
index 1d3d950f..c1c5899a 100644
--- a/source/Applewin.h
+++ b/source/Applewin.h
@@ -6,7 +6,6 @@
void LogFileTimeUntilFirstKeyReadReset(void);
void LogFileTimeUntilFirstKeyRead(void);
-void SetCurrentCLK6502();
bool SetCurrentImageDir(const char* pszImageDir);
extern const UINT16* GetOldAppleWinVersion(void);
@@ -18,6 +17,9 @@ extern eApple2Type g_Apple2Type;
eApple2Type GetApple2Type(void);
void SetApple2Type(eApple2Type type);
+double Get6502BaseClock(void);
+void SetCurrentCLK6502(void);
+
void SingleStep(bool bReinit);
extern bool g_bFullSpeed;
diff --git a/source/Common.h b/source/Common.h
index f72fbfa4..6cab9950 100644
--- a/source/Common.h
+++ b/source/Common.h
@@ -1,19 +1,11 @@
#pragma once
-const double _M14 = (157500000.0 / 11.0); // 14.3181818... * 10^6
-const double CLK_6502 = ((_M14 * 65.0) / 912.0); // 65 cycles per 912 14M clocks
+const double _14M_NTSC = (157500000.0 / 11.0); // 14.3181818... * 10^6
+const double _14M_PAL = 14.25045e6; // UTAIIe:3-17
+const double CLK_6502_NTSC = ((_14M_NTSC * 65.0) / 912.0); // 65 cycles per 912 14M clocks
+const double CLK_6502_PAL = _14M_PAL / 14.0;
//const double CLK_6502 = 23 * 44100; // 1014300
-// The effective Z-80 clock rate is 2.041MHz
-// See: http://www.apple2info.net/hardware/softcard/SC-SWHW_a2in.pdf
-const double CLK_Z80 = (CLK_6502 * 2);
-
-// TODO: Clean up from Common.h, Video.cpp, and NTSC.h !!!
-const UINT uCyclesPerLine = 65; // 25 cycles of HBL & 40 cycles of HBL'
-const UINT uVisibleLinesPerFrame = 64*3; // 192
-const UINT uLinesPerFrame = 262; // 64 in each third of the screen & 70 in VBL
-const DWORD dwClksPerFrame = uCyclesPerLine * uLinesPerFrame; // 17030
-
#define NUM_SLOTS 8
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
@@ -107,6 +99,7 @@ enum AppMode_e
#define REGVALUE_VIDEO_STYLE "Video Style" // GH#616: Added at 1.28.2
#define REGVALUE_VIDEO_HALF_SCAN_LINES "Half Scan Lines" // GH#616: Deprecated from 1.28.2
#define REGVALUE_VIDEO_MONO_COLOR "Monochrome Color"
+#define REGVALUE_VIDEO_REFRESH_RATE "Video Refresh Rate"
#define REGVALUE_SERIAL_PORT_NAME "Serial Port Name"
#define REGVALUE_ENHANCE_DISK_SPEED "Enhance Disk Speed"
#define REGVALUE_CUSTOM_SPEED "Custom Speed"
diff --git a/source/Configuration/Config.h b/source/Configuration/Config.h
index e60a3285..909f0d21 100644
--- a/source/Configuration/Config.h
+++ b/source/Configuration/Config.h
@@ -4,6 +4,7 @@
#include "../CPU.h"
#include "../DiskImage.h" // Disk_Status_e
#include "../Harddisk.h" // HD_CardIsEnabled()
+#include "../Video.h" // VideoRefreshRate_e, GetVideoRefreshRate()
class CConfigNeedingRestart
{
@@ -11,7 +12,8 @@ public:
CConfigNeedingRestart(UINT bEnableTheFreezesF8Rom = false) :
m_Apple2Type( GetApple2Type() ),
m_CpuType( GetMainCpu() ),
- m_uSaveLoadStateMsg(0)
+ m_uSaveLoadStateMsg(0),
+ m_videoRefreshRate( GetVideoRefreshRate() )
{
m_bEnableHDD = HD_CardIsEnabled();
m_bEnableTheFreezesF8Rom = bEnableTheFreezesF8Rom;
@@ -29,17 +31,19 @@ public:
m_bEnableHDD = other.m_bEnableHDD;
m_bEnableTheFreezesF8Rom = other.m_bEnableTheFreezesF8Rom;
m_uSaveLoadStateMsg = other.m_uSaveLoadStateMsg;
+ m_videoRefreshRate = other.m_videoRefreshRate;
return *this;
}
bool operator== (const CConfigNeedingRestart& other) const
{
return m_Apple2Type == other.m_Apple2Type &&
- m_CpuType == other.m_CpuType &&
- memcmp(m_Slot, other.m_Slot, sizeof(m_Slot)) == 0 &&
- m_bEnableHDD == other.m_bEnableHDD &&
- m_bEnableTheFreezesF8Rom == other.m_bEnableTheFreezesF8Rom &&
- m_uSaveLoadStateMsg == other.m_uSaveLoadStateMsg;
+ m_CpuType == other.m_CpuType &&
+ memcmp(m_Slot, other.m_Slot, sizeof(m_Slot)) == 0 &&
+ m_bEnableHDD == other.m_bEnableHDD &&
+ m_bEnableTheFreezesF8Rom == other.m_bEnableTheFreezesF8Rom &&
+ m_uSaveLoadStateMsg == other.m_uSaveLoadStateMsg &&
+ m_videoRefreshRate == other.m_videoRefreshRate;
}
bool operator!= (const CConfigNeedingRestart& other) const
@@ -54,4 +58,5 @@ public:
bool m_bEnableHDD;
UINT m_bEnableTheFreezesF8Rom;
UINT m_uSaveLoadStateMsg;
+ VideoRefreshRate_e m_videoRefreshRate;
};
diff --git a/source/Configuration/PageConfig.cpp b/source/Configuration/PageConfig.cpp
index ca4b5faf..7d548589 100644
--- a/source/Configuration/PageConfig.cpp
+++ b/source/Configuration/PageConfig.cpp
@@ -121,6 +121,7 @@ BOOL CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPARAM
case IDC_CHECK_HALF_SCAN_LINES:
case IDC_CHECK_VERTICAL_BLEND:
case IDC_CHECK_FS_SHOW_SUBUNIT_STATUS:
+ case IDC_CHECK_50HZ_VIDEO:
// Checked in DlgOK()
break;
@@ -205,6 +206,8 @@ BOOL CPageConfig::DlgProcInternal(HWND hWnd, UINT message, WPARAM wparam, LPARAM
m_PropertySheetHelper.FillComboBox(hWnd,IDC_SERIALPORT, sg_SSC.GetSerialPortChoices(), sg_SSC.GetSerialPort());
EnableWindow(GetDlgItem(hWnd, IDC_SERIALPORT), !sg_SSC.IsActive() ? TRUE : FALSE);
+ CheckDlgButton(hWnd, IDC_CHECK_50HZ_VIDEO, (GetVideoRefreshRate() == VR_50HZ) ? BST_CHECKED : BST_UNCHECKED);
+
SendDlgItemMessage(hWnd,IDC_SLIDER_CPU_SPEED,TBM_SETRANGE,1,MAKELONG(0,40));
SendDlgItemMessage(hWnd,IDC_SLIDER_CPU_SPEED,TBM_SETPAGESIZE,0,5);
SendDlgItemMessage(hWnd,IDC_SLIDER_CPU_SPEED,TBM_SETTICFREQ,10,0);
@@ -286,6 +289,13 @@ void CPageConfig::DlgOK(HWND hWnd)
bVideoReinit = true;
}
+ const bool isNewVideoRate50Hz = IsDlgButtonChecked(hWnd, IDC_CHECK_50HZ_VIDEO) != 0;
+ const bool isCurrentVideoRate50Hz = GetVideoRefreshRate() == VR_50HZ;
+ if (isCurrentVideoRate50Hz != isNewVideoRate50Hz)
+ {
+ m_PropertySheetHelper.GetConfigNew().m_videoRefreshRate = isNewVideoRate50Hz ? VR_50HZ : VR_60HZ;
+ }
+
if (bVideoReinit)
{
Config_Save_Video();
diff --git a/source/Configuration/PropertySheetHelper.cpp b/source/Configuration/PropertySheetHelper.cpp
index 37c79443..94967fc4 100644
--- a/source/Configuration/PropertySheetHelper.cpp
+++ b/source/Configuration/PropertySheetHelper.cpp
@@ -407,6 +407,11 @@ void CPropertySheetHelper::ApplyNewConfig(const CConfigNeedingRestart& ConfigNew
{
REGSAVE(TEXT(REGVALUE_THE_FREEZES_F8_ROM), ConfigNew.m_bEnableTheFreezesF8Rom);
}
+
+ if (CONFIG_CHANGED_LOCAL(m_videoRefreshRate))
+ {
+ REGSAVE(TEXT(REGVALUE_VIDEO_REFRESH_RATE), ConfigNew.m_videoRefreshRate);
+ }
}
void CPropertySheetHelper::ApplyNewConfig(void)
@@ -423,6 +428,7 @@ void CPropertySheetHelper::SaveCurrentConfig(void)
m_ConfigOld.m_Slot[5] = g_Slot5;
m_ConfigOld.m_bEnableHDD = HD_CardIsEnabled();
m_ConfigOld.m_bEnableTheFreezesF8Rom = sg_PropertySheet.GetTheFreezesF8Rom();
+ m_ConfigOld.m_videoRefreshRate = GetVideoRefreshRate();
// Reset flags each time:
m_ConfigOld.m_uSaveLoadStateMsg = 0;
@@ -441,6 +447,7 @@ void CPropertySheetHelper::RestoreCurrentConfig(void)
g_Slot5 = m_ConfigOld.m_Slot[5];
HD_SetEnabled(m_ConfigOld.m_bEnableHDD);
sg_PropertySheet.SetTheFreezesF8Rom(m_ConfigOld.m_bEnableTheFreezesF8Rom);
+ SetVideoRefreshRate(m_ConfigOld.m_videoRefreshRate);
}
bool CPropertySheetHelper::IsOkToSaveLoadState(HWND hWnd, const bool bConfigChanged)
@@ -491,6 +498,9 @@ bool CPropertySheetHelper::HardwareConfigChanged(HWND hWnd)
if (CONFIG_CHANGED(m_CpuType))
strMsgMain += ". Emulated main CPU has changed\n";
+ if (CONFIG_CHANGED(m_videoRefreshRate))
+ strMsgMain += ". Video refresh rate has changed\n";
+
if (CONFIG_CHANGED(m_Slot[4]))
strMsgMain += GetSlot(4);
diff --git a/source/Memory.cpp b/source/Memory.cpp
index 2329d886..8c547203 100644
--- a/source/Memory.cpp
+++ b/source/Memory.cpp
@@ -2202,7 +2202,7 @@ bool MemLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
SetLastRamWrite( yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTRAMWRITE) ? TRUE : FALSE ); // NB. This is set later for II,II+ by slot-0 LC or Saturn
}
- if (unitVersion == 3)
+ if (unitVersion >= 3)
{
for (UINT i=0; i MB_SetSoundcardType()
@@ -2312,7 +2313,7 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
pMB++;
}
- AY8910_InitClock((int)(CLK_6502 * g_PhasorClockScaleFactor));
+ AY8910_InitClock((int)(Get6502BaseClock() * g_PhasorClockScaleFactor));
// NB. g_SoundcardType & g_bPhasorEnable setup in MB_InitializeIO() -> MB_SetSoundcardType()
diff --git a/source/NTSC.cpp b/source/NTSC.cpp
index a7efccae..0d2eb052 100644
--- a/source/NTSC.cpp
+++ b/source/NTSC.cpp
@@ -134,7 +134,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// "There are exactly 17030 (65 x 262) 6502 cycles in every television scan of an American Apple."
#define VIDEO_SCANNER_MAX_HORZ 65 // TODO: use Video.cpp: kHClocks
#define VIDEO_SCANNER_MAX_VERT 262 // TODO: use Video.cpp: kNTSCScanLines
- static const int VIDEO_SCANNER_6502_CYCLES = VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_MAX_VERT;
+ static const UINT VIDEO_SCANNER_6502_CYCLES = VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_MAX_VERT;
+
+ #define VIDEO_SCANNER_MAX_VERT_PAL 312
+ static const UINT VIDEO_SCANNER_6502_CYCLES_PAL = VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_MAX_VERT_PAL;
+
+ static UINT g_videoScannerMaxVert = VIDEO_SCANNER_MAX_VERT; // default to NTSC
+ static UINT g_videoScanner6502Cycles = VIDEO_SCANNER_6502_CYCLES; // default to NTSC
#define VIDEO_SCANNER_HORZ_COLORBURST_BEG 12
#define VIDEO_SCANNER_HORZ_COLORBURST_END 16
@@ -212,9 +218,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// Tables
// Video scanner tables are now runtime-generated using UTAIIe logic
- static unsigned short g_aClockVertOffsetsHGR[VIDEO_SCANNER_MAX_VERT];
- static unsigned short g_aClockVertOffsetsTXT[33];
- static unsigned short APPLE_IIP_HORZ_CLOCK_OFFSET[5][VIDEO_SCANNER_MAX_HORZ];
+ static unsigned short g_aClockVertOffsetsHGR[VIDEO_SCANNER_MAX_VERT_PAL];
+ static unsigned short g_aClockVertOffsetsTXT[VIDEO_SCANNER_MAX_VERT_PAL/8];
+ static unsigned short APPLE_IIP_HORZ_CLOCK_OFFSET[5][VIDEO_SCANNER_MAX_HORZ]; // 5 = CEILING(312/64) = CEILING(262/64)
static unsigned short APPLE_IIE_HORZ_CLOCK_OFFSET[5][VIDEO_SCANNER_MAX_HORZ];
#ifdef _DEBUG
@@ -243,7 +249,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0x0B80,0x0F80,0x1380,0x1780,0x1B80,0x1F80
};
- static unsigned short g_kClockVertOffsetsTXT[33] =
+ static unsigned short g_kClockVertOffsetsTXT[33] = // 33 = CEILING(262/8)
{
0x0000,0x0080,0x0100,0x0180,0x0200,0x0280,0x0300,0x0380,
0x0000,0x0080,0x0100,0x0180,0x0200,0x0280,0x0300,0x0380,
@@ -768,7 +774,7 @@ inline void updateVideoScannerHorzEOLSimple()
{
g_nVideoClockHorz = 0;
- if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT)
+ if (++g_nVideoClockVert == g_videoScannerMaxVert)
{
g_nVideoClockVert = 0;
@@ -807,7 +813,7 @@ inline void updateVideoScannerHorzEOL()
g_nVideoClockHorz = 0;
- if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT)
+ if (++g_nVideoClockVert == g_videoScannerMaxVert)
{
g_nVideoClockVert = 0;
@@ -851,7 +857,7 @@ inline void updateVideoScannerHorzEOL_14M()
g_nVideoClockHorz = 0;
- if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT)
+ if (++g_nVideoClockVert == g_videoScannerMaxVert)
{
g_nVideoClockVert = 0;
@@ -897,7 +903,7 @@ inline void updateVideoScannerHorzEOL()
g_nVideoClockHorz = 0;
- if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT)
+ if (++g_nVideoClockVert == g_videoScannerMaxVert)
{
g_nVideoClockVert = 0;
@@ -1838,8 +1844,8 @@ uint32_t*NTSC_VideoGetChromaTable( bool bHueTypeMonochrome, bool bMonitorTypeCol
//===========================================================================
void NTSC_VideoClockResync(const DWORD dwCyclesThisFrame)
{
- g_nVideoClockVert = (uint16_t) (dwCyclesThisFrame / VIDEO_SCANNER_MAX_HORZ) % VIDEO_SCANNER_MAX_VERT;
- g_nVideoClockHorz = (uint16_t) (dwCyclesThisFrame % VIDEO_SCANNER_MAX_HORZ);
+ g_nVideoClockVert = (uint16_t)(dwCyclesThisFrame / VIDEO_SCANNER_MAX_HORZ) % g_videoScannerMaxVert;
+ g_nVideoClockHorz = (uint16_t)(dwCyclesThisFrame % VIDEO_SCANNER_MAX_HORZ);
}
//===========================================================================
@@ -1861,7 +1867,7 @@ uint16_t NTSC_VideoGetScannerAddress ( const ULONG uExecutedCycles )
g_nVideoClockHorz += VIDEO_SCANNER_MAX_HORZ;
g_nVideoClockVert -= 1;
if ((SHORT)g_nVideoClockVert < 0)
- g_nVideoClockVert = VIDEO_SCANNER_MAX_VERT-1;
+ g_nVideoClockVert = g_videoScannerMaxVert-1;
}
uint16_t addr;
@@ -2154,8 +2160,8 @@ void NTSC_VideoInit( uint8_t* pFramebuffer ) // wsVideoInit
//===========================================================================
void NTSC_VideoReinitialize( DWORD cyclesThisFrame, bool bInitVideoScannerAddress )
{
- _ASSERT(cyclesThisFrame < VIDEO_SCANNER_6502_CYCLES);
- if (cyclesThisFrame >= VIDEO_SCANNER_6502_CYCLES) cyclesThisFrame = 0; // error
+ _ASSERT(cyclesThisFrame < g_videoScanner6502Cycles);
+ if (cyclesThisFrame >= g_videoScanner6502Cycles) cyclesThisFrame = 0; // error
g_nVideoClockVert = (uint16_t) (cyclesThisFrame / VIDEO_SCANNER_MAX_HORZ);
g_nVideoClockHorz = cyclesThisFrame % VIDEO_SCANNER_MAX_HORZ;
@@ -2193,7 +2199,7 @@ void NTSC_VideoInitChroma()
//===========================================================================
-// Pre: cyclesLeftToUpdate = [0...VIDEO_SCANNER_6502_CYCLES]
+// Pre: cyclesLeftToUpdate = [0...g_videoScanner6502Cycles]
// . 2-14: After one emulated 6502/65C02 opcode (optionally with IRQ)
// . ~1000: After 1ms of Z80 emulation
// . 17030: From NTSC_VideoRedrawWholeScreen()
@@ -2208,7 +2214,7 @@ static void VideoUpdateCycles( int cyclesLeftToUpdate )
g_pFuncUpdateGraphicsScreen(cycles); // lines [currV...159]
cyclesLeftToUpdate -= cycles;
- const int cyclesFromLine160ToLine261 = VIDEO_SCANNER_6502_CYCLES - (VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_Y_MIXED);
+ const int cyclesFromLine160ToLine261 = g_videoScanner6502Cycles - (VIDEO_SCANNER_MAX_HORZ * VIDEO_SCANNER_Y_MIXED);
cycles = cyclesLeftToUpdate < cyclesFromLine160ToLine261 ? cyclesLeftToUpdate : cyclesFromLine160ToLine261;
g_pFuncUpdateGraphicsScreen(cycles); // lines [160..191..261]
cyclesLeftToUpdate -= cycles;
@@ -2217,7 +2223,7 @@ static void VideoUpdateCycles( int cyclesLeftToUpdate )
}
else
{
- const int cyclesToLine262 = VIDEO_SCANNER_MAX_HORZ * (VIDEO_SCANNER_MAX_VERT - g_nVideoClockVert - 1) + cyclesToEndOfLine;
+ const int cyclesToLine262 = VIDEO_SCANNER_MAX_HORZ * (g_videoScannerMaxVert - g_nVideoClockVert - 1) + cyclesToEndOfLine;
int cycles = cyclesLeftToUpdate < cyclesToLine262 ? cyclesLeftToUpdate : cyclesToLine262;
g_pFuncUpdateGraphicsScreen(cycles); // lines [currV...261]
cyclesLeftToUpdate -= cycles;
@@ -2235,9 +2241,9 @@ static void VideoUpdateCycles( int cyclesLeftToUpdate )
}
//===========================================================================
-void NTSC_VideoUpdateCycles( long cycles6502 )
+void NTSC_VideoUpdateCycles( UINT cycles6502 )
{
- _ASSERT(cycles6502 && cycles6502 < VIDEO_SCANNER_6502_CYCLES); // Use NTSC_VideoRedrawWholeScreen() instead
+ _ASSERT(cycles6502 && cycles6502 < g_videoScanner6502Cycles); // Use NTSC_VideoRedrawWholeScreen() instead
if (g_bDelayVideoMode)
{
@@ -2269,7 +2275,7 @@ void NTSC_VideoRedrawWholeScreen( void )
g_nVideoClockHorz = 0;
updateVideoScannerAddress();
- VideoUpdateCycles(VIDEO_SCANNER_6502_CYCLES);
+ VideoUpdateCycles(g_videoScanner6502Cycles);
VideoUpdateCycles(horz); // Finally update to get to correct H-pos
@@ -2324,19 +2330,37 @@ static void CheckVideoTables( void )
CheckVideoTables2(A2TYPE_APPLE2E, VF_TEXT);
}
+static bool IsNTSC(void)
+{
+ return g_videoScannerMaxVert == VIDEO_SCANNER_MAX_VERT;
+}
+
static void GenerateVideoTables( void )
{
eApple2Type currentApple2Type = GetApple2Type();
+ uint32_t currentVideoMode = g_uVideoMode;
+ int currentHiresPage = g_nHiresPage;
+ int currentTextPage = g_nTextPage;
+
+ g_nHiresPage = g_nTextPage = 1;
//
// g_aClockVertOffsetsHGR[]
//
g_uVideoMode = VF_HIRES;
- for (UINT i=0, cycle=VIDEO_SCANNER_HORZ_START; i= 4)
+ {
+ VideoRefreshRate_e rate = (VideoRefreshRate_e)yamlLoadHelper.LoadUint(SS_YAML_KEY_VIDEO_REFRESH_RATE);
+ SetVideoRefreshRate(rate); // Trashes: g_dwCyclesThisFrame
+ SetCurrentCLK6502();
+ }
+
+ g_nAltCharSetOffset = yamlLoadHelper.LoadBool(SS_YAML_KEY_ALT_CHARSET) ? 256 : 0;
+ g_uVideoMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_VIDEO_MODE);
+ g_dwCyclesThisFrame = yamlLoadHelper.LoadUint(SS_YAML_KEY_CYCLES_THIS_FRAME);
yamlLoadHelper.PopMap();
}
@@ -777,7 +790,7 @@ WORD VideoGetScannerAddress(DWORD nCycles, VideoScanner_e videoScannerAddr /*= V
//
const int kScanLines = g_bVideoScannerNTSC ? kNTSCScanLines : kPALScanLines;
const int kScanCycles = kScanLines * kHClocks;
- _ASSERT(nCycles < kScanCycles);
+ _ASSERT(nCycles < (UINT)kScanCycles);
nCycles %= kScanCycles;
// calculate horizontal scanning state
@@ -795,7 +808,7 @@ WORD VideoGetScannerAddress(DWORD nCycles, VideoScanner_e videoScannerAddr /*= V
int h_4 = (nHState >> 4) & 1;
int h_5 = (nHState >> 5) & 1;
- // calculate vertical scanning state
+ // calculate vertical scanning state (UTAIIe:3-15,T3.2)
//
int nVLine = nCycles / kHClocks; // which vertical scanning line
int nVState = kVLine0State + nVLine; // V state bits
@@ -868,7 +881,7 @@ WORD VideoGetScannerAddress(DWORD nCycles, VideoScanner_e videoScannerAddr /*= V
nAddressP |= p2b << 11; // a11
}
- // VBL' = v_4' | v_3' = (v_4 & v_3)' (UTAIIe:5-10,#3)
+ // VBL' = v_4' | v_3' = (v_4 & v_3)' (UTAIIe:5-10,#3), (UTAIIe:3-15,T3.2)
if (videoScannerAddr == VS_PartialAddrH)
return nAddressH;
@@ -1231,6 +1244,10 @@ void Config_Load_Video()
REGLOAD(TEXT(REGVALUE_VIDEO_STYLE) ,(DWORD*)&g_eVideoStyle);
REGLOAD(TEXT(REGVALUE_VIDEO_MONO_COLOR),&g_nMonochromeRGB);
+ DWORD rate = VR_60HZ;
+ REGLOAD(TEXT(REGVALUE_VIDEO_REFRESH_RATE), &rate);
+ SetVideoRefreshRate((VideoRefreshRate_e)rate);
+
//
const UINT16* pOldVersion = GetOldAppleWinVersion();
@@ -1275,6 +1292,7 @@ void Config_Save_Video()
REGSAVE(TEXT(REGVALUE_VIDEO_MODE) ,g_eVideoType);
REGSAVE(TEXT(REGVALUE_VIDEO_STYLE) ,g_eVideoStyle);
REGSAVE(TEXT(REGVALUE_VIDEO_MONO_COLOR),g_nMonochromeRGB);
+ REGSAVE(TEXT(REGVALUE_VIDEO_REFRESH_RATE), GetVideoRefreshRate());
}
//===========================================================================
@@ -1305,6 +1323,22 @@ bool IsVideoStyle(VideoStyle_e mask)
return (g_eVideoStyle & mask) != 0;
}
+//===========================================================================
+
+VideoRefreshRate_e GetVideoRefreshRate(void)
+{
+ return (g_bVideoScannerNTSC == false) ? VR_50HZ : VR_60HZ;
+}
+
+void SetVideoRefreshRate(VideoRefreshRate_e rate)
+{
+ if (rate != VR_50HZ)
+ rate = VR_60HZ;
+
+ g_bVideoScannerNTSC = (rate == VR_60HZ);
+ NTSC_SetRefreshRate(rate);
+}
+
//===========================================================================
static void videoCreateDIBSection()
{
diff --git a/source/Video.h b/source/Video.h
index 2a7e2253..693e7dc7 100644
--- a/source/Video.h
+++ b/source/Video.h
@@ -29,6 +29,13 @@
// VS_TEXT_OPTIMIZED=4,
};
+ enum VideoRefreshRate_e
+ {
+ VR_NONE,
+ VR_50HZ,
+ VR_60HZ
+ };
+
enum VideoFlag_e
{
VF_80COL = 0x00000001,
@@ -193,7 +200,7 @@ bool VideoGetSWTEXT(void);
bool VideoGetSWAltCharSet(void);
void VideoSaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
-void VideoLoadSnapshot(class YamlLoadHelper& yamlLoadHelper);
+void VideoLoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT version);
extern bool g_bDisplayPrintScreenFileName;
extern bool g_bShowPrintScreenWarningDialog;
@@ -226,3 +233,6 @@ void SetVideoType(VideoType_e newVideoType);
VideoStyle_e GetVideoStyle(void);
void SetVideoStyle(VideoStyle_e newVideoStyle);
bool IsVideoStyle(VideoStyle_e mask);
+
+VideoRefreshRate_e GetVideoRefreshRate(void);
+void SetVideoRefreshRate(VideoRefreshRate_e rate);
diff --git a/source/Z80VICE/z80.cpp b/source/Z80VICE/z80.cpp
index c4b1ba5c..27563cb4 100644
--- a/source/Z80VICE/z80.cpp
+++ b/source/Z80VICE/z80.cpp
@@ -5510,7 +5510,10 @@ static void opcode_fd(BYTE ip1, BYTE ip2, BYTE ip3, WORD ip12, WORD ip23)
/* Z80 mainloop. */
-static const double uZ80ClockMultiplier = CLK_Z80 / CLK_6502;
+// The effective Z-80 clock rate is 2.041MHz
+// See: http://www.apple2info.net/hardware/softcard/SC-SWHW_a2in.pdf
+static const double uZ80ClockMultiplier = 2;
+
inline static ULONG ConvertZ80TStatesTo6502Cycles(UINT uTStates)
{
return (uTStates < 0) ? 0 : (ULONG) ((double)uTStates / uZ80ClockMultiplier);