This commit is contained in:
michaelangel007 2019-07-22 12:06:53 -07:00
commit 89eab1156c
44 changed files with 1554 additions and 402 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,6 +8,34 @@ https://github.com/AppleWin/AppleWin/issues/new
Tom Charlesworth
1.29.0.0 - 8 Jul 2019
---------------------
. [Change #544] Support for .woz disk images.
- WOZ1 and WOZ2 formats supported.
- read-only: images forced to write-protected (so 'Stickybear Town Builder' doesn't work).
- only 5.25" (not 3.5").
- known issues: 'Wizardry III' not booting.
1.28.8.0 - 28 Jun 2019
----------------------
. [Change #648] Support 50Hz(PAL) video refresh rate and implicitly PAL 1.016MHz.
- 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 #652] Fix for 6522 TIMER1's period to be N+2 cycles.
1.28.6.0 - 2 Jun 2019
---------------------
. [Bug #651] Cycle-accurate interrupts:
- Interrupts sources are checked after every opcode (full-speed after every 40 opcodes).
- 6522 TIMERs in free-running mode now account for the underflowed cycles when resetting the count.
1.28.5.0 - 6 Apr 2019
---------------------
@ -63,7 +91,7 @@ Tom Charlesworth
- Any v1 save-state files should be loaded into AppleWin 1.27, and then re-saved to a v2 save-state file.
. [Change #597] Removed the functionality for CTRL+F10 to reveal the mouse cursor.
. [Change #585] Added a 'Swap' HDD button to the Configuration->Input property sheet.
. [Bug #608] Mockingboard's 6522 TIMER1 wasn't generating an interrupt quickly enough for Broadside's detection routine.
. [Bug #608,#236] Mockingboard's 6522 TIMER1 wasn't generating an interrupt quickly enough for detection routines for Broadside and Ultima III Jukebox.
1.27.13.0 - 8 Dec 2018

View File

@ -27,6 +27,7 @@ MSVC 2017 Community
* [x] Graphics debugger and GPU profiler for DirectX
* [x] Static analysis tools
* [x] VC++ 2017 v141 toolset (x86,x64)
* [x] Windows Universal CRT SDK
* [x] Visual Studio C++ core features
* [x] Windows 8.1 SDK
* [x] Windows 10 SDK (10.0.15063.0) for Desktop C++ x86 and x64

View File

@ -98,7 +98,11 @@
<li>Or: Allow the emulated Apple II to read the Enter key state when Alt (Open Apple key) is pressed.
</ul>
-rgb-card-invert-bit7<br>
Force the RGB card (in "Color (RGB Monitor)" video mode) to invert bit7 in MIX mode. Enables the correct rendering for Dragon Wars.
Force the RGB card (in "Color (RGB Monitor)" video mode) to invert bit7 in MIX mode. Enables the correct rendering for Dragon Wars.<br><br>
-50hz<br>
Support 50Hz(PAL) video refresh rate and PAL 1.016MHz base CPU clock.<br><br>
-60hz<br>
Support 60Hz(NTSC) video refresh rate and NTSC 1.020MHz base CPU clock (default).<br>
<br>
<P style="FONT-WEIGHT: bold">Debug arguments:

View File

@ -78,6 +78,11 @@
processor speed from half-speed to as fast as your PC can emulate.<br>
<br>
<strong>50Hz video:</strong><br>
When checked, this option will run the emulated machine with a 50Hz(PAL) video refresh rate.
The default is unchecked, for 60Hz(NTSC).<br>
<br>
<strong>Benchmark Emulator:</strong><br>
This will run a benchmark test that will show how fast your PC can emulate an
Apple //e system with this emulator. In order to run the benchmark, the

View File

@ -57,7 +57,7 @@ successfully detect the format. Otherwise, it will revert to DOS
order, which is by far the most common format. To force ProDOS
order, give the file an extension of ".PO". </p>
<p style="font-weight: bold;">Nibble Images :</p>
<p style="font-weight: bold;">Nibble Images:</p>
<p>Nibble images contain all of the data on a
disk; not just the data in sectors but also the sector headers
@ -66,13 +66,18 @@ that would be recorded on a real disk's surface. At 232,960
bytes, nibble images are bigger than other images, but they can
be useful for making images of copy protected software. </p>
<p style="font-weight: bold;">2mg Images :</p>
<p style="font-weight: bold;">2mg Images:</p>
<p>2mg (or 2img) images are a wrapper around DOS, ProDOS or Nibble images.
They contain extra meta-data describing for example, DOS volume number and
write-protection.
</p>
<p style="font-weight: bold;">WOZ Images:</p>
<p>The WOZ Disk Image format is an offshoot of the <A href="https://applesaucefdc.com/woz">Applesauce project</A>. Capturing highly accurate bit data is of no use if you don't have a container to hold the data. The WOZ format was designed to be able to contain every possible Apple ][ disk structure and layout. It can be so accurate that even copy protected software can't tell that it isn't an original disk.
</p>
<p style="font-weight: bold;">Compressed Images :</p>
<p>All of the above can optionally be either gzip'ed or zipped. If a zip archive
@ -80,8 +85,8 @@ contains multiple files, then AppleWin only supports using the first file. For b
with hard disk images, uncompress first, as writing back to the image requires a full
image re-compression after every block write. Examples of typical extensions are:
<ul>
<li>.gz, .dsk.gz, .nib.gz, .2mg.gz</li>
<li>.zip, .dsk.zip, .nib.zip, .2mg.zip</li>
<li>.gz, .dsk.gz, .nib.gz, .2mg.gz, .woz.gz</li>
<li>.zip, .dsk.zip, .nib.zip, .2mg.zip, .woz.zip</li>
</ul>
</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

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,5,0
#define APPLEWIN_VERSION 1,29,0,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;
@ -138,6 +138,7 @@ void LogFileTimeUntilFirstKeyReadReset(void)
// Log the time from emulation restart/reboot until the first key read: BIT $C000
// . AZTEC.DSK (DOS 3.3) does prior LDY $C000 reads, but the BIT $C000 is at the "Press any key" message
// . Phasor1.dsk / ProDOS 1.1.1: PC=E797: B1 50: LDA ($50),Y / "Select an Option:" message
// . Rescue Raiders v1.3,v1.5: PC=895: LDA $C000 / boot to intro
void LogFileTimeUntilFirstKeyRead(void)
{
if (!g_fh || bLogKeyReadDone)
@ -145,6 +146,7 @@ void LogFileTimeUntilFirstKeyRead(void)
if ( (mem[regs.pc-3] != 0x2C) // AZTEC: bit $c000
&& !((regs.pc-2) == 0xE797 && mem[regs.pc-2] == 0xB1 && mem[regs.pc-1] == 0x50) // Phasor1: lda ($50),y
&& !((regs.pc-3) == 0x0895 && mem[regs.pc-3] == 0xAD) // Rescue Raiders v1.3,v1.5: lda $c000
)
return;
@ -345,6 +347,7 @@ static void ContinueExecution(void)
//
const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame();
if (g_dwCyclesThisFrame >= dwClksPerFrame)
{
g_dwCyclesThisFrame -= dwClksPerFrame;
@ -376,14 +379,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 +406,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 +632,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 +1189,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 +1436,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 +1554,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)
@ -1613,7 +1640,7 @@ int APIENTRY WinMain(HINSTANCE passinstance, HINSTANCE, LPSTR lpCmdLine, int)
}
// Need to test if it's safe to call ResetMachineState(). In the meantime, just call DiskReset():
sg_Disk2Card.Reset(); // Switch from a booting A][+ to a non-autostart A][, so need to turn off floppy motor
sg_Disk2Card.Reset(true); // Switch from a booting A][+ to a non-autostart A][, so need to turn off floppy motor
LogFileOutput("Main: DiskReset()\n");
HD_Reset(); // GH#515
LogFileOutput("Main: HDDReset()\n");

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

@ -130,12 +130,7 @@ regsrec regs;
unsigned __int64 g_nCumulativeCycles = 0;
static ULONG g_nCyclesExecuted; // # of cycles executed up to last IO access
//static signed long g_uInternalExecutedCycles;
// TODO: Use IRQ_CHECK_TIMEOUT=128 when running at full-speed else with IRQ_CHECK_TIMEOUT=1
// - What about when running benchmark?
static const int IRQ_CHECK_TIMEOUT = 128;
static signed int g_nIrqCheckTimeout = IRQ_CHECK_TIMEOUT;
//
@ -417,21 +412,29 @@ static __forceinline void IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
}
}
static __forceinline void CheckInterruptSources(ULONG uExecutedCycles)
const int IRQ_CHECK_OPCODE_FULL_SPEED = 40; // ~128 cycles (assume 3 cycles per opcode)
static int g_fullSpeedOpcodeCount = IRQ_CHECK_OPCODE_FULL_SPEED;
static __forceinline void CheckInterruptSources(ULONG uExecutedCycles, const bool bVideoUpdate)
{
if (g_nIrqCheckTimeout < 0)
if (!bVideoUpdate)
{
MB_UpdateCycles(uExecutedCycles);
sg_Mouse.SetVBlank( !VideoGetVblBar(uExecutedCycles) );
g_nIrqCheckTimeout = IRQ_CHECK_TIMEOUT;
g_fullSpeedOpcodeCount--;
if (g_fullSpeedOpcodeCount >= 0)
return;
g_fullSpeedOpcodeCount = IRQ_CHECK_OPCODE_FULL_SPEED;
}
MB_UpdateCycles(uExecutedCycles);
sg_Mouse.SetVBlank( !VideoGetVblBar(uExecutedCycles) );
}
// GH#608: IRQ needs to occur within 17 cycles (6 opcodes) of configuring the timer interrupt
void CpuAdjustIrqCheck(UINT uCyclesUntilInterrupt)
{
if (uCyclesUntilInterrupt < IRQ_CHECK_TIMEOUT)
g_nIrqCheckTimeout = uCyclesUntilInterrupt;
const UINT opcodesUntilInterrupt = uCyclesUntilInterrupt/3; // assume 3 cycles per opcode
if (g_bFullSpeed && opcodesUntilInterrupt < IRQ_CHECK_OPCODE_FULL_SPEED)
g_fullSpeedOpcodeCount = opcodesUntilInterrupt;
}
//===========================================================================

View File

@ -318,7 +318,7 @@ static DWORD Cpu6502(DWORD uTotalCycles, const bool bVideoUpdate)
#undef $
}
CheckInterruptSources(uExecutedCycles);
CheckInterruptSources(uExecutedCycles, bVideoUpdate);
NMI(uExecutedCycles, flagc, flagn, flagv, flagz);
IRQ(uExecutedCycles, flagc, flagn, flagv, flagz);

View File

@ -321,7 +321,7 @@ static DWORD Cpu65C02(DWORD uTotalCycles, const bool bVideoUpdate)
#undef $
}
CheckInterruptSources(uExecutedCycles);
CheckInterruptSources(uExecutedCycles, bVideoUpdate);
NMI(uExecutedCycles, flagc, flagn, flagv, flagz);
IRQ(uExecutedCycles, flagc, flagn, flagv, flagz);

View File

@ -406,7 +406,7 @@ static DWORD Cpu65D02(DWORD uTotalCycles, const bool bVideoUpdate)
}
#undef $
CheckInterruptSources(uExecutedCycles);
CheckInterruptSources(uExecutedCycles, bVideoUpdate);
NMI(uExecutedCycles, flagc, flagn, flagv, flagz);
IRQ(uExecutedCycles, flagc, flagn, flagv, flagz);

View File

@ -49,7 +49,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
| (flagz ? AF_ZERO : 0) \
| AF_RESERVED | AF_BREAK;
// CYC(a): This can be optimised, as only certain opcodes will affect uExtraCycles
#define CYC(a) uExecutedCycles += (a)+uExtraCycles; g_nIrqCheckTimeout -= (a)+uExtraCycles;
#define CYC(a) uExecutedCycles += (a)+uExtraCycles;
#define POP (*(mem+((regs.sp >= 0x1FF) ? (regs.sp = 0x100) : ++regs.sp)))
#define PUSH(a) *(mem+regs.sp--) = (a); \
if (regs.sp < 0x100) \

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) / (65.0*14.0+2.0); // 65 cycles per 912 14M clocks
const double CLK_6502_PAL = (_14M_PAL * 65.0) / (65.0*14.0+2.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,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;
};

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

@ -3727,15 +3727,16 @@ Update_t CmdDisk ( int nArgs)
if (nArgs > 2)
goto _Help;
int drive = sg_Disk2Card.GetCurrentDrive() + 1;
char buffer[200] = "";
ConsoleBufferPushFormat(buffer, "D%d at T$%X (%d), phase $%X, offset $%X, %s",
drive,
sg_Disk2Card.GetCurrentTrack(),
sg_Disk2Card.GetCurrentTrack(),
sg_Disk2Card.GetCurrentPhase(),
ConsoleBufferPushFormat(buffer, "D%d at T$%s, phase $%s, offset $%X, mask $%02X, extraCycles %.2f, %s",
sg_Disk2Card.GetCurrentDrive() + 1,
sg_Disk2Card.GetCurrentTrackString().c_str(),
sg_Disk2Card.GetCurrentPhaseString().c_str(),
sg_Disk2Card.GetCurrentOffset(),
sg_Disk2Card.GetCurrentState());
sg_Disk2Card.GetCurrentLSSBitMask(),
sg_Disk2Card.GetCurrentExtraCycles(),
sg_Disk2Card.GetCurrentState()
);
return ConsoleUpdate();
}

View File

@ -56,18 +56,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Disk2InterfaceCard::Disk2InterfaceCard(void)
{
m_currDrive = 0;
ResetSwitches();
m_floppyLatch = 0;
m_floppyMotorOn = 0;
m_floppyLoadMode = 0;
m_floppyWriteMode = 0;
m_phases = 0;
m_saveDiskImage = true; // Save the DiskImage name to Registry
m_slot = 0;
m_diskLastCycle = 0;
m_diskLastReadLatchCycle = 0;
m_enhanceDisk = true;
ResetLogicStateSequencer();
// Debug:
#if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
m_bLogDisk_NibblesRW = false;
@ -82,11 +81,40 @@ bool Disk2InterfaceCard::GetEnhanceDisk(void) { return m_enhanceDisk; }
void Disk2InterfaceCard::SetEnhanceDisk(bool bEnhanceDisk) { m_enhanceDisk = bEnhanceDisk; }
int Disk2InterfaceCard::GetCurrentDrive(void) { return m_currDrive; }
int Disk2InterfaceCard::GetCurrentTrack(void) { return m_floppyDrive[m_currDrive].m_track; }
int Disk2InterfaceCard::GetCurrentPhase(void) { return m_floppyDrive[m_currDrive].m_phase; }
int Disk2InterfaceCard::GetCurrentTrack(void) { return ImagePhaseToTrack(m_floppyDrive[m_currDrive].m_disk.m_imagehandle, m_floppyDrive[m_currDrive].m_phasePrecise, false); }
float Disk2InterfaceCard::GetCurrentPhase(void) { return m_floppyDrive[m_currDrive].m_phasePrecise; }
int Disk2InterfaceCard::GetCurrentOffset(void) { return m_floppyDrive[m_currDrive].m_disk.m_byte; }
int Disk2InterfaceCard::GetTrack(const int drive) { return m_floppyDrive[drive].m_track; }
BYTE Disk2InterfaceCard::GetCurrentLSSBitMask(void) { return m_floppyDrive[m_currDrive].m_disk.m_bitMask; }
double Disk2InterfaceCard::GetCurrentExtraCycles(void) { return m_floppyDrive[m_currDrive].m_disk.m_extraCycles; }
int Disk2InterfaceCard::GetTrack(const int drive) { return ImagePhaseToTrack(m_floppyDrive[drive].m_disk.m_imagehandle, m_floppyDrive[drive].m_phasePrecise, false); }
std::string Disk2InterfaceCard::GetCurrentTrackString(void)
{
const UINT trackInt = (UINT)(m_floppyDrive[m_currDrive].m_phasePrecise / 2);
const float trackFrac = (m_floppyDrive[m_currDrive].m_phasePrecise / 2) - (float)trackInt;
char szInt[8] = "";
sprintf(szInt, "%02X", trackInt); // "$NN"
char szFrac[8] = "";
sprintf(szFrac, "%.02f", trackFrac); // "0.nn"
return std::string(szInt) + std::string(szFrac+1);
}
std::string Disk2InterfaceCard::GetCurrentPhaseString(void)
{
const UINT phaseInt = (UINT)(m_floppyDrive[m_currDrive].m_phasePrecise);
const float phaseFrac = m_floppyDrive[m_currDrive].m_phasePrecise - (float)phaseInt;
char szInt[8] = "";
sprintf(szInt, "%02X", phaseInt); // "$NN"
char szFrac[8] = "";
sprintf(szFrac, "%.02f", phaseFrac); // "0.nn"
return std::string(szInt) + std::string(szFrac+1);
}
LPCTSTR Disk2InterfaceCard::GetCurrentState(void)
{
if (m_floppyDrive[m_currDrive].m_disk.m_imagehandle == NULL)
@ -175,7 +203,7 @@ void Disk2InterfaceCard::SaveLastDiskImage(const int drive)
//===========================================================================
// Called by ControlMotor() & Enable()
void Disk2InterfaceCard::CheckSpinning(const ULONG nExecutedCycles)
void Disk2InterfaceCard::CheckSpinning(const ULONG uExecutedCycles)
{
DWORD modechange = (m_floppyMotorOn && !m_floppyDrive[m_currDrive].m_spinning);
@ -188,7 +216,7 @@ void Disk2InterfaceCard::CheckSpinning(const ULONG nExecutedCycles)
if (modechange)
{
// Set m_diskLastCycle when motor changes: not spinning (ie. off for 1 sec) -> on
CpuCalcCycles(nExecutedCycles);
CpuCalcCycles(uExecutedCycles);
m_diskLastCycle = g_nCumulativeCycles;
}
}
@ -237,7 +265,7 @@ void Disk2InterfaceCard::AllocTrack(const int drive)
//===========================================================================
void Disk2InterfaceCard::ReadTrack(const int drive)
void Disk2InterfaceCard::ReadTrack(const int drive, ULONG uExecutedCycles)
{
if (! IsDriveValid( drive ))
return;
@ -245,8 +273,9 @@ void Disk2InterfaceCard::ReadTrack(const int drive)
FloppyDrive* pDrive = &m_floppyDrive[ drive ];
FloppyDisk* pFloppy = &pDrive->m_disk;
if (pDrive->m_track >= ImageGetNumTracks(pFloppy->m_imagehandle))
if (ImagePhaseToTrack(pFloppy->m_imagehandle, pDrive->m_phasePrecise, false) >= ImageGetNumTracks(pFloppy->m_imagehandle))
{
_ASSERT(0); // What can cause this? Add a comment to replace this assert.
pFloppy->m_trackimagedata = false;
return;
}
@ -257,17 +286,45 @@ void Disk2InterfaceCard::ReadTrack(const int drive)
if (pFloppy->m_trackimage && pFloppy->m_imagehandle)
{
#if LOG_DISK_TRACKS
LOG_DISK("track $%02X%s read\r\n", pDrive->m_track, (pDrive->m_phase & 1) ? ".5" : " ");
CpuCalcCycles(uExecutedCycles);
const ULONG cycleDelta = (ULONG)(g_nCumulativeCycles - pDrive->m_lastStepperCycle);
LOG_DISK("track $%s read (time since last stepper %.3fms)\r\n", GetCurrentTrackString().c_str(), ((float)cycleDelta) / (CLK_6502_NTSC / 1000.0));
#endif
const UINT32 currentPosition = pFloppy->m_byte;
const UINT32 currentTrackLength = pFloppy->m_nibbles;
ImageReadTrack(
pFloppy->m_imagehandle,
pDrive->m_track,
pDrive->m_phase,
pDrive->m_phasePrecise,
pFloppy->m_trackimage,
&pFloppy->m_nibbles,
&pFloppy->m_bitCount,
m_enhanceDisk);
pFloppy->m_byte = 0;
if (!ImageIsWOZ(pFloppy->m_imagehandle) || (currentTrackLength == 0))
{
pFloppy->m_byte = 0;
}
else
{
_ASSERT(pFloppy->m_nibbles && pFloppy->m_bitCount);
if (pFloppy->m_nibbles == 0 || pFloppy->m_bitCount == 0)
{
pFloppy->m_nibbles = 1;
pFloppy->m_bitCount = 8;
}
pFloppy->m_byte = (currentPosition * pFloppy->m_nibbles) / currentTrackLength; // Ref: WOZ-1.01
if (pFloppy->m_byte == (pFloppy->m_nibbles-1)) // Last nibble may not be complete, so advance by 1 nibble
pFloppy->m_byte = 0;
pFloppy->m_bitOffset = pFloppy->m_byte*8;
pFloppy->m_bitMask = 1 << 7;
pFloppy->m_extraCycles = 0.0;
pDrive->m_headWindow = 0;
}
pFloppy->m_trackimagedata = (pFloppy->m_nibbles != 0);
}
}
@ -308,8 +365,11 @@ void Disk2InterfaceCard::WriteTrack(const int drive)
FloppyDrive* pDrive = &m_floppyDrive[ drive ];
FloppyDisk* pFloppy = &pDrive->m_disk;
if (pDrive->m_track >= ImageGetNumTracks(pFloppy->m_imagehandle))
if (ImagePhaseToTrack(pFloppy->m_imagehandle, pDrive->m_phasePrecise, false) >= ImageGetNumTracks(pFloppy->m_imagehandle))
{
_ASSERT(0); // What can cause this? Add a comment to replace this assert.
return;
}
if (pFloppy->m_bWriteProtected)
return;
@ -317,12 +377,11 @@ void Disk2InterfaceCard::WriteTrack(const int drive)
if (pFloppy->m_trackimage && pFloppy->m_imagehandle)
{
#if LOG_DISK_TRACKS
LOG_DISK("track $%02X%s write\r\n", pDrive->m_track, (pDrive->m_phase & 0) ? ".5" : " "); // TODO: hard-coded to whole tracks - see below (nickw)
LOG_DISK("track $%s write\r\n", GetCurrentTrackString().c_str());
#endif
ImageWriteTrack(
pFloppy->m_imagehandle,
pDrive->m_track,
pDrive->m_phase, // TODO: this should never be used; it's the current phase (half-track), not that of the track to be written (nickw)
pDrive->m_phasePrecise,
pFloppy->m_trackimage,
pFloppy->m_nibbles);
}
@ -359,7 +418,7 @@ void __stdcall Disk2InterfaceCard::ControlMotor(WORD, WORD address, BYTE, BYTE,
m_floppyMotorOn = newState;
// NB. Motor off doesn't reset the Command Decoder like reset. (UTAIIe figures 9.7 & 9.8 chip C2)
// - so it doesn't reset this state: m_floppyLoadMode, m_floppyWriteMode, m_phases
// - so it doesn't reset this state: m_floppyLoadMode, m_floppyWriteMode, m_magnetStates
#if LOG_DISK_MOTOR
LOG_DISK("motor %s\r\n", (m_floppyMotorOn) ? "on" : "off");
#endif
@ -388,68 +447,75 @@ void __stdcall Disk2InterfaceCard::ControlStepper(WORD, WORD address, BYTE, BYTE
#endif
}
int phase = (address >> 1) & 3;
int phase_bit = (1 << phase);
// update phases (magnet states)
{
const int phase = (address >> 1) & 3;
const int phase_bit = (1 << phase);
#if 1
// update the magnet states
if (address & 1)
{
// phase on
m_phases |= phase_bit;
}
else
{
// phase off
m_phases &= ~phase_bit;
// update the magnet states
if (address & 1)
m_magnetStates |= phase_bit; // phase on
else
m_magnetStates &= ~phase_bit; // phase off
}
CpuCalcCycles(uExecutedCycles);
#if LOG_DISK_PHASES
const ULONG cycleDelta = (ULONG)(g_nCumulativeCycles - pDrive->m_lastStepperCycle);
#endif
pDrive->m_lastStepperCycle = g_nCumulativeCycles;
// check for any stepping effect from a magnet
// - move only when the magnet opposite the cog is off
// - move in the direction of an adjacent magnet if one is on
// - do not move if both adjacent magnets are on
// - do not move if both adjacent magnets are on (ie. quarter track)
// momentum and timing are not accounted for ... maybe one day!
int direction = 0;
if (m_phases & (1 << ((pDrive->m_phase + 1) & 3)))
if (m_magnetStates & (1 << ((pDrive->m_phase + 1) & 3)))
direction += 1;
if (m_phases & (1 << ((pDrive->m_phase + 3) & 3)))
if (m_magnetStates & (1 << ((pDrive->m_phase + 3) & 3)))
direction -= 1;
// apply magnet step, if any
if (direction)
// Only calculate quarterDirection for WOZ, as NIB/DSK don't support half phases.
int quarterDirection = 0;
if (ImageIsWOZ(pFloppy->m_imagehandle))
{
pDrive->m_phase = MAX(0, MIN(79, pDrive->m_phase + direction));
const int nNumTracksInImage = ImageGetNumTracks(pFloppy->m_imagehandle);
const int newtrack = (nNumTracksInImage == 0) ? 0
: MIN(nNumTracksInImage-1, pDrive->m_phase >> 1); // (round half tracks down)
if (newtrack != pDrive->m_track)
if ((m_magnetStates == 0xC || // 1100
m_magnetStates == 0x6 || // 0110
m_magnetStates == 0x3 || // 0011
m_magnetStates == 0x9)) // 1001
{
FlushCurrentTrack(m_currDrive);
pDrive->m_track = newtrack;
pFloppy->m_trackimagedata = false;
m_formatTrack.DriveNotWritingTrack();
quarterDirection = direction;
direction = 0;
}
// Feature Request #201 Show track status
// https://github.com/AppleWin/AppleWin/issues/201
FrameDrawDiskStatus( (HDC)0 );
}
#else
// substitute alternate stepping code here to test
#endif
pDrive->m_phase = MAX(0, MIN(79, pDrive->m_phase + direction));
float newPhasePrecise = (float)(pDrive->m_phase) + (float)quarterDirection * 0.5f;
if (newPhasePrecise < 0)
newPhasePrecise = 0;
// apply magnet step, if any
if (newPhasePrecise != pDrive->m_phasePrecise)
{
FlushCurrentTrack(m_currDrive);
pDrive->m_phasePrecise = newPhasePrecise;
pFloppy->m_trackimagedata = false;
m_formatTrack.DriveNotWritingTrack();
FrameDrawDiskStatus((HDC)0); // Show track status (GH#201)
}
#if LOG_DISK_PHASES
LOG_DISK("track $%02X%s phases %d%d%d%d phase %d %s address $%4X\r\n",
pDrive->m_phase >> 1,
(pDrive->m_phase & 1) ? ".5" : " ",
(m_phases >> 3) & 1,
(m_phases >> 2) & 1,
(m_phases >> 1) & 1,
(m_phases >> 0) & 1,
phase,
LOG_DISK("track $%s magnet-states %d%d%d%d phase %d %s address $%4X last-stepper %.3fms\r\n",
GetCurrentTrackString().c_str(),
(m_magnetStates >> 3) & 1,
(m_magnetStates >> 2) & 1,
(m_magnetStates >> 1) & 1,
(m_magnetStates >> 0) & 1,
(address >> 1) & 3, // phase
(address & 1) ? "on " : "off",
address);
address,
((float)cycleDelta)/(CLK_6502_NTSC/1000.0));
#endif
}
@ -797,14 +863,14 @@ bool Disk2InterfaceCard::LogWriteCheckSyncFF(ULONG& uCycleDelta)
//===========================================================================
void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles)
void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles)
{
/* m_floppyLoadMode = 0; */
FloppyDrive* pDrive = &m_floppyDrive[m_currDrive];
FloppyDisk* pFloppy = &pDrive->m_disk;
if (!pFloppy->m_trackimagedata && pFloppy->m_imagehandle)
ReadTrack(m_currDrive);
ReadTrack(m_currDrive, uExecutedCycles);
if (!pFloppy->m_trackimagedata)
{
@ -814,7 +880,7 @@ void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BY
// Improve precision of "authentic" drive mode - GH#125
UINT uSpinNibbleCount = 0;
CpuCalcCycles(nExecutedCycles); // g_nCumulativeCycles required for uSpinNibbleCount & LogWriteCheckSyncFF()
CpuCalcCycles(uExecutedCycles); // g_nCumulativeCycles required for uSpinNibbleCount & LogWriteCheckSyncFF()
if (!m_enhanceDisk && pDrive->m_spinning)
{
@ -877,6 +943,9 @@ void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BY
}
else if (!pFloppy->m_bWriteProtected) // && m_floppyWriteMode
{
if (!pDrive->m_spinning)
return; // If not spinning then only 1 bit-cell gets written?
*(pFloppy->m_trackimage + pFloppy->m_byte) = m_floppyLatch;
pFloppy->m_trackimagedirty = true;
@ -911,17 +980,246 @@ void __stdcall Disk2InterfaceCard::ReadWrite(WORD pc, WORD addr, BYTE bWrite, BY
//===========================================================================
void Disk2InterfaceCard::ResetLogicStateSequencer(void)
{
m_shiftReg = 0;
m_latchDelay = 0;
m_resetSequencer = true;
m_dbgLatchDelayedCnt = 0;
}
void Disk2InterfaceCard::UpdateBitStreamPositionAndDiskCycle(const ULONG uExecutedCycles)
{
FloppyDisk& floppy = m_floppyDrive[m_currDrive].m_disk;
CpuCalcCycles(uExecutedCycles);
const UINT bitCellDelta = GetBitCellDelta(ImageGetOptimalBitTiming(floppy.m_imagehandle));
UpdateBitStreamPosition(floppy, bitCellDelta);
m_diskLastCycle = g_nCumulativeCycles;
}
UINT Disk2InterfaceCard::GetBitCellDelta(const BYTE optimalBitTiming)
{
FloppyDisk& floppy = m_floppyDrive[m_currDrive].m_disk;
// NB. m_extraCycles is needed to retain accuracy:
// . Read latch #1: 0-> 9: cycleDelta= 9, bitCellDelta=2, extraCycles=1
// . Read latch #2: 9->20: cycleDelta=11, bitCellDelta=2, extraCycles=3
// . Overall: 0->20: cycleDelta=20, bitCellDelta=5, extraCycles=0
UINT bitCellDelta;
#if 0
if (optimalBitTiming == 32)
{
const ULONG cycleDelta = (ULONG)(g_nCumulativeCycles - m_diskLastCycle) + (BYTE) m_extraCycles;
bitCellDelta = cycleDelta / 4; // DIV 4 for 4us per bit-cell
m_extraCycles = cycleDelta & 3; // MOD 4 : remainder carried forward for next time
}
else
#endif
{
const double cycleDelta = (double)(g_nCumulativeCycles - m_diskLastCycle) + floppy.m_extraCycles;
const double bitTime = 0.125 * (double)optimalBitTiming; // 125ns units
bitCellDelta = (UINT) floor( cycleDelta / bitTime );
floppy.m_extraCycles = (double)cycleDelta - ((double)bitCellDelta * bitTime);
}
return bitCellDelta;
}
void Disk2InterfaceCard::UpdateBitStreamPosition(FloppyDisk& floppy, const ULONG bitCellDelta)
{
_ASSERT(floppy.m_bitCount); // Should never happen - ReadTrack() will handle this
if (floppy.m_bitCount == 0)
return;
floppy.m_bitOffset += bitCellDelta;
if (floppy.m_bitOffset >= floppy.m_bitCount)
floppy.m_bitOffset %= floppy.m_bitCount;
UpdateBitStreamOffsets(floppy);
}
void Disk2InterfaceCard::UpdateBitStreamOffsets(FloppyDisk& floppy)
{
floppy.m_byte = floppy.m_bitOffset / 8;
const UINT remainder = 7 - (floppy.m_bitOffset & 7);
floppy.m_bitMask = 1 << remainder;
}
void __stdcall Disk2InterfaceCard::ReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles)
{
/* m_floppyLoadMode = 0; */
FloppyDrive& drive = m_floppyDrive[m_currDrive];
FloppyDisk& floppy = drive.m_disk;
if (!floppy.m_trackimagedata && floppy.m_imagehandle)
ReadTrack(m_currDrive, uExecutedCycles);
if (!floppy.m_trackimagedata)
{
_ASSERT(0); // Can't happen for WOZ - ReadTrack() should return an empty track
m_floppyLatch = 0xFF;
return;
}
// Don't change latch if drive off after 1 second drive-off delay (UTAIIe page 9-13)
// "DRIVES OFF forces the data register to hold its present state." (UTAIIe page 9-12)
// Note: Sherwood Forest sets shift mode and reads with the drive off.
// TODO: And same for a write?
if (!drive.m_spinning) // GH#599
return;
CpuCalcCycles(uExecutedCycles);
// Skipping forward a large amount of bitcells means the bitstream will very likely be out-of-sync.
// The first 1-bit will produce a latch nibble, and this 1-bit is unlikely to be the nibble's high bit.
// So we need to ensure we run enough bits through the sequencer to re-sync.
// NB. For Planetfall 13 bitcells(NG) / 14 bitcells(OK)
const UINT significantBitCells = 50; // 5x 10-bit sync FF nibbles
UINT bitCellDelta = GetBitCellDelta(ImageGetOptimalBitTiming(floppy.m_imagehandle));
UINT bitCellRemainder;
if (bitCellDelta <= significantBitCells)
{
bitCellRemainder = bitCellDelta;
}
else
{
bitCellRemainder = significantBitCells;
bitCellDelta -= significantBitCells;
UpdateBitStreamPosition(floppy, bitCellDelta);
m_latchDelay = 0;
}
m_diskLastCycle = g_nCumulativeCycles;
//
if (!m_floppyWriteMode)
{
// m_diskLastReadLatchCycle = g_nCumulativeCycles; // Not used by WOZ (only by NIB)
#if LOG_DISK_NIBBLES_READ
bool newLatchData = false;
#endif
for (UINT i = 0; i < bitCellRemainder; i++)
{
BYTE n = floppy.m_trackimage[floppy.m_byte];
drive.m_headWindow <<= 1;
drive.m_headWindow |= (n & floppy.m_bitMask) ? 1 : 0;
BYTE outputBit = (drive.m_headWindow & 0xf) ? (drive.m_headWindow >> 1) & 1
: rand() & 1;
floppy.m_bitMask >>= 1;
if (!floppy.m_bitMask)
{
floppy.m_bitMask = 1 << 7;
floppy.m_byte++;
}
floppy.m_bitOffset++;
if (floppy.m_bitOffset == floppy.m_bitCount)
{
floppy.m_bitMask = 1 << 7;
floppy.m_bitOffset = 0;
floppy.m_byte = 0;
}
if (m_resetSequencer)
{
m_resetSequencer = false; // LSS takes some cycles to reset (ref?)
continue;
}
//
m_shiftReg <<= 1;
m_shiftReg |= outputBit;
if (m_latchDelay)
{
m_latchDelay -= 4;
if (m_latchDelay < 0)
m_latchDelay = 0;
if (m_shiftReg)
{
m_dbgLatchDelayedCnt = 0;
}
else // m_shiftReg==0
{
m_latchDelay += 4; // extend by 4us (so 7us again) - GH#662
m_dbgLatchDelayedCnt++;
#if LOG_DISK_NIBBLES_READ
if (m_dbgLatchDelayedCnt >= 3)
{
LOG_DISK("read: latch held due to 0: PC=%04X, cnt=%02X\r\n", regs.pc, m_dbgLatchDelayedCnt);
}
#endif
}
}
if (!m_latchDelay)
{
#if LOG_DISK_NIBBLES_READ
if (newLatchData)
{
LOG_DISK("read skipped latch data: %04X = %02X\r\n", floppy.m_byte, m_floppyLatch);
newLatchData = false;
}
#endif
m_floppyLatch = m_shiftReg;
if (m_shiftReg & 0x80)
{
m_latchDelay = 7;
m_shiftReg = 0;
#if LOG_DISK_NIBBLES_READ
// May not actually be read by 6502 (eg. Prologue's CHKSUM 4&4 nibble pair), but still pass to the log's nibble reader
m_formatTrack.DecodeLatchNibbleRead(m_floppyLatch);
newLatchData = true;
#endif
}
}
}
#if LOG_DISK_NIBBLES_READ
if (m_floppyLatch & 0x80)
{
#if LOG_DISK_NIBBLES_USE_RUNTIME_VAR
if (m_bLogDisk_NibblesRW)
#endif
{
LOG_DISK("read %04X = %02X\r\n", floppy.m_byte, m_floppyLatch);
}
}
#endif
}
else if (!floppy.m_bWriteProtected) // && m_floppyWriteMode
{
//TODO
}
// Show track status (GH#201) - NB. Prevent flooding of forcing UI to redraw!!!
if ((floppy.m_byte & 0xFF) == 0)
FrameDrawDiskStatus( (HDC)0 );
}
//===========================================================================
void Disk2InterfaceCard::Reset(const bool bIsPowerCycle/*=false*/)
{
// RESET forces all switches off (UTAIIe Table 9.1)
m_currDrive = 0;
m_floppyMotorOn = 0;
m_floppyLoadMode = 0;
m_floppyWriteMode = 0;
m_phases = 0;
ResetSwitches();
m_formatTrack.Reset();
ResetLogicStateSequencer();
if (bIsPowerCycle) // GH#460
{
// NB. This doesn't affect the drive head (ie. drive's track position)
@ -937,6 +1235,15 @@ void Disk2InterfaceCard::Reset(const bool bIsPowerCycle/*=false*/)
}
}
void Disk2InterfaceCard::ResetSwitches(void)
{
m_currDrive = 0;
m_floppyMotorOn = 0;
m_floppyLoadMode = 0;
m_floppyWriteMode = 0;
m_magnetStates = 0;
}
//===========================================================================
bool Disk2InterfaceCard::UserSelectNewDiskImage(const int drive, LPCSTR pszFilename/*=""*/)
@ -958,8 +1265,8 @@ bool Disk2InterfaceCard::UserSelectNewDiskImage(const int drive, LPCSTR pszFilen
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = g_hFrameWindow;
ofn.hInstance = g_hInstance;
ofn.lpstrFilter = TEXT("All Images\0*.bin;*.do;*.dsk;*.nib;*.po;*.gz;*.zip;*.2mg;*.2img;*.iie;*.apl\0")
TEXT("Disk Images (*.bin,*.do,*.dsk,*.nib,*.po,*.gz,*.zip,*.2mg,*.2img,*.iie)\0*.bin;*.do;*.dsk;*.nib;*.po;*.gz;*.zip;*.2mg;*.2img;*.iie\0")
ofn.lpstrFilter = TEXT("All Images\0*.bin;*.do;*.dsk;*.nib;*.po;*.gz;*.woz;*.zip;*.2mg;*.2img;*.iie;*.apl\0")
TEXT("Disk Images (*.bin,*.do,*.dsk,*.nib,*.po,*.gz,*.woz,*.zip,*.2mg,*.2img,*.iie)\0*.bin;*.do;*.dsk;*.nib;*.po;*.gz;*.woz;*.zip;*.2mg;*.2img;*.iie\0")
TEXT("All Files\0*.*\0");
ofn.lpstrFile = filename;
ofn.nMaxFile = MAX_PATH;
@ -990,7 +1297,7 @@ bool Disk2InterfaceCard::UserSelectNewDiskImage(const int drive, LPCSTR pszFilen
//===========================================================================
void __stdcall Disk2InterfaceCard::LoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG)
void __stdcall Disk2InterfaceCard::LoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG uExecutedCycles)
{
/* m_floppyLoadMode = 1; */
@ -1007,11 +1314,22 @@ void __stdcall Disk2InterfaceCard::LoadWriteProtect(WORD, WORD, BYTE write, BYTE
// . write mode doesn't prevent reading write protect (GH#537):
// "If for some reason the above write protect check were entered with the READ/WRITE switch in WRITE,
// the write protect switch would still be read correctly" (UTAIIe page 9-21)
// . Sequencer "SR" (Shift Right) command only loads QA (bit7) of data register (UTAIIe page 9-21)
if (m_floppyDrive[m_currDrive].m_disk.m_bWriteProtected)
m_floppyLatch |= 0x80;
else
m_floppyLatch &= 0x7F;
}
if (ImageIsWOZ(m_floppyDrive[m_currDrive].m_disk.m_imagehandle))
{
#if LOG_DISK_NIBBLES_READ
LOG_DISK("reset LSS: ~PC=%04X\r\n", regs.pc);
#endif
ResetLogicStateSequencer(); // reset sequencer (Ref: WOZ-1.01)
// m_latchDelay = 7; // TODO: Treat like a regular $C0EC latch load?
UpdateBitStreamPositionAndDiskCycle(uExecutedCycles); // Fix E7-copy protection
}
}
//===========================================================================
@ -1181,6 +1499,9 @@ BYTE __stdcall Disk2InterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE
UINT uSlot = ((addr & 0xff) >> 4) - 8;
Disk2InterfaceCard* pCard = (Disk2InterfaceCard*) MemGetSlotParameters(uSlot);
ImageInfo* pImage = pCard->m_floppyDrive[pCard->m_currDrive].m_disk.m_imagehandle;
bool isWOZ = ImageIsWOZ(pImage);
switch (addr & 0xF)
{
case 0x0: pCard->ControlStepper(pc, addr, bWrite, d, nExecutedCycles); break;
@ -1195,7 +1516,9 @@ BYTE __stdcall Disk2InterfaceCard::IORead(WORD pc, WORD addr, BYTE bWrite, BYTE
case 0x9: pCard->ControlMotor(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xA: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xB: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xC: pCard->ReadWrite(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xC: if (!isWOZ) pCard->ReadWrite(pc, addr, bWrite, d, nExecutedCycles);
else pCard->ReadWriteWOZ(pc, addr, bWrite, d, nExecutedCycles);
break;
case 0xD: pCard->LoadWriteProtect(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xE: pCard->SetReadMode(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xF: pCard->SetWriteMode(pc, addr, bWrite, d, nExecutedCycles); break;
@ -1213,6 +1536,9 @@ BYTE __stdcall Disk2InterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE
UINT uSlot = ((addr & 0xff) >> 4) - 8;
Disk2InterfaceCard* pCard = (Disk2InterfaceCard*) MemGetSlotParameters(uSlot);
ImageInfo* pImage = pCard->m_floppyDrive[pCard->m_currDrive].m_disk.m_imagehandle;
bool isWOZ = ImageIsWOZ(pImage);
switch (addr & 0xF)
{
case 0x0: pCard->ControlStepper(pc, addr, bWrite, d, nExecutedCycles); break;
@ -1227,7 +1553,9 @@ BYTE __stdcall Disk2InterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE
case 0x9: pCard->ControlMotor(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xA: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xB: pCard->Enable(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xC: pCard->ReadWrite(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xC: if (!isWOZ) pCard->ReadWrite(pc, addr, bWrite, d, nExecutedCycles);
else pCard->ReadWriteWOZ(pc, addr, bWrite, d, nExecutedCycles);
break;
case 0xD: pCard->LoadWriteProtect(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xE: pCard->SetReadMode(pc, addr, bWrite, d, nExecutedCycles); break;
case 0xF: pCard->SetWriteMode(pc, addr, bWrite, d, nExecutedCycles); break;
@ -1246,7 +1574,9 @@ BYTE __stdcall Disk2InterfaceCard::IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE
// Unit version history:
// 2: Added: Format Track state & DiskLastCycle
// 3: Added: DiskLastReadLatchCycle
static const UINT kUNIT_VERSION = 3;
// 4: Added: WOZ state
// Split up 'Unit' putting some state into a new 'Floppy'
static const UINT kUNIT_VERSION = 4;
#define SS_YAML_VALUE_CARD_DISK2 "Disk]["
@ -1259,16 +1589,27 @@ static const UINT kUNIT_VERSION = 3;
#define SS_YAML_KEY_FLOPPY_WRITE_MODE "Floppy Write Mode"
#define SS_YAML_KEY_LAST_CYCLE "Last Cycle"
#define SS_YAML_KEY_LAST_READ_LATCH_CYCLE "Last Read Latch Cycle"
#define SS_YAML_KEY_LSS_SHIFT_REG "LSS Shift Reg"
#define SS_YAML_KEY_LSS_LATCH_DELAY "LSS Latch Delay"
#define SS_YAML_KEY_LSS_RESET_SEQUENCER "LSS Reset Sequencer"
#define SS_YAML_KEY_DISK2UNIT "Unit"
#define SS_YAML_KEY_FILENAME "Filename"
#define SS_YAML_KEY_TRACK "Track"
#define SS_YAML_KEY_PHASE "Phase"
#define SS_YAML_KEY_PHASE_PRECISE "Phase (precise)"
#define SS_YAML_KEY_TRACK "Track" // deprecated at v4
#define SS_YAML_KEY_HEAD_WINDOW "Head Window"
#define SS_YAML_KEY_LAST_STEPPER_CYCLE "Last Stepper Cycle"
#define SS_YAML_KEY_FLOPPY "Floppy"
#define SS_YAML_KEY_BYTE "Byte"
#define SS_YAML_KEY_NIBBLES "Nibbles"
#define SS_YAML_KEY_BIT_OFFSET "Bit Offset"
#define SS_YAML_KEY_BIT_COUNT "Bit Count"
#define SS_YAML_KEY_EXTRA_CYCLES "Extra Cycles"
#define SS_YAML_KEY_WRITE_PROTECTED "Write Protected"
#define SS_YAML_KEY_SPINNING "Spinning"
#define SS_YAML_KEY_WRITE_LIGHT "Write Light"
#define SS_YAML_KEY_NIBBLES "Nibbles"
#define SS_YAML_KEY_TRACK_IMAGE_DATA "Track Image Data"
#define SS_YAML_KEY_TRACK_IMAGE_DIRTY "Track Image Dirty"
#define SS_YAML_KEY_TRACK_IMAGE "Track Image"
@ -1279,17 +1620,16 @@ std::string Disk2InterfaceCard::GetSnapshotCardName(void)
return name;
}
void Disk2InterfaceCard::SaveSnapshotDisk2Unit(YamlSaveHelper& yamlSaveHelper, UINT unit)
void Disk2InterfaceCard::SaveSnapshotFloppy(YamlSaveHelper& yamlSaveHelper, UINT unit)
{
YamlSaveHelper::Label label(yamlSaveHelper, "%s%d:\n", SS_YAML_KEY_DISK2UNIT, unit);
YamlSaveHelper::Label label(yamlSaveHelper, "%s:\n", SS_YAML_KEY_FLOPPY);
yamlSaveHelper.SaveString(SS_YAML_KEY_FILENAME, m_floppyDrive[unit].m_disk.m_fullname);
yamlSaveHelper.SaveUint(SS_YAML_KEY_TRACK, m_floppyDrive[unit].m_track);
yamlSaveHelper.SaveUint(SS_YAML_KEY_PHASE, m_floppyDrive[unit].m_phase);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_BYTE, m_floppyDrive[unit].m_disk.m_byte);
yamlSaveHelper.SaveBool(SS_YAML_KEY_WRITE_PROTECTED, m_floppyDrive[unit].m_disk.m_bWriteProtected);
yamlSaveHelper.SaveUint(SS_YAML_KEY_SPINNING, m_floppyDrive[unit].m_spinning);
yamlSaveHelper.SaveUint(SS_YAML_KEY_WRITE_LIGHT, m_floppyDrive[unit].m_writelight);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_NIBBLES, m_floppyDrive[unit].m_disk.m_nibbles);
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_BIT_OFFSET, m_floppyDrive[unit].m_disk.m_bitOffset); // v4
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_BIT_COUNT, m_floppyDrive[unit].m_disk.m_bitCount); // v4
yamlSaveHelper.SaveDouble(SS_YAML_KEY_EXTRA_CYCLES, m_floppyDrive[unit].m_disk.m_extraCycles); // v4
yamlSaveHelper.SaveBool(SS_YAML_KEY_WRITE_PROTECTED, m_floppyDrive[unit].m_disk.m_bWriteProtected);
yamlSaveHelper.SaveUint(SS_YAML_KEY_TRACK_IMAGE_DATA, m_floppyDrive[unit].m_disk.m_trackimagedata);
yamlSaveHelper.SaveUint(SS_YAML_KEY_TRACK_IMAGE_DIRTY, m_floppyDrive[unit].m_disk.m_trackimagedirty);
@ -1300,13 +1640,26 @@ void Disk2InterfaceCard::SaveSnapshotDisk2Unit(YamlSaveHelper& yamlSaveHelper, U
}
}
void Disk2InterfaceCard::SaveSnapshotDriveUnit(YamlSaveHelper& yamlSaveHelper, UINT unit)
{
YamlSaveHelper::Label label(yamlSaveHelper, "%s%d:\n", SS_YAML_KEY_DISK2UNIT, unit);
yamlSaveHelper.SaveUint(SS_YAML_KEY_PHASE, m_floppyDrive[unit].m_phase);
yamlSaveHelper.SaveFloat(SS_YAML_KEY_PHASE_PRECISE, m_floppyDrive[unit].m_phasePrecise); // v4
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_HEAD_WINDOW, m_floppyDrive[unit].m_headWindow); // v4
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_STEPPER_CYCLE, m_floppyDrive[unit].m_lastStepperCycle); // v4
yamlSaveHelper.SaveUint(SS_YAML_KEY_SPINNING, m_floppyDrive[unit].m_spinning);
yamlSaveHelper.SaveUint(SS_YAML_KEY_WRITE_LIGHT, m_floppyDrive[unit].m_writelight);
SaveSnapshotFloppy(yamlSaveHelper, unit);
}
void Disk2InterfaceCard::SaveSnapshot(class YamlSaveHelper& yamlSaveHelper)
{
YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_VERSION);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_PHASES, m_phases);
yamlSaveHelper.SaveUint(SS_YAML_KEY_CURRENT_DRIVE, m_currDrive);
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_PHASES, m_magnetStates);
yamlSaveHelper.SaveBool(SS_YAML_KEY_DISK_ACCESSED, false); // deprecated
yamlSaveHelper.SaveBool(SS_YAML_KEY_ENHANCE_DISK, m_enhanceDisk);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_FLOPPY_LATCH, m_floppyLatch);
@ -1314,29 +1667,24 @@ void Disk2InterfaceCard::SaveSnapshot(class YamlSaveHelper& yamlSaveHelper)
yamlSaveHelper.SaveBool(SS_YAML_KEY_FLOPPY_WRITE_MODE, m_floppyWriteMode == TRUE);
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_CYCLE, m_diskLastCycle); // v2
yamlSaveHelper.SaveHexUint64(SS_YAML_KEY_LAST_READ_LATCH_CYCLE, m_diskLastReadLatchCycle); // v3
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_LSS_SHIFT_REG, m_shiftReg); // v4
yamlSaveHelper.SaveInt(SS_YAML_KEY_LSS_LATCH_DELAY, m_latchDelay); // v4
yamlSaveHelper.SaveBool(SS_YAML_KEY_LSS_RESET_SEQUENCER, m_resetSequencer); // v4
m_formatTrack.SaveSnapshot(yamlSaveHelper); // v2
SaveSnapshotDisk2Unit(yamlSaveHelper, DRIVE_1);
SaveSnapshotDisk2Unit(yamlSaveHelper, DRIVE_2);
SaveSnapshotDriveUnit(yamlSaveHelper, DRIVE_1);
SaveSnapshotDriveUnit(yamlSaveHelper, DRIVE_2);
}
void Disk2InterfaceCard::LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, UINT unit)
bool Disk2InterfaceCard::LoadSnapshotFloppy(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track)
{
std::string disk2UnitName = std::string(SS_YAML_KEY_DISK2UNIT) + (unit == DRIVE_1 ? std::string("0") : std::string("1"));
if (!yamlLoadHelper.GetSubMap(disk2UnitName))
throw std::string("Card: Expected key: ") + disk2UnitName;
bool bImageError = false;
m_floppyDrive[unit].m_disk.m_fullname[0] = 0;
m_floppyDrive[unit].m_disk.m_imagename[0] = 0;
m_floppyDrive[unit].m_disk.m_bWriteProtected = false; // Default to false (until image is successfully loaded below)
std::string filename = yamlLoadHelper.LoadString(SS_YAML_KEY_FILENAME);
if (!filename.empty())
bool bImageError = filename.empty();
if (!bImageError)
{
DWORD dwAttributes = GetFileAttributes(filename.c_str());
if(dwAttributes == INVALID_FILE_ATTRIBUTES)
if (dwAttributes == INVALID_FILE_ATTRIBUTES)
{
// Get user to browse for file
UserSelectNewDiskImage(unit, filename.c_str());
@ -1347,38 +1695,106 @@ void Disk2InterfaceCard::LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, U
bImageError = (dwAttributes == INVALID_FILE_ATTRIBUTES);
if (!bImageError)
{
if(InsertDisk(unit, filename.c_str(), dwAttributes & FILE_ATTRIBUTE_READONLY, IMAGE_DONT_CREATE) != eIMAGE_ERROR_NONE)
if (InsertDisk(unit, filename.c_str(), dwAttributes & FILE_ATTRIBUTE_READONLY, IMAGE_DONT_CREATE) != eIMAGE_ERROR_NONE)
bImageError = true;
// DiskInsert() zeros m_floppyDrive[unit], then sets up:
// . imagename
// . fullname
// . writeprotected
// . m_imagename
// . m_fullname
// . m_bWriteProtected
}
}
m_floppyDrive[unit].m_track = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK);
m_floppyDrive[unit].m_phase = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASE);
m_floppyDrive[unit].m_disk.m_byte = yamlLoadHelper.LoadUint(SS_YAML_KEY_BYTE);
yamlLoadHelper.LoadBool(SS_YAML_KEY_WRITE_PROTECTED); // Consume
m_floppyDrive[unit].m_spinning = yamlLoadHelper.LoadUint(SS_YAML_KEY_SPINNING);
m_floppyDrive[unit].m_writelight = yamlLoadHelper.LoadUint(SS_YAML_KEY_WRITE_LIGHT);
m_floppyDrive[unit].m_disk.m_nibbles = yamlLoadHelper.LoadUint(SS_YAML_KEY_NIBBLES);
m_floppyDrive[unit].m_disk.m_trackimagedata = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK_IMAGE_DATA) ? true : false;
m_floppyDrive[unit].m_disk.m_trackimagedirty = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK_IMAGE_DIRTY) ? true : false;
m_floppyDrive[unit].m_disk.m_byte = yamlLoadHelper.LoadUint(SS_YAML_KEY_BYTE);
m_floppyDrive[unit].m_disk.m_nibbles = yamlLoadHelper.LoadUint(SS_YAML_KEY_NIBBLES);
m_floppyDrive[unit].m_disk.m_trackimagedata = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK_IMAGE_DATA) ? true : false;
m_floppyDrive[unit].m_disk.m_trackimagedirty = yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK_IMAGE_DIRTY) ? true : false;
if (version >= 4)
{
m_floppyDrive[unit].m_disk.m_bitOffset = yamlLoadHelper.LoadUint(SS_YAML_KEY_BIT_OFFSET);
m_floppyDrive[unit].m_disk.m_bitCount = yamlLoadHelper.LoadUint(SS_YAML_KEY_BIT_COUNT);
m_floppyDrive[unit].m_disk.m_extraCycles = yamlLoadHelper.LoadDouble(SS_YAML_KEY_EXTRA_CYCLES);
if (m_floppyDrive[unit].m_disk.m_bitCount && (m_floppyDrive[unit].m_disk.m_bitOffset >= m_floppyDrive[unit].m_disk.m_bitCount))
throw std::string("Disk image: bitOffset >= bitCount");
if (ImageIsWOZ(m_floppyDrive[unit].m_disk.m_imagehandle))
UpdateBitStreamOffsets(m_floppyDrive[unit].m_disk); // overwrites m_byte, inits m_bitMask
}
std::vector<BYTE> track(NIBBLES_PER_TRACK);
if (yamlLoadHelper.GetSubMap(SS_YAML_KEY_TRACK_IMAGE))
{
yamlLoadHelper.LoadMemory(&track[0], NIBBLES_PER_TRACK);
yamlLoadHelper.PopMap();
}
return bImageError;
}
bool Disk2InterfaceCard::LoadSnapshotDriveUnitv3(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track)
{
_ASSERT(version <= 3);
std::string disk2UnitName = std::string(SS_YAML_KEY_DISK2UNIT) + (unit == DRIVE_1 ? std::string("0") : std::string("1"));
if (!yamlLoadHelper.GetSubMap(disk2UnitName))
throw std::string("Card: Expected key: ") + disk2UnitName;
bool bImageError = LoadSnapshotFloppy(yamlLoadHelper, unit, version, track);
yamlLoadHelper.LoadUint(SS_YAML_KEY_TRACK); // consume
m_floppyDrive[unit].m_phase = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASE);
m_floppyDrive[unit].m_phasePrecise = (float) m_floppyDrive[unit].m_phase;
m_floppyDrive[unit].m_spinning = yamlLoadHelper.LoadUint(SS_YAML_KEY_SPINNING);
m_floppyDrive[unit].m_writelight = yamlLoadHelper.LoadUint(SS_YAML_KEY_WRITE_LIGHT);
yamlLoadHelper.PopMap();
return bImageError;
}
bool Disk2InterfaceCard::LoadSnapshotDriveUnitv4(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track)
{
_ASSERT(version >= 4);
std::string disk2UnitName = std::string(SS_YAML_KEY_DISK2UNIT) + (unit == DRIVE_1 ? std::string("0") : std::string("1"));
if (!yamlLoadHelper.GetSubMap(disk2UnitName))
throw std::string("Card: Expected key: ") + disk2UnitName;
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_FLOPPY))
throw std::string("Card: Expected key: ") + SS_YAML_KEY_FLOPPY;
bool bImageError = LoadSnapshotFloppy(yamlLoadHelper, unit, version, track);
yamlLoadHelper.PopMap();
//
if (!filename.empty() && !bImageError)
m_floppyDrive[unit].m_phase = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASE);
m_floppyDrive[unit].m_phasePrecise = yamlLoadHelper.LoadFloat(SS_YAML_KEY_PHASE_PRECISE);
m_floppyDrive[unit].m_headWindow = yamlLoadHelper.LoadUint(SS_YAML_KEY_HEAD_WINDOW) & 0xf;
m_floppyDrive[unit].m_lastStepperCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_LAST_STEPPER_CYCLE);
m_floppyDrive[unit].m_spinning = yamlLoadHelper.LoadUint(SS_YAML_KEY_SPINNING);
m_floppyDrive[unit].m_writelight = yamlLoadHelper.LoadUint(SS_YAML_KEY_WRITE_LIGHT);
yamlLoadHelper.PopMap();
return bImageError;
}
void Disk2InterfaceCard::LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version)
{
bool bImageError = false;
std::vector<BYTE> track(NIBBLES_PER_TRACK);
if (version <= 3)
bImageError = LoadSnapshotDriveUnitv3(yamlLoadHelper, unit, version, track);
else
bImageError = LoadSnapshotDriveUnitv4(yamlLoadHelper, unit, version, track);
if (!bImageError)
{
if ((m_floppyDrive[unit].m_disk.m_trackimage == NULL) && m_floppyDrive[unit].m_disk.m_nibbles)
AllocTrack(unit);
@ -1391,9 +1807,9 @@ void Disk2InterfaceCard::LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, U
if (bImageError)
{
m_floppyDrive[unit].m_disk.m_trackimagedata = false;
m_floppyDrive[unit].m_disk.m_trackimagedirty = false;
m_floppyDrive[unit].m_disk.m_nibbles = 0;
m_floppyDrive[unit].m_disk.m_trackimagedata = false;
m_floppyDrive[unit].m_disk.m_trackimagedirty = false;
m_floppyDrive[unit].m_disk.m_nibbles = 0;
}
}
@ -1405,8 +1821,8 @@ bool Disk2InterfaceCard::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT
if (version < 1 || version > kUNIT_VERSION)
throw std::string("Card: wrong version");
m_phases = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASES);
m_currDrive = yamlLoadHelper.LoadUint(SS_YAML_KEY_CURRENT_DRIVE);
m_currDrive = yamlLoadHelper.LoadUint(SS_YAML_KEY_CURRENT_DRIVE);
m_magnetStates = yamlLoadHelper.LoadUint(SS_YAML_KEY_PHASES);
(void) yamlLoadHelper.LoadBool(SS_YAML_KEY_DISK_ACCESSED); // deprecated - but retrieve the value to avoid the "State: Unknown key (Disk Accessed)" warning
m_enhanceDisk = yamlLoadHelper.LoadBool(SS_YAML_KEY_ENHANCE_DISK);
m_floppyLatch = yamlLoadHelper.LoadUint(SS_YAML_KEY_FLOPPY_LATCH);
@ -1424,6 +1840,13 @@ bool Disk2InterfaceCard::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT
m_diskLastReadLatchCycle = yamlLoadHelper.LoadUint64(SS_YAML_KEY_LAST_READ_LATCH_CYCLE);
}
if (version >= 4)
{
m_shiftReg = yamlLoadHelper.LoadUint(SS_YAML_KEY_LSS_SHIFT_REG) & 0xff;
m_latchDelay = yamlLoadHelper.LoadInt(SS_YAML_KEY_LSS_LATCH_DELAY);
m_resetSequencer = yamlLoadHelper.LoadBool(SS_YAML_KEY_LSS_RESET_SEQUENCER);
}
// Eject all disks first in case Drive-2 contains disk to be inserted into Drive-1
for (UINT i=0; i<NUM_DRIVES; i++)
{
@ -1431,8 +1854,8 @@ bool Disk2InterfaceCard::LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT
m_floppyDrive[i].clear();
}
LoadSnapshotDriveUnit(yamlLoadHelper, DRIVE_1);
LoadSnapshotDriveUnit(yamlLoadHelper, DRIVE_2);
LoadSnapshotDriveUnit(yamlLoadHelper, DRIVE_1, version);
LoadSnapshotDriveUnit(yamlLoadHelper, DRIVE_2, version);
FrameRefreshStatus(DRAW_LEDS | DRAW_BUTTON_DRIVES);

View File

@ -61,6 +61,10 @@ public:
//
m_byte = 0;
m_nibbles = 0;
m_bitOffset = 0;
m_bitCount = 0;
m_bitMask = 1 << 7;
m_extraCycles = 0.0;
m_trackimage = NULL;
m_trackimagedata = false;
m_trackimagedirty = false;
@ -72,8 +76,12 @@ public:
std::string m_strFilenameInZip; // "" or <FILENAME.EXT>
ImageInfo* m_imagehandle; // Init'd by InsertDisk() -> ImageOpen()
bool m_bWriteProtected;
int m_byte;
int m_nibbles; // Init'd by ReadTrack() -> ImageReadTrack()
int m_byte; // byte offset
int m_nibbles; // # nibbles in track / Init'd by ReadTrack() -> ImageReadTrack()
UINT m_bitOffset; // bit offset
UINT m_bitCount; // # bits in track
BYTE m_bitMask;
double m_extraCycles;
LPBYTE m_trackimage;
bool m_trackimagedata;
bool m_trackimagedirty;
@ -91,16 +99,20 @@ public:
void clear()
{
m_phasePrecise = 0;
m_phase = 0;
m_track = 0;
m_lastStepperCycle = 0;
m_headWindow = 0;
m_spinning = 0;
m_writelight = 0;
m_disk.clear();
}
public:
int m_phase;
int m_track;
float m_phasePrecise; // Phase precise to half a phase (aka quarter track)
int m_phase; // Integral phase number
unsigned __int64 m_lastStepperCycle;
BYTE m_headWindow;
DWORD m_spinning;
DWORD m_writelight;
FloppyDisk m_disk;
@ -132,10 +144,14 @@ public:
bool GetProtect(const int drive);
void SetProtect(const int drive, const bool bWriteProtect);
int GetCurrentDrive(void);
int GetCurrentTrack();
int GetTrack(const int drive);
int GetCurrentPhase(void);
int GetCurrentTrack(void);
float GetCurrentPhase(void);
int GetCurrentOffset(void);
BYTE GetCurrentLSSBitMask(void);
double GetCurrentExtraCycles(void);
int GetTrack(const int drive);
std::string GetCurrentTrackString(void);
std::string GetCurrentPhaseString(void);
LPCTSTR GetCurrentState(void);
bool UserSelectNewDiskImage(const int drive, LPCSTR pszFilename="");
void UpdateDriveState(DWORD cycles);
@ -158,21 +174,32 @@ public:
static BYTE __stdcall IOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles);
private:
void CheckSpinning(const ULONG nExecutedCycles);
void ResetSwitches(void);
void CheckSpinning(const ULONG uExecutedCycles);
Disk_Status_e GetDriveLightStatus(const int drive);
bool IsDriveValid(const int drive);
void AllocTrack(const int drive);
void ReadTrack(const int drive);
void ReadTrack(const int drive, ULONG uExecutedCycles);
void RemoveDisk(const int drive);
void WriteTrack(const int drive);
LPCTSTR DiskGetFullPathName(const int drive);
void SaveSnapshotDisk2Unit(YamlSaveHelper& yamlSaveHelper, UINT unit);
void LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, UINT unit);
void ResetLogicStateSequencer(void);
void UpdateBitStreamPositionAndDiskCycle(const ULONG uExecutedCycles);
UINT GetBitCellDelta(const BYTE optimalBitTiming);
void UpdateBitStreamPosition(FloppyDisk& floppy, const ULONG bitCellDelta);
void UpdateBitStreamOffsets(FloppyDisk& floppy);
void SaveSnapshotFloppy(YamlSaveHelper& yamlSaveHelper, UINT unit);
void SaveSnapshotDriveUnit(YamlSaveHelper& yamlSaveHelper, UINT unit);
bool LoadSnapshotFloppy(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track);
bool LoadSnapshotDriveUnitv3(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track);
bool LoadSnapshotDriveUnitv4(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version, std::vector<BYTE>& track);
void LoadSnapshotDriveUnit(YamlLoadHelper& yamlLoadHelper, UINT unit, UINT version);
void __stdcall ControlStepper(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles);
void __stdcall ControlMotor(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles);
void __stdcall Enable(WORD, WORD address, BYTE, BYTE, ULONG uExecutedCycles);
void __stdcall ReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExecutedCycles);
void __stdcall ReadWrite(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles);
void __stdcall ReadWriteWOZ(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG uExecutedCycles);
void __stdcall LoadWriteProtect(WORD, WORD, BYTE write, BYTE value, ULONG);
void __stdcall SetReadMode(WORD, WORD, BYTE, BYTE, ULONG);
void __stdcall SetWriteMode(WORD, WORD, BYTE, BYTE, ULONG uExecutedCycles);
@ -189,7 +216,11 @@ private:
BOOL m_floppyMotorOn;
BOOL m_floppyLoadMode; // for efficiency this is not used; it's extremely unlikely to affect emulation (nickw)
BOOL m_floppyWriteMode;
WORD m_phases; // state bits for stepper magnet phases 0 - 3
// Although the magnets are a property of the drive, their state is a property of the controller card,
// since the magnets will only be on for whichever of the 2 drives is currently selected.
WORD m_magnetStates; // state bits for stepper motor magnet states (phases 0 - 3)
bool m_saveDiskImage;
UINT m_slot;
unsigned __int64 m_diskLastCycle;
@ -197,8 +228,14 @@ private:
FormatTrack m_formatTrack;
bool m_enhanceDisk;
static const UINT SPINNING_CYCLES = 20000*64; // 1280000 cycles = 1.25s
static const UINT WRITELIGHT_CYCLES = 20000*64; // 1280000 cycles = 1.25s
static const UINT SPINNING_CYCLES = 1000*1000; // 1M cycles = ~1.000s
static const UINT WRITELIGHT_CYCLES = 1000*1000; // 1M cycles = ~1.000s
// Logic State Sequencer (for WOZ):
BYTE m_shiftReg;
int m_latchDelay;
bool m_resetSequencer;
UINT m_dbgLatchDelayedCnt;
// Debug:
#if LOG_DISK_NIBBLES_USE_RUNTIME_VAR

View File

@ -1,5 +1,7 @@
#pragma once
#define NIBBLES_PER_TRACK 0x1A00
#define NIBBLES_PER_TRACK_NIB 0x1A00
#define NIBBLES_PER_TRACK_WOZ2 0x1A18 // 6680
#define NIBBLES_PER_TRACK NIBBLES_PER_TRACK_WOZ2 // MAX(NIBBLES_PER_TRACK_NIB, NIBBLES_PER_TRACK_WOZ2)
const UINT NUM_SECTORS = 16;

View File

@ -264,17 +264,18 @@ void FormatTrack::DecodeLatchNibble(BYTE floppylatch, bool bIsWrite, bool bIsSyn
m_VolTrkSecChk[i] = ((m_VolTrkSecChk4and4[i*2] & 0x55) << 1) | (m_VolTrkSecChk4and4[i*2+1] & 0x55);
#if LOG_DISK_NIBBLES_READ
const bool chk = (m_VolTrkSecChk[0] ^ m_VolTrkSecChk[1] ^ m_VolTrkSecChk[2] ^ m_VolTrkSecChk[3]) == 0;
if (!bIsWrite)
{
BYTE addrPrologue = m_bAddressPrologueIsDOS3_2 ? (BYTE)kADDR_PROLOGUE_DOS3_2 : (BYTE)kADDR_PROLOGUE_DOS3_3;
LOG_DISK("read D5AA%02X detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X\r\n", addrPrologue, m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3]);
LOG_DISK("read D5AA%02X detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X %s\r\n", addrPrologue, m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3], chk?"":"(bad)");
}
#endif
#if LOG_DISK_NIBBLES_WRITE
if (bIsWrite)
{
BYTE addrPrologue = m_bAddressPrologueIsDOS3_2 ? (BYTE)kADDR_PROLOGUE_DOS3_2 : (BYTE)kADDR_PROLOGUE_DOS3_3;
LOG_DISK("write D5AA%02X detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X\r\n", addrPrologue, m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3]);
LOG_DISK("write D5AA%02X detected - Vol:%02X Trk:%02X Sec:%02X Chk:%02X %s\r\n", addrPrologue, m_VolTrkSecChk[0], m_VolTrkSecChk[1], m_VolTrkSecChk[2], m_VolTrkSecChk[3], chk?"":"(bad)");
}
#endif

View File

@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "StdAfx.h"
#include "Common.h"
#include "DiskImage.h"
#include "DiskImageHelper.h"
@ -152,19 +153,21 @@ void ImageInitialize(void)
//===========================================================================
void ImageReadTrack( ImageInfo* const pImageInfo,
const int nTrack,
const int nQuarterTrack,
float phase, // phase [0..79] +/- 0.5
LPBYTE pTrackImageBuffer,
int* pNibbles,
UINT* pBitCount,
bool enhanceDisk)
{
_ASSERT(nTrack >= 0);
if (nTrack < 0)
return;
_ASSERT(phase >= 0);
if (phase < 0)
phase = 0;
if (pImageInfo->pImageType->AllowRW() && pImageInfo->ValidTrack[nTrack])
const UINT track = pImageInfo->pImageType->PhaseToTrack(phase);
if (pImageInfo->pImageType->AllowRW() && pImageInfo->ValidTrack[track])
{
pImageInfo->pImageType->Read(pImageInfo, nTrack, nQuarterTrack, pTrackImageBuffer, pNibbles, enhanceDisk);
pImageInfo->pImageType->Read(pImageInfo, phase, pTrackImageBuffer, pNibbles, pBitCount, enhanceDisk);
}
else
{
@ -176,19 +179,20 @@ void ImageReadTrack( ImageInfo* const pImageInfo,
//===========================================================================
void ImageWriteTrack( ImageInfo* const pImageInfo,
const int nTrack,
const int nQuarterTrack,
LPBYTE pTrackImage,
float phase, // phase [0..79] +/- 0.5
LPBYTE pTrackImageBuffer,
const int nNibbles)
{
_ASSERT(nTrack >= 0);
if (nTrack < 0)
return;
_ASSERT(phase >= 0);
if (phase < 0)
phase = 0;
const UINT track = pImageInfo->pImageType->PhaseToTrack(phase);
if (pImageInfo->pImageType->AllowRW() && !pImageInfo->bWriteProtected)
{
pImageInfo->pImageType->Write(pImageInfo, nTrack, nQuarterTrack, pTrackImage, nNibbles);
pImageInfo->ValidTrack[nTrack] = 1;
pImageInfo->pImageType->Write(pImageInfo, phase, pTrackImageBuffer, nNibbles);
pImageInfo->ValidTrack[track] = 1;
}
}
@ -220,7 +224,7 @@ bool ImageWriteBlock( ImageInfo* const pImageInfo,
//===========================================================================
int ImageGetNumTracks(ImageInfo* const pImageInfo)
UINT ImageGetNumTracks(ImageInfo* const pImageInfo)
{
return pImageInfo ? pImageInfo->uNumTracks : 0;
}
@ -246,6 +250,33 @@ UINT ImageGetImageSize(ImageInfo* const pImageInfo)
return pImageInfo ? pImageInfo->uImageSize : 0;
}
bool ImageIsWOZ(ImageInfo* const pImageInfo)
{
return pImageInfo ? (pImageInfo->pImageType->GetType() == eImageWOZ1 || pImageInfo->pImageType->GetType() == eImageWOZ2) : false;
}
BYTE ImageGetOptimalBitTiming(ImageInfo* const pImageInfo)
{
return pImageInfo ? pImageInfo->optimalBitTiming : 32;
}
UINT ImagePhaseToTrack(ImageInfo* const pImageInfo, const float phase, const bool limit/*=true*/)
{
if (!pImageInfo)
return 0;
UINT track = pImageInfo->pImageType->PhaseToTrack(phase);
if (limit)
{
const UINT numTracksInImage = ImageGetNumTracks(pImageInfo);
track = (numTracksInImage == 0) ? 0
: MIN(numTracksInImage - 1, track);
}
return track;
}
void GetImageTitle(LPCTSTR pPathname, TCHAR* pImageName, TCHAR* pFullName)
{
TCHAR imagetitle[ MAX_DISK_FULL_NAME+1 ];

View File

@ -71,15 +71,18 @@ BOOL ImageBoot(ImageInfo* const pImageInfo);
void ImageDestroy(void);
void ImageInitialize(void);
void ImageReadTrack(ImageInfo* const pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk);
void ImageWriteTrack(ImageInfo* const pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles);
void ImageReadTrack(ImageInfo* const pImageInfo, float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk);
void ImageWriteTrack(ImageInfo* const pImageInfo, float phase, LPBYTE pTrackImageBuffer, int nNibbles);
bool ImageReadBlock(ImageInfo* const pImageInfo, UINT nBlock, LPBYTE pBlockBuffer);
bool ImageWriteBlock(ImageInfo* const pImageInfo, UINT nBlock, LPBYTE pBlockBuffer);
int ImageGetNumTracks(ImageInfo* const pImageInfo);
UINT ImageGetNumTracks(ImageInfo* const pImageInfo);
bool ImageIsWriteProtected(ImageInfo* const pImageInfo);
bool ImageIsMultiFileZip(ImageInfo* const pImageInfo);
const char* ImageGetPathname(ImageInfo* const pImageInfo);
UINT ImageGetImageSize(ImageInfo* const pImageInfo);
bool ImageIsWOZ(ImageInfo* const pImageInfo);
BYTE ImageGetOptimalBitTiming(ImageInfo* const pImageInfo);
UINT ImagePhaseToTrack(ImageInfo* const pImageInfo, const float phase, const bool limit=true);
void GetImageTitle(LPCTSTR pPathname, TCHAR* pImageName, TCHAR* pFullName);

View File

@ -70,8 +70,8 @@ LPBYTE CImageBase::ms_pWorkBuffer = NULL;
bool CImageBase::ReadTrack(ImageInfo* pImageInfo, const int nTrack, LPBYTE pTrackBuffer, const UINT uTrackSize)
{
const long Offset = pImageInfo->uOffset + nTrack * uTrackSize;
memcpy(pTrackBuffer, &pImageInfo->pImageBuffer[Offset], uTrackSize);
const long offset = pImageInfo->uOffset + nTrack * uTrackSize;
memcpy(pTrackBuffer, &pImageInfo->pImageBuffer[offset], uTrackSize);
return true;
}
@ -630,18 +630,20 @@ public:
return ePossibleMatch;
}
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk)
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{
ReadTrack(pImageInfo, nTrack, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eDOSOrder, nTrack);
const UINT track = PhaseToTrack(phase);
ReadTrack(pImageInfo, track, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eDOSOrder, track);
if (!enhanceDisk)
SkewTrack(nTrack, *pNibbles, pTrackImageBuffer);
SkewTrack(track, *pNibbles, pTrackImageBuffer);
}
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles)
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{
DenibblizeTrack(pTrackImage, eDOSOrder, nNibbles);
WriteTrack(pImageInfo, nTrack, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
const UINT track = PhaseToTrack(phase);
DenibblizeTrack(pTrackImageBuffer, eDOSOrder, nNibbles);
WriteTrack(pImageInfo, track, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
}
virtual bool AllowCreate(void) { return true; }
@ -696,23 +698,25 @@ public:
return ePossibleMatch;
}
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk)
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{
ReadTrack(pImageInfo, nTrack, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eProDOSOrder, nTrack);
const UINT track = PhaseToTrack(phase);
ReadTrack(pImageInfo, track, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eProDOSOrder, track);
if (!enhanceDisk)
SkewTrack(nTrack, *pNibbles, pTrackImageBuffer);
SkewTrack(track, *pNibbles, pTrackImageBuffer);
}
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles)
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{
DenibblizeTrack(pTrackImage, eProDOSOrder, nNibbles);
WriteTrack(pImageInfo, nTrack, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
const UINT track = PhaseToTrack(phase);
DenibblizeTrack(pTrackImageBuffer, eProDOSOrder, nNibbles);
WriteTrack(pImageInfo, track, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
}
virtual eImageType GetType(void) { return eImagePO; }
virtual const char* GetCreateExtensions(void) { return ".po"; }
virtual const char* GetRejectExtensions(void) { return ".do;.iie;.nib;.prg"; }
virtual const char* GetRejectExtensions(void) { return ".do;.iie;.nib;.prg;.woz"; }
};
//-------------------------------------
@ -724,7 +728,7 @@ public:
CNib1Image(void) {}
virtual ~CNib1Image(void) {}
static const UINT NIB1_TRACK_SIZE = NIBBLES_PER_TRACK;
static const UINT NIB1_TRACK_SIZE = NIBBLES_PER_TRACK_NIB;
virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt)
{
@ -735,16 +739,18 @@ public:
return eMatch;
}
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk)
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{
ReadTrack(pImageInfo, nTrack, pTrackImageBuffer, NIB1_TRACK_SIZE);
const UINT track = PhaseToTrack(phase);
ReadTrack(pImageInfo, track, pTrackImageBuffer, NIB1_TRACK_SIZE);
*pNibbles = NIB1_TRACK_SIZE;
}
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles)
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{
_ASSERT(nNibbles == NIB1_TRACK_SIZE); // Must be true - as nNibbles gets init'd by ImageReadTrace()
WriteTrack(pImageInfo, nTrack, pTrackImage, nNibbles);
const UINT track = PhaseToTrack(phase);
WriteTrack(pImageInfo, track, pTrackImageBuffer, nNibbles);
}
virtual bool AllowCreate(void) { return true; }
@ -752,7 +758,7 @@ public:
virtual eImageType GetType(void) { return eImageNIB1; }
virtual const char* GetCreateExtensions(void) { return ".nib"; }
virtual const char* GetRejectExtensions(void) { return ".do;.iie;.po;.prg"; }
virtual const char* GetRejectExtensions(void) { return ".do;.iie;.po;.prg;.woz"; }
};
//-------------------------------------
@ -775,21 +781,23 @@ public:
return eMatch;
}
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk)
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{
ReadTrack(pImageInfo, nTrack, pTrackImageBuffer, NIB2_TRACK_SIZE);
const UINT track = PhaseToTrack(phase);
ReadTrack(pImageInfo, track, pTrackImageBuffer, NIB2_TRACK_SIZE);
*pNibbles = NIB2_TRACK_SIZE;
}
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles)
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{
_ASSERT(nNibbles == NIB2_TRACK_SIZE); // Must be true - as nNibbles gets init'd by ImageReadTrace()
WriteTrack(pImageInfo, nTrack, pTrackImage, nNibbles);
const UINT track = PhaseToTrack(phase);
WriteTrack(pImageInfo, track, pTrackImageBuffer, nNibbles);
}
virtual eImageType GetType(void) { return eImageNIB2; }
virtual const char* GetCreateExtensions(void) { return ".nb2"; }
virtual const char* GetRejectExtensions(void) { return ".do;.iie;.po;.prg;.2mg;.2img"; }
virtual const char* GetRejectExtensions(void) { return ".do;.iie;.po;.prg;.woz;.2mg;.2img"; }
};
//-------------------------------------
@ -851,8 +859,10 @@ public:
return eMatch;
}
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk)
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{
UINT track = PhaseToTrack(phase);
// IF WE HAVEN'T ALREADY DONE SO, READ THE IMAGE FILE HEADER
if (!m_pHeader)
{
@ -872,19 +882,19 @@ public:
if (*(m_pHeader+13) <= 2)
{
ConvertSectorOrder(m_pHeader+14);
SetFilePointer(pImageInfo->hFile, nTrack*TRACK_DENIBBLIZED_SIZE+30, NULL, FILE_BEGIN);
SetFilePointer(pImageInfo->hFile, track*TRACK_DENIBBLIZED_SIZE+30, NULL, FILE_BEGIN);
ZeroMemory(ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE);
DWORD bytesread;
ReadFile(pImageInfo->hFile, ms_pWorkBuffer, TRACK_DENIBBLIZED_SIZE, &bytesread, NULL);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eSIMSYSTEMOrder, nTrack);
*pNibbles = NibblizeTrack(pTrackImageBuffer, eSIMSYSTEMOrder, track);
}
// OTHERWISE, IF THIS IMAGE CONTAINS NIBBLE INFORMATION, READ IT DIRECTLY INTO THE TRACK BUFFER
else
{
*pNibbles = *(LPWORD)(m_pHeader+nTrack*2+14);
*pNibbles = *(LPWORD)(m_pHeader+track*2+14);
LONG Offset = 88;
while (nTrack--)
Offset += *(LPWORD)(m_pHeader+nTrack*2+14);
while (track--)
Offset += *(LPWORD)(m_pHeader+track*2+14);
SetFilePointer(pImageInfo->hFile, Offset, NULL,FILE_BEGIN);
ZeroMemory(pTrackImageBuffer, *pNibbles);
DWORD dwBytesRead;
@ -892,14 +902,14 @@ public:
}
}
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles)
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{
// note: unimplemented
}
virtual eImageType GetType(void) { return eImageIIE; }
virtual const char* GetCreateExtensions(void) { return ".iie"; }
virtual const char* GetRejectExtensions(void) { return ".do.;.nib;.po;.prg;.2mg;.2img"; }
virtual const char* GetRejectExtensions(void) { return ".do.;.nib;.po;.prg;.woz;.2mg;.2img"; }
private:
void ConvertSectorOrder(LPBYTE sourceorder)
@ -973,7 +983,7 @@ public:
virtual eImageType GetType(void) { return eImageAPL; }
virtual const char* GetCreateExtensions(void) { return ".apl"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.iie;.nib;.po;.2mg;.2img"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.iie;.nib;.po;.woz;.2mg;.2img"; }
};
//-------------------------------------
@ -1024,7 +1034,154 @@ public:
virtual eImageType GetType(void) { return eImagePRG; }
virtual const char* GetCreateExtensions(void) { return ".prg"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.iie;.nib;.po;.2mg;.2img"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.iie;.nib;.po;.woz;.2mg;.2img"; }
};
//-------------------------------------
class CWOZEmptyTrack
{
public:
CWOZEmptyTrack(void)
{
m_pWOZEmptyTrack = new BYTE[CWOZHelper::EMPTY_TRACK_SIZE];
srand(1); // Use a fixed seed for determinism
for (UINT i = 0; i < CWOZHelper::EMPTY_TRACK_SIZE; i++)
{
BYTE n = 0;
for (UINT j = 0; j < 8; j++)
{
if (rand() < ((RAND_MAX * 3) / 10)) // ~30% of buffer are 1 bits
n |= 1 << j;
}
m_pWOZEmptyTrack[i] = n;
}
}
virtual ~CWOZEmptyTrack(void) { delete m_pWOZEmptyTrack; }
void ReadEmptyTrack(LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount)
{
memcpy(pTrackImageBuffer, m_pWOZEmptyTrack, CWOZHelper::EMPTY_TRACK_SIZE);
*pNibbles = CWOZHelper::EMPTY_TRACK_SIZE;
*pBitCount = CWOZHelper::EMPTY_TRACK_SIZE * 8;
return;
}
private:
BYTE* m_pWOZEmptyTrack;
};
//-------------------------------------
class CWOZ1Image : public CImageBase, private CWOZEmptyTrack
{
public:
CWOZ1Image(void) {}
virtual ~CWOZ1Image(void) {}
virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt)
{
CWOZHelper::WOZHeader* pWozHdr = (CWOZHelper::WOZHeader*) pImage;
if (pWozHdr->id1 != CWOZHelper::ID1_WOZ1 || pWozHdr->id2 != CWOZHelper::ID2)
return eMismatch;
if (pWozHdr->crc32)
{
// TODO: check crc
}
m_uNumTracksInImage = CWOZHelper::MAX_TRACKS_5_25;
return eMatch;
}
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{
BYTE*& pTrackMap = pImageInfo->pTrackMap;
const int trackFromTMAP = pTrackMap[(UINT)(phase * 2)];
if (trackFromTMAP == 0xFF)
return ReadEmptyTrack(pTrackImageBuffer, pNibbles, pBitCount);
ReadTrack(pImageInfo, trackFromTMAP, pTrackImageBuffer, CWOZHelper::WOZ1_TRACK_SIZE);
CWOZHelper::TRKv1* pTRK = (CWOZHelper::TRKv1*) &pTrackImageBuffer[CWOZHelper::WOZ1_TRK_OFFSET];
*pNibbles = pTRK->bytesUsed;
*pBitCount = pTRK->bitCount;
}
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{
// TODO
_ASSERT(0);
}
// TODO: Uncomment and fix-up if we want to allow .woz image creation (eg. for INIT or FORMAT)
// virtual bool AllowCreate(void) { return true; }
// virtual UINT GetImageSizeForCreate(void) { return 0; }//TODO
virtual eImageType GetType(void) { return eImageWOZ1; }
virtual const char* GetCreateExtensions(void) { return ".woz"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.nib;.iie;.po;.prg"; }
};
//-------------------------------------
class CWOZ2Image : public CImageBase, private CWOZEmptyTrack
{
public:
CWOZ2Image(void) {}
virtual ~CWOZ2Image(void) {}
virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt)
{
CWOZHelper::WOZHeader* pWozHdr = (CWOZHelper::WOZHeader*) pImage;
if (pWozHdr->id1 != CWOZHelper::ID1_WOZ2 || pWozHdr->id2 != CWOZHelper::ID2)
return eMismatch;
if (pWozHdr->crc32)
{
// TODO: check crc
}
m_uNumTracksInImage = CWOZHelper::MAX_TRACKS_5_25;
return eMatch;
}
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk)
{
BYTE*& pTrackMap = pImageInfo->pTrackMap;
const int trackFromTMAP = pTrackMap[(UINT)(phase * 2)];
if (trackFromTMAP == 0xFF)
return ReadEmptyTrack(pTrackImageBuffer, pNibbles, pBitCount);
CWOZHelper::TRKv2* pTRKS = (CWOZHelper::TRKv2*) &pImageInfo->pImageBuffer[pImageInfo->uOffset];
CWOZHelper::TRKv2* pTRK = &pTRKS[trackFromTMAP];
*pBitCount = pTRK->bitCount;
*pNibbles = (pTRK->bitCount+7) / 8;
_ASSERT(*pNibbles <= NIBBLES_PER_TRACK_WOZ2);
if (*pNibbles > NIBBLES_PER_TRACK_WOZ2)
return ReadEmptyTrack(pTrackImageBuffer, pNibbles, pBitCount); // TODO: Enlarge track buffer, but for now just return an empty track
memcpy(pTrackImageBuffer, &pImageInfo->pImageBuffer[pTRK->startBlock*512], *pNibbles);
}
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles)
{
// TODO
_ASSERT(0);
}
// TODO: Uncomment and fix-up if we want to allow .woz image creation (eg. for INIT or FORMAT)
// virtual bool AllowCreate(void) { return true; }
// virtual UINT GetImageSizeForCreate(void) { return 0; }//TODO
virtual eImageType GetType(void) { return eImageWOZ2; }
virtual const char* GetCreateExtensions(void) { return ".woz"; }
virtual const char* GetRejectExtensions(void) { return ".do;.dsk;.nib;.iie;.po;.prg"; }
};
//-----------------------------------------------------------------------------
@ -1048,6 +1205,8 @@ eDetectResult CMacBinaryHelper::DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DW
return eMismatch;
}
//-----------------------------------------------------------------------------
eDetectResult C2IMGHelper::DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DWORD& dwOffset)
{
Header2IMG* pHdr = (Header2IMG*) pImage;
@ -1099,7 +1258,7 @@ eDetectResult C2IMGHelper::DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DWORD&
break;
case e2IMGFormatNIBData:
{
if (pHdr->DiskDataLength != TRACKS_STANDARD*NIBBLES_PER_TRACK)
if (pHdr->DiskDataLength != TRACKS_STANDARD*NIBBLES_PER_TRACK_NIB)
return eMismatch;
}
break;
@ -1126,11 +1285,59 @@ bool C2IMGHelper::IsLocked(void)
//-----------------------------------------------------------------------------
// Pre: already matched the WOZ header
eDetectResult CWOZHelper::ProcessChunks(const LPBYTE pImage, const DWORD dwImageSize, DWORD& dwOffset, BYTE*& pTrackMap)
{
UINT32* pImage32 = (uint32_t*) (pImage + sizeof(WOZHeader));
UINT32 imageSizeRemaining = dwImageSize - sizeof(WOZHeader);
while(imageSizeRemaining > 8)
{
UINT32 chunkId = *pImage32++;
UINT32 chunkSize = *pImage32++;
imageSizeRemaining -= 8;
switch(chunkId)
{
case INFO_CHUNK_ID:
m_pInfo = (InfoChunkv2*)(pImage32-2);
if (m_pInfo->v1.version > InfoChunk::maxSupportedVersion)
return eMismatch;
if (m_pInfo->v1.diskType != InfoChunk::diskType5_25)
return eMismatch;
break;
case TMAP_CHUNK_ID:
pTrackMap = (uint8_t*)pImage32;
break;
case TRKS_CHUNK_ID:
dwOffset = dwImageSize - imageSizeRemaining; // offset into image of track data
break;
case WRIT_CHUNK_ID: // WOZ v2 (optional)
break;
case META_CHUNK_ID: // (optional)
break;
default: // no idea what this chunk is, so skip it
_ASSERT(0);
break;
}
pImage32 = (UINT32*) ((BYTE*)pImage32 + chunkSize);
imageSizeRemaining -= chunkSize;
_ASSERT(imageSizeRemaining >= 0);
if (imageSizeRemaining < 0)
return eMismatch;
}
return eMatch;
}
//-----------------------------------------------------------------------------
// NB. Of the 6 cases (floppy/harddisk x gzip/zip/normal) only harddisk-normal isn't read entirely to memory
// - harddisk-normal-create also doesn't create a max size image-buffer
// DETERMINE THE FILE'S EXTENSION AND CONVERT IT TO LOWERCASE
void GetCharLowerExt(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize)
void CImageHelperBase::GetCharLowerExt(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize)
{
LPCTSTR pImageFileExt = pszImageFilename;
@ -1146,7 +1353,7 @@ void GetCharLowerExt(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSiz
CharLowerBuff(pszExt, _tcslen(pszExt));
}
void GetCharLowerExt2(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize)
void CImageHelperBase::GetCharLowerExt2(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize)
{
TCHAR szFilename[MAX_PATH];
_tcsncpy(szFilename, pszImageFilename, MAX_PATH);
@ -1187,7 +1394,7 @@ ImageError_e CImageHelperBase::CheckGZipFile(LPCTSTR pszImageFilename, ImageInfo
DWORD dwSize = nLen;
DWORD dwOffset = 0;
CImageBase* pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, &pImageInfo->bWriteProtected);
CImageBase* pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, pImageInfo->bWriteProtected, pImageInfo->pTrackMap, pImageInfo->optimalBitTiming);
if (!pImageType)
return eIMAGE_ERROR_UNSUPPORTED;
@ -1196,11 +1403,7 @@ ImageError_e CImageHelperBase::CheckGZipFile(LPCTSTR pszImageFilename, ImageInfo
if (Type == eImageAPL || Type == eImageIIE || Type == eImagePRG)
return eIMAGE_ERROR_UNSUPPORTED;
pImageInfo->FileType = eFileGZip;
pImageInfo->uOffset = dwOffset;
pImageInfo->pImageType = pImageType;
pImageInfo->uImageSize = dwSize;
SetImageInfo(pImageInfo, eFileGZip, dwOffset, pImageType, dwSize);
return eIMAGE_ERROR_NONE;
}
@ -1283,7 +1486,7 @@ ImageError_e CImageHelperBase::CheckZipFile(LPCTSTR pszImageFilename, ImageInfo*
DWORD dwSize = nLen;
DWORD dwOffset = 0;
CImageBase* pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, &pImageInfo->bWriteProtected);
CImageBase* pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, pImageInfo->bWriteProtected, pImageInfo->pTrackMap, pImageInfo->optimalBitTiming);
if (!pImageType)
{
@ -1300,11 +1503,7 @@ ImageError_e CImageHelperBase::CheckZipFile(LPCTSTR pszImageFilename, ImageInfo*
if (global_info.number_entry > 1)
pImageInfo->bWriteProtected = 1; // Zip archives with multiple files are read-only (for now)
pImageInfo->FileType = eFileZip;
pImageInfo->uOffset = dwOffset;
pImageInfo->pImageType = pImageType;
pImageInfo->uImageSize = dwSize;
SetImageInfo(pImageInfo, eFileZip, dwOffset, pImageType, dwSize);
return eIMAGE_ERROR_NONE;
}
@ -1384,7 +1583,7 @@ ImageError_e CImageHelperBase::CheckNormalFile(LPCTSTR pszImageFilename, ImageIn
return eIMAGE_ERROR_BAD_SIZE;
}
pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, &pImageInfo->bWriteProtected);
pImageType = Detect(pImageInfo->pImageBuffer, dwSize, szExt, dwOffset, pImageInfo->bWriteProtected, pImageInfo->pTrackMap, pImageInfo->optimalBitTiming);
if (bTempDetectBuffer)
{
delete [] pImageInfo->pImageBuffer;
@ -1437,12 +1636,18 @@ ImageError_e CImageHelperBase::CheckNormalFile(LPCTSTR pszImageFilename, ImageIn
return eIMAGE_ERROR_UNSUPPORTED;
}
pImageInfo->FileType = eFileNormal;
SetImageInfo(pImageInfo, eFileNormal, dwOffset, pImageType, dwSize);
return eIMAGE_ERROR_NONE;
}
//-------------------------------------
void CImageHelperBase::SetImageInfo(ImageInfo* pImageInfo, FileType_e eFileGZip, DWORD dwOffset, CImageBase* pImageType, DWORD dwSize)
{
pImageInfo->FileType = eFileGZip;
pImageInfo->uOffset = dwOffset;
pImageInfo->pImageType = pImageType;
pImageInfo->uImageSize = dwSize;
return eIMAGE_ERROR_NONE;
}
//-------------------------------------
@ -1517,54 +1722,68 @@ CDiskImageHelper::CDiskImageHelper(void) :
m_vecImageTypes.push_back( new CIIeImage );
m_vecImageTypes.push_back( new CAplImage );
m_vecImageTypes.push_back( new CPrgImage );
m_vecImageTypes.push_back( new CWOZ1Image );
m_vecImageTypes.push_back( new CWOZ2Image );
}
CImageBase* CDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_)
CImageBase* CDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming)
{
dwOffset = 0;
m_MacBinaryHelper.DetectHdr(pImage, dwSize, dwOffset);
m_Result2IMG = m_2IMGHelper.DetectHdr(pImage, dwSize, dwOffset);
// CALL THE DETECTION FUNCTIONS IN ORDER, LOOKING FOR A MATCH
eImageType ImageType = eImageUNKNOWN;
eImageType PossibleType = eImageUNKNOWN;
eImageType imageType = eImageUNKNOWN;
eImageType possibleType = eImageUNKNOWN;
if (m_Result2IMG == eMatch)
{
if (m_2IMGHelper.IsImageFormatDOS33())
ImageType = eImageDO;
imageType = eImageDO;
else if (m_2IMGHelper.IsImageFormatProDOS())
ImageType = eImagePO;
imageType = eImagePO;
if (ImageType != eImageUNKNOWN)
if (imageType != eImageUNKNOWN)
{
CImageBase* pImageType = GetImage(ImageType);
CImageBase* pImageType = GetImage(imageType);
if (!pImageType || !pImageType->IsValidImageSize(dwSize))
ImageType = eImageUNKNOWN;
imageType = eImageUNKNOWN;
}
}
if (ImageType == eImageUNKNOWN)
if (imageType == eImageUNKNOWN)
{
for (UINT uLoop=0; uLoop < GetNumImages() && ImageType == eImageUNKNOWN; uLoop++)
for (UINT uLoop=0; uLoop < GetNumImages() && imageType == eImageUNKNOWN; uLoop++)
{
if (*pszExt && _tcsstr(GetImage(uLoop)->GetRejectExtensions(), pszExt))
continue;
eDetectResult Result = GetImage(uLoop)->Detect(pImage, dwSize, pszExt);
if (Result == eMatch)
ImageType = GetImage(uLoop)->GetType();
else if ((Result == ePossibleMatch) && (PossibleType == eImageUNKNOWN))
PossibleType = GetImage(uLoop)->GetType();
imageType = GetImage(uLoop)->GetType();
else if ((Result == ePossibleMatch) && (possibleType == eImageUNKNOWN))
possibleType = GetImage(uLoop)->GetType();
}
}
if (ImageType == eImageUNKNOWN)
ImageType = PossibleType;
if (imageType == eImageUNKNOWN)
imageType = possibleType;
CImageBase* pImageType = GetImage(ImageType);
CImageBase* pImageType = GetImage(imageType);
if (!pImageType)
return NULL;
if (pImageType)
if (imageType == eImageWOZ1 || imageType == eImageWOZ2)
{
if (m_WOZHelper.ProcessChunks(pImage, dwSize, dwOffset, pTrackMap) != eMatch)
return NULL;
// if (m_WOZHelper.IsWriteProtected() && !writeProtected) // Force write-protected until writing is supported
writeProtected = true;
optimalBitTiming = m_WOZHelper.GetOptimalBitTiming();
}
else
{
if (pImageType->AllowRW())
{
@ -1578,8 +1797,8 @@ CImageBase* CDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* p
{
pImageType->SetVolumeNumber( m_2IMGHelper.GetVolumeNumber() );
if (m_2IMGHelper.IsLocked() && !*pWriteProtected_)
*pWriteProtected_ = 1;
if (m_2IMGHelper.IsLocked() && !writeProtected)
writeProtected = true;
}
else
{
@ -1634,7 +1853,7 @@ CHardDiskImageHelper::CHardDiskImageHelper(void) :
m_vecImageTypes.push_back( new CHDVImage );
}
CImageBase* CHardDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_)
CImageBase* CHardDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming)
{
dwOffset = 0;
m_Result2IMG = m_2IMGHelper.DetectHdr(pImage, dwSize, dwOffset);
@ -1659,11 +1878,14 @@ CImageBase* CHardDiskImageHelper::Detect(LPBYTE pImage, DWORD dwSize, const TCHA
{
if (m_Result2IMG == eMatch)
{
if (m_2IMGHelper.IsLocked() && !*pWriteProtected_)
*pWriteProtected_ = 1;
if (m_2IMGHelper.IsLocked() && !writeProtected)
writeProtected = true;
}
}
pTrackMap = 0; // TODO: WOZ
optimalBitTiming = 0; // TODO: WOZ
return pImageType;
}

View File

@ -10,7 +10,7 @@
#define ZIP_SUFFIX_LEN (sizeof(ZIP_SUFFIX)-1)
enum eImageType {eImageUNKNOWN, eImageDO, eImagePO, eImageNIB1, eImageNIB2, eImageHDV, eImageIIE, eImageAPL, eImagePRG};
enum eImageType {eImageUNKNOWN, eImageDO, eImagePO, eImageNIB1, eImageNIB2, eImageHDV, eImageIIE, eImageAPL, eImagePRG, eImageWOZ1, eImageWOZ2};
enum eDetectResult {eMismatch, ePossibleMatch, eMatch};
class CImageBase;
@ -35,6 +35,8 @@ struct ImageInfo
BYTE ValidTrack[TRACKS_MAX];
UINT uNumTracks;
BYTE* pImageBuffer;
BYTE* pTrackMap; // WOZ only
BYTE optimalBitTiming; // WOZ only
};
//-------------------------------------
@ -54,9 +56,9 @@ public:
virtual bool Boot(ImageInfo* pImageInfo) { return false; }
virtual eDetectResult Detect(const LPBYTE pImage, const DWORD dwImageSize, const TCHAR* pszExt) = 0;
virtual void Read(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImageBuffer, int* pNibbles, bool enhanceDisk) { }
virtual void Read(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int* pNibbles, UINT* pBitCount, bool enhanceDisk) { }
virtual bool Read(ImageInfo* pImageInfo, UINT nBlock, LPBYTE pBlockBuffer) { return false; }
virtual void Write(ImageInfo* pImageInfo, int nTrack, int nQuarterTrack, LPBYTE pTrackImage, int nNibbles) { }
virtual void Write(ImageInfo* pImageInfo, const float phase, LPBYTE pTrackImageBuffer, int nNibbles) { }
virtual bool Write(ImageInfo* pImageInfo, UINT nBlock, LPBYTE pBlockBuffer) { return false; }
virtual bool AllowBoot(void) { return false; } // Only: APL and PRG
@ -71,6 +73,11 @@ public:
void SetVolumeNumber(const BYTE uVolumeNumber) { m_uVolumeNumber = uVolumeNumber; }
bool IsValidImageSize(const DWORD uImageSize);
// To accurately convert a half phase (quarter track) back to a track (round half tracks down), use: ceil(phase)/2, eg:
// . phase=4,+1 half phase = phase 4.5 => ceil(4.5)/2 = track 2 (OK)
// . phase=4,-1 half phase = phase 3.5 => ceil(3.5)/2 = track 2 (OK)
UINT PhaseToTrack(const float phase) { return ((UINT)ceil(phase)) >> 1; }
enum SectorOrder_e {eProDOSOrder, eDOSOrder, eSIMSYSTEMOrder, NUM_SECTOR_ORDERS};
protected:
@ -122,7 +129,7 @@ private:
// http://apple2.org.za/gswv/a2zine/Docs/DiskImage_2MG_Info.txt
#pragma pack(push)
#pragma pack(1) // Ensure Header2IMG is packed
#pragma pack(1) // Ensure Header2IMG & WOZ structs are packed
class C2IMGHelper : public CHdrHelper
{
@ -181,6 +188,98 @@ private:
bool m_bIsFloppy;
};
class CWOZHelper : public CHdrHelper
{
public:
CWOZHelper() :
m_pInfo(NULL)
{}
virtual ~CWOZHelper(void) {}
virtual eDetectResult DetectHdr(LPBYTE& pImage, DWORD& dwImageSize, DWORD& dwOffset) { _ASSERT(0); return eMismatch; }
virtual UINT GetMaxHdrSize(void) { return sizeof(WOZHeader); }
eDetectResult ProcessChunks(const LPBYTE pImage, const DWORD dwImageSize, DWORD& dwOffset, BYTE*& pTrackMap);
bool IsWriteProtected(void) { return m_pInfo->v1.writeProtected == 1; }
BYTE GetOptimalBitTiming(void) { return (m_pInfo->v1.version == 1) ? CWOZHelper::InfoChunkv2::optimalBitTiming5_25 : m_pInfo->optimalBitTiming; }
static const UINT32 ID1_WOZ1 = '1ZOW'; // 'WOZ1'
static const UINT32 ID1_WOZ2 = '2ZOW'; // 'WOZ2'
static const UINT32 ID2 = 0x0A0D0AFF;
struct WOZHeader
{
UINT32 id1; // 'WOZ1' or 'WOZ2'
UINT32 id2;
UINT32 crc32;
};
static const UINT32 MAX_TRACKS_5_25 = 40;
static const UINT32 WOZ1_TRACK_SIZE = 6656; // 0x1A00
static const UINT32 WOZ1_TRK_OFFSET = 6646;
static const UINT32 EMPTY_TRACK_SIZE = 6400;
struct TRKv1
{
UINT16 bytesUsed;
UINT16 bitCount;
UINT16 splicePoint;
BYTE spliceNibble;
BYTE spliceBitCount;
UINT16 reserved;
};
struct TRKv2
{
UINT16 startBlock; // relative to start of file
UINT16 blockCount; // number of blocks for this BITS data
UINT32 bitCount;
};
private:
static const UINT32 INFO_CHUNK_ID = 'OFNI'; // 'INFO'
static const UINT32 TMAP_CHUNK_ID = 'PAMT'; // 'TMAP'
static const UINT32 TRKS_CHUNK_ID = 'SKRT'; // 'TRKS'
static const UINT32 WRIT_CHUNK_ID = 'TIRW'; // 'WRIT' - WOZv2
static const UINT32 META_CHUNK_ID = 'ATEM'; // 'META'
struct InfoChunk
{
UINT32 id;
UINT32 size;
BYTE version;
BYTE diskType;
BYTE writeProtected; // 1 = Floppy is write protected
BYTE synchronized; // 1 = Cross track sync was used during imaging
BYTE cleaned; // 1 = MC3470 fake bits have been removed
BYTE creator[32]; // Name of software that created the WOZ file.
// String in UTF-8. No BOM. Padded to 32 bytes
// using space character (0x20).
static const BYTE maxSupportedVersion = 2;
static const BYTE diskType5_25 = 1;
static const BYTE diskType3_5 = 2;
};
struct InfoChunkv2
{
InfoChunk v1;
BYTE diskSides; // 5.25 will always be 1; 3.5 can be 1 or 2
BYTE bootSectorFormat;
BYTE optimalBitTiming; // in 125ns increments (And a standard bit rate for 5.25 disk would be 32 (4us))
UINT16 compatibleHardware;
UINT16 requiredRAM; // in K (1024 bytes)
UINT16 largestTrack; // in blocks (512 bytes)
static const BYTE bootUnknown = 0;
static const BYTE bootSector16 = 1;
static const BYTE bootSector13 = 2;
static const BYTE bootSectorBoth = 3;
static const BYTE optimalBitTiming5_25 = 32;
};
InfoChunkv2* m_pInfo;
};
#pragma pack(pop)
//-------------------------------------
@ -190,7 +289,8 @@ class CImageHelperBase
public:
CImageHelperBase(const bool bIsFloppy) :
m_2IMGHelper(bIsFloppy),
m_Result2IMG(eMismatch)
m_Result2IMG(eMismatch),
m_WOZHelper()
{
}
virtual ~CImageHelperBase(void)
@ -202,7 +302,7 @@ public:
ImageError_e Open(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, const bool bCreateIfNecessary, std::string& strFilenameInZip);
void Close(ImageInfo* pImageInfo, const bool bDeleteFile);
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_) = 0;
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming) = 0;
virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize) = 0;
virtual UINT GetMaxImageSize(void) = 0;
virtual UINT GetMinDetectSize(const UINT uImageSize, bool* pTempDetectBuffer) = 0;
@ -211,6 +311,9 @@ protected:
ImageError_e CheckGZipFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo);
ImageError_e CheckZipFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, std::string& strFilenameInZip);
ImageError_e CheckNormalFile(LPCTSTR pszImageFilename, ImageInfo* pImageInfo, const bool bCreateIfNecessary);
void GetCharLowerExt(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize);
void GetCharLowerExt2(TCHAR* pszExt, LPCTSTR pszImageFilename, const UINT uExtSize);
void SetImageInfo(ImageInfo* pImageInfo, FileType_e eFileGZip, DWORD dwOffset, CImageBase* pImageType, DWORD dwSize);
UINT GetNumImages(void) { return m_vecImageTypes.size(); };
CImageBase* GetImage(UINT uIndex) { _ASSERT(uIndex<GetNumImages()); return m_vecImageTypes[uIndex]; }
@ -233,6 +336,7 @@ protected:
C2IMGHelper m_2IMGHelper;
eDetectResult m_Result2IMG;
CWOZHelper m_WOZHelper;
};
//-------------------------------------
@ -243,7 +347,7 @@ public:
CDiskImageHelper(void);
virtual ~CDiskImageHelper(void) {}
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_);
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming);
virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize);
virtual UINT GetMaxImageSize(void);
virtual UINT GetMinDetectSize(const UINT uImageSize, bool* pTempDetectBuffer);
@ -269,7 +373,7 @@ public:
CHardDiskImageHelper(void);
virtual ~CHardDiskImageHelper(void) {}
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool* pWriteProtected_);
virtual CImageBase* Detect(LPBYTE pImage, DWORD dwSize, const TCHAR* pszExt, DWORD& dwOffset, bool& writeProtected, BYTE*& pTrackMap, BYTE& optimalBitTiming);
virtual CImageBase* GetImageForCreation(const TCHAR* pszExt, DWORD* pCreateImageSize);
virtual UINT GetMaxImageSize(void);
virtual UINT GetMinDetectSize(const UINT uImageSize, bool* pTempDetectBuffer);

View File

@ -574,7 +574,7 @@ void KeybLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
keycode = (BYTE) yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTKEY);
if (version == 2)
if (version >= 2)
keywaiting = (BOOL) yamlLoadHelper.LoadBool(SS_YAML_KEY_KEYWAITING);
yamlLoadHelper.PopMap();

View File

@ -1745,6 +1745,8 @@ void MemReset()
g_eExpansionRomType = eExpRomNull;
g_uPeripheralRomSlot = 0;
ZeroMemory(memdirty, 0x100);
//
int iByte;
@ -2202,7 +2204,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);
}
@ -1737,13 +1738,46 @@ void MB_EndOfVideoFrame()
//-----------------------------------------------------------------------------
static bool CheckTimerUnderflowAndIrq(USHORT& timerCounter, int& timerIrqDelay, const USHORT nClocks, bool* pTimerUnderflow=NULL)
{
int oldTimer = timerCounter; // Catch the case for 0x0000 -> -ve, as this isn't an underflow
int timer = timerCounter;
timer -= nClocks;
timerCounter = (USHORT)timer;
bool timerIrq = false;
if (timerIrqDelay) // Deal with any previous counter underflow which didn't yet result in an IRQ
{
timerIrqDelay -= nClocks;
if (timerIrqDelay <= 0)
{
timerIrqDelay = 0;
timerIrq = true;
}
// don't re-underflow if TIMER = 0x0000 or 0xFFFF (so just return)
}
else if (oldTimer > 0 && timer <= 0) // Underflow occurs for 0x0001 -> 0x0000
{
if (pTimerUnderflow)
*pTimerUnderflow = true; // Just for Willy Byte!
if (timer <= -2)
timerIrq = true;
else // TIMER = 0x0000 or 0xFFFF
timerIrqDelay = 2 + timer; // ...so 2 or 1 cycles until IRQ
}
return timerIrq;
}
// Called by:
// . CpuExecute() every ~1000 @ 1MHz
// . CheckInterruptSources() every 128 cycles
// . MB_Read() / MB_Write()
void MB_UpdateCycles(ULONG uExecutedCycles)
{
if(g_SoundcardType == CT_Empty)
if (g_SoundcardType == CT_Empty)
return;
CpuCalcCycles(uExecutedCycles);
@ -1752,19 +1786,13 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
_ASSERT(uCycles < 0x10000);
USHORT nClocks = (USHORT) uCycles;
for(int i=0; i<NUM_SY6522; i++)
for (int i=0; i<NUM_SY6522; i++)
{
SY6522_AY8910* pMB = &g_MB[i];
USHORT OldTimer1 = pMB->sy6522.TIMER1_COUNTER.w;
USHORT OldTimer2 = pMB->sy6522.TIMER2_COUNTER.w;
pMB->sy6522.TIMER1_COUNTER.w -= nClocks;
pMB->sy6522.TIMER2_COUNTER.w -= nClocks;
// Check for counter underflow
bool bTimer1Underflow = (!(OldTimer1 & 0x8000) && (pMB->sy6522.TIMER1_COUNTER.w & 0x8000));
bool bTimer2Underflow = (!(OldTimer2 & 0x8000) && (pMB->sy6522.TIMER2_COUNTER.w & 0x8000));
bool bTimer1Underflow = false; // Just for Willy Byte!
const bool bTimer1Irq = CheckTimerUnderflowAndIrq(pMB->sy6522.TIMER1_COUNTER.w, pMB->sy6522.timer1IrqDelay, nClocks, &bTimer1Underflow);
const bool bTimer2Irq = CheckTimerUnderflowAndIrq(pMB->sy6522.TIMER2_COUNTER.w, pMB->sy6522.timer2IrqDelay, nClocks);
if (!pMB->bTimer1Active && bTimer1Underflow)
{
@ -1774,11 +1802,12 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
{
// Fix for Willy Byte - need to confirm that 6522 really does this!
// . It never accesses IER/IFR/TIMER1 regs to clear IRQ
// . NB. Willy Byte doesn't work with Phasor.
UpdateIFR(pMB, IxR_TIMER1); // Deassert the TIMER IRQ
}
}
if (pMB->bTimer1Active && bTimer1Underflow)
if (pMB->bTimer1Active && bTimer1Irq)
{
UpdateIFR(pMB, 0, IxR_TIMER1);
@ -1786,7 +1815,7 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
if (g_nMBTimerDevice == i)
MB_Update();
if((pMB->sy6522.ACR & RUNMODE) == RM_ONESHOT)
if ((pMB->sy6522.ACR & RUNMODE) == RM_ONESHOT)
{
// One-shot mode
// - Phasor's playback code uses one-shot mode
@ -1797,11 +1826,21 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
{
// Free-running mode
// - Ultima4/5 change ACCESS_TIMER1 after a couple of IRQs into tune
pMB->sy6522.TIMER1_COUNTER.w = pMB->sy6522.TIMER1_LATCH.w;
pMB->sy6522.TIMER1_COUNTER.w += pMB->sy6522.TIMER1_LATCH.w; // GH#651: account for underflowed cycles too
pMB->sy6522.TIMER1_COUNTER.w += 2; // GH#652: account for extra 2 cycles (Rockwell, Fig.16: period=N+2cycles)
// - or maybe the counter doesn't count down during these 2 cycles?
if (pMB->sy6522.TIMER1_COUNTER.w > pMB->sy6522.TIMER1_LATCH.w)
{
if (pMB->sy6522.TIMER1_LATCH.w)
pMB->sy6522.TIMER1_COUNTER.w %= pMB->sy6522.TIMER1_LATCH.w; // Only occurs if LATCH.w<0x0007 (# cycles for longest opcode)
else
pMB->sy6522.TIMER1_COUNTER.w = 0;
}
StartTimer1(pMB);
}
}
else if (pMB->bTimer2Active && bTimer2Underflow)
if (pMB->bTimer2Active && bTimer2Irq)
{
UpdateIFR(pMB, 0, IxR_TIMER2);
@ -1811,7 +1850,14 @@ void MB_UpdateCycles(ULONG uExecutedCycles)
}
else
{
pMB->sy6522.TIMER2_COUNTER.w = pMB->sy6522.TIMER2_LATCH.w;
pMB->sy6522.TIMER2_COUNTER.w += pMB->sy6522.TIMER2_LATCH.w;
if (pMB->sy6522.TIMER2_COUNTER.w > pMB->sy6522.TIMER2_LATCH.w)
{
if (pMB->sy6522.TIMER2_LATCH.w)
pMB->sy6522.TIMER2_COUNTER.w %= pMB->sy6522.TIMER2_LATCH.w;
else
pMB->sy6522.TIMER2_COUNTER.w = 0;
}
StartTimer2(pMB);
}
}
@ -1903,7 +1949,8 @@ void MB_GetSnapshot_v1(SS_CARD_MOCKINGBOARD_v1* const pSS, const DWORD dwSlot)
// Unit version history:
// 2: Added: Timer1 & Timer2 active
// 3: Added: Unit state
const UINT kUNIT_VERSION = 3;
// 4: Added: 6522 timerIrqDelay
const UINT kUNIT_VERSION = 4;
const UINT NUM_MB_UNITS = 2;
const UINT NUM_PHASOR_UNITS = 2;
@ -1937,6 +1984,8 @@ const UINT NUM_PHASOR_UNITS = 2;
#define SS_YAML_KEY_SPEECH_IRQ "Speech IRQ Pending"
#define SS_YAML_KEY_TIMER1_ACTIVE "Timer1 Active"
#define SS_YAML_KEY_TIMER2_ACTIVE "Timer2 Active"
#define SS_YAML_KEY_SY6522_TIMER1_IRQ_DELAY "Timer1 IRQ Delay"
#define SS_YAML_KEY_SY6522_TIMER2_IRQ_DELAY "Timer2 IRQ Delay"
#define SS_YAML_KEY_PHASOR_UNIT "Unit"
#define SS_YAML_KEY_PHASOR_CLOCK_SCALE_FACTOR "Clock Scale Factor"
@ -1964,8 +2013,10 @@ static void SaveSnapshotSY6522(YamlSaveHelper& yamlSaveHelper, SY6522& sy6522)
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SY6522_REG_DDRA, sy6522.DDRA);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_SY6522_REG_T1_COUNTER, sy6522.TIMER1_COUNTER.w);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_SY6522_REG_T1_LATCH, sy6522.TIMER1_LATCH.w);
yamlSaveHelper.SaveUint(SS_YAML_KEY_SY6522_TIMER1_IRQ_DELAY, sy6522.timer1IrqDelay); // v4
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_SY6522_REG_T2_COUNTER, sy6522.TIMER2_COUNTER.w);
yamlSaveHelper.SaveHexUint16(SS_YAML_KEY_SY6522_REG_T2_LATCH, sy6522.TIMER2_LATCH.w);
yamlSaveHelper.SaveUint(SS_YAML_KEY_SY6522_TIMER2_IRQ_DELAY, sy6522.timer2IrqDelay); // v4
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SY6522_REG_SERIAL_SHIFT, sy6522.SERIAL_SHIFT);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SY6522_REG_ACR, sy6522.ACR);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SY6522_REG_PCR, sy6522.PCR);
@ -2017,7 +2068,7 @@ void MB_SaveSnapshot(YamlSaveHelper& yamlSaveHelper, const UINT uSlot)
}
}
static void LoadSnapshotSY6522(YamlLoadHelper& yamlLoadHelper, SY6522& sy6522)
static void LoadSnapshotSY6522(YamlLoadHelper& yamlLoadHelper, SY6522& sy6522, UINT version)
{
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_SY6522))
throw std::string("Card: Expected key: ") + std::string(SS_YAML_KEY_SY6522);
@ -2037,6 +2088,14 @@ static void LoadSnapshotSY6522(YamlLoadHelper& yamlLoadHelper, SY6522& sy6522)
sy6522.IER = yamlLoadHelper.LoadUint(SS_YAML_KEY_SY6522_REG_IER);
sy6522.ORA_NO_HS = 0; // Not saved
sy6522.timer1IrqDelay = sy6522.timer2IrqDelay = 0;
if (version >= 4)
{
sy6522.timer1IrqDelay = yamlLoadHelper.LoadUint(SS_YAML_KEY_SY6522_TIMER1_IRQ_DELAY);
sy6522.timer2IrqDelay = yamlLoadHelper.LoadUint(SS_YAML_KEY_SY6522_TIMER2_IRQ_DELAY);
}
yamlLoadHelper.PopMap();
}
@ -2079,7 +2138,7 @@ bool MB_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version)
if (!yamlLoadHelper.GetSubMap(unit))
throw std::string("Card: Expected key: ") + std::string(unit);
LoadSnapshotSY6522(yamlLoadHelper, pMB->sy6522);
LoadSnapshotSY6522(yamlLoadHelper, pMB->sy6522, version);
AY8910_LoadSnapshot(yamlLoadHelper, nDeviceNum, std::string(""));
LoadSnapshotSSI263(yamlLoadHelper, pMB->SpeechChip);
@ -2131,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()
@ -2201,7 +2260,7 @@ bool Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version
if (!yamlLoadHelper.GetSubMap(unit))
throw std::string("Card: Expected key: ") + std::string(unit);
LoadSnapshotSY6522(yamlLoadHelper, pMB->sy6522);
LoadSnapshotSY6522(yamlLoadHelper, pMB->sy6522, version);
AY8910_LoadSnapshot(yamlLoadHelper, nDeviceNum+0, std::string("-A"));
AY8910_LoadSnapshot(yamlLoadHelper, nDeviceNum+1, std::string("-B"));
LoadSnapshotSSI263(yamlLoadHelper, pMB->SpeechChip);
@ -2254,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

@ -125,13 +125,22 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
static int g_nHiresPage = 1;
static int g_nTextPage = 1;
static bool g_bDelayVideoMode = false;
static uint32_t g_uNewVideoModeFlags = 0;
// Understanding the Apple II, Timing Generation and the Video Scanner, Pg 3-11
// Vertical Scanning
// Horizontal Scanning
// "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
@ -209,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
@ -240,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,
@ -765,7 +774,7 @@ inline void updateVideoScannerHorzEOLSimple()
{
g_nVideoClockHorz = 0;
if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT)
if (++g_nVideoClockVert == g_videoScannerMaxVert)
{
g_nVideoClockVert = 0;
@ -804,7 +813,7 @@ inline void updateVideoScannerHorzEOL()
g_nVideoClockHorz = 0;
if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT)
if (++g_nVideoClockVert == g_videoScannerMaxVert)
{
g_nVideoClockVert = 0;
@ -848,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;
@ -894,7 +903,7 @@ inline void updateVideoScannerHorzEOL()
g_nVideoClockHorz = 0;
if (++g_nVideoClockVert == VIDEO_SCANNER_MAX_VERT)
if (++g_nVideoClockVert == g_videoScannerMaxVert)
{
g_nVideoClockVert = 0;
@ -1835,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);
}
//===========================================================================
@ -1852,13 +1861,13 @@ uint16_t NTSC_VideoGetScannerAddress ( const ULONG uExecutedCycles )
const uint16_t currVideoClockHorz = g_nVideoClockHorz;
// Required for ANSI STORY (end credits) vert scrolling mid-scanline mixed mode: DGR80, TEXT80, DGR80
g_nVideoClockHorz -= 2;
g_nVideoClockHorz -= 1;
if ((SHORT)g_nVideoClockHorz < 0)
{
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;
@ -1884,8 +1893,15 @@ void NTSC_SetVideoTextMode( int cols )
}
//===========================================================================
void NTSC_SetVideoMode( uint32_t uVideoModeFlags )
void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay/*=false*/ )
{
if (bDelay)
{
g_bDelayVideoMode = true;
g_uNewVideoModeFlags = uVideoModeFlags;
return;
}
g_nVideoMixed = uVideoModeFlags & VF_MIXED;
g_nVideoCharSet = VideoGetSWAltCharSet() ? 1 : 0;
@ -2144,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;
@ -2183,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()
@ -2198,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;
@ -2207,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;
@ -2225,9 +2241,21 @@ static void VideoUpdateCycles( int cyclesLeftToUpdate )
}
//===========================================================================
void NTSC_VideoUpdateCycles( long cycles6502 )
void NTSC_VideoUpdateCycles( UINT cycles6502 )
{
_ASSERT(cycles6502 < VIDEO_SCANNER_6502_CYCLES); // Use NTSC_VideoRedrawWholeScreen() instead
_ASSERT(cycles6502 && cycles6502 < g_videoScanner6502Cycles); // Use NTSC_VideoRedrawWholeScreen() instead
if (g_bDelayVideoMode)
{
VideoUpdateCycles(1); // Video mode change is delayed by 1 cycle
g_bDelayVideoMode = false;
NTSC_SetVideoMode(g_uNewVideoModeFlags);
cycles6502--;
if (!cycles6502)
return;
}
VideoUpdateCycles(cycles6502);
}
@ -2247,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
@ -2302,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)
{
g_aClockVertOffsetsHGR[i] = VideoGetScannerAddress(cycle, VS_PartialAddrV);
_ASSERT(g_aClockVertOffsetsHGR[i] == g_kClockVertOffsetsHGR[i]);
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);
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);
}
}
//
@ -2322,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)
{
g_aClockVertOffsetsTXT[i] = VideoGetScannerAddress(cycle, VS_PartialAddrV);
_ASSERT(g_aClockVertOffsetsTXT[i] == g_kClockVertOffsetsTXT[i]);
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);
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);
}
}
//
@ -2339,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]);
}
}
@ -2354,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]);
}
}
@ -2362,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++)
{
@ -2393,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

@ -4,7 +4,7 @@
extern uint32_t g_nChromaSize;
// Prototypes (Public) ________________________________________________
extern void NTSC_SetVideoMode( uint32_t uVideoModeFlags );
extern void NTSC_SetVideoMode( uint32_t uVideoModeFlags, bool bDelay=false );
extern void NTSC_SetVideoStyle();
extern void NTSC_SetVideoTextMode( int cols );
extern uint32_t*NTSC_VideoGetChromaTable( bool bHueTypeMonochrome, bool bMonitorTypeColorTV );
@ -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

@ -115,6 +115,8 @@ struct SY6522
IWORD TIMER1_LATCH;
IWORD TIMER2_COUNTER;
IWORD TIMER2_LATCH;
int timer1IrqDelay;
int timer2IrqDelay;
//
BYTE SERIAL_SHIFT; // $0A
BYTE ACR; // $0B - Auxiliary Control Register

View File

@ -92,7 +92,7 @@ struct DISK2_Unit
DWORD spinning;
DWORD writelight;
int nibbles;
BYTE nTrack[NIBBLES_PER_TRACK];
BYTE nTrack[NIBBLES_PER_TRACK_NIB];
};
struct SS_CARD_DISK2

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,17 +542,7 @@ void VideoRedrawScreenDuringFullSpeed(DWORD dwCyclesThisFrame, bool bInit /*=fal
void VideoRedrawScreenAfterFullSpeed(DWORD dwCyclesThisFrame)
{
if (g_bVideoScannerNTSC)
{
NTSC_VideoClockResync(dwCyclesThisFrame);
}
else // PAL
{
_ASSERT(0);
g_nVideoClockVert = (uint16_t) (dwCyclesThisFrame / kHClocks) % kPALScanLines;
g_nVideoClockHorz = (uint16_t) (dwCyclesThisFrame % kHClocks);
}
NTSC_VideoClockResync(dwCyclesThisFrame);
VideoRedrawScreen(); // Better (no flicker) than using: NTSC_VideoReinitialize() or VideoReinitialize()
}
@ -652,6 +642,8 @@ BYTE VideoSetMode(WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCycles)
{
address &= 0xFF;
const uint32_t oldVideoMode = g_uVideoMode;
switch (address)
{
case 0x00: g_uVideoMode &= ~VF_80STORE; break;
@ -675,7 +667,11 @@ BYTE VideoSetMode(WORD, WORD address, BYTE write, BYTE, ULONG uExecutedCycles)
if (!IS_APPLE2)
RGB_SetVideoMode(address);
NTSC_SetVideoMode( g_uVideoMode );
bool delay = true;
if ((oldVideoMode ^ g_uVideoMode) & VF_PAGE2)
delay = false; // PAGE2 flag changed state, so no 1 cycle delay (GH#656)
NTSC_SetVideoMode( g_uVideoMode, delay );
return MemReadFloatingBus(uExecutedCycles);
}
@ -724,9 +720,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)
{
@ -737,19 +734,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();
}
@ -771,7 +776,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
@ -789,7 +794,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
@ -862,7 +867,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;
@ -875,6 +880,7 @@ WORD VideoGetScannerAddress(DWORD nCycles, VideoScanner_e videoScannerAddr /*= V
//===========================================================================
// TODO: Consider replacing simply with: return g_nVideoClockVert < kVDisplayableScanLines
bool VideoGetVblBar(const DWORD uExecutedCycles)
{
// get video scanner position
@ -1225,6 +1231,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();
@ -1269,6 +1279,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());
}
//===========================================================================
@ -1299,6 +1310,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

@ -342,6 +342,34 @@ std::string YamlLoadHelper::LoadString(const std::string& key)
return value;
}
float YamlLoadHelper::LoadFloat(const std::string key)
{
bool bFound;
std::string value = m_yamlHelper.GetMapValue(*m_pMapYaml, key, bFound);
if (value == "")
{
m_bDoGetMapRemainder = false;
throw std::string(m_currentMapName + ": Missing: " + key);
}
#if (_MSC_VER >= 1900)
return strtof(value.c_str(), NULL); // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015 version 14.0)
#else
return (float) strtod(value.c_str(), NULL); // NB. strtof() requires VS2015
#endif
}
double YamlLoadHelper::LoadDouble(const std::string key)
{
bool bFound;
std::string value = m_yamlHelper.GetMapValue(*m_pMapYaml, key, bFound);
if (value == "")
{
m_bDoGetMapRemainder = false;
throw std::string(m_currentMapName + ": Missing: " + key);
}
return strtod(value.c_str(), NULL);
}
void YamlLoadHelper::LoadMemory(const LPBYTE pMemBase, const size_t size)
{
m_yamlHelper.LoadMemory(*m_pMapYaml, pMemBase, size);
@ -371,7 +399,7 @@ void YamlSaveHelper::SaveUint(const char* key, UINT value)
void YamlSaveHelper::SaveHexUint4(const char* key, UINT value)
{
Save("%s: 0x%01X\n", key, value);
Save("%s: 0x%01X\n", key, value & 0xf);
}
void YamlSaveHelper::SaveHexUint8(const char* key, UINT value)
@ -414,6 +442,16 @@ void YamlSaveHelper::SaveString(const char* key, const char* value)
Save("%s: %s\n", key, (value[0] != 0) ? value : "\"\"");
}
void YamlSaveHelper::SaveFloat(const char* key, float value)
{
Save("%s: %f\n", key, value);
}
void YamlSaveHelper::SaveDouble(const char* key, double value)
{
Save("%s: %f\n", key, value);
}
// Pre: uMemSize must be multiple of 8
void YamlSaveHelper::SaveMemory(const LPBYTE pMemBase, const UINT uMemSize)
{

View File

@ -97,6 +97,8 @@ public:
bool LoadBool(const std::string key);
std::string LoadString_NoThrow(const std::string& key, bool& bFound);
std::string LoadString(const std::string& key);
float LoadFloat(const std::string key);
double LoadDouble(const std::string key);
void LoadMemory(const LPBYTE pMemBase, const size_t size);
bool GetSubMap(const std::string key)
@ -213,7 +215,9 @@ public:
void SaveHexUint32(const char* key, UINT value);
void SaveHexUint64(const char* key, UINT64 value);
void SaveBool(const char* key, bool value);
void SaveString(const char* key, const char* value);
void SaveString(const char* key, const char* value);
void SaveFloat(const char* key, float value);
void SaveDouble(const char* key, double value);
void SaveMemory(const LPBYTE pMemBase, const UINT uMemSize);
class Label

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

View File

@ -54,7 +54,7 @@ static __forceinline void DoIrqProfiling(DWORD uCycles)
{
}
static __forceinline void CheckInterruptSources(ULONG uExecutedCycles)
static __forceinline void CheckInterruptSources(ULONG uExecutedCycles, const bool bVideoUpdate)
{
}