// ===================================================================================== // Copyright (c) 2021-2023 Dave Bernazzani (wavemotion-dave) // // Copying and distribution of this emulator, its source code and associated // readme files, with or without modification, are permitted in any medium without // royalty provided the this copyright notice is used and wavemotion-dave (NINTV-DS) // and Kyle Davis (BLISS) are thanked profusely. // // The NINTV-DS emulator is offered as-is, without any warranty. // ===================================================================================== #include #include #include "MemoryBus.h" #include "../ds_tools.h" UINT16 MAX_READ_OVERLAPPED_MEMORIES = 2; UINT16 MAX_WRITE_OVERLAPPED_MEMORIES = 3; // ---------------------------------------------------------------------------------------------- // We use this class and single object to fill all unused memory locations in the memory map. // Returns 0xFFFF on all access as a real intellivision would with unused memory regions. // ---------------------------------------------------------------------------------------------- class UnusedMemory : public Memory { public: UnusedMemory() {}; virtual ~UnusedMemory() {} virtual void reset() {} UINT8 getByteWidth() {return 2;} UINT16 getReadSize() {return 2;} UINT16 getReadAddress() {return 0;} UINT16 getReadAddressMask() {return 0xFFFF;} inline virtual UINT16 peek(UINT16 location) {return 0xFFFF;} UINT16 getWriteSize() {return 2;} UINT16 getWriteAddress() {return 0;} UINT16 getWriteAddressMask() {return 0xFFFF;} virtual void poke(UINT16 location, UINT16 value) {} virtual void poke_cheat(UINT16 location, UINT16 value) {return;} } MyUnusedMemory; // ------------------------------------------------------------------------------- // This is a serious resource hog... it's multiple 16-bit 64k arrays take // up a significant bit of our RAM... the max overlapped memories is what // soaks up quite a bit of main RAM. We limit this for older DS hardware // memories per address location which is sufficient provided we are only // loading normal ROMs into a stock intellivision with, at most, an intellivoice // or the JLP cart as the only peripherals... still, this is a strain on the // older DS-LITE/PHAT. The original BLISS core allowed 16 overlapping memory // regions (to handle page flipping) which does fit into the DSi but is too // large for the original DS-LITE/PHAT so for older hardware, we strip down // to the bare essentials. // ------------------------------------------------------------------------------- UINT32 *overlappedMemoryPool = NULL; MemoryBus::MemoryBus() { // ------------------------------------------------------------------------------------- // We swap in a larger memory model for the DSi to handle really complex page flipping // ------------------------------------------------------------------------------------- if (isDSiMode()) { MAX_READ_OVERLAPPED_MEMORIES = 16; // Good enough for any page-flipping game. This is massive! MAX_WRITE_OVERLAPPED_MEMORIES = 17; // Need one extra here to handle the GRAM mirrors up in odd splaces in ROM } else { MAX_READ_OVERLAPPED_MEMORIES = 2; // Good enough for almost all games except very large page-flipping games MAX_WRITE_OVERLAPPED_MEMORIES = 3; // Need one extra here to handle the GRAM mirrors up in odd splaces in ROM } UINT32 size = 1 << (sizeof(UINT16) << 3); UINT32 i; writeableMemoryCounts = new UINT8[size]; memset(writeableMemoryCounts, 0, sizeof(UINT8) * size); writeableMemorySpace = new Memory**[size]; // --------------------------------------------------------------------------------------------------------------------------- // We do this rather than allocate piecemeal so we avoid malloc overhead and extra bytes padded (saves almost 500K on DS) // On the DS with 3 overlapped memories (enough for most games), this is still 1.5MB of memory (out of the 3.5MB available) // On the DSi with a full 16 overlapped memories (enough for any game), this is a whopping 8MB (out of the 15.5MB available) // --------------------------------------------------------------------------------------------------------------------------- overlappedMemoryPool = new UINT32[size*(MAX_READ_OVERLAPPED_MEMORIES+MAX_WRITE_OVERLAPPED_MEMORIES)]; for (i = 0; i < size; i++) { writeableMemorySpace[i] = (Memory **)overlappedMemoryPool; for (int j=0; jreset(); } void MemoryBus::addMemory(Memory* m) { UINT8 bitCount = sizeof(UINT16)<<3; UINT8 bitShifts[sizeof(UINT16)<<3]; UINT8 i; //get the important info UINT16 readSize = m->getReadSize(); UINT16 readAddress = m->getReadAddress(); UINT16 readAddressMask = m->getReadAddressMask(); UINT16 writeSize = m->getWriteSize(); UINT16 writeAddress = m->getWriteAddress(); UINT16 writeAddressMask = m->getWriteAddressMask(); if (mappedMemoryCount >= MAX_MAPPED_MEMORIES) { FatalError("GAME TOO COMPLEX - MAX MEMORIES"); return; } //add all of the readable locations, if any if (readAddressMask != 0) { UINT8 zeroCount = 0; for (i = 0; i < bitCount; i++) { if (!(readAddressMask & (1<= MAX_READ_OVERLAPPED_MEMORIES) { FatalError("ERROR MAX READABLE MEM OVERLAP"); return; } readableMemorySpace[k][memCount] = m; readableMemoryCounts[k]++; } } } //add all of the writeable locations, if any if (writeAddressMask != 0) { UINT8 zeroCount = 0; for (i = 0; i < bitCount; i++) { if (!(writeAddressMask & (1<= MAX_WRITE_OVERLAPPED_MEMORIES) { FatalError("ERROR MAX WRITEABLE MEM OVERLAP"); return; } writeableMemorySpace[k][memCount] = m; writeableMemoryCounts[k]++; } } } //add it to our list of memories mappedMemories[mappedMemoryCount] = m; mappedMemoryCount++; } void MemoryBus::removeMemory(Memory* m) { UINT8 bitCount = sizeof(UINT16)<<3; UINT8 bitShifts[sizeof(UINT16)<<3]; UINT32 i; //get the important info UINT16 readSize = m->getReadSize(); UINT16 readAddress = m->getReadAddress(); UINT16 readAddressMask = m->getReadAddressMask(); UINT16 writeSize = m->getWriteSize(); UINT16 writeAddress = m->getWriteAddress(); UINT16 writeAddressMask = m->getWriteAddressMask(); //add all of the readable locations, if any if (readAddressMask != 0) { UINT8 zeroCount = 0; for (i = 0; i < bitCount; i++) { if (!(readAddressMask & (1<peek(location); } return value; } // --------------------------------------------------------------------------------------- // Poke is less common than peek... so we're less concerned about optimization here. // --------------------------------------------------------------------------------------- ITCM_CODE void MemoryBus::poke(UINT16 location, UINT16 value) { UINT8 numMemories = writeableMemoryCounts[location]; for (UINT16 i = 0; i < numMemories; i++) { writeableMemorySpace[location][i]->poke(location, value); } // For the lower 4K ... keep the "fast memory" updated if (location < 0x1000) { *((UINT16 *)0x06860000 + location) = value; } } // --------------------------------------------------------------------------------------- // Poke Cheat Codes does not need any optimization - only happens once after ROM load. // We allow poke to both readable and writable memory spaces - most of the time we are // modifying a ROM location to provide some special cheat effect. We don't need to // update the "fast memory" as the cheats are applied post ROM load but pre "fast buffer". // --------------------------------------------------------------------------------------- void MemoryBus::poke_cheat(UINT16 location, UINT16 value) { UINT8 numMemories = readableMemoryCounts[location]; for (UINT16 i = 0; i < numMemories; i++) { readableMemorySpace[location][i]->poke_cheat(location, value); } numMemories = writeableMemoryCounts[location]; for (UINT16 i = 0; i < numMemories; i++) { writeableMemorySpace[location][i]->poke_cheat(location, value); } }