mirror of
https://github.com/DaedalusX64/daedalus.git
synced 2025-04-02 10:21:48 -04:00
892 lines
25 KiB
C++
892 lines
25 KiB
C++
/*
|
|
Copyright (C) 2001 StrmnNrmn
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
// Various stuff to map an address onto the correct memory region
|
|
|
|
#include "stdafx.h"
|
|
|
|
|
|
#include "Core/CPU.h"
|
|
#include "Core/DMA.h"
|
|
#include "Core/FlashMem.h"
|
|
#include "Core/Interrupt.h"
|
|
#include "Core/Memory.h"
|
|
#include "Core/ROM.h"
|
|
#include "Core/ROMBuffer.h"
|
|
#include "Config/ConfigOptions.h"
|
|
#include "Debug/DBGConsole.h"
|
|
#include "Debug/DebugLog.h"
|
|
#include "Debug/DebugLog.h"
|
|
#include "Debug/Dump.h" // Dump_GetSaveDirectory()
|
|
#include "Ultra/ultra_R4300.h"
|
|
#include "HLEAudio/AudioPlugin.h"
|
|
#include "HLEGraphics/GraphicsPlugin.h"
|
|
|
|
static const u32 kMaximumMemSize = MEMORY_8_MEG;
|
|
|
|
#undef min
|
|
|
|
#ifdef DAEDALUS_LOG
|
|
static void DisplayVIControlInfo( u32 control_reg );
|
|
#endif
|
|
|
|
// VirtualAlloc is only supported on Win32 architectures
|
|
#ifdef DAEDALUS_W32
|
|
#define DAED_USE_VIRTUAL_ALLOC
|
|
#endif
|
|
|
|
void MemoryUpdateSPStatus( u32 flags );
|
|
void MemoryUpdateMI( u32 value );
|
|
static void MemoryUpdateDP( u32 value );
|
|
static void MemoryModeRegMI( u32 value );
|
|
static void MemoryUpdatePI( u32 value );
|
|
static void MemoryUpdatePIF();
|
|
|
|
static void Memory_InitTables();
|
|
|
|
const u32 MemoryRegionSizes[NUM_MEM_BUFFERS] =
|
|
{
|
|
0x04, // This seems enough (Salvy)
|
|
kMaximumMemSize, // RD_RAM
|
|
0x2000, // SP_MEM
|
|
|
|
0x40, // PIF_RAM
|
|
|
|
//1*1024*1024, // RD_REG (Don't need this much really)?
|
|
0x30, // RD_REG0
|
|
|
|
0x20, // SP_REG
|
|
0x08, // SP_PC_REG
|
|
0x20, // DPC_REG
|
|
0x10, // MI_REG
|
|
0x38, // VI_REG
|
|
0x18, // AI_REG
|
|
0x34, // PI_REG
|
|
0x20, // RI_REG
|
|
0x1C, // SI_REG
|
|
|
|
0x20000, // SAVE
|
|
0x20000 // MEMPACK
|
|
};
|
|
|
|
u32 gRamSize = kMaximumMemSize; // Size of emulated RAM
|
|
|
|
#ifdef DAEDALUS_PROFILE_EXECUTION
|
|
u32 gTLBReadHit = 0;
|
|
u32 gTLBWriteHit = 0;
|
|
#endif
|
|
|
|
#ifdef DAED_USE_VIRTUAL_ALLOC
|
|
static void * gMemBase = nullptr; // Virtual memory base
|
|
#endif
|
|
|
|
// ROM write support
|
|
u32 g_pWriteRom;
|
|
bool g_RomWritten;
|
|
|
|
// Ram base, offset by 0x80000000 and 0xa0000000
|
|
u8 * g_pu8RamBase_8000 = nullptr;
|
|
//u8 * g_pu8RamBase_A000 = nullptr;
|
|
|
|
MemFuncRead g_MemoryLookupTableRead[0x4000];
|
|
MemFuncWrite g_MemoryLookupTableWrite[0x4000];
|
|
void * g_pMemoryBuffers[NUM_MEM_BUFFERS];
|
|
|
|
|
|
#include "Memory_Read.inl"
|
|
#include "Memory_WriteValue.inl"
|
|
|
|
#ifndef DAEDALUS_SILENT
|
|
#include "Memory_ReadInternal.inl"
|
|
#endif
|
|
|
|
bool Memory_Init()
|
|
{
|
|
gRamSize = kMaximumMemSize;
|
|
|
|
#ifdef DAED_USE_VIRTUAL_ALLOC
|
|
gMemBase = VirtualAlloc(0, 512*1024*1024, MEM_RESERVE, PAGE_READWRITE);
|
|
if (gMemBase == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uintptr_t base = reinterpret_cast<uintptr_t>(gMemBase);
|
|
|
|
g_pMemoryBuffers[ MEM_RD_RAM ] = (u8*)VirtualAlloc( (void*)(base+0x00000000), 8*1024*1024,MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_SP_MEM ] = (u8*)VirtualAlloc( (void*)(base+0x04000000), 0x2000, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_RD_REG0 ] = (u8*)VirtualAlloc( (void*)(base+0x03F00000), 0x30, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_SP_REG ] = (u8*)VirtualAlloc( (void*)(base+0x04040000), 0x20, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_SP_PC_REG ] = (u8*)VirtualAlloc( (void*)(base+0x04080000), 0x08, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_DPC_REG ] = (u8*)VirtualAlloc( (void*)(base+0x04100000), 0x20, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_MI_REG ] = (u8*)VirtualAlloc( (void*)(base+0x04300000), 0x10, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_VI_REG ] = (u8*)VirtualAlloc( (void*)(base+0x04400000), 0x38, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_AI_REG ] = (u8*)VirtualAlloc( (void*)(base+0x04500000), 0x18, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_PI_REG ] = (u8*)VirtualAlloc( (void*)(base+0x04600000), 0x34, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_RI_REG ] = (u8*)VirtualAlloc( (void*)(base+0x04700000), 0x20, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_SI_REG ] = (u8*)VirtualAlloc( (void*)(base+0x04800000), 0x1C, MEM_COMMIT, PAGE_READWRITE );
|
|
//cartDom2 = (u8*)VirtualAlloc( (void*)(base+0x05000000), 0x10000, MEM_COMMIT, PAGE_READWRITE );
|
|
//cartDom1 = (u8*)VirtualAlloc( (void*)(base+0x06000000), 0x10000, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_SAVE ] = (u8*)VirtualAlloc( (void*)(base+0x08000000), 0x20000, MEM_COMMIT, PAGE_READWRITE );
|
|
//g_pMemoryBuffers[MEM_CARTROM ] = (u8*)VirtualAlloc( (void*)(base+0x10000000), cart_size, MEM_COMMIT, PAGE_READWRITE);
|
|
g_pMemoryBuffers[ MEM_PIF_RAM ] = (u8*)VirtualAlloc( (void*)(base+0x1FC00000), 0x40, MEM_COMMIT, PAGE_READWRITE );
|
|
//cartDom4 = (u8*)VirtualAlloc( (void*)(base+0x1FD00000), 0x10000, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_MEMPACK ] = (u8*)VirtualAlloc( nullptr, 0x20000, MEM_COMMIT, PAGE_READWRITE );
|
|
g_pMemoryBuffers[ MEM_UNUSED ] = new u8[ MemoryRegionSizes[MEM_UNUSED] ];
|
|
|
|
#else
|
|
//u32 count = 0;
|
|
for (u32 m = 0; m < NUM_MEM_BUFFERS; m++)
|
|
{
|
|
u32 region_size = MemoryRegionSizes[m];
|
|
// Skip zero sized areas. An example of this is the cart rom
|
|
if (region_size > 0)
|
|
{
|
|
//count+=region_size;
|
|
g_pMemoryBuffers[m] = new u8[region_size];
|
|
//g_pMemoryBuffers[m] = Memory_AllocRegion(region_size);
|
|
|
|
if (g_pMemoryBuffers[m] == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Necessary?
|
|
memset(g_pMemoryBuffers[m], 0, region_size);
|
|
/*if (region_size < 0x100) // dirty, check if this is a I/O range
|
|
{
|
|
g_pMemoryBuffers[m] = MAKE_UNCACHED_PTR(g_pMemoryBuffers[m]);
|
|
}*/
|
|
}
|
|
}
|
|
//printf("%d bytes used of memory\n",count);
|
|
#endif
|
|
|
|
g_pu8RamBase_8000 = ((u8*)g_pMemoryBuffers[MEM_RD_RAM]) - 0x80000000;
|
|
//g_pu8RamBase_A000 = ((u8*)g_pMemoryBuffers[MEM_RD_RAM]) - 0xa0000000;
|
|
//g_pu8RamBase_A000 = ((u8*)MAKE_UNCACHED_PTR(g_pMemoryBuffers[MEM_RD_RAM])) - 0xa0000000;
|
|
|
|
g_RomWritten = false;
|
|
|
|
|
|
Memory_InitTables();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Memory_Fini(void)
|
|
{
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
DPF(DEBUG_MEMORY, "Freeing Memory");
|
|
#endif
|
|
#ifdef DAED_USE_VIRTUAL_ALLOC
|
|
|
|
//
|
|
// We have to free this buffer separately
|
|
//
|
|
if (g_pMemoryBuffers[MEM_UNUSED])
|
|
{
|
|
delete [] reinterpret_cast< u8 * >( g_pMemoryBuffers[MEM_UNUSED] );
|
|
g_pMemoryBuffers[MEM_UNUSED] = nullptr;
|
|
}
|
|
|
|
VirtualFree( gMemBase, 0, MEM_RELEASE );
|
|
gMemBase = nullptr;
|
|
|
|
#else
|
|
for (u32 m = 0; m < NUM_MEM_BUFFERS; m++)
|
|
{
|
|
if (g_pMemoryBuffers[m] != nullptr)
|
|
{
|
|
delete [] (u8*)(g_pMemoryBuffers[m]);
|
|
g_pMemoryBuffers[m] = nullptr;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
g_pu8RamBase_8000 = nullptr;
|
|
//g_pu8RamBase_A000 = nullptr;
|
|
|
|
memset( g_pMemoryBuffers, 0, sizeof( g_pMemoryBuffers ) );
|
|
}
|
|
|
|
bool Memory_Reset()
|
|
{
|
|
u32 main_mem = g_ROM.settings.ExpansionPakUsage != PAK_UNUSED ? MEMORY_8_MEG : MEMORY_4_MEG;
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
DBGConsole_Msg(0, "Reseting Memory - %d MB", main_mem/(1024*1024));
|
|
#endif
|
|
if (main_mem > kMaximumMemSize)
|
|
{
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
DBGConsole_Msg( 0, "Memory_Reset: Can't reset with more than %dMB ram", kMaximumMemSize / (1024*1024) );
|
|
#endif
|
|
main_mem = kMaximumMemSize;
|
|
}
|
|
|
|
// Set memory size to specified value
|
|
// Note that we do not reallocate the memory - we always have 8Mb!
|
|
gRamSize = main_mem;
|
|
|
|
// Reinit the tables - this will update the RAM pointers
|
|
Memory_InitTables();
|
|
|
|
// Required - virtual alloc gives zeroed memory but this is also used when resetting
|
|
// Clear memory
|
|
for (u32 i = 0; i < NUM_MEM_BUFFERS; i++)
|
|
{
|
|
if (g_pMemoryBuffers[i])
|
|
{
|
|
memset(g_pMemoryBuffers[i], 0, MemoryRegionSizes[i]);
|
|
}
|
|
}
|
|
|
|
gDMAUsed = false;
|
|
return true;
|
|
}
|
|
|
|
void Memory_Cleanup()
|
|
{
|
|
}
|
|
|
|
static void Memory_Tlb_Hack()
|
|
{
|
|
bool RomBaseKnown {RomBuffer::IsRomLoaded() && RomBuffer::IsRomAddressFixed()};
|
|
|
|
const void * rom_address = RomBaseKnown ? RomBuffer::GetFixedRomBaseAddress() : nullptr;
|
|
if (rom_address != nullptr)
|
|
{
|
|
u32 offset = 0;
|
|
switch(g_ROM.rh.CountryID)
|
|
{
|
|
case 0x45: offset = 0x34b30; break;
|
|
case 0x4A: offset = 0x34b70; break;
|
|
case 0x50: offset = 0x329f0; break;
|
|
default:
|
|
offset = 0x34b30; // we can not handle
|
|
return;
|
|
}
|
|
|
|
u32 start_addr = 0x7F000000 >> 18;
|
|
u32 end_addr = 0x7FFFFFFF >> 18;
|
|
|
|
u8 * pRead = (u8*)(reinterpret_cast< uintptr_t >(rom_address) + offset - (start_addr << 18));
|
|
|
|
for (u32 i = start_addr; i <= end_addr; i++)
|
|
{
|
|
g_MemoryLookupTableRead[i].pRead = pRead;
|
|
}
|
|
}
|
|
|
|
g_MemoryLookupTableRead[0x70000000 >> 18].pRead = (u8*)(reinterpret_cast< uintptr_t >( g_pMemoryBuffers[MEM_RD_RAM]) - 0x70000000);
|
|
}
|
|
|
|
static void Memory_InitFunc(u32 start, u32 size, const u32 ReadRegion, const u32 WriteRegion, mReadFunction ReadFunc, mWriteFunction WriteFunc)
|
|
{
|
|
u32 start_addr = (start >> 18);
|
|
u32 end_addr = ((start + size - 1) >> 18);
|
|
|
|
while (start_addr <= end_addr)
|
|
{
|
|
g_MemoryLookupTableRead[start_addr|(0x8000>>2)].ReadFunc= ReadFunc;
|
|
g_MemoryLookupTableWrite[start_addr|(0x8000>>2)].WriteFunc = WriteFunc;
|
|
|
|
g_MemoryLookupTableRead[start_addr|(0xA000>>2)].ReadFunc= ReadFunc;
|
|
g_MemoryLookupTableWrite[start_addr|(0xA000>>2)].WriteFunc = WriteFunc;
|
|
|
|
if (ReadRegion)
|
|
{
|
|
g_MemoryLookupTableRead[start_addr|(0x8000>>2)].pRead = (u8*)(reinterpret_cast< uintptr_t >(g_pMemoryBuffers[ReadRegion]) - (((start>>16)|0x8000) << 16));
|
|
g_MemoryLookupTableRead[start_addr|(0xA000>>2)].pRead = (u8*)(reinterpret_cast< uintptr_t >(g_pMemoryBuffers[ReadRegion]) - (((start>>16)|0xA000) << 16));
|
|
}
|
|
|
|
if (WriteRegion)
|
|
{
|
|
g_MemoryLookupTableWrite[start_addr|(0x8000>>2)].pWrite = (u8*)(reinterpret_cast< uintptr_t >(g_pMemoryBuffers[WriteRegion]) - (((start>>16)|0x8000) << 16));
|
|
g_MemoryLookupTableWrite[start_addr|(0xA000>>2)].pWrite = (u8*)(reinterpret_cast< uintptr_t >(g_pMemoryBuffers[WriteRegion]) - (((start>>16)|0xA000) << 16));
|
|
}
|
|
|
|
start_addr++;
|
|
}
|
|
}
|
|
|
|
void Memory_InitTables()
|
|
{
|
|
memset(g_MemoryLookupTableRead, 0, sizeof(MemFuncRead) * 0x4000);
|
|
memset(g_MemoryLookupTableWrite, 0, sizeof(MemFuncWrite) * 0x4000);
|
|
|
|
u32 i = 0;
|
|
for (i = 0; i < (0x10000 >> 2); i++)
|
|
{
|
|
g_MemoryLookupTableRead[i].pRead = nullptr;
|
|
g_MemoryLookupTableWrite[i].pWrite = nullptr;
|
|
}
|
|
|
|
// 0x00000000 - 0x7FFFFFFF Mapped Memory
|
|
for (i = 0; i < (0x8000 >> 2); i++)
|
|
{
|
|
g_MemoryLookupTableRead[i].ReadFunc = ReadMapped;
|
|
g_MemoryLookupTableWrite[i].WriteFunc = WriteValueMapped;
|
|
}
|
|
|
|
// Invalidate all entries, mapped regions are untouched (0x00000000 - 0x7FFFFFFF, 0xC0000000 - 0x10000000 )
|
|
for (i = (0x8000 >> 2); i < (0xC000 >> 2); i++)
|
|
{
|
|
g_MemoryLookupTableRead[i].ReadFunc = ReadInvalid;
|
|
g_MemoryLookupTableWrite[i].WriteFunc = WriteValueInvalid;
|
|
}
|
|
|
|
// 0xC0000000 - 0x10000000 Mapped Memory
|
|
for (i = (0xC000 >> 2); i < (0x10000 >> 2); i++)
|
|
{
|
|
g_MemoryLookupTableRead[i].ReadFunc = ReadMapped;
|
|
g_MemoryLookupTableWrite[i].WriteFunc = WriteValueMapped;
|
|
}
|
|
|
|
u32 rom_size = RomBuffer::GetRomSize();
|
|
u32 ram_size = gRamSize;
|
|
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
DBGConsole_Msg(0, "Initialising %s main memory", (ram_size == MEMORY_8_MEG) ? "8Mb" : "4Mb");
|
|
#endif
|
|
// Init RDRAM
|
|
// By default we init with EPAK (8Mb)
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_RDRAM,
|
|
MEMORY_SIZE_RDRAM_DEFAULT,
|
|
MEM_RD_RAM,
|
|
MEM_RD_RAM,
|
|
Read_8000_807F,
|
|
WriteValue_8000_807F
|
|
);
|
|
|
|
// Need to turn off the EPAK
|
|
if (ram_size != MEMORY_8_MEG)
|
|
{
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_EXRDRAM,
|
|
MEMORY_SIZE_EXRDRAM,
|
|
MEM_UNUSED,
|
|
MEM_UNUSED,
|
|
ReadInvalid,
|
|
WriteValueInvalid
|
|
);
|
|
}
|
|
|
|
// RDRAM Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_RAMREGS0,
|
|
MEMORY_SIZE_RAMREGS0,
|
|
MEM_RD_REG0,
|
|
MEM_RD_REG0,
|
|
Read_83F0_83F0,
|
|
WriteValue_83F0_83F0
|
|
);
|
|
|
|
|
|
// DMEM/IMEM
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_SPMEM,
|
|
MEMORY_SIZE_SPMEM,
|
|
MEM_SP_MEM,
|
|
MEM_SP_MEM,
|
|
Read_8400_8400,
|
|
WriteValue_8400_8400
|
|
);
|
|
|
|
// SP Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_SPREG_1,
|
|
MEMORY_SIZE_SPREG_1,
|
|
MEM_SP_REG,
|
|
MEM_UNUSED,
|
|
Read_8404_8404,
|
|
WriteValue_8404_8404
|
|
);
|
|
|
|
// SP PC/OBOST
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_SPREG_2,
|
|
MEMORY_SIZE_SPREG_2,
|
|
MEM_SP_PC_REG,
|
|
MEM_SP_PC_REG,
|
|
Read_8408_8408,
|
|
WriteValue_8408_8408
|
|
);
|
|
// DPC Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_DPC,
|
|
MEMORY_SIZE_DPC,
|
|
MEM_DPC_REG,
|
|
MEM_UNUSED,
|
|
Read_8410_841F,
|
|
WriteValue_8410_841F
|
|
);
|
|
|
|
// DPS Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_DPS,
|
|
MEMORY_SIZE_DPS,
|
|
MEM_UNUSED,
|
|
MEM_UNUSED,
|
|
Read_8420_842F,
|
|
WriteValue_8420_842F
|
|
);
|
|
|
|
// MI reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_MI,
|
|
MEMORY_SIZE_MI,
|
|
MEM_MI_REG,
|
|
MEM_UNUSED,
|
|
Read_8430_843F,
|
|
WriteValue_8430_843F
|
|
);
|
|
|
|
// VI Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_VI,
|
|
MEMORY_SIZE_VI,
|
|
MEM_UNUSED,
|
|
MEM_UNUSED,
|
|
Read_8440_844F,
|
|
WriteValue_8440_844F
|
|
);
|
|
|
|
// AI Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_AI,
|
|
MEMORY_SIZE_AI,
|
|
MEM_AI_REG,
|
|
MEM_UNUSED,
|
|
Read_8450_845F,
|
|
WriteValue_8450_845F
|
|
);
|
|
|
|
// PI Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_PI,
|
|
MEMORY_SIZE_PI,
|
|
MEM_PI_REG,
|
|
MEM_UNUSED,
|
|
Read_8460_846F,
|
|
WriteValue_8460_846F
|
|
);
|
|
|
|
// RI Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_RI,
|
|
MEMORY_SIZE_RI,
|
|
MEM_RI_REG,
|
|
MEM_RI_REG,
|
|
Read_8470_847F,
|
|
WriteValue_8470_847F
|
|
);
|
|
|
|
// SI Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_SI,
|
|
MEMORY_SIZE_SI,
|
|
MEM_SI_REG,
|
|
MEM_UNUSED,
|
|
Read_8480_848F,
|
|
WriteValue_8480_848F
|
|
);
|
|
|
|
// Ignore C1A1 and C2A1
|
|
// As a matter of fact handling C2A1 breaks Pokemon Stadium 1 and F-Zero U
|
|
|
|
// Cartridge Domain 2 Address 1 (SRAM)
|
|
/*Memory_InitFunc
|
|
(
|
|
MEMORY_START_C2A1,
|
|
MEMORY_SIZE_C2A1,
|
|
MEM_UNUSED,
|
|
MEM_UNUSED,
|
|
ReadInvalid,
|
|
WriteValueInvalid
|
|
);*/
|
|
|
|
// Cartridge Domain 1 Address 1 (SRAM)
|
|
/*Memory_InitFunc
|
|
(
|
|
MEMORY_START_C1A1,
|
|
MEMORY_SIZE_C1A1,
|
|
MEM_UNUSED,
|
|
MEM_UNUSED,
|
|
ReadInvalid,
|
|
WriteValueInvalid
|
|
);*/
|
|
|
|
// PIF Reg
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_PIF,
|
|
MEMORY_SIZE_PIF,
|
|
MEM_UNUSED,
|
|
MEM_UNUSED,
|
|
Read_9FC0_9FCF,
|
|
WriteValue_9FC0_9FCF
|
|
);
|
|
|
|
// Cartridge Domain 2 Address 2 (FlashRam)
|
|
// FlashRam Read is at 0x800, and FlashRam Write at 0x801
|
|
// BUT since we shift off the insignificant bits, we can't do that, so is handled in the functions itself
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_C2A2,
|
|
MEMORY_SIZE_C2A2,
|
|
MEM_UNUSED,
|
|
MEM_UNUSED,
|
|
ReadFlashRam,
|
|
WriteValue_FlashRam
|
|
);
|
|
|
|
// Cartridge Domain 1 Address 2 (Rom)
|
|
Memory_InitFunc
|
|
(
|
|
MEMORY_START_ROM_IMAGE,
|
|
rom_size,
|
|
MEM_UNUSED,
|
|
MEM_UNUSED,
|
|
ReadROM,
|
|
WriteValue_ROM
|
|
);
|
|
|
|
// Hack the TLB Map per game
|
|
if (g_ROM.GameHacks == GOLDEN_EYE)
|
|
{
|
|
Memory_Tlb_Hack();
|
|
}
|
|
|
|
// Init/Reset flash Ram
|
|
Flash_Init();
|
|
|
|
// Debug only
|
|
#ifndef DAEDALUS_SILENT
|
|
Memory_InitInternalTables( ram_size );
|
|
#endif
|
|
}
|
|
|
|
void MemoryUpdateSPStatus( u32 flags )
|
|
{
|
|
#ifdef DEBUG_SP_STATUS_REG
|
|
DBGConsole_Msg( 0, "----------" );
|
|
if (flags & SP_CLR_HALT) DBGConsole_Msg( 0, "SP: Clearing Halt" );
|
|
if (flags & SP_SET_HALT) DBGConsole_Msg( 0, "SP: Setting Halt" );
|
|
if (flags & SP_CLR_BROKE) DBGConsole_Msg( 0, "SP: Clearing Broke" );
|
|
// No SP_SET_BROKE
|
|
if (flags & SP_CLR_INTR) DBGConsole_Msg( 0, "SP: Clearing Interrupt" );
|
|
if (flags & SP_SET_INTR) DBGConsole_Msg( 0, "SP: Setting Interrupt" );
|
|
if (flags & SP_CLR_SSTEP) DBGConsole_Msg( 0, "SP: Clearing Single Step" );
|
|
if (flags & SP_SET_SSTEP) DBGConsole_Msg( 0, "SP: Setting Single Step" );
|
|
if (flags & SP_CLR_INTR_BREAK) DBGConsole_Msg( 0, "SP: Clearing Interrupt on break" );
|
|
if (flags & SP_SET_INTR_BREAK) DBGConsole_Msg( 0, "SP: Setting Interrupt on break" );
|
|
if (flags & SP_CLR_SIG0) DBGConsole_Msg( 0, "SP: Clearing Sig0 (Yield)" );
|
|
if (flags & SP_SET_SIG0) DBGConsole_Msg( 0, "SP: Setting Sig0 (Yield)" );
|
|
if (flags & SP_CLR_SIG1) DBGConsole_Msg( 0, "SP: Clearing Sig1 (Yielded)" );
|
|
if (flags & SP_SET_SIG1) DBGConsole_Msg( 0, "SP: Setting Sig1 (Yielded)" );
|
|
if (flags & SP_CLR_SIG2) DBGConsole_Msg( 0, "SP: Clearing Sig2 (TaskDone)" );
|
|
if (flags & SP_SET_SIG2) DBGConsole_Msg( 0, "SP: Setting Sig2 (TaskDone)" );
|
|
if (flags & SP_CLR_SIG3) DBGConsole_Msg( 0, "SP: Clearing Sig3" );
|
|
if (flags & SP_SET_SIG3) DBGConsole_Msg( 0, "SP: Setting Sig3" );
|
|
if (flags & SP_CLR_SIG4) DBGConsole_Msg( 0, "SP: Clearing Sig4" );
|
|
if (flags & SP_SET_SIG4) DBGConsole_Msg( 0, "SP: Setting Sig4" );
|
|
if (flags & SP_CLR_SIG5) DBGConsole_Msg( 0, "SP: Clearing Sig5" );
|
|
if (flags & SP_SET_SIG5) DBGConsole_Msg( 0, "SP: Setting Sig5" );
|
|
if (flags & SP_CLR_SIG6) DBGConsole_Msg( 0, "SP: Clearing Sig6" );
|
|
if (flags & SP_SET_SIG6) DBGConsole_Msg( 0, "SP: Setting Sig6" );
|
|
if (flags & SP_CLR_SIG7) DBGConsole_Msg( 0, "SP: Clearing Sig7" );
|
|
if (flags & SP_SET_SIG7) DBGConsole_Msg( 0, "SP: Setting Sig7" );
|
|
#endif
|
|
|
|
// If !HALT && !BROKE
|
|
|
|
bool start_rsp = false;
|
|
bool stop_rsp = false;
|
|
|
|
u32 clr_bits = 0;
|
|
u32 set_bits = 0;
|
|
|
|
if (flags & SP_CLR_HALT)
|
|
{
|
|
clr_bits |= SP_STATUS_HALT;
|
|
start_rsp = true;
|
|
}
|
|
|
|
if (flags & SP_SET_HALT)
|
|
{
|
|
set_bits |= SP_STATUS_HALT;
|
|
stop_rsp = true;
|
|
}
|
|
|
|
if (flags & SP_CLR_BROKE)
|
|
{
|
|
clr_bits |= SP_STATUS_BROKE;
|
|
start_rsp = true;
|
|
}
|
|
|
|
// No SP_SET_BROKE
|
|
|
|
if (flags & SP_CLR_INTR)
|
|
{
|
|
Memory_MI_ClrRegisterBits(MI_INTR_REG, MI_INTR_SP);
|
|
R4300_Interrupt_UpdateCause3();
|
|
}
|
|
if (flags & SP_SET_INTR) // Shouldn't ever set this?
|
|
{
|
|
Memory_MI_SetRegisterBits(MI_INTR_REG, MI_INTR_SP);
|
|
R4300_Interrupt_UpdateCause3();
|
|
}
|
|
if (flags & SP_CLR_SSTEP) clr_bits |= SP_STATUS_SSTEP;
|
|
if (flags & SP_SET_SSTEP) set_bits |= SP_STATUS_SSTEP;
|
|
if (flags & SP_CLR_INTR_BREAK) clr_bits |= SP_STATUS_INTR_BREAK;
|
|
if (flags & SP_SET_INTR_BREAK) set_bits |= SP_STATUS_INTR_BREAK;
|
|
if (flags & SP_CLR_SIG0) clr_bits |= SP_STATUS_SIG0;
|
|
if (flags & SP_SET_SIG0) set_bits |= SP_STATUS_SIG0;
|
|
if (flags & SP_CLR_SIG1) clr_bits |= SP_STATUS_SIG1;
|
|
if (flags & SP_SET_SIG1) set_bits |= SP_STATUS_SIG1;
|
|
if (flags & SP_CLR_SIG2) clr_bits |= SP_STATUS_SIG2;
|
|
if (flags & SP_SET_SIG2) set_bits |= SP_STATUS_SIG2;
|
|
if (flags & SP_CLR_SIG3) clr_bits |= SP_STATUS_SIG3;
|
|
if (flags & SP_SET_SIG3) set_bits |= SP_STATUS_SIG3;
|
|
if (flags & SP_CLR_SIG4) clr_bits |= SP_STATUS_SIG4;
|
|
if (flags & SP_SET_SIG4) set_bits |= SP_STATUS_SIG4;
|
|
if (flags & SP_CLR_SIG5) clr_bits |= SP_STATUS_SIG5;
|
|
if (flags & SP_SET_SIG5) set_bits |= SP_STATUS_SIG5;
|
|
if (flags & SP_CLR_SIG6) clr_bits |= SP_STATUS_SIG6;
|
|
if (flags & SP_SET_SIG6) set_bits |= SP_STATUS_SIG6;
|
|
if (flags & SP_CLR_SIG7) clr_bits |= SP_STATUS_SIG7;
|
|
if (flags & SP_SET_SIG7) set_bits |= SP_STATUS_SIG7;
|
|
|
|
#ifdef DAEDALUS_ENABLE_ASSERTS
|
|
u32 new_status = Memory_SP_SetRegisterBits( SP_STATUS_REG, ~clr_bits, set_bits );
|
|
#else
|
|
Memory_SP_SetRegisterBits( SP_STATUS_REG, ~clr_bits, set_bits );
|
|
#endif
|
|
//
|
|
// We execute the task here, after we've written to the SP status register.
|
|
//
|
|
if( start_rsp )
|
|
{
|
|
#ifdef DAEDALUS_ENABLE_ASSERTS
|
|
//DAEDALUS_ASSERT( !gRSPHLEActive, "RSP HLE already active. Status was %08x, now %08x", status, new_status );
|
|
#endif
|
|
|
|
// Check for tasks whenever the RSP is started
|
|
RSP_HLE_ProcessTask();
|
|
}
|
|
#ifdef DAEDALUS_ENABLE_ASSERTS
|
|
else if ( stop_rsp )
|
|
{
|
|
//DAEDALUS_ASSERT( !RSP_IsRunningHLE(), "Stopping RSP while HLE task still running. Not good!" );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#undef DISPLAY_DPC_WRITES
|
|
|
|
void MemoryUpdateDP( u32 flags )
|
|
{
|
|
// Ignore address, as this is only called with DPC_STATUS_REG write
|
|
// DBGConsole_Msg(0, "DP Status: 0x%08x", flags);
|
|
|
|
u32 dpc_status = Memory_DPC_GetRegister(DPC_STATUS_REG);
|
|
bool unfreeze_task = false;
|
|
|
|
// ToDO : Avoid branching
|
|
if (flags & DPC_CLR_XBUS_DMEM_DMA) dpc_status &= ~DPC_STATUS_XBUS_DMEM_DMA;
|
|
if (flags & DPC_SET_XBUS_DMEM_DMA) dpc_status |= DPC_STATUS_XBUS_DMEM_DMA;
|
|
if (flags & DPC_CLR_FREEZE) { dpc_status &= ~DPC_STATUS_FREEZE; unfreeze_task = true; }
|
|
if (flags & DPC_SET_FREEZE) dpc_status |= DPC_STATUS_FREEZE;
|
|
if (flags & DPC_CLR_FLUSH) dpc_status &= ~DPC_STATUS_FLUSH;
|
|
if (flags & DPC_SET_FLUSH) dpc_status |= DPC_STATUS_FLUSH;
|
|
|
|
/*
|
|
if (flags & DPC_CLR_TMEM_CTR) Memory_DPC_SetRegister(DPC_TMEM_REG, 0);
|
|
if (flags & DPC_CLR_PIPE_CTR) Memory_DPC_SetRegister(DPC_PIPEBUSY_REG, 0);
|
|
if (flags & DPC_CLR_CMD_CTR) Memory_DPC_SetRegister(DPC_BUFBUSY_REG, 0);
|
|
if (flags & DPC_CLR_CLOCK_CTR) Memory_DPC_SetRegister(DPC_CLOCK_REG, 0);
|
|
*/
|
|
|
|
#ifdef DISPLAY_DPC_WRITES
|
|
if ( flags & DPC_CLR_XBUS_DMEM_DMA ) DBGConsole_Msg( 0, "DPC_CLR_XBUS_DMEM_DMA" );
|
|
if ( flags & DPC_SET_XBUS_DMEM_DMA ) DBGConsole_Msg( 0, "DPC_SET_XBUS_DMEM_DMA" );
|
|
if ( flags & DPC_CLR_FREEZE ) DBGConsole_Msg( 0, "DPC_CLR_FREEZE" );
|
|
if ( flags & DPC_SET_FREEZE ) DBGConsole_Msg( 0, "DPC_SET_FREEZE" );
|
|
if ( flags & DPC_CLR_FLUSH ) DBGConsole_Msg( 0, "DPC_CLR_FLUSH" );
|
|
if ( flags & DPC_SET_FLUSH ) DBGConsole_Msg( 0, "DPC_SET_FLUSH" );
|
|
if ( flags & DPC_CLR_TMEM_CTR ) DBGConsole_Msg( 0, "DPC_CLR_TMEM_CTR" );
|
|
if ( flags & DPC_CLR_PIPE_CTR ) DBGConsole_Msg( 0, "DPC_CLR_PIPE_CTR" );
|
|
if ( flags & DPC_CLR_CMD_CTR ) DBGConsole_Msg( 0, "DPC_CLR_CMD_CTR" );
|
|
if ( flags & DPC_CLR_CLOCK_CTR ) DBGConsole_Msg( 0, "DPC_CLR_CLOCK_CTR" );
|
|
|
|
DBGConsole_Msg( 0, "Modified DPC_STATUS_REG - now %08x", dpc_status );
|
|
#endif
|
|
|
|
Memory_DPC_SetRegister(DPC_STATUS_REG, dpc_status);
|
|
|
|
if (unfreeze_task)
|
|
{
|
|
u32 status = Memory_SP_GetRegister( SP_STATUS_REG );
|
|
if((status & SP_STATUS_HALT) == 0)
|
|
{
|
|
#ifdef DAEDALUS_ENABLE_ASSERTS
|
|
DAEDALUS_ASSERT( (status & SP_STATUS_BROKE) == 0, "Unexpected RSP HLE status %08x", status );
|
|
#endif
|
|
RSP_HLE_ProcessTask();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MemoryUpdateMI( u32 value )
|
|
{
|
|
u32 mi_intr_mask_reg = Memory_MI_GetRegister(MI_INTR_MASK_REG);
|
|
u32 mi_intr_reg = Memory_MI_GetRegister(MI_INTR_REG);
|
|
|
|
|
|
u32 clr = 0, set = 0;
|
|
|
|
// From Corn - nicer way to avoid branching
|
|
clr = (value & MI_INTR_MASK_CLR_SP) >> 0;
|
|
set = (value & MI_INTR_MASK_SET_SP) >> 1;
|
|
clr |= (value & MI_INTR_MASK_CLR_SI) >> 1;
|
|
set |= (value & MI_INTR_MASK_SET_SI) >> 2;
|
|
clr |= (value & MI_INTR_MASK_CLR_AI) >> 2;
|
|
set |= (value & MI_INTR_MASK_SET_AI) >> 3;
|
|
clr |= (value & MI_INTR_MASK_CLR_VI) >> 3;
|
|
set |= (value & MI_INTR_MASK_SET_VI) >> 4;
|
|
clr |= (value & MI_INTR_MASK_CLR_PI) >> 4;
|
|
set |= (value & MI_INTR_MASK_SET_PI) >> 5;
|
|
clr |= (value & MI_INTR_MASK_CLR_DP) >> 5;
|
|
set |= (value & MI_INTR_MASK_SET_DP) >> 6;
|
|
|
|
mi_intr_mask_reg &= ~clr;
|
|
mi_intr_mask_reg |= set;
|
|
|
|
Memory_MI_SetRegister( MI_INTR_MASK_REG, mi_intr_mask_reg );
|
|
|
|
// Check if any interrupts are enabled now, and immediately trigger an interrupt
|
|
//if (mi_intr_mask_reg & 0x0000003F & mi_intr_reg)
|
|
if (mi_intr_mask_reg & mi_intr_reg)
|
|
{
|
|
R4300_Interrupt_UpdateCause3();
|
|
}
|
|
}
|
|
|
|
void MemoryModeRegMI( u32 value )
|
|
{
|
|
u32 mi_mode_reg = Memory_MI_GetRegister(MI_MODE_REG);
|
|
|
|
// TODO : Avoid branching
|
|
if (value & MI_SET_RDRAM) mi_mode_reg |= MI_MODE_RDRAM;
|
|
else if (value & MI_CLR_RDRAM) mi_mode_reg &= ~MI_MODE_RDRAM;
|
|
|
|
if (value & MI_SET_INIT) mi_mode_reg |= MI_MODE_INIT;
|
|
else if (value & MI_CLR_INIT) mi_mode_reg &= ~MI_MODE_INIT;
|
|
|
|
if (value & MI_SET_EBUS) mi_mode_reg |= MI_MODE_EBUS;
|
|
else if (value & MI_CLR_EBUS) mi_mode_reg &= ~MI_MODE_EBUS;
|
|
|
|
Memory_MI_SetRegister( MI_MODE_REG, mi_mode_reg );
|
|
|
|
if (value & MI_CLR_DP_INTR)
|
|
{
|
|
Memory_MI_ClrRegisterBits(MI_INTR_REG, MI_INTR_DP);
|
|
R4300_Interrupt_UpdateCause3();
|
|
}
|
|
}
|
|
|
|
#ifdef DAEDALUS_LOG
|
|
static void DisplayVIControlInfo( u32 control_reg )
|
|
{
|
|
DPF( DEBUG_VI, "VI Control:", (control_reg & VI_CTRL_GAMMA_DITHER_ON) ? "On" : "Off" );
|
|
|
|
const char *szMode = "Disabled/Unknown";
|
|
if ((control_reg & VI_CTRL_TYPE_16) == VI_CTRL_TYPE_16) szMode = "16-bit";
|
|
else if ((control_reg & VI_CTRL_TYPE_32) == VI_CTRL_TYPE_32) szMode = "32-bit";
|
|
|
|
DPF( DEBUG_VI, " ColorDepth: %s", szMode );
|
|
DPF( DEBUG_VI, " Gamma Dither: %s", (control_reg & VI_CTRL_GAMMA_DITHER_ON) ? "On" : "Off" );
|
|
DPF( DEBUG_VI, " Gamma: %s", (control_reg & VI_CTRL_GAMMA_ON) ? "On" : "Off" );
|
|
DPF( DEBUG_VI, " Divot: %s", (control_reg & VI_CTRL_DIVOT_ON) ? "On" : "Off" );
|
|
DPF( DEBUG_VI, " Interlace: %s", (control_reg & VI_CTRL_SERRATE_ON) ? "On" : "Off" );
|
|
DPF( DEBUG_VI, " AAMask: 0x%02x", (control_reg&VI_CTRL_ANTIALIAS_MASK)>>8 );
|
|
DPF( DEBUG_VI, " DitherFilter: %s", (control_reg & VI_CTRL_DITHER_FILTER_ON) ? "On" : "Off" );
|
|
}
|
|
#endif
|
|
|
|
void MemoryUpdatePI( u32 value )
|
|
{
|
|
if (value & PI_STATUS_RESET)
|
|
{
|
|
// What to do when is busy?
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
DPF( DEBUG_PI, "PI: Resetting Status. PC: 0x%08x", gCPUState.CurrentPC );
|
|
#endif
|
|
// Reset PIC here
|
|
Memory_PI_SetRegister(PI_STATUS_REG, 0);
|
|
}
|
|
if (value & PI_STATUS_CLR_INTR)
|
|
{
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
DPF( DEBUG_PI, "PI: Clearing interrupt flag. PC: 0x%08x", gCPUState.CurrentPC );
|
|
#endif
|
|
Memory_MI_ClrRegisterBits(MI_INTR_REG, MI_INTR_PI);
|
|
R4300_Interrupt_UpdateCause3();
|
|
}
|
|
}
|
|
|
|
// The PIF control byte has been written to - process this command
|
|
void MemoryUpdatePIF()
|
|
{
|
|
u8 * pPIFRam = (u8 *)g_pMemoryBuffers[MEM_PIF_RAM];
|
|
u8 command = pPIFRam[ 0x3F ^ U8_TWIDDLE];
|
|
if (command == 0x08)
|
|
{
|
|
pPIFRam[ 0x3F ^ U8_TWIDDLE ] = 0x00;
|
|
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
DBGConsole_Msg( 0, "[GSI Interrupt control value: 0x%02x", command );
|
|
#endif
|
|
Memory_SI_SetRegisterBits(SI_STATUS_REG, SI_STATUS_INTERRUPT);
|
|
Memory_MI_SetRegisterBits(MI_INTR_REG, MI_INTR_SI);
|
|
R4300_Interrupt_UpdateCause3();
|
|
}
|
|
#ifdef DAEDALUS_DEBUG_CONSOLE
|
|
else
|
|
{
|
|
DBGConsole_Msg( 0, "[GUnknown control value: 0x%02x", command );
|
|
}
|
|
#endif
|
|
}
|