Compare commits

...

4 Commits

Author SHA1 Message Date
Andrea c0bfb0b0fe
linux munmap: fix comment and simplify memory free. (PR #1293) 2024-04-26 21:53:09 +01:00
tomcw 664d7c2d86 Phasor card: in native mode, writes to SSI263 regs 0,1 & 2 all clear the SSI263 IRQ. (#1197) 2024-04-13 13:23:10 +01:00
TomCh b00c9b0d3f
Fix for alloc of 'memimage': so that two adjacent 64K regions map to same physical 64K region. Fixes #1285. (PR #1286)
Includes platform-specific code for both Windows & Linux.
Co-authored-by: @audetto
2024-04-01 10:20:43 +01:00
tomcw d3ff855f2d Minor: replace IS_APPLE2 macro with function 2024-03-28 22:36:34 +00:00
3 changed files with 177 additions and 26 deletions

View File

@ -56,9 +56,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "YamlHelper.h"
// In this file allocate the 64KB of RAM with aligned memory allocations (0x10000)
// to ease mapping between Apple ][ and host memory space (while debugging).
// this is not available in Visual Studio
// to ease mapping between Apple ][ and host memory space (while debugging) & also to fix GH#1285.
// This is not available in Windows CRT:
// https://en.cppreference.com/w/c/memory/aligned_alloc
#ifdef _MSC_VER
@ -66,6 +65,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#define ALIGNED_ALLOC(size) (LPBYTE)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE)
#define ALIGNED_FREE(ptr) VirtualFree(ptr, 0, MEM_RELEASE)
#else
#include <unistd.h>
#include <sys/mman.h>
// use plain "new" in gcc (where debugging needs are less important)
#define ALIGNED_ALLOC(size) new BYTE[size]
#define ALIGNED_FREE(ptr) delete [] ptr
@ -242,7 +243,15 @@ static LPBYTE RWpages[kMaxExMemoryBanks]; // pointers to RW memory banks
static const UINT kNumAnnunciators = 4;
static bool g_Annunciator[kNumAnnunciators] = {};
static const UINT num64KPages = 2; // number of 64K pages used to create hardware circular buffer
#ifdef _MSC_VER
static HANDLE g_hMemImage = NULL; // NB. When not initialised, this handle is NULL (not INVALID_HANDLE_VALUE)
#else
static FILE * g_hMemTempFile = NULL;
#endif
BYTE __stdcall IO_Annunciator(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles);
static void FreeMemImage(void);
//=============================================================================
@ -1289,7 +1298,7 @@ void MemDestroy()
{
ALIGNED_FREE(memaux);
ALIGNED_FREE(memmain);
ALIGNED_FREE(memimage);
FreeMemImage();
delete [] memdirty;
delete [] memrom;
@ -1509,12 +1518,153 @@ bool MemIsAddrCodeMemory(const USHORT addr)
//===========================================================================
static void FreeMemImage(void)
{
#ifdef _MSC_VER
if (g_hMemImage)
{
for (UINT i = 0; i < num64KPages; i++)
UnmapViewOfFile(memimage + i * _6502_MEM_LEN);
CloseHandle(g_hMemImage);
g_hMemImage = NULL;
}
else
{
ALIGNED_FREE(memimage);
}
#else
if (g_hMemTempFile)
{
// unmap the whole region, everything inside will be unmapped too
munmap(memimage, num64KPages * _6502_MEM_LEN);
fclose(g_hMemTempFile);
g_hMemTempFile = NULL;
}
else
{
ALIGNED_FREE(memimage);
}
#endif
}
static LPBYTE AllocMemImage(void)
{
#ifdef _MSC_VER
LPBYTE baseAddr = NULL;
// Allocate memory for 'memimage' (and the alias 'mem')
// . Setup so we have 2 consecutive virtual 64K regions pointing to the same physical 64K region.
// . This is a fix (and optimisation) for 6502 opcodes that do a 16-bit read at 6502 address $FFFF. (GH#1285)
SYSTEM_INFO info;
GetSystemInfo(&info);
bool res = (info.dwAllocationGranularity == _6502_MEM_LEN);
if (res)
{
UINT retry = 10;
do
{
res = false;
const SIZE_T totalVirtualSize = _6502_MEM_LEN * num64KPages;
baseAddr = (LPBYTE)VirtualAlloc(0, totalVirtualSize, MEM_RESERVE, PAGE_NOACCESS);
if (baseAddr == NULL)
break;
VirtualFree(baseAddr, 0, MEM_RELEASE);
// Create a file mapping object of [64K] size that is backed by the system paging file.
g_hMemImage = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, _6502_MEM_LEN, NULL);
// NB. Returns NULL on failure (not INVALID_HANDLE_VALUE)
if (g_hMemImage == NULL)
break;
UINT count = 0;
while (count < num64KPages)
{
// MSDN: "To specify a suggested base address for the view, use the MapViewOfFileEx function. However, this practice is not recommended."
// The OS (ie. another process) may've beaten us to this suggested baseAddr. This is why we retry multiple times.
if (!MapViewOfFileEx(g_hMemImage, FILE_MAP_ALL_ACCESS, 0, 0, _6502_MEM_LEN, baseAddr + count * _6502_MEM_LEN))
break;
count++;
}
res = (count == num64KPages);
if (res)
break;
// Failed this time, so clean-up and retry...
FreeMemImage();
}
while (retry--);
#if 1
if (res) // test
{
baseAddr[0x0000] = 0x11;
baseAddr[0xffff] = 0x22;
USHORT value = *((USHORT*)(baseAddr + 0xffff));
_ASSERT(value == 0x1122);
}
#endif
}
else
{
LogFileOutput("MemInitialize: SYSETEM_INFO.wAllocationGranularity = 0x%08X.\n", info.dwAllocationGranularity);
}
if (!res)
{
LogFileOutput("MemInitialize: Failed to map 2 adjacent virtual 64K pages (reverting to old method).\n");
baseAddr = ALIGNED_ALLOC(_6502_MEM_LEN);
}
return baseAddr;
#else
g_hMemTempFile = tmpfile();
if (g_hMemTempFile)
{
const int fd = fileno(g_hMemTempFile);
if (!ftruncate(fd, _6502_MEM_LEN))
{
LPBYTE baseAddr = static_cast<LPBYTE>(mmap(NULL, num64KPages * _6502_MEM_LEN, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
if (baseAddr)
{
bool ok = true;
for (UINT i = 0; i < num64KPages; i++)
{
void * target = baseAddr + i * _6502_MEM_LEN;
void * addr = mmap(target, _6502_MEM_LEN, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
ok = ok && (target == addr);
}
// we could fclose the file here
// but we keep it as a reminder of how to free the memory later
if (ok)
{
return baseAddr;
}
// revert to ALIGNED_ALLOC
munmap(baseAddr, num64KPages * _6502_MEM_LEN);
}
}
fclose(g_hMemTempFile);
g_hMemTempFile = NULL;
}
LogFileOutput("MemInitialize: Failed to map 2 adjacent virtual 64K pages (reverting to old method).\n");
return ALIGNED_ALLOC(_6502_MEM_LEN);
#endif
}
//===========================================================================
void MemInitialize()
{
// ALLOCATE MEMORY FOR THE APPLE MEMORY IMAGE AND ASSOCIATED DATA STRUCTURES
memaux = ALIGNED_ALLOC(_6502_MEM_LEN); // NB. alloc even if model is Apple II/II+, since it's used by VidHD card
memmain = ALIGNED_ALLOC(_6502_MEM_LEN);
memimage = ALIGNED_ALLOC(_6502_MEM_LEN);
memimage = AllocMemImage();
memdirty = new BYTE[0x100];
memrom = new BYTE[0x3000 * MaxRomPages];

View File

@ -127,6 +127,7 @@ BYTE SSI263::Read(ULONG nExecutedCycles)
{
// Regardless of register, just return inverted A/!R in bit7
// . inverted "A/!R" is high for REQ (ie. Request, as phoneme nearly complete)
// NB. this doesn't clear the IRQ
return MemReadFloatingBus(m_currentMode & 1, nExecutedCycles);
}
@ -140,6 +141,25 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
ssiRegs[nReg] = nValue;
#endif
// Notes:
// . Phasor's text-to-speech playback has no CTL H->L
// - ISR just writes CTL=0 (and new ART+AMP values), and writes DUR=x (and new PHON)
// - since no CTL H->L, then DUR value doesn't take affect (so continue using previous)
// - so the write to DURPHON must clear the IRQ
// . Does a write of CTL=0 clear IRQ? (ie. CTL 0->0)
// . Does a write of CTL=1 clear IRQ? (ie. CTL 0->1)
// - SSI263 datasheet says: "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..."
// . Does phoneme output only happen when CTL=0? (Otherwise device is in PD mode)
// SSI263 datasheet is not clear, but a write to DURPHON must clear the IRQ.
// . Empirically writes to regs 0,1 & 2 all clear the IRQ (and writes to 3,4..7 don't) (GH#1197)
// NB. For Mockingboard, A/!R is ack'ed by 6522's PCR handshake and D7 is cleared.
if (m_cardMode == PH_Phasor && nReg <= SSI_RATEINF)
{
CpuIrqDeassert(IS_SPEECH);
m_currentMode &= ~1; // Clear SSI263's D7 pin
}
switch(nReg)
{
case SSI_DURPHON:
@ -151,25 +171,6 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
SSI_Output();
#endif
// Notes:
// . Phasor's text-to-speech playback has no CTL H->L
// - ISR just writes CTL=0 (and new ART+AMP values), and writes DUR=x (and new PHON)
// - since no CTL H->L, then DUR value doesn't take affect (so continue using previous)
// - so the write to DURPHON must clear the IRQ
// . Does a write of CTL=0 clear IRQ? (ie. CTL 0->0)
// . Does a write of CTL=1 clear IRQ? (ie. CTL 0->1)
// - SSI263 datasheet says: "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..."
// . Does phoneme output only happen when CTL=0? (Otherwise device is in PD mode)
// SSI263 datasheet is not clear, but a write to DURPHON must clear the IRQ.
// NB. For Mockingboard, A/!R is ack'ed by 6522's PCR handshake.
if (m_cardMode == PH_Phasor)
{
CpuIrqDeassert(IS_SPEECH);
}
m_currentMode &= ~1; // Clear SSI263's D7 pin
m_durationPhoneme = nValue;
Play(nValue & PHONEME_MASK);

View File

@ -491,7 +491,7 @@ void GetAppleWindowTitle()
if (g_hCustomRomF8 != INVALID_HANDLE_VALUE)
g_pAppTitle += TEXT(" (custom rom)");
else if (GetPropertySheet().GetTheFreezesF8Rom() && IS_APPLE2)
else if (GetPropertySheet().GetTheFreezesF8Rom() && IsApple2PlusOrClone(GetApple2Type()))
g_pAppTitle += TEXT(" (The Freeze's non-autostart F8 rom)");
switch (g_nAppMode)
@ -546,7 +546,7 @@ void ResetMachineState()
// todo: consolidate CtrlReset() and ResetMachineState()
void CtrlReset()
{
if (!IS_APPLE2)
if (IsAppleIIeOrAbove(GetApple2Type()))
{
// For A][ & A][+, reset doesn't reset the LC switches (UTAII:5-29)
// TODO: What about Saturn cards? Presumably the same as the A][ & A][+ slot0 LC?