Support an extra Saturn card in slot 3 and for all Apple II models. (#1279, PR #1284)

. Command line config only, and only permitted in slot 3 for now.
. Save-state Unit v9: Extended: memory (added 'Last Slot to Set Main Mem LC', 'MMU LC Mode').
. Add LanguageCardManager class.
This commit is contained in:
TomCh 2024-03-22 22:36:50 +01:00 committed by GitHub
parent 40bf9cd2d3
commit 10bf60e149
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 348 additions and 137 deletions

View File

@ -43,6 +43,9 @@
-s0 &lt;saturn|saturn64|saturn128&gt;<br> -s0 &lt;saturn|saturn64|saturn128&gt;<br>
Insert a Saturn 64K or Saturn 128K card into slot 0 in the Apple II or II+ machines (or similar clone).<br> Insert a Saturn 64K or Saturn 128K card into slot 0 in the Apple II or II+ machines (or similar clone).<br>
Where -s0 saturn is an alias for -s0 saturn128.<br><br> Where -s0 saturn is an alias for -s0 saturn128.<br><br>
-s3 &lt;saturn|saturn128&gt;<br>
Insert a Saturn 128K card into slot 3 in any Apple II machine.<br>
Use this configuration for Robocom Ltd's (Robo Systems') CAD software in combination with the Interface Module protection dongle in the Game I/O Connector.<br><br>
-s0 &lt;languagecard|lc&gt;<br> -s0 &lt;languagecard|lc&gt;<br>
Insert an Apple 16K Language Card into slot 0 in the original Apple II and use the F8 auto-start ROM.<br> Insert an Apple 16K Language Card into slot 0 in the original Apple II and use the F8 auto-start ROM.<br>
NB. The Apple II+ already defaults to having a Language Card, so this switch is not required.<br><br> NB. The Apple II+ already defaults to having a Language Card, so this switch is not required.<br><br>

View File

@ -49,7 +49,7 @@
<li>If 'W' is inverse: RAM is write enabled. <li>If 'W' is inverse: RAM is write enabled.
<li>If 'W' is not inverse: RAM is write protected. <li>If 'W' is not inverse: RAM is write protected.
<li>'rNN' will appear if a RamWorks 64K bank is active. <li>'rNN' will appear if a RamWorks 64K bank is active.
<li>'sNN' will appear if a Saturn 16K bank is active. <li>'sNN' will appear if a Saturn 16K bank is active (slot 0 only).
</ul> </ul>
</ul> </ul>
</p> </p>

View File

@ -23,6 +23,7 @@
<li>Apple model: ][, ][+, //e, Enhanced //e or clone (eg. Pravets)</li> <li>Apple model: ][, ][+, //e, Enhanced //e or clone (eg. Pravets)</li>
<li>Apple ]['s slot-0 language card: 16K Language Card, Saturn 64K or Saturn 128K</li> <li>Apple ]['s slot-0 language card: 16K Language Card, Saturn 64K or Saturn 128K</li>
<li>Apple //e's auxiliary card: 80 Column, Extended 80 Column or RamWorks III</li> <li>Apple //e's auxiliary card: 80 Column, Extended 80 Column or RamWorks III</li>
<li>Any Apple II: Saturn 128K cards in slots other than slot 0</li>
<li>Disk][ (even during r/w operation)</li> <li>Disk][ (even during r/w operation)</li>
<li>Hard disk (even during r/w operation)</li> <li>Hard disk (even during r/w operation)</li>
<li>Mockingboard & Phasor cards</li> <li>Mockingboard & Phasor cards</li>
@ -35,6 +36,7 @@
<li>Uthernet & Uthernet II cards</li> <li>Uthernet & Uthernet II cards</li>
<li>4Play & SNES MAX joystick cards</li> <li>4Play & SNES MAX joystick cards</li>
<li>VidHD card</li> <li>VidHD card</li>
<li>Game I/O Connector copy protection dongle</li>
</ul> </ul>
The following are not yet persisted to the file: The following are not yet persisted to the file:
<ul> <ul>

View File

@ -115,21 +115,22 @@ void CardManager::InsertInternal(UINT slot, SS_CARDTYPE type)
case CT_Uthernet2: case CT_Uthernet2:
m_slot[slot] = new Uthernet2(slot); m_slot[slot] = new Uthernet2(slot);
break; break;
case CT_LanguageCard: case CT_LanguageCard:
_ASSERT(m_pLanguageCard == NULL); case CT_LanguageCardIIe:
if (m_pLanguageCard) break; // Only support one language card _ASSERT(slot == SLOT0);
m_slot[slot] = m_pLanguageCard = LanguageCardSlot0::create(slot); if (GetLanguageCardMgr().SetLanguageCard(type))
m_slot[SLOT0] = GetLanguageCardMgr().GetLanguageCard();
break; break;
case CT_Saturn128K: case CT_Saturn128K:
_ASSERT(m_pLanguageCard == NULL); if (slot == SLOT0)
if (m_pLanguageCard) break; // Only support one language card {
m_slot[slot] = m_pLanguageCard = new Saturn128K(slot, Saturn128K::GetSaturnMemorySize()); if (GetLanguageCardMgr().SetLanguageCard(type))
break; m_slot[SLOT0] = GetLanguageCardMgr().GetLanguageCard();
case CT_LanguageCardIIe: }
_ASSERT(m_pLanguageCard == NULL); else
if (m_pLanguageCard) break; // Only support one language card {
m_slot[slot] = m_pLanguageCard = LanguageCardUnit::create(slot); m_slot[slot] = new Saturn128K(slot, Saturn128K::kMaxSaturnBanks);
}
break; break;
default: default:
@ -165,9 +166,13 @@ void CardManager::RemoveInternal(UINT slot)
m_pParallelPrinterCard = NULL; m_pParallelPrinterCard = NULL;
break; break;
case CT_LanguageCard: case CT_LanguageCard:
case CT_Saturn128K:
case CT_LanguageCardIIe: case CT_LanguageCardIIe:
m_pLanguageCard = NULL; _ASSERT(slot == SLOT0);
GetLanguageCardMgr().SetLanguageCard(CT_Empty);
break;
case CT_Saturn128K:
if (slot == SLOT0)
GetLanguageCardMgr().SetLanguageCard(CT_Empty);
break; break;
case CT_Z80: case CT_Z80:
m_pZ80Card = NULL; m_pZ80Card = NULL;

View File

@ -2,6 +2,7 @@
#include "Card.h" #include "Card.h"
#include "Disk2CardManager.h" #include "Disk2CardManager.h"
#include "LanguageCard.h"
#include "MockingboardCardManager.h" #include "MockingboardCardManager.h"
#include "Common.h" #include "Common.h"
@ -11,7 +12,6 @@ public:
CardManager(void) : CardManager(void) :
m_pMouseCard(NULL), m_pMouseCard(NULL),
m_pSSC(NULL), m_pSSC(NULL),
m_pLanguageCard(NULL),
m_pParallelPrinterCard(NULL), m_pParallelPrinterCard(NULL),
m_pZ80Card(NULL) m_pZ80Card(NULL)
{ {
@ -53,6 +53,7 @@ public:
// //
Disk2CardManager& GetDisk2CardMgr(void) { return m_disk2CardMgr; } Disk2CardManager& GetDisk2CardMgr(void) { return m_disk2CardMgr; }
LanguageCardManager& GetLanguageCardMgr(void) { return m_languageCardMgr; }
MockingboardCardManager& GetMockingboardCardMgr(void) { return m_mockingboardCardMgr; } MockingboardCardManager& GetMockingboardCardMgr(void) { return m_mockingboardCardMgr; }
class CMouseInterface* GetMouseCard(void) { return m_pMouseCard; } class CMouseInterface* GetMouseCard(void) { return m_pMouseCard; }
bool IsMouseCardInstalled(void) { return m_pMouseCard != NULL; } bool IsMouseCardInstalled(void) { return m_pMouseCard != NULL; }
@ -61,8 +62,6 @@ public:
class ParallelPrinterCard* GetParallelPrinterCard(void) { return m_pParallelPrinterCard; } class ParallelPrinterCard* GetParallelPrinterCard(void) { return m_pParallelPrinterCard; }
bool IsParallelPrinterCardInstalled(void) { return m_pParallelPrinterCard != NULL; } bool IsParallelPrinterCardInstalled(void) { return m_pParallelPrinterCard != NULL; }
class LanguageCardUnit* GetLanguageCard(void) { return m_pLanguageCard; }
void InitializeIO(LPBYTE pCxRomPeripheral); void InitializeIO(LPBYTE pCxRomPeripheral);
void Destroy(void); void Destroy(void);
void Reset(const bool powerCycle); void Reset(const bool powerCycle);
@ -78,10 +77,10 @@ private:
Card* m_slot[NUM_SLOTS]; Card* m_slot[NUM_SLOTS];
Card* m_aux; Card* m_aux;
Disk2CardManager m_disk2CardMgr; Disk2CardManager m_disk2CardMgr;
LanguageCardManager m_languageCardMgr;
MockingboardCardManager m_mockingboardCardMgr; MockingboardCardManager m_mockingboardCardMgr;
class CMouseInterface* m_pMouseCard; class CMouseInterface* m_pMouseCard;
class CSuperSerialCard* m_pSSC; class CSuperSerialCard* m_pSSC;
class LanguageCardUnit* m_pLanguageCard;
class ParallelPrinterCard* m_pParallelPrinterCard; class ParallelPrinterCard* m_pParallelPrinterCard;
class Z80Card* m_pZ80Card; class Z80Card* m_pZ80Card;
}; };

View File

@ -157,6 +157,18 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
lpNextArg = GetNextArg(lpNextArg); lpNextArg = GetNextArg(lpNextArg);
g_cmdLine.szImageName_harddisk[SLOT7][HARDDISK_2] = lpCmdLine; g_cmdLine.szImageName_harddisk[SLOT7][HARDDISK_2] = lpCmdLine;
} }
else if (strcmp(lpCmdLine, "-s0") == 0) // Language Card options for Apple II/II+
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (strcmp(lpCmdLine, "saturn") == 0 || strcmp(lpCmdLine, "saturn128") == 0)
g_cmdLine.uSaturnBanks = Saturn128K::kMaxSaturnBanks;
else if (strcmp(lpCmdLine, "saturn64") == 0)
g_cmdLine.uSaturnBanks = Saturn128K::kMaxSaturnBanks / 2;
else if (strcmp(lpCmdLine, "languagecard") == 0 || strcmp(lpCmdLine, "lc") == 0)
g_cmdLine.bSlot0LanguageCard = true;
}
else if (lpCmdLine[0] == '-' && lpCmdLine[1] == 's' && lpCmdLine[2] >= '1' && lpCmdLine[2] <= '7') else if (lpCmdLine[0] == '-' && lpCmdLine[1] == 's' && lpCmdLine[2] >= '1' && lpCmdLine[2] <= '7')
{ {
const UINT slot = lpCmdLine[2] - '0'; const UINT slot = lpCmdLine[2] - '0';
@ -176,6 +188,8 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
} }
if (strcmp(lpCmdLine, "hdc") == 0) if (strcmp(lpCmdLine, "hdc") == 0)
g_cmdLine.slotInsert[slot] = CT_GenericHDD; g_cmdLine.slotInsert[slot] = CT_GenericHDD;
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) if (strcmp(lpCmdLine, "megaaudio") == 0)
{ {
g_cmdLine.slotInsert[slot] = CT_MegaAudio; g_cmdLine.slotInsert[slot] = CT_MegaAudio;
@ -343,18 +357,6 @@ bool ProcessCmdLine(LPSTR lpCmdLine)
g_cmdLine.uRamWorksExPages = 1; g_cmdLine.uRamWorksExPages = 1;
} }
#endif #endif
else if (strcmp(lpCmdLine, "-s0") == 0)
{
lpCmdLine = GetCurrArg(lpNextArg);
lpNextArg = GetNextArg(lpNextArg);
if (strcmp(lpCmdLine, "saturn") == 0 || strcmp(lpCmdLine, "saturn128") == 0)
g_cmdLine.uSaturnBanks = Saturn128K::kMaxSaturnBanks;
else if (strcmp(lpCmdLine, "saturn64") == 0)
g_cmdLine.uSaturnBanks = Saturn128K::kMaxSaturnBanks/2;
else if (strcmp(lpCmdLine, "languagecard") == 0 || strcmp(lpCmdLine, "lc") == 0)
g_cmdLine.bSlot0LanguageCard = true;
}
else if (strcmp(lpCmdLine, "-f8rom") == 0) // Use custom 2K ROM at [$F800..$FFFF] else if (strcmp(lpCmdLine, "-f8rom") == 0) // Use custom 2K ROM at [$F800..$FFFF]
{ {
lpCmdLine = GetCurrArg(lpNextArg); lpCmdLine = GetCurrArg(lpNextArg);

View File

@ -2594,7 +2594,7 @@ void _DrawSoftSwitchLanguageCardBank( RECT & rect, const int iBankDisplay, int b
int iActiveBank = -1; int iActiveBank = -1;
char cMemType = '?'; // Default to RAMWORKS char cMemType = '?'; // Default to RAMWORKS
if (GetCurrentExpansionMemType() == CT_RamWorksIII) { cMemType = 'r'; iActiveBank = GetRamWorksActiveBank(); } if (GetCurrentExpansionMemType() == CT_RamWorksIII) { cMemType = 'r'; iActiveBank = GetRamWorksActiveBank(); }
if (GetCurrentExpansionMemType() == CT_Saturn128K) { cMemType = 's'; iActiveBank = GetCardMgr().GetLanguageCard()->GetActiveBank(); } if (GetCurrentExpansionMemType() == CT_Saturn128K) { cMemType = 's'; iActiveBank = GetCardMgr().GetLanguageCardMgr().GetLanguageCard()->GetActiveBank(); }
if (iActiveBank >= 0) if (iActiveBank >= 0)
{ {

View File

@ -24,11 +24,32 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/* Description: Language Card and Saturn 128K emulation /* Description: Language Card and Saturn 128K emulation
* *
* Author: various * Author: various
*
* Note: Here's what isn't fully emulated:
* From UTAII 5-42 (Application Note: Multiple RAM Card Configurations)
* . For II/II, INHIBIT' (disable motherboard ROM for $D000-$FFFF) and Apple's 16K RAM card isn't correct:
* . "If the expansion RAM is not enabled on a RAM card, the ROM on the card will respond to $F800-$FFFF addressing - period."
* . In UTAIIe 5-24, Sather describes this as "a particularly nettlesome associated fact"!
* . NB. "When INHIBIT' is low on the Apple IIe, all motherboard ROM is disabled, including high RAM."
* . Note: I assume a Saturn card "will release the $F800-$FFFF range when RAM on the card is disabled", since there's no F8 ROM on the Saturn.
* . Summary: for a II/II+ with an *Apple* 16K RAM card in slot 0, when (High) RAM is disabled, then:
* . ROM on the slot 0 card will respond, along with any Saturn card(s) in other slots which pull INHIBIT' low.
* . *** AppleWin emulates a slot 0 LC as if the Sather h/w mod had been applied.
* . [UTAII 5-42] "Enable two RAM cards for writing simultaneously..."
* "both RAM cards will accept the data from a single store instruction to the $D000-$FFFF range"
* *** AppleWin only stores to the last accessed RAM card.
* . Presumably enabling two RAM cards for reading RAM will both respond and the result is the OR-sum?
* *** AppleWin only loads from the last accessed RAM card.
* . The 16K RAM card has a socket for an F8 ROM, whereas the Saturn card doesn't.
* Also see UTAII 6-6, where Firmware card and 16K RAM card are described.
* . Sather refers to the Apple 16K RAM card, which is just the Apple Language Card.
*
*/ */
#include "StdAfx.h" #include "StdAfx.h"
#include "LanguageCard.h" #include "LanguageCard.h"
#include "CardManager.h"
#include "Core.h" #include "Core.h"
#include "CPU.h" // GH#700 #include "CPU.h" // GH#700
#include "Log.h" #include "Log.h"
@ -45,17 +66,30 @@ LanguageCardUnit * LanguageCardUnit::create(UINT slot)
LanguageCardUnit::LanguageCardUnit(SS_CARDTYPE type, UINT slot) : LanguageCardUnit::LanguageCardUnit(SS_CARDTYPE type, UINT slot) :
Card(type, slot), Card(type, slot),
m_uLastRamWrite(0) m_uLastRamWrite(0),
m_memMode(kMemModeInitialState),
m_pMemory(NULL)
{ {
if (m_slot != LanguageCardUnit::kSlot0) if (type != CT_Saturn128K && m_slot != LanguageCardUnit::kSlot0)
ThrowErrorInvalidSlot(); ThrowErrorInvalidSlot();
SetMemMainLanguageCard(NULL, true); if (m_slot == SLOT0)
SetMemMainLanguageCard(NULL, SLOT0, true);
} }
LanguageCardUnit::~LanguageCardUnit(void) LanguageCardUnit::~LanguageCardUnit(void)
{ {
SetMemMainLanguageCard(NULL); // Nothing to do for SetMemMainLanguageCard():
// . if //e, then no ptr to clean up (since just using memmain)
// . else: subclass will do ptr clean up
}
void LanguageCardUnit::Reset(const bool powerCycle)
{
// For power on: card's ctor will have set card's local memmode to LanguageCardUnit::kMemModeInitialState.
// For reset: II/II+ unaffected, so only for //e or above.
if (IsAppleIIeOrAbove(GetApple2Type()))
SetLCMemMode(LanguageCardUnit::kMemModeInitialState);
} }
void LanguageCardUnit::InitializeIO(LPBYTE pCxRomPeripheral) void LanguageCardUnit::InitializeIO(LPBYTE pCxRomPeripheral)
@ -67,9 +101,10 @@ BYTE __stdcall LanguageCardUnit::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValu
{ {
UINT uSlot = ((uAddr & 0xff) >> 4) - 8; UINT uSlot = ((uAddr & 0xff) >> 4) - 8;
LanguageCardUnit* pLC = (LanguageCardUnit*) MemGetSlotParameters(uSlot); LanguageCardUnit* pLC = (LanguageCardUnit*) MemGetSlotParameters(uSlot);
_ASSERT(uSlot == SLOT0);
DWORD memmode = GetMemMode(); UINT memmode = pLC->GetLCMemMode();
DWORD lastmemmode = memmode; UINT lastmemmode = memmode;
memmode &= ~(MF_BANK2 | MF_HIGHRAM); memmode &= ~(MF_BANK2 | MF_HIGHRAM);
if (!(uAddr & 8)) if (!(uAddr & 8))
@ -95,7 +130,16 @@ BYTE __stdcall LanguageCardUnit::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValu
} }
pLC->SetLastRamWrite( ((uAddr & 1) && !bWrite) ); // UTAIIe:5-23 pLC->SetLastRamWrite( ((uAddr & 1) && !bWrite) ); // UTAIIe:5-23
SetMemMode(memmode); pLC->SetLCMemMode(memmode);
const bool bCardChanged = GetCardMgr().GetLanguageCardMgr().GetLastSlotToSetMainMemLC() != SLOT0;
if (bCardChanged)
{
if (pLC->QueryType() == CT_LanguageCardIIe)
SetMemMainLanguageCard(NULL, SLOT0, true);
else // CT_LanguageCard
SetMemMainLanguageCard(pLC->m_pMemory, SLOT0);
}
// //
@ -104,8 +148,10 @@ BYTE __stdcall LanguageCardUnit::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValu
// IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND // IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND
// WRITE TABLES. // WRITE TABLES.
if (lastmemmode != memmode) if ((lastmemmode != memmode) || bCardChanged)
{ {
// NB. Always SetMemMode() - locally may be same, but card may've changed
SetMemMode((GetMemMode() & ~MF_LANGCARD_MASK) | (memmode & MF_LANGCARD_MASK));
MemUpdatePaging(0); // Initialize=0 MemUpdatePaging(0); // Initialize=0
} }
@ -143,6 +189,11 @@ bool LanguageCardUnit::IsOpcodeRMWabs(WORD addr)
return false; return false;
} }
void LanguageCardUnit::SetGlobalLCMemMode(void)
{
SetMemMode((GetMemMode() & ~MF_LANGCARD_MASK) | (GetLCMemMode() & MF_LANGCARD_MASK));
}
//------------------------------------- //-------------------------------------
LanguageCardSlot0 * LanguageCardSlot0::create(UINT slot) LanguageCardSlot0 * LanguageCardSlot0::create(UINT slot)
@ -154,13 +205,16 @@ LanguageCardSlot0::LanguageCardSlot0(SS_CARDTYPE type, UINT slot)
: LanguageCardUnit(type, slot) : LanguageCardUnit(type, slot)
{ {
m_pMemory = new BYTE[kMemBankSize]; m_pMemory = new BYTE[kMemBankSize];
SetMemMainLanguageCard(m_pMemory); if (m_slot == SLOT0)
SetMemMainLanguageCard(m_pMemory, SLOT0);
} }
LanguageCardSlot0::~LanguageCardSlot0(void) LanguageCardSlot0::~LanguageCardSlot0(void)
{ {
delete [] m_pMemory; delete [] m_pMemory;
m_pMemory = NULL; m_pMemory = NULL;
if (m_slot == SLOT0)
SetMemMainLanguageCard(NULL, SLOT0);
} }
// //
@ -186,15 +240,15 @@ const std::string& LanguageCardSlot0::GetSnapshotCardName(void)
void LanguageCardSlot0::SaveLCState(YamlSaveHelper& yamlSaveHelper) void LanguageCardSlot0::SaveLCState(YamlSaveHelper& yamlSaveHelper)
{ {
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MEMORYMODE, GetMemMode() & (MF_WRITERAM|MF_HIGHRAM|MF_BANK2)); yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MEMORYMODE, GetLCMemMode() & MF_LANGCARD_MASK);
yamlSaveHelper.SaveUint(SS_YAML_KEY_LASTRAMWRITE, GetLastRamWrite() ? 1 : 0); yamlSaveHelper.SaveUint(SS_YAML_KEY_LASTRAMWRITE, GetLastRamWrite() ? 1 : 0);
} }
void LanguageCardSlot0::LoadLCState(YamlLoadHelper& yamlLoadHelper) void LanguageCardSlot0::LoadLCState(YamlLoadHelper& yamlLoadHelper)
{ {
DWORD memMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MEMORYMODE) & MF_LANGCARD_MASK; UINT memMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MEMORYMODE) & MF_LANGCARD_MASK;
BOOL lastRamWrite = yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTRAMWRITE) ? TRUE : FALSE; BOOL lastRamWrite = yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTRAMWRITE) ? TRUE : FALSE;
SetMemMode( (GetMemMode() & ~MF_LANGCARD_MASK) | memMode ); SetLCMemMode(memMode);
SetLastRamWrite(lastRamWrite); SetLastRamWrite(lastRamWrite);
} }
@ -261,7 +315,8 @@ Saturn128K::Saturn128K(UINT slot, UINT banks)
for (UINT i = 1; i < m_uSaturnTotalBanks; i++) for (UINT i = 1; i < m_uSaturnTotalBanks; i++)
m_aSaturnBanks[i] = new BYTE[kMemBankSize]; // Saturn banks are 16K, max 8 banks/card m_aSaturnBanks[i] = new BYTE[kMemBankSize]; // Saturn banks are 16K, max 8 banks/card
SetMemMainLanguageCard( m_aSaturnBanks[ m_uSaturnActiveBank ] ); if (slot == SLOT0)
::SetMemMainLanguageCard(m_aSaturnBanks[m_uSaturnActiveBank], SLOT0);
} }
Saturn128K::~Saturn128K(void) Saturn128K::~Saturn128K(void)
@ -276,6 +331,9 @@ Saturn128K::~Saturn128K(void)
m_aSaturnBanks[i] = NULL; m_aSaturnBanks[i] = NULL;
} }
} }
// NB. want the Saturn128K object that set the ptr via ::SetMemMainLanguageCard() to now set it to NULL (may be from SLOT0 or another slot)
// In reality, dtor only called when whole VM is being destroyed, so won't have have use-after-frees.
} }
UINT Saturn128K::GetActiveBank(void) UINT Saturn128K::GetActiveBank(void)
@ -317,7 +375,8 @@ BYTE __stdcall Saturn128K::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULO
return bWrite ? 0 : MemReadFloatingBus(nExecutedCycles); return bWrite ? 0 : MemReadFloatingBus(nExecutedCycles);
bool bBankChanged = false; bool bBankChanged = false;
DWORD memmode=0, lastmemmode=0; UINT memmode = pLC->GetLCMemMode();
UINT lastmemmode = memmode;
if (uAddr & (1<<2)) if (uAddr & (1<<2))
{ {
@ -334,13 +393,11 @@ BYTE __stdcall Saturn128K::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULO
pLC->m_uSaturnActiveBank = pLC->m_uSaturnTotalBanks-1; // FIXME: just prevent crash for now! pLC->m_uSaturnActiveBank = pLC->m_uSaturnTotalBanks-1; // FIXME: just prevent crash for now!
} }
SetMemMainLanguageCard( pLC->m_aSaturnBanks[ pLC->m_uSaturnActiveBank ] ); ::SetMemMainLanguageCard(pLC->m_aSaturnBanks[pLC->m_uSaturnActiveBank], uSlot);
bBankChanged = true; bBankChanged = true;
} }
else else
{ {
memmode = GetMemMode();
lastmemmode = memmode;
memmode &= ~(MF_BANK2 | MF_HIGHRAM); memmode &= ~(MF_BANK2 | MF_HIGHRAM);
if (!(uAddr & 8)) if (!(uAddr & 8))
@ -355,15 +412,24 @@ BYTE __stdcall Saturn128K::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULO
memmode &= ~MF_WRITERAM; memmode &= ~MF_WRITERAM;
pLC->SetLastRamWrite(uAddr & 1); // Saturn differs from Apple's 16K LC: any access (LC is read-only) pLC->SetLastRamWrite(uAddr & 1); // Saturn differs from Apple's 16K LC: any access (LC is read-only)
SetMemMode(memmode); pLC->SetLCMemMode(memmode);
bBankChanged = GetCardMgr().GetLanguageCardMgr().GetLastSlotToSetMainMemLC() != uSlot;
if (bBankChanged)
{
::SetMemMainLanguageCard(pLC->m_aSaturnBanks[pLC->m_uSaturnActiveBank], uSlot);
}
} }
// NB. Unlike LC, no need to check if next opcode is STA $C002-5, as Saturn is not for //e // NB. Saturn can be put in any slot but MemOptimizeForModeChanging() currently only supports LC in slot 0.
// . This optimization (check if next opcode is STA $C002-5) isn't essential, so skip it for now.
// IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND // IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND
// WRITE TABLES. // WRITE TABLES.
if ((lastmemmode != memmode) || bBankChanged) if ((lastmemmode != memmode) || bBankChanged)
{ {
// NB. Always SetMemMode() - locally may be same, but card or bank may've changed
SetMemMode((GetMemMode() & ~MF_LANGCARD_MASK) | (memmode & MF_LANGCARD_MASK));
MemUpdatePaging(0); // Initialize=0 MemUpdatePaging(0); // Initialize=0
} }
@ -393,13 +459,6 @@ const std::string& Saturn128K::GetSnapshotCardName(void)
void Saturn128K::SaveSnapshot(YamlSaveHelper& yamlSaveHelper) void Saturn128K::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
{ {
if (!IsApple2PlusOrClone(GetApple2Type()))
{
_ASSERT(0);
LogFileOutput("Warning: Save-state attempted to save %s for //e or above\n", GetSnapshotCardName().c_str());
return; // No Saturn support for //e and above
}
YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_SATURN_VER); YamlSaveHelper::Slot slot(yamlSaveHelper, GetSnapshotCardName(), m_slot, kUNIT_SATURN_VER);
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE); YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", SS_YAML_KEY_STATE);
@ -453,13 +512,18 @@ bool Saturn128K::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
yamlLoadHelper.PopMap(); yamlLoadHelper.PopMap();
} }
SetMemMainLanguageCard( m_aSaturnBanks[ m_uSaturnActiveBank ] ); // NB. MemInitializeFromSnapshot() called at end of Snapshot_LoadState_v2():
// . SetMemMainLanguageCard() for the slot/card that last set the 16KB LC bank
// NB. MemUpdatePaging(TRUE) called at end of Snapshot_LoadState_v2() // . MemUpdatePaging(TRUE)
return true; return true;
} }
void Saturn128K::SetMemMainLanguageCard(void)
{
::SetMemMainLanguageCard(m_aSaturnBanks[m_uSaturnActiveBank], m_slot);
}
void Saturn128K::SetSaturnMemorySize(UINT banks) void Saturn128K::SetSaturnMemorySize(UINT banks)
{ {
g_uSaturnBanksFromCmdLine = banks; g_uSaturnBanksFromCmdLine = banks;
@ -469,3 +533,70 @@ UINT Saturn128K::GetSaturnMemorySize()
{ {
return g_uSaturnBanksFromCmdLine; return g_uSaturnBanksFromCmdLine;
} }
//-------------------------------------
/*
* LangauageCardManager:
* . manage reset for all cards (eg. II/II+'s LC is unaffected, whereas //e's LC is)
* . manage lastSlotToSetMainMemLC
* . TODO: assist with debugger's display of "sNN" for active 16K bank
*/
void LanguageCardManager::Reset(const bool powerCycle /*=false*/)
{
if (IsApple2PlusOrClone(GetApple2Type()) && !powerCycle) // For reset : II/II+ unaffected
return;
if (GetLanguageCard())
GetLanguageCard()->SetLastRamWrite(0);
if (IsApple2PlusOrClone(GetApple2Type()) && GetCardMgr().QuerySlot(SLOT0) == CT_Empty)
SetMemMode(0);
else
SetMemMode(LanguageCardUnit::kMemModeInitialState);
}
void LanguageCardManager::SetMemModeFromSnapshot(void)
{
// If multiple "Language Cards" (eg. LC+Saturn or 2xSaturn) then setup via the last card that selected the 16KB LC bank.
// NB. Skip if not Saturn card (ie. a LC), since LC's are only in slot0 and in the ctor it has called SetMainMemLanguageCard()
if (GetCardMgr().QuerySlot(m_lastSlotToSetMainMemLCFromSnapshot) == CT_Saturn128K)
{
Saturn128K& saturn = dynamic_cast<Saturn128K&>(GetCardMgr().GetRef(m_lastSlotToSetMainMemLCFromSnapshot));
saturn.SetMemMainLanguageCard();
}
dynamic_cast<LanguageCardUnit&>(GetCardMgr().GetRef(m_lastSlotToSetMainMemLCFromSnapshot)).SetGlobalLCMemMode();
}
bool LanguageCardManager::SetLanguageCard(SS_CARDTYPE type)
{
if (type == CT_Empty)
{
m_pLanguageCard = NULL;
return true;
}
_ASSERT(GetLanguageCard() == NULL);
if (GetLanguageCard())
return false; // Only support one language card
switch (type)
{
case CT_LanguageCard:
m_pLanguageCard = LanguageCardSlot0::create(SLOT0);
break;
case CT_LanguageCardIIe:
m_pLanguageCard = LanguageCardUnit::create(SLOT0);
break;
case CT_Saturn128K:
m_pLanguageCard = new Saturn128K(SLOT0, Saturn128K::GetSaturnMemorySize());
break;
default:
_ASSERT(0);
return false;
}
return true;
}

View File

@ -15,10 +15,9 @@ public:
virtual ~LanguageCardUnit(void); virtual ~LanguageCardUnit(void);
virtual void Destroy(void) {} virtual void Destroy(void) {}
virtual void Reset(const bool powerCycle) {} virtual void Reset(const bool powerCycle);
virtual void Update(const ULONG nExecutedCycles) {} virtual void Update(const ULONG nExecutedCycles) {}
virtual void InitializeIO(LPBYTE pCxRomPeripheral); virtual void InitializeIO(LPBYTE pCxRomPeripheral);
virtual UINT GetActiveBank(void) { return 0; } // Always 0 as only 1x 16K bank virtual UINT GetActiveBank(void) { return 0; } // Always 0 as only 1x 16K bank
virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper) { } // A no-op for //e - called from CardManager::SaveSnapshot() virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper) { } // A no-op for //e - called from CardManager::SaveSnapshot()
@ -26,6 +25,9 @@ public:
BOOL GetLastRamWrite(void) { return m_uLastRamWrite; } BOOL GetLastRamWrite(void) { return m_uLastRamWrite; }
void SetLastRamWrite(BOOL count) { m_uLastRamWrite = count; } void SetLastRamWrite(BOOL count) { m_uLastRamWrite = count; }
UINT GetLCMemMode(void) { return m_memMode; }
void SetLCMemMode(UINT memMode) { m_memMode = memMode; }
void SetGlobalLCMemMode(void);
SS_CARDTYPE GetMemoryType(void) { return QueryType(); } SS_CARDTYPE GetMemoryType(void) { return QueryType(); }
bool IsOpcodeRMWabs(WORD addr); bool IsOpcodeRMWabs(WORD addr);
@ -37,8 +39,11 @@ public:
protected: protected:
LanguageCardUnit(SS_CARDTYPE type, UINT slot); LanguageCardUnit(SS_CARDTYPE type, UINT slot);
LPBYTE m_pMemory;
private: private:
UINT m_uLastRamWrite; UINT m_uLastRamWrite;
UINT m_memMode;
}; };
// //
@ -64,8 +69,6 @@ protected:
void SaveLCState(class YamlSaveHelper& yamlSaveHelper); void SaveLCState(class YamlSaveHelper& yamlSaveHelper);
void LoadLCState(class YamlLoadHelper& yamlLoadHelper); void LoadLCState(class YamlLoadHelper& yamlLoadHelper);
LPBYTE m_pMemory;
private: private:
const std::string& GetSnapshotMemStructName(void); const std::string& GetSnapshotMemStructName(void);
}; };
@ -85,6 +88,8 @@ public:
virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper); virtual void SaveSnapshot(YamlSaveHelper& yamlSaveHelper);
virtual bool LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version); virtual bool LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version);
void SetMemMainLanguageCard(void);
static UINT GetSaturnMemorySize(); static UINT GetSaturnMemorySize();
static void SetSaturnMemorySize(UINT banks); static void SetSaturnMemorySize(UINT banks);
@ -103,3 +108,34 @@ private:
UINT m_uSaturnActiveBank; // Saturn 128K Language Card Bank 0 .. 7 UINT m_uSaturnActiveBank; // Saturn 128K Language Card Bank 0 .. 7
LPBYTE m_aSaturnBanks[kMaxSaturnBanks]; LPBYTE m_aSaturnBanks[kMaxSaturnBanks];
}; };
//
// Language Card manager
//
class LanguageCardManager
{
public:
LanguageCardManager(void) :
m_pLanguageCard(NULL),
m_lastSlotToSetMainMemLC(SLOT0),
m_lastSlotToSetMainMemLCFromSnapshot(SLOT0)
{}
~LanguageCardManager(void) {}
void Reset(const bool powerCycle = false);
UINT GetLastSlotToSetMainMemLC(void) { return m_lastSlotToSetMainMemLC; }
void SetLastSlotToSetMainMemLC(UINT slot) { m_lastSlotToSetMainMemLC = slot; }
void SetLastSlotToSetMainMemLCFromSnapshot(UINT slot) { m_lastSlotToSetMainMemLCFromSnapshot = slot; }
LanguageCardUnit* GetLanguageCard(void) { return m_pLanguageCard; }
bool SetLanguageCard(SS_CARDTYPE type);
void SetMemModeFromSnapshot(void);
private:
LanguageCardUnit* m_pLanguageCard;
UINT m_lastSlotToSetMainMemLC;
UINT m_lastSlotToSetMainMemLCFromSnapshot;
};

View File

@ -76,20 +76,20 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// . Sather uses INTCXROM instead of SLOTCXROM' (used by the Apple//e Tech Ref Manual), so keep to this // . Sather uses INTCXROM instead of SLOTCXROM' (used by the Apple//e Tech Ref Manual), so keep to this
// convention too since UTAIIe is the reference for most of the logic that we implement in the emulator. // convention too since UTAIIe is the reference for most of the logic that we implement in the emulator.
#define SW_80STORE (memmode & MF_80STORE) #define SW_80STORE (g_memmode & MF_80STORE)
#define SW_ALTZP (memmode & MF_ALTZP) #define SW_ALTZP (g_memmode & MF_ALTZP)
#define SW_AUXREAD (memmode & MF_AUXREAD) #define SW_AUXREAD (g_memmode & MF_AUXREAD)
#define SW_AUXWRITE (memmode & MF_AUXWRITE) #define SW_AUXWRITE (g_memmode & MF_AUXWRITE)
#define SW_BANK2 (memmode & MF_BANK2) #define SW_BANK2 (g_memmode & MF_BANK2)
#define SW_HIGHRAM (memmode & MF_HIGHRAM) #define SW_HIGHRAM (g_memmode & MF_HIGHRAM)
#define SW_HIRES (memmode & MF_HIRES) #define SW_HIRES (g_memmode & MF_HIRES)
#define SW_PAGE2 (memmode & MF_PAGE2) #define SW_PAGE2 (g_memmode & MF_PAGE2)
#define SW_SLOTC3ROM (memmode & MF_SLOTC3ROM) #define SW_SLOTC3ROM (g_memmode & MF_SLOTC3ROM)
#define SW_INTCXROM (memmode & MF_INTCXROM) #define SW_INTCXROM (g_memmode & MF_INTCXROM)
#define SW_WRITERAM (memmode & MF_WRITERAM) #define SW_WRITERAM (g_memmode & MF_WRITERAM)
#define SW_IOUDIS (memmode & MF_IOUDIS) #define SW_IOUDIS (g_memmode & MF_IOUDIS)
#define SW_ALTROM0 (memmode & MF_ALTROM0) // For Copam Base64A #define SW_ALTROM0 (g_memmode & MF_ALTROM0) // For Copam Base64A
#define SW_ALTROM1 (memmode & MF_ALTROM1) // For Copam Base64A #define SW_ALTROM1 (g_memmode & MF_ALTROM1) // For Copam Base64A
/* /*
MEMORY MANAGEMENT SOFT SWITCHES MEMORY MANAGEMENT SOFT SWITCHES
@ -224,7 +224,7 @@ static LPBYTE pCxRomPeripheral = NULL;
static LPBYTE g_pMemMainLanguageCard = NULL; static LPBYTE g_pMemMainLanguageCard = NULL;
static DWORD memmode = LanguageCardUnit::kMemModeInitialState; static DWORD g_memmode = LanguageCardUnit::kMemModeInitialState;
static BOOL modechanging = 0; // An Optimisation: means delay calling UpdatePaging() for 1 instruction static BOOL modechanging = 0; // An Optimisation: means delay calling UpdatePaging() for 1 instruction
static UINT memrompages = 1; static UINT memrompages = 1;
@ -371,25 +371,27 @@ UINT GetRamWorksActiveBank(void)
static BOOL GetLastRamWrite(void) static BOOL GetLastRamWrite(void)
{ {
if (GetCardMgr().GetLanguageCard()) if (GetCardMgr().GetLanguageCardMgr().GetLanguageCard())
return GetCardMgr().GetLanguageCard()->GetLastRamWrite(); return GetCardMgr().GetLanguageCardMgr().GetLanguageCard()->GetLastRamWrite();
return 0; return 0;
} }
static void SetLastRamWrite(BOOL count) static void SetLastRamWrite(BOOL count)
{ {
if (GetCardMgr().GetLanguageCard()) if (GetCardMgr().GetLanguageCardMgr().GetLanguageCard())
GetCardMgr().GetLanguageCard()->SetLastRamWrite(count); GetCardMgr().GetLanguageCardMgr().GetLanguageCard()->SetLastRamWrite(count);
} }
// //
void SetMemMainLanguageCard(LPBYTE ptr, bool bMemMain /*=false*/) void SetMemMainLanguageCard(LPBYTE ptr, UINT slot, bool bMemMain /*=false*/)
{ {
if (bMemMain) if (bMemMain)
g_pMemMainLanguageCard = memmain+0xC000; g_pMemMainLanguageCard = memmain+0xC000;
else else
g_pMemMainLanguageCard = ptr; g_pMemMainLanguageCard = ptr;
GetCardMgr().GetLanguageCardMgr().SetLastSlotToSetMainMemLC(slot);
} }
LPBYTE GetCxRomPeripheral(void) LPBYTE GetCxRomPeripheral(void)
@ -646,12 +648,12 @@ static BYTE __stdcall IOWrite_C07x(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULON
case 0xC: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); case 0xC: return IO_Null(pc, addr, bWrite, d, nExecutedCycles);
case 0xD: return IO_Null(pc, addr, bWrite, d, nExecutedCycles); case 0xD: return IO_Null(pc, addr, bWrite, d, nExecutedCycles);
case 0xE: if (IS_APPLE2C()) case 0xE: if (IS_APPLE2C())
SetMemMode(memmode | MF_IOUDIS); // On: disable IOU access for addresses $C058 to $C05F; enable access to DHIRES switch SetMemMode(g_memmode | MF_IOUDIS); // On: disable IOU access for addresses $C058 to $C05F; enable access to DHIRES switch
else else
return IO_Null(pc, addr, bWrite, d, nExecutedCycles); return IO_Null(pc, addr, bWrite, d, nExecutedCycles);
break; break;
case 0xF: if (IS_APPLE2C()) case 0xF: if (IS_APPLE2C())
SetMemMode(memmode & ~MF_IOUDIS); // Off: enable IOU access for addresses $C058 to $C05F; disable access to DHIRES switch SetMemMode(g_memmode & ~MF_IOUDIS); // Off: enable IOU access for addresses $C058 to $C05F; disable access to DHIRES switch
else else
return IO_Null(pc, addr, bWrite, d, nExecutedCycles); return IO_Null(pc, addr, bWrite, d, nExecutedCycles);
break; break;
@ -1076,14 +1078,14 @@ static bool IsCardInSlot(UINT slot)
DWORD GetMemMode(void) DWORD GetMemMode(void)
{ {
return memmode; return g_memmode;
} }
void SetMemMode(DWORD uNewMemMode) void SetMemMode(DWORD uNewMemMode)
{ {
#if defined(_DEBUG) && 0 #if defined(_DEBUG) && 0
static DWORD dwOldDiff = 0; static DWORD dwOldDiff = 0;
DWORD dwDiff = memmode ^ uNewMemMode; DWORD dwDiff = g_memmode ^ uNewMemMode;
dwDiff &= ~(MF_SLOTC3ROM | MF_INTCXROM); dwDiff &= ~(MF_SLOTC3ROM | MF_INTCXROM);
if (dwOldDiff != dwDiff) if (dwOldDiff != dwDiff)
{ {
@ -1118,7 +1120,7 @@ void SetMemMode(DWORD uNewMemMode)
OutputDebugString(str.c_str()); OutputDebugString(str.c_str());
} }
#endif #endif
memmode = uNewMemMode; g_memmode = uNewMemMode;
} }
//=========================================================================== //===========================================================================
@ -1130,18 +1132,15 @@ static void UpdatePaging(BOOL initialize);
// . CtrlReset() Soft-reset (Ctrl+Reset) for //e // . CtrlReset() Soft-reset (Ctrl+Reset) for //e
void MemResetPaging() void MemResetPaging()
{ {
ResetPaging(0); // Initialize=0 ResetPaging(FALSE); // Initialize=0
} }
// Call by:
// . MemResetPaging() -> ResetPaging(FALSE)
// . MemReset() -> ResetPaging(TRUE)
static void ResetPaging(BOOL initialize) static void ResetPaging(BOOL initialize)
{ {
SetLastRamWrite(0); GetCardMgr().GetLanguageCardMgr().Reset(initialize);
if (IsApple2PlusOrClone(GetApple2Type()) && GetCardMgr().QuerySlot(SLOT0) == CT_Empty)
SetMemMode(0);
else
SetMemMode(LanguageCardUnit::kMemModeInitialState);
UpdatePaging(initialize); UpdatePaging(initialize);
} }
@ -1777,6 +1776,9 @@ void MemInitializeFromSnapshot(void)
// NB. Copied to /mem/ by UpdatePaging(TRUE) // NB. Copied to /mem/ by UpdatePaging(TRUE)
} }
GetCardMgr().GetLanguageCardMgr().SetMemModeFromSnapshot();
// Finally setup the paging tables
MemUpdatePaging(TRUE); MemUpdatePaging(TRUE);
// //
@ -1949,7 +1951,7 @@ void MemReset()
mem = memimage; mem = memimage;
// INITIALIZE PAGING, FILLING IN THE 64K MEMORY IMAGE // INITIALIZE PAGING, FILLING IN THE 64K MEMORY IMAGE
ResetPaging(TRUE); // Initialize=1, init memmode ResetPaging(TRUE); // Initialize=1, init g_memmode
MemAnnunciatorReset(); MemAnnunciatorReset();
// INITIALIZE & RESET THE CPU // INITIALIZE & RESET THE CPU
@ -2013,7 +2015,7 @@ static void DebugFlip(WORD address, ULONG nExecutedCycles)
BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nExecutedCycles) BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nExecutedCycles)
{ {
address &= 0xFF; address &= 0xFF;
DWORD lastmemmode = memmode; DWORD lastmemmode = g_memmode;
#if defined(_DEBUG) && defined(DEBUG_FLIP_TIMINGS) #if defined(_DEBUG) && defined(DEBUG_FLIP_TIMINGS)
DebugFlip(address, nExecutedCycles); DebugFlip(address, nExecutedCycles);
#endif #endif
@ -2023,22 +2025,22 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE
{ {
switch (address) switch (address)
{ {
case 0x00: SetMemMode(memmode & ~MF_80STORE); break; case 0x00: SetMemMode(g_memmode & ~MF_80STORE); break;
case 0x01: SetMemMode(memmode | MF_80STORE); break; case 0x01: SetMemMode(g_memmode | MF_80STORE); break;
case 0x02: SetMemMode(memmode & ~MF_AUXREAD); break; case 0x02: SetMemMode(g_memmode & ~MF_AUXREAD); break;
case 0x03: SetMemMode(memmode | MF_AUXREAD); break; case 0x03: SetMemMode(g_memmode | MF_AUXREAD); break;
case 0x04: SetMemMode(memmode & ~MF_AUXWRITE); break; case 0x04: SetMemMode(g_memmode & ~MF_AUXWRITE); break;
case 0x05: SetMemMode(memmode | MF_AUXWRITE); break; case 0x05: SetMemMode(g_memmode | MF_AUXWRITE); break;
case 0x06: SetMemMode(memmode & ~MF_INTCXROM); break; case 0x06: SetMemMode(g_memmode & ~MF_INTCXROM); break;
case 0x07: SetMemMode(memmode | MF_INTCXROM); break; case 0x07: SetMemMode(g_memmode | MF_INTCXROM); break;
case 0x08: SetMemMode(memmode & ~MF_ALTZP); break; case 0x08: SetMemMode(g_memmode & ~MF_ALTZP); break;
case 0x09: SetMemMode(memmode | MF_ALTZP); break; case 0x09: SetMemMode(g_memmode | MF_ALTZP); break;
case 0x0A: SetMemMode(memmode & ~MF_SLOTC3ROM); break; case 0x0A: SetMemMode(g_memmode & ~MF_SLOTC3ROM); break;
case 0x0B: SetMemMode(memmode | MF_SLOTC3ROM); break; case 0x0B: SetMemMode(g_memmode | MF_SLOTC3ROM); break;
case 0x54: SetMemMode(memmode & ~MF_PAGE2); break; case 0x54: SetMemMode(g_memmode & ~MF_PAGE2); break;
case 0x55: SetMemMode(memmode | MF_PAGE2); break; case 0x55: SetMemMode(g_memmode | MF_PAGE2); break;
case 0x56: SetMemMode(memmode & ~MF_HIRES); break; case 0x56: SetMemMode(g_memmode & ~MF_HIRES); break;
case 0x57: SetMemMode(memmode | MF_HIRES); break; case 0x57: SetMemMode(g_memmode | MF_HIRES); break;
#ifdef RAMWORKS #ifdef RAMWORKS
case 0x71: // extended memory aux page number case 0x71: // extended memory aux page number
case 0x73: // Ramworks III set aux page number case 0x73: // Ramworks III set aux page number
@ -2066,10 +2068,10 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE
{ {
switch (address) switch (address)
{ {
case 0x58: SetMemMode(memmode & ~MF_ALTROM0); break; case 0x58: SetMemMode(g_memmode & ~MF_ALTROM0); break;
case 0x59: SetMemMode(memmode | MF_ALTROM0); break; case 0x59: SetMemMode(g_memmode | MF_ALTROM0); break;
case 0x5A: SetMemMode(memmode & ~MF_ALTROM1); break; case 0x5A: SetMemMode(g_memmode & ~MF_ALTROM1); break;
case 0x5B: SetMemMode(memmode | MF_ALTROM1); break; case 0x5B: SetMemMode(g_memmode | MF_ALTROM1); break;
} }
} }
@ -2078,10 +2080,10 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE
// IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND // IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND
// WRITE TABLES. // WRITE TABLES.
if ((lastmemmode != memmode) || modechanging) if ((lastmemmode != g_memmode) || modechanging)
{ {
// NB. Must check MF_SLOTC3ROM too, as IoHandlerCardsIn() depends on both MF_INTCXROM|MF_SLOTC3ROM // NB. Must check MF_SLOTC3ROM too, as IoHandlerCardsIn() depends on both MF_INTCXROM|MF_SLOTC3ROM
if ((lastmemmode & (MF_INTCXROM|MF_SLOTC3ROM)) != (memmode & (MF_INTCXROM|MF_SLOTC3ROM))) if ((lastmemmode & (MF_INTCXROM|MF_SLOTC3ROM)) != (g_memmode & (MF_INTCXROM|MF_SLOTC3ROM)))
{ {
if (!SW_INTCXROM) if (!SW_INTCXROM)
{ {
@ -2123,6 +2125,9 @@ bool MemOptimizeForModeChanging(WORD programcounter, WORD address)
{ {
if (IsAppleIIeOrAbove(GetApple2Type())) if (IsAppleIIeOrAbove(GetApple2Type()))
{ {
if (programcounter > 0xFFFC) // Prevent out of bounds access!
return false;
// IF THE EMULATED PROGRAM HAS JUST UPDATED THE MEMORY WRITE MODE AND IS // IF THE EMULATED PROGRAM HAS JUST UPDATED THE MEMORY WRITE MODE AND IS
// ABOUT TO UPDATE THE MEMORY READ MODE, HOLD OFF ON ANY PROCESSING UNTIL // ABOUT TO UPDATE THE MEMORY READ MODE, HOLD OFF ON ANY PROCESSING UNTIL
// IT DOES SO. // IT DOES SO.
@ -2136,6 +2141,8 @@ bool MemOptimizeForModeChanging(WORD programcounter, WORD address)
return true; return true;
} }
// TODO: support Saturn in any slot.
// NB. GH#602 asks for any examples of this happening:
if ((address >= 0x80) && (address <= 0x8F) && (programcounter < 0xC000) && // Now: LC if ((address >= 0x80) && (address <= 0x8F) && (programcounter < 0xC000) && // Now: LC
(((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0048D) || // Next: STA $C004(RAMWRTOFF) or STA $C005(RAMWRTON) (((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0048D) || // Next: STA $C004(RAMWRTOFF) or STA $C005(RAMWRTON)
((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0028D))) // or STA $C002(RAMRDOFF) or STA $C003(RAMRDON) ((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0028D))) // or STA $C002(RAMRDOFF) or STA $C003(RAMRDON)
@ -2165,7 +2172,7 @@ void MemAnnunciatorReset(void)
if (IsCopamBase64A(GetApple2Type())) if (IsCopamBase64A(GetApple2Type()))
{ {
SetMemMode(memmode & ~(MF_ALTROM0|MF_ALTROM1)); SetMemMode(g_memmode & ~(MF_ALTROM0|MF_ALTROM1));
UpdatePaging(FALSE); // Initialize=FALSE UpdatePaging(FALSE); // Initialize=FALSE
} }
} }
@ -2208,6 +2215,8 @@ void MemRemoveNoSlotClock(void)
#define SS_YAML_KEY_EXPANSIONROMTYPE "Expansion ROM Type" #define SS_YAML_KEY_EXPANSIONROMTYPE "Expansion ROM Type"
#define SS_YAML_KEY_PERIPHERALROMSLOT "Peripheral ROM Slot" #define SS_YAML_KEY_PERIPHERALROMSLOT "Peripheral ROM Slot"
#define SS_YAML_KEY_ANNUNCIATOR "Annunciator" #define SS_YAML_KEY_ANNUNCIATOR "Annunciator"
#define SS_YAML_KEY_LASTSLOTTOSETMAINMEMLC "Last Slot to Set Main Mem LC"
#define SS_YAML_KEY_MMULCMODE "MMU LC Mode"
// //
@ -2272,16 +2281,20 @@ void MemSaveSnapshot(YamlSaveHelper& yamlSaveHelper)
// Scope so that "Memory" & "Main Memory" are at same indent level // Scope so that "Memory" & "Main Memory" are at same indent level
{ {
YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", MemGetSnapshotStructName().c_str()); YamlSaveHelper::Label state(yamlSaveHelper, "%s:\n", MemGetSnapshotStructName().c_str());
DWORD saveMemMode = memmode; // LC bits
if (IsApple2PlusOrClone(GetApple2Type())) // . For II,II+: set later by slot-0 LC or Saturn
saveMemMode &= ~MF_LANGCARD_MASK; // For II,II+: clear LC bits - set later by slot-0 LC or Saturn // . For //e,//c: set in SS_YAML_KEY_MMULCMODE
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MEMORYMODE, saveMemMode); yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MEMORYMODE, g_memmode & ~MF_LANGCARD_MASK); // Clear LC bits
if (!IsApple2PlusOrClone(GetApple2Type())) // NB. This is set later for II,II+ by slot-0 LC or Saturn if (!IsApple2PlusOrClone(GetApple2Type())) // NB. Thesed are set later for II,II+ by slot-0 LC or Saturn
{
yamlSaveHelper.SaveHexUint32(SS_YAML_KEY_MMULCMODE, g_memmode & MF_LANGCARD_MASK);
yamlSaveHelper.SaveUint(SS_YAML_KEY_LASTRAMWRITE, GetLastRamWrite() ? 1 : 0); yamlSaveHelper.SaveUint(SS_YAML_KEY_LASTRAMWRITE, GetLastRamWrite() ? 1 : 0);
}
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_IOSELECT, IO_SELECT); yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_IOSELECT, IO_SELECT);
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_IOSELECT_INT, INTC8ROM ? 1 : 0); yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_IOSELECT_INT, INTC8ROM ? 1 : 0);
yamlSaveHelper.SaveUint(SS_YAML_KEY_EXPANSIONROMTYPE, (UINT) g_eExpansionRomType); yamlSaveHelper.SaveUint(SS_YAML_KEY_EXPANSIONROMTYPE, (UINT) g_eExpansionRomType);
yamlSaveHelper.SaveUint(SS_YAML_KEY_PERIPHERALROMSLOT, g_uPeripheralRomSlot); yamlSaveHelper.SaveUint(SS_YAML_KEY_PERIPHERALROMSLOT, g_uPeripheralRomSlot);
yamlSaveHelper.SaveUint(SS_YAML_KEY_LASTSLOTTOSETMAINMEMLC, GetCardMgr().GetLanguageCardMgr().GetLastSlotToSetMainMemLC());
for (UINT i=0; i<kNumAnnunciators; i++) for (UINT i=0; i<kNumAnnunciators; i++)
{ {
@ -2326,12 +2339,21 @@ bool MemLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
else else
{ {
UINT uMemMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MEMORYMODE); UINT uMemMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MEMORYMODE);
if (IsApple2PlusOrClone(GetApple2Type())) if (IsApple2PlusOrClone(GetApple2Type()) || unitVersion >= 9)
uMemMode &= ~MF_LANGCARD_MASK; // For II,II+: clear LC bits - set later by slot-0 LC or Saturn uMemMode &= ~MF_LANGCARD_MASK; // For II,II+: clear LC bits - set later by slot-0 LC or Saturn (or some other slot-n Saturn)
// For //e,//c: (>=v9) clear LC bits - set later after reading all cards and we know which card contributes these bits
// For //e (<9): don't clear, as only old versions only supported the IIe LC from the MMU
SetMemMode(uMemMode); SetMemMode(uMemMode);
if (!IsApple2PlusOrClone(GetApple2Type())) if (!IsApple2PlusOrClone(GetApple2Type())) // NB. These are set later for II,II+ by slot-0 LC or Saturn
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 >= 9)
{
UINT LCMemMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_MMULCMODE);
dynamic_cast<LanguageCardUnit&>(GetCardMgr().GetRef(SLOT0)).SetLCMemMode(LCMemMode);
}
SetLastRamWrite(yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTRAMWRITE) ? TRUE : FALSE);
}
} }
if (unitVersion >= 3) if (unitVersion >= 3)
@ -2343,6 +2365,12 @@ bool MemLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
} }
} }
GetCardMgr().GetLanguageCardMgr().SetLastSlotToSetMainMemLCFromSnapshot(SLOT0);
if (unitVersion >= 9)
{
GetCardMgr().GetLanguageCardMgr().SetLastSlotToSetMainMemLCFromSnapshot(yamlLoadHelper.LoadUint(SS_YAML_KEY_LASTSLOTTOSETMAINMEMLC));
}
yamlLoadHelper.PopMap(); yamlLoadHelper.PopMap();
// //

View File

@ -115,6 +115,7 @@ SS_CARDTYPE GetCurrentExpansionMemType(void);
void SetRamWorksMemorySize(UINT pages); void SetRamWorksMemorySize(UINT pages);
UINT GetRamWorksActiveBank(void); UINT GetRamWorksActiveBank(void);
void SetMemMainLanguageCard(LPBYTE ptr, bool bMemMain=false); void SetMemMainLanguageCard(LPBYTE ptr, UINT slot, bool bMemMain=false);
LPBYTE GetCxRomPeripheral(void); LPBYTE GetCxRomPeripheral(void);
UINT GetLastSlotToSetMainMemLC(void);

View File

@ -67,7 +67,8 @@ static YamlHelper yamlHelper;
// v6: Added 'Unit Miscellaneous' for NoSlotClock(NSC) // v6: Added 'Unit Miscellaneous' for NoSlotClock(NSC)
// v7: Extended: joystick (added 'Paddle Inactive Cycle') // v7: Extended: joystick (added 'Paddle Inactive Cycle')
// v8: Added 'Unit Game I/O Connector' for Game I/O Connector // v8: Added 'Unit Game I/O Connector' for Game I/O Connector
#define UNIT_APPLE2_VER 8 // v9: Extended: memory (added 'Last Slot to Set Main Mem LC', 'MMU LC Mode')
#define UNIT_APPLE2_VER 9
#define UNIT_SLOTS_VER 1 #define UNIT_SLOTS_VER 1
@ -292,7 +293,7 @@ static void ParseSlots(YamlLoadHelper& yamlLoadHelper, UINT unitVersion)
SS_CARDTYPE type = Card::GetCardType(card); SS_CARDTYPE type = Card::GetCardType(card);
bool bRes = false; bool bRes = false;
if (slot == 0) if (slot == SLOT0)
{ {
SetExpansionMemType(type); // calls GetCardMgr().Insert() & InsertAux() SetExpansionMemType(type); // calls GetCardMgr().Insert() & InsertAux()
} }

View File

@ -549,6 +549,7 @@ void CtrlReset()
if (!IS_APPLE2) if (!IS_APPLE2)
{ {
// For A][ & A][+, reset doesn't reset the LC switches (UTAII:5-29) // 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?
MemResetPaging(); MemResetPaging();
// For A][ & A][+, reset doesn't reset the video mode (UTAII:4-4) // For A][ & A][+, reset doesn't reset the video mode (UTAII:4-4)

View File

@ -770,9 +770,11 @@ static void RepeatInitialization(void)
GetCardMgr().GetParallelPrinterCard()->SetEnableDumpToRealPrinter(true); GetCardMgr().GetParallelPrinterCard()->SetEnableDumpToRealPrinter(true);
} }
if (g_cmdLine.slotInsert[SLOT3] != CT_Empty && g_cmdLine.slotInsert[SLOT3] == CT_VidHD) // For now just support VidHD in slot 3 if (g_cmdLine.slotInsert[SLOT3] != CT_Empty)
{ {
GetCardMgr().Insert(SLOT3, g_cmdLine.slotInsert[SLOT3]); // NB. Only support Saturn in slot 3, otherwise there's more Config UI to change
if (g_cmdLine.slotInsert[SLOT3] == CT_VidHD || g_cmdLine.slotInsert[SLOT3] == CT_Saturn128K) // For now just support VidHD and Saturn128 in slot 3)
GetCardMgr().Insert(SLOT3, g_cmdLine.slotInsert[SLOT3]);
} }
if (g_cmdLine.slotInsert[SLOT4] != CT_Empty) if (g_cmdLine.slotInsert[SLOT4] != CT_Empty)