Support IIe aux slot: empty or with 80-col(1KiB) card (#1341, PR #1351)

Add new command line switch: -aux <empty|std80|ext80|rw3>
Add 6502/65C02 x normal/debugger alt read support for CPU emulation (#1353).
Fix bug in MemReadFloatingBus() reading from mem[] - no good, if MF_AUXREAD is set.
Support odd 80-col text video mode when aux slot is empty:
. add a new videoMode flag for VF_80COL_AUX_EMPTY.
Correctly support 80COL & DHIRES soft-switches when aux slot is empty or with std80 card.
Support VidHD's SHR with -aux <empty|std80>.
Save-state: support aux slot empty or with std80 card.
This commit is contained in:
TomCh
2024-12-30 21:39:16 +00:00
committed by GitHub
parent a2b03483ee
commit 16b2cf329e
17 changed files with 591 additions and 135 deletions
+73 -1
View File
@@ -462,6 +462,7 @@ static __forceinline bool IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
//===========================================================================
// 6502 & no debugger
#define READ _READ_WITH_IO_F8xx
#define WRITE(value) _WRITE_WITH_IO_F8xx(value)
#define HEATMAP_X(address)
@@ -473,20 +474,47 @@ static __forceinline bool IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
//-------
// 6502 & no debugger & alt read/write support
#define READ _READ_ALT
#define WRITE(value) _WRITE_ALT(value)
#define Cpu6502 Cpu6502_altRW
#include "CPU/cpu6502.h" // MOS 6502
#undef Cpu6502
#undef READ
#undef WRITE
//-------
// 65C02 & no debugger
#define READ _READ
#define WRITE(value) _WRITE(value)
#include "CPU/cpu65C02.h" // WDC 65C02
#undef READ
#undef WRITE
//-------
// 65C02 & no debugger & alt read/write support
#define READ _READ_ALT
#define WRITE(value) _WRITE_ALT(value)
#define Cpu65C02 Cpu65C02_altRW
#include "CPU/cpu65C02.h" // WDC 65C02
#undef Cpu65C02
#undef READ
#undef WRITE
#undef HEATMAP_X
//-----------------
// 6502 & debugger
#define READ Heatmap_ReadByte_With_IO_F8xx(addr, uExecutedCycles)
#define WRITE(value) Heatmap_WriteByte_With_IO_F8xx(addr, value, uExecutedCycles);
#define HEATMAP_X(address) Heatmap_X(address)
#include "CPU/cpu_heatmap.inl"
@@ -500,6 +528,20 @@ static __forceinline bool IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
//-------
// 6502 & debugger & alt read/write support
#define READ _READ_ALT
#define WRITE(value) _WRITE_ALT(value)
#define Cpu6502 Cpu6502_debug_altRW
#include "CPU/cpu6502.h" // MOS 6502
#undef Cpu6502
#undef READ
#undef WRITE
//-------
// 65C02 & debugger
#define READ Heatmap_ReadByte(addr, uExecutedCycles)
#define WRITE(value) Heatmap_WriteByte(addr, value, uExecutedCycles);
@@ -507,6 +549,19 @@ static __forceinline bool IRQ(ULONG& uExecutedCycles, BOOL& flagc, BOOL& flagn,
#include "CPU/cpu65C02.h" // WDC 65C02
#undef Cpu65C02
#undef READ
#undef WRITE
//-------
// 65C02 & debugger & alt read/write support
#define READ _READ_ALT
#define WRITE(value) _WRITE_ALT(value)
#define Cpu65C02 Cpu65C02_debug_altRW
#include "CPU/cpu65C02.h" // WDC 65C02
#undef Cpu65C02
#undef READ
#undef WRITE
#undef HEATMAP_X
@@ -517,6 +572,14 @@ static uint32_t InternalCpuExecute(const uint32_t uTotalCycles, const bool bVide
{
if (g_nAppMode == MODE_RUNNING || g_nAppMode == MODE_BENCHMARK)
{
if (IsAppleIIe(GetApple2Type()) && (GetCardMgr().QueryAux() == CT_Empty || GetCardMgr().QueryAux() == CT_80Col))
{
if (GetMainCpu() == CPU_6502)
return Cpu6502_altRW(uTotalCycles, bVideoUpdate); // Apple //e
else
return Cpu65C02_altRW(uTotalCycles, bVideoUpdate); // Enhanced Apple //e
}
if (GetMainCpu() == CPU_6502)
return Cpu6502(uTotalCycles, bVideoUpdate); // Apple ][, ][+, //e, Clones
else
@@ -525,6 +588,15 @@ static uint32_t InternalCpuExecute(const uint32_t uTotalCycles, const bool bVide
else
{
_ASSERT(g_nAppMode == MODE_STEPPING || g_nAppMode == MODE_DEBUG);
if (IsAppleIIe(GetApple2Type()) && (GetCardMgr().QueryAux() == CT_Empty || GetCardMgr().QueryAux() == CT_80Col))
{
if (GetMainCpu() == CPU_6502)
return Cpu6502_debug_altRW(uTotalCycles, bVideoUpdate); // Apple //e
else
return Cpu65C02_debug_altRW(uTotalCycles, bVideoUpdate); // Enhanced Apple //e
}
if (GetMainCpu() == CPU_6502)
return Cpu6502_debug(uTotalCycles, bVideoUpdate); // Apple ][, ][+, //e, Clones
else
+22
View File
@@ -59,6 +59,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
? IORead[(addr>>4) & 0xFF](regs.pc,addr,0,0,uExecutedCycles) \
: *(mem+addr) \
)
#define _READ_ALT ( \
(memreadPageType[addr >> 8] == MEM_Normal) \
? *(mem+addr) \
: (memreadPageType[addr >> 8] == MEM_Aux1K) \
? *(mem+TEXT_PAGE1_BEGIN+(addr&(TEXT_PAGE1_SIZE-1))) \
: (memreadPageType[addr >> 8] == MEM_IORead) \
? IORead[(addr >> 4) & 0xFF](regs.pc, addr, 0, 0, uExecutedCycles) \
: MemReadFloatingBus(uExecutedCycles) \
)
#define _READ_WITH_IO_F8xx ( /* GH#827 */\
((addr & 0xF000) == 0xC000) \
? IORead[(addr>>4) & 0xFF](regs.pc,addr,0,0,uExecutedCycles) \
@@ -81,6 +90,19 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
IOWrite[(addr>>4) & 0xFF](regs.pc,addr,1,(BYTE)(a),uExecutedCycles);\
} \
}
#define _WRITE_ALT(a) { \
{ \
memdirty[memwriteDirtyPage[addr >> 8]] = 0xFF; \
LPBYTE page = memwrite[addr >> 8]; \
if (page) { \
*(page+(addr & 0xFF)) = (BYTE)(a); \
if (memVidHD) /* GH#997 */\
*(memVidHD + addr) = (BYTE)(a); \
} \
else if ((addr & 0xF000) == 0xC000) \
IOWrite[(addr>>4) & 0xFF](regs.pc,addr,1,(BYTE)(a),uExecutedCycles);\
} \
}
#define _WRITE_WITH_IO_F8xx(a) { /* GH#827 */\
if (addr >= 0xF800) \
IO_F8xx(regs.pc,addr,1,(BYTE)(a),uExecutedCycles); \
+47 -14
View File
@@ -178,76 +178,84 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (strcmp(lpCmdLine, "empty") == 0)
{
g_cmdLine.bSlotEmpty[slot] = true;
if (strcmp(lpCmdLine, "diskii") == 0)
}
else if (strcmp(lpCmdLine, "diskii") == 0)
{
g_cmdLine.slotInsert[slot] = CT_Disk2;
if (strcmp(lpCmdLine, "diskii13") == 0)
}
else if (strcmp(lpCmdLine, "diskii13") == 0)
{
g_cmdLine.slotInsert[slot] = CT_Disk2;
g_cmdLine.slotInfo[slot].isDiskII13 = true;
}
if (strcmp(lpCmdLine, "hdc") == 0)
else if (strcmp(lpCmdLine, "hdc") == 0)
{
g_cmdLine.slotInsert[slot] = CT_GenericHDD;
}
if (strcmp(lpCmdLine, "hdc-sp") == 0)
else if (strcmp(lpCmdLine, "hdc-sp") == 0)
{
g_cmdLine.slotInsert[slot] = CT_GenericHDD;
g_cmdLine.slotInfo[slot].useHdcFirmwareMode = HdcSmartPort;
}
if (strcmp(lpCmdLine, "hdc-bm2") == 0)
else if (strcmp(lpCmdLine, "hdc-bm2") == 0)
{
g_cmdLine.slotInsert[slot] = CT_GenericHDD;
g_cmdLine.slotInfo[slot].useHdcFirmwareMode = HdcBlockMode2Devices;
}
if (strcmp(lpCmdLine, "hdc-bm4") == 0)
else if (strcmp(lpCmdLine, "hdc-bm4") == 0)
{
g_cmdLine.slotInsert[slot] = CT_GenericHDD;
g_cmdLine.slotInfo[slot].useHdcFirmwareMode = HdcBlockMode4Devices;
}
if (strcmp(lpCmdLine, "saturn") == 0 || strcmp(lpCmdLine, "saturn128") == 0) // Support Saturn128 card in slot 1-7 too (GH#1279)
else if (strcmp(lpCmdLine, "saturn") == 0 || strcmp(lpCmdLine, "saturn128") == 0) // Support Saturn128 card in slot 1-7 too (GH#1279)
{
g_cmdLine.slotInsert[slot] = CT_Saturn128K;
}
if (strcmp(lpCmdLine, "megaaudio") == 0)
else if (strcmp(lpCmdLine, "megaaudio") == 0)
{
g_cmdLine.slotInsert[slot] = CT_MegaAudio;
g_cmdLine.supportExtraMBCardTypes = true;
}
if (strcmp(lpCmdLine, "sdmusic") == 0)
else if (strcmp(lpCmdLine, "sdmusic") == 0)
{
g_cmdLine.slotInsert[slot] = CT_SDMusic;
g_cmdLine.supportExtraMBCardTypes = true;
}
if (strcmp(lpCmdLine, "6522a-bad") == 0)
else if (strcmp(lpCmdLine, "6522a-bad") == 0)
{
g_cmdLine.slotInfo[slot].useBad6522A = true;
}
if (strcmp(lpCmdLine, "6522b-bad") == 0)
else if (strcmp(lpCmdLine, "6522b-bad") == 0)
{
g_cmdLine.slotInfo[slot].useBad6522B = true;
}
if (strcmp(lpCmdLine, "parallel") == 0)
else if (strcmp(lpCmdLine, "parallel") == 0)
{
if (slot == SLOT1)
g_cmdLine.slotInsert[slot] = CT_GenericPrinter;
else
LogFileOutput("Parallel Printer card currently only supported in slot 1\n");
}
if (strcmp(lpCmdLine, "ssc") == 0)
else if (strcmp(lpCmdLine, "ssc") == 0)
{
if (slot == SLOT2)
g_cmdLine.slotInsert[slot] = CT_SSC;
else
LogFileOutput("SSC currently only supported in slot 2\n");
}
if (strcmp(lpCmdLine, "vidhd") == 0)
else if (strcmp(lpCmdLine, "vidhd") == 0)
{
if (slot == SLOT3)
g_cmdLine.slotInsert[slot] = CT_VidHD;
else
LogFileOutput("VidHD currently only supported in slot 3\n");
}
else
{
LogFileOutput("Unsupported slot-%d card: %s\n", slot, lpCmdLine);
}
}
else if (lpCmdLine[3] == 'd' && (lpCmdLine[4] == '1' || lpCmdLine[4] == '2')) // -s[1..7]d[1|2] <dsk-image>
{
@@ -289,6 +297,31 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
LogFileOutput("Unsupported arg: %s\n", lpCmdLine);
}
}
else if (strcmp(lpCmdLine, "-aux") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (strcmp(lpCmdLine, "empty") == 0)
{
g_cmdLine.auxSlotEmpty = true;
}
else if (strcmp(lpCmdLine, "std80") == 0)
{
g_cmdLine.auxSlotInsert = CT_80Col;
}
else if (strcmp(lpCmdLine, "ext80") == 0)
{
g_cmdLine.auxSlotInsert = CT_Extended80Col;
}
else if (strcmp(lpCmdLine, "rw3") == 0)
{
g_cmdLine.auxSlotInsert = CT_RamWorksIII;
}
else
{
LogFileOutput("Unsupported aux slot card: %s\n", lpCmdLine);
}
}
else if (strcmp(lpCmdLine, "-harddisknumblocks") == 0) // number of blocks to report for ProDOS
{
lpCmdLine = GetCurrArg(lpNextArg);
+4
View File
@@ -60,6 +60,8 @@ struct CmdLine
bestFullScreenResolution = false;
userSpecifiedWidth = 0;
userSpecifiedHeight = 0;
auxSlotEmpty = false;
auxSlotInsert = CT_Empty;
for (UINT i = 0; i < NUM_SLOTS; i++)
{
@@ -115,6 +117,8 @@ struct CmdLine
UINT userSpecifiedHeight;
std::string wavFileSpeaker;
std::string wavFileMockingboard;
bool auxSlotEmpty;
SS_CARDTYPE auxSlotInsert;
};
bool ProcessCmdLine(LPSTR lpCmdLine);
+5
View File
@@ -219,6 +219,11 @@ inline bool IsApple2PlusOrClone(eApple2Type type) // Apple ][,][+,][J-Plus or cl
return (type & (APPLE2E_MASK|APPLE2C_MASK)) == 0;
}
inline bool IsAppleIIe(eApple2Type type) // Apple //e,Enhanced//e or clone //e,Enhanced//e
{
return type & APPLE2E_MASK;
}
inline bool IsAppleIIeOrAbove(eApple2Type type) // Apple //e,Enhanced//e,//c or clone //e,Enhanced//e
{
return !IsApple2PlusOrClone(type);
+5 -3
View File
@@ -6811,13 +6811,13 @@ enum ViewVideoPage_t
VIEW_PAGE_5 // Pseudo
};
Update_t _ViewOutput ( ViewVideoPage_t iPage, int bVideoModeFlags )
static Update_t _ViewOutput ( ViewVideoPage_t iPage, UINT bVideoModeFlags )
{
switch ( iPage )
{
case VIEW_PAGE_X:
bVideoModeFlags |= !GetVideo().VideoGetSWPAGE2() ? 0 : VF_PAGE2;
bVideoModeFlags |= !GetVideo().VideoGetSWMIXED() ? 0 : VF_MIXED;
bVideoModeFlags |= GetVideo().VideoGetSWPAGE2() ? VF_PAGE2 : 0;
bVideoModeFlags |= GetVideo().VideoGetSWMIXED() ? VF_MIXED : 0;
break; // Page Current & current MIXED state
case VIEW_PAGE_0: bVideoModeFlags |= VF_PAGE0; break; // Pseudo Page 0 ($0000)
case VIEW_PAGE_1: bVideoModeFlags |= 0 ; break; // Hardware Page 1 ($2000), NOTE: VF_HIRES will be passed in
@@ -6830,6 +6830,8 @@ Update_t _ViewOutput ( ViewVideoPage_t iPage, int bVideoModeFlags )
break;
}
bVideoModeFlags |= GetVideo().VideoGet80COLAUXEMPTY() ? VF_80COL_AUX_EMPTY : 0; // Preserve this flag
DebugVideoMode::Instance().Set(bVideoModeFlags);
GetFrame().VideoRefreshScreen( bVideoModeFlags, true );
return UPDATE_NOTHING; // intentional
+322 -69
View File
@@ -200,9 +200,32 @@ SOFT SWITCH STATUS FLAGS
// . memshadow[0] = &memaux[0x0000]
// . memshadow[1] = &memaux[0x0100]
//
// memreadPageType (used by _READ_ALT for CPU emulation)
// - 1 byte entry per 256-byte page
// - Required specifically for when:
// . the aux slot is empty, so that it can return floating-bus
// . the aux slot has an 80-col(1KiB) card, so reads are restricted to this mem space
//
// memwriteDirtyPage (used by _WRITE_ALT for CPU emulation)
// - 1 byte entry per 256-byte page
// - Required specifically for the 80-Col(1KiB) card so that writes *outside* the 1KiB area only set dirty pages *inside* the 1KiB area!
//
// Apple //e 64K (aux slot is empty or has 80-Col(1KiB) card) - GH#1341
// . MMU still supports RAMRDOFF/RAMRDON/RAMWRTOFF/RAMWRTON/ALTZPOFF/ALTZPON
// . DHIRESON: no affect, as the Ext 80-col card enables this (so no DHGR or DGR - although a full VidHD would support this)
// . NB. With a VidHD card, then SHR video works correctly to HDMI-out
// . Apple //e 64K (aux slot is empty)
// . aux writes still get written to memaux (write-only memory! Used by VidHD card)
// . aux reads are from floating bus
// . Apple //e 64K (w/ 80-Col(1KiB) card in aux slot)
// . aux reads & writes are to the 1KiB of aux mem
// . aux writes outside of the aux TEXT1 get written to memaux (if there's a VidHD card)
//
static LPBYTE memshadow[0x100];
LPBYTE memwrite[0x100];
static LPBYTE memshadow[0x100];
LPBYTE memwrite[0x100];
BYTE memreadPageType[0x100];
BYTE memwriteDirtyPage[0x100];
iofunction IORead[256];
iofunction IOWrite[256];
@@ -320,7 +343,7 @@ void SetExpansionMemType(const SS_CARDTYPE type)
else
newSlot0Card = CT_Empty; // NB. No slot0 for //e
}
else if (type == CT_RamWorksIII)
else if (type == CT_80Col || type == CT_Extended80Col || type == CT_RamWorksIII)
{
g_MemTypeAppleIIe = type;
if (IsApple2PlusOrClone(GetApple2Type()))
@@ -328,6 +351,20 @@ void SetExpansionMemType(const SS_CARDTYPE type)
else
newSlotAuxCard = type;
}
else if (type == CT_Empty) // NB. This sets global state depending on machine type
{
if (IsApple2PlusOrClone(GetApple2Type()))
{
g_MemTypeAppleII = CT_Empty;
g_MemTypeAppleIIPlus = CT_Empty;
newSlot0Card = CT_Empty;
}
else
{
g_MemTypeAppleIIe = CT_Empty;
newSlotAuxCard = CT_Empty;
}
}
GetCardMgr().Insert(SLOT0, newSlot0Card);
GetCardMgr().InsertAux(newSlotAuxCard);
@@ -1155,6 +1192,8 @@ static void ResetPaging(BOOL initialize)
//===========================================================================
static void UpdatePagingForAltRW(void);
void MemUpdatePaging(BOOL initialize)
{
UpdatePaging(initialize);
@@ -1181,7 +1220,12 @@ static void UpdatePaging(BOOL initialize)
}
for (loop = 0x00; loop < 0x02; loop++)
memshadow[loop] = SW_ALTZP ? memaux+(loop << 8) : memmain+(loop << 8);
{
memshadow[loop] = SW_ALTZP ? memaux + (loop << 8) : memmain + (loop << 8);
// re-init this, since for //e aux slot 80-col(1KiB) card, this can change
memwrite[loop] = mem + (loop << 8);
}
for (loop = 0x02; loop < 0xC0; loop++)
{
@@ -1289,6 +1333,106 @@ static void UpdatePaging(BOOL initialize)
memcpy(mem+(loop << 8),memshadow[loop],256);
}
}
UpdatePagingForAltRW();
}
// For Cpu6502_altRW() & Cpu65C02_altRW()
static void UpdatePagingForAltRW(void)
{
UINT loop;
const BYTE memType = (GetCardMgr().QueryAux() == CT_Empty) ? MEM_FloatingBus
: (GetCardMgr().QueryAux() == CT_80Col) ? MEM_Aux1K
: MEM_Normal;
for (loop = 0x00; loop < 0x02; loop++)
memreadPageType[loop] = SW_ALTZP ? memType : MEM_Normal;
for (loop = 0x02; loop < 0xC0; loop++)
memreadPageType[loop] = SW_AUXREAD ? memType : MEM_Normal;
for (loop = 0xC0; loop < 0xD0; loop++)
memreadPageType[loop] = MEM_IORead;
for (loop = 0xD0; loop < 0x100; loop++)
memreadPageType[loop] = (SW_HIGHRAM && SW_ALTZP) ? memType : MEM_Normal;
if (SW_80STORE)
{
for (loop = 0x04; loop < 0x08; loop++)
memreadPageType[loop] = SW_PAGE2 ? memType : MEM_Normal;
for (loop = 0x20; loop < 0x40; loop++)
memreadPageType[loop] = (SW_PAGE2 && SW_HIRES) ? memType : MEM_Normal;
}
if (GetCardMgr().QueryAux() == CT_80Col)
{
// Overide the MEM_Aux1K set above (slightly quicker code-path during CPU emulation)
if (SW_AUXREAD || (SW_80STORE && SW_PAGE2))
for (loop = 0x04; loop < 0x08; loop++)
memreadPageType[loop] = MEM_Normal;
}
//
for (loop = 0x00; loop<0x100; loop++)
memwriteDirtyPage[loop] = loop;
if (GetCardMgr().QueryAux() == CT_80Col)
{
// Dirty pages are only in the 1K range
const BYTE kTextPage = TEXT_PAGE1_BEGIN >> 8;
for (loop = 0x00; loop < 0x02; loop++)
if (SW_ALTZP)
memwriteDirtyPage[loop] = kTextPage + (loop & 3);
for (loop = 0x02; loop < 0xC0; loop++)
if (SW_AUXWRITE)
memwriteDirtyPage[loop] = kTextPage + (loop & 3);
for (loop = 0xD0; loop < 0x100; loop++)
if (SW_HIGHRAM && SW_ALTZP)
memwriteDirtyPage[loop] = kTextPage + (loop & 3);
if (SW_80STORE && SW_PAGE2)
{
for (loop = 0x04; loop < 0x08; loop++)
memwriteDirtyPage[loop] = kTextPage + (loop & 3);
for (loop = 0x20; loop < 0x40; loop++)
memwriteDirtyPage[loop] = kTextPage + (loop & 3);
}
// Map all aux writes into the 1K memory
// . Need to combine with memwriteDirtyPage[], to that the right page is marked as dirty
for (loop = 0x00; loop < 0x02; loop++)
if (SW_ALTZP)
memwrite[loop] = memaux + TEXT_PAGE1_BEGIN + ((loop & 3) << 8);
for (loop = 0x02; loop < 0xC0; loop++)
if (SW_AUXWRITE)
memwrite[loop] = (memwrite[loop] - (loop << 8)) + TEXT_PAGE1_BEGIN + ((loop & 3) << 8);
for (loop = 0xD0; loop < 0x100; loop++)
if (SW_HIGHRAM && SW_ALTZP)
memwrite[loop] = memaux + TEXT_PAGE1_BEGIN + ((loop & 3) << 8);
if (SW_80STORE && SW_PAGE2)
{
for (loop = 0x04; loop < 0x08; loop++)
memwrite[loop] = mem + TEXT_PAGE1_BEGIN + ((loop & 3) << 8);
if (SW_HIRES)
{
for (loop = 0x20; loop < 0x40; loop++)
memwrite[loop] = mem + TEXT_PAGE1_BEGIN + ((loop & 3) << 8);
}
}
}
}
//
@@ -1364,13 +1508,20 @@ static LPBYTE MemGetPtrBANK1(const WORD offset, const LPBYTE pMemBase)
//-------------------------------------
LPBYTE MemGetAuxPtr(const WORD offset)
#if 0 // Unused
LPBYTE MemGetAuxPtrWithLC(const WORD offset)
{
LPBYTE lpMem = MemGetPtrBANK1(offset, memaux);
if (lpMem)
return lpMem;
lpMem = (memshadow[(offset >> 8)] == (memaux+(offset & 0xFF00)))
return MemGetAuxPtr(offset);
}
#endif
LPBYTE MemGetAuxPtr(const WORD offset)
{
LPBYTE lpMem = (memshadow[(offset >> 8)] == (memaux+(offset & 0xFF00)))
? mem+offset // Return 'mem' copy if possible, as page could be dirty
: memaux+offset;
@@ -1422,15 +1573,20 @@ LPBYTE MemGetAuxPtr(const WORD offset)
// . if no, then return memmain, as the mem(cache) isn't involved in memmain (any writes will go directly to this backing-store).
//
LPBYTE MemGetMainPtr(const WORD offset)
LPBYTE MemGetMainPtrWithLC(const WORD offset)
{
LPBYTE lpMem = MemGetPtrBANK1(offset, memmain);
if (lpMem)
return lpMem;
return (memshadow[(offset >> 8)] == (memmain+(offset & 0xFF00)))
? mem+offset // Return 'mem' copy if possible, as page could be dirty
: memmain+offset;
return MemGetMainPtr(offset);
}
LPBYTE MemGetMainPtr(const WORD offset)
{
return (memshadow[(offset >> 8)] == (memmain + (offset & 0xFF00)))
? mem + offset // Return 'mem' copy if possible, as page could be dirty
: memmain + offset;
}
//===========================================================================
@@ -1488,7 +1644,7 @@ LPBYTE MemGetCxRomPeripheral()
// . false: I/O memory or floating bus
bool MemIsAddrCodeMemory(const USHORT addr)
{
if (addr < 0xC000 || addr > FIRMWARE_EXPANSION_END) // Assume all A][ types have at least 48K
if (addr < APPLE_IO_BEGIN || addr > FIRMWARE_EXPANSION_END) // Assume all A][ types have at least 48K
return true;
if (addr < APPLE_SLOT_BEGIN) // [$C000..C0FF]
@@ -1665,6 +1821,7 @@ static LPBYTE AllocMemImage(void)
void MemInitialize()
{
// ALLOCATE MEMORY FOR THE APPLE MEMORY IMAGE AND ASSOCIATED DATA STRUCTURES
// NB. alloc memaux even if a IIe with an empty aux slot - writes still go to memaux, but reads are from floating bus
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 = AllocMemImage();
@@ -1940,10 +2097,13 @@ void MemInitializeFromSnapshot(void)
memVidHD = NULL;
if (IsApple2PlusOrClone(GetApple2Type()) && (GetCardMgr().QuerySlot(SLOT3) == CT_VidHD))
if ((GetCardMgr().QuerySlot(SLOT3) == CT_VidHD))
{
VidHDCard& vidHD = dynamic_cast<VidHDCard&>(GetCardMgr().GetRef(SLOT3));
memVidHD = vidHD.IsWriteAux() ? memaux : NULL;
if (IsApple2PlusOrClone(GetApple2Type()) || IsIIeWithoutAuxMem())
{
VidHDCard& vidHD = dynamic_cast<VidHDCard&>(GetCardMgr().GetRef(SLOT3));
memVidHD = vidHD.IsWriteAux() ? memaux : NULL;
}
}
}
@@ -2120,19 +2280,39 @@ void MemReset()
//===========================================================================
BYTE MemReadFloatingBus(const ULONG uExecutedCycles)
static BYTE ReadFloatingBus(const ULONG uExecutedCycles, const bool fullSpeed, const bool auxEmpty = false)
{
return mem[ NTSC_VideoGetScannerAddress(uExecutedCycles) ]; // OK: This does the 2-cycle adjust for ANSI STORY (End Credits)
BYTE* pMain = MemGetMainPtr(0x0000);
if (auxEmpty && (SW_AUXREAD || (SW_80STORE && SW_PAGE2)))
{
// Special case: Aux slot empty and in 80-col mode: video generator reading floating bus. (GH#1341)
// Can't rely on using "mem" (ie. the CPU read cache), since "80STORE && PAGE2" will have switched in the non-existent memory from "memaux"!
// NB. Only care about $400-7FF (ie. TEXT page 1)
pMain = memmain;
}
return pMain[NTSC_VideoGetScannerAddress(uExecutedCycles, fullSpeed)]; // OK: This does the 2-cycle adjust for ANSI STORY (End Credits)
}
//===========================================================================
BYTE MemReadFloatingBus(const ULONG uExecutedCycles)
{
return ReadFloatingBus(uExecutedCycles, g_bFullSpeed);
}
BYTE MemReadFloatingBus(const BYTE highbit, const ULONG uExecutedCycles)
{
BYTE r = MemReadFloatingBus(uExecutedCycles);
BYTE r = ReadFloatingBus(uExecutedCycles, g_bFullSpeed);
return (r & ~0x80) | (highbit ? 0x80 : 0);
}
BYTE MemReadFloatingBusFromNTSC(void)
{
// fullspeed=false: to avoid NTSC_VideoGetScannerAddress() calling NTSC_VideoClockResync()
// NB. g_bFullSpeed only true when doing NTSC_VideoRedrawWholeScreen()
return ReadFloatingBus(0, false, true);
}
//===========================================================================
//#define DEBUG_FLIP_TIMINGS
@@ -2206,6 +2386,12 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE
break;
#endif
}
if (GetCardMgr().QuerySlot(SLOT3) == CT_VidHD && GetCardMgr().QueryAux() == CT_80Col)
{
// NB. if aux slot is empty, then writes already occur to memaux
memVidHD = MemIsWriteAux(g_memmode) ? memaux : NULL;
}
}
else // Apple ][,][+,][J-Plus or clone ][,][+
{
@@ -2274,6 +2460,25 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE
//===========================================================================
// NB. Not particularly accurate (but good enough for now)
// . 80STORE && PAGE2 just means that writes occur to aux $400-7FF (and $2000-$3FFF if HIRES=1), not the entire aux 64K
bool MemIsWriteAux(uint32_t memMode)
{
return (memMode & MF_AUXWRITE) || // Write to aux: $200-$BFFF
((memMode & MF_80STORE) && (memMode & MF_PAGE2)); // Write to aux: $400-$7FF and $2000-$3FFF
}
//===========================================================================
bool IsIIeWithoutAuxMem(void)
{
return IsAppleIIe(GetApple2Type()) &&
(GetCardMgr().QueryAux() == CT_Empty || GetCardMgr().QueryAux() == CT_80Col);
}
//===========================================================================
bool MemOptimizeForModeChanging(WORD programcounter, WORD address)
{
if (IsAppleIIeOrAbove(GetApple2Type()))
@@ -2380,8 +2585,10 @@ static const UINT kUNIT_AUXSLOT_VER = 2;
// Unit version history:
// 2: Added: RGB card state
// 3: Extended: RGB card state ('80COL changed')
static const UINT kUNIT_CARD_VER = 3;
// 4: Support aux empty or aux 1KiB card
static const UINT kUNIT_CARD_VER = 4;
#define SS_YAML_VALUE_CARD_EMPTY "Empty"
#define SS_YAML_VALUE_CARD_80COL "80 Column"
#define SS_YAML_VALUE_CARD_EXTENDED80COL "Extended 80 Column"
#define SS_YAML_VALUE_CARD_RAMWORKSIII "RamWorksIII"
@@ -2559,7 +2766,6 @@ bool MemLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
return true;
}
// TODO: Switch from checking 'g_uMaxExPages == n' to using g_SlotAux
void MemSaveSnapshotAux(YamlSaveHelper& yamlSaveHelper)
{
if (IS_APPLE2)
@@ -2578,23 +2784,40 @@ void MemSaveSnapshotAux(YamlSaveHelper& yamlSaveHelper)
{
YamlSaveHelper::Label unitState(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
std::string card = g_uMaxExPages == 0 ? SS_YAML_VALUE_CARD_80COL : // todo: support empty slot
g_uMaxExPages == 1 ? SS_YAML_VALUE_CARD_EXTENDED80COL :
SS_YAML_VALUE_CARD_RAMWORKSIII;
const SS_CARDTYPE cardType = GetCardMgr().QueryAux();
std::string card = cardType == CT_Empty ? SS_YAML_VALUE_CARD_EMPTY :
cardType == CT_80Col ? SS_YAML_VALUE_CARD_80COL :
cardType == CT_Extended80Col ? SS_YAML_VALUE_CARD_EXTENDED80COL :
cardType == CT_RamWorksIII ? SS_YAML_VALUE_CARD_RAMWORKSIII :
"";
_ASSERT(!card.empty());
yamlSaveHelper.SaveString(SS_YAML_KEY_CARD, card.c_str());
yamlSaveHelper.Save("%s: %d\n", SS_YAML_KEY_VERSION, kUNIT_CARD_VER);
// Card state
if (cardType == CT_80Col)
{
YamlSaveHelper::Label cardState(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
// 1KiB memory
{
const UINT bank = 1;
LPBYTE pMemBase = MemGetBankPtr(bank);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", MemGetSnapshotAuxMemStructName().c_str());
yamlSaveHelper.SaveMemory(pMemBase + TEXT_PAGE1_BEGIN, TEXT_PAGE1_SIZE);
}
}
else if (cardType == CT_Extended80Col || cardType == CT_RamWorksIII)
{
YamlSaveHelper::Label cardState(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
yamlSaveHelper.Save("%s: 0x%02X # [0,1..7F] 0=no aux mem, 1=128K system, etc\n", SS_YAML_KEY_NUMAUXBANKS, g_uMaxExPages);
yamlSaveHelper.Save("%s: 0x%02X # [ 0..7E] 0=memaux\n", SS_YAML_KEY_ACTIVEAUXBANK, g_uActiveBank);
for(UINT uBank = 1; uBank <= g_uMaxExPages; uBank++)
for(UINT bank = 1; bank <= g_uMaxExPages; bank++)
{
MemSaveSnapshotMemory(yamlSaveHelper, false, uBank);
MemSaveSnapshotMemory(yamlSaveHelper, false, bank);
}
RGB_SaveSnapshot(yamlSaveHelper);
@@ -2602,66 +2825,92 @@ void MemSaveSnapshotAux(YamlSaveHelper& yamlSaveHelper)
}
}
static void MemLoadSnapshotAuxCommon(YamlLoadHelper& yamlLoadHelper, const std::string& card)
static SS_CARDTYPE MemLoadSnapshotAuxCommon(YamlLoadHelper& yamlLoadHelper, const std::string& card)
{
// "State"
UINT numAuxBanks = yamlLoadHelper.LoadUint(SS_YAML_KEY_NUMAUXBANKS);
UINT activeAuxBank = yamlLoadHelper.LoadUint(SS_YAML_KEY_ACTIVEAUXBANK);
g_uMaxExPages = 1; // Must be at least 1 (for aux mem) - regardless of Apple2 type!
g_uActiveBank = 0;
_ASSERT(MemGetBankPtr(1, false)); // Ensure there is always aux mem (eg. for CT_80Col or CT_VidHD)
SS_CARDTYPE type = CT_Empty;
if (card == SS_YAML_VALUE_CARD_80COL)
{
type = CT_80Col;
if (numAuxBanks != 0 || activeAuxBank != 0)
throw std::runtime_error(SS_YAML_KEY_UNIT ": AuxSlot: Bad aux slot card state");
}
SS_CARDTYPE cardType;
if (card == SS_YAML_VALUE_CARD_EMPTY)
cardType = CT_Empty;
else if (card == SS_YAML_VALUE_CARD_80COL)
cardType = CT_80Col;
else if (card == SS_YAML_VALUE_CARD_EXTENDED80COL)
{
type = CT_Extended80Col;
if (numAuxBanks != 1 || activeAuxBank != 0)
throw std::runtime_error(SS_YAML_KEY_UNIT ": AuxSlot: Bad aux slot card state");
}
cardType = CT_Extended80Col;
else if (card == SS_YAML_VALUE_CARD_RAMWORKSIII)
{
type = CT_RamWorksIII;
if (numAuxBanks < 2 || numAuxBanks > 0x7F || (activeAuxBank+1) > numAuxBanks)
throw std::runtime_error(SS_YAML_KEY_UNIT ": AuxSlot: Bad aux slot card state");
}
cardType = CT_RamWorksIII;
else
{
// todo: support empty slot
type = CT_Empty;
throw std::runtime_error(SS_YAML_KEY_UNIT ": AuxSlot: Unknown card: " + card);
// "State"
UINT numAuxBanks = 0, activeAuxBank = 0;
if (card == SS_YAML_VALUE_CARD_EXTENDED80COL || card == SS_YAML_VALUE_CARD_RAMWORKSIII)
{
numAuxBanks = yamlLoadHelper.LoadUint(SS_YAML_KEY_NUMAUXBANKS);
activeAuxBank = yamlLoadHelper.LoadUint(SS_YAML_KEY_ACTIVEAUXBANK);
}
g_uMaxExPages = numAuxBanks;
g_uActiveBank = activeAuxBank;
//
for(UINT uBank = 1; uBank <= g_uMaxExPages; uBank++)
if (cardType == CT_Empty)
{
LPBYTE pBank = MemGetBankPtr(uBank, false);
if (!pBank)
{
pBank = RWpages[uBank-1] = ALIGNED_ALLOC(_6502_MEM_LEN);
}
// "Auxiliary Memory Bankxx"
std::string auxMemName = MemGetSnapshotAuxMemStructName() + ByteToHexStr(uBank-1);
// nothing to do here
}
else if (cardType == CT_80Col)
{
const UINT bank1 = 1;
LPBYTE pBank = MemGetBankPtr(bank1, false);
_ASSERT(pBank);
std::string auxMemName = MemGetSnapshotAuxMemStructName();
if (!yamlLoadHelper.GetSubMap(auxMemName))
throw std::runtime_error("Memory: Missing map name: " + auxMemName);
yamlLoadHelper.LoadMemory(pBank, _6502_MEM_LEN);
yamlLoadHelper.LoadMemory(pBank + TEXT_PAGE1_BEGIN, TEXT_PAGE1_SIZE);
yamlLoadHelper.PopMap();
}
else
{
if (cardType == CT_Extended80Col)
{
if (numAuxBanks != 1 || activeAuxBank != 0)
throw std::runtime_error(SS_YAML_KEY_UNIT ": AuxSlot: Bad aux slot card state");
}
else // cardType == CT_RamWorksIII
{
if (numAuxBanks < 2 || numAuxBanks > 0x7F || (activeAuxBank + 1) > numAuxBanks)
throw std::runtime_error(SS_YAML_KEY_UNIT ": AuxSlot: Bad aux slot card state");
}
GetCardMgr().InsertAux(type);
g_uMaxExPages = numAuxBanks;
g_uActiveBank = activeAuxBank;
//
for (UINT bank = 1; bank <= g_uMaxExPages; bank++)
{
LPBYTE pBank = MemGetBankPtr(bank, false);
if (!pBank)
pBank = RWpages[bank - 1] = ALIGNED_ALLOC(_6502_MEM_LEN);
// "Auxiliary Memory Bankxx"
std::string auxMemName = MemGetSnapshotAuxMemStructName() + ByteToHexStr(bank - 1);
if (!yamlLoadHelper.GetSubMap(auxMemName))
throw std::runtime_error("Memory: Missing map name: " + auxMemName);
yamlLoadHelper.LoadMemory(pBank, _6502_MEM_LEN);
yamlLoadHelper.PopMap();
}
}
GetCardMgr().InsertAux(cardType);
memaux = RWpages[g_uActiveBank];
// NB. MemUpdatePaging(TRUE) called at end of Snapshot_LoadState_v2()
return cardType;
}
static void MemLoadSnapshotAuxVer1(YamlLoadHelper& yamlLoadHelper)
@@ -2675,12 +2924,16 @@ static void MemLoadSnapshotAuxVer2(YamlLoadHelper& yamlLoadHelper)
std::string card = yamlLoadHelper.LoadString(SS_YAML_KEY_CARD);
UINT cardVersion = yamlLoadHelper.LoadUint(SS_YAML_KEY_VERSION);
if (!yamlLoadHelper.GetSubMap(std::string(SS_YAML_KEY_STATE)))
throw std::runtime_error(SS_YAML_KEY_UNIT ": Expected sub-map name: " SS_YAML_KEY_STATE);
if (card != SS_YAML_VALUE_CARD_EMPTY)
{
if (!yamlLoadHelper.GetSubMap(std::string(SS_YAML_KEY_STATE)))
throw std::runtime_error(SS_YAML_KEY_UNIT ": Expected sub-map name: " SS_YAML_KEY_STATE);
}
MemLoadSnapshotAuxCommon(yamlLoadHelper, card);
SS_CARDTYPE cardType = MemLoadSnapshotAuxCommon(yamlLoadHelper, card);
RGB_LoadSnapshot(yamlLoadHelper, cardVersion);
if (card == SS_YAML_VALUE_CARD_EXTENDED80COL || card == SS_YAML_VALUE_CARD_RAMWORKSIII)
RGB_LoadSnapshot(yamlLoadHelper, cardVersion);
}
bool MemLoadSnapshotAux(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
+9 -1
View File
@@ -36,11 +36,16 @@ enum MemoryInitPattern_e
, NUM_MIP
};
// For Cpu6502_altRead() & Cpu65C02_altRead()
enum { MEM_Normal = 0, MEM_IORead, MEM_FloatingBus, MEM_Aux1K, MEM_NoSlotClock };
typedef BYTE (__stdcall *iofunction)(WORD nPC, WORD nAddr, BYTE nWriteFlag, BYTE nWriteValue, ULONG nExecutedCycles);
extern iofunction IORead[256];
extern iofunction IOWrite[256];
extern LPBYTE memwrite[0x100];
extern BYTE memreadPageType[0x100];
extern BYTE memwriteDirtyPage[0x100];
extern LPBYTE mem;
extern LPBYTE memdirty;
extern LPBYTE memVidHD;
@@ -56,11 +61,14 @@ void MemDestroy ();
bool MemCheckSLOTC3ROM();
bool MemCheckINTCXROM();
LPBYTE MemGetAuxPtr(const WORD);
LPBYTE MemGetMainPtrWithLC(const WORD);
LPBYTE MemGetMainPtr(const WORD);
LPBYTE MemGetBankPtr(const UINT nBank, const bool isSaveSnapshotOrDebugging = true);
LPBYTE MemGetCxRomPeripheral();
uint32_t GetMemMode(void);
void SetMemMode(uint32_t memmode);
bool MemIsWriteAux(uint32_t memMode);
bool IsIIeWithoutAuxMem(void);
bool MemOptimizeForModeChanging(WORD programcounter, WORD address);
bool MemIsAddrCodeMemory(const USHORT addr);
void MemInitialize ();
@@ -71,6 +79,7 @@ void MemInitializeIO(void);
void MemInitializeFromSnapshot(void);
BYTE MemReadFloatingBus(const ULONG uExecutedCycles);
BYTE MemReadFloatingBus(const BYTE highbit, const ULONG uExecutedCycles);
BYTE MemReadFloatingBusFromNTSC(void);
void MemReset ();
void MemResetPaging ();
void MemUpdatePaging(BOOL initialize);
@@ -102,4 +111,3 @@ UINT GetRamWorksActiveBank(void);
void SetMemMainLanguageCard(LPBYTE ptr, UINT slot, bool bMemMain=false);
LPBYTE GetCxRomPeripheral(void);
UINT GetLastSlotToSetMainMemLC(void);
+1
View File
@@ -4,6 +4,7 @@ enum
{
// Note: All are in bytes!
TEXT_PAGE1_BEGIN = 0x0400,
TEXT_PAGE1_SIZE = 0x0400,
APPLE_SLOT_SIZE = 0x0100, // 1 page = $Cx00 .. $CxFF (slot 1 .. 7)
APPLE_IO_BEGIN = 0xC000,
+20 -11
View File
@@ -1380,8 +1380,8 @@ void updateScreenDoubleHires80 (long cycles6502 ) // wsUpdateVideoDblHires
}
else if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START)
{
uint8_t *pMain = MemGetMainPtr(addr);
uint8_t *pAux = MemGetAuxPtr (addr);
uint8_t *pMain = MemGetMainPtr(addr);
uint8_t *pAux = MemGetAuxPtr(addr);
uint8_t m = pMain[0];
uint8_t a = pAux [0];
@@ -1481,7 +1481,7 @@ void updateScreenDoubleLores80 (long cycles6502) // wsUpdateVideoDblLores
else if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START)
{
uint8_t *pMain = MemGetMainPtr(addr);
uint8_t *pAux = MemGetAuxPtr (addr);
uint8_t *pAux = MemGetAuxPtr(addr);
uint8_t m = pMain[0];
uint8_t a = pAux [0];
@@ -1772,11 +1772,14 @@ void updateScreenText80 (long cycles6502)
if (g_nVideoClockHorz >= VIDEO_SCANNER_HORZ_START)
{
uint8_t *pMain = MemGetMainPtr(addr);
uint8_t *pAux = MemGetAuxPtr (addr);
uint8_t *pAux = MemGetAuxPtr(addr);
uint8_t m = pMain[0];
uint8_t a = pAux [0];
if (g_uNewVideoModeFlags & VF_80COL_AUX_EMPTY)
a = MemReadFloatingBusFromNTSC();
uint16_t main = getCharSetBits( m );
uint16_t aux = getCharSetBits( a );
@@ -1911,9 +1914,9 @@ void NTSC_VideoClockResync(const uint32_t dwCyclesThisFrame)
}
//===========================================================================
uint16_t NTSC_VideoGetScannerAddress ( const ULONG uExecutedCycles )
uint16_t NTSC_VideoGetScannerAddress(const ULONG uExecutedCycles, const bool fullSpeed)
{
if (g_bFullSpeed)
if (fullSpeed)
{
// Ensure that NTSC video-scanner gets updated during full-speed, so video-dependent Apple II code doesn't hang
NTSC_VideoClockResync( CpuGetCyclesThisVideoFrame(uExecutedCycles) );
@@ -1943,7 +1946,7 @@ uint16_t NTSC_VideoGetScannerAddress ( const ULONG uExecutedCycles )
void NTSC_GetVideoVertHorzForDebugger(uint16_t& vert, uint16_t& horz)
{
ResetCyclesExecutedForDebugger(); // if in full-speed, then reset cycles so that CpuCalcCycles() doesn't ASSERT
NTSC_VideoGetScannerAddress(0);
NTSC_VideoGetScannerAddress(0, g_bFullSpeed);
vert = g_nVideoClockVert;
horz = g_nVideoClockHorz;
}
@@ -1965,10 +1968,13 @@ void NTSC_SetVideoTextMode( int cols )
else
g_pFuncUpdateTextScreen = updateScreenText80RGB;
}
else if( cols == 40 )
g_pFuncUpdateTextScreen = updateScreenText40;
else
g_pFuncUpdateTextScreen = updateScreenText80;
{
if (cols == 40)
g_pFuncUpdateTextScreen = updateScreenText40;
else
g_pFuncUpdateTextScreen = updateScreenText80;
}
}
//===========================================================================
@@ -2808,7 +2814,10 @@ uint16_t NTSC_GetScannerAddressAndData(uint32_t& data, int& dataSize)
if (dataSize == 2)
{
uint8_t* pAux = MemGetAuxPtr(addr);
data = pAux[0] << 8;
uint8_t a = pAux[0];
if (g_uNewVideoModeFlags & VF_80COL_AUX_EMPTY)
a = MemReadFloatingBusFromNTSC();
data = a << 8;
}
uint8_t* pMain = MemGetMainPtr(addr);
data |= pMain[0];
+1 -1
View File
@@ -11,7 +11,7 @@ void NTSC_SetVideoStyle(void);
void NTSC_SetVideoTextMode(int cols);
uint32_t* NTSC_VideoGetChromaTable(bool bHueTypeMonochrome, bool bMonitorTypeColorTV);
void NTSC_VideoClockResync(const uint32_t dwCyclesThisFrame);
uint16_t NTSC_VideoGetScannerAddress(const ULONG uExecutedCycles);
uint16_t NTSC_VideoGetScannerAddress(const ULONG uExecutedCycles, const bool fullSpeed);
void NTSC_GetVideoVertHorzForDebugger(uint16_t& vert, uint16_t& horz);
uint16_t NTSC_GetVideoVertForDebugger(void);
void NTSC_Destroy(void);
+10 -8
View File
@@ -36,14 +36,15 @@
Implementation notes:
. II/II+
. Mirrors the 80STORE/PAGE2/AUXREAD/AUXWRITE switches to VidHD.
. Reuses 'memaux' that's for the //e models.
. Reuses 'memaux' that's for the //e models with aux card of 64KiB (or more).
. AUXWRITE=1: writes occur to both main & memaux.
. 80STORE=1 && PAGE2=1: same as AUXWRITE=1 (but should be changed to *only* allow writes to aux's TEXT1 & HGR2 areas).
. Only 6502 (not 65C02) emulation supports this dual write to main & memaux (via the 'memVidHD' pointer):
- So a II/II+ with a 65C02 won't correctly support VidHD cards.
- And a //e with a 6502 will incur a slight overhead to test 'memVidHD' pointer (which is always NULL for //e's).
. VidHD card's save-state includes VidHD's aux mem ($400-$9FFF).
. //e with 1KiB 80-Col card: AppleWin doesn't support this - so currently out of scope.
. //e with 1KiB 80-Col card or empty aux slot: supported.
. Reuses 'memaux' that's for the //e models with aux card of 64KiB (or more).
*/
#include "StdAfx.h"
@@ -100,8 +101,7 @@ void VidHDCard::VideoIOWrite(WORD pc, WORD addr, BYTE bWrite, BYTE value, ULONG
bool VidHDCard::IsWriteAux(void)
{
return (m_memMode & MF_AUXWRITE) || // Write to aux: $200-$BFFF
((m_memMode & MF_80STORE) && (m_memMode & MF_PAGE2)); // Write to aux: $400-$7FF and $2000-$3FFF
return MemIsWriteAux(m_memMode);
}
//===========================================================================
@@ -205,12 +205,13 @@ void VidHDCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_BORDER_COLOR, m_BORDERCOLOR);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SHADOW, m_SHADOW);
if (IsApple2PlusOrClone(GetApple2Type())) // Save aux mem for II/II+
if (IsApple2PlusOrClone(GetApple2Type()) || IsIIeWithoutAuxMem()) // Save aux mem for II/II+ or //e without aux mem
{
// Save [$400-$9FFF]
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", MemGetSnapshotAuxMemStructName().c_str());
LPBYTE pMemBase = MemGetBankPtr(1);
const UINT bank1 = 1;
LPBYTE pMemBase = MemGetBankPtr(bank1);
yamlSaveHelper.SaveMemory(pMemBase, (SHR_MEMORY_END + 1) - TEXT_PAGE1_BEGIN, TEXT_PAGE1_BEGIN);
}
}
@@ -226,13 +227,14 @@ bool VidHDCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
m_BORDERCOLOR = yamlLoadHelper.LoadUint(SS_YAML_KEY_BORDER_COLOR);
m_SHADOW = yamlLoadHelper.LoadUint(SS_YAML_KEY_SHADOW);
if (IsApple2PlusOrClone(GetApple2Type())) // Load aux mem for II/II+
if (IsApple2PlusOrClone(GetApple2Type()) || IsIIeWithoutAuxMem()) // Load aux mem for II/II+ or //e without aux mem
{
// Load [$400-$9FFF]
if (!yamlLoadHelper.GetSubMap(MemGetSnapshotAuxMemStructName()))
throw std::runtime_error("Memory: Missing map name: " + MemGetSnapshotAuxMemStructName());
LPBYTE pMemBase = MemGetBankPtr(1, false);
const UINT bank1 = 1;
LPBYTE pMemBase = MemGetBankPtr(bank1, false);
yamlLoadHelper.LoadMemory(pMemBase, (SHR_MEMORY_END + 1) - TEXT_PAGE1_BEGIN, TEXT_PAGE1_BEGIN);
yamlLoadHelper.PopMap();
+39 -17
View File
@@ -185,26 +185,26 @@ BYTE Video::VideoSetMode(WORD pc, WORD address, BYTE write, BYTE d, ULONG uExecu
address &= 0xFF;
switch (address)
{
case 0x00: g_uVideoMode &= ~VF_80STORE; break;
case 0x01: g_uVideoMode |= VF_80STORE; break;
case 0x0C: if (!IS_APPLE2){g_uVideoMode &= ~VF_80COL; NTSC_SetVideoTextMode(40);}; break;
case 0x0D: if (!IS_APPLE2){g_uVideoMode |= VF_80COL; NTSC_SetVideoTextMode(80);}; break;
case 0x0E: if (!IS_APPLE2) g_nAltCharSetOffset = 0; break; // Alternate char set off
case 0x0F: if (!IS_APPLE2) g_nAltCharSetOffset = 256; break; // Alternate char set on
case 0x00: g_uVideoMode &= ~VF_80STORE; break;
case 0x01: g_uVideoMode |= VF_80STORE; break;
case 0x0C: if (!IS_APPLE2) { g_uVideoMode &= ~VF_80COL; NTSC_SetVideoTextMode(40); } break;
case 0x0D: if (!IS_APPLE2) { g_uVideoMode |= VF_80COL; NTSC_SetVideoTextMode(80); } break;
case 0x0E: if (!IS_APPLE2) g_nAltCharSetOffset = 0; break; // Alternate char set off
case 0x0F: if (!IS_APPLE2) g_nAltCharSetOffset = 256; break; // Alternate char set on
case 0x22: if (vidHD) vidHD->VideoIOWrite(pc, address, write, d, uExecutedCycles); break; // VidHD IIgs video mode register
case 0x29: if (vidHD) vidHD->VideoIOWrite(pc, address, write, d, uExecutedCycles); break; // VidHD IIgs video mode register
case 0x34: if (vidHD) vidHD->VideoIOWrite(pc, address, write, d, uExecutedCycles); break; // VidHD IIgs video mode register
case 0x35: if (vidHD) vidHD->VideoIOWrite(pc, address, write, d, uExecutedCycles); break; // VidHD IIgs video mode register
case 0x50: g_uVideoMode &= ~VF_TEXT; break;
case 0x51: g_uVideoMode |= VF_TEXT; break;
case 0x52: g_uVideoMode &= ~VF_MIXED; break;
case 0x53: g_uVideoMode |= VF_MIXED; break;
case 0x54: g_uVideoMode &= ~VF_PAGE2; break;
case 0x55: g_uVideoMode |= VF_PAGE2; break;
case 0x56: g_uVideoMode &= ~VF_HIRES; break;
case 0x57: g_uVideoMode |= VF_HIRES; break;
case 0x5E: if (!IS_APPLE2) g_uVideoMode |= VF_DHIRES; break;
case 0x5F: if (!IS_APPLE2) g_uVideoMode &= ~VF_DHIRES; break;
case 0x50: g_uVideoMode &= ~VF_TEXT; break;
case 0x51: g_uVideoMode |= VF_TEXT; break;
case 0x52: g_uVideoMode &= ~VF_MIXED; break;
case 0x53: g_uVideoMode |= VF_MIXED; break;
case 0x54: g_uVideoMode &= ~VF_PAGE2; break;
case 0x55: g_uVideoMode |= VF_PAGE2; break;
case 0x56: g_uVideoMode &= ~VF_HIRES; break;
case 0x57: g_uVideoMode |= VF_HIRES; break;
case 0x5E: if (!IS_APPLE2) g_uVideoMode |= VF_DHIRES; break;
case 0x5F: if (!IS_APPLE2) g_uVideoMode &= ~VF_DHIRES; break;
}
if (vidHD && vidHD->IsSHR())
@@ -220,7 +220,24 @@ BYTE Video::VideoSetMode(WORD pc, WORD address, BYTE write, BYTE d, ULONG uExecu
if ((oldVideoMode ^ g_uVideoMode) & (VF_TEXT|VF_MIXED))
delay = true;
NTSC_SetVideoMode(g_uVideoMode, delay);
uint32_t ntscVideoMode = g_uVideoMode;
if ((!IS_APPLE2) && (GetCardMgr().QueryAux() == CT_Empty || GetCardMgr().QueryAux() == CT_80Col)) // aux empty or 80col (GH#1341)
{
g_uVideoMode &= ~VF_DHIRES;
if (GetCardMgr().QueryAux() == CT_Empty)
{
if ((g_uVideoMode & VF_80COL) == 0)
g_uVideoMode &= ~VF_80COL_AUX_EMPTY;
else
g_uVideoMode |= VF_80COL_AUX_EMPTY;
}
ntscVideoMode = g_uVideoMode;
if (!(ntscVideoMode & VF_TEXT))
ntscVideoMode &= ~VF_80COL; // if (aux=empty or aux=80col) && not TEXT: then 80COL switch is ignored
}
NTSC_SetVideoMode(ntscVideoMode, delay);
return MemReadFloatingBus(uExecutedCycles);
}
@@ -267,6 +284,11 @@ bool Video::VideoGetSWAltCharSet(void)
return g_nAltCharSetOffset != 0;
}
bool Video::VideoGet80COLAUXEMPTY(void)
{
return g_uVideoMode & VF_80COL_AUX_EMPTY ? true : false;
}
//===========================================================================
#define SS_YAML_KEY_ALT_CHARSET "Alt Char Set"
+6 -4
View File
@@ -57,10 +57,11 @@ enum VideoFlag_e
VF_PAGE2 = 0x00000020, // Text or Hires
VF_TEXT = 0x00000040,
VF_SHR = 0x00000080, // For VidHD's support for IIgs SHR video modes
VF_PAGE0 = 0x00000100, // Pseudo Page $00 (Poorman's heatmap)
VF_PAGE3 = 0x00000200, // Pseudo Page $60 (Poorman's heatmap)
VF_PAGE4 = 0x00000400, // Pseudo Page $80 (Poorman's heatmap)
VF_PAGE5 = 0x00000800, // Pseudo Page $A0 (Poorman's heatmap)
VF_80COL_AUX_EMPTY = 0x00000100, // For 80COL when aux slot is empty (returns floating bus)
VF_PAGE0 = 0x10000000, // Debugger: Pseudo Page $00 (Poorman's heatmap)
VF_PAGE3 = 0x20000000, // Debugger: Pseudo Page $60 (Poorman's heatmap)
VF_PAGE4 = 0x40000000, // Debugger: Pseudo Page $80 (Poorman's heatmap)
VF_PAGE5 = 0x80000000, // Debugger: Pseudo Page $A0 (Poorman's heatmap)
};
enum AppleFont_e
@@ -234,6 +235,7 @@ public:
bool VideoGetSWPAGE2(void);
bool VideoGetSWTEXT(void);
bool VideoGetSWAltCharSet(void);
bool VideoGet80COLAUXEMPTY(void);
void VideoSaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
void VideoLoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT version);
+22 -3
View File
@@ -712,20 +712,23 @@ static void RepeatInitialization(void)
if (g_cmdLine.uRamWorksExPages)
{
SetRamWorksMemorySize(g_cmdLine.uRamWorksExPages);
SetExpansionMemType(CT_RamWorksIII);
if (!g_cmdLine.auxSlotEmpty)
SetExpansionMemType(CT_RamWorksIII);
g_cmdLine.uRamWorksExPages = 0; // Don't reapply after a restart
}
#endif
if (g_cmdLine.uSaturnBanks)
{
Saturn128K::SetSaturnMemorySize(g_cmdLine.uSaturnBanks); // Set number of banks before constructing Saturn card
SetExpansionMemType(CT_Saturn128K);
if (!g_cmdLine.bSlotEmpty[SLOT0])
SetExpansionMemType(CT_Saturn128K);
g_cmdLine.uSaturnBanks = 0; // Don't reapply after a restart
}
if (g_cmdLine.bSlot0LanguageCard)
{
SetExpansionMemType(CT_LanguageCard);
if (!g_cmdLine.bSlotEmpty[SLOT0])
SetExpansionMemType(CT_LanguageCard);
g_cmdLine.bSlot0LanguageCard = false; // Don't reapply after a restart
}
@@ -824,6 +827,22 @@ static void RepeatInitialization(void)
}
}
// Aux slot
if (g_cmdLine.auxSlotEmpty)
{
GetCardMgr().RemoveAux();
SetExpansionMemType(CT_Empty);
}
else if (g_cmdLine.auxSlotInsert != CT_Empty)
{
if (GetCardMgr().QueryAux() != g_cmdLine.auxSlotInsert) // Ignore if already got this card type in aux slot
{
GetCardMgr().InsertAux(g_cmdLine.auxSlotInsert);
SetExpansionMemType(g_cmdLine.auxSlotInsert);
}
}
// Create window after inserting/removing VidHD card (as it affects width & height)
{
Win32Frame::GetWin32Frame().SetViewportScale(Win32Frame::GetWin32Frame().GetViewportScale(), true);
+3 -3
View File
@@ -662,9 +662,9 @@ void Win32Frame::GetTrackSector(UINT slot, int& drive1Track, int& drive2Track, i
{
// we can't just read from mem[ 0xD357 ] since it might be bank-switched from ROM
// and we need the Language Card RAM
const int nProDOStrack = *MemGetMainPtr(0xC356); // LC1 $D356
const int nProDOSsector = *MemGetMainPtr(0xC357); // LC1 $D357
const int nProDOSslot = *MemGetMainPtr(0xC359) / 16; // LC1 $D359
const int nProDOStrack = *MemGetMainPtrWithLC(0xC356); // LC1 $D356
const int nProDOSsector = *MemGetMainPtrWithLC(0xC357); // LC1 $D357
const int nProDOSslot = *MemGetMainPtrWithLC(0xC359) / 16; // LC1 $D359
if ((nProDOSslot == slot)
&& (nProDOStrack >= 0 && nProDOStrack < 40)
+2
View File
@@ -12,6 +12,8 @@ SynchronousEventManager g_SynchronousEventMgr;
// From Memory.cpp
LPBYTE memwrite[0x100]; // TODO: Init
BYTE memreadPageType[0x100]; // TODO: Init
BYTE memwriteDirtyPage[0x100]; // TODO: Init
LPBYTE mem = NULL; // TODO: Init
LPBYTE memdirty = NULL; // TODO: Init
LPBYTE memVidHD = NULL; // TODO: Init