diff --git a/source/CPU.cpp b/source/CPU.cpp index 588e4a81..f0a5f199 100644 --- a/source/CPU.cpp +++ b/source/CPU.cpp @@ -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 diff --git a/source/CPU/cpu_general.inl b/source/CPU/cpu_general.inl index bc9586d2..8fcfcb81 100644 --- a/source/CPU/cpu_general.inl +++ b/source/CPU/cpu_general.inl @@ -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); \ diff --git a/source/CmdLine.cpp b/source/CmdLine.cpp index 6fc09274..52bf589b 100644 --- a/source/CmdLine.cpp +++ b/source/CmdLine.cpp @@ -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] { @@ -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); diff --git a/source/CmdLine.h b/source/CmdLine.h index 074815e4..e54d8430 100644 --- a/source/CmdLine.h +++ b/source/CmdLine.h @@ -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); diff --git a/source/Common.h b/source/Common.h index 3f0c93d7..9c6d713d 100644 --- a/source/Common.h +++ b/source/Common.h @@ -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); diff --git a/source/Debugger/Debug.cpp b/source/Debugger/Debug.cpp index a867273f..2986c72e 100644 --- a/source/Debugger/Debug.cpp +++ b/source/Debugger/Debug.cpp @@ -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 diff --git a/source/Memory.cpp b/source/Memory.cpp index 9a077e66..3e6d33e5 100644 --- a/source/Memory.cpp +++ b/source/Memory.cpp @@ -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(GetCardMgr().GetRef(SLOT3)); - memVidHD = vidHD.IsWriteAux() ? memaux : NULL; + if (IsApple2PlusOrClone(GetApple2Type()) || IsIIeWithoutAuxMem()) + { + VidHDCard& vidHD = dynamic_cast(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) diff --git a/source/Memory.h b/source/Memory.h index 4949c9e8..ad3cde1c 100644 --- a/source/Memory.h +++ b/source/Memory.h @@ -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); diff --git a/source/MemoryDefs.h b/source/MemoryDefs.h index 10963b9f..773b92e4 100644 --- a/source/MemoryDefs.h +++ b/source/MemoryDefs.h @@ -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, diff --git a/source/NTSC.cpp b/source/NTSC.cpp index a41b6044..d54388d1 100644 --- a/source/NTSC.cpp +++ b/source/NTSC.cpp @@ -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]; diff --git a/source/NTSC.h b/source/NTSC.h index 8656856d..270d3091 100644 --- a/source/NTSC.h +++ b/source/NTSC.h @@ -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); diff --git a/source/VidHD.cpp b/source/VidHD.cpp index 66a05ba8..5d633629 100644 --- a/source/VidHD.cpp +++ b/source/VidHD.cpp @@ -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(); diff --git a/source/Video.cpp b/source/Video.cpp index a394a750..18034533 100644 --- a/source/Video.cpp +++ b/source/Video.cpp @@ -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" diff --git a/source/Video.h b/source/Video.h index 1d9a8e9b..ba6bf1f0 100644 --- a/source/Video.h +++ b/source/Video.h @@ -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); diff --git a/source/Windows/AppleWin.cpp b/source/Windows/AppleWin.cpp index 67e0a693..941d07e6 100644 --- a/source/Windows/AppleWin.cpp +++ b/source/Windows/AppleWin.cpp @@ -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); diff --git a/source/Windows/WinFrame.cpp b/source/Windows/WinFrame.cpp index e6b52b9a..9aa52013 100644 --- a/source/Windows/WinFrame.cpp +++ b/source/Windows/WinFrame.cpp @@ -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) diff --git a/test/TestCPU6502/TestCPU6502.cpp b/test/TestCPU6502/TestCPU6502.cpp index 5e0ee46f..c598476b 100644 --- a/test/TestCPU6502/TestCPU6502.cpp +++ b/test/TestCPU6502/TestCPU6502.cpp @@ -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