diff --git a/source/LanguageCard.cpp b/source/LanguageCard.cpp index 2c61139c..2ae13fc3 100644 --- a/source/LanguageCard.cpp +++ b/source/LanguageCard.cpp @@ -49,19 +49,28 @@ LanguageCardUnit::~LanguageCardUnit(void) SetMemMainLanguageCard(NULL); } -DWORD LanguageCardUnit::SetPaging(WORD address, DWORD memmode, BOOL& modechanging, bool write) +void LanguageCardUnit::InitializeIO(void) { + RegisterIoHandler(kSlot0, &LanguageCardUnit::IO, &LanguageCardUnit::IO, NULL, NULL, this, NULL); +} + +BYTE __stdcall LanguageCardUnit::IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULONG nExecutedCycles) +{ + LanguageCardUnit* pLC = (LanguageCardUnit*) MemGetSlotParameters(kSlot0); + + DWORD memmode = GetMemMode(); + DWORD lastmemmode = memmode; memmode &= ~(MF_BANK2 | MF_HIGHRAM); - if (!(address & 8)) + if (!(uAddr & 8)) memmode |= MF_BANK2; - if (((address & 2) >> 1) == (address & 1)) + if (((uAddr & 2) >> 1) == (uAddr & 1)) memmode |= MF_HIGHRAM; - if (address & 1) // GH#392 + if (uAddr & 1) // GH#392 { - if (!write && GetLastRamWrite()) + if (!bWrite && pLC->GetLastRamWrite()) { memmode |= MF_WRITERAM; // UTAIIe:5-23 } @@ -71,9 +80,36 @@ DWORD LanguageCardUnit::SetPaging(WORD address, DWORD memmode, BOOL& modechangin memmode &= ~MF_WRITERAM; // UTAIIe:5-23 } - SetLastRamWrite( ((address & 1) && !write) ); // UTAIIe:5-23 + pLC->SetLastRamWrite( ((uAddr & 1) && !bWrite) ); // UTAIIe:5-23 + SetMemMode(memmode); - return memmode; + // + + if (IS_APPLE2E) + { + // 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 + // IT DOES SO. + // + // NB. A 6502 interrupt occurring between these memory write & read updates could lead to incorrect behaviour. + // - although any data-race is probably a bug in the 6502 code too. + if ((PC < 0xC000) && + (((*(LPDWORD)(mem+PC) & 0x00FFFEFF) == 0x00C0048D) || // Next: STA $C004 or STA $C005 + ((*(LPDWORD)(mem+PC) & 0x00FFFEFF) == 0x00C0028D))) // Next: STA $C002 or STA $C003 + { + SetModeChanging(1); + return bWrite ? 0 : MemReadFloatingBus(nExecutedCycles); + } + } + + // IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND + // WRITE TABLES. + if (lastmemmode != memmode) + { + MemUpdatePaging(0); // Initialize=0 + } + + return bWrite ? 0 : MemReadFloatingBus(nExecutedCycles); } //------------------------------------- @@ -94,7 +130,7 @@ LanguageCardSlot0::~LanguageCardSlot0(void) // static const UINT kUNIT_LANGUAGECARD_VER = 1; -static const UINT kSLOT_LANGUAGECARD = 0; +static const UINT kSLOT_LANGUAGECARD = LanguageCardUnit::kSlot0; #define SS_YAML_VALUE_CARD_LANGUAGECARD "Language Card" @@ -188,7 +224,9 @@ Saturn128K::Saturn128K(UINT banks) for (UINT i=0; im_uSaturnTotalBanks); + if (!pLC->m_uSaturnTotalBanks) + return bWrite ? 0 : MemReadFloatingBus(nExecutedCycles); + + bool bBankChanged = false; + DWORD memmode=0, lastmemmode=0; + + if (uAddr & (1<<2)) { - m_uSaturnActiveBank = 0 // Saturn 128K Language Card Bank 0 .. 7 - | (address >> 1) & 4 - | (address >> 0) & 3; + pLC->m_uSaturnActiveBank = 0 // Saturn 128K Language Card Bank 0 .. 7 + | (uAddr >> 1) & 4 + | (uAddr >> 0) & 3; - if (m_uSaturnActiveBank >= m_uSaturnTotalBanks) + if (pLC->m_uSaturnActiveBank >= pLC->m_uSaturnTotalBanks) { // EG. Run RAMTEST128K tests on a Saturn 64K card // TODO: Saturn::UpdatePaging() should deal with this case: // . Technically read floating-bus, write to nothing // . But the mem cache doesn't support floating-bus reads from non-I/O space - m_uSaturnActiveBank = m_uSaturnTotalBanks-1; // FIXME: just prevent crash for now! + pLC->m_uSaturnActiveBank = pLC->m_uSaturnTotalBanks-1; // FIXME: just prevent crash for now! } - SetMemMainLanguageCard( m_aSaturnBanks[ m_uSaturnActiveBank ] ); - - modechanging = 1; + SetMemMainLanguageCard( pLC->m_aSaturnBanks[ pLC->m_uSaturnActiveBank ] ); + bBankChanged = true; } else { + memmode = GetMemMode(); + lastmemmode = memmode; memmode &= ~(MF_BANK2 | MF_HIGHRAM); - if (!(address & 8)) + if (!(uAddr & 8)) memmode |= MF_BANK2; - if (((address & 2) >> 1) == (address & 1)) + if (((uAddr & 2) >> 1) == (uAddr & 1)) memmode |= MF_HIGHRAM; - if (address & 1 && GetLastRamWrite()) // Saturn differs from Apple's 16K LC: any access (LC is read-only) + if (uAddr & 1 && pLC->GetLastRamWrite())// Saturn differs from Apple's 16K LC: any access (LC is read-only) memmode |= MF_WRITERAM; else memmode &= ~MF_WRITERAM; - SetLastRamWrite(address & 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); } - return memmode; + // NB. Unlike LC, no need to check if next opcode is STA $C002-5, as Saturn is not for //e + + // IF THE MEMORY PAGING MODE HAS CHANGED, UPDATE OUR MEMORY IMAGES AND + // WRITE TABLES. + if ((lastmemmode != memmode) || bBankChanged) + { + MemUpdatePaging(0); // Initialize=0 + } + + return bWrite ? 0 : MemReadFloatingBus(nExecutedCycles); } // static const UINT kUNIT_SATURN_VER = 1; -static const UINT kSLOT_SATURN = 0; +static const UINT kSLOT_SATURN = LanguageCardUnit::kSlot0; #define SS_YAML_VALUE_CARD_SATURN128 "Saturn 128" diff --git a/source/LanguageCard.h b/source/LanguageCard.h index 7134b1d5..f7cfaaa5 100644 --- a/source/LanguageCard.h +++ b/source/LanguageCard.h @@ -10,7 +10,7 @@ public: LanguageCardUnit(void); virtual ~LanguageCardUnit(void); - virtual DWORD SetPaging(WORD address, DWORD memmode, BOOL& modechanging, bool write); + virtual void InitializeIO(void); virtual void SetMemorySize(UINT banks) {} // No-op for //e and slot-0 16K LC virtual UINT GetActiveBank(void) { return 0; } // Always 0 as only 1x 16K bank virtual void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper) { _ASSERT(0); } // Not used for //e @@ -20,7 +20,10 @@ public: void SetLastRamWrite(BOOL count) { m_uLastRamWrite = count; } SS_CARDTYPE GetMemoryType(void) { return m_type; } + static BYTE __stdcall IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULONG nExecutedCycles); + static const UINT kMemModeInitialState; + static const UINT kSlot0 = 0; protected: SS_CARDTYPE m_type; @@ -49,10 +52,10 @@ protected: void SaveLCState(class YamlSaveHelper& yamlSaveHelper); void LoadLCState(class YamlLoadHelper& yamlLoadHelper); + LPBYTE m_pMemory; + private: std::string GetSnapshotMemStructName(void); - - LPBYTE m_pMemory; }; // @@ -65,12 +68,14 @@ public: Saturn128K(UINT banks); virtual ~Saturn128K(void); - virtual DWORD SetPaging(WORD address, DWORD memmode, BOOL& modechanging, bool write); + virtual void InitializeIO(void); virtual void SetMemorySize(UINT banks); virtual UINT GetActiveBank(void); virtual void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper); virtual bool LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, UINT slot, UINT version); + static BYTE __stdcall IO(WORD PC, WORD uAddr, BYTE bWrite, BYTE uValue, ULONG nExecutedCycles); + // "The boards consist of 16K banks of memory (4 banks for the 64K board, 8 banks for the 128K), accessed one at a time" - Ref: "64K/128K RAM BOARD", Saturn Systems, Ch.1 Introduction(pg-5) static const UINT kMaxSaturnBanks = 8; // 8 * 16K = 128K static std::string GetSnapshotCardName(void); diff --git a/source/Memory.cpp b/source/Memory.cpp index 4c164140..da8caa25 100644 --- a/source/Memory.cpp +++ b/source/Memory.cpp @@ -229,7 +229,7 @@ static void ResetDefaultMachineMemTypes(void) g_MemTypeAppleIIe = CT_Extended80Col; } -// Called from MemInitialize(), MemLoadSnapshot() +// Called from MemInitialize(), MemLoadSnapshot(), MemSetSnapshot_v1() static void SetExpansionMemTypeDefault(void) { SS_CARDTYPE defaultType = IsApple2Original(GetApple2Type()) ? g_MemTypeAppleII @@ -280,25 +280,28 @@ void SetExpansionMemType(const SS_CARDTYPE type) newSlotAuxCard = type; } + g_Slot0 = newSlot0Card; + g_SlotAux = newSlotAuxCard; +} + +void CreateLanguageCard(void) +{ + delete g_pLanguageCard; + g_pLanguageCard = NULL; + if (IsApple2PlusOrClone(GetApple2Type())) { - delete g_pLanguageCard; - - if (newSlot0Card == CT_Saturn128K) + if (g_Slot0 == CT_Saturn128K) g_pLanguageCard = new Saturn128K(g_uSaturnBanksFromCmdLine); - else if (newSlot0Card == CT_LanguageCard) + else if (g_Slot0 == CT_LanguageCard) g_pLanguageCard = new LanguageCardSlot0; else g_pLanguageCard = NULL; } else { - delete g_pLanguageCard; g_pLanguageCard = new LanguageCardUnit; } - - g_Slot0 = newSlot0Card; - g_SlotAux = newSlotAuxCard; } SS_CARDTYPE GetCurrentExpansionMemType(void) @@ -969,6 +972,11 @@ static bool IsCardInSlot(const UINT uSlot) //=========================================================================== +void SetModeChanging(BOOL value) +{ + modechanging = value; +} + DWORD GetMemMode(void) { return memmode; @@ -1037,6 +1045,8 @@ void MemUpdatePaging(BOOL initialize) static void UpdatePaging(BOOL initialize) { + modechanging = 0; + // SAVE THE CURRENT PAGING SHADOW TABLE LPBYTE oldshadow[256]; if (!initialize) @@ -1437,7 +1447,7 @@ void MemInitialize() SetExpansionMemTypeDefault(); #ifdef RAMWORKS - if (GetCurrentExpansionMemType() == CT_RamWorksIII) + if (g_SlotAux == CT_RamWorksIII) { // allocate memory for RAMWorks III - up to 8MB g_uActiveBank = 0; @@ -1452,6 +1462,8 @@ void MemInitialize() // + CreateLanguageCard(); + MemInitializeROM(); MemInitializeCustomF8ROM(); MemInitializeIO(); @@ -1580,9 +1592,10 @@ void MemInitializeCustomF8ROM(void) { memcpy(memrom, OldRom, Apple2RomSize); // ROM at $D000...$FFFF bRes = FALSE; - // NB. Keep g_hCustomRomF8 handle open - so that any next restart can load it again } + // NB. If succeeded, then keep g_hCustomRomF8 handle open - so that any next restart can load it again + if (!bRes) { MessageBox( g_hFrameWindow, "Failed to read custom F8 rom", TEXT("AppleWin Error"), MB_OK ); @@ -1616,8 +1629,10 @@ void MemInitializeIO(void) { InitIoHandlers(); - const UINT uSlot = 0; - RegisterIoHandler(uSlot, MemSetPaging, MemSetPaging, NULL, NULL, NULL, NULL); + if (g_pLanguageCard) + g_pLanguageCard->InitializeIO(); + else + RegisterIoHandler(LanguageCardUnit::kSlot0, IO_Null, IO_Null, NULL, NULL, NULL, NULL); // TODO: Cleanup peripheral setup!!! PrintLoadRom(pCxRomPeripheral, 1); // $C100 : Parallel printer f/w @@ -1904,12 +1919,7 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE #endif // DETERMINE THE NEW MEMORY PAGING MODE. - if (address >= 0x80 && address <= 0x8F) - { - if (!IS_APPLE2 || (IsApple2PlusOrClone(GetApple2Type()) && g_Slot0 != CT_Empty)) - SetMemMode( g_pLanguageCard->SetPaging(address, memmode, modechanging, write ? true : false) ); - } - else if (!IS_APPLE2) + if (!IS_APPLE2) { switch (address) { @@ -1943,24 +1953,19 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE } } - if (GetCurrentExpansionMemType() != CT_Saturn128K) // TODO: Not sure this optimisation is valid for Saturn, so skip it for now + if (IS_APPLE2E) { - // IF THE EMULATED PROGRAM HAS JUST UPDATE 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 // IT DOES SO. // // NB. A 6502 interrupt occurring between these memory write & read updates could lead to incorrect behaviour. // - although any data-race is probably a bug in the 6502 code too. if ((address >= 4) && (address <= 5) && - ((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0028D)) { + ((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0028D)) // Next: STA $C002 or STA $C003 + { modechanging = 1; - return write ? 0 : MemReadFloatingBus(1, nExecutedCycles); - } - if ((address >= 0x80) && (address <= 0x8F) && (programcounter < 0xC000) && - (((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0048D) || - ((*(LPDWORD)(mem+programcounter) & 0x00FFFEFF) == 0x00C0028D))) { - modechanging = 1; - return write ? 0 : MemReadFloatingBus(1, nExecutedCycles); + return 0; // For $C004 & $C005: entry to this func is always via a write to $C004 or $C005 } } @@ -1968,8 +1973,6 @@ BYTE __stdcall MemSetPaging(WORD programcounter, WORD address, BYTE write, BYTE // WRITE TABLES. if ((lastmemmode != memmode) || modechanging) { - modechanging = 0; - // 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))) { @@ -2027,6 +2030,7 @@ void MemSetSnapshot_v1(const DWORD MemMode, const BOOL LastWriteRam, const BYTE* ResetDefaultMachineMemTypes(); g_MemTypeAppleII = CT_LanguageCard; // SSv1 doesn't save machine type - so if current machine is Apple II then give it 64K + LC SetExpansionMemTypeDefault(); + CreateLanguageCard(); // Create LC here, as for SSv1 there is no slot-0 state SetMemMode(MemMode ^ MF_INTCXROM); // Convert from SLOTCXROM to INTCXROM SetLastRamWrite(LastWriteRam); @@ -2037,7 +2041,6 @@ void MemSetSnapshot_v1(const DWORD MemMode, const BOOL LastWriteRam, const BYTE* // - modechanging = 0; // NB. MemUpdatePaging(TRUE) called at end of Snapshot_LoadState_v1() UpdatePaging(1); // Initialize=1 } @@ -2133,8 +2136,12 @@ bool MemLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version) // Create default LC type for AppleII machine (do prior to loading saved LC state) ResetDefaultMachineMemTypes(); if (version == 1) - g_MemTypeAppleII = CT_LanguageCard; // version=1: original Apple II always had a LC + g_MemTypeAppleII = CT_LanguageCard; // version=1: original Apple II always has a LC + else + g_MemTypeAppleIIPlus = CT_Empty; // version=2+: Apple II/II+ initially start with slot-0 empty SetExpansionMemTypeDefault(); + CreateLanguageCard(); // Create default LC now for: (a) //e which has no slot-0 LC (so this is final) + // (b) II/II+ which get re-created later if slot-0 has a card // @@ -2181,7 +2188,6 @@ bool MemLoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version) // - modechanging = 0; // NB. MemUpdatePaging(TRUE) called at end of Snapshot_LoadState_v2() UpdatePaging(1); // Initialize=1 (Still needed, even with call to MemUpdatePaging() - why?) // TC-TODO: At this point, the cards haven't been loaded, so the card's expansion ROM is unknown - so pointless(?) calling this now diff --git a/source/Memory.h b/source/Memory.h index ff81ddab..6538ae01 100644 --- a/source/Memory.h +++ b/source/Memory.h @@ -66,6 +66,7 @@ LPBYTE MemGetBankPtr(const UINT nBank); LPBYTE MemGetCxRomPeripheral(); DWORD GetMemMode(void); void SetMemMode(DWORD memmode); +void SetModeChanging(BOOL value); bool MemIsAddrCodeMemory(const USHORT addr); void MemInitialize (); void MemInitializeROM(void); @@ -92,6 +93,7 @@ BYTE __stdcall MemSetPaging(WORD pc, WORD addr, BYTE bWrite, BYTE d, ULONG nExec enum SS_CARDTYPE; void SetExpansionMemType(const SS_CARDTYPE type); SS_CARDTYPE GetCurrentExpansionMemType(void); +void CreateLanguageCard(void); void SetRamWorksMemorySize(UINT pages); UINT GetRamWorksActiveBank(void); diff --git a/source/SaveState.cpp b/source/SaveState.cpp index c61cabf5..ca2fed79 100644 --- a/source/SaveState.cpp +++ b/source/SaveState.cpp @@ -371,7 +371,6 @@ static void ParseSlots(YamlLoadHelper& yamlLoadHelper, UINT version) if (!yamlLoadHelper.GetSubMap(std::string(SS_YAML_KEY_STATE))) throw std::string(SS_YAML_KEY_UNIT ": Expected sub-map name: " SS_YAML_KEY_STATE); - bool bIsCardSupported = true; SS_CARDTYPE type = CT_Empty; bool bRes = false; @@ -420,21 +419,22 @@ static void ParseSlots(YamlLoadHelper& yamlLoadHelper, UINT version) { type = CT_LanguageCard; SetExpansionMemType(type); + CreateLanguageCard(); bRes = GetLanguageCard()->LoadSnapshot(yamlLoadHelper, slot, version); } else if (card == Saturn128K::GetSnapshotCardName()) { type = CT_Saturn128K; SetExpansionMemType(type); + CreateLanguageCard(); bRes = GetLanguageCard()->LoadSnapshot(yamlLoadHelper, slot, version); } else { - bIsCardSupported = false; throw std::string("Slots: Unknown card: " + card); // todo: don't throw - just ignore & continue } - if (bRes && bIsCardSupported) + if (bRes) { m_ConfigNew.m_Slot[slot] = type; }