Commit 4c909bac authored by Flyinghead's avatar Flyinghead
Browse files

Unify vmem/vmem32 (WIP). Support different RX and RW ptrs

parent 2230e913
......@@ -311,7 +311,8 @@ endif
SOURCES_CXX += $(CORE_DIR)/core/libretro/libretro.cpp \
$(CORE_DIR)/core/libretro/audiostream.cpp \
$(CORE_DIR)/core/libretro/common.cpp
$(CORE_DIR)/core/libretro/common.cpp \
$(CORE_DIR)/core/libretro/vmem_utils.cpp
SOURCES_C += $(LIBRETRO_COMM_DIR)/memmap/memalign.c \
$(LIBRETRO_COMM_DIR)/file/retro_stat.c
......
......@@ -251,3 +251,15 @@
#ifdef HOST_NO_AREC
#error Dont use HOST_NO_AREC
#endif
// Some restrictions on FEAT_NO_RWX_PAGES
#if defined(FEAT_NO_RWX_PAGES) && FEAT_SHREC == DYNAREC_JIT
#if HOST_CPU != CPU_X64 && HOST_CPU != CPU_ARM64
#error "FEAT_NO_RWX_PAGES Only implemented for X64 and ARMv8"
#endif
#endif
#define RAM_SIZE_MAX (32*1024*1024)
#define VRAM_SIZE_MAX (16*1024*1024)
#define ARAM_SIZE_MAX (8*1024*1024)
......@@ -27,7 +27,7 @@
#include "deps/vixl/aarch64/macro-assembler-aarch64.h"
using namespace vixl::aarch64;
extern void Arm64CacheFlush(void* start, void* end);
extern void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end);
class DSPAssembler : public MacroAssembler
{
......@@ -53,10 +53,9 @@ public:
Stp(xzr, xzr, MemOperand(x0, 48));
Ret();
FinalizeCode();
#ifdef _ANDROID
Arm64CacheFlush(GetBuffer()->GetStartAddress<void*>(), GetBuffer()->GetEndAddress<void*>());
#endif
vmem_platform_flush_cache(
GetBuffer()->GetStartAddress<void*>(), GetBuffer()->GetEndAddress<void*>(),
GetBuffer()->GetStartAddress<void*>(), GetBuffer()->GetEndAddress<void*>());
return;
}
......@@ -367,7 +366,9 @@ public:
FinalizeCode();
Arm64CacheFlush(GetBuffer()->GetStartAddress<void*>(), GetBuffer()->GetEndAddress<void*>());
vmem_platform_flush_cache(
GetBuffer()->GetStartAddress<void*>(), GetBuffer()->GetEndAddress<void*>(),
GetBuffer()->GetStartAddress<void*>(), GetBuffer()->GetEndAddress<void*>());
}
private:
......
......@@ -28,7 +28,7 @@
using namespace vixl::aarch64;
//#include "deps/vixl/aarch32/disasm-aarch32.h"
extern void Arm64CacheFlush(void* start, void* end);
extern void vmem_platform_flush_cache(void *icache_start, void *icache_end, void *dcache_start, void *dcache_end);
extern u32 arm_single_op(u32 opcode);
extern "C" void arm_dispatch();
extern "C" void arm_exit();
......@@ -41,7 +41,7 @@ extern reg_pair arm_Reg[RN_ARM_REG_COUNT];
MacroAssembler *assembler;
extern "C" void armFlushICache(void *bgn, void *end) {
Arm64CacheFlush(bgn, end);
vmem_platform_flush_cache(bgn, end, bgn, end);
}
static MemOperand arm_reg_operand(u32 regn)
......@@ -143,7 +143,9 @@ void armv_end(void* codestart, u32 cycl)
assembler->FinalizeCode();
verify(assembler->GetBuffer()->GetCursorOffset() <= assembler->GetBuffer()->GetCapacity());
Arm64CacheFlush(codestart, assembler->GetBuffer()->GetEndAddress<void*>());
vmem_platform_flush_cache(
codestart, assembler->GetBuffer()->GetEndAddress<void*>(),
codestart, assembler->GetBuffer()->GetEndAddress<void*>());
icPtr += assembler->GetBuffer()->GetSizeInBytes();
#if 0
......
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#endif
#include <set>
#include "_vmem.h"
#include "vmem32.h"
#include "hw/aica/aica_if.h"
#include "hw/sh4/dyna/blockmanager.h"
......@@ -33,7 +22,6 @@ _vmem_WriteMem32FP* _vmem_WF32[HANDLER_COUNT];
//upper 8b of the address
void* _vmem_MemInfo_ptr[0x100];
void _vmem_get_ptrs(u32 sz,bool write,void*** vmap,void*** func)
{
*vmap=_vmem_MemInfo_ptr;
......@@ -429,6 +417,7 @@ void _vmem_term()
#include "hw/sh4/sh4_mem.h"
u8* virt_ram_base;
bool vmem_4gb_space;
void* malloc_pages(size_t size) {
#ifdef _WIN32
......@@ -444,380 +433,206 @@ void* malloc_pages(size_t size) {
#endif
}
static bool _vmem_reserve_nonvmem(void)
{
virt_ram_base = 0;
p_sh4rcb=(Sh4RCB*)malloc_pages(sizeof(Sh4RCB));
mem_b.size=RAM_SIZE;
mem_b.data=(u8*)malloc_pages(RAM_SIZE);
vram.size=VRAM_SIZE;
vram.data=(u8*)malloc_pages(VRAM_SIZE);
aica_ram.size=ARAM_SIZE;
aica_ram.data=(u8*)malloc_pages(ARAM_SIZE);
return true;
}
void _vmem_bm_reset_nvmem(void);
// Resets the FPCB table (by either clearing it to the default val
// or by flushing it and making it fault on access again.
void _vmem_bm_reset(void)
{
#if !defined(TARGET_NO_NVMEM)
if (virt_ram_base) {
_vmem_bm_reset_nvmem();
}
#endif
#if FEAT_SHREC != DYNAREC_NONE
if (!virt_ram_base)
bm_vmem_pagefill((void**)p_sh4rcb->fpcb, FPCB_SIZE);
#endif
// If we allocated it via vmem:
if (virt_ram_base)
vmem_platform_reset_mem(p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb));
else
// We allocated it via a regular malloc/new/whatever on the heap
bm_vmem_pagefill((void**)p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb));
}
#if !defined(TARGET_NO_NVMEM)
#define MAP_RAM_START_OFFSET 0
#define MAP_VRAM_START_OFFSET (MAP_RAM_START_OFFSET+RAM_SIZE)
#define MAP_ARAM_START_OFFSET (MAP_VRAM_START_OFFSET+VRAM_SIZE)
#ifdef _WIN32
HANDLE mem_handle;
static void* _nvmem_map_buffer(u32 dst,u32 addrsz,u32 offset,u32 size, bool w)
{
void* ptr;
void* rv;
u32 map_times=addrsz/size;
verify((addrsz%size)==0);
verify(map_times>=1);
// This gets called whenever there is a pagefault, it is possible that it lands
// on the fpcb memory range, which is allocated on miss. Returning true tells the
// fault handler this was us, and that the page is resolved and can continue the execution.
bool BM_LockedWrite(u8* address) {
if (!virt_ram_base)
return false; // No vmem, therefore not us who caused this.
rv= MapViewOfFileEx(mem_handle,FILE_MAP_READ | (w?FILE_MAP_WRITE:0),0,offset,size,&virt_ram_base[dst]);
if (!rv)
return 0;
uintptr_t ptrint = (uintptr_t)address;
uintptr_t start = (uintptr_t)p_sh4rcb->fpcb;
uintptr_t end = start + sizeof(p_sh4rcb->fpcb);
for (u32 i=1;i<map_times;i++)
{
dst+=size;
ptr=MapViewOfFileEx(mem_handle,FILE_MAP_READ | (w?FILE_MAP_WRITE:0),0,offset,size,&virt_ram_base[dst]);
if (!ptr) return 0;
if (ptrint >= start && ptrint < end) {
// Alloc the page then and initialize it to default values
void *aligned_addr = (void*)(ptrint & (~PAGE_MASK));
vmem_platform_ondemand_page(aligned_addr, PAGE_SIZE);
bm_vmem_pagefill((void**)aligned_addr, PAGE_SIZE);
return true;
}
return rv;
}
static void* _nvmem_unused_buffer(u32 start,u32 end)
{
void* ptr=VirtualAlloc(&virt_ram_base[start],end-start,MEM_RESERVE,PAGE_NOACCESS);
if (ptr == 0)
return 0;
return ptr;
}
static void* _nvmem_alloc_mem(void)
{
mem_handle = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, RAM_SIZE + VRAM_SIZE + ARAM_SIZE, 0);
void* rv = (u8*)VirtualAlloc(0, 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE, MEM_RESERVE, PAGE_NOACCESS);
if (rv) VirtualFree(rv,0,MEM_RELEASE);
return rv;
}
#else
#ifndef MAP_NOSYNC
#define MAP_NOSYNC 0 //missing from linux :/ -- could be the cause of android slowness ?
#endif
#ifdef ANDROID
#include <linux/ashmem.h>
#ifndef ASHMEM_DEVICE
#define ASHMEM_DEVICE "/dev/ashmem"
#endif
int ashmem_create_region(const char *name, size_t size)
{
int fd, ret;
fd = open(ASHMEM_DEVICE, O_RDWR);
if (fd < 0)
return fd;
if (name) {
char buf[ASHMEM_NAME_LEN];
strlcpy(buf, name, sizeof(buf));
ret = ioctl(fd, ASHMEM_SET_NAME, buf);
if (ret < 0)
goto error;
}
ret = ioctl(fd, ASHMEM_SET_SIZE, size);
if (ret < 0)
goto error;
return fd;
error:
close(fd);
return ret;
}
#endif
int vmem_fd;
static void* _nvmem_unused_buffer(u32 start,u32 end)
{
void* ptr=mmap(&virt_ram_base[start], end-start, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0);
if (MAP_FAILED==ptr)
{
printf("nvmem_unused_buffer failed!\n");
return 0;
}
return ptr;
}
static void * _nvmem_map_buffer(u32 dst,u32 addrsz,u32 offset,u32 size, bool w)
{
void* ptr;
void* rv;
printf("MAP %08X w/ %d\n",dst,offset);
u32 map_times=addrsz/size;
verify((addrsz%size)==0);
verify(map_times>=1);
u32 prot=PROT_READ|(w?PROT_WRITE:0);
rv= mmap(&virt_ram_base[dst], size, prot, MAP_SHARED | MAP_NOSYNC | MAP_FIXED, vmem_fd, offset);
if (MAP_FAILED==rv || rv!=(void*)&virt_ram_base[dst] || (mprotect(rv,size,prot)!=0))
{
printf("MAP1 failed %d\n",errno);
return 0;
}
for (u32 i=1;i<map_times;i++)
{
dst+=size;
ptr=mmap(&virt_ram_base[dst], size, prot , MAP_SHARED | MAP_NOSYNC | MAP_FIXED, vmem_fd, offset);
if (MAP_FAILED==ptr || ptr!=(void*)&virt_ram_base[dst] || (mprotect(rv,size,prot)!=0))
{
printf("MAP2 failed %d\n",errno);
return 0;
}
}
return rv;
}
static void* _nvmem_alloc_mem(void)
{
string path = get_writable_data_path("dcnzorz_mem");
#ifdef __MACH__
vmem_fd = open(path.c_str(),O_CREAT|O_RDWR|O_TRUNC,S_IRWXU|S_IRWXG|S_IRWXO);
unlink(path.c_str());
#elif defined(ANDROID)
vmem_fd = ashmem_create_region(0,RAM_SIZE + VRAM_SIZE +ARAM_SIZE);
#else
vmem_fd = shm_open(path.c_str(), O_CREAT | O_EXCL | O_RDWR,S_IREAD | S_IWRITE);
shm_unlink(path.c_str());
if (vmem_fd==-1)
{
vmem_fd = open(path.c_str(),O_CREAT|O_RDWR|O_TRUNC,S_IRWXU|S_IRWXG|S_IRWXO);
unlink(path.c_str());
}
#endif
#if defined(__MACH__) || !defined(ANDROID)
verify(ftruncate(vmem_fd,RAM_SIZE + VRAM_SIZE +ARAM_SIZE)==0);
#endif
u32 sz= 512*1024*1024 + sizeof(Sh4RCB) + ARAM_SIZE + 0x10000;
void* rv=mmap(0, sz, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
verify(rv != NULL);
munmap(rv,sz);
return (u8*)rv + 0x10000 - size_t(rv)%0x10000;//align to 64 KB (Needed for linaro mmap not to extend to next region)
}
#endif
#define map_buffer(dsts,dste,offset,sz,w) {ptr=_nvmem_map_buffer(dsts,dste-dsts,offset,sz,w);if (!ptr) return false;}
#define unused_buffer(start,end) {ptr=_nvmem_unused_buffer(start,end);if (!ptr) return false;}
u32 pagecnt;
void _vmem_bm_reset_nvmem(void)
{
#if defined(TARGET_NO_NVMEM)
return;
#endif
#ifdef IOS
/* On iOS & nacl we allways allocate all of the mapping table */
mprotect(p_sh4rcb, sizeof(p_sh4rcb->fpcb), PROT_READ | PROT_WRITE);
return;
#endif
pagecnt=0;
#ifdef _WIN32
VirtualFree(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MEM_DECOMMIT);
#else
mprotect(p_sh4rcb, sizeof(p_sh4rcb->fpcb), PROT_NONE);
#ifdef __HAIKU__
posix_madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),POSIX_MADV_DONTNEED);
#else
madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MADV_DONTNEED);
#endif
#ifdef MADV_REMOVE
madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MADV_REMOVE);
#else
#ifdef __HAIKU__
posix_madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),POSIX_MADV_DONTNEED);
#else
/* OSX, IOS */
madvise(p_sh4rcb,sizeof(p_sh4rcb->fpcb),MADV_FREE);
#endif
#endif
#endif
printf("Freeing fpcb\n");
return false;
}
bool BM_LockedWrite(u8* address)
static void _vmem_set_p0_mappings()
{
if (!_nvmem_enabled())
return false;
#if FEAT_SHREC != DYNAREC_NONE
size_t addr=address-(u8*)p_sh4rcb->fpcb;
address=(u8*)p_sh4rcb->fpcb+ (addr&~SH4_PAGE_MASK);
if (addr<sizeof(p_sh4rcb->fpcb))
{
//printf("Allocated %d PAGES [%08X]\n",++pagecnt,addr);
#ifdef _WIN32
verify(VirtualAlloc(address,PAGE_SIZE,MEM_COMMIT,PAGE_READWRITE));
#else
mprotect (address, PAGE_SIZE, PROT_READ | PROT_WRITE);
#endif
bm_vmem_pagefill((void**)address,PAGE_SIZE);
return true;
}
#else
die("BM_LockedWrite and NO REC");
#endif
return false;
const vmem_mapping mem_mappings[] = {
// P0/U0
{0x00000000, 0x00800000, 0, 0, false}, // Area 0 -> unused
{0x00800000, 0x00800000 + ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, // Aica
{0x00800000 + ARAM_SIZE, 0x02800000, 0, 0, false}, // unused
{0x02800000, 0x02800000 + ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, // Aica mirror
{0x02800000 + ARAM_SIZE, 0x04000000, 0, 0, false}, // unused
{0x04000000, 0x05000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // Area 1 (vram, 16MB, wrapped on DC as 2x8MB)
{0x05000000, 0x06000000, 0, 0, false}, // 32 bit path (unused)
{0x06000000, 0x07000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // VRAM mirror
{0x07000000, 0x08000000, 0, 0, false}, // 32 bit path (unused) mirror
{0x08000000, 0x0C000000, 0, 0, false}, // Area 2
{0x0C000000, 0x10000000, MAP_RAM_START_OFFSET, RAM_SIZE, true}, // Area 3 (main RAM + 3 mirrors)
{0x10000000, 0x80000000, 0, 0, false}, // Area 4-7 (unused)
};
vmem_platform_create_mappings(&mem_mappings[0], ARRAY_SIZE(mem_mappings));
}
bool _vmem_reserve(void)
{
void* ptr=0;
verify((sizeof(Sh4RCB)%PAGE_SIZE)==0);
if (settings.dynarec.disable_nvmem)
return _vmem_reserve_nonvmem();
virt_ram_base=(u8*)_nvmem_alloc_mem();
if (virt_ram_base==0)
return _vmem_reserve_nonvmem();
p_sh4rcb=(Sh4RCB*)virt_ram_base;
#ifdef _WIN32
verify(p_sh4rcb==VirtualAlloc(p_sh4rcb,sizeof(Sh4RCB),MEM_RESERVE,PAGE_NOACCESS));
verify(VirtualAlloc((u8*)p_sh4rcb + sizeof(p_sh4rcb->fpcb),sizeof(Sh4RCB)-sizeof(p_sh4rcb->fpcb),MEM_COMMIT,PAGE_READWRITE));
#else
void *ret = mmap(p_sh4rcb,sizeof(Sh4RCB),PROT_READ|PROT_WRITE,MAP_PRIVATE | MAP_ANON, -1, 0);
verify(p_sh4rcb == ret);
#endif
virt_ram_base+=sizeof(Sh4RCB);
//Area 0
//[0x00000000 ,0x00800000) -> unused
unused_buffer(0x00000000,0x00800000);
//I wonder, aica ram warps here ?.?
//I really should check teh docs before codin ;p
//[0x00800000,0x00A00000);
map_buffer(0x00800000,0x01000000,MAP_ARAM_START_OFFSET,ARAM_SIZE,false);
map_buffer(0x20000000,0x20000000+ARAM_SIZE,MAP_ARAM_START_OFFSET,ARAM_SIZE,true);
aica_ram.size=ARAM_SIZE;
aica_ram.data=(u8*)ptr;
//[0x01000000 ,0x04000000) -> unused
unused_buffer(0x01000000,0x04000000);
VMemType vmemstatus = MemTypeError;
//Area 1
//[0x04000000,0x05000000) -> vram (16mb, warped on dc)
map_buffer(0x04000000,0x05000000,MAP_VRAM_START_OFFSET,VRAM_SIZE,true);
// Use vmem only if settings mandate so, and if we have proper exception handlers.
#ifndef TARGET_NO_EXCEPTIONS
if (!settings.dynarec.disable_nvmem)
vmemstatus = vmem_platform_init((void**)&virt_ram_base, (void**)&p_sh4rcb);
#endif
vram.size=VRAM_SIZE;
vram.data=(u8*)ptr;
// Fallback to statically allocated buffers, this results in slow-ops being generated.
if (vmemstatus == MemTypeError) {
printf("Warning! nvmem is DISABLED (due to failure or not being built-in\n");
virt_ram_base = 0;
//[0x05000000,0x06000000) -> unused (32b path)
unused_buffer(0x05000000,0x06000000);
// Allocate it all and initialize it.
p_sh4rcb = (Sh4RCB*)malloc_pages(sizeof(Sh4RCB));
bm_vmem_pagefill((void**)p_sh4rcb->fpcb, sizeof(p_sh4rcb->fpcb));
//[0x06000000,0x07000000) -> vram mirror
map_buffer(0x06000000,0x07000000,MAP_VRAM_START_OFFSET,VRAM_SIZE,true);
mem_b.size = RAM_SIZE;
mem_b.data = (u8*)malloc_pages(RAM_SIZE);
vram.size = VRAM_SIZE;
vram.data = (u8*)malloc_pages(VRAM_SIZE);
//[0x07000000,0x08000000) -> unused (32b path) mirror
unused_buffer(0x07000000,0x08000000);
//Area 2
//[0x08000000,0x0C000000) -> unused
unused_buffer(0x08000000,0x0C000000);
//Area 3
//[0x0C000000,0x0D000000) -> main ram
//[0x0D000000,0x0E000000) -> main ram mirror
//[0x0E000000,0x0F000000) -> main ram mirror
//[0x0F000000,0x10000000) -> main ram mirror
map_buffer(0x0C000000,0x10000000,MAP_RAM_START_OFFSET,RAM_SIZE,true);
mem_b.size=RAM_SIZE;
mem_b.data=(u8*)ptr;
//Area 4
//Area 5
//Area 6
//Area 7
//all -> Unused
//[0x10000000,0x20000000) -> unused
unused_buffer(0x10000000,0x20000000);
aica_ram.size = ARAM_SIZE;
aica_ram.data = (u8*)malloc_pages(ARAM_SIZE);
}
else {
printf("Info: nvmem is enabled, with addr space of size %s\n", vmemstatus == MemType4GB ? "4GB" : "512MB");
printf("Info: p_sh4rcb: %p virt_ram_base: %p\n", p_sh4rcb, virt_ram_base);
// Map the different parts of the memory file into the new memory range we got.
if (vmemstatus == MemType512MB)
{
vmem_mapping mem_mappings[] = {
{0x00000000, 0x00800000, 0, 0, false}, // Area 0 -> unused
{0x00800000, 0x01000000, MAP_ARAM_START_OFFSET, ARAM_SIZE, false}, // Aica
{0x20000000, 0x20000000+ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true},
{0x01000000, 0x04000000, 0, 0, false}, // More unused
{0x04000000, 0x05000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // Area 1 (vram, 16MB, wrapped on DC as 2x8MB)
{0x05000000, 0x06000000, 0, 0, false}, // 32 bit path (unused)
{0x06000000, 0x07000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // VRAM mirror
{0x07000000, 0x08000000, 0, 0, false}, // 32 bit path (unused) mirror
{0x08000000, 0x0C000000, 0, 0, false}, // Area 2
{0x0C000000, 0x10000000, MAP_RAM_START_OFFSET, RAM_SIZE, true}, // Area 3 (main RAM + 3 mirrors)
{0x10000000, 0x20000000, 0, 0, false}, // Area 4-7 (unused)
};
vmem_platform_create_mappings(&mem_mappings[0], ARRAY_SIZE(mem_mappings));
// Point buffers to actual data pointers
aica_ram.data = &virt_ram_base[0x20000000]; // Points to the writable AICA addrspace
vram.data = &virt_ram_base[0x04000000]; // Points to first vram mirror (writable and lockable)
mem_b.data = &virt_ram_base[0x0C000000]; // Main memory, first mirror
}
else
{
_vmem_set_p0_mappings();
const vmem_mapping mem_mappings[] = {
// P1
{0x80000000, 0x80800000, 0, 0, false}, // Area 0 -> unused
{0x80800000, 0x80800000 + ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, // Aica
{0x80800000 + ARAM_SIZE, 0x82800000, 0, 0, false}, // unused
{0x82800000, 0x82800000 + ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, // Aica mirror
{0x82800000 + ARAM_SIZE, 0x84000000, 0, 0, false}, // unused
{0x84000000, 0x85000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // Area 1 (vram, 16MB, wrapped on DC as 2x8MB)
{0x85000000, 0x86000000, 0, 0, false}, // 32 bit path (unused)
{0x86000000, 0x87000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // VRAM mirror
{0x87000000, 0x88000000, 0, 0, false}, // 32 bit path (unused) mirror
{0x88000000, 0x8C000000, 0, 0, false}, // Area 2
{0x8C000000, 0x90000000, MAP_RAM_START_OFFSET, RAM_SIZE, true}, // Area 3 (main RAM + 3 mirrors)
{0x90000000, 0xA0000000, 0, 0, false}, // Area 4-7 (unused)
// P2
{0xA0000000, 0xA0800000, 0, 0, false}, // Area 0 -> unused
{0xA0800000, 0xA0800000 + ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, // Aica
{0xA0800000 + ARAM_SIZE, 0xA2800000, 0, 0, false}, // unused
{0xA2800000, 0xA2800000 + ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, // Aica mirror
{0xA2800000 + ARAM_SIZE, 0xA4000000, 0, 0, false}, // unused
{0xA4000000, 0xA5000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // Area 1 (vram, 16MB, wrapped on DC as 2x8MB)
{0xA5000000, 0xA6000000, 0, 0, false}, // 32 bit path (unused)
{0xA6000000, 0xA7000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // VRAM mirror
{0xA7000000, 0xA8000000, 0, 0, false}, // 32 bit path (unused) mirror
{0xA8000000, 0xAC000000, 0, 0, false}, // Area 2
{0xAC000000, 0xB0000000, MAP_RAM_START_OFFSET, RAM_SIZE, true}, // Area 3 (main RAM + 3 mirrors)
{0xB0000000, 0xC0000000, 0, 0, false}, // Area 4-7 (unused)
// P3
{0xC0000000, 0xC0800000, 0, 0, false}, // Area 0 -> unused
{0xC0800000, 0xC0800000 + ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, // Aica
{0xC0800000 + ARAM_SIZE, 0xC2800000, 0, 0, false}, // unused
{0xC2800000, 0xC2800000 + ARAM_SIZE, MAP_ARAM_START_OFFSET, ARAM_SIZE, true}, // Aica mirror
{0xC2800000 + ARAM_SIZE, 0xC4000000, 0, 0, false}, // unused
{0xC4000000, 0xC5000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // Area 1 (vram, 16MB, wrapped on DC as 2x8MB)
{0xC5000000, 0xC6000000, 0, 0, false}, // 32 bit path (unused)
{0xC6000000, 0xC7000000, MAP_VRAM_START_OFFSET, VRAM_SIZE, true}, // VRAM mirror
{0xC7000000, 0xC8000000, 0, 0, false}, // 32 bit path (unused) mirror