mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-06-28 01:49:33 -06:00

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@9 8ced0084-cf51-0410-be5f-012b33b47a6e
316 lines
8.0 KiB
C++
316 lines
8.0 KiB
C++
// Copyright (C) 2003-2008 Dolphin Project.
|
|
|
|
// 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, version 2.0.
|
|
|
|
// 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 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
// TODO: create a OS-neutral version of this file and put it in Common.
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <windows.h>
|
|
|
|
#include <vector>
|
|
|
|
#include "Common.h"
|
|
#include "x64Analyzer.h"
|
|
#include "PowerPC/Jit64/Jit.h"
|
|
|
|
#include "MemTools.h"
|
|
|
|
//#ifdef ASDFKJLASJDF
|
|
namespace EMM
|
|
{
|
|
/* DESIGN
|
|
|
|
THIS IS NOT THE CURRENT STATE OF THIS FILE - IT'S UNFINISHED
|
|
|
|
We grab 4GB of virtual address space, and locate memories in there. The memories are either
|
|
VirtualAlloc or mapped swapfile.
|
|
|
|
I/O areas are mapped into the virtual memspace, and VirtualProtected where necessary.
|
|
|
|
Every chunk is mapped twice into memory, once into the virtual memspace, and once elsewhere.
|
|
This second mapping is used when a "read+writable" pointer is requested for a region. This
|
|
would generally be for internal use by IO functions, and for actually performing the writes
|
|
and reads after detecting them.
|
|
|
|
There is individual read and write protection for each chunk of memory.
|
|
|
|
Every region has a default read-write handler. If an exception is caught, this is executed.
|
|
|
|
The default read-write handlers use the "writable" pointers.
|
|
|
|
There should be a method to mark a region for "write notification". Dynarecs can use this
|
|
to flush their code caches if a region is written to.
|
|
|
|
At this moment, there can only be one wrapped memspace at a time.
|
|
*/
|
|
|
|
DWORD_PTR memspaceBottom = 0;
|
|
DWORD_PTR memspaceTop = 0;
|
|
|
|
enum MSFlags
|
|
{
|
|
MEMSPACE_MIRROR_FIRST_PART = 1,
|
|
MEMSPACE_MIRROR_OF_PREVIOUS = 2,
|
|
MEMSPACE_MAPPED_HARDWARE = 4,
|
|
};
|
|
|
|
struct MemSpaceEntry
|
|
{
|
|
u64 emulatedBase;
|
|
u64 emulatedSize;
|
|
u32 flags;
|
|
};
|
|
|
|
#define MEGABYTE 1024*1024
|
|
|
|
const MemSpaceEntry GCMemSpace[] =
|
|
{
|
|
{0x80000000, 24*MEGABYTE, MEMSPACE_MIRROR_FIRST_PART},
|
|
{0xC0000000, 24*MEGABYTE, MEMSPACE_MIRROR_OF_PREVIOUS},
|
|
{0xCC000000, 0x10000, MEMSPACE_MAPPED_HARDWARE},
|
|
{0xE0000000, 0x4000, 0}, //cache
|
|
};
|
|
|
|
struct Watch
|
|
{
|
|
int ID;
|
|
EAddr startAddr;
|
|
EAddr endAddr;
|
|
WR watchFor;
|
|
WatchCallback callback;
|
|
WatchType type;
|
|
u64 userData;
|
|
};
|
|
|
|
std::vector<Watch> watches;
|
|
|
|
|
|
|
|
void UpdateProtection(EAddr startAddr, EAddr endAddr)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
int AddWatchRegion(EAddr startAddr, EAddr endAddr, WR watchFor, WatchType type, WatchCallback callback, u64 userData)
|
|
{
|
|
static int watchIDGen = 0;
|
|
|
|
Watch watch;
|
|
watch.ID = watchIDGen++;
|
|
watch.startAddr = startAddr;
|
|
watch.endAddr = endAddr;
|
|
watch.watchFor = watchFor;
|
|
watch.callback = callback;
|
|
watch.userData = userData;
|
|
watch.type = type;
|
|
watches.push_back(watch);
|
|
UpdateProtection(startAddr, endAddr);
|
|
|
|
return watch.ID;
|
|
}
|
|
|
|
|
|
|
|
void Notify(EAddr address, WR action)
|
|
{
|
|
for (std::vector<Watch>::iterator iter = watches.begin(); iter != watches.end(); ++iter)
|
|
{
|
|
if (action & iter->type)
|
|
{
|
|
if (address >= iter->startAddr && address < iter->endAddr)
|
|
{
|
|
//Alright!
|
|
iter->callback(address, Access32 /*TODO*/, action, iter->ID);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class MemSpace
|
|
{
|
|
MemSpaceEntry *entries;
|
|
|
|
u64 emulatedBottom;
|
|
u64 emulatedTop;
|
|
u64 emulatedSize;
|
|
|
|
void *virtualBase;
|
|
|
|
public:
|
|
|
|
void Init(const MemSpaceEntry *e, int count)
|
|
{
|
|
/*
|
|
//first pass: figure out minimum address, and total amount of allocated memory
|
|
emulatedBase = 0xFFFFFFFFFFFFFFFFL;
|
|
emulatedTop = 0;
|
|
|
|
u64 mappedTotal = 0;
|
|
for (int i=0; i<count; i++)
|
|
{
|
|
if (e[i].emulatedBase < emulatedBase)
|
|
emulatedBase = e[i].emulatedBase;
|
|
if (e[i].emulatedBase+e[i].emulatedSize > emulatedTop)
|
|
emulatedTop = e[i].emulatedBase+e[i].emulatedSize;
|
|
if (e[i].flags & MEMSPACE_MIRROR_FIRST_PART)
|
|
{
|
|
mappedTotal += e[i].emulatedSize;
|
|
}
|
|
}
|
|
emulatedSize = emulatedTop - emulatedBase;
|
|
|
|
// The above stuff is not used atm - we just grab 4G
|
|
|
|
//second pass: grab 4G of virtual address space
|
|
virtualBase = VirtualAlloc(0, 0x100000000L, MEM_RESERVE, PAGE_READWRITE);
|
|
|
|
//also grab a bunch of virtual memory while we're at it
|
|
|
|
|
|
//Release the 4G space!
|
|
//Let's hope no weirdo thread klomps in here and grabs it
|
|
VirtualFree(base, 0, MEM_RELEASE);
|
|
|
|
for (int i=0; i<count; i++)
|
|
{
|
|
if (e[i].flags & MEMSPACE_MIRROR_FIRST_PART)
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
//TODO: fill all empty parts of the address space with no-access virtualalloc space
|
|
*/
|
|
}
|
|
|
|
u64 GetVirtualBaseAddr() {return (u64)virtualBase;}
|
|
void *GetVirtualBase() {return virtualBase;}
|
|
|
|
void Shutdown()
|
|
{
|
|
|
|
}
|
|
};
|
|
|
|
LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs)
|
|
{
|
|
switch (pPtrs->ExceptionRecord->ExceptionCode)
|
|
{
|
|
case EXCEPTION_ACCESS_VIOLATION:
|
|
{
|
|
int accessType = (int)pPtrs->ExceptionRecord->ExceptionInformation[0];
|
|
if (accessType == 8) //Rule out DEP
|
|
{
|
|
MessageBox(0, _T("Tried to execute code that's not marked executable. This is likely a JIT bug.\n"), 0, 0);
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
//Where in the x86 code are we?
|
|
PVOID codeAddr = pPtrs->ExceptionRecord->ExceptionAddress;
|
|
unsigned char *codePtr = (unsigned char*)codeAddr;
|
|
|
|
if (!Jit64::IsInJitCode(codePtr)) {
|
|
// Let's not prevent debugging.
|
|
return (DWORD)EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
//Figure out what address was hit
|
|
DWORD_PTR badAddress = (DWORD_PTR)pPtrs->ExceptionRecord->ExceptionInformation[1];
|
|
//TODO: First examine the address, make sure it's within the emulated memory space
|
|
if (badAddress < memspaceBottom) {
|
|
PanicAlert("Exception handler - access below memory space. %08x%08x",
|
|
badAddress >> 32, badAddress);
|
|
}
|
|
u32 emAddress = (u32)(badAddress - memspaceBottom);
|
|
|
|
//Now we have the emulated address.
|
|
//_assert_msg_(DYNA_REC,0,"MT : %08x",emAddress);
|
|
|
|
//Let's notify everyone who wants to be notified
|
|
//Notify(emAddress, accessType == 0 ? Read : Write);
|
|
|
|
CONTEXT *ctx = pPtrs->ContextRecord;
|
|
//opportunity to change the debug regs!
|
|
|
|
//We could emulate the memory accesses here, but then they would still be around to take up
|
|
//execution resources. Instead, we backpatch and retry.
|
|
Jit64::BackPatch(codePtr, accessType);
|
|
|
|
// We no longer touch Rip, since we return back to the instruction, after overwriting it with a
|
|
// trampoline jump and some nops
|
|
//ctx->Rip = (DWORD_PTR)codeAddr + info.instructionSize;
|
|
}
|
|
return (DWORD)EXCEPTION_CONTINUE_EXECUTION;
|
|
|
|
case EXCEPTION_STACK_OVERFLOW:
|
|
MessageBox(0, _T("Stack overflow!"), 0,0);
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
|
//No SSE support? Or simply bad codegen?
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
case EXCEPTION_PRIV_INSTRUCTION:
|
|
//okay, dynarec codegen is obviously broken.
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
case EXCEPTION_IN_PAGE_ERROR:
|
|
//okay, something went seriously wrong, out of memory?
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
case EXCEPTION_BREAKPOINT:
|
|
//might want to do something fun with this one day?
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
|
|
default:
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
}
|
|
|
|
void InstallExceptionHandler()
|
|
{
|
|
#ifdef _M_X64
|
|
// Make sure this is only called once per process execution
|
|
// Instead, could make a Uninstall function, but whatever..
|
|
static bool handlerInstalled = false;
|
|
if (handlerInstalled)
|
|
return;
|
|
|
|
AddVectoredExceptionHandler(TRUE, Handler);
|
|
handlerInstalled = true;
|
|
#endif
|
|
}
|
|
|
|
}
|
|
#else
|
|
|
|
namespace EMM {
|
|
|
|
void InstallExceptionHandler()
|
|
{
|
|
/*
|
|
* signal(xyz);
|
|
*/
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|