From bd86088c5958ec8aa9bd2731b992c7a39ec316e1 Mon Sep 17 00:00:00 2001 From: TomCh Date: Fri, 28 Jun 2019 21:34:34 +0100 Subject: [PATCH] Support 50Hz(PAL) (#648) (PR #658) - Added Configuration GUI to include checkbox for "50Hz" - Implicitly use PAL or NTSC base 6502 clocks depending on video refresh rate - Added new -50hz and -60hz command line switches - Updated save-state for video refresh rate 1.28.8.0: Updated version & history.txt --- AppleWinExpress2015.vcxproj | 1 + bin/History.txt | 9 +- resource/Applewin.rc | 1 + resource/resource.h | 1 + resource/version.h | 2 +- source/Applewin.cpp | 39 ++++-- source/Applewin.h | 4 +- source/Common.h | 17 +-- source/Configuration/Config.h | 17 ++- source/Configuration/PageConfig.cpp | 10 ++ source/Configuration/PropertySheetHelper.cpp | 10 ++ source/Memory.cpp | 2 +- source/Mockingboard.cpp | 9 +- source/NTSC.cpp | 119 ++++++++++++++----- source/NTSC.h | 6 +- source/SaveState.cpp | 8 +- source/Video.cpp | 62 +++++++--- source/Video.h | 12 +- source/Z80VICE/z80.cpp | 5 +- 19 files changed, 251 insertions(+), 83 deletions(-) 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);