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
This commit is contained in:
TomCh 2019-06-28 21:34:34 +01:00 committed by GitHub
parent dbcb789442
commit bd86088c59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 251 additions and 83 deletions

View File

@ -369,6 +369,7 @@
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>source\cpu;source\emulator;source\debugger;zlib;zip_lib;libyaml\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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"

View File

@ -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,6 +31,7 @@ public:
m_bEnableHDD = other.m_bEnableHDD;
m_bEnableTheFreezesF8Rom = other.m_bEnableTheFreezesF8Rom;
m_uSaveLoadStateMsg = other.m_uSaveLoadStateMsg;
m_videoRefreshRate = other.m_videoRefreshRate;
return *this;
}
@ -39,7 +42,8 @@ public:
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_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;
};

View File

@ -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();

View File

@ -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);

View File

@ -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<kNumAnnunciators; i++)
{

View File

@ -205,7 +205,8 @@ static HANDLE g_hSSI263Event[g_nNumEvents] = {NULL}; // 1: Phoneme finished play
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
// NB. Not important if NTSC or PAL - just need to pick a sensible period
static const double g_f6522TimerPeriod_NoIRQ = CLK_6502_NTSC / 60.0; // Constant whatever the CLK is set to
static bool g_bCritSectionValid = false; // Deleting CritialSection when not valid causes crash on Win98
static CRITICAL_SECTION g_CriticalSection; // To guard 6522's IFR
@ -1627,7 +1628,7 @@ static BYTE __stdcall PhasorIO(WORD PC, WORD nAddr, BYTE bWrite, BYTE nValue, UL
g_PhasorClockScaleFactor = (nAddr & 4) ? 2 : 1;
AY8910_InitClock((int)(CLK_6502 * g_PhasorClockScaleFactor));
AY8910_InitClock((int)(Get6502BaseClock() * g_PhasorClockScaleFactor));
return MemReadFloatingBus(nExecutedCycles);
}
@ -2189,7 +2190,7 @@ bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
pMB++;
}
AY8910_InitClock((int)CLK_6502);
AY8910_InitClock((int)Get6502BaseClock());
// NB. g_SoundcardType & g_bPhasorEnable setup in MB_InitializeIO() -> 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()

View File

@ -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<VIDEO_SCANNER_MAX_VERT; i++, cycle+=VIDEO_SCANNER_MAX_HORZ)
{
UINT i = 0, cycle = VIDEO_SCANNER_HORZ_START;
for (; i < VIDEO_SCANNER_MAX_VERT; i++, cycle += VIDEO_SCANNER_MAX_HORZ)
{
g_aClockVertOffsetsHGR[i] = VideoGetScannerAddress(cycle, VS_PartialAddrV);
_ASSERT(g_aClockVertOffsetsHGR[i] == g_kClockVertOffsetsHGR[i]);
if (IsNTSC()) _ASSERT(g_aClockVertOffsetsHGR[i] == g_kClockVertOffsetsHGR[i]);
}
if (!IsNTSC())
{
for (; i < VIDEO_SCANNER_MAX_VERT_PAL; i++, cycle += VIDEO_SCANNER_MAX_HORZ)
g_aClockVertOffsetsHGR[i] = VideoGetScannerAddress(cycle, VS_PartialAddrV);
}
}
//
@ -2344,10 +2368,18 @@ static void GenerateVideoTables( void )
//
g_uVideoMode = VF_TEXT;
for (UINT i=0, cycle=VIDEO_SCANNER_HORZ_START; i<(256+8)/8; i++, cycle+=VIDEO_SCANNER_MAX_HORZ*8)
{
UINT i = 0, cycle = VIDEO_SCANNER_HORZ_START;
for (; i < (256 + 8) / 8; i++, cycle += VIDEO_SCANNER_MAX_HORZ * 8)
{
g_aClockVertOffsetsTXT[i] = VideoGetScannerAddress(cycle, VS_PartialAddrV);
_ASSERT(g_aClockVertOffsetsTXT[i] == g_kClockVertOffsetsTXT[i]);
if (IsNTSC()) _ASSERT(g_aClockVertOffsetsTXT[i] == g_kClockVertOffsetsTXT[i]);
}
if (!IsNTSC())
{
for (; i < VIDEO_SCANNER_MAX_VERT_PAL / 8; i++, cycle += VIDEO_SCANNER_MAX_HORZ * 8)
g_aClockVertOffsetsTXT[i] = VideoGetScannerAddress(cycle, VS_PartialAddrV);
}
}
//
@ -2361,7 +2393,7 @@ static void GenerateVideoTables( void )
for (UINT i=0, cycle=j*64*VIDEO_SCANNER_MAX_HORZ; i<VIDEO_SCANNER_MAX_HORZ; i++, cycle++)
{
APPLE_IIP_HORZ_CLOCK_OFFSET[j][i] = VideoGetScannerAddress(cycle, VS_PartialAddrH);
_ASSERT(APPLE_IIP_HORZ_CLOCK_OFFSET[j][i] == kAPPLE_IIP_HORZ_CLOCK_OFFSET[j][i]);
if (IsNTSC()) _ASSERT(APPLE_IIP_HORZ_CLOCK_OFFSET[j][i] == kAPPLE_IIP_HORZ_CLOCK_OFFSET[j][i]);
}
}
@ -2376,7 +2408,7 @@ static void GenerateVideoTables( void )
for (UINT i=0, cycle=j*64*VIDEO_SCANNER_MAX_HORZ; i<VIDEO_SCANNER_MAX_HORZ; i++, cycle++)
{
APPLE_IIE_HORZ_CLOCK_OFFSET[j][i] = VideoGetScannerAddress(cycle, VS_PartialAddrH);
_ASSERT(APPLE_IIE_HORZ_CLOCK_OFFSET[j][i] == kAPPLE_IIE_HORZ_CLOCK_OFFSET[j][i]);
if (IsNTSC()) _ASSERT(APPLE_IIE_HORZ_CLOCK_OFFSET[j][i] == kAPPLE_IIE_HORZ_CLOCK_OFFSET[j][i]);
}
}
@ -2384,11 +2416,15 @@ static void GenerateVideoTables( void )
CheckVideoTables();
VideoResetState();
// VideoResetState();
SetApple2Type(currentApple2Type);
g_uVideoMode = currentVideoMode;
g_nHiresPage = currentHiresPage;
g_nTextPage = currentTextPage;
}
void GenerateBaseColors(baseColors_t pBaseNtscColors)
static void GenerateBaseColors(baseColors_t pBaseNtscColors)
{
for (UINT i=0; i<16; i++)
{
@ -2415,3 +2451,26 @@ void GenerateBaseColors(baseColors_t pBaseNtscColors)
(*pBaseNtscColors)[i] = * (bgra_t*) &color;
}
}
//===========================================================================
void NTSC_SetRefreshRate(VideoRefreshRate_e rate)
{
if (rate == VR_50HZ)
{
g_videoScannerMaxVert = VIDEO_SCANNER_MAX_VERT_PAL;
g_videoScanner6502Cycles = VIDEO_SCANNER_6502_CYCLES_PAL;
}
else
{
g_videoScannerMaxVert = VIDEO_SCANNER_MAX_VERT;
g_videoScanner6502Cycles = VIDEO_SCANNER_6502_CYCLES;
}
GenerateVideoTables();
}
UINT NTSC_GetCyclesPerFrame(void)
{
return g_videoScanner6502Cycles;
}

View File

@ -14,6 +14,10 @@
extern void NTSC_VideoReinitialize( DWORD cyclesThisFrame, bool bInitVideoScannerAddress );
extern void NTSC_VideoInitAppleType();
extern void NTSC_VideoInitChroma();
extern void NTSC_VideoUpdateCycles( long cycles6502 );
extern void NTSC_VideoUpdateCycles( UINT cycles6502 );
extern void NTSC_VideoRedrawWholeScreen( void );
extern UINT NTSC_GetFrameBufferBorderlessWidth( void );
enum VideoRefreshRate_e;
void NTSC_SetRefreshRate(VideoRefreshRate_e rate);
UINT NTSC_GetCyclesPerFrame(void);

View File

@ -67,7 +67,8 @@ static YamlHelper yamlHelper;
// Unit version history:
// v2: Extended: keyboard (added 'Key Waiting'), memory (LC mem type for II/II+, inverted MF_INTCXROM bit)
// v3: Extended: memory (added 'AnnunciatorN')
#define UNIT_APPLE2_VER 3
// v4: Extended: video (added 'Video Refresh Rate')
#define UNIT_APPLE2_VER 4
#define UNIT_SLOTS_VER 1
@ -219,7 +220,7 @@ static void ParseUnitApple2(YamlLoadHelper& yamlLoadHelper, UINT version)
JoyLoadSnapshot(yamlLoadHelper);
KeybLoadSnapshot(yamlLoadHelper, version);
SpkrLoadSnapshot(yamlLoadHelper);
VideoLoadSnapshot(yamlLoadHelper);
VideoLoadSnapshot(yamlLoadHelper, version);
MemLoadSnapshot(yamlLoadHelper, version);
// g_Apple2Type may've changed: so redraw frame (title, buttons, leds, etc)
@ -395,6 +396,7 @@ static void Snapshot_LoadState_v2(void)
HD_Reset();
KeybReset();
VideoResetState();
SetVideoRefreshRate(VR_60HZ); // Default to 60Hz as older save-states won't contain refresh rate
MB_InitializeForLoadingSnapshot(); // GH#609
sg_SSC.CommReset();
#ifdef USE_SPEECH_API
@ -420,7 +422,7 @@ static void Snapshot_LoadState_v2(void)
// . A change in h/w via loading a save-state avoids this VM restart
// The latter is the desired approach (as the former needs a "power-on" / F2 to start things again)
sg_PropertySheet.ApplyNewConfig(m_ConfigNew, ConfigOld);
sg_PropertySheet.ApplyNewConfig(m_ConfigNew, ConfigOld); // Mainly just saves (some) new state to Registry
MemInitializeROM();
MemInitializeCustomF8ROM();

View File

@ -91,7 +91,7 @@ uint32_t g_uVideoMode = VF_TEXT; // Current Video Mode (this is the last se
DWORD g_eVideoType = VT_DEFAULT;
static VideoStyle_e g_eVideoStyle = VS_HALF_SCANLINES;
static const bool g_bVideoScannerNTSC = true; // NTSC video scanning (or PAL)
static bool g_bVideoScannerNTSC = true; // NTSC video scanning (or PAL)
//-------------------------------------
@ -542,6 +542,9 @@ void VideoRedrawScreenDuringFullSpeed(DWORD dwCyclesThisFrame, bool bInit /*=fal
void VideoRedrawScreenAfterFullSpeed(DWORD dwCyclesThisFrame)
{
#if 1
NTSC_VideoClockResync(dwCyclesThisFrame);
#else
if (g_bVideoScannerNTSC)
{
NTSC_VideoClockResync(dwCyclesThisFrame);
@ -552,6 +555,7 @@ void VideoRedrawScreenAfterFullSpeed(DWORD dwCyclesThisFrame)
g_nVideoClockVert = (uint16_t) (dwCyclesThisFrame / kHClocks) % kPALScanLines;
g_nVideoClockHorz = (uint16_t) (dwCyclesThisFrame % kHClocks);
}
#endif
VideoRedrawScreen(); // Better (no flicker) than using: NTSC_VideoReinitialize() or VideoReinitialize()
}
@ -730,9 +734,10 @@ bool VideoGetSWAltCharSet(void)
//===========================================================================
#define SS_YAML_KEY_ALTCHARSET "Alt Char Set"
#define SS_YAML_KEY_VIDEOMODE "Video Mode"
#define SS_YAML_KEY_CYCLESTHISFRAME "Cycles This Frame"
#define SS_YAML_KEY_ALT_CHARSET "Alt Char Set"
#define SS_YAML_KEY_VIDEO_MODE "Video Mode"
#define SS_YAML_KEY_CYCLES_THIS_FRAME "Cycles This Frame"
#define SS_YAML_KEY_VIDEO_REFRESH_RATE "Video Refresh Rate"
static std::string VideoGetSnapshotStructName(void)
{
@ -743,19 +748,27 @@ static std::string VideoGetSnapshotStructName(void)
void VideoSaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", VideoGetSnapshotStructName().c_str());
yamlSaveHelper.SaveBool(SS_YAML_KEY_ALTCHARSET, g_nAltCharSetOffset ? true : false);
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_VIDEOMODE, g_uVideoMode);
yamlSaveHelper.SaveUint(SS_YAML_KEY_CYCLESTHISFRAME, g_dwCyclesThisFrame);
yamlSaveHelper.SaveBool(SS_YAML_KEY_ALT_CHARSET, g_nAltCharSetOffset ? true : false);
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_VIDEO_MODE, g_uVideoMode);
yamlSaveHelper.SaveUint(SS_YAML_KEY_CYCLES_THIS_FRAME, g_dwCyclesThisFrame);
yamlSaveHelper.SaveUint(SS_YAML_KEY_VIDEO_REFRESH_RATE, (UINT)GetVideoRefreshRate());
}
void VideoLoadSnapshot(YamlLoadHelper& yamlLoadHelper)
void VideoLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
{
if (!yamlLoadHelper.GetSubMap(VideoGetSnapshotStructName()))
return;
g_nAltCharSetOffset = yamlLoadHelper.LoadBool(SS_YAML_KEY_ALTCHARSET) ? 256 : 0;
g_uVideoMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_VIDEOMODE);
g_dwCyclesThisFrame = yamlLoadHelper.LoadUint(SS_YAML_KEY_CYCLESTHISFRAME);
if (version >= 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()
{

View File

@ -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);

View File

@ -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);